OSDN Git Service

C# 8.0 のnull許容参照型を有効化
[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 #nullable enable
28
29 using System;
30 using System.Collections.Generic;
31 using System.Linq;
32 using System.Text;
33 using System.Reflection;
34 using System.IO;
35 using System.Drawing;
36 using System.Drawing.Imaging;
37 using System.Windows.Forms;
38 using System.ComponentModel;
39 using System.Collections;
40 using System.Globalization;
41
42 namespace OpenTween
43 {
44     public class GrowlHelper
45     {
46         private Assembly? _connector = null;
47         private Assembly? _core = null;
48
49         private object? _growlNTreply;
50         private object? _growlNTdm;
51         private object? _growlNTnew;
52         private object? _growlNTusevent;
53         private object? _growlApp;
54
55         private object? _targetConnector;
56         bool _initialized = false;
57
58         public class NotifyCallbackEventArgs : EventArgs
59         {
60             public long StatusId { get; set; }
61             public NotifyType NotifyType { get; set; }
62             public NotifyCallbackEventArgs(NotifyType notifyType, string statusId)
63             {
64                 if (statusId.Length > 1)
65                 {
66                     this.StatusId = long.Parse(statusId);
67                     this.NotifyType = notifyType;
68                 }
69             }
70         }
71
72         public event EventHandler<NotifyCallbackEventArgs> NotifyClicked;
73
74         public string AppName { get; }
75
76         public enum NotifyType
77         {
78             Reply = 0,
79             DirectMessage = 1,
80             Notify = 2,
81             UserStreamEvent = 3,
82         }
83
84         public GrowlHelper(string appName)
85             => this.AppName = appName;
86
87         public bool IsAvailable
88         {
89             get
90             {
91                 if (_connector == null || _core == null || !_initialized)
92                     return false;
93                 else
94                     return true;
95             }
96         }
97
98         private byte[] IconToByteArray(string filename)
99         {
100             using var ic = new Icon(filename);
101             return IconToByteArray(ic);
102         }
103
104         private byte[] IconToByteArray(Icon icondata)
105         {
106             using var ms = new MemoryStream();
107             using var ic = new Icon(icondata, 48, 48);
108             ic.ToBitmap().Save(ms, ImageFormat.Png);
109             return ms.ToArray();
110         }
111
112         public static bool IsDllExists
113         {
114             get
115             {
116                 var dir = Application.StartupPath;
117                 var connectorPath = Path.Combine(dir, "Growl.Connector.dll");
118                 var corePath = Path.Combine(dir, "Growl.CoreLibrary.dll");
119                 if (File.Exists(connectorPath) && File.Exists(corePath))
120                     return true;
121                 else
122                     return false;
123             }
124         }
125
126         public bool RegisterGrowl()
127         {
128             _initialized = false;
129             var dir = Application.StartupPath;
130             var connectorPath = Path.Combine(dir, "Growl.Connector.dll");
131             var corePath = Path.Combine(dir, "Growl.CoreLibrary.dll");
132
133             try
134             {
135                 if (!IsDllExists) return false;
136                 _connector = Assembly.LoadFile(connectorPath);
137                 _core = Assembly.LoadFile(corePath);
138             }
139             catch (Exception)
140             {
141                 return false;
142             }
143
144             try
145             {
146                 _targetConnector = _connector.CreateInstance("Growl.Connector.GrowlConnector");
147                 var _t = _connector.GetType("Growl.Connector.NotificationType");
148
149                 _growlNTreply = _t.InvokeMember(null,
150                     BindingFlags.CreateInstance, null, null, new object[] { "REPLY", "Reply" }, CultureInfo.InvariantCulture);
151
152                 _growlNTdm = _t.InvokeMember(null,
153                     BindingFlags.CreateInstance, null, null, new object[] { "DIRECT_MESSAGE", "DirectMessage" }, CultureInfo.InvariantCulture);
154
155                 _growlNTnew = _t.InvokeMember(null,
156                     BindingFlags.CreateInstance, null, null, new object[] { "NOTIFY", "新着通知" }, CultureInfo.InvariantCulture);
157
158                 _growlNTusevent = _t.InvokeMember(null,
159                     BindingFlags.CreateInstance, null, null, new object[] { "USERSTREAM_EVENT", "UserStream Event" }, CultureInfo.InvariantCulture);
160
161                 var encryptType =
162                         _connector.GetType("Growl.Connector.Cryptography+SymmetricAlgorithmType").InvokeMember(
163                             "PlainText", BindingFlags.GetField, null, null, null, CultureInfo.InvariantCulture);
164                 _targetConnector.GetType().InvokeMember("EncryptionAlgorithm", BindingFlags.SetProperty, null, _targetConnector, new object[] { encryptType }, CultureInfo.InvariantCulture);
165
166                 _growlApp = _connector.CreateInstance(
167                     "Growl.Connector.Application", false, BindingFlags.Default, null, new object[] { AppName }, null, null);
168
169
170                 if (File.Exists(Path.Combine(Application.StartupPath, "Icons\\Tween.png")))
171                 {
172                     // Icons\Tween.pngを使用
173                     var ci = _core.GetType(
174                         "Growl.CoreLibrary.Resource").GetConstructor(
175                         BindingFlags.NonPublic | BindingFlags.Instance,
176                         null, new Type[] { typeof(string) }, null);
177
178                     var data = ci.Invoke(new object[] { Path.Combine(Application.StartupPath, "Icons\\Tween.png") });
179                     var pi = _growlApp.GetType().GetProperty("Icon");
180                     pi.SetValue(_growlApp, data, null);
181
182                 }
183                 else if (File.Exists(Path.Combine(Application.StartupPath, "Icons\\MIcon.ico")))
184                 {
185                     // アイコンセットにMIcon.icoが存在する場合それを使用
186                     var cibd = _core.GetType(
187                         "Growl.CoreLibrary.BinaryData").GetConstructor(
188                         BindingFlags.Public | BindingFlags.Instance,
189                         null, new Type[] { typeof(byte[]) }, null);
190                     var bdata = cibd.Invoke(
191                         new object[] { IconToByteArray(Path.Combine(Application.StartupPath, "Icons\\MIcon.ico")) });
192
193                     var ciRes = _core.GetType(
194                         "Growl.CoreLibrary.Resource").GetConstructor(
195                         BindingFlags.NonPublic | BindingFlags.Instance,
196                         null, new Type[] { bdata.GetType() }, null);
197
198                     var data = ciRes.Invoke(new object[] { bdata });
199                     var pi = _growlApp.GetType().GetProperty("Icon");
200                     pi.SetValue(_growlApp, data, null);
201                 }
202                 else
203                 {
204                     //内蔵アイコンリソースを使用
205                     var cibd = _core.GetType(
206                         "Growl.CoreLibrary.BinaryData").GetConstructor(
207                         BindingFlags.Public | BindingFlags.Instance,
208                         null, new Type[] { typeof(byte[]) }, null);
209                     var bdata = cibd.Invoke(
210                         new object[] { IconToByteArray(Properties.Resources.MIcon) });
211
212                     var ciRes = _core.GetType(
213                         "Growl.CoreLibrary.Resource").GetConstructor(
214                         BindingFlags.NonPublic | BindingFlags.Instance,
215                         null, new Type[] { bdata.GetType() }, null);
216
217                     var data = ciRes.Invoke(new object[] { bdata });
218                     var pi = _growlApp.GetType().GetProperty("Icon");
219                     pi.SetValue(_growlApp, data, null);
220                 }
221
222                 var mi = _targetConnector.GetType().GetMethod("Register", new Type[] { _growlApp.GetType(), _connector.GetType("Growl.Connector.NotificationType[]") });
223
224                 _t = _connector.GetType("Growl.Connector.NotificationType");
225
226                 var arglist = new ArrayList
227                 {
228                     _growlNTreply,
229                     _growlNTdm,
230                     _growlNTnew,
231                     _growlNTusevent,
232                 };
233
234                 mi.Invoke(_targetConnector, new object[] { _growlApp, arglist.ToArray(_t) });
235
236                 // コールバックメソッドの登録
237                 var tGrowlConnector = _connector.GetType("Growl.Connector.GrowlConnector");
238                 var evNotificationCallback = tGrowlConnector.GetEvent("NotificationCallback");
239                 var tDelegate = evNotificationCallback.EventHandlerType;
240                 var miHandler = typeof(GrowlHelper).GetMethod("GrowlCallbackHandler", BindingFlags.NonPublic | BindingFlags.Instance);
241                 var d = Delegate.CreateDelegate(tDelegate, this, miHandler);
242                 var miAddHandler = evNotificationCallback.GetAddMethod();
243                 object[] addHandlerArgs = { d };
244                 miAddHandler.Invoke(_targetConnector, addHandlerArgs);
245
246                 _initialized = true;
247             }
248             catch (Exception)
249             {
250                 _initialized = false;
251                 return false;
252             }
253
254             return true;
255         }
256
257         public void Notify(NotifyType notificationType, string id, string title, string text, Image? icon = null, string url = "")
258         {
259             if (!_initialized) return;
260             var notificationName = "";
261             switch (notificationType)
262             {
263                 case NotifyType.Reply:
264                     notificationName = "REPLY";
265                     break;
266                 case NotifyType.DirectMessage:
267                     notificationName = "DIRECT_MESSAGE";
268                     break;
269                 case NotifyType.Notify:
270                     notificationName = "NOTIFY";
271                     break;
272                 case NotifyType.UserStreamEvent:
273                     notificationName = "USERSTREAM_EVENT";
274                     break;
275             }
276             object? n;
277             if (icon != null || !string.IsNullOrEmpty(url))
278             {
279                 var gCore = _core!.GetType("Growl.CoreLibrary.Resource");
280                 object? res;
281                 if (icon != null)
282                 {
283                     res = gCore.InvokeMember("op_Implicit",
284                                              BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod,
285                                              null,
286                                              null,
287                                              new object[] { icon },
288                                              CultureInfo.InvariantCulture);
289                 }
290                 else
291                 {
292                     res = gCore.InvokeMember("op_Implicit",
293                                              BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod,
294                                              null,
295                                              null,
296                                              new object[] { url },
297                                              CultureInfo.InvariantCulture);
298                 }
299                 var priority =
300                         _connector!.GetType("Growl.Connector.Priority").InvokeMember(
301                             "Normal", BindingFlags.GetField, null, null, null, CultureInfo.InvariantCulture);
302                 n = _connector!.GetType("Growl.Connector.Notification").InvokeMember(
303                         "Notification",
304                         BindingFlags.CreateInstance,
305                         null,
306                         _connector,
307                         new object[] {AppName,
308                                       notificationName,
309                                       id,
310                                       title,
311                                       text,
312                                       res,
313                                       false,
314                                       priority,
315                                       "aaa"},
316                         CultureInfo.InvariantCulture);
317             }
318             else
319             {
320                 n = _connector!.GetType("Growl.Connector.Notification").InvokeMember(
321                         "Notification",
322                         BindingFlags.CreateInstance,
323                         null,
324                         _connector,
325                         new object[] {AppName,
326                                       notificationName,
327                                       id,
328                                       title,
329                                       text},
330                         CultureInfo.InvariantCulture);
331             }
332             //_targetConnector.GetType.InvokeMember("Notify", BindingFlags.InvokeMethod, null, _targetConnector, new object[] {n});
333             var cc = _connector.GetType("Growl.Connector.CallbackContext").InvokeMember(
334                 null, BindingFlags.CreateInstance, null, _connector,
335                 new object[] { "some fake information", notificationName },
336                 CultureInfo.InvariantCulture);
337             _targetConnector!.GetType().InvokeMember("Notify", BindingFlags.InvokeMethod, null, _targetConnector, new object[] { n, cc }, CultureInfo.InvariantCulture);
338         }
339
340         private void GrowlCallbackHandler(object response, object callbackData, object state)
341         {
342             try
343             {
344                 // 定数取得
345                 var vCLICK =
346                 _core!.GetType("Growl.CoreLibrary.CallbackResult").GetField(
347                             "CLICK",
348                            BindingFlags.Public | BindingFlags.Static).GetRawConstantValue();
349                 // 実際の値
350                 var vResult = callbackData.GetType().GetProperty(
351                             "Result",
352                             BindingFlags.Public | BindingFlags.Instance).GetGetMethod().Invoke(callbackData, null);
353                 vResult = (int)vResult;
354                 var notifyId = (string)callbackData.GetType().GetProperty("NotificationID").GetGetMethod().Invoke(callbackData, null);
355                 var notifyName = (string)callbackData.GetType().GetProperty("Type").GetGetMethod().Invoke(callbackData, null);
356                 if (vCLICK.Equals(vResult))
357                 {
358                     NotifyType nt;
359                     switch (notifyName)
360                     {
361                         case "REPLY":
362                             nt = NotifyType.Reply;
363                             break;
364                         case "DIRECT_MESSAGE":
365                             nt = NotifyType.DirectMessage;
366                             break;
367                         case "NOTIFY":
368                             nt = NotifyType.Notify;
369                             break;
370                         case "USERSTREAM_EVENT":
371                             nt = NotifyType.UserStreamEvent;
372                             break;
373                         default:
374                             return;
375                     }
376
377                     NotifyClicked?.Invoke(this, new NotifyCallbackEventArgs(nt, notifyId));
378                 }
379             }
380             catch (Exception)
381             {
382                 return;
383             }
384         }
385     }
386 }