-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
210 lines (185 loc) · 5.32 KB
/
index.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
'use strict'
const cote = require('cote')
const cron = require('cron')
const cronstrue = require('cronstrue')
const fs = require('fs')
const path = require('path')
const u = require('elife-utils')
const jughead = require('jughead')
const archieml = require('archieml')
/* understand/
* This is the main entry point where we start.
*
* outcome/
* Start our microservice.
*/
function main() {
loadReminders((err) => {
if(err) u.showErr(err)
startMicroservice()
registerWithCommMgr()
})
}
let REMINDERS = {
cronjobs: [],
reminders: [],
}
/* outcome/
* Clear all existing reminders and load them freshly from the reminder
* file.
*/
function loadReminders(cb) {
fs.readFile(reminderFile(), 'utf8', (err, data) => {
if(err) {
if(err.code == 'ENOENT') {
clearReminders()
cb()
} else cb(err)
}
else {
clearReminders()
let d = archieml.load(data)
for(let i = 0;i < d.reminders.length;i++) {
addReminder(d.reminders[i])
}
cb()
}
})
}
function clearReminders() {
let cjs = REMINDERS.cronjobs;
REMINDERS.reminders = []
REMINDERS.cronjobs = []
for(let i = 0;i < cjs.length;i++) cjs[i].stop()
}
/* outcome/
* Save reminders into an ArchieML file
*/
function saveReminders(cb) {
let pfx = `This is your Reminder File. Every pair of lines below represent
(a) a reminder time in cron format (cron: xxxx)
(b) a message to ping you with at the appropriate time (message: xxxx)
You can add, remove, and edit reminders in this file and then
ask your avatar to load your changes by using the '/reminder_reload'
command.
(If you need help with the 'cron' format go to http://crontab.org)
`
fs.writeFile(reminderFile(), pfx, (err) => {
if(err) cb(err)
else {
let txt = jughead.archieml({ reminders: REMINDERS.reminders })
fs.appendFile(reminderFile(), txt, cb)
}
})
}
/* outcome/
* Get path to our reminder file
*/
function reminderFile() {
return path.join(u.dataLoc(), 'eskill-alarm.txt')
}
/* outcome/
* Given a user entered reminder string:
* * * * * * Remind me
* 30 7 * * * Remind me
* We split it into a reminder ({ when:..,msg:.. })
* And check that the 'when' is a valid cron string by trying to parse
* it into a human-displayable string.
*/
function line2reminder(txt) {
let parts = txt.split(' ')
if(parts.length < 6) return
let reminder = {
when: parts.slice(0,5).join(' '),
msg: parts.slice(5).join(' '),
}
try {
cronstrue.toString(reminder.when) // just to check
return reminder
} catch(e) {
return undefined
}
}
function addReminder(reminder) {
try {
let cronjob = new cron.CronJob(reminder.when, () =>{
sendReply(reminder.msg)
})
cronjob.start()
REMINDERS.cronjobs.push(cronjob)
REMINDERS.reminders.push(reminder)
} catch(e) {}
}
/* microservice key (identity of the microservice) */
let msKey = 'everlife-reminder-alarm'
function startMicroservice() {
/* understand/
* The microservice (partitioned by key to prevent
* conflicting with other services).
*/
const svc = new cote.Responder({
name: 'Everlife Reminder Alarm Skill',
key: msKey,
})
/* outcome/
* Respond to user messages
*/
svc.on('msg', (req, cb) => {
if(req.msg.startsWith("/remind_me ")) {
cb(null, true)
add_reminder_1(req)
} else if(req.msg.startsWith("/reminder_reload")) {
cb(null, true)
loadReminders()
} else {
cb()
}
})
function add_reminder_1(req) {
let reminder = line2reminder(req.msg.substring('/remind_me '.length))
if(!reminder) {
sendReply(`Didn't get that. Use <when to remind> <what to say>`, req)
sendReply(`<when to remind> is in crontab format`, req)
sendReply(`For example: 13 15 * * * Daily meeting (at 1:15 PM)`, req)
} else {
saveReminders((err) => {
if(err) {
sendReply(`Failed to save reminder`, req)
u.showErr(err)
} else {
let display = cronstrue.toString(reminder.when)
sendReply(`Great - will remind you to ${reminder.msg} ${display}`, req)
addReminder(reminder)
}
})
}
}
}
const commMgrClient = new cote.Requester({
name: 'Elife Alarm -> CommMgr',
key: 'everlife-communication-svc',
})
function sendReply(msg, req) {
if(!req) req = {}
req.USELASTCHAN = true
req.type = 'reply'
req.msg = String(msg)
commMgrClient.send(req, (err) => {
if(err) u.showErr(err)
})
}
/* outcome/
* Register ourselves as a message handler with the communication
* manager so we can handle requests for reminder alarms.
*/
function registerWithCommMgr() {
commMgrClient.send({
type: 'register-msg-handler',
mskey: msKey,
mstype: 'msg',
mshelp: [ { cmd: '/remind_me', txt: 'Add reminder (cron format)' } ],
}, (err) => {
if(err) u.showErr(err)
})
}
main()