1 from __future__
import generators
4 from dateutil
.parser
import parse
as raw_time_parser
7 from common
import get_timestamp
, resize
, extract_my_db
, real_where
, safeint
, \
9 from basic_commands
import reply
10 from connection
import hal
11 from globals import reminder_dbs
, subscription_dbs
, scheduler
, private
, \
13 from safety
import safe
14 from ircbot
import IRCDict
, irc_lower
16 os
.environ
['TZ'] = "GMT"
19 def parse_time(time_string
):
20 return time
.mktime(raw_time_parser(time_string
).timetuple())
22 def get_reminder_db(who
, where
):
23 return extract_my_db(reminder_dbs
, who
, where
, lambda: [])
25 def get_subscription_db(who
, where
):
26 return extract_my_db(subscription_dbs
, who
, where
, IRCDict
)
28 def describe_reminder(reminder
):
29 (when
, message
, groups
, repeat
) = reminder
30 description
= '"%s" at %s.' % (message
, get_timestamp(when
))
32 groupstr
= ",".join(groups
)
33 description
+= " Groups: " + groupstr
36 minutes
= str(repeat
/ 60 % 60).zfill(2)
37 description
+= " Repeating every %d:%s." % (hours
, minutes
)
40 reminder_res
= {"add": re
.compile(r
'^"((?:[^"]|\")*)" +at +([^"]*)$',
42 "set": re
.compile(r
'^(\d+) +(\w+) +(.*)$', re
.IGNORECASE
),
43 "repeat h+:mm": re
.compile(r
'^(\d+) +every +(\d+):(\d\d)$',
45 "repeat x units": re
.compile(r
'^(\d+) +every +(\d+) +' +
46 r
'(week|day|hour|minute)s?$',
48 "repeat off": re
.compile(r
'^(\d+) +(off|disable|stop|never|none'
53 time_units
= {"minute": 60,
58 def reminder_index(who
, where
, reminders
, whichstr
):
59 which
= safeint(whichstr
)
61 reply(who
, where
, "Which reminder?")
63 if which
> len(reminders
):
64 reply(who
, where
, "I don't have that many reminders.")
68 def fire_reminder(where
, reminders
, reminder
):
69 (when
, message
, groups
, repeat
) = reminder
71 if when
< (curtime
- 60):
72 delay
= curtime
- when
73 reply('', where
, "Reminder delayed by %d minutes: %s" % (delay
// 60,
77 reply('', where
, message
, all
=True)
78 if where
in hal
.channels
:
79 subscriptions
= get_subscription_db("", where
)
80 please_inform_us
= Set()
82 if group
in subscriptions
:
83 please_inform_us
= please_inform_us
.union(subscriptions
[group
])
84 informable
= whois
.voice_or_better(where
)
85 inform_us
= informable
.intersection(please_inform_us
)
87 poke_list
= [unlower
.get(who
, who
) for who
in inform_us
]
88 poke_str
= ", ".join(poke_list
)
89 reply("", where
, poke_str
+ ", that one's for you!")
92 skip
= delay
// repeat
93 reply('', where
, "(skipping %d delayed repititions)" % skip
, all
=True)
94 real_repeat
= (skip
+ 1) * repeat
97 reminder
[0] += real_repeat
98 schedule_reminder(where
, reminders
, reminder
)
100 reminders
.remove(reminder
)
101 fire_reminder
= safe(fire_reminder
)
103 def schedule_reminder(where
, reminders
, reminder
):
104 scheduler
.enterabs(reminder
[0], 0, fire_reminder
, (where
, reminders
, reminder
))
106 def add_reminder(who
, where
, reminders
, reminder
):
107 if len(reminders
) < 10 or where
== "#casualgameplay":
108 reminders
.append(reminder
)
109 schedule_reminder(real_where(who
, where
), reminders
, reminder
)
110 reply(who
, where
, "Done.")
113 "I'm sorry, %s, I'm afraid I can't do that. You're limited to 10."
116 def cancel_reminder(where
, reminders
, reminder
):
118 scheduler
.cancel((reminder
[0], 0, fire_reminder
, (where
, reminders
, reminder
)))
122 def reschedule(who
, where
, reminders
, reminder
, when
):
123 rwhere
= real_where(who
, where
)
124 cancel_reminder(rwhere
, reminders
, reminder
)
126 schedule_reminder(rwhere
, reminders
, reminder
)
128 def reminder(who
, where
, reminder_args
):
129 "$reminder is so complex it has its own help system: $reminder help"
130 (command
, args
) = resize(reminder_args
.split(" ", 1), 2)
131 reminders
= get_reminder_db(who
, where
)
133 reply(who
, where
, "Available reminder commands: add del list set repeat help")
134 elif command
in ("add", "new", "create", "make"):
135 parsed
= reminder_res
["add"].search(args
)
137 reply(who
, where
, "I don't understand that. Try $reminder help add")
139 (msg
, whenstr
) = parsed
.groups()
141 when
= parse_time(whenstr
)
143 reply(who
, where
, "I don't understand that time.")
145 reminder
= [when
, msg
, [], 0]
146 add_reminder(who
, where
, reminders
, reminder
)
147 elif command
in ("del", "delete", "remove", "rm"):
148 which
= reminder_index(who
, where
, reminders
, args
)
151 reminder
= reminders
[which
]
153 cancel_reminder(real_where(who
, where
), reminders
, reminder
)
154 reply(who
, where
, "Deleted reminder %d: %s" %
155 (which
+1, describe_reminder(reminder
)))
156 elif command
in ("list", "show"):
157 if len(reminders
) > 5:
158 reply(who
, where
, "Listing %d reminders in private " % len(reminders
)
159 + "to avoid channel clutter")
162 reply(who
, where
, "No reminders.")
164 for reminder
in reminders
:
165 reply(who
, where
, "%2d. %s" % (index
, describe_reminder(reminder
)))
167 elif command
in ("change", "alter", "set"):
168 parsed
= reminder_res
["set"].search(args
)
170 reply(who
, where
, "I don't understand that. Try $reminder help set")
172 (whichstr
, property, value
) = parsed
.groups()
173 which
= reminder_index(who
, where
, reminders
, whichstr
)
176 reminder
= reminders
[which
]
177 lproperty
= property.lower()
178 if lproperty
in ("message", "msg"):
180 elif lproperty
== "time":
182 when
= parse_time(value
)
183 reschedule(who
, where
, reminders
, reminder
, when
)
185 reply(who
, where
, "I don't understand that time.")
187 elif lproperty
in ("group", "groups"):
188 groups
= value
.split(",")
189 groups
= [group
.strip() for group
in groups
]
191 reply(who
, where
, "Done.")
192 elif command
== "repeat":
193 parse_hm
= reminder_res
["repeat h+:mm"].search(args
)
194 parse_units
= reminder_res
["repeat x units"].search(args
)
195 parse_off
= reminder_res
["repeat off"].search(args
)
196 parsed
= parse_hm
or parse_units
or parse_off
198 which
= reminder_index(who
, where
, reminders
, parsed
.groups()[0])
201 reminder
= reminders
[which
]
203 reply(who
, where
, "I don't understand that. Try $reminder help repeat")
206 (whichstr
, hourstr
, minutestr
) = parsed
.groups()
207 hours
= safeint(hourstr
)
209 reply(who
, where
, "Bad number of hours.")
211 minutes
= safeint(minutestr
)
212 # Mathematicians, read at your own peril.
214 reply(who
, where
, "Bad number of minutes.")
216 if hours
== minutes
== 0:
217 reply(who
, where
, "Repeating continuously sounds like a bad idea.")
219 reminder
[3] = 60 * (minutes
+ 60 * hours
)
221 "Reminder number %d now repeating every %d hours and %d minutes."
222 % (which
+1, hours
, minutes
))
224 (whichstr
, numstr
, unit
) = parsed
.groups()
226 num
= safeint(numstr
)
228 reply(who
, where
, "Bad number of %ss." % unit
)
230 if unit
not in time_units
:
231 reply(who
, where
, "I don't know that unit.")
233 reminder
[3] = num
* time_units
[unit
]
234 reply(who
, where
, "Reminder number %d now repeating every %d %ss."
235 % (which
+1, num
, unit
))
238 reply(who
, where
, "Repeating disabled on reminder %d." % which
+1)
240 elif command
== "help":
242 reply(who
, where
, "To get help on a specific command, type $reminder " \
243 + "help <command> (e.g. $reminder help add). " \
244 + "Available reminder commands: add del list set "\
246 elif args
in ("add", "new", "create", "make"):
247 reply(who
, where
, '$reminder add "<msg>" at <time>: Adds a new ' \
248 + "reminder. When <time> comes, I will say <msg>. A" \
249 + " variety of time formats are supported. Use $time" \
250 + " to get my current time for comparison.")
251 elif args
in ("del", "delete", "remove", "rm"):
252 reply(who
, where
, "$reminder del <reminder>: Delete reminder number " \
254 elif args
in ("change", "alter", "set"):
255 reply(who
, where
, "$reminder set <reminder> <property> <value>: Change" \
256 + " <property> of reminder number <reminder> to " \
257 + "<value>. Availale properties: message, time, group")
258 elif args
in ("list", "show"):
259 reply(who
, where
, "$reminder list: Print a list of reminders. If there" \
260 + " are more than 5, I will reply in private.")
261 elif args
== "repeat":
262 reply(who
, where
, "$reminder repeat <reminder> every <interval>: " \
263 + "Reminder number <reminder> will be repeated every " \
264 + "<interval>. Use $reminder repeat <reminder> off to"\
265 + " stop repeating a reminder.")
267 reply(who
, where
, "$reminder help <command>: get help on <command>. " \
268 + "You know, like you just did.")
270 reply(who
, where
, "I don't understand that.")
271 commands
['reminder'] = (perms
.admin
, reminder
)
272 commands
['reminders'] = (perms
.admin
, reminder
)
274 def reminders_list(who
, where
, args
):
275 "$reminders-list: Asks Hal for the current list of reminders."
276 reminder(who
, where
, "list")
277 commands
['reminders-list'] = (perms
.voice
, reminders_list
)
279 def subscribe(who
, where
, args
):
280 "$subscribe <groups>: Subscribes you to the (comma-separated) list of reminder groups."
282 reply(who
, where
, "Don't you think subscribing to reminders that are" +
283 " already in PM is a bit... pointless?")
286 unlower
[irc_lower(who
)] = who
287 subscriptions
= get_subscription_db(who
, where
)
288 targets
= [target
.strip() for target
in args
.split(",")]
289 new_subscription
= False
290 for target
in targets
:
291 subscribers
= extract(subscriptions
, target
, Set
)
292 user
= irc_lower(who
)
293 if user
not in subscribers
:
294 subscribers
.add(user
)
295 new_subscription
= True
297 reply(who
, where
, "Done.")
299 reply(who
, where
, "You're already subscribed.")
300 commands
['subscribe'] = (perms
.voice
, subscribe
)
302 def unsubscribe(who
, where
, args
):
303 "$unsubscribe <groups>: Unsubscribes you from the (comma-separated) list of reminder groups."
304 subscriptions
= get_subscription_db(who
, where
)
305 targets
= [target
.strip() for target
in args
.split(",")]
306 lost_subscription
= False
307 for target
in targets
:
308 subscribers
= extract(subscriptions
, target
, Set
)
309 user
= irc_lower(who
)
310 if user
in subscribers
:
311 subscribers
.discard(user
)
312 lost_subscription
= True
313 if lost_subscription
:
314 reply(who
, where
, "Done.")
316 reply(who
, where
, "You weren't subscribed.")
317 commands
['unsubscribe'] = (perms
.voice
, unsubscribe
)
320 for where
in reminder_dbs
.keys():
321 db
= reminder_dbs
[where
]
323 schedule_reminder(where
, db
, reminder
)
324 while True: # Looping in case of errors.
325 safe(scheduler
.run_forever
)()