wrote fully fleshed out mailbot. has to be connected to ticketfrei.py #11

This commit is contained in:
b3yond 2018-01-05 17:13:41 +01:00
parent 5c98aa7677
commit a0ca940008
3 changed files with 129 additions and 51 deletions

2
.gitignore vendored
View file

@ -1,7 +1,9 @@
*.swp *.swp
*.pyc *.pyc
.idea/ .idea/
__pycache__/
last_mention last_mention
last_mail
ticketfrei.cfg ticketfrei.cfg
seen_toots.pickle seen_toots.pickle
seen_toots.pickle.part seen_toots.pickle.part

View file

@ -1,6 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import sendmail import sendmail
import ssl
import time
import trigger
import datetime import datetime
import email import email
import logger import logger
@ -8,13 +11,14 @@ import pytoml as toml
import imaplib import imaplib
import sys import sys
class Mailbot(object): class Mailbot(object):
""" """
Bot which sends Mails if mentioned via twitter/mastodon, and tells Bot which sends Mails if mentioned via twitter/mastodon, and tells
other bots that it received mails. other bots that it received mails.
""" """
def __init__(self, config, logger): def __init__(self, config, trigger, logger, history_path="last_mail"):
""" """
Creates a Bot who listens to mails and forwards them to other Creates a Bot who listens to mails and forwards them to other
bots. bots.
@ -22,19 +26,26 @@ class Mailbot(object):
:param config: (dictionary) config.toml as a dictionary of dictionaries :param config: (dictionary) config.toml as a dictionary of dictionaries
""" """
self.config = config self.config = config
self.trigger = trigger
self.logger = logger self.logger = logger
self.history_path = history_path
self.last_mail = self.get_history(self.history_path)
try: try:
self.mailinglist = self.config["mail"]["list"] self.mailinglist = self.config["mail"]["list"]
except KeyError: except KeyError:
self.mailinglist = None self.mailinglist = None
self.mailbox = imaplib.IMAP4_SSL(self.config["mail"]["imapserver"]) self.mailbox = imaplib.IMAP4_SSL(self.config["mail"]["imapserver"])
# context = ssl.create_default_context() context = ssl.create_default_context()
# print(self.mailbox.starttls(ssl_context=context)) # print is a debug
try: try:
rv, data = self.mailbox.login(self.config["mail"]["user"], self.mailbox.starttls(ssl_context=context)
self.config["mail"]["passphrase"]) except:
logmsg = logger.generate_tb(sys.exc_info())
logger.log(logmsg)
try:
self.mailbox.login(self.config["mail"]["user"], self.config["mail"]["passphrase"])
except imaplib.IMAP4.error: except imaplib.IMAP4.error:
logmsg = "Login to mail server failed." logmsg = "Login to mail server failed."
logmsg = logmsg + logger.generate_tb(sys.exc_info()) logmsg = logmsg + logger.generate_tb(sys.exc_info())
@ -47,53 +58,119 @@ class Mailbot(object):
:return: :return:
""" """
rv, data = self.mailbox.select("Inbox") rv, data = self.mailbox.select("Inbox")
msgs = []
if rv == 'OK': if rv == 'OK':
rv, data = self.mailbox.search(None, "ALL")
print(data)
rv, data = self.mailbox.search(None, "ALL") rv, data = self.mailbox.search(None, "ALL")
if rv != 'OK': if rv != 'OK':
print("No messages found!") return msgs
return
for num in data[0].split(): for num in data[0].split():
rv, data = self.mailbox.fetch(num, '(RFC822)') rv, data = self.mailbox.fetch(num, '(RFC822)')
if rv != 'OK': if rv != 'OK':
print("ERROR getting message", num) logmsg = "Didn't receive mail. Error: " + rv + str(data)
return self.logger.log(logmsg)
return msgs
msg = email.message_from_bytes(data[0][1]) msg = email.message_from_bytes(data[0][1])
hdr = email.header.make_header(email.header.decode_header(msg['Subject']))
subject = str(hdr)
print('Message %s: %s' % (num, subject))
print('Raw Date:', msg['Date'])
# Now convert to local date-time
date_tuple = email.utils.parsedate_tz(msg['Date'])
if date_tuple:
local_date = datetime.datetime.fromtimestamp(
email.utils.mktime_tz(date_tuple))
print ("Local Date:", local_date.strftime("%a, %d %b %Y %H:%M:%S"))
#print(msg.as_string())
author = msg.get("From") # get mail author from email header
text = msg.get_payload()
print(author)
print(text)
# :todo check if they match trigger
# :todo return a nice list of warning messages
def send_warning(self, statuses): # get a comparable date out of the email
date_tuple = email.utils.parsedate_tz(msg['Date'])
date_tuple = datetime.datetime.fromtimestamp(email.utils.mktime_tz(date_tuple))
date = (date_tuple - datetime.datetime(1970, 1, 1)).total_seconds()
if date > self.get_history(self.history_path):
self.last_mail = date
self.save_last_mail()
msgs.append(msg)
return msgs
def get_history(self, path):
""" This counter is needed to keep track of your mails, so you
don't double parse them
:param path: string: contains path to the file where the ID of the
last_mail is stored.
:return: last_mail: ID of the last mail the bot parsed
""" """
sends warnings by twitter & mastodon to a mailing list. try:
with open(path, "r+") as f:
last_mail = f.read()
except IOError:
with open(path, "w+") as f:
last_mail = "0"
f.write(last_mail)
return float(last_mail)
def save_last_mail(self):
""" Saves the last retweeted tweet in last_mention. """
with open(self.history_path, "w") as f:
f.write(str(self.last_mail))
def send_report(self, statuses):
"""
sends reports by twitter & mastodon to a mailing list.
:param statuses: (list) of status strings
""" """
for status in statuses: for status in statuses:
mailer = sendmail.Mailer(self.config) mailer = sendmail.Mailer(self.config)
mailer.send(status, self.mailinglist, "Warnung: Kontrolleure gesehen") mailer.send(status, self.mailinglist, "Warnung: Kontrolleure gesehen")
def to_social(self, msg):
"""
sends a report from the mailing list to social
:param msg: email.parser.Message object
:return: post: (string) of author + text
"""
# get a comparable date out of the email
date_tuple = email.utils.parsedate_tz(msg['Date'])
date_tuple = datetime.datetime.fromtimestamp(email.utils.mktime_tz(date_tuple))
date = (date_tuple-datetime.datetime(1970,1,1)).total_seconds()
author = msg.get("From") # get mail author from email header
# :todo take only the part before the @
text = msg.get_payload()
post = author + ": " + text
self.last_mail = date
self.save_last_mail()
return post
def flow(self, statuses):
"""
to be iterated
:param statuses: (list) of statuses to send to mailinglist
:return: list of statuses to post in mastodon & twitter
"""
self.send_report(statuses)
msgs = self.listen()
statuses = []
for msg in msgs:
if self.trigger.is_ok(msg.get_payload()):
statuses.append(self.to_social(msg))
return statuses
if __name__ == "__main__": if __name__ == "__main__":
# read config in TOML format (https://github.com/toml-lang/toml#toml) # read config in TOML format (https://github.com/toml-lang/toml#toml)
with open('config.toml') as configfile: with open('config.toml') as configfile:
config = toml.load(configfile) config = toml.load(configfile)
logger = logger.Logger(config) logger = logger.Logger(config)
trigger = trigger.Trigger(config)
m = Mailbot(config, logger) m = Mailbot(config, trigger, logger)
m.listen() statuses = []
try:
while 1:
print("Received Reports: " + str(m.flow(statuses)))
time.sleep(1)
except KeyboardInterrupt:
print("Good bye. Remember to restart the bot!")
except:
exc = sys.exc_info() # returns tuple [Exception type, Exception object, Traceback object]
message = logger.generate_tb(exc)
m.logger.log(message)
m.save_last_mail()
m.logger.shutdown(message)

View file

@ -22,14 +22,14 @@ class RetweetBot(object):
last_mention: the ID of the last tweet which mentioned you last_mention: the ID of the last tweet which mentioned you
""" """
def __init__(self, trigger, config, logger, historypath="last_mention"): def __init__(self, trigger, config, logger, history_path="last_mention"):
""" """
Initializes the bot and loads all the necessary data. Initializes the bot and loads all the necessary data.
:param trigger: object of the trigger :param trigger: object of the trigger
:param config: (dictionary) config.toml as a dictionary of dictionaries :param config: (dictionary) config.toml as a dictionary of dictionaries
:param logger: object of the logger :param logger: object of the logger
:param historypath: Path to the file with ID of the last retweeted :param history_path: Path to the file with ID of the last retweeted
Tweet Tweet
""" """
self.config = config self.config = config
@ -42,8 +42,8 @@ class RetweetBot(object):
keys[3]) # access_token_secret keys[3]) # access_token_secret
self.api = tweepy.API(auth) self.api = tweepy.API(auth)
self.historypath = historypath self.history_path = history_path
self.last_mention = self.get_history(self.historypath) self.last_mention = self.get_history(self.history_path)
self.trigger = trigger self.trigger = trigger
self.waitcounter = 0 self.waitcounter = 0
@ -87,7 +87,7 @@ class RetweetBot(object):
def save_last_mention(self): def save_last_mention(self):
""" Saves the last retweeted tweet in last_mention. """ """ Saves the last retweeted tweet in last_mention. """
with open(self.historypath, "w") as f: with open(self.history_path, "w") as f:
f.write(str(self.last_mention)) f.write(str(self.last_mention))
def waiting(self): def waiting(self):
@ -136,7 +136,7 @@ class RetweetBot(object):
logmsg = logmsg + self.logger.generate_tb(sys.exc_info()) logmsg = logmsg + self.logger.generate_tb(sys.exc_info())
self.logger.log(logmsg) self.logger.log(logmsg)
self.waitcounter += 10 self.waitcounter += 10
return None return []
def retweet(self, status): def retweet(self, status):
""" """
@ -200,19 +200,18 @@ class RetweetBot(object):
mentions = self.crawl_mentions() mentions = self.crawl_mentions()
mastodon = [] mastodon = []
if mentions is not None: for status in mentions:
for status in mentions: # Is the Text of the Tweet in the triggerlist?
# Is the Text of the Tweet in the triggerlist? if self.trigger.is_ok(status.text):
if self.trigger.is_ok(status.text): # Retweet status
# Retweet status toot = self.retweet(status)
toot = self.retweet(status) if toot:
if toot: mastodon.append(toot)
mastodon.append(toot)
# save the id so it doesn't get crawled again # save the id so it doesn't get crawled again
if status.id > self.last_mention: if status.id > self.last_mention:
self.last_mention = status.id self.last_mention = status.id
self.save_last_mention() self.save_last_mention()
# Return Retweets for tooting on mastodon # Return Retweets for tooting on mastodon
return mastodon return mastodon