OSDN Git Service

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