This repository has been archived by the owner. It is now read-only.
Permalink
157 lines (140 sloc)
4.28 KB
# Description: | |
# Forgetful? Add reminders! | |
# | |
# Dependencies: | |
# "chrono-node": "^0.1.10" | |
# "moment": "^2.8.1" | |
# "lodash": "^2.4.1" | |
# | |
# Configuration: | |
# None | |
# | |
# Commands: | |
# hubot remind me (on <date>|in <time>) to <action> - Set a reminder in <time> to do an <action> <time> is in the format 1 day, 2 hours, 5 minutes etc. Time segments are optional, as are commas | |
# hubot delete reminder <action> - Delete reminder matching <action> (exact match required) | |
# hubot show reminders | |
# | |
# Author: | |
# whitman | |
# jtwalters | |
_ = require('lodash') | |
moment = require('moment') | |
chrono = require('chrono-node') | |
timeoutIds = {} | |
class Reminders | |
constructor: (@robot) -> | |
@cache = [] | |
@currentTimeout = null | |
# Load reminders from brain, on loaded event | |
@robot.brain.on('loaded', => | |
if @robot.brain.data.reminders | |
@cache = _.map(@robot.brain.data.reminders, (item) -> | |
new Reminder(item) | |
) | |
console.log("loaded #{@cache.length} reminders") | |
@queue() | |
) | |
# Persist reminders to the brain, on save event | |
@robot.brain.on('save', => | |
@robot.brain.data.reminders = @cache | |
) | |
add: (reminder) -> | |
@cache.push(reminder) | |
@cache.sort((a, b) -> a.due - b.due) | |
@queue() | |
removeFirst: -> | |
reminder = @cache.shift() | |
return reminder | |
queue: -> | |
return if @cache.length is 0 | |
now = (new Date).getTime() | |
trigger = => | |
reminder = @removeFirst() | |
@robot.reply(reminder.msg_envelope, 'you asked me to remind you to ' + reminder.action) | |
@queue() | |
# setTimeout uses a 32-bit INT | |
extendTimeout = (timeout, callback) -> | |
if timeout > 0x7FFFFFFF | |
setTimeout(-> | |
extendTimeout(timeout - 0x7FFFFFFF, callback) | |
, 0x7FFFFFFF) | |
else | |
setTimeout(callback, timeout) | |
reminder = @cache[0] | |
duration = reminder.due - now | |
duration = 0 if duration < 0 | |
clearTimeout(timeoutIds[reminder]) | |
timeoutIds[reminder] = extendTimeout(reminder.due - now, trigger) | |
console.log("reminder set with duration of #{duration}") | |
class Reminder | |
constructor: (data) -> | |
{@msg_envelope, @action, @time, @due} = data | |
if @time and !@due | |
@time.replace(/^\s+|\s+$/g, '') | |
periods = | |
weeks: | |
value: 0 | |
regex: "weeks?" | |
days: | |
value: 0 | |
regex: "days?" | |
hours: | |
value: 0 | |
regex: "hours?|hrs?" | |
minutes: | |
value: 0 | |
regex: "minutes?|mins?" | |
seconds: | |
value: 0 | |
regex: "seconds?|secs?" | |
for period of periods | |
pattern = new RegExp('^.*?([\\d\\.]+)\\s*(?:(?:' + periods[period].regex + ')).*$', 'i') | |
matches = pattern.exec(@time) | |
periods[period].value = parseInt(matches[1]) if matches | |
@due = (new Date).getTime() | |
@due += ( | |
(periods.weeks.value * 604800) + | |
(periods.days.value * 86400) + | |
(periods.hours.value * 3600) + | |
(periods.minutes.value * 60) + | |
periods.seconds.value | |
) * 1000 | |
formatDue: -> | |
dueDate = new Date(@due) | |
duration = dueDate - new Date | |
if duration > 0 and duration < 86400000 | |
'in ' + moment.duration(duration).humanize() | |
else | |
'on ' + moment(dueDate).format("dddd, MMMM Do YYYY, h:mm:ss a") | |
module.exports = (robot) -> | |
reminders = new Reminders(robot) | |
robot.respond(/show reminders$/i, (msg) -> | |
text = '' | |
for reminder in reminders.cache | |
text += "#{reminder.action} #{reminder.formatDue()}\n" | |
msg.send(text) | |
) | |
robot.respond(/delete reminder (.+)$/i, (msg) -> | |
query = msg.match[1] | |
prevLength = reminders.cache.length | |
reminders.cache = _.reject(reminders.cache, {action: query}) | |
reminders.queue() | |
msg.send("Deleted reminder #{query}") if reminders.cache.length isnt prevLength | |
) | |
robot.respond(/remind me (in|on) (.+?) to (.*)/i, (msg) -> | |
type = msg.match[1] | |
time = msg.match[2] | |
action = msg.match[3] | |
options = | |
msg_envelope: msg.envelope, | |
action: action | |
time: time | |
if type is 'on' | |
# parse the date (convert to timestamp) | |
due = chrono.parseDate(time).getTime() | |
if due.toString() isnt 'Invalid Date' | |
options.due = due | |
reminder = new Reminder(options) | |
reminders.add(reminder) | |
msg.send "I'll remind you to #{action} #{reminder.formatDue()}" | |
) |