OSDN Git Service

バージョン12.2の準備
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / Notification / Notifier.cs
1 // Copyright (C) 2020 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
2 //\r
3 // Licensed under the Apache License, Version 2.0 (the "License");\r
4 // you may not use this file except in compliance with the License.\r
5 // You may obtain a copy of the License at\r
6 //\r
7 //    http://www.apache.org/licenses/LICENSE-2.0\r
8 //\r
9 // Unless required by applicable law or agreed to in writing, software\r
10 // distributed under the License is distributed on an "AS IS" BASIS,\r
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
12 // See the License for the specific language governing permissions and\r
13 // limitations under the License.\r
14 \r
15 using System;\r
16 using System.Collections.Generic;\r
17 using System.Linq;\r
18 using System.Threading.Tasks;\r
19 using KancolleSniffer.Model;\r
20 using KancolleSniffer.Net;\r
21 using KancolleSniffer.Util;\r
22 using KancolleSniffer.View;\r
23 \r
24 namespace KancolleSniffer.Notification\r
25 {\r
26     public class Notifier : IUpdateContext, Sniffer.IRepeatingTimerController\r
27     {\r
28         private readonly Scheduler _scheduler;\r
29         private readonly Method _method;\r
30 \r
31         private class Method\r
32         {\r
33             public Action FlashWindow;\r
34             public Action<string, string> ShowToaster;\r
35             public Action<string, int> PlaySound;\r
36         }\r
37 \r
38         public UpdateContext Context { get; set; }\r
39 \r
40         private TimeStep Step => Context.GetStep();\r
41 \r
42         private NotificationConfig Notifications => Context.Config.Notifications;\r
43 \r
44         public Notifier(Action flashWindow, Action<string, string> showToaster, Action<string, int> playSound)\r
45         {\r
46             _method = new Method\r
47             {\r
48                 FlashWindow = flashWindow,\r
49                 ShowToaster = showToaster,\r
50                 PlaySound = playSound,\r
51             };\r
52             _scheduler = new Scheduler(Alarm);\r
53         }\r
54 \r
55         public void Stop(string key)\r
56         {\r
57             _scheduler.StopRepeat(key,\r
58                 (key == "入渠終了" || key == "遠征終了") &&\r
59                 (Context.Config.Notifications[key].Flags & NotificationType.Cont) != 0);\r
60         }\r
61 \r
62         public void Stop(string key, int fleet) => _scheduler.StopRepeat(key, fleet);\r
63 \r
64         public void Suspend(string exception = null) => _scheduler.SuspendRepeat(exception);\r
65 \r
66         public void Resume() => _scheduler.ResumeRepeat();\r
67 \r
68         public void StopAllRepeat() => _scheduler.StopAllRepeat();\r
69 \r
70         public void StopRepeatingTimer(IEnumerable<string> names)\r
71         {\r
72             foreach (var name in names)\r
73                 _scheduler.StopRepeat(name);\r
74         }\r
75 \r
76         public void NotifyShipItemCount()\r
77         {\r
78             var ship = Context.Sniffer.ShipCounter;\r
79             if (ship.Alarm)\r
80             {\r
81                 var message = $"残り{ship.Rest:D}隻";\r
82                 _scheduler.Enqueue("艦娘数超過", message);\r
83                 ship.Alarm = false;\r
84             }\r
85             var item = Context.Sniffer.ItemCounter;\r
86             if (item.Alarm)\r
87             {\r
88                 var message = $"残り{item.Rest:D}個";\r
89                 _scheduler.Enqueue("装備数超過", message);\r
90                 item.Alarm = false;\r
91             }\r
92             _scheduler.Flush();\r
93         }\r
94 \r
95         public void NotifyDamagedShip()\r
96         {\r
97             _scheduler.StopRepeat("大破警告");\r
98             if (!Context.Sniffer.BadlyDamagedShips.Any())\r
99                 return;\r
100             SetNotification("大破警告", string.Join(" ", Context.Sniffer.BadlyDamagedShips));\r
101             _scheduler.Flush();\r
102         }\r
103 \r
104         public void NotifyTimers()\r
105         {\r
106             for (var i = 0; i < Context.Sniffer.Missions.Length; i++)\r
107             {\r
108                 var entry = Context.Sniffer.Missions[i];\r
109                 if (entry.Name == "前衛支援任務" || entry.Name == "艦隊決戦支援任務")\r
110                     continue;\r
111                 CheckAlarm("遠征終了", entry.Timer, i + 1, entry.Name);\r
112             }\r
113             for (var i = 0; i < Context.Sniffer.NDock.Length; i++)\r
114             {\r
115                 var entry = Context.Sniffer.NDock[i];\r
116                 CheckAlarm("入渠終了", entry.Timer, i, entry.Name);\r
117             }\r
118             for (var i = 0; i < Context.Sniffer.KDock.Length; i++)\r
119             {\r
120                 var timer = Context.Sniffer.KDock[i];\r
121                 CheckAlarm("建造完了", timer, i, "");\r
122             }\r
123             NotifyCondTimers();\r
124             NotifyAkashiTimer();\r
125             _scheduler.Flush();\r
126         }\r
127 \r
128         private void CheckAlarm(string key, AlarmTimer timer, int fleet, string subject)\r
129         {\r
130             if (timer.CheckAlarm(Step))\r
131             {\r
132                 SetNotification(key, fleet, subject);\r
133                 return;\r
134             }\r
135             var pre = TimeSpan.FromSeconds(Notifications[key].PreliminaryPeriod);\r
136             if (pre == TimeSpan.Zero)\r
137                 return;\r
138             if (timer.CheckAlarm(Step + pre))\r
139                 SetPreNotification(key, fleet, subject);\r
140         }\r
141 \r
142         private void NotifyCondTimers()\r
143         {\r
144             var notice = Context.Sniffer.GetConditionNotice(Step);\r
145             var pre = TimeSpan.FromSeconds(Notifications["疲労回復"].PreliminaryPeriod);\r
146             var preNotice = pre == TimeSpan.Zero\r
147                 ? new int[ShipInfo.FleetCount]\r
148                 : Context.Sniffer.GetConditionNotice(Step + pre);\r
149             var conditions = Context.Config.NotifyConditions;\r
150             for (var i = 0; i < ShipInfo.FleetCount; i++)\r
151             {\r
152                 if (conditions.Contains(notice[i]))\r
153                 {\r
154                     SetNotification("疲労回復" + notice[i], i, "cond" + notice[i]);\r
155                 }\r
156                 else if (conditions.Contains(preNotice[i]))\r
157                 {\r
158                     SetPreNotification("疲労回復" + preNotice[i], i, "cond" + notice[i]);\r
159                 }\r
160             }\r
161         }\r
162 \r
163         private void NotifyAkashiTimer()\r
164         {\r
165             var akashi = Context.Sniffer.AkashiTimer;\r
166             var msgs = akashi.GetNotice(Step);\r
167             if (msgs.Length == 0)\r
168             {\r
169                 _scheduler.StopRepeat("泊地修理");\r
170                 return;\r
171             }\r
172             if (!akashi.CheckRepairing(Context.GetStep().Now) && !(akashi.CheckPresetRepairing() && Context.Config.UsePresetAkashi))\r
173             {\r
174                 _scheduler.StopRepeat("泊地修理");\r
175                 return;\r
176             }\r
177             var skipPreliminary = false;\r
178             if (msgs[0].Proceeded == "20分経過しました。")\r
179             {\r
180                 SetNotification("泊地修理20分経過", msgs[0].Proceeded);\r
181                 msgs[0].Proceeded = "";\r
182                 skipPreliminary = true;\r
183                 // 修理完了がいるかもしれないので続ける\r
184             }\r
185             for (var i = 0; i < ShipInfo.FleetCount; i++)\r
186             {\r
187                 if (msgs[i].Proceeded != "")\r
188                     SetNotification("泊地修理進行", i, msgs[i].Proceeded);\r
189                 if (msgs[i].Completed != "")\r
190                     SetNotification("泊地修理完了", i, msgs[i].Completed);\r
191             }\r
192             var pre = TimeSpan.FromSeconds(Notifications["泊地修理20分経過"].PreliminaryPeriod);\r
193             if (skipPreliminary || pre == TimeSpan.Zero)\r
194                 return;\r
195             if ((msgs = akashi.GetNotice(Step + pre))[0].Proceeded == "20分経過しました。")\r
196                 SetPreNotification("泊地修理20分経過", 0, msgs[0].Proceeded);\r
197         }\r
198 \r
199         public void NotifyQuestComplete()\r
200         {\r
201             Context.Sniffer.GetQuestNotifications(out var notify, out var stop);\r
202             foreach (var questName in notify)\r
203                 SetNotification("任務達成", 0, questName);\r
204             foreach (var questName in stop)\r
205                 _scheduler.StopRepeat("任務達成", questName);\r
206             _scheduler.Flush();\r
207         }\r
208 \r
209         private void SetNotification(string key, string subject)\r
210         {\r
211             SetNotification(key, 0, subject);\r
212         }\r
213 \r
214         private void SetNotification(string key, int fleet, string subject)\r
215         {\r
216             var spec = Spec(key);\r
217             _scheduler.Enqueue(key, fleet, subject,\r
218                 (spec.Flags & Context.Config.NotificationFlags & NotificationType.Repeat) == 0\r
219                     ? 0\r
220                     : spec.RepeatInterval);\r
221         }\r
222 \r
223         private void SetPreNotification(string key, int fleet, string subject)\r
224         {\r
225             if ((Spec(key).Flags & NotificationType.Preliminary) != 0)\r
226                 _scheduler.Enqueue(key, fleet, subject, 0, true);\r
227         }\r
228 \r
229         private NotificationSpec Spec(string key)\r
230         {\r
231             return Notifications[_scheduler.KeyToName(key)];\r
232         }\r
233 \r
234         private void Alarm(string balloonTitle, string balloonMessage, string name)\r
235         {\r
236             if (Check(name, NotificationType.FlashWindow))\r
237                 _method.FlashWindow();\r
238             if (Check(name, NotificationType.ShowBaloonTip))\r
239                 _method.ShowToaster(balloonTitle, balloonMessage);\r
240             if (Check(name, NotificationType.PlaySound))\r
241                 _method.PlaySound(Context.Config.Sounds[name], Context.Config.Sounds.Volume);\r
242             if (Context.Config.Pushbullet.On && CheckPush(name))\r
243             {\r
244                 Task.Run(() =>\r
245                 {\r
246                     PushNotification.PushToPushbullet(Context.Config.Pushbullet.Token, balloonTitle,\r
247                         balloonMessage);\r
248                 });\r
249             }\r
250             if (Context.Config.Pushover.On && CheckPush(name))\r
251             {\r
252                 Task.Run(() =>\r
253                 {\r
254                     PushNotification.PushToPushover(Context.Config.Pushover.ApiKey, Context.Config.Pushover.UserKey,\r
255                         balloonTitle, balloonMessage);\r
256                 });\r
257             }\r
258         }\r
259 \r
260         private bool Check(string name, NotificationType type)\r
261         {\r
262             return (Flags(name) & type) != 0;\r
263         }\r
264 \r
265         private NotificationType Flags(string name)\r
266         {\r
267             return Context.Config.NotificationFlags & Notifications[name].Flags;\r
268         }\r
269 \r
270         private bool CheckPush(string name)\r
271         {\r
272             return (Notifications[name].Flags & NotificationType.Push) != 0;\r
273         }\r
274     }\r
275 }