Merge lp:~gingerchris/endroid/assorted_bug_fixes into lp:~gingerchris/endroid/remote_changes
- assorted_bug_fixes
- Merge into remote_changes
Proposed by
ChrisD
Status: | Merged |
---|---|
Approved by: | ChrisD |
Approved revision: | 115 |
Merged at revision: | 90 |
Proposed branch: | lp:~gingerchris/endroid/assorted_bug_fixes |
Merge into: | lp:~gingerchris/endroid/remote_changes |
Diff against target: |
339 lines (+90/-69) 8 files modified
doc/wiki/GettingStarted (+5/-5) doc/wiki/Reference (+1/-1) src/endroid/pluginmanager.py (+2/-2) src/endroid/plugins/periodicpinger.py (+1/-1) src/endroid/plugins/pounce.py (+46/-44) src/endroid/plugins/sms/__init__.py (+3/-5) src/endroid/plugins/trains.py (+26/-10) src/endroid/usermanagement.py (+6/-1) |
To merge this branch: | bzr merge lp:~gingerchris/endroid/assorted_bug_fixes |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
ChrisD | Approve | ||
Phil Connell (community) | Approve | ||
Review via email: mp+234849@code.launchpad.net |
Commit message
SMS period bugfix, pounce and trains bugfixes
Description of the change
- Fix bug in SMS period reset (wrong number of args)
- Pounce:
- remove pounce message cmd (merge with punce)
- add checking to see if user is known (all the way down the APIs) or empty string
- always messages the pounce setter when the target comes online (even when message specified)
- Add pounce setting username to the pounce message
- Trains:
- Fix help
- Fix bug with DB entry removal and failure to extract results
- Add better error if time no understood
- Add better error if HTTP request fails
- Support 4 digit 24 hour times and error impossible hours
- Minor changes to: wiki pages, pluginmanager
To post a comment you must log in.
Revision history for this message
Phil Connell (pconnell) : | # |
review:
Approve
Revision history for this message
ChrisD (gingerchris) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'doc/wiki/GettingStarted' | |||
2 | --- doc/wiki/GettingStarted 2013-09-11 14:24:24 +0000 | |||
3 | +++ doc/wiki/GettingStarted 2014-09-16 16:39:09 +0000 | |||
4 | @@ -7,11 +7,11 @@ | |||
5 | 7 | Installing EnDroid is straightforward. Installation packages are provided (currently only for Ubuntu) [[https://launchpad.net/endroid/+download|on Launchpad]]. It is also possible to run it directly | 7 | Installing EnDroid is straightforward. Installation packages are provided (currently only for Ubuntu) [[https://launchpad.net/endroid/+download|on Launchpad]]. It is also possible to run it directly |
6 | 8 | by checking out the code using `bzr branch lp:endroid`. | 8 | by checking out the code using `bzr branch lp:endroid`. |
7 | 9 | 9 | ||
9 | 10 | Note: !EnDroid requires python 2.7 or later to run, and depends on Twisted, Wokkel, PyTZ and python-dateutil. | 10 | Note: !EnDroid requires python 2.7 or later to run, and depends on Twisted, Wokkel, PyTZ, treq, jinja2 and python-dateutil. |
10 | 11 | 11 | ||
11 | 12 | In order to run EnDroid needs to load certain settings from its config file. An example one is at `/usr/share/doc/endroid/examples/endroid.conf` if !EnDroid has been installed or in the | 12 | In order to run EnDroid needs to load certain settings from its config file. An example one is at `/usr/share/doc/endroid/examples/endroid.conf` if !EnDroid has been installed or in the |
12 | 13 | source repository at `etc/endroid.conf`. The settings that must be added to the config file are details of an XMPP account for !EnDroid to use (free accounts can be created easily e.g. from | 13 | source repository at `etc/endroid.conf`. The settings that must be added to the config file are details of an XMPP account for !EnDroid to use (free accounts can be created easily e.g. from |
14 | 14 | [[http://comm.unicate.me/|comm.unicate.me]]). | 14 | [[http://www.jabber.at/|jabber.at]]). |
15 | 15 | 15 | ||
16 | 16 | Note: '''Do not use your own account''' as !EnDroid will cut down its contact list to users listed in the config file. | 16 | Note: '''Do not use your own account''' as !EnDroid will cut down its contact list to users listed in the config file. |
17 | 17 | 17 | ||
18 | @@ -21,12 +21,12 @@ | |||
19 | 21 | = Running EnDroid = | 21 | = Running EnDroid = |
20 | 22 | 22 | ||
21 | 23 | * If you installed !EnDroid, you should be able to run it by typing `endroid` into the console. | 23 | * If you installed !EnDroid, you should be able to run it by typing `endroid` into the console. |
23 | 24 | * If you pulled the source, run !EnDroid from within the `src` directory with `./endroid.sh` | 24 | * If you pulled the source, run !EnDroid using `src/endroid.sh` |
24 | 25 | * See `-h` for full list of command line arguments, some key ones are: | 25 | * See `-h` for full list of command line arguments, some key ones are: |
26 | 26 | * `<config-file>` - specify an alternative config file. | 26 | * `[-c <config-file>]` - specify an alternative config file. |
27 | 27 | * `[-l|--level] <log-level>` - specify the verbosity of the console log (lower is more verbose) | 27 | * `[-l|--level] <log-level>` - specify the verbosity of the console log (lower is more verbose) |
28 | 28 | * `[-L|--logfile] <log-file>` - redirect the console logging to a file. | 28 | * `[-L|--logfile] <log-file>` - redirect the console logging to a file. |
30 | 29 | * `[-t|--logtraffic]` - if `-t` is present, `twisted` will log all the raw message xml to the file log. | 29 | * `[-t|--logtraffic]` - log all the raw message xml to the file log. |
31 | 30 | 30 | ||
32 | 31 | See the [[../Debugging|debugging page]] for more details on logging and `Manhole`. | 31 | See the [[../Debugging|debugging page]] for more details on logging and `Manhole`. |
33 | 32 | 32 | ||
34 | 33 | 33 | ||
35 | === modified file 'doc/wiki/Reference' | |||
36 | --- doc/wiki/Reference 2014-09-02 10:51:47 +0000 | |||
37 | +++ doc/wiki/Reference 2014-09-16 16:39:09 +0000 | |||
38 | @@ -233,7 +233,7 @@ | |||
39 | 233 | For examples of using the context-aware response callbacks, see the Remote | 233 | For examples of using the context-aware response callbacks, see the Remote |
40 | 234 | and Timer plugins. | 234 | and Timer plugins. |
41 | 235 | 235 | ||
43 | 236 | === Context awareness === | 236 | === Context awareness === |
44 | 237 | This is implemented by allowing plugins to attach callbacks to | 237 | This is implemented by allowing plugins to attach callbacks to |
45 | 238 | MessageHandler.send_chat() for 'when user next replies' and 'if no reply | 238 | MessageHandler.send_chat() for 'when user next replies' and 'if no reply |
46 | 239 | received'. There is a config file option for the latter. | 239 | received'. There is a config file option for the latter. |
47 | 240 | 240 | ||
48 | === modified file 'src/endroid/pluginmanager.py' | |||
49 | --- src/endroid/pluginmanager.py 2014-08-11 15:30:01 +0000 | |||
50 | +++ src/endroid/pluginmanager.py 2014-09-16 16:39:09 +0000 | |||
51 | @@ -305,8 +305,8 @@ | |||
52 | 305 | __import__(modname) | 305 | __import__(modname) |
53 | 306 | except ImportError as i: | 306 | except ImportError as i: |
54 | 307 | logging.error(i) | 307 | logging.error(i) |
57 | 308 | logging.error("**Could Not Import Plugin \"" + modname | 308 | logging.error("**Could not import plugin \"" + modname |
58 | 309 | + "\". Check That It Exists In Your PYTHONPATH.") | 309 | + "\". Check that it exists in your PYTHONPATH.") |
59 | 310 | return | 310 | return |
60 | 311 | except Exception as e: | 311 | except Exception as e: |
61 | 312 | logging.error(e) | 312 | logging.error(e) |
62 | 313 | 313 | ||
63 | === modified file 'src/endroid/plugins/periodicpinger.py' | |||
64 | --- src/endroid/plugins/periodicpinger.py 2014-09-10 13:13:58 +0000 | |||
65 | +++ src/endroid/plugins/periodicpinger.py 2014-09-16 16:39:09 +0000 | |||
66 | @@ -62,7 +62,7 @@ | |||
67 | 62 | """Pings a user on each server Endroid knows about.""" | 62 | """Pings a user on each server Endroid knows about.""" |
68 | 63 | 63 | ||
69 | 64 | users_to_ping = set(self._get_one_from_each()) | 64 | users_to_ping = set(self._get_one_from_each()) |
71 | 65 | logging.debug('PeriodicPinger: Pinging {}...'.format(users_to_ping)) | 65 | self.log.debug('PeriodicPinger: Pinging {}...'.format(users_to_ping)) |
72 | 66 | for user in users_to_ping: | 66 | for user in users_to_ping: |
73 | 67 | self.usermanagement.ping(user) | 67 | self.usermanagement.ping(user) |
74 | 68 | 68 | ||
75 | 69 | 69 | ||
76 | === modified file 'src/endroid/plugins/pounce.py' | |||
77 | --- src/endroid/plugins/pounce.py 2014-08-14 10:01:44 +0000 | |||
78 | +++ src/endroid/plugins/pounce.py 2014-09-16 16:39:09 +0000 | |||
79 | @@ -12,59 +12,61 @@ | |||
80 | 12 | name = "pounce" | 12 | name = "pounce" |
81 | 13 | help = ("Pounces on a user by sending them a message immediately when they" | 13 | help = ("Pounces on a user by sending them a message immediately when they" |
82 | 14 | " come online. \n" | 14 | " come online. \n" |
86 | 15 | "Use 'pounce message <user> <message>', \n" | 15 | "Use 'pounce <user> [<message>]' to be told when user comes online " |
87 | 16 | "or 'stalk <user>' (to be notified when a user goes offline), \n" | 16 | "and optionally send them message when they do, \n" |
88 | 17 | "or 'pounce <user>' to be told when the user comes online.") | 17 | "or 'stalk <user>' to be notified when a user goes offline.") |
89 | 18 | 18 | ||
90 | 19 | 19 | ||
92 | 20 | @command(helphint='{user}') | 20 | @command(helphint='{user} [{message}]') |
93 | 21 | def pounce(self, msg, args): | 21 | def pounce(self, msg, args): |
117 | 22 | subject = args.strip() # strip spaces from the outside if necessary | 22 | reply = None |
118 | 23 | if args[:8] == 'message ': | 23 | args = args.strip() |
119 | 24 | # we don't want to handle this, as it's a 'pounce message' call | 24 | if args: |
120 | 25 | msg.unhandled() | 25 | arg_tokens = args.split(' ') |
121 | 26 | return | 26 | target = arg_tokens.pop(0) |
122 | 27 | if self.rosters.is_online(subject): | 27 | message = " ".join(arg_tokens) |
123 | 28 | msg.reply(subject + ' is already online.') | 28 | if message: |
124 | 29 | return | 29 | message = "{} says: {}".format(msg.sender, message) |
102 | 30 | |||
103 | 31 | message = subject + ' came online.' | ||
104 | 32 | cb = lambda: self.messages.send_chat(msg.sender, message) | ||
105 | 33 | |||
106 | 34 | self.rosters.register_presence_callback(user=subject, callback=cb, | ||
107 | 35 | available=True) | ||
108 | 36 | msg.reply("I'll let you know when {} comes online.".format(subject)) | ||
109 | 37 | |||
110 | 38 | @command(helphint="{user} {message}") | ||
111 | 39 | def pounce_message(self, msg, args): | ||
112 | 40 | args_split = args.split(' ') | ||
113 | 41 | tojid = args_split[0] | ||
114 | 42 | |||
115 | 43 | if args_split[1:]: | ||
116 | 44 | message = msg.sender + ' pounced! ' + ' '.join(args_split[1:]) | ||
125 | 45 | else: | 30 | else: |
142 | 46 | msg.reply('You need to specify both a user and a message for me to ' | 31 | reply = "Doesn't look like you've told me who to pounce" |
143 | 47 | 'deliver when I pounce.') | 32 | |
144 | 48 | return | 33 | if not reply and target not in self.rosters.users: |
145 | 49 | 34 | reply = "I don't know the user {}.".format(target) | |
146 | 50 | if self.rosters.is_online(tojid): | 35 | |
147 | 51 | self.messages.send_chat(tojid, message, msg.sender) | 36 | if not reply and self.rosters.is_online(target): |
148 | 52 | msg.reply("The user was already online, so I just told them direct.") | 37 | if message: |
149 | 53 | return | 38 | # Send them the message now |
150 | 54 | 39 | self.messages.send_chat(target, message, msg.sender) | |
151 | 55 | cb = lambda: self.messages.send_chat(tojid, message, msg.sender) | 40 | reply = target + " was already online, so I just told them direct." |
152 | 56 | 41 | else: | |
153 | 57 | self.rosters.register_presence_callback(user=tojid, callback=cb, | 42 | reply = target + " is already online." |
154 | 58 | available=True) | 43 | |
155 | 59 | 44 | if not reply: | |
156 | 60 | msg.reply("Lying in wait for {} with message '{}'".format(tojid, message)) | 45 | if message: |
157 | 61 | 46 | # When the target comes online send them message and the | |
158 | 47 | # requestor the pounce_message | ||
159 | 48 | pounce_message = target + ' came online - message sent' | ||
160 | 49 | reply = ("I'll let you know when {} comes online and send " | ||
161 | 50 | "them the message.".format(target)) | ||
162 | 51 | else: | ||
163 | 52 | pounce_message = target + ' came online.' | ||
164 | 53 | reply = "I'll let you know when {} comes online.".format(target) | ||
165 | 54 | |||
166 | 55 | def cb(): | ||
167 | 56 | self.messages.send_chat(msg.sender, pounce_message) | ||
168 | 57 | if message: | ||
169 | 58 | self.messages.send_chat(target, message) | ||
170 | 59 | |||
171 | 60 | self.rosters.register_presence_callback(user=target, callback=cb, | ||
172 | 61 | available=True) | ||
173 | 62 | |||
174 | 63 | msg.reply(reply) | ||
175 | 62 | 64 | ||
176 | 63 | @command(helphint="{user}") | 65 | @command(helphint="{user}") |
177 | 64 | def stalk(self, msg, args): | 66 | def stalk(self, msg, args): |
178 | 65 | cb = lambda: self.messages.send_chat(msg.sender, | 67 | cb = lambda: self.messages.send_chat(msg.sender, |
179 | 66 | 'User {} is offline'.format(args)) | 68 | 'User {} is offline'.format(args)) |
180 | 67 | self.rosters.register_presence_callback(user=args, callback=cb, | 69 | self.rosters.register_presence_callback(user=args, callback=cb, |
182 | 68 | unavailable=True) | 70 | unavailable=True) |
183 | 69 | 71 | ||
184 | 70 | msg.reply("You will be notified when user {} goes offline.".format(args)) | 72 | msg.reply("You will be notified when user {} goes offline.".format(args)) |
185 | 71 | 73 | ||
186 | === modified file 'src/endroid/plugins/sms/__init__.py' | |||
187 | --- src/endroid/plugins/sms/__init__.py 2014-09-12 12:22:44 +0000 | |||
188 | +++ src/endroid/plugins/sms/__init__.py 2014-09-16 16:39:09 +0000 | |||
189 | @@ -123,10 +123,8 @@ | |||
190 | 123 | 123 | ||
191 | 124 | # logging file stuff | 124 | # logging file stuff |
192 | 125 | self.log = logging.getLogger(__name__) | 125 | self.log = logging.getLogger(__name__) |
197 | 126 | try: | 126 | logfile = os.path.expanduser(self.vars.get('logfile', '~/.endroid/sms.log')) |
198 | 127 | logfile = os.path.expanduser(self.vars['logfile']) | 127 | self.log.info("Logging SMS activity to {}".format(logfile)) |
195 | 128 | except KeyError: | ||
196 | 129 | logfile = os.path.expanduser('~/.endroid/sms.log') | ||
199 | 130 | handler = logging.FileHandler(logfile) | 128 | handler = logging.FileHandler(logfile) |
200 | 131 | formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') | 129 | formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') |
201 | 132 | handler.setFormatter(formatter) | 130 | handler.setFormatter(formatter) |
202 | @@ -245,7 +243,7 @@ | |||
203 | 245 | 243 | ||
204 | 246 | return users_blocked | 244 | return users_blocked |
205 | 247 | 245 | ||
207 | 248 | def _sms_reset_number_sent(self): | 246 | def _sms_reset_number_sent(self, unused_params): |
208 | 249 | """Resets the number of texts sent in the database.""" | 247 | """Resets the number of texts sent in the database.""" |
209 | 250 | 248 | ||
210 | 251 | self.db.empty_table(DB_LIMIT_TABLE) | 249 | self.db.empty_table(DB_LIMIT_TABLE) |
211 | 252 | 250 | ||
212 | === modified file 'src/endroid/plugins/trains.py' | |||
213 | --- src/endroid/plugins/trains.py 2014-08-12 10:28:57 +0000 | |||
214 | +++ src/endroid/plugins/trains.py 2014-09-16 16:39:09 +0000 | |||
215 | @@ -22,9 +22,6 @@ | |||
216 | 22 | 22 | ||
217 | 23 | class TrainTimes(CommandPlugin): | 23 | class TrainTimes(CommandPlugin): |
218 | 24 | name = "traintimes" | 24 | name = "traintimes" |
219 | 25 | help_topics = { | ||
220 | 26 | "": "When do trains leave?", | ||
221 | 27 | } | ||
222 | 28 | 25 | ||
223 | 29 | def endroid_init(self): | 26 | def endroid_init(self): |
224 | 30 | if not self.database.table_exists(STATION_TABLE): | 27 | if not self.database.table_exists(STATION_TABLE): |
225 | @@ -32,6 +29,9 @@ | |||
226 | 32 | if not self.database.table_exists(HOME_TABLE): | 29 | if not self.database.table_exists(HOME_TABLE): |
227 | 33 | self.database.create_table(HOME_TABLE, ("jid", "station")) | 30 | self.database.create_table(HOME_TABLE, ("jid", "station")) |
228 | 34 | 31 | ||
229 | 32 | def help(self): | ||
230 | 33 | return "When do trains leave?" | ||
231 | 34 | |||
232 | 35 | def _station_update(self, msg, args, table, jid, display): | 35 | def _station_update(self, msg, args, table, jid, display): |
233 | 36 | if not args: | 36 | if not args: |
234 | 37 | rows = self.database.fetch(table, ("station",), | 37 | rows = self.database.fetch(table, ("station",), |
235 | @@ -43,7 +43,7 @@ | |||
236 | 43 | msg.reply_to_sender("You don't have a {} station set." | 43 | msg.reply_to_sender("You don't have a {} station set." |
237 | 44 | .format(display)) | 44 | .format(display)) |
238 | 45 | return | 45 | return |
240 | 46 | self.database.delete(table, {"jid": msg.sender}) | 46 | self.database.delete(table, {"jid" : jid}) |
241 | 47 | if args != "delete": | 47 | if args != "delete": |
242 | 48 | self.database.insert(table, {"jid": jid, "station": args}) | 48 | self.database.insert(table, {"jid": jid, "station": args}) |
243 | 49 | msg.reply_to_sender("Your new {} station is: {}" | 49 | msg.reply_to_sender("Your new {} station is: {}" |
244 | @@ -54,11 +54,15 @@ | |||
245 | 54 | 54 | ||
246 | 55 | @command(helphint="{<station name>|delete}") | 55 | @command(helphint="{<station name>|delete}") |
247 | 56 | def nearest_station(self, msg, args): | 56 | def nearest_station(self, msg, args): |
248 | 57 | # Use msg.sender_full (with resource) because a person might | ||
249 | 58 | # be in different locations with different resources | ||
250 | 57 | self._station_update(msg, args, STATION_TABLE, msg.sender_full, | 59 | self._station_update(msg, args, STATION_TABLE, msg.sender_full, |
251 | 58 | "nearest") | 60 | "nearest") |
252 | 59 | 61 | ||
253 | 60 | @command(helphint="{<station name>|delete}") | 62 | @command(helphint="{<station name>|delete}") |
254 | 61 | def home_station(self, msg, args): | 63 | def home_station(self, msg, args): |
255 | 64 | # Use msg.sender (no resource) because a person probably | ||
256 | 65 | # lives in the same place regardless of XMPP resource | ||
257 | 62 | self._station_update(msg, args, HOME_TABLE, msg.sender, "home") | 66 | self._station_update(msg, args, HOME_TABLE, msg.sender, "home") |
258 | 63 | 67 | ||
259 | 64 | @command(helphint="from <stn> to <stn> [[arriving|leaving] at <time>]", | 68 | @command(helphint="from <stn> to <stn> [[arriving|leaving] at <time>]", |
260 | @@ -70,7 +74,10 @@ | |||
261 | 70 | return | 74 | return |
262 | 71 | 75 | ||
263 | 72 | def extract_results(data): | 76 | def extract_results(data): |
265 | 73 | results = RESULTRE.findall(ULRE.search(data).group(1)) | 77 | result = None |
266 | 78 | m = ULRE.search(data) | ||
267 | 79 | if m: | ||
268 | 80 | results = RESULTRE.findall(m.group(1)) | ||
269 | 74 | if results: | 81 | if results: |
270 | 75 | msg.reply(u"Trains from {} to {}: {}" | 82 | msg.reply(u"Trains from {} to {}: {}" |
271 | 76 | .format(src, dst, | 83 | .format(src, dst, |
272 | @@ -78,8 +85,12 @@ | |||
273 | 78 | else: | 85 | else: |
274 | 79 | msg.reply("Either your request is malformed, or there are no matching trains") | 86 | msg.reply("Either your request is malformed, or there are no matching trains") |
275 | 80 | 87 | ||
278 | 81 | home, src, dst, typ, when, time, _,_,_ = match.groups() | 88 | home, src, dst, typ, when, raw_time, _,_,_ = match.groups() |
279 | 82 | time = self._canonical_time(time) if time is not None else time | 89 | time = self._canonical_time(raw_time) if raw_time is not None else None |
280 | 90 | if raw_time and not time: | ||
281 | 91 | msg.reply("I can't understand the time you've entered can you try another way?") | ||
282 | 92 | return | ||
283 | 93 | |||
284 | 83 | if dst == "home" or home == "home": | 94 | if dst == "home" or home == "home": |
285 | 84 | rows = self.database.fetch(HOME_TABLE, ("station",), | 95 | rows = self.database.fetch(HOME_TABLE, ("station",), |
286 | 85 | {"jid": msg.sender}) | 96 | {"jid": msg.sender}) |
287 | @@ -104,17 +115,22 @@ | |||
288 | 104 | "a" if typ == "arriving" else "", | 115 | "a" if typ == "arriving" else "", |
289 | 105 | ("/" + when.replace(" ", "-")) if when else "") | 116 | ("/" + when.replace(" ", "-")) if when else "") |
290 | 106 | getPage("http://www.traintimes.org.uk" + urllib.quote(url) | 117 | getPage("http://www.traintimes.org.uk" + urllib.quote(url) |
292 | 107 | ).addCallbacks(extract_results, msg.unhandled) | 118 | ).addCallbacks(extract_results, lambda x: msg.reply("Bad train request")) |
293 | 108 | 119 | ||
294 | 109 | @staticmethod | 120 | @staticmethod |
295 | 110 | def _canonical_time(time): | 121 | def _canonical_time(time): |
296 | 111 | match = TIMERE.match(time.strip()) | 122 | match = TIMERE.match(time.strip()) |
297 | 112 | assert match, "We've already checked this - how can it fail?!" | 123 | assert match, "We've already checked this - how can it fail?!" |
298 | 113 | hour, minute, half = match.groups() | 124 | hour, minute, half = match.groups() |
299 | 125 | if len(hour) == 4 and not minute and not half: | ||
300 | 126 | # This is just a 4 digit number, treat as if its a time in 24h | ||
301 | 127 | minute = hour[2:] | ||
302 | 128 | hour = hour[:2] | ||
303 | 114 | hour, minute = map(lambda n: int(n) if n else 0, (hour, minute)) | 129 | hour, minute = map(lambda n: int(n) if n else 0, (hour, minute)) |
304 | 130 | if hour > 24: | ||
305 | 131 | return None | ||
306 | 132 | |||
307 | 115 | if half and half == 'pm': | 133 | if half and half == 'pm': |
308 | 116 | if hour <= 12: | 134 | if hour <= 12: |
309 | 117 | hour += 12 | 135 | hour += 12 |
310 | 118 | if not minute: | ||
311 | 119 | minute = 0 | ||
312 | 120 | return "{}:{:02}".format(hour, minute) | 136 | return "{}:{:02}".format(hour, minute) |
313 | 121 | 137 | ||
314 | === modified file 'src/endroid/usermanagement.py' | |||
315 | --- src/endroid/usermanagement.py 2014-08-22 12:28:28 +0000 | |||
316 | +++ src/endroid/usermanagement.py 2014-09-16 16:39:09 +0000 | |||
317 | @@ -480,9 +480,11 @@ | |||
318 | 480 | :param unavailable: set True to have callback fire when user goes offline | 480 | :param unavailable: set True to have callback fire when user goes offline |
319 | 481 | """ | 481 | """ |
320 | 482 | if not (unavailable or available): | 482 | if not (unavailable or available): |
321 | 483 | # let us know that our callback will never fire | ||
322 | 484 | raise ValueError('Callback specified neither available nor unavailable') | 483 | raise ValueError('Callback specified neither available nor unavailable') |
323 | 485 | 484 | ||
324 | 485 | if user not in self.users(): | ||
325 | 486 | raise ValueError('Invalid user {}'.format(user)) | ||
326 | 487 | |||
327 | 486 | if available: | 488 | if available: |
328 | 487 | self._callbacks_when_available[user].append(callback) | 489 | self._callbacks_when_available[user].append(callback) |
329 | 488 | if unavailable: | 490 | if unavailable: |
330 | @@ -905,6 +907,9 @@ | |||
331 | 905 | :param available: set True to have callback fire when user comes online | 907 | :param available: set True to have callback fire when user comes online |
332 | 906 | :param unavailable: set True to have callback fire when user goes offline | 908 | :param unavailable: set True to have callback fire when user goes offline |
333 | 907 | """ | 909 | """ |
334 | 910 | |||
335 | 911 | if user not in self.users: | ||
336 | 912 | raise ValueError('Invalid user {}'.format(user)) | ||
337 | 908 | self._usermanagement._register_presence_callback(user, callback, | 913 | self._usermanagement._register_presence_callback(user, callback, |
338 | 909 | available=available, | 914 | available=available, |
339 | 910 | unavailable=unavailable) | 915 | unavailable=unavailable) |