-
Notifications
You must be signed in to change notification settings - Fork 2
/
prayerTimeSchedule.js
354 lines (286 loc) · 13.7 KB
/
prayerTimeSchedule.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
const schedule = require('node-schedule');
const PrayerTimeFireStore = require('./prayerTimeFireStore');
const PrayerTimeWrapper = require('./prayerTimeWrapper');
const moment = require('moment-timezone')
const request = require('request');
class PrayerTimeScheduler {
constructor(options, db, discordClient) {
this.db = db;
this.dc = discordClient;
this.pt_fs = new PrayerTimeFireStore(db);
this.rotationSchedule = null; // the one that rotate every one day and recalculate all the times for the new day
this.reminderSchedulers = [];
this.ptwOpt = null;
if (options && options.ptwOptions !== undefined) {
this.ptwOpt = options.ptwOptions;
}
this.ptw = new PrayerTimeWrapper(this.ptwOpt);
this.remindTimeRange = {
time: 10,
unit: 'm' // minutes
}
if (options && options.remindTimeRange) {
this.remindTimeRange = options.remindTimeRange;
}
this.timesToScheduls = [
'Fajr',
'Dhuhr',
'Asr',
'Maghrib',
'Isha'
];
if (options && options.timesToScheduls) {
this.timesToScheduls = options.timesToScheduls;
}
}
isTimeTypeScheduled(timeType) {
// console.log('time to schedules');
// console.dir(this.timesToScheduls);
// console.log(' ============= dd =========' + timeType);
// let res = (timeType in this.timesToScheduls);
// console.log('res = ' + res.toString());
// return res;
for (let i = 0; i < this.timesToScheduls.length; i++) {
if (this.timesToScheduls[i] === timeType) {
return true;
}
}
return false;
}
getCitiesAndSchedule(remindersDistances) {
// get cities from database
let self = this;
let ptw = new PrayerTimeWrapper(this.ptwOpt);
console.log("getting cities");
this.pt_fs.getAllCities().then(function (snapshot) {
snapshot.forEach(doc => {
// here we get all cities docs
let data = doc.data();
let city = data.city;
let country = data.country;
self.oneCityScheduling(country, city, ptw, remindersDistances);
console.log(doc.id + ' => ' + data);
console.dir(doc.data());
});
}).catch(function (err) {
console.error(err);
});
}
/**
*
* @param {*} country
* @param {*} city
* @param {*} timings
* @param {*} dateFormated
* @param {*} timeZone
* @param {it's the distance of te reminder time from the prayer time, this var is an array of reminders distance object {distance:, unit:}} remindersDistances
*/
oneCitySchedule(country, city, timings, dateFormated, timeZone, remindersDistances) {
let self = this;
console.log("city !!!!!!! = " + city);
let schedulePlanned = false;
let timingsNames = Object.keys(timings);
for (let i = 0; i < timingsNames.length; i++) {
let timeName = timingsNames[i];
let time = timings[timeName];
console.log('timeName = ' + timeName);
if (self.isTimeTypeScheduled(timeName)) {
// console.log('timeName = ' + timeName);
// console.log('date iso = ' + dateFormated);
// console.log('time = ' + time);
let prayerTime_ = `${dateFormated} ${time}`;
// console.log('jobTime = ' + prayerTime_);
let prayerTime = moment.tz(prayerTime_, timeZone);
if(prayerTime.isDST()) {
console.log("================== time is a DST ===================");
} else {
console.log("================== time is ok no DST ==================");
}
// console.log('jobe time = ' + jobTime.format());
let currentTime = new Date();
if (prayerTime.toDate() - currentTime > 0) {
console.log(`${country}/${city}:\nyea we get a time`);
console.log(prayerTime.format());
if (!remindersDistances) {
remindersDistances = [{
distance: 10,
unit: "m"
}];
}
// setting schedules following the reminders list
for (let i = 0; i < remindersDistances.length; i++) {
console.log('Reminder (distance)');
let rd = remindersDistances[i];
console.dir(rd);
let jobTime = prayerTime.clone();
jobTime.subtract(rd.distance, rd.unit);
let jobTimeDate = jobTime.toDate();
if (jobTimeDate - currentTime > 0) {
console.log("cool reminder nice");
console.log('reminder time = ' + jobTime.format());
// use jobtime date to create the scheduler
let schedulePos = self.reminderSchedulers.length;
self.reminderSchedulers.push(
schedule.scheduleJob(jobTimeDate, function () {
// here go the closures
// let city = city;
// let country = country;
//
console.log('The world is going to be better today.');
console.log('reminder trigered at ' + (new Date()).toString());
console.log('due to schedule at : ' + jobTime.format());
// here go the send to user
console.log("city = " + city);
console.log("country = " + country);
self.alertCityUsers(country, city, timeName, rd).catch(function (err) {
console.error(err);
});
if (timeName === 'Isha' && i === 0) { // only within the first reminder (you can later when you add it's now the prayer time, you can do it at that time We will see)
self.pt_fs.doesCityExist(country, city).then((exists) => {
if(exists) {
console.log("city exist scheduling next day !!!!");
self.oneCityScheduleNextDay(country, city, dateFormated, timeZone, remindersDistances);
} else {
console.log('city no more exists, stoped no more scheduling !!!');
}
}).catch((err) => {
console.error(err);
});
}
// remove the scheduler obj from the list
self.removeSchedule(schedulePos);
})
);
schedulePlanned = true;
console.log('Schedule was set');
} else {
// treat the case where the salat time isn't reached yet but we passed the remind time
// send the messages right away.
// here it make sense to alert people
console.log('----------------- ' + timeName + ' passed reminders time but still not arrived yet! ------------');
console.log("country = " + country);
console.log("city = " + city);
self.alertCityUsers(country, city, timeName, rd).catch(function (err) {
console.error(err);
});
}
}
} else {
// treat here the case when the current time have passed the prayer time
// one of the thing is to send messages rigth away
console.log('----------------- ' + timeName + ' passed ! ------------');
// or just do nothing (that's too logical, because generaly it will be ok, at init, only one crashes happen, a prayer can be missed and it's ok!!! that's it ) (stay you may like to site the passed prayer !!! whatever)
}
}
}
return schedulePlanned;
}
oneCityScheduleNextDay(country, city, dateFormated, timeZone, remindersDistances) {
let self = this;
let ptw = new PrayerTimeWrapper(this.ptwOpt);
let nextDay = moment.tz(dateFormated, timeZone).add(1, 'day');
let nextDayFormated = nextDay.format('YYYY-MM-DD');
nextDay = moment.tz(nextDayFormated, timeZone);
ptw.setOptions({
country,
city,
date_or_timestamp: nextDay.toDate()
});
ptw.getTimingByCity().then(function (o) {
let timings = o.body.data.timings;
self.oneCitySchedule(country, city, timings, nextDayFormated, timeZone, remindersDistances);
}).catch(function (err) {
console.error(err);
});
}
oneCityScheduling(country, city, ptw, remindersDistances) {
let self = this;
ptw.setOptions({
city,
country
});
ptw.getTimingByCity().then(function (o) {
let timeZone = o.body.data.meta.timezone;
console.log("time zone =========================== " + timeZone);
let day = numberToTwoDigit(o.body.data.date.gregorian.day.toString());
let month = numberToTwoDigit(o.body.data.date.gregorian.month.number.toString());
let year = o.body.data.date.gregorian.year.toString();
let dateFormated = `${year}-${month}-${day}`;
let timings = o.body.data.timings;
// [to see] NOTE: we are providing remindersDistances, at discordBot level for testing (same for all users), we will change that to by users (we have to make more enchancement to user registration process (so it include those info, and in case not provided, we fallback to a default.))
let haveSchedule = self.oneCitySchedule(country, city, timings, dateFormated, timeZone, remindersDistances);
// check if any schedule was set! if not do it with the next day
if (!haveSchedule) {
console.log(" ======= current day is passed! Scheduling the next day !!!!!!!!!!!");
self.oneCityScheduleNextDay(country, city, dateFormated, timeZone, remindersDistances);
}
}).catch(function (err) {
console.error('Error: !!!');
console.error(err);
});
}
newCityScheduling (country, city, remindersDistances) {
let ptw = new PrayerTimeWrapper(this.ptwOpt);
this.oneCityScheduling(country, city, ptw);
}
removeSchedule(pos) {
this.reminderSchedulers.splice(pos, 1);
}
// [to see] NOte: when you get reminders from users, you can check if it's in or not (the best is to have a 3, or 4 or predefined, bot config, set of values, and users choose just one of them, then in all cities you schedule such a thing, and send to just the one that have that, the other choice, is to save such an info at registration time, and handle that too at remove time, so it's always effective)
alertCityUsers(country, city, prayerTime, reminderDistance) {
let self = this;
return new Promise(function (resolve, reject) {
console.log('get users from db');
self.pt_fs.getACityUsers(country, city).then(function (snapshot) {
snapshot.forEach(doc => {
let d = doc.data();
console.log('going to fetch user');
let user = self.dc.users.get(d.id);
console.log('user (' + d.id + ',' + d.username + ') ===>');
console.dir(user);
self.alertUser(user, country, city, prayerTime, reminderDistance);
});
console.log('finish');
resolve(snapshot);
}).catch(function (err) {
console.error(err);
reject(err);
});
});
}
alertUser(user, country, city, prayerTime, reminderDistance) {
let msg = `Time for Salat ${prayerTime} is in ${reminderDistance.distance} ${verbaliseTimeUnit(reminderDistance.unit)}`;
console.log(msg);
user.send(msg);
}
}
function verbaliseTimeUnit(unit) {
switch (unit) {
case 'm':
return 'minutes';
case 'mins':
return 'minutes';
case 'min':
return 'minute';
case 'h':
return 'hours';
}
}
function uriFormatParams(params) {
return "?" + Object
.keys(params)
.map(function (key) {
return key + "=" + encodeURIComponent(params[key])
})
.join("&")
}
function getUri(endPoint, params) {
return endPoint + uriFormatParams(params);
}
function numberToTwoDigit(number) {
if (number.length === 1) {
number = "0" + number;
}
return number;
}
module.exports = PrayerTimeScheduler;