1 // Copyright (C) 2020 Kazuhiro Fujieda <fujieda@users.osdn.me>
\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
7 // http://www.apache.org/licenses/LICENSE-2.0
\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
16 using System.Collections.Generic;
\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
24 namespace KancolleSniffer.Notification
\r
26 public class Notifier : IUpdateContext, Sniffer.IRepeatingTimerController
\r
28 private readonly Scheduler _scheduler;
\r
29 private readonly Method _method;
\r
31 private class Method
\r
33 public Action FlashWindow;
\r
34 public Action<string, string> ShowToaster;
\r
35 public Action<string, int> PlaySound;
\r
38 public UpdateContext Context { get; set; }
\r
40 private TimeStep Step => Context.GetStep();
\r
42 private NotificationConfig Notifications => Context.Config.Notifications;
\r
44 public Notifier(Action flashWindow, Action<string, string> showToaster, Action<string, int> playSound)
\r
46 _method = new Method
\r
48 FlashWindow = flashWindow,
\r
49 ShowToaster = showToaster,
\r
50 PlaySound = playSound,
\r
52 _scheduler = new Scheduler(Alarm);
\r
55 public void Stop(string key)
\r
57 _scheduler.StopRepeat(key,
\r
58 (key == "入渠終了" || key == "遠征終了") &&
\r
59 (Context.Config.Notifications[key].Flags & NotificationType.Cont) != 0);
\r
62 public void Stop(string key, int fleet) => _scheduler.StopRepeat(key, fleet);
\r
64 public void Suspend(string exception = null) => _scheduler.SuspendRepeat(exception);
\r
66 public void Resume() => _scheduler.ResumeRepeat();
\r
68 public void StopAllRepeat() => _scheduler.StopAllRepeat();
\r
70 public void StopRepeatingTimer(IEnumerable<string> names)
\r
72 foreach (var name in names)
\r
73 _scheduler.StopRepeat(name);
\r
76 public void NotifyShipItemCount()
\r
78 var ship = Context.Sniffer.ShipCounter;
\r
81 var message = $"残り{ship.Rest:D}隻";
\r
82 _scheduler.Enqueue("艦娘数超過", message);
\r
85 var item = Context.Sniffer.ItemCounter;
\r
88 var message = $"残り{item.Rest:D}個";
\r
89 _scheduler.Enqueue("装備数超過", message);
\r
95 public void NotifyDamagedShip()
\r
97 _scheduler.StopRepeat("大破警告");
\r
98 if (!Context.Sniffer.BadlyDamagedShips.Any())
\r
100 SetNotification("大破警告", string.Join(" ", Context.Sniffer.BadlyDamagedShips));
\r
101 _scheduler.Flush();
\r
104 public void NotifyTimers()
\r
106 for (var i = 0; i < Context.Sniffer.Missions.Length; i++)
\r
108 var entry = Context.Sniffer.Missions[i];
\r
109 if (entry.Name == "前衛支援任務" || entry.Name == "艦隊決戦支援任務")
\r
111 CheckAlarm("遠征終了", entry.Timer, i + 1, entry.Name);
\r
113 for (var i = 0; i < Context.Sniffer.NDock.Length; i++)
\r
115 var entry = Context.Sniffer.NDock[i];
\r
116 CheckAlarm("入渠終了", entry.Timer, i, entry.Name);
\r
118 for (var i = 0; i < Context.Sniffer.KDock.Length; i++)
\r
120 var timer = Context.Sniffer.KDock[i];
\r
121 CheckAlarm("建造完了", timer, i, "");
\r
123 NotifyCondTimers();
\r
124 NotifyAkashiTimer();
\r
125 _scheduler.Flush();
\r
128 private void CheckAlarm(string key, AlarmTimer timer, int fleet, string subject)
\r
130 if (timer.CheckAlarm(Step))
\r
132 SetNotification(key, fleet, subject);
\r
135 var pre = TimeSpan.FromSeconds(Notifications[key].PreliminaryPeriod);
\r
136 if (pre == TimeSpan.Zero)
\r
138 if (timer.CheckAlarm(Step + pre))
\r
139 SetPreNotification(key, fleet, subject);
\r
142 private void NotifyCondTimers()
\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
152 if (conditions.Contains(notice[i]))
\r
154 SetNotification("疲労回復" + notice[i], i, "cond" + notice[i]);
\r
156 else if (conditions.Contains(preNotice[i]))
\r
158 SetPreNotification("疲労回復" + preNotice[i], i, "cond" + notice[i]);
\r
163 private void NotifyAkashiTimer()
\r
165 var akashi = Context.Sniffer.AkashiTimer;
\r
166 var msgs = akashi.GetNotice(Step);
\r
167 if (msgs.Length == 0)
\r
169 _scheduler.StopRepeat("泊地修理");
\r
172 if (!akashi.CheckRepairing(Context.GetStep().Now) && !(akashi.CheckPresetRepairing() && Context.Config.UsePresetAkashi))
\r
174 _scheduler.StopRepeat("泊地修理");
\r
177 var skipPreliminary = false;
\r
178 if (msgs[0].Proceeded == "20分経過しました。")
\r
180 SetNotification("泊地修理20分経過", msgs[0].Proceeded);
\r
181 msgs[0].Proceeded = "";
\r
182 skipPreliminary = true;
\r
183 // 修理完了がいるかもしれないので続ける
\r
185 for (var i = 0; i < ShipInfo.FleetCount; i++)
\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
192 var pre = TimeSpan.FromSeconds(Notifications["泊地修理20分経過"].PreliminaryPeriod);
\r
193 if (skipPreliminary || pre == TimeSpan.Zero)
\r
195 if ((msgs = akashi.GetNotice(Step + pre))[0].Proceeded == "20分経過しました。")
\r
196 SetPreNotification("泊地修理20分経過", 0, msgs[0].Proceeded);
\r
199 public void NotifyQuestComplete()
\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
209 private void SetNotification(string key, string subject)
\r
211 SetNotification(key, 0, subject);
\r
214 private void SetNotification(string key, int fleet, string subject)
\r
216 var spec = Spec(key);
\r
217 _scheduler.Enqueue(key, fleet, subject,
\r
218 (spec.Flags & Context.Config.NotificationFlags & NotificationType.Repeat) == 0
\r
220 : spec.RepeatInterval);
\r
223 private void SetPreNotification(string key, int fleet, string subject)
\r
225 if ((Spec(key).Flags & NotificationType.Preliminary) != 0)
\r
226 _scheduler.Enqueue(key, fleet, subject, 0, true);
\r
229 private NotificationSpec Spec(string key)
\r
231 return Notifications[_scheduler.KeyToName(key)];
\r
234 private void Alarm(string balloonTitle, string balloonMessage, string name)
\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
246 PushNotification.PushToPushbullet(Context.Config.Pushbullet.Token, balloonTitle,
\r
250 if (Context.Config.Pushover.On && CheckPush(name))
\r
254 PushNotification.PushToPushover(Context.Config.Pushover.ApiKey, Context.Config.Pushover.UserKey,
\r
255 balloonTitle, balloonMessage);
\r
260 private bool Check(string name, NotificationType type)
\r
262 return (Flags(name) & type) != 0;
\r
265 private NotificationType Flags(string name)
\r
267 return Context.Config.NotificationFlags & Notifications[name].Flags;
\r
270 private bool CheckPush(string name)
\r
272 return (Notifications[name].Flags & NotificationType.Push) != 0;
\r