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
=== modified file 'doc/wiki/GettingStarted'
--- doc/wiki/GettingStarted 2013-09-11 14:24:24 +0000
+++ doc/wiki/GettingStarted 2014-09-16 16:39:09 +0000
@@ -7,11 +7,11 @@
7Installing 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 directly7Installing 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
8by checking out the code using `bzr branch lp:endroid`.8by checking out the code using `bzr branch lp:endroid`.
99
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.
1111
12In 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 12In 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
13source 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 13source 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[[http://comm.unicate.me/|comm.unicate.me]]). 14[[http://www.jabber.at/|jabber.at]]).
1515
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.
1717
@@ -21,12 +21,12 @@
21= Running EnDroid =21= Running EnDroid =
2222
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.
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`
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 * `<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)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.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.29 * `[-t|--logtraffic]` - log all the raw message xml to the file log.
3030
31See the [[../Debugging|debugging page]] for more details on logging and `Manhole`.31See the [[../Debugging|debugging page]] for more details on logging and `Manhole`.
3232
3333
=== modified file 'doc/wiki/Reference'
--- doc/wiki/Reference 2014-09-02 10:51:47 +0000
+++ doc/wiki/Reference 2014-09-16 16:39:09 +0000
@@ -233,7 +233,7 @@
233For examples of using the context-aware response callbacks, see the Remote233For examples of using the context-aware response callbacks, see the Remote
234and Timer plugins.234and Timer plugins.
235235
236=== Context awareness === 236=== Context awareness ===
237This is implemented by allowing plugins to attach callbacks to 237This is implemented by allowing plugins to attach callbacks to
238MessageHandler.send_chat() for 'when user next replies' and 'if no reply238MessageHandler.send_chat() for 'when user next replies' and 'if no reply
239received'. There is a config file option for the latter.239received'. There is a config file option for the latter.
240240
=== modified file 'src/endroid/pluginmanager.py'
--- src/endroid/pluginmanager.py 2014-08-11 15:30:01 +0000
+++ src/endroid/pluginmanager.py 2014-09-16 16:39:09 +0000
@@ -305,8 +305,8 @@
305 __import__(modname)305 __import__(modname)
306 except ImportError as i:306 except ImportError as i:
307 logging.error(i)307 logging.error(i)
308 logging.error("**Could Not Import Plugin \"" + modname308 logging.error("**Could not import plugin \"" + modname
309 + "\". Check That It Exists In Your PYTHONPATH.")309 + "\". Check that it exists in your PYTHONPATH.")
310 return310 return
311 except Exception as e:311 except Exception as e:
312 logging.error(e)312 logging.error(e)
313313
=== modified file 'src/endroid/plugins/periodicpinger.py'
--- src/endroid/plugins/periodicpinger.py 2014-09-10 13:13:58 +0000
+++ src/endroid/plugins/periodicpinger.py 2014-09-16 16:39:09 +0000
@@ -62,7 +62,7 @@
62 """Pings a user on each server Endroid knows about."""62 """Pings a user on each server Endroid knows about."""
6363
64 users_to_ping = set(self._get_one_from_each())64 users_to_ping = set(self._get_one_from_each())
65 logging.debug('PeriodicPinger: Pinging {}...'.format(users_to_ping))65 self.log.debug('PeriodicPinger: Pinging {}...'.format(users_to_ping))
66 for user in users_to_ping:66 for user in users_to_ping:
67 self.usermanagement.ping(user)67 self.usermanagement.ping(user)
6868
6969
=== modified file 'src/endroid/plugins/pounce.py'
--- src/endroid/plugins/pounce.py 2014-08-14 10:01:44 +0000
+++ src/endroid/plugins/pounce.py 2014-09-16 16:39:09 +0000
@@ -12,59 +12,61 @@
12 name = "pounce"12 name = "pounce"
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"
14 " come online. \n"14 " come online. \n"
15 "Use 'pounce message <user> <message>', \n"15 "Use 'pounce <user> [<message>]' to be told when user comes online "
16 "or 'stalk <user>' (to be notified when a user goes offline), \n"16 "and optionally send them message when they do, \n"
17 "or 'pounce <user>' to be told when the user comes online.")17 "or 'stalk <user>' to be notified when a user goes offline.")
1818
19 19
20 @command(helphint='{user}')20 @command(helphint='{user} [{message}]')
21 def pounce(self, msg, args):21 def pounce(self, msg, args):
22 subject = args.strip() # strip spaces from the outside if necessary22 reply = None
23 if args[:8] == 'message ':23 args = args.strip()
24 # we don't want to handle this, as it's a 'pounce message' call24 if args:
25 msg.unhandled()25 arg_tokens = args.split(' ')
26 return26 target = arg_tokens.pop(0)
27 if self.rosters.is_online(subject):27 message = " ".join(arg_tokens)
28 msg.reply(subject + ' is already online.')28 if message:
29 return29 message = "{} says: {}".format(msg.sender, message)
30
31 message = subject + ' came online.'
32 cb = lambda: self.messages.send_chat(msg.sender, message)
33
34 self.rosters.register_presence_callback(user=subject, callback=cb,
35 available=True)
36 msg.reply("I'll let you know when {} comes online.".format(subject))
37
38 @command(helphint="{user} {message}")
39 def pounce_message(self, msg, args):
40 args_split = args.split(' ')
41 tojid = args_split[0]
42
43 if args_split[1:]:
44 message = msg.sender + ' pounced! ' + ' '.join(args_split[1:])
45 else:30 else:
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"
47 'deliver when I pounce.')32
48 return33 if not reply and target not in self.rosters.users:
4934 reply = "I don't know the user {}.".format(target)
50 if self.rosters.is_online(tojid):35
51 self.messages.send_chat(tojid, message, msg.sender)36 if not reply and self.rosters.is_online(target):
52 msg.reply("The user was already online, so I just told them direct.")37 if message:
53 return38 # Send them the message now
5439 self.messages.send_chat(target, message, msg.sender)
55 cb = lambda: self.messages.send_chat(tojid, message, msg.sender)40 reply = target + " was already online, so I just told them direct."
5641 else:
57 self.rosters.register_presence_callback(user=tojid, callback=cb,42 reply = target + " is already online."
58 available=True)43
5944 if not reply:
60 msg.reply("Lying in wait for {} with message '{}'".format(tojid, message))45 if message:
6146 # When the target comes online send them message and the
47 # requestor the pounce_message
48 pounce_message = target + ' came online - message sent'
49 reply = ("I'll let you know when {} comes online and send "
50 "them the message.".format(target))
51 else:
52 pounce_message = target + ' came online.'
53 reply = "I'll let you know when {} comes online.".format(target)
54
55 def cb():
56 self.messages.send_chat(msg.sender, pounce_message)
57 if message:
58 self.messages.send_chat(target, message)
59
60 self.rosters.register_presence_callback(user=target, callback=cb,
61 available=True)
62
63 msg.reply(reply)
6264
63 @command(helphint="{user}")65 @command(helphint="{user}")
64 def stalk(self, msg, args):66 def stalk(self, msg, args):
65 cb = lambda: self.messages.send_chat(msg.sender,67 cb = lambda: self.messages.send_chat(msg.sender,
66 'User {} is offline'.format(args))68 'User {} is offline'.format(args))
67 self.rosters.register_presence_callback(user=args, callback=cb,69 self.rosters.register_presence_callback(user=args, callback=cb,
68 unavailable=True)70 unavailable=True)
6971
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))
7173
=== modified file 'src/endroid/plugins/sms/__init__.py'
--- src/endroid/plugins/sms/__init__.py 2014-09-12 12:22:44 +0000
+++ src/endroid/plugins/sms/__init__.py 2014-09-16 16:39:09 +0000
@@ -123,10 +123,8 @@
123123
124 # logging file stuff124 # logging file stuff
125 self.log = logging.getLogger(__name__)125 self.log = logging.getLogger(__name__)
126 try:126 logfile = os.path.expanduser(self.vars.get('logfile', '~/.endroid/sms.log'))
127 logfile = os.path.expanduser(self.vars['logfile'])127 self.log.info("Logging SMS activity to {}".format(logfile))
128 except KeyError:
129 logfile = os.path.expanduser('~/.endroid/sms.log')
130 handler = logging.FileHandler(logfile)128 handler = logging.FileHandler(logfile)
131 formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')129 formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
132 handler.setFormatter(formatter)130 handler.setFormatter(formatter)
@@ -245,7 +243,7 @@
245243
246 return users_blocked244 return users_blocked
247245
248 def _sms_reset_number_sent(self):246 def _sms_reset_number_sent(self, unused_params):
249 """Resets the number of texts sent in the database."""247 """Resets the number of texts sent in the database."""
250248
251 self.db.empty_table(DB_LIMIT_TABLE)249 self.db.empty_table(DB_LIMIT_TABLE)
252250
=== modified file 'src/endroid/plugins/trains.py'
--- src/endroid/plugins/trains.py 2014-08-12 10:28:57 +0000
+++ src/endroid/plugins/trains.py 2014-09-16 16:39:09 +0000
@@ -22,9 +22,6 @@
2222
23class TrainTimes(CommandPlugin):23class TrainTimes(CommandPlugin):
24 name = "traintimes"24 name = "traintimes"
25 help_topics = {
26 "": "When do trains leave?",
27 }
2825
29 def endroid_init(self):26 def endroid_init(self):
30 if not self.database.table_exists(STATION_TABLE):27 if not self.database.table_exists(STATION_TABLE):
@@ -32,6 +29,9 @@
32 if not self.database.table_exists(HOME_TABLE):29 if not self.database.table_exists(HOME_TABLE):
33 self.database.create_table(HOME_TABLE, ("jid", "station"))30 self.database.create_table(HOME_TABLE, ("jid", "station"))
3431
32 def help(self):
33 return "When do trains leave?"
34
35 def _station_update(self, msg, args, table, jid, display):35 def _station_update(self, msg, args, table, jid, display):
36 if not args:36 if not args:
37 rows = self.database.fetch(table, ("station",),37 rows = self.database.fetch(table, ("station",),
@@ -43,7 +43,7 @@
43 msg.reply_to_sender("You don't have a {} station set."43 msg.reply_to_sender("You don't have a {} station set."
44 .format(display))44 .format(display))
45 return45 return
46 self.database.delete(table, {"jid": msg.sender})46 self.database.delete(table, {"jid" : jid})
47 if args != "delete":47 if args != "delete":
48 self.database.insert(table, {"jid": jid, "station": args})48 self.database.insert(table, {"jid": jid, "station": args})
49 msg.reply_to_sender("Your new {} station is: {}"49 msg.reply_to_sender("Your new {} station is: {}"
@@ -54,11 +54,15 @@
5454
55 @command(helphint="{<station name>|delete}")55 @command(helphint="{<station name>|delete}")
56 def nearest_station(self, msg, args):56 def nearest_station(self, msg, args):
57 # Use msg.sender_full (with resource) because a person might
58 # be in different locations with different resources
57 self._station_update(msg, args, STATION_TABLE, msg.sender_full,59 self._station_update(msg, args, STATION_TABLE, msg.sender_full,
58 "nearest")60 "nearest")
5961
60 @command(helphint="{<station name>|delete}")62 @command(helphint="{<station name>|delete}")
61 def home_station(self, msg, args):63 def home_station(self, msg, args):
64 # Use msg.sender (no resource) because a person probably
65 # lives in the same place regardless of XMPP resource
62 self._station_update(msg, args, HOME_TABLE, msg.sender, "home")66 self._station_update(msg, args, HOME_TABLE, msg.sender, "home")
6367
64 @command(helphint="from <stn> to <stn> [[arriving|leaving] at <time>]",68 @command(helphint="from <stn> to <stn> [[arriving|leaving] at <time>]",
@@ -70,7 +74,10 @@
70 return74 return
7175
72 def extract_results(data):76 def extract_results(data):
73 results = RESULTRE.findall(ULRE.search(data).group(1))77 result = None
78 m = ULRE.search(data)
79 if m:
80 results = RESULTRE.findall(m.group(1))
74 if results:81 if results:
75 msg.reply(u"Trains from {} to {}: {}"82 msg.reply(u"Trains from {} to {}: {}"
76 .format(src, dst,83 .format(src, dst,
@@ -78,8 +85,12 @@
78 else:85 else:
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")
8087
81 home, src, dst, typ, when, time, _,_,_ = match.groups()88 home, src, dst, typ, when, raw_time, _,_,_ = match.groups()
82 time = self._canonical_time(time) if time is not None else time89 time = self._canonical_time(raw_time) if raw_time is not None else None
90 if raw_time and not time:
91 msg.reply("I can't understand the time you've entered can you try another way?")
92 return
93
83 if dst == "home" or home == "home":94 if dst == "home" or home == "home":
84 rows = self.database.fetch(HOME_TABLE, ("station",),95 rows = self.database.fetch(HOME_TABLE, ("station",),
85 {"jid": msg.sender})96 {"jid": msg.sender})
@@ -104,17 +115,22 @@
104 "a" if typ == "arriving" else "",115 "a" if typ == "arriving" else "",
105 ("/" + when.replace(" ", "-")) if when else "")116 ("/" + when.replace(" ", "-")) if when else "")
106 getPage("http://www.traintimes.org.uk" + urllib.quote(url)117 getPage("http://www.traintimes.org.uk" + urllib.quote(url)
107 ).addCallbacks(extract_results, msg.unhandled)118 ).addCallbacks(extract_results, lambda x: msg.reply("Bad train request"))
108119
109 @staticmethod120 @staticmethod
110 def _canonical_time(time):121 def _canonical_time(time):
111 match = TIMERE.match(time.strip())122 match = TIMERE.match(time.strip())
112 assert match, "We've already checked this - how can it fail?!"123 assert match, "We've already checked this - how can it fail?!"
113 hour, minute, half = match.groups()124 hour, minute, half = match.groups()
125 if len(hour) == 4 and not minute and not half:
126 # This is just a 4 digit number, treat as if its a time in 24h
127 minute = hour[2:]
128 hour = hour[:2]
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))
130 if hour > 24:
131 return None
132
115 if half and half == 'pm':133 if half and half == 'pm':
116 if hour <= 12:134 if hour <= 12:
117 hour += 12135 hour += 12
118 if not minute:
119 minute = 0
120 return "{}:{:02}".format(hour, minute)136 return "{}:{:02}".format(hour, minute)
121137
=== modified file 'src/endroid/usermanagement.py'
--- src/endroid/usermanagement.py 2014-08-22 12:28:28 +0000
+++ src/endroid/usermanagement.py 2014-09-16 16:39:09 +0000
@@ -480,9 +480,11 @@
480 :param unavailable: set True to have callback fire when user goes offline480 :param unavailable: set True to have callback fire when user goes offline
481 """481 """
482 if not (unavailable or available):482 if not (unavailable or available):
483 # let us know that our callback will never fire
484 raise ValueError('Callback specified neither available nor unavailable')483 raise ValueError('Callback specified neither available nor unavailable')
485484
485 if user not in self.users():
486 raise ValueError('Invalid user {}'.format(user))
487
486 if available:488 if available:
487 self._callbacks_when_available[user].append(callback)489 self._callbacks_when_available[user].append(callback)
488 if unavailable:490 if unavailable:
@@ -905,6 +907,9 @@
905 :param available: set True to have callback fire when user comes online907 :param available: set True to have callback fire when user comes online
906 :param unavailable: set True to have callback fire when user goes offline908 :param unavailable: set True to have callback fire when user goes offline
907 """909 """
910
911 if user not in self.users:
912 raise ValueError('Invalid user {}'.format(user))
908 self._usermanagement._register_presence_callback(user, callback,913 self._usermanagement._register_presence_callback(user, callback,
909 available=available,914 available=available,
910 unavailable=unavailable)915 unavailable=unavailable)

Subscribers

People subscribed via source and target branches

to all changes: