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()}" | |
| ) |