OSDN Git Service

88feca03dd3315af4384de070b09c39c0c70e562
[opentween/open-tween.git] / OpenTween / Growl.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
3 //           (c) 2008-2011 Moz (@syo68k)
4 //           (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
5 //           (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
6 //           (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
7 //           (c) 2011      kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
8 // All rights reserved.
9 // 
10 // This file is part of OpenTween.
11 // 
12 // This program is free software; you can redistribute it and/or modify it
13 // under the terms of the GNU General public License as published by the Free
14 // Software Foundation; either version 3 of the License, or (at your option)
15 // any later version.
16 // 
17 // This program is distributed in the hope that it will be useful, but
18 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General public License
20 // for more details. 
21 // 
22 // You should have received a copy of the GNU General public License along
23 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
24 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
25 // Boston, MA 02110-1301, USA.
26
27 using System;
28 using System.Collections.Generic;
29 using System.Linq;
30 using System.Text;
31 using System.Reflection;
32 using System.IO;
33 using System.Drawing;
34 using System.Drawing.Imaging;
35 using System.Windows.Forms;
36 using System.ComponentModel;
37 using System.Collections;
38 using System.Globalization;
39
40 namespace OpenTween
41 {
42     public class GrowlHelper
43     {
44         private Assembly _connector = null;
45         private Assembly _core = null;
46
47         private object _growlNTreply;
48         private object _growlNTdm;
49         private object _growlNTnew;
50         private object _growlNTusevent;
51         private object _growlApp;
52
53         private object _targetConnector;
54         bool _initialized = false;
55
56         public class NotifyCallbackEventArgs : EventArgs
57         {
58             public long StatusId { get; set; }
59             public NotifyType NotifyType { get; set; }
60             public NotifyCallbackEventArgs(NotifyType notifyType, string statusId)
61             {
62                 if (statusId.Length > 1)
63                 {
64                     this.StatusId = long.Parse(statusId);
65                     this.NotifyType = notifyType;
66                 }
67             }
68         }
69
70         public event EventHandler<NotifyCallbackEventArgs> NotifyClicked;
71
72         public string AppName { get; }
73
74         public enum NotifyType
75         {
76             Reply = 0,
77             DirectMessage = 1,
78             Notify = 2,
79             UserStreamEvent = 3,
80         }
81
82         public GrowlHelper(string appName)
83             => this.AppName = appName;
84
85         public bool IsAvailable
86         {
87             get
88             {
89                 if (_connector == null || _core == null || !_initialized)
90                     return false;
91                 else
92                     return true;
93             }
94         }
95
96         private byte[] IconToByteArray(string filename)
97         {
98             using (Icon ic = new Icon(filename))
99             {
100                 return IconToByteArray(ic);
101             }
102         }
103
104         private byte[] IconToByteArray(Icon icondata)
105         {
106             using (MemoryStream ms = new MemoryStream())
107             {
108                 using (Icon ic = new Icon(icondata, 48, 48))
109                 {
110                     ic.ToBitmap().Save(ms, ImageFormat.Png);
111                     return ms.ToArray();
112                 }
113             }
114         }
115
116         public static bool IsDllExists
117         {
118             get
119             {
120                 string dir = Application.StartupPath;
121                 string connectorPath = Path.Combine(dir, "Growl.Connector.dll");
122                 string corePath = Path.Combine(dir, "Growl.CoreLibrary.dll");
123                 if (File.Exists(connectorPath) && File.Exists(corePath))
124                     return true;
125                 else
126                     return false;
127             }
128         }
129
130         public bool RegisterGrowl()
131         {
132             _initialized = false;
133             string dir = Application.StartupPath;
134             string connectorPath = Path.Combine(dir, "Growl.Connector.dll");
135             string corePath = Path.Combine(dir, "Growl.CoreLibrary.dll");
136
137             try
138             {
139                 if (!IsDllExists) return false;
140                 _connector = Assembly.LoadFile(connectorPath);
141                 _core = Assembly.LoadFile(corePath);
142             }
143             catch (Exception)
144             {
145                 return false;
146             }
147
148             try
149             {
150                 _targetConnector = _connector.CreateInstance("Growl.Connector.GrowlConnector");
151                 Type _t = _connector.GetType("Growl.Connector.NotificationType");
152
153                 _growlNTreply = _t.InvokeMember(null,
154                     BindingFlags.CreateInstance, null, null, new object[] { "REPLY", "Reply" }, CultureInfo.InvariantCulture);
155
156                 _growlNTdm = _t.InvokeMember(null,
157                     BindingFlags.CreateInstance, null, null, new object[] { "DIRECT_MESSAGE", "DirectMessage" }, CultureInfo.InvariantCulture);
158
159                 _growlNTnew = _t.InvokeMember(null,
160                     BindingFlags.CreateInstance, null, null, new object[] { "NOTIFY", "新着通知" }, CultureInfo.InvariantCulture);
161
162                 _growlNTusevent = _t.InvokeMember(null,
163                     BindingFlags.CreateInstance, null, null, new object[] { "USERSTREAM_EVENT", "UserStream Event" }, CultureInfo.InvariantCulture);
164
165                 object encryptType =
166                         _connector.GetType("Growl.Connector.Cryptography+SymmetricAlgorithmType").InvokeMember(
167                             "PlainText", BindingFlags.GetField, null, null, null, CultureInfo.InvariantCulture);
168                 _targetConnector.GetType().InvokeMember("EncryptionAlgorithm", BindingFlags.SetProperty, null, _targetConnector, new object[] { encryptType }, CultureInfo.InvariantCulture);
169
170                 _growlApp = _connector.CreateInstance(
171                     "Growl.Connector.Application", false, BindingFlags.Default, null, new object[] { AppName }, null, null);
172
173
174                 if (File.Exists(Path.Combine(Application.StartupPath, "Icons\\Tween.png")))
175                 {
176                     // Icons\Tween.pngを使用
177                     ConstructorInfo ci = _core.GetType(
178                         "Growl.CoreLibrary.Resource").GetConstructor(
179                         BindingFlags.NonPublic | BindingFlags.Instance,
180                         null, new Type[] { typeof(string) }, null);
181
182                     object data = ci.Invoke(new object[] { Path.Combine(Application.StartupPath, "Icons\\Tween.png") });
183                     PropertyInfo pi = _growlApp.GetType().GetProperty("Icon");
184                     pi.SetValue(_growlApp, data, null);
185
186                 }
187                 else if (File.Exists(Path.Combine(Application.StartupPath, "Icons\\MIcon.ico")))
188                 {
189                     // アイコンセットにMIcon.icoが存在する場合それを使用
190                     ConstructorInfo cibd = _core.GetType(
191                         "Growl.CoreLibrary.BinaryData").GetConstructor(
192                         BindingFlags.Public | BindingFlags.Instance,
193                         null, new Type[] { typeof(byte[]) }, null);
194                     object bdata = cibd.Invoke(
195                         new object[] { IconToByteArray(Path.Combine(Application.StartupPath, "Icons\\MIcon.ico")) });
196
197                     ConstructorInfo ciRes = _core.GetType(
198                         "Growl.CoreLibrary.Resource").GetConstructor(
199                         BindingFlags.NonPublic | BindingFlags.Instance,
200                         null, new Type[] { bdata.GetType() }, null);
201
202                     object data = ciRes.Invoke(new object[] { bdata });
203                     PropertyInfo pi = _growlApp.GetType().GetProperty("Icon");
204                     pi.SetValue(_growlApp, data, null);
205                 }
206                 else
207                 {
208                     //内蔵アイコンリソースを使用
209                     ConstructorInfo cibd = _core.GetType(
210                         "Growl.CoreLibrary.BinaryData").GetConstructor(
211                         BindingFlags.Public | BindingFlags.Instance,
212                         null, new Type[] { typeof(byte[]) }, null);
213                     object bdata = cibd.Invoke(
214                         new object[] { IconToByteArray(Properties.Resources.MIcon) });
215
216                     ConstructorInfo ciRes = _core.GetType(
217                         "Growl.CoreLibrary.Resource").GetConstructor(
218                         BindingFlags.NonPublic | BindingFlags.Instance,
219                         null, new Type[] { bdata.GetType() }, null);
220
221                     object data = ciRes.Invoke(new object[] { bdata });
222                     PropertyInfo pi = _growlApp.GetType().GetProperty("Icon");
223                     pi.SetValue(_growlApp, data, null);
224                 }
225
226                 MethodInfo mi = _targetConnector.GetType().GetMethod("Register", new Type[] { _growlApp.GetType(), _connector.GetType("Growl.Connector.NotificationType[]") });
227
228                 _t = _connector.GetType("Growl.Connector.NotificationType");
229
230                 var arglist = new ArrayList
231                 {
232                     _growlNTreply,
233                     _growlNTdm,
234                     _growlNTnew,
235                     _growlNTusevent,
236                 };
237
238                 mi.Invoke(_targetConnector, new object[] { _growlApp, arglist.ToArray(_t) });
239
240                 // コールバックメソッドの登録
241                 Type tGrowlConnector = _connector.GetType("Growl.Connector.GrowlConnector");
242                 EventInfo evNotificationCallback = tGrowlConnector.GetEvent("NotificationCallback");
243                 Type tDelegate = evNotificationCallback.EventHandlerType;
244                 MethodInfo miHandler = typeof(GrowlHelper).GetMethod("GrowlCallbackHandler", BindingFlags.NonPublic | BindingFlags.Instance);
245                 Delegate d = Delegate.CreateDelegate(tDelegate, this, miHandler);
246                 MethodInfo miAddHandler = evNotificationCallback.GetAddMethod();
247                 object[] addHandlerArgs = { d };
248                 miAddHandler.Invoke(_targetConnector, addHandlerArgs);
249
250                 _initialized = true;
251             }
252             catch (Exception)
253             {
254                 _initialized = false;
255                 return false;
256             }
257
258             return true;
259         }
260
261         public void Notify(NotifyType notificationType, string id, string title, string text, Image icon = null, string url = "")
262         {
263             if (!_initialized) return;
264             string notificationName = "";
265             switch (notificationType)
266             {
267                 case NotifyType.Reply:
268                     notificationName = "REPLY";
269                     break;
270                 case NotifyType.DirectMessage:
271                     notificationName = "DIRECT_MESSAGE";
272                     break;
273                 case NotifyType.Notify:
274                     notificationName = "NOTIFY";
275                     break;
276                 case NotifyType.UserStreamEvent:
277                     notificationName = "USERSTREAM_EVENT";
278                     break;
279             }
280             object n = null;
281             if (icon != null || !string.IsNullOrEmpty(url))
282             {
283                 Type gCore = _core.GetType("Growl.CoreLibrary.Resource");
284                 object res = null;
285                 if (icon != null)
286                 {
287                     res = gCore.InvokeMember("op_Implicit",
288                                              BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod,
289                                              null,
290                                              null,
291                                              new object[] { icon },
292                                              CultureInfo.InvariantCulture);
293                 }
294                 else
295                 {
296                     res = gCore.InvokeMember("op_Implicit",
297                                              BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod,
298                                              null,
299                                              null,
300                                              new object[] { url },
301                                              CultureInfo.InvariantCulture);
302                 }
303                 object priority =
304                         _connector.GetType("Growl.Connector.Priority").InvokeMember(
305                             "Normal", BindingFlags.GetField, null, null, null, CultureInfo.InvariantCulture);
306                 n = _connector.GetType("Growl.Connector.Notification").InvokeMember(
307                         "Notification",
308                         BindingFlags.CreateInstance,
309                         null,
310                         _connector,
311                         new object[] {AppName,
312                                       notificationName,
313                                       id,
314                                       title,
315                                       text,
316                                       res,
317                                       false,
318                                       priority,
319                                       "aaa"},
320                         CultureInfo.InvariantCulture);
321             }
322             else
323             {
324                 n = _connector.GetType("Growl.Connector.Notification").InvokeMember(
325                         "Notification",
326                         BindingFlags.CreateInstance,
327                         null,
328                         _connector,
329                         new object[] {AppName,
330                                       notificationName,
331                                       id,
332                                       title,
333                                       text},
334                         CultureInfo.InvariantCulture);
335             }
336             //_targetConnector.GetType.InvokeMember("Notify", BindingFlags.InvokeMethod, null, _targetConnector, new object[] {n});
337             object cc = _connector.GetType("Growl.Connector.CallbackContext").InvokeMember(
338                 null, BindingFlags.CreateInstance, null, _connector,
339                 new object[] { "some fake information", notificationName },
340                 CultureInfo.InvariantCulture);
341             _targetConnector.GetType().InvokeMember("Notify", BindingFlags.InvokeMethod, null, _targetConnector, new object[] { n, cc }, CultureInfo.InvariantCulture);
342         }
343
344         private void GrowlCallbackHandler(object response, object callbackData, object state)
345         {
346             try
347             {
348                 // 定数取得
349                 object vCLICK =
350                 _core.GetType("Growl.CoreLibrary.CallbackResult").GetField(
351                             "CLICK",
352                            BindingFlags.Public | BindingFlags.Static).GetRawConstantValue();
353                 // 実際の値
354                 object vResult = callbackData.GetType().GetProperty(
355                             "Result",
356                             BindingFlags.Public | BindingFlags.Instance).GetGetMethod().Invoke(callbackData, null);
357                 vResult = (int)vResult;
358                 string notifyId = (string)callbackData.GetType().GetProperty("NotificationID").GetGetMethod().Invoke(callbackData, null);
359                 string notifyName = (string)callbackData.GetType().GetProperty("Type").GetGetMethod().Invoke(callbackData, null);
360                 if (vCLICK.Equals(vResult))
361                 {
362                     NotifyType nt;
363                     switch (notifyName)
364                     {
365                         case "REPLY":
366                             nt = NotifyType.Reply;
367                             break;
368                         case "DIRECT_MESSAGE":
369                             nt = NotifyType.DirectMessage;
370                             break;
371                         case "NOTIFY":
372                             nt = NotifyType.Notify;
373                             break;
374                         case "USERSTREAM_EVENT":
375                             nt = NotifyType.UserStreamEvent;
376                             break;
377                         default:
378                             return;
379                     }
380
381                     NotifyClicked?.Invoke(this, new NotifyCallbackEventArgs(nt, notifyId));
382                 }
383             }
384             catch (Exception)
385             {
386                 return;
387             }
388         }
389     }
390 }