Merge lp:~gingerchris/endroid/assorted_bug_fixes into lp:~gingerchris/endroid/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
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 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 by checking out the code using `bzr branch lp:endroid`.
7
8- Note: !EnDroid requires python 2.7 or later to run, and depends on Twisted, Wokkel, PyTZ and python-dateutil.
9+ Note: !EnDroid requires python 2.7 or later to run, and depends on Twisted, Wokkel, PyTZ, treq, jinja2 and python-dateutil.
10
11 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 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-[[http://comm.unicate.me/|comm.unicate.me]]).
14+[[http://www.jabber.at/|jabber.at]]).
15
16 Note: '''Do not use your own account''' as !EnDroid will cut down its contact list to users listed in the config file.
17
18@@ -21,12 +21,12 @@
19 = Running EnDroid =
20
21 * If you installed !EnDroid, you should be able to run it by typing `endroid` into the console.
22- * If you pulled the source, run !EnDroid from within the `src` directory with `./endroid.sh`
23+ * If you pulled the source, run !EnDroid using `src/endroid.sh`
24 * See `-h` for full list of command line arguments, some key ones are:
25- * `<config-file>` - specify an alternative config file.
26+ * `[-c <config-file>]` - specify an alternative config file.
27 * `[-l|--level] <log-level>` - specify the verbosity of the console log (lower is more verbose)
28 * `[-L|--logfile] <log-file>` - redirect the console logging to a file.
29- * `[-t|--logtraffic]` - if `-t` is present, `twisted` will log all the raw message xml to the file log.
30+ * `[-t|--logtraffic]` - log all the raw message xml to the file log.
31
32 See the [[../Debugging|debugging page]] for more details on logging and `Manhole`.
33
34
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 For examples of using the context-aware response callbacks, see the Remote
40 and Timer plugins.
41
42-=== Context awareness ===
43+=== Context awareness ===
44 This is implemented by allowing plugins to attach callbacks to
45 MessageHandler.send_chat() for 'when user next replies' and 'if no reply
46 received'. There is a config file option for the latter.
47
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 __import__(modname)
53 except ImportError as i:
54 logging.error(i)
55- logging.error("**Could Not Import Plugin \"" + modname
56- + "\". Check That It Exists In Your PYTHONPATH.")
57+ logging.error("**Could not import plugin \"" + modname
58+ + "\". Check that it exists in your PYTHONPATH.")
59 return
60 except Exception as e:
61 logging.error(e)
62
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 """Pings a user on each server Endroid knows about."""
68
69 users_to_ping = set(self._get_one_from_each())
70- logging.debug('PeriodicPinger: Pinging {}...'.format(users_to_ping))
71+ self.log.debug('PeriodicPinger: Pinging {}...'.format(users_to_ping))
72 for user in users_to_ping:
73 self.usermanagement.ping(user)
74
75
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 name = "pounce"
81 help = ("Pounces on a user by sending them a message immediately when they"
82 " come online. \n"
83- "Use 'pounce message <user> <message>', \n"
84- "or 'stalk <user>' (to be notified when a user goes offline), \n"
85- "or 'pounce <user>' to be told when the user comes online.")
86+ "Use 'pounce <user> [<message>]' to be told when user comes online "
87+ "and optionally send them message when they do, \n"
88+ "or 'stalk <user>' to be notified when a user goes offline.")
89
90
91- @command(helphint='{user}')
92+ @command(helphint='{user} [{message}]')
93 def pounce(self, msg, args):
94- subject = args.strip() # strip spaces from the outside if necessary
95- if args[:8] == 'message ':
96- # we don't want to handle this, as it's a 'pounce message' call
97- msg.unhandled()
98- return
99- if self.rosters.is_online(subject):
100- msg.reply(subject + ' is already online.')
101- return
102-
103- message = subject + ' came online.'
104- cb = lambda: self.messages.send_chat(msg.sender, message)
105-
106- self.rosters.register_presence_callback(user=subject, callback=cb,
107- available=True)
108- msg.reply("I'll let you know when {} comes online.".format(subject))
109-
110- @command(helphint="{user} {message}")
111- def pounce_message(self, msg, args):
112- args_split = args.split(' ')
113- tojid = args_split[0]
114-
115- if args_split[1:]:
116- message = msg.sender + ' pounced! ' + ' '.join(args_split[1:])
117+ reply = None
118+ args = args.strip()
119+ if args:
120+ arg_tokens = args.split(' ')
121+ target = arg_tokens.pop(0)
122+ message = " ".join(arg_tokens)
123+ if message:
124+ message = "{} says: {}".format(msg.sender, message)
125 else:
126- msg.reply('You need to specify both a user and a message for me to '
127- 'deliver when I pounce.')
128- return
129-
130- if self.rosters.is_online(tojid):
131- self.messages.send_chat(tojid, message, msg.sender)
132- msg.reply("The user was already online, so I just told them direct.")
133- return
134-
135- cb = lambda: self.messages.send_chat(tojid, message, msg.sender)
136-
137- self.rosters.register_presence_callback(user=tojid, callback=cb,
138- available=True)
139-
140- msg.reply("Lying in wait for {} with message '{}'".format(tojid, message))
141-
142+ reply = "Doesn't look like you've told me who to pounce"
143+
144+ if not reply and target not in self.rosters.users:
145+ reply = "I don't know the user {}.".format(target)
146+
147+ if not reply and self.rosters.is_online(target):
148+ if message:
149+ # Send them the message now
150+ self.messages.send_chat(target, message, msg.sender)
151+ reply = target + " was already online, so I just told them direct."
152+ else:
153+ reply = target + " is already online."
154+
155+ if not reply:
156+ if message:
157+ # When the target comes online send them message and the
158+ # requestor the pounce_message
159+ pounce_message = target + ' came online - message sent'
160+ reply = ("I'll let you know when {} comes online and send "
161+ "them the message.".format(target))
162+ else:
163+ pounce_message = target + ' came online.'
164+ reply = "I'll let you know when {} comes online.".format(target)
165+
166+ def cb():
167+ self.messages.send_chat(msg.sender, pounce_message)
168+ if message:
169+ self.messages.send_chat(target, message)
170+
171+ self.rosters.register_presence_callback(user=target, callback=cb,
172+ available=True)
173+
174+ msg.reply(reply)
175
176 @command(helphint="{user}")
177 def stalk(self, msg, args):
178 cb = lambda: self.messages.send_chat(msg.sender,
179 'User {} is offline'.format(args))
180 self.rosters.register_presence_callback(user=args, callback=cb,
181- unavailable=True)
182+ unavailable=True)
183
184 msg.reply("You will be notified when user {} goes offline.".format(args))
185
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
191 # logging file stuff
192 self.log = logging.getLogger(__name__)
193- try:
194- logfile = os.path.expanduser(self.vars['logfile'])
195- except KeyError:
196- logfile = os.path.expanduser('~/.endroid/sms.log')
197+ logfile = os.path.expanduser(self.vars.get('logfile', '~/.endroid/sms.log'))
198+ self.log.info("Logging SMS activity to {}".format(logfile))
199 handler = logging.FileHandler(logfile)
200 formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
201 handler.setFormatter(formatter)
202@@ -245,7 +243,7 @@
203
204 return users_blocked
205
206- def _sms_reset_number_sent(self):
207+ def _sms_reset_number_sent(self, unused_params):
208 """Resets the number of texts sent in the database."""
209
210 self.db.empty_table(DB_LIMIT_TABLE)
211
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
217 class TrainTimes(CommandPlugin):
218 name = "traintimes"
219- help_topics = {
220- "": "When do trains leave?",
221- }
222
223 def endroid_init(self):
224 if not self.database.table_exists(STATION_TABLE):
225@@ -32,6 +29,9 @@
226 if not self.database.table_exists(HOME_TABLE):
227 self.database.create_table(HOME_TABLE, ("jid", "station"))
228
229+ def help(self):
230+ return "When do trains leave?"
231+
232 def _station_update(self, msg, args, table, jid, display):
233 if not args:
234 rows = self.database.fetch(table, ("station",),
235@@ -43,7 +43,7 @@
236 msg.reply_to_sender("You don't have a {} station set."
237 .format(display))
238 return
239- self.database.delete(table, {"jid": msg.sender})
240+ self.database.delete(table, {"jid" : jid})
241 if args != "delete":
242 self.database.insert(table, {"jid": jid, "station": args})
243 msg.reply_to_sender("Your new {} station is: {}"
244@@ -54,11 +54,15 @@
245
246 @command(helphint="{<station name>|delete}")
247 def nearest_station(self, msg, args):
248+ # Use msg.sender_full (with resource) because a person might
249+ # be in different locations with different resources
250 self._station_update(msg, args, STATION_TABLE, msg.sender_full,
251 "nearest")
252
253 @command(helphint="{<station name>|delete}")
254 def home_station(self, msg, args):
255+ # Use msg.sender (no resource) because a person probably
256+ # lives in the same place regardless of XMPP resource
257 self._station_update(msg, args, HOME_TABLE, msg.sender, "home")
258
259 @command(helphint="from <stn> to <stn> [[arriving|leaving] at <time>]",
260@@ -70,7 +74,10 @@
261 return
262
263 def extract_results(data):
264- results = RESULTRE.findall(ULRE.search(data).group(1))
265+ result = None
266+ m = ULRE.search(data)
267+ if m:
268+ results = RESULTRE.findall(m.group(1))
269 if results:
270 msg.reply(u"Trains from {} to {}: {}"
271 .format(src, dst,
272@@ -78,8 +85,12 @@
273 else:
274 msg.reply("Either your request is malformed, or there are no matching trains")
275
276- home, src, dst, typ, when, time, _,_,_ = match.groups()
277- time = self._canonical_time(time) if time is not None else time
278+ home, src, dst, typ, when, raw_time, _,_,_ = match.groups()
279+ time = self._canonical_time(raw_time) if raw_time is not None else None
280+ if raw_time and not time:
281+ msg.reply("I can't understand the time you've entered can you try another way?")
282+ return
283+
284 if dst == "home" or home == "home":
285 rows = self.database.fetch(HOME_TABLE, ("station",),
286 {"jid": msg.sender})
287@@ -104,17 +115,22 @@
288 "a" if typ == "arriving" else "",
289 ("/" + when.replace(" ", "-")) if when else "")
290 getPage("http://www.traintimes.org.uk" + urllib.quote(url)
291- ).addCallbacks(extract_results, msg.unhandled)
292+ ).addCallbacks(extract_results, lambda x: msg.reply("Bad train request"))
293
294 @staticmethod
295 def _canonical_time(time):
296 match = TIMERE.match(time.strip())
297 assert match, "We've already checked this - how can it fail?!"
298 hour, minute, half = match.groups()
299+ if len(hour) == 4 and not minute and not half:
300+ # This is just a 4 digit number, treat as if its a time in 24h
301+ minute = hour[2:]
302+ hour = hour[:2]
303 hour, minute = map(lambda n: int(n) if n else 0, (hour, minute))
304+ if hour > 24:
305+ return None
306+
307 if half and half == 'pm':
308 if hour <= 12:
309 hour += 12
310- if not minute:
311- minute = 0
312 return "{}:{:02}".format(hour, minute)
313
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 :param unavailable: set True to have callback fire when user goes offline
319 """
320 if not (unavailable or available):
321- # let us know that our callback will never fire
322 raise ValueError('Callback specified neither available nor unavailable')
323
324+ if user not in self.users():
325+ raise ValueError('Invalid user {}'.format(user))
326+
327 if available:
328 self._callbacks_when_available[user].append(callback)
329 if unavailable:
330@@ -905,6 +907,9 @@
331 :param available: set True to have callback fire when user comes online
332 :param unavailable: set True to have callback fire when user goes offline
333 """
334+
335+ if user not in self.users:
336+ raise ValueError('Invalid user {}'.format(user))
337 self._usermanagement._register_presence_callback(user, callback,
338 available=available,
339 unavailable=unavailable)

Subscribers

People subscribed via source and target branches

to all changes: