forked from ticketfrei/ticketfrei
wrote fully fleshed out mailbot. has to be connected to ticketfrei.py #11
This commit is contained in:
parent
5c98aa7677
commit
a0ca940008
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -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
|
||||||
|
|
143
mailbot.py
143
mailbot.py
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue