Script to grab stop codes from allstops.xml and sort
[ottawa-travel-planner.git] / mailPlanner.py
blob16fd5810df7e6f404ed493bb4b2c2b872fc1305b
1 #!/usr/bin/python
2 # vi: set softtabstop=4 shiftwidth=4 tabstop=8 expandtab:
4 import email.FeedParser
5 import email.iterators
6 from email.MIMEText import MIMEText
7 from email.Utils import parseaddr
8 import smtplib
9 import sys
10 import traceback
11 import syslog
12 import re
14 import Planner
15 import PlanTime
16 import time
17 import CommandParser
18 import ShortFormatter
20 FROM_ADDRESS = "oc@hurts.ca"
21 SMTP_SERVER = "127.0.0.1"
23 HELP_TEXT = """Examples:
24 31 antares to 81 florence
25 stop 6097 to bayshore at 830 pm
26 merivale and baseline to 6097 by 9"""
28 def logIt(string):
29 syslog.syslog(syslog.LOG_INFO, string)
31 def sendMessage(msg, to):
32 msg['From'] = FROM_ADDRESS
33 msg['To'] = to
35 s = smtplib.SMTP(SMTP_SERVER)
36 s.sendmail(parseaddr(FROM_ADDRESS)[1], [parseaddr(msg['To'])[1]],
37 msg.as_string())
38 s.close()
40 def sendError(e, replyto):
41 sendMessage(MIMEText(e.args[0]), replyto)
43 def sendHelp(replyto):
44 sendMessage(MIMEText(HELP_TEXT), replyto)
46 def findFirstLine(msg):
47 for line in msg.get_payload(None, True).splitlines():
48 s = line.strip()
49 if len(s) != 0:
50 return s
51 return None
53 def handleMessage(msg, replyto):
54 try:
55 s = None
57 # Look for a text/plain part.
58 for subpart in email.iterators.typed_subpart_iterator(msg, "text",
59 "plain"):
60 s = findFirstLine(subpart)
61 if s is not None:
62 break
63 else:
64 # No text/plain sub-parts, or the message body was empty
65 raise Exception("Your message was empty, or was not in plain "
66 + "text format.\n\n"
67 + "For help, send the word 'help' by itself.\n")
69 if s is None:
70 logIt("Empty command from %s" % replyto)
71 sendHelp(replyto)
72 return 0
74 logIt(s)
75 if s.lower().startswith("help"):
76 sendHelp(replyto)
77 return 0
79 cmd = CommandParser.CommandParser(s).cmd
81 # Leave in 3 minutes if time is unspecified.
82 if cmd.time is None:
83 cmd.time = PlanTime.PlanTime(time.time() + 180,
84 PlanTime.MUST_LEAVE_AFTER)
86 itin = Planner.plan(cmd.start, cmd.end, cmd.time)
87 sf = ShortFormatter.ShortFormatter(itin.entries)
89 text = "\n".join(sf.lines)
90 if itin.anyUnparsed():
91 text += "\nWarning: This itinerary display may be incomplete!"
93 logIt("success, %d entries" % len(itin.entries))
94 sendMessage(MIMEText(text), replyto)
95 except Exception, e:
96 logIt("Exception: %s" % e)
97 logIt(traceback.format_exc())
98 sendError(e, replyto)
100 # Always return success. Otherwise Postfix will send a bounce message.
101 return 0
103 def looksLikePostmaster(msg, replyto):
104 r = replyto.lower()
106 # from the vacation(1) manpage plus some extras
107 for s in ('-request@', 'postmaster', 'uucp', 'mailer-daemon', 'mailer',
108 '-relay', 'abuse@', 'mailerdaemon', 'majordomo', 'maildaemon'):
109 if r.find(s) != -1:
110 return True
112 p = msg["precedence"]
113 if p and p.lower() in ('bulk', 'junk', 'list'):
114 return True
116 return False
118 def main():
119 fp = email.FeedParser.FeedParser()
120 for line in sys.stdin:
121 fp.feed(line)
122 msg = fp.close()
124 # if these headers are here, we can reply semi-intelligently with errors
125 replyto = msg["reply-to"] or msg["from"]
126 if looksLikePostmaster(msg, replyto):
127 logIt("Skipping reply to postmaster address '%s'" % replyto)
128 return 0
130 return handleMessage(msg, replyto)
132 if __name__ == '__main__':
133 syslog.openlog(sys.argv[0], syslog.LOG_DAEMON)
134 sys.exit(main())