Merge branch 'master' of https://github.com/b3yond/ticketfrei into multi-deployment
This commit is contained in:
commit
2b4d8650c9
|
@ -10,14 +10,14 @@ server = 'yourmastodoninstance' # Instance where you have your Mastodon account
|
||||||
[tapp]
|
[tapp]
|
||||||
# You get those keys when you follow these steps:
|
# You get those keys when you follow these steps:
|
||||||
# https://developer.twitter.com/en/docs/basics/authentication/guides/access-tokens
|
# https://developer.twitter.com/en/docs/basics/authentication/guides/access-tokens
|
||||||
consumer_key = "OD0CLn6twBxHjN2DqMkKuSvli"
|
consumer_key = "your_consumer_key"
|
||||||
consumer_secret = "XkvbViwjBWoWoJzIlseJLXmg2fqluq4HYqvwOwoSHGwxdTNi4l"
|
consumer_secret = "your_consumer_secret"
|
||||||
|
|
||||||
[tuser]
|
[tuser]
|
||||||
# You get those keys when you follow these steps:
|
# You get those keys when you follow these steps:
|
||||||
# https://developer.twitter.com/en/docs/basics/authentication/guides/access-tokens
|
# https://developer.twitter.com/en/docs/basics/authentication/guides/access-tokens
|
||||||
access_token_key = "876046057721008128-J35moxFXUvLb24MnaMVbVpqiEtxBlcc"
|
access_token_key = "your_access_token_key"
|
||||||
access_token_secret = "I7PQZMHuJDS5WslgUhqEeZbEWGhwLhmOetvwFoTn8YDKW"
|
access_token_secret = "your_acces_token_secret"
|
||||||
|
|
||||||
[mail]
|
[mail]
|
||||||
# This is the mail the bot uses to send emails.
|
# This is the mail the bot uses to send emails.
|
||||||
|
|
82
logger.py
82
logger.py
|
@ -1,82 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import os
|
|
||||||
import datetime
|
|
||||||
import traceback
|
|
||||||
import sys
|
|
||||||
import sendmail
|
|
||||||
|
|
||||||
|
|
||||||
class Logger(object):
|
|
||||||
"""
|
|
||||||
builds log files, writes the log messages.
|
|
||||||
If a critical error occurs, handles the bugtracking and error
|
|
||||||
messages.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, config):
|
|
||||||
"""
|
|
||||||
logs everything & sends bugtracking messages.
|
|
||||||
|
|
||||||
:param config: config file
|
|
||||||
"""
|
|
||||||
self.config = config
|
|
||||||
|
|
||||||
# initialize logging
|
|
||||||
if config["logging"]["logpath"]:
|
|
||||||
self.logpath = os.path.join(self.config["logging"]["logpath"], str(datetime.datetime.now()))
|
|
||||||
else:
|
|
||||||
self.logpath = os.path.join("logs", str(datetime.datetime.now()))
|
|
||||||
print("Path of logfile: " + self.logpath)
|
|
||||||
|
|
||||||
# intialize shutdown contact
|
|
||||||
try:
|
|
||||||
self.no_shutdown_contact = False
|
|
||||||
self.contact = self.config['mail']['contact']
|
|
||||||
except KeyError:
|
|
||||||
self.no_shutdown_contact = True
|
|
||||||
|
|
||||||
def log(self, message):
|
|
||||||
"""
|
|
||||||
Writing an error message & sometimes a traceback to a logfile in logs/
|
|
||||||
and prints it.
|
|
||||||
|
|
||||||
:param message: (string) Logger message to be displayed
|
|
||||||
"""
|
|
||||||
time = str(datetime.datetime.now())
|
|
||||||
line = "[" + time + "] " + message + "\n"
|
|
||||||
with open(self.logpath, 'a') as f:
|
|
||||||
try:
|
|
||||||
f.write(line)
|
|
||||||
except UnicodeEncodeError:
|
|
||||||
message = "Failed to save log message due to UTF-8 error. "
|
|
||||||
message = message + self.generate_tb(sys.exc_info())
|
|
||||||
self.log(message)
|
|
||||||
print(line, end="")
|
|
||||||
|
|
||||||
def generate_tb(self, exc):
|
|
||||||
tb = traceback.extract_tb(exc[2]) # returns StackSummary object
|
|
||||||
tb = "\n".join(tb.format()) # string of the actual traceback
|
|
||||||
message = ("Traceback (most recent call last):\n",
|
|
||||||
tb,
|
|
||||||
exc[0].__name__) # the type of the Exception
|
|
||||||
message = "".join(message) # concatenate to full traceback message
|
|
||||||
return message
|
|
||||||
|
|
||||||
def shutdown(self, tb):
|
|
||||||
""" If something breaks, it shuts down the bot and messages the owner.
|
|
||||||
|
|
||||||
:param tb: (string) traceback
|
|
||||||
"""
|
|
||||||
logmessage = "Shit went wrong, closing down.\n" + tb + "\n\n"
|
|
||||||
if self.no_shutdown_contact:
|
|
||||||
self.log(logmessage)
|
|
||||||
return
|
|
||||||
logmessage = logmessage + "Sending message to " + self.contact
|
|
||||||
self.log(logmessage)
|
|
||||||
try:
|
|
||||||
mailer = sendmail.Mailer(self.config)
|
|
||||||
mailer.send(tb, self.contact, "Ticketfrei Crash Report", attachment=self.logpath)
|
|
||||||
except:
|
|
||||||
self.log("Error while shutdown: " + self.generate_tb(sys.exc_info()))
|
|
||||||
print()
|
|
46
mailbot.py
46
mailbot.py
|
@ -6,10 +6,11 @@ import time
|
||||||
import trigger
|
import trigger
|
||||||
import datetime
|
import datetime
|
||||||
import email
|
import email
|
||||||
import logger
|
import logging
|
||||||
import pytoml as toml
|
import pytoml as toml
|
||||||
import imaplib
|
import imaplib
|
||||||
import sys
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Mailbot(object):
|
class Mailbot(object):
|
||||||
|
@ -18,7 +19,7 @@ class Mailbot(object):
|
||||||
other bots that it received mails.
|
other bots that it received mails.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config, trigger, logger, history_path="last_mail"):
|
def __init__(self, config, trigger, 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.
|
||||||
|
@ -27,7 +28,6 @@ class Mailbot(object):
|
||||||
"""
|
"""
|
||||||
self.config = config
|
self.config = config
|
||||||
self.trigger = trigger
|
self.trigger = trigger
|
||||||
self.logger = logger
|
|
||||||
|
|
||||||
self.history_path = history_path
|
self.history_path = history_path
|
||||||
self.last_mail = self.get_history(self.history_path)
|
self.last_mail = self.get_history(self.history_path)
|
||||||
|
@ -42,15 +42,18 @@ class Mailbot(object):
|
||||||
try:
|
try:
|
||||||
self.mailbox.starttls(ssl_context=context)
|
self.mailbox.starttls(ssl_context=context)
|
||||||
except:
|
except:
|
||||||
logmsg = logger.generate_tb(sys.exc_info())
|
logger.error('StartTLS failed', exc_info=True)
|
||||||
logger.log(logmsg)
|
|
||||||
try:
|
try:
|
||||||
self.mailbox.login(self.config["mail"]["user"], self.config["mail"]["passphrase"])
|
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."
|
logger.error("Login to mail server failed", exc_info=True)
|
||||||
logmsg = logmsg + logger.generate_tb(sys.exc_info())
|
try:
|
||||||
logger.log(logmsg)
|
mailer = sendmail.Mailer(config)
|
||||||
logger.shutdown(logmsg)
|
mailer.send('', config['mail']['contact'],
|
||||||
|
'Ticketfrei Crash Report',
|
||||||
|
attachment=config['logging']['logpath'])
|
||||||
|
except:
|
||||||
|
logger.error('Mail sending failed', exc_info=True)
|
||||||
|
|
||||||
def listen(self):
|
def listen(self):
|
||||||
"""
|
"""
|
||||||
|
@ -67,8 +70,7 @@ class Mailbot(object):
|
||||||
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':
|
||||||
logmsg = "Didn't receive mail. Error: " + rv + str(data)
|
logger.error("Couldn't fetch mail %s %s" % (rv, str(data)))
|
||||||
self.logger.log(logmsg)
|
|
||||||
return msgs
|
return msgs
|
||||||
msg = email.message_from_bytes(data[0][1])
|
msg = email.message_from_bytes(data[0][1])
|
||||||
|
|
||||||
|
@ -157,10 +159,12 @@ if __name__ == "__main__":
|
||||||
with open('config.toml') as configfile:
|
with open('config.toml') as configfile:
|
||||||
config = toml.load(configfile)
|
config = toml.load(configfile)
|
||||||
|
|
||||||
logger = logger.Logger(config)
|
fh = logging.FileHandler(config['logging']['logpath'])
|
||||||
trigger = trigger.Trigger(config)
|
fh.setLevel(logging.DEBUG)
|
||||||
|
logger.addHandler(fh)
|
||||||
|
|
||||||
m = Mailbot(config, trigger, logger)
|
trigger = trigger.Trigger(config)
|
||||||
|
m = Mailbot(config, trigger)
|
||||||
statuses = []
|
statuses = []
|
||||||
try:
|
try:
|
||||||
while 1:
|
while 1:
|
||||||
|
@ -169,8 +173,12 @@ if __name__ == "__main__":
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("Good bye. Remember to restart the bot!")
|
print("Good bye. Remember to restart the bot!")
|
||||||
except:
|
except:
|
||||||
exc = sys.exc_info() # returns tuple [Exception type, Exception object, Traceback object]
|
logger.error('Shutdown', exc_info=True)
|
||||||
message = logger.generate_tb(exc)
|
|
||||||
m.logger.log(message)
|
|
||||||
m.save_last_mail()
|
m.save_last_mail()
|
||||||
m.logger.shutdown(message)
|
try:
|
||||||
|
mailer = sendmail.Mailer(config)
|
||||||
|
mailer.send('', config['mail']['contact'],
|
||||||
|
'Ticketfrei Crash Report',
|
||||||
|
attachment=config['logging']['logpath'])
|
||||||
|
except:
|
||||||
|
logger.error('Mail sending failed', exc_info=True)
|
||||||
|
|
33
retootbot.py
33
retootbot.py
|
@ -7,12 +7,14 @@ import pickle
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
import trigger
|
import trigger
|
||||||
import logger
|
import logging
|
||||||
import sys
|
import sendmail
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class RetootBot(object):
|
class RetootBot(object):
|
||||||
def __init__(self, config, trigger, logger):
|
def __init__(self, config, trigger):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.trigger = trigger
|
self.trigger = trigger
|
||||||
self.client_id = self.register()
|
self.client_id = self.register()
|
||||||
|
@ -25,8 +27,6 @@ class RetootBot(object):
|
||||||
except IOError:
|
except IOError:
|
||||||
self.seen_toots = set()
|
self.seen_toots = set()
|
||||||
|
|
||||||
self.logger = logger
|
|
||||||
|
|
||||||
def register(self):
|
def register(self):
|
||||||
client_id = os.path.join(
|
client_id = os.path.join(
|
||||||
'appkeys',
|
'appkeys',
|
||||||
|
@ -68,8 +68,7 @@ class RetootBot(object):
|
||||||
notification['status']['content'])
|
notification['status']['content'])
|
||||||
if not self.trigger.is_ok(text_content):
|
if not self.trigger.is_ok(text_content):
|
||||||
continue
|
continue
|
||||||
self.logger.log('Boosting toot from %s: %s' % (
|
logger.info('Boosting toot from %s: %s' % (
|
||||||
# notification['status']['id'],
|
|
||||||
notification['status']['account']['acct'],
|
notification['status']['account']['acct'],
|
||||||
notification['status']['content']))
|
notification['status']['content']))
|
||||||
self.m.status_reblog(notification['status']['id'])
|
self.m.status_reblog(notification['status']['id'])
|
||||||
|
@ -92,10 +91,12 @@ if __name__ == '__main__':
|
||||||
with open('config.toml') as configfile:
|
with open('config.toml') as configfile:
|
||||||
config = toml.load(configfile)
|
config = toml.load(configfile)
|
||||||
|
|
||||||
trigger = trigger.Trigger(config)
|
fh = logging.FileHandler(config['logging']['logpath'])
|
||||||
logger = logger.Logger(config)
|
fh.setLevel(logging.DEBUG)
|
||||||
|
logger.addHandler(fh)
|
||||||
|
|
||||||
bot = RetootBot(config, trigger, logger)
|
trigger = trigger.Trigger(config)
|
||||||
|
bot = RetootBot(config, trigger)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
|
@ -104,7 +105,11 @@ if __name__ == '__main__':
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("Good bye. Remember to restart the bot!")
|
print("Good bye. Remember to restart the bot!")
|
||||||
except:
|
except:
|
||||||
exc = sys.exc_info() # returns tuple [Exception type, Exception object, Traceback object]
|
logger.error('Shutdown', exc_info=True)
|
||||||
message = logger.generate_tb(exc)
|
try:
|
||||||
bot.logger.log(message)
|
mailer = sendmail.Mailer(config)
|
||||||
bot.logger.shutdown(message)
|
mailer.send('', config['mail']['contact'],
|
||||||
|
'Ticketfrei Crash Report',
|
||||||
|
attachment=config['logging']['logpath'])
|
||||||
|
except:
|
||||||
|
logger.error('Mail sending failed', exc_info=True)
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import tweepy
|
import tweepy
|
||||||
import sys
|
|
||||||
import requests
|
import requests
|
||||||
import pytoml as toml
|
import pytoml as toml
|
||||||
import trigger
|
import trigger
|
||||||
from time import sleep
|
from time import sleep
|
||||||
import logger
|
import logging
|
||||||
|
import sendmail
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class RetweetBot(object):
|
class RetweetBot(object):
|
||||||
|
@ -22,7 +24,7 @@ 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, history_path="last_mention"):
|
def __init__(self, trigger, config, history_path="last_mention"):
|
||||||
"""
|
"""
|
||||||
Initializes the bot and loads all the necessary data.
|
Initializes the bot and loads all the necessary data.
|
||||||
|
|
||||||
|
@ -47,8 +49,6 @@ class RetweetBot(object):
|
||||||
self.trigger = trigger
|
self.trigger = trigger
|
||||||
self.waitcounter = 0
|
self.waitcounter = 0
|
||||||
|
|
||||||
self.logger = logger
|
|
||||||
|
|
||||||
def get_api_keys(self):
|
def get_api_keys(self):
|
||||||
"""
|
"""
|
||||||
How to get these keys is described in doc/twitter_api.md
|
How to get these keys is described in doc/twitter_api.md
|
||||||
|
@ -127,14 +127,10 @@ class RetweetBot(object):
|
||||||
mentions = self.api.mentions_timeline(since_id=self.last_mention)
|
mentions = self.api.mentions_timeline(since_id=self.last_mention)
|
||||||
return mentions
|
return mentions
|
||||||
except tweepy.RateLimitError:
|
except tweepy.RateLimitError:
|
||||||
logmsg = "Twitter API Error: Rate Limit Exceeded."
|
logger.error("Twitter API Error: Rate Limit Exceeded", exc_info=True)
|
||||||
logmsg = logmsg + self.logger.generate_tb(sys.exc_info())
|
|
||||||
self.logger.log(logmsg)
|
|
||||||
self.waitcounter += 60*15 + 1
|
self.waitcounter += 60*15 + 1
|
||||||
except requests.exceptions.ConnectionError:
|
except requests.exceptions.ConnectionError:
|
||||||
logmsg = "Twitter API Error: Bad Connection."
|
logger.error("Twitter API Error: Bad Connection", exc_info=True)
|
||||||
logmsg = logmsg + self.logger.generate_tb(sys.exc_info())
|
|
||||||
self.logger.log(logmsg)
|
|
||||||
self.waitcounter += 10
|
self.waitcounter += 10
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
@ -148,21 +144,16 @@ class RetweetBot(object):
|
||||||
while 1:
|
while 1:
|
||||||
try:
|
try:
|
||||||
self.api.retweet(status.id)
|
self.api.retweet(status.id)
|
||||||
self.logger.log("Retweeted: " + self.format_mastodon(status))
|
logger.info("Retweeted: " + self.format_mastodon(status))
|
||||||
if status.id > self.last_mention:
|
if status.id > self.last_mention:
|
||||||
self.last_mention = status.id
|
self.last_mention = status.id
|
||||||
return self.format_mastodon(status)
|
return self.format_mastodon(status)
|
||||||
except requests.exceptions.ConnectionError:
|
except requests.exceptions.ConnectionError:
|
||||||
logmsg = "Twitter API Error: Bad Connection."
|
logger.error("Twitter API Error: Bad Connection", exc_info=True)
|
||||||
logmsg = logmsg + self.logger.generate_tb(sys.exc_info())
|
|
||||||
self.logger.log(logmsg)
|
|
||||||
sleep(10)
|
sleep(10)
|
||||||
# maybe one day we get rid of this error:
|
# maybe one day we get rid of this error:
|
||||||
except tweepy.TweepError:
|
except tweepy.TweepError:
|
||||||
logmsg = "Twitter Error."
|
logger.error("Twitter Error", exc_info=True)
|
||||||
logmsg = logmsg + self.logger.generate_tb(sys.exc_info())
|
|
||||||
self.logger.log(logmsg)
|
|
||||||
# self.log.log("Twitter API Error: You probably already retweeted this tweet: " + status.text)
|
|
||||||
if status.id > self.last_mention:
|
if status.id > self.last_mention:
|
||||||
self.last_mention = status.id
|
self.last_mention = status.id
|
||||||
return None
|
return None
|
||||||
|
@ -180,9 +171,7 @@ class RetweetBot(object):
|
||||||
self.api.update_status(status=post)
|
self.api.update_status(status=post)
|
||||||
return
|
return
|
||||||
except requests.exceptions.ConnectionError:
|
except requests.exceptions.ConnectionError:
|
||||||
logmsg = "Twitter API Error: Bad Connection."
|
logger.error("Twitter API Error: Bad Connection", exc_info=True)
|
||||||
logmsg = logmsg + self.logger.generate_tb(sys.exc_info())
|
|
||||||
self.logger.log(logmsg)
|
|
||||||
sleep(10)
|
sleep(10)
|
||||||
|
|
||||||
def flow(self, to_tweet=()):
|
def flow(self, to_tweet=()):
|
||||||
|
@ -221,10 +210,12 @@ if __name__ == "__main__":
|
||||||
with open('config.toml') as configfile:
|
with open('config.toml') as configfile:
|
||||||
config = toml.load(configfile)
|
config = toml.load(configfile)
|
||||||
|
|
||||||
trigger = trigger.Trigger(config)
|
fh = logging.FileHandler(config['logging']['logpath'])
|
||||||
logger = logger.Logger(config)
|
fh.setLevel(logging.DEBUG)
|
||||||
|
logger.addHandler(fh)
|
||||||
|
|
||||||
bot = RetweetBot(trigger, config, logger)
|
trigger = trigger.Trigger(config)
|
||||||
|
bot = RetweetBot(trigger, config)
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
bot.flow()
|
bot.flow()
|
||||||
|
@ -232,8 +223,12 @@ if __name__ == "__main__":
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("Good bye. Remember to restart the bot!")
|
print("Good bye. Remember to restart the bot!")
|
||||||
except:
|
except:
|
||||||
exc = sys.exc_info() # returns tuple [Exception type, Exception object, Traceback object]
|
logger.error('Shutdown', exc_info=True)
|
||||||
message = logger.generate_tb(exc)
|
|
||||||
bot.logger.log(message)
|
|
||||||
bot.save_last_mention()
|
bot.save_last_mention()
|
||||||
bot.logger.shutdown(message)
|
try:
|
||||||
|
mailer = sendmail.Mailer(config)
|
||||||
|
mailer.send('', config['mail']['contact'],
|
||||||
|
'Ticketfrei Crash Report',
|
||||||
|
attachment=config['logging']['logpath'])
|
||||||
|
except:
|
||||||
|
logger.error('Mail sending failed', exc_info=True)
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import logging
|
||||||
import pytoml as toml
|
import pytoml as toml
|
||||||
import logger
|
|
||||||
import time
|
import time
|
||||||
import sys
|
import sendmail
|
||||||
|
|
||||||
from retootbot import RetootBot
|
from retootbot import RetootBot
|
||||||
from retweetbot import RetweetBot
|
from retweetbot import RetweetBot
|
||||||
|
@ -15,12 +15,14 @@ if __name__ == '__main__':
|
||||||
with open('config.toml') as configfile:
|
with open('config.toml') as configfile:
|
||||||
config = toml.load(configfile)
|
config = toml.load(configfile)
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
fh = logging.FileHandler(config['logging']['logpath'])
|
||||||
|
fh.setLevel(logging.DEBUG)
|
||||||
|
logger.addHandler(fh)
|
||||||
|
|
||||||
trigger = Trigger(config)
|
trigger = Trigger(config)
|
||||||
|
mbot = RetootBot(config, trigger)
|
||||||
logger = logger.Logger(config)
|
tbot = RetweetBot(trigger, config)
|
||||||
|
|
||||||
mbot = RetootBot(config, trigger, logger)
|
|
||||||
tbot = RetweetBot(trigger, config, logger)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
statuses = []
|
statuses = []
|
||||||
|
@ -31,8 +33,12 @@ if __name__ == '__main__':
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("Good bye. Remember to restart the bot!")
|
print("Good bye. Remember to restart the bot!")
|
||||||
except:
|
except:
|
||||||
exc = sys.exc_info() # returns tuple [Exception type, Exception object, Traceback object]
|
logger.error('Shutdown', exc_info=True)
|
||||||
message = logger.generate_tb(exc)
|
|
||||||
tbot.logger.log(message)
|
|
||||||
tbot.save_last_mention()
|
tbot.save_last_mention()
|
||||||
tbot.logger.shutdown(message)
|
try:
|
||||||
|
mailer = sendmail.Mailer(config)
|
||||||
|
mailer.send('', config['mail']['contact'],
|
||||||
|
'Ticketfrei Crash Report',
|
||||||
|
attachment=config['logging']['logpath'])
|
||||||
|
except:
|
||||||
|
logger.error('Mail sending failed', exc_info=True)
|
||||||
|
|
Loading…
Reference in a new issue