From 29a577508fe7ca489b71f1e55b67f20a3fa8ca96 Mon Sep 17 00:00:00 2001 From: sid Date: Fri, 25 May 2018 02:38:27 +0200 Subject: [PATCH 01/33] added basic telegram backend support --- active_bots/telegrambot.py | 30 ++++++++ db.py | 15 ++++ user.py | 136 +++++++++++++++++++++++++++---------- 3 files changed, 144 insertions(+), 37 deletions(-) create mode 100644 active_bots/telegrambot.py diff --git a/active_bots/telegrambot.py b/active_bots/telegrambot.py new file mode 100644 index 0000000..64c5849 --- /dev/null +++ b/active_bots/telegrambot.py @@ -0,0 +1,30 @@ +from bot import Bot +import logging +from report import Report +from twx.botapi import TelegramBot + + +logger = logging.getLogger(__name__) + + +class TelegramBot(Bot): + def crawl(self, user): + tb = TelegramBot(user.get_telegram_credentials()) + updates = tb.get_updates().wait() + reports = [] + for update in updates: + reports.append(Report(update.message.from.username,self, + update.message.text,None,update.message.date)) + return reports + + def post(self, user, report): + tb = TelegramBot(user.get_telegram_credentials()) + text = report.text + if len(text) > 4096: + text = text[:4096 - 4] + u' ...' + try: + for subscriber_id in user.get_telegram_subscribers(): + tb.send_message(subscriber_id, text).wait() + except Exception: + logger.error('Error telegramming: ' + user.get_city() + ': ' + + report.id, exc_info=True) diff --git a/db.py b/db.py index abafec1..e72dac5 100644 --- a/db.py +++ b/db.py @@ -108,6 +108,21 @@ class DB(object): FOREIGN KEY(twitter_accounts_id) REFERENCES twitter_accounts(id) ); + CREATE TABLE IF NOT EXISTS telegram_accounts ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, + user_id INTEGER, + api_token TEXT, + active INTEGER, + FOREIGN KEY(user_id) REFERENCES user(id) + ); + CREATE TABLE IF NOT EXISTS telegram_subscribers ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, + user_id INTEGER, + subscriber_id INTEGER, + active INTEGER, + FOREIGN KEY(user_id) REFERENCES user(id), + UNIQUE(user_id, subscriber_id) ON CONFLICT IGNORE + ); CREATE TABLE IF NOT EXISTS mailinglist ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, user_id INTEGER, diff --git a/user.py b/user.py index 40f915a..b28a370 100644 --- a/user.py +++ b/user.py @@ -13,7 +13,7 @@ class User(object): self.uid = uid def check_password(self, password): - db.execute("SELECT passhash FROM user WHERE id=?;", (self.uid, )) + db.execute("SELECT passhash FROM user WHERE id=?;", (self.uid,)) passhash, = db.cur.fetchone() return scrypt_mcf_check(passhash.encode('ascii'), password.encode('utf-8')) @@ -23,11 +23,12 @@ class User(object): db.execute("UPDATE user SET passhash=? WHERE id=?;", (passhash, self.uid)) db.commit() + password = property(None, password) # setter only, can't read back @property def enabled(self): - db.execute("SELECT enabled FROM user WHERE user_id=?;", (self.uid, )) + db.execute("SELECT enabled FROM user WHERE user_id=?;", (self.uid,)) return bool(db.cur.fetchone()[0]) @enabled.setter @@ -38,11 +39,11 @@ class User(object): @property def emails(self): - db.execute("SELECT email FROM email WHERE user_id=?;", (self.uid, )) - return (*db.cur.fetchall(), ) + db.execute("SELECT email FROM email WHERE user_id=?;", (self.uid,)) + return (*db.cur.fetchall(),) def delete_email(self, email): - db.execute("SELECT COUNT(*) FROM email WHERE user_id=?", (self.uid, )) + db.execute("SELECT COUNT(*) FROM email WHERE user_id=?", (self.uid,)) if db.cur.fetchone()[0] == 1: return False # don't allow to delete last email db.execute("DELETE FROM email WHERE user_id=? AND email=?;", @@ -52,13 +53,13 @@ class User(object): def email_token(self, email): return jwt.encode({ - 'email': email, - 'uid': self.uid - }, db.secret).decode('ascii') + 'email': email, + 'uid': self.uid + }, db.secret).decode('ascii') def is_appropriate(self, report): db.execute("SELECT pattern FROM triggerpatterns WHERE user_id=?;", - (self.uid, )) + (self.uid,)) for pattern, in db.cur.fetchall(): if pattern.search(report.text) is not None: break @@ -66,19 +67,60 @@ class User(object): # no pattern matched return False db.execute("SELECT word FROM badwords WHERE user_id=?;", - (self.uid, )) + (self.uid,)) badwords = [word.lower() for word, in db.cur.fetchall()] for word in report.text.lower().split(): if word in badwords: return False return True - def get_masto_credentials(self): - db.execute("SELECT access_token, instance_id FROM mastodon_accounts WHERE user_id = ? AND active = 1;", - (self.uid, )) + def get_telegram_credentials(self): + db.execute("""SELECT api_token + FROM telegram_accounts + WHERE user_id = ? AND active = 1;""", + (self.uid,)) row = db.cur.fetchone() - db.execute("SELECT instance, client_id, client_secret FROM mastodon_instances WHERE id = ?;", - (row[1], )) + return row[0] + + def save_telegram_credentials(self, api_token): + db.execute("""INSERT INTO telegram_accounts ( + user_id, api_token, active) VALUES(?, ?, 1);""", + (self.uid, api_token)) + db.commit() + + + def get_telegram_subscribers(self): + db.execute("""SELECT subscriber_id + FROM telegram_subscribers + WHERE user_id = ? AND active = 1;""", + (self.uid,)) + rows = db.cur.fetchall() + return rows + + def add_telegram_subscribers(self, subscriber_id): + db.execute("""INSERT INTO telegram_subscribers ( + user_id, subscriber_id) VALUES(?, ?);""", + (self.uid, subscriber_id)) + db.commit() + + def remove_telegram_subscribers(self, subscriber_id): + db.execute("""DELETE + FROM telegram_subscribers + WHERE user_id = ? + AND subscriber_id = ?;""", + (self.uid, subscriber_id)) + db.commit() + + def get_masto_credentials(self): + db.execute("""SELECT access_token, instance_id + FROM mastodon_accounts + WHERE user_id = ? AND active = 1;""", + (self.uid,)) + row = db.cur.fetchone() + db.execute("""SELECT instance, client_id, client_secret + FROM mastodon_instances + WHERE id = ?;""", + (row[1],)) instance = db.cur.fetchone() return instance[1], instance[2], row[0], instance[0] @@ -92,7 +134,7 @@ class User(object): def get_seen_toot(self): db.execute("SELECT toot_id FROM seen_toots WHERE user_id = ?;", - (self.uid, )) + (self.uid,)) return db.cur.fetchone()[0] def save_seen_toot(self, toot_id): @@ -101,7 +143,7 @@ class User(object): def get_seen_tweet(self): db.execute("SELECT tweet_id FROM seen_tweets WHERE user_id = ?;", - (self.uid, )) + (self.uid,)) return db.cur.fetchone()[0] def save_seen_tweet(self, tweet_id): @@ -110,7 +152,7 @@ class User(object): def get_seen_dm(self): db.execute("SELECT message_id FROM seen_dms WHERE user_id = ?;", - (self.uid, )) + (self.uid,)) return db.cur.fetchone() def save_seen_dm(self, tweet_id): @@ -118,11 +160,14 @@ class User(object): (tweet_id, self.uid)) def get_mailinglist(self): - db.execute("SELECT email FROM mailinglist WHERE user_id = ? AND active = 1;", (self.uid, )) + db.execute("""SELECT email + FROM mailinglist + WHERE user_id = ? AND active = 1;""", (self.uid,)) return db.cur.fetchone()[0] def get_seen_mail(self): - db.execute("SELECT mail_date FROM seen_mails WHERE user_id = ?;", (self.uid, )) + db.execute("""SELECT mail_date + FROM seen_mails WHERE user_id = ?;""", (self.uid,)) return db.cur.fetchone()[0] def save_seen_mail(self, mail_date): @@ -130,38 +175,50 @@ class User(object): (mail_date, self.uid)) def get_trigger_words(self, table): - db.execute("SELECT words FROM ? WHERE user_id = ?;", (table, self.uid,)) + db.execute("""SELECT words + FROM ? WHERE user_id = ?;""", (table, self.uid,)) return db.cur.fetchone()[0] def state(self): return dict(foo='bar') def save_request_token(self, token): - db.execute("INSERT INTO twitter_request_tokens(user_id, request_token, request_token_secret) VALUES(?, ?, ?);", - (self.uid, token["oauth_token"], token["oauth_token_secret"])) + db.execute("""INSERT INTO + twitter_request_tokens( + user_id, request_token, request_token_secret + ) VALUES(?, ?, ?);""", + (self.uid, token["oauth_token"], + token["oauth_token_secret"])) db.commit() def get_request_token(self): - db.execute("SELECT request_token, request_token_secret FROM twitter_request_tokens WHERE user_id = ?;", (self.uid,)) + db.execute("""SELECT request_token, request_token_secret + FROM twitter_request_tokens + WHERE user_id = ?;""", (self.uid,)) request_token = db.cur.fetchone() - db.execute("DELETE FROM twitter_request_tokens WHERE user_id = ?;", (self.uid,)) + db.execute("""DELETE FROM twitter_request_tokens + WHERE user_id = ?;""", (self.uid,)) db.commit() - return {"oauth_token" : request_token[0], - "oauth_token_secret" : request_token[1]} + return {"oauth_token": request_token[0], + "oauth_token_secret": request_token[1]} def save_twitter_token(self, access_token, access_token_secret): - db.execute( - "INSERT INTO twitter_accounts(user_id, client_id, client_secret) VALUES(?, ?, ?);", - (self.uid, access_token, access_token_secret)) + db.execute(""""INSERT INTO twitter_accounts( + user_id, client_id, client_secret + ) VALUES(?, ?, ?);""", + (self.uid, access_token, access_token_secret)) db.commit() def get_twitter_token(self): - db.execute("SELECT access_token, access_token_secret FROM twitter_accouts WHERE user_id = ?;", - (self.uid, )) + db.execute("""SELECT access_token, access_token_secret + FROM twitter_accouts WHERE user_id = ?;""", + (self.uid,)) return db.cur.fetchall() def get_mastodon_app_keys(self, instance): - db.execute("SELECT client_id, client_secret FROM mastodon_instances WHERE instance = ?;", (instance, )) + db.execute("""SELECT client_id, client_secret + FROM mastodon_instances + WHERE instance = ?;""", (instance,)) try: row = db.cur.fetchone() client_id = row[0] @@ -169,19 +226,24 @@ class User(object): return client_id, client_secret except TypeError: app_name = "ticketfrei" + str(db.secret)[0:4] - client_id, client_secret = Mastodon.create_app(app_name, api_base_url=instance) - db.execute("INSERT INTO mastodon_instances(instance, client_id, client_secret) VALUES(?, ?, ?);", + client_id, client_secret \ + = Mastodon.create_app(app_name, api_base_url=instance) + db.execute("""INSERT INTO mastodon_instances( + instance, client_id, client_secret + ) VALUES(?, ?, ?);""", (instance, client_id, client_secret)) db.commit() return client_id, client_secret def save_masto_token(self, access_token, instance): - db.execute("SELECT id FROM mastodon_instances WHERE instance = ?;", (instance, )) + db.execute("""SELECT id + FROM mastodon_instances + WHERE instance = ?;""", (instance,)) instance_id = db.cur.fetchone()[0] db.execute("INSERT INTO mastodon_accounts(user_id, access_token, instance_id, active) " "VALUES(?, ?, ?, ?);", (self.uid, access_token, instance_id, 1)) db.commit() def get_city(self): - db.execute("SELECT city FROM user WHERE id == ?;", (self.uid, )) + db.execute("SELECT city FROM user WHERE id == ?;", (self.uid,)) return db.cur.fetchone()[0] From a48ba9ebf83c157ad568816b3de45597030f6815 Mon Sep 17 00:00:00 2001 From: sid <25916907+git-sid@users.noreply.github.com> Date: Tue, 29 May 2018 07:07:15 +0200 Subject: [PATCH 02/33] added /start, /stop, /help command check & small fixes --- active_bots/telegrambot.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/active_bots/telegrambot.py b/active_bots/telegrambot.py index 64c5849..4534047 100644 --- a/active_bots/telegrambot.py +++ b/active_bots/telegrambot.py @@ -1,7 +1,7 @@ from bot import Bot import logging from report import Report -from twx.botapi import TelegramBot +from twx.botapi import TelegramBot as Telegram logger = logging.getLogger(__name__) @@ -9,16 +9,27 @@ logger = logging.getLogger(__name__) class TelegramBot(Bot): def crawl(self, user): - tb = TelegramBot(user.get_telegram_credentials()) + tb = Telegram(user.get_telegram_credentials()) updates = tb.get_updates().wait() reports = [] for update in updates: - reports.append(Report(update.message.from.username,self, - update.message.text,None,update.message.date)) + if update.message.text.lower() == "/start": + user.add_telegram_subscribers(update.message.from.id) + tb.send_message(update.message.from.id, "You are now subscribed to report notifications.") #TODO: /start message should be set in frontend + elif update.message.text.lower() == "/stop": + user.remove_telegram_subscribers(update.message.from.id) + tb.send_message(update.message.from.id, "You are now unsubscribed from report notifications.") #TODO: /stop message should be set in frontend + elif update.message.text.lower() == "/help": + tb.send_message(update.message.from.id, "Send reports here to share them with other users. Use /start and /stop to be included/excluded.") #TODO: /help message should be set in frontend + else: + for row in user.get_telegram_subscribers(): + if update.message.from.id == row[0]: + reports.append(Report(update.message.from.username, self, update.message.text, None, update.message.date)) + break return reports def post(self, user, report): - tb = TelegramBot(user.get_telegram_credentials()) + tb = Telegram(user.get_telegram_credentials()) text = report.text if len(text) > 4096: text = text[:4096 - 4] + u' ...' From a8504971ea3720f1ce279611af69afbab212ec10 Mon Sep 17 00:00:00 2001 From: sid <25916907+git-sid@users.noreply.github.com> Date: Sat, 30 Jun 2018 21:32:22 +0200 Subject: [PATCH 03/33] completed telegram subscriber list functionality --- active_bots/telegrambot.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/active_bots/telegrambot.py b/active_bots/telegrambot.py index 64c5849..87733ae 100644 --- a/active_bots/telegrambot.py +++ b/active_bots/telegrambot.py @@ -13,8 +13,12 @@ class TelegramBot(Bot): updates = tb.get_updates().wait() reports = [] for update in updates: - reports.append(Report(update.message.from.username,self, - update.message.text,None,update.message.date)) + if update.message.text =="/stop": + remove_telegram_subscribers(self, update.message.from.id) + else: + add_telegram_subscribers(self, update.message.from.id) + reports.append(Report(update.message.from.username,self, + update.message.text,None,update.message.date)) return reports def post(self, user, report): From ef0ce8f9f11d72d5c309ba70bf1ad82d3dd18f00 Mon Sep 17 00:00:00 2001 From: b3yond Date: Wed, 8 Aug 2018 10:55:51 +0200 Subject: [PATCH 04/33] Advertising mail notifications on the city page --- template/settings.tpl | 11 +++++++++-- user.py | 5 +++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/template/settings.tpl b/template/settings.tpl index 371d66e..56a4c5d 100644 --- a/template/settings.tpl +++ b/template/settings.tpl @@ -89,8 +89,15 @@

With your bot, we generated you a page, which you can use for promotion: Ticketfrei {{city}} You can change what your users will read there, and adjust it to your - needs. You should definitely adjust the Social Media profile links. This is just the default text we - suggest: + needs. +

+

+ You should definitely adjust the Social Media profile links. + Also consider adding this link to the text: Link to the mail subscription page. + Your readers can use this to subscribe to mail notifications. +

+

+ So this is the default text we suggest:

diff --git a/user.py b/user.py index 232a754..fb592be 100644 --- a/user.py +++ b/user.py @@ -278,6 +278,11 @@ also pass trotzdem auf, wer auf dem Bahnsteig steht. Aber je mehr Leute mitmachen, desto eher kannst du dir sicher sein, dass wir sie finden, bevor sie uns finden. +Wenn du immer direkt gewarnt werden willst, kannst du auch die +E-Mail-Benachrichtigungen aktivieren. Gib einfach +hier deine +E-Mail-Adresse an. + Also, wenn du weniger Glück hast, und der erste bist, der einen Kontrolleur sieht: From ec399db2eb3e14daeb0a5c15f845efb51b74ed16 Mon Sep 17 00:00:00 2001 From: b3yond Date: Wed, 8 Aug 2018 14:04:31 +0200 Subject: [PATCH 05/33] started to build a mailbot implementing bot.py --- active_bots/mailbot.py | 60 ++++++++++++++++++++++++++++++++++++++++++ report.py | 2 +- user.py | 2 +- 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 active_bots/mailbot.py diff --git a/active_bots/mailbot.py b/active_bots/mailbot.py new file mode 100644 index 0000000..172d701 --- /dev/null +++ b/active_bots/mailbot.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 + +import logging +import sendmail +import datetime +import email +import report +from bot import Bot + + +logger = logging.getLogger(__name__) + + +class Mailbot(Bot): + + # returns a list of Report objects + def crawl(self, user): + reports = [] + mails = [] # todo: look if new reports are in mailbox + for msg in mails: + if msg.date > user.get_seen_mail(): + reports.append(make_report(msg, user)) + return reports + + # post/boost Report object + def post(self, user, report): + recipients = user.get_mailinglist() + for rec in recipients: + rec = rec[0] + unsubscribe_link = "" # todo: generate unsubscribe link + body = report.text + unsubscribe_link + if report.author != rec: + try: + sendmail.sendmail(rec, "Ticketfrei " + user.get_city() + + " Report", body=body) + except Exception: + logger.error("Sending Mail failed.", exc_info=True) + + +def make_report(msg, user): + """ + generates a report out of a mail + + :param msg: email.parser.Message object + :return: post: report.Report object + """ + # 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 = report.Report(author, "mail", text, None, date) + user.save_seen_mail(date) + return post diff --git a/report.py b/report.py index aa34fed..5cf2c13 100644 --- a/report.py +++ b/report.py @@ -19,7 +19,7 @@ class Report(object): :param timestamp: time of the report """ self.author = author - self.type = source + self.source = source self.text = text self.timestamp = timestamp self.id = id diff --git a/user.py b/user.py index fb592be..faf535c 100644 --- a/user.py +++ b/user.py @@ -141,7 +141,7 @@ schlitz def get_mailinglist(self): db.execute("SELECT email FROM mailinglist WHERE user_id = ? AND active = 1;", (self.uid, )) - return db.cur.fetchone()[0] + return db.cur.fetchall() def get_seen_mail(self): db.execute("SELECT mail_date FROM seen_mails WHERE user_id = ?;", (self.uid, )) From 57a2e4dcb19ff5d20ee003abb26d2d42b181ff77 Mon Sep 17 00:00:00 2001 From: b3yond Date: Wed, 8 Aug 2018 17:09:26 +0200 Subject: [PATCH 06/33] the mailbot can now receive messages from /var/mail/test --- active_bots/mailbot.py | 30 ++++++++++++++++++++---------- active_bots/mastodonbot.py | 5 ++++- active_bots/twitterDMs.py | 5 ++++- active_bots/twitterbot.py | 6 +++++- db.py | 8 ++++++++ user.py | 10 +++++----- 6 files changed, 46 insertions(+), 18 deletions(-) diff --git a/active_bots/mailbot.py b/active_bots/mailbot.py index 172d701..6efe9f4 100644 --- a/active_bots/mailbot.py +++ b/active_bots/mailbot.py @@ -3,6 +3,7 @@ import logging import sendmail import datetime +import mailbox import email import report from bot import Bot @@ -16,9 +17,9 @@ class Mailbot(Bot): # returns a list of Report objects def crawl(self, user): reports = [] - mails = [] # todo: look if new reports are in mailbox + mails = mailbox.mbox('/var/mail/test') # todo: adjust to actual mailbox file for msg in mails: - if msg.date > user.get_seen_mail(): + if get_date_from_header(msg['Date']) > user.get_seen_mail(): reports.append(make_report(msg, user)) return reports @@ -29,10 +30,11 @@ class Mailbot(Bot): rec = rec[0] unsubscribe_link = "" # todo: generate unsubscribe link body = report.text + unsubscribe_link + print(body) if report.author != rec: try: sendmail.sendmail(rec, "Ticketfrei " + user.get_city() + - " Report", body=body) + " Report", body=body) except Exception: logger.error("Sending Mail failed.", exc_info=True) @@ -45,16 +47,24 @@ def make_report(msg, user): :return: post: report.Report object """ # 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() + date = get_date_from_header(msg['Date']) - author = msg.get("From") # get mail author from email header - # :todo take only the part before the @ + author = msg['From'] # get mail author from email header + # :todo take only the part in between the < > text = msg.get_payload() post = report.Report(author, "mail", text, None, date) user.save_seen_mail(date) return post + + +def get_date_from_header(header): + """ + :param header: msg['Date'] + :return: float: total seconds + """ + date_tuple = email.utils.parsedate_tz(header) + date_tuple = datetime.datetime.fromtimestamp( + email.utils.mktime_tz(date_tuple) + ) + return (date_tuple - datetime.datetime(1970, 1, 1)).total_seconds() diff --git a/active_bots/mastodonbot.py b/active_bots/mastodonbot.py index 1007110..a7f735b 100755 --- a/active_bots/mastodonbot.py +++ b/active_bots/mastodonbot.py @@ -53,7 +53,10 @@ class MastodonBot(Bot): return mentions def post(self, user, report): - m = Mastodon(*user.get_masto_credentials()) + try: + m = Mastodon(*user.get_masto_credentials()) + except TypeError: + return # no mastodon account for this user. if report.source == self: try: m.status_reblog(report.id) diff --git a/active_bots/twitterDMs.py b/active_bots/twitterDMs.py index deb1a8b..5d40c6e 100644 --- a/active_bots/twitterDMs.py +++ b/active_bots/twitterDMs.py @@ -27,7 +27,10 @@ class TwitterBot(Bot): :return: reports: (list of report.Report objects) """ reports = [] - api = self.get_api(user) + try: + api = self.get_api(user) + except IndexError: + return reports # no twitter account for this user. last_dm = user.get_seen_dm() try: if last_dm == None: diff --git a/active_bots/twitterbot.py b/active_bots/twitterbot.py index 696ed56..b5160fd 100755 --- a/active_bots/twitterbot.py +++ b/active_bots/twitterbot.py @@ -12,6 +12,7 @@ logger = logging.getLogger(__name__) class TwitterBot(Bot): + def get_api(self, user): keys = user.get_twitter_credentials() auth = tweepy.OAuthHandler(consumer_key=keys[0], @@ -60,7 +61,10 @@ class TwitterBot(Bot): return [] def post(self, user, report): - api = self.get_api(user) + try: + api = self.get_api(user) + except IndexError: + return # no twitter account for this user. try: if report.source == self: api.retweet(report.id) diff --git a/db.py b/db.py index 03f59a6..6e545fc 100644 --- a/db.py +++ b/db.py @@ -122,6 +122,12 @@ class DB(object): active INTEGER, FOREIGN KEY(user_id) REFERENCES user(id) ); + CREATE TABLE IF NOT EXISTS seen_mail ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, + user_id INTEGER, + mail_date REAL, + FOREIGN KEY(user_id) REFERENCES user(id) + ); CREATE TABLE IF NOT EXISTS cities ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, user_id INTEGER, @@ -211,6 +217,8 @@ u\d\d? active) VALUES(?, ?, ?);""", (uid, "", 1)) self.commit() user = User(uid) + self.execute("INSERT INTO seen_mail (user_id, mail_date) VALUES (?,?)", + (uid, 0)) user.set_city(city) return user diff --git a/user.py b/user.py index faf535c..d061799 100644 --- a/user.py +++ b/user.py @@ -57,11 +57,11 @@ class User(object): }, db.secret).decode('ascii') def is_appropriate(self, report): - db.execute("SELECT pattern FROM triggerpatterns WHERE user_id=?;", + db.execute("SELECT patterns FROM triggerpatterns WHERE user_id=?;", (self.uid, )) - patterns = db.cur.fetchone() + patterns = db.cur.fetchone()[0] for pattern in patterns.splitlines(): - if pattern.search(report.text) is not None: + if pattern in report.text: break else: # no pattern matched @@ -81,7 +81,7 @@ nigger neger schlitz """ - db.execute("SELECT word FROM badwords WHERE user_id=?;", + db.execute("SELECT words FROM badwords WHERE user_id=?;", (self.uid, )) badwords = db.cur.fetchone() for word in report.text.lower().splitlines(): @@ -144,7 +144,7 @@ schlitz return db.cur.fetchall() def get_seen_mail(self): - db.execute("SELECT mail_date FROM seen_mails WHERE user_id = ?;", (self.uid, )) + db.execute("SELECT mail_date FROM seen_mail WHERE user_id = ?;", (self.uid, )) return db.cur.fetchone()[0] def save_seen_mail(self, mail_date): From 372e0612a62f25e4189cb267e5f80f5547fc5ab3 Mon Sep 17 00:00:00 2001 From: b3yond Date: Thu, 9 Aug 2018 15:01:51 +0200 Subject: [PATCH 07/33] wrote unsubscribe function, but BE & FE have different secrets --- active_bots/mailbot.py | 8 +++++--- db.py | 9 ++++++++- frontend.py | 11 +++++++++-- user.py | 4 ++++ 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/active_bots/mailbot.py b/active_bots/mailbot.py index 6efe9f4..dc03bb3 100644 --- a/active_bots/mailbot.py +++ b/active_bots/mailbot.py @@ -7,7 +7,8 @@ import mailbox import email import report from bot import Bot - +from config import config +from db import db logger = logging.getLogger(__name__) @@ -28,8 +29,9 @@ class Mailbot(Bot): recipients = user.get_mailinglist() for rec in recipients: rec = rec[0] - unsubscribe_link = "" # todo: generate unsubscribe link - body = report.text + unsubscribe_link + unsubscribe_text = "\n_______\nYou don't want to receive those messages? Unsubscribe with this link: " + body = report.text + unsubscribe_text + config['web']['host'] + "/city/mail/unsubscribe/" \ + + db.mail_subscription_token(rec, user.get_city()) print(body) if report.author != rec: try: diff --git a/db.py b/db.py index 6e545fc..17ade92 100644 --- a/db.py +++ b/db.py @@ -165,12 +165,19 @@ class DB(object): :param city: string :return: a token with an encoded json dict { email: x, city: y } """ - return jwt.encode({ + token = jwt.encode({ 'email': email, 'city': city }, self.secret).decode('ascii') + print("mail_subscription_token") + print(token) + print(self.secret) + return token def confirm_subscription(self, token): + print("confirm_subscription") + print(token) + print(self.secret) json = jwt.decode(token, self.secret) return json['email'], json['city'] diff --git a/frontend.py b/frontend.py index 980fae1..79a0a4b 100755 --- a/frontend.py +++ b/frontend.py @@ -109,13 +109,20 @@ def subscribe_mail(city): @view('template/city.tpl') def confirm_subscribe(token): email, city = db.confirm_subscription(token) - print(email) # debug - print(city) # debug user = db.by_city(city) user.add_subscriber(email) redirect('/city/' + city) +@get('/city/mail/unsubscribe/') +@view('template/mail.tpl') +def unsubscribe(token): + email, city = db.confirm_subscription(token) + user = db.by_city(city) + user.remove_subscriber(email) + redirect('/city/' + city) + + @get('/settings') @view('template/settings.tpl') def settings(user): diff --git a/user.py b/user.py index d061799..0d6ab0b 100644 --- a/user.py +++ b/user.py @@ -166,6 +166,10 @@ schlitz db.execute("INSERT INTO mailinglist(user_id, email, active) VALUES(?, ?, ?);", (self.uid, email, 1)) db.commit() + def remove_subscriber(self, email): + db.execute("UPDATE mailinglist SET active = 0 WHERE email = ? AND user_id = ?;", (email, self.uid)) + db.commit() + def set_badwords(self, words): db.execute("UPDATE badwords SET words = ? WHERE user_id = ?;", (words, self.uid)) From c7aa87cb3bcfcd77522c73f1e62fddfbdd2f440b Mon Sep 17 00:00:00 2001 From: b3yond Date: Sat, 8 Sep 2018 09:33:40 +0200 Subject: [PATCH 08/33] BE & FE store secret in DB. Unsubscribing works --- active_bots/mailbot.py | 5 +++-- db.py | 30 +++++++++++++++++++++++------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/active_bots/mailbot.py b/active_bots/mailbot.py index dc03bb3..40285ed 100644 --- a/active_bots/mailbot.py +++ b/active_bots/mailbot.py @@ -18,7 +18,7 @@ class Mailbot(Bot): # returns a list of Report objects def crawl(self, user): reports = [] - mails = mailbox.mbox('/var/mail/test') # todo: adjust to actual mailbox file + mails = mailbox.mbox('/var/mail/test') # todo: adjust to actual mailbox for msg in mails: if get_date_from_header(msg['Date']) > user.get_seen_mail(): reports.append(make_report(msg, user)) @@ -27,12 +27,13 @@ class Mailbot(Bot): # post/boost Report object def post(self, user, report): recipients = user.get_mailinglist() + print(recipients) # debug for rec in recipients: rec = rec[0] unsubscribe_text = "\n_______\nYou don't want to receive those messages? Unsubscribe with this link: " body = report.text + unsubscribe_text + config['web']['host'] + "/city/mail/unsubscribe/" \ + db.mail_subscription_token(rec, user.get_city()) - print(body) + print(body) # debug if report.author != rec: try: sendmail.sendmail(rec, "Ticketfrei " + user.get_city() + diff --git a/db.py b/db.py index 17ade92..d8f1e53 100644 --- a/db.py +++ b/db.py @@ -14,7 +14,7 @@ class DB(object): self.conn = sqlite3.connect(dbfile) self.cur = self.conn.cursor() self.create() - self.secret = urandom(32) + self.secret = self.get_secret() def execute(self, *args, **kwargs): return self.cur.execute(*args, **kwargs) @@ -138,8 +138,30 @@ class DB(object): FOREIGN KEY(user_id) REFERENCES user(id), UNIQUE(user_id, city) ON CONFLICT IGNORE ); + CREATE TABLE IF NOT EXISTS secret ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, + secret BLOB + ); ''') + def get_secret(self): + """ + At __init__(), the db needs a secret. It tries to fetch it from the db, + and if it fails, it generates a new one. + + :return: + """ + # select only the newest secret. should be only one row anyway. + self.execute("SELECT secret FROM secret ORDER BY id DESC LIMIT 1") + try: + return self.cur.fetchone()[0] + except TypeError: + new_secret = urandom(32) + self.execute("INSERT INTO secret (secret) VALUES (?);", + (new_secret, )) + self.commit() + return new_secret + def user_token(self, email, password): """ This function is called by the register confirmation process. It wants @@ -169,15 +191,9 @@ class DB(object): 'email': email, 'city': city }, self.secret).decode('ascii') - print("mail_subscription_token") - print(token) - print(self.secret) return token def confirm_subscription(self, token): - print("confirm_subscription") - print(token) - print(self.secret) json = jwt.decode(token, self.secret) return json['email'], json['city'] From 96329e968e3f3d37cff6376da8856d573d6354e6 Mon Sep 17 00:00:00 2001 From: b3yond Date: Sat, 8 Sep 2018 11:14:00 +0200 Subject: [PATCH 09/33] finished #23 code. rw city page info display. Unsubscribe = Delete --- active_bots/mailbot.py | 2 -- db.py | 7 ++++--- frontend.py | 29 +++++++++++++++++++---------- template/city.tpl | 8 ++++++++ template/mail.tpl | 9 +++++---- template/propaganda.tpl | 26 +++++++++++++++++--------- template/settings.tpl | 40 ++++++++++++++++++++++++++++++---------- user.py | 37 +++++++++++++++++++++++++++++++------ 8 files changed, 114 insertions(+), 44 deletions(-) diff --git a/active_bots/mailbot.py b/active_bots/mailbot.py index 40285ed..f51fb26 100644 --- a/active_bots/mailbot.py +++ b/active_bots/mailbot.py @@ -27,13 +27,11 @@ class Mailbot(Bot): # post/boost Report object def post(self, user, report): recipients = user.get_mailinglist() - print(recipients) # debug for rec in recipients: rec = rec[0] unsubscribe_text = "\n_______\nYou don't want to receive those messages? Unsubscribe with this link: " body = report.text + unsubscribe_text + config['web']['host'] + "/city/mail/unsubscribe/" \ + db.mail_subscription_token(rec, user.get_city()) - print(body) # debug if report.author != rec: try: sendmail.sendmail(rec, "Ticketfrei " + user.get_city() + diff --git a/db.py b/db.py index d8f1e53..c379a0f 100644 --- a/db.py +++ b/db.py @@ -119,7 +119,6 @@ class DB(object): id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, user_id INTEGER, email TEXT, - active INTEGER, FOREIGN KEY(user_id) REFERENCES user(id) ); CREATE TABLE IF NOT EXISTS seen_mail ( @@ -133,6 +132,7 @@ class DB(object): user_id INTEGER, city TEXT, markdown TEXT, + mail_md TEXT, masto_link TEXT, twit_link TEXT, FOREIGN KEY(user_id) REFERENCES user(id), @@ -264,13 +264,14 @@ u\d\d? return User(uid) def user_facing_properties(self, city): - self.execute("""SELECT city, markdown, masto_link, twit_link + self.execute("""SELECT city, markdown, mail_md, masto_link, twit_link FROM cities WHERE city=?;""", (city, )) try: - city, markdown, masto_link, twit_link = self.cur.fetchone() + city, markdown, mail_md, masto_link, twit_link = self.cur.fetchone() return dict(city=city, markdown=markdown, + mail_md=mail_md, masto_link=masto_link, twit_link=twit_link, mailinglist=city + "@" + config["web"]["host"]) diff --git a/frontend.py b/frontend.py index 79a0a4b..d3bf26f 100755 --- a/frontend.py +++ b/frontend.py @@ -9,6 +9,7 @@ from sendmail import sendmail from session import SessionPlugin from mastodon import Mastodon + def url(route): return '%s://%s/%s' % ( request.urlparts.scheme, @@ -78,13 +79,14 @@ def login_post(): @get('/city/') -@view('template/city.tpl') -def city_page(city): +def city_page(city, info=None): citydict = db.user_facing_properties(city) if citydict is not None: - return citydict - redirect('/') - return dict(info='There is no Ticketfrei bot in your city yet. Create one yourself!') + citydict['info'] = info + return bottle.template('template/city.tpl', **citydict) + return bottle.template('template/propaganda.tpl', + **dict(info='There is no Ticketfrei bot in your city' + ' yet. Create one yourself!')) @get('/city/mail/') @@ -102,25 +104,26 @@ def subscribe_mail(city): print(confirm_link) # only for local testing # send mail with code to email sendmail(email, "Subscribe to Ticketfrei " + city + " Mail Notifications", - body="To subscribe to the mail notifications for Ticketfrei " + city + ", click on this link: " + token) + body="To subscribe to the mail notifications for Ticketfrei " + + city + ", click on this link: " + token) + return city_page(city, info="Thanks! You will receive a confirmation mail.") @get('/city/mail/confirm/') -@view('template/city.tpl') def confirm_subscribe(token): email, city = db.confirm_subscription(token) user = db.by_city(city) user.add_subscriber(email) - redirect('/city/' + city) + return city_page(city, info="Thanks for subscribing to mail notifications!") @get('/city/mail/unsubscribe/') -@view('template/mail.tpl') def unsubscribe(token): email, city = db.confirm_subscription(token) user = db.by_city(city) user.remove_subscriber(email) - redirect('/city/' + city) + return city_page(city, info="You successfully unsubscribed " + email + + " from the mail notifications.") @get('/settings') @@ -136,6 +139,12 @@ def update_markdown(user): return user.state() +@post('/settings/mail_md') +@view('template/settings.tpl') +def update_mail_md(user): + user.set_mail_md(request.forms['mail_md']) + return user.state() + @post('/settings/goodlist') @view('template/settings.tpl') def update_trigger_patterns(user): diff --git a/template/city.tpl b/template/city.tpl index 7953c52..5613168 100644 --- a/template/city.tpl +++ b/template/city.tpl @@ -6,4 +6,12 @@ import markdown as md html = md.markdown(markdown) %> +% if info is not None: +
+
+

{{!info}}

+
+
+% end + {{!html}} diff --git a/template/mail.tpl b/template/mail.tpl index 66c0e86..58b58e9 100644 --- a/template/mail.tpl +++ b/template/mail.tpl @@ -3,13 +3,14 @@ <% import markdown as md -html = md.markdown(markdown) +html = md.markdown(mail_md) %> +{{!html}} + - - -{{!html}} +
+

Back to Ticketfrei {{!city}} overview

diff --git a/template/propaganda.tpl b/template/propaganda.tpl index 4a3b034..0aafcbf 100644 --- a/template/propaganda.tpl +++ b/template/propaganda.tpl @@ -1,4 +1,12 @@ % rebase('template/wrapper.tpl') +% if defined('info'): +
+
+

{{!info}}

+
+
+
+% end % include('template/login-plain.tpl')

Features

@@ -42,17 +50,17 @@ reclaim public transportation.

- On short term we want to do this by helping users to avoid - controllers and fines - on long term by pressuring public - transportation companies to offer their services free of - charge, financed by the public. + On short term we want to do this by helping users to avoid + controllers and fines - on long term by pressuring public + transportation companies to offer their services free of + charge, financed by the public.

- Because with Ticketfrei you're able to use trains and - subways for free anyway. Take part and create a new + Because with Ticketfrei you're able to use trains and + subways for free anyway. Take part and create a new understanding of what public transportation should look like!

- - - + + + diff --git a/template/settings.tpl b/template/settings.tpl index 56a4c5d..acc2b5e 100644 --- a/template/settings.tpl +++ b/template/settings.tpl @@ -87,14 +87,16 @@

Edit your city page

- With your bot, we generated you a page, which you can use for promotion: Ticketfrei {{city}} You can change what your users will read there, and adjust it to your + With your bot, we generated you a page, which you can use for promotion: + Ticketfrei {{city}} You + can change what your users will read there, and adjust it to your needs.

You should definitely adjust the Social Media profile links. - Also consider adding this link to the text: Link to the mail subscription page. - Your readers can use this to subscribe to mail notifications. + Also consider adding this link to the text: Link to the mail subscription page. Your readers + can use this to subscribe to mail notifications.

So this is the default text we suggest: @@ -105,12 +107,30 @@

+
+

Edit your mail subscription page

+

+ There is also a page where users can subscribe to mail notifications: + Ticketfrei {{city}}. + You can change what your users will read there, and adjust it to your + needs. +

+

+ So this is the default text we suggest: +

+
+ + +
+
+

Edit your trigger patterns

- These words have to be contained in a report. - If none of these expressions is in the report, it will be ignored by the bot. - You can use the defaults, or enter some expressions specific to your city and language. + These words have to be contained in a report. If none of these + expressions is in the report, it will be ignored by the bot. You can + use the defaults, or enter some expressions specific to your city and + language.

@@ -122,9 +142,9 @@

Edit the blacklist

- These words are not allowed in reports. - If you encounter spam, you can add more here - the bot will ignore reports which use such words. - + These words are not allowed in reports. If you encounter spam, you can + add more here - the bot will ignore reports which use such words. + diff --git a/user.py b/user.py index 0d6ab0b..c5be91c 100644 --- a/user.py +++ b/user.py @@ -140,7 +140,7 @@ schlitz db.commit() def get_mailinglist(self): - db.execute("SELECT email FROM mailinglist WHERE user_id = ? AND active = 1;", (self.uid, )) + db.execute("SELECT email FROM mailinglist WHERE user_id = ?;", (self.uid, )) return db.cur.fetchall() def get_seen_mail(self): @@ -163,11 +163,11 @@ schlitz return db.cur.fetchone()[0] def add_subscriber(self, email): - db.execute("INSERT INTO mailinglist(user_id, email, active) VALUES(?, ?, ?);", (self.uid, email, 1)) + db.execute("INSERT INTO mailinglist(user_id, email) VALUES(?, ?);", (self.uid, email)) db.commit() def remove_subscriber(self, email): - db.execute("UPDATE mailinglist SET active = 0 WHERE email = ? AND user_id = ?;", (email, self.uid)) + db.execute("DELETE FROM mailinglist WHERE email = ? AND user_id = ?;", (email, self.uid)) db.commit() def set_badwords(self, words): @@ -184,6 +184,7 @@ schlitz # necessary: # - city # - markdown + # - mail_md # - goodlist # - blacklist # - logged in with twitter? @@ -192,6 +193,7 @@ schlitz citydict = db.user_facing_properties(self.get_city()) return dict(city=citydict['city'], markdown=citydict['markdown'], + mail_md=citydict['mail_md'], triggerwords=self.get_trigger_words(), badwords=self.get_badwords(), enabled=self.enabled) @@ -251,6 +253,11 @@ schlitz (markdown, self.uid)) db.commit() + def set_mail_md(self, mail_md): + db.execute("UPDATE cities SET mail_md = ? WHERE user_id = ?;", + (mail_md, self.uid)) + db.commit() + def get_city(self): db.execute("SELECT city FROM cities WHERE user_id == ?;", (self.uid, )) return db.cur.fetchone()[0] @@ -351,7 +358,25 @@ sicher vor Zensur. Um Mastodon zu benutzen, besucht diese Seite: [https://joinmastodon.org/](https://joinmastodon.org/) """ - db.execute("""INSERT INTO cities(user_id, city, markdown, masto_link, - twit_link) VALUES(?,?,?,?,?)""", - (self.uid, city, markdown, masto_link, twit_link)) + mail_md = """# Immer up-to-date + +Du bist viel unterwegs und hast keine Lust, jedes Mal auf das Profil des Bots +zu schauen? Kein Problem. Unsere Mail Notifications benachrichtigen dich, wenn +irgendwo Kontis gesehen werden. + +Wenn du uns deine E-Mail-Adresse gibst, kriegst du bei jedem Konti-Report eine +Mail. Wenn du eine Mail-App auf dem Handy hast, so wie +[K9Mail](https://k9mail.github.io/), kriegst du sogar eine Push Notification. So +bist du immer Up-to-date über alles, was im Verkehrsnetz passiert. + +## Keine Sorge + +Wir benutzen deine E-Mail-Adresse selbstverständlich für nichts anderes. Du +kannst die Benachrichtigungen jederzeit deaktivieren, mit jeder Mail wird ein +unsubscribe-link mitgeschickt. + """ + db.execute("""INSERT INTO cities(user_id, city, markdown, mail_md, + masto_link, twit_link) VALUES(?,?,?,?,?,?)""", + (self.uid, city, markdown, mail_md, masto_link, twit_link)) db.commit() + From 7ca904564ccf885f1fb5a171d17a459f9a5e2764 Mon Sep 17 00:00:00 2001 From: b3yond Date: Sat, 8 Sep 2018 16:06:25 +0200 Subject: [PATCH 10/33] Port is configurable now --- config.toml.example | 1 + frontend.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/config.toml.example b/config.toml.example index ec7a14b..dca90a7 100644 --- a/config.toml.example +++ b/config.toml.example @@ -6,6 +6,7 @@ consumer_secret = "your_consumer_secret" [web] host = "0.0.0.0" # will be used by bottle as a host. +port = 80 contact = "b3yond@riseup.net" [mail] diff --git a/frontend.py b/frontend.py index d3bf26f..6133ba1 100755 --- a/frontend.py +++ b/frontend.py @@ -264,6 +264,6 @@ bottle.install(SessionPlugin('/')) if __name__ == '__main__': # testing only - bottle.run(host='localhost', port=8080) + bottle.run(host='localhost', port=config["web"]["port"]) else: application.catchall = False From b5f6854a1c1f5650fc29119e75438d1126bce22b Mon Sep 17 00:00:00 2001 From: b3yond Date: Sat, 8 Sep 2018 16:31:02 +0200 Subject: [PATCH 11/33] host is configurable now --- frontend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend.py b/frontend.py index 6133ba1..82eebf8 100755 --- a/frontend.py +++ b/frontend.py @@ -264,6 +264,6 @@ bottle.install(SessionPlugin('/')) if __name__ == '__main__': # testing only - bottle.run(host='localhost', port=config["web"]["port"]) + bottle.run(host=config["web"]["host"], port=config["web"]["port"]) else: application.catchall = False From b85360b0a84b3f39ae1e75eceb2be184c974c6b7 Mon Sep 17 00:00:00 2001 From: git-sid <25916907+git-sid@users.noreply.github.com> Date: Sun, 9 Sep 2018 14:26:46 +0200 Subject: [PATCH 12/33] added twx dependency to README.md instruction --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 46f5110..da364b7 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ virtualenv -p python3 . Install the dependencies: ```shell -pip install tweepy pytoml Mastodon.py bottle pyjwt pylibscrypt Markdown +pip install tweepy pytoml Mastodon.py bottle pyjwt pylibscrypt Markdown twx ``` Configure the bot: @@ -179,7 +179,7 @@ virtualenv -p python3 . Install the dependencies: ```shell -pip install tweepy pytoml Mastodon.py bottle pyjwt pylibscrypt Markdown +pip install tweepy pytoml Mastodon.py bottle pyjwt pylibscrypt Markdown twx ``` Configure the bot: From 40c834020a92cf009e72e88bb1068e852a2e5ce5 Mon Sep 17 00:00:00 2001 From: b3yond Date: Sun, 9 Sep 2018 14:57:40 +0200 Subject: [PATCH 13/33] logging confirmation links for debug purposes --- active_bots/telegrambot.py | 6 +++--- frontend.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/active_bots/telegrambot.py b/active_bots/telegrambot.py index a52f197..bd833f0 100644 --- a/active_bots/telegrambot.py +++ b/active_bots/telegrambot.py @@ -16,17 +16,17 @@ class TelegramBot(Bot): if update.message.text.lower() == "/start": user.add_telegram_subscribers(update.message.sender.id) tb.send_message(update.message.sender.id, "You are now \ - subscribed to report notifications.") + subscribed to report notifications.") #TODO: /start message should be set in frontend elif update.message.text.lower() == "/stop": user.remove_telegram_subscribers(update.message.sender.id) tb.send_message(update.message.sender.id, "You are now \ - unsubscribed from report notifications.") + unsubscribed from report notifications.") #TODO: /stop message should be set in frontend elif update.message.text.lower() == "/help": tb.send_message(update.message.sender.id, "Send reports here to \ share them with other users. Use /start and /stop to \ - be included/excluded.") + be included/excluded.") #TODO: /help message should be set in frontend else: reports.append(Report(update.message.sender.username, self, diff --git a/frontend.py b/frontend.py index 82eebf8..cb948ce 100755 --- a/frontend.py +++ b/frontend.py @@ -39,13 +39,13 @@ def register_post(): return dict(error='Email address already in use.') # send confirmation mail try: - print(url('confirm/' + city + '/%s' % db.user_token(email, password))) # only for local testing + link = url('confirm/' + city + '/%s' % db.user_token(email, password)) + print(link) # only for local testing + logger.info('confirmation link to ' + email + ": " + link) sendmail( email, "Confirm your account", - "Complete your registration here: %s" % ( - url('confirm/' + city + '/%s' % db.user_token(email, password)) - ) + "Complete your registration here: %s" % (link) ) return dict(info='Confirmation mail sent.') except Exception: From f1d7215ebaa3eb3c9a6d0ea9a67754c0d3fb4ebf Mon Sep 17 00:00:00 2001 From: b3yond Date: Sun, 9 Sep 2018 15:09:40 +0200 Subject: [PATCH 14/33] debug level was not enough --- frontend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend.py b/frontend.py index cb948ce..b57288e 100755 --- a/frontend.py +++ b/frontend.py @@ -41,7 +41,7 @@ def register_post(): try: link = url('confirm/' + city + '/%s' % db.user_token(email, password)) print(link) # only for local testing - logger.info('confirmation link to ' + email + ": " + link) + logger.error('confirmation link to ' + email + ": " + link) sendmail( email, "Confirm your account", From fd8b29c55fb1a41cf6fb0dce0ca4931d01ec5a8b Mon Sep 17 00:00:00 2001 From: b3yond Date: Sun, 9 Sep 2018 15:45:25 +0200 Subject: [PATCH 15/33] appropriate success message --- frontend.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frontend.py b/frontend.py index b57288e..e6c3b91 100755 --- a/frontend.py +++ b/frontend.py @@ -246,9 +246,7 @@ def login_mastodon(user): try: access_token = m.log_in(masto_email, masto_pass) user.save_masto_token(access_token, instance_url) - return dict( - info='Thanks for supporting decentralized social networks!' - ) + city_page(user.get_city(), info='Thanks for supporting decentralized social networks!') except Exception: logger.error('Login to Mastodon failed.', exc_info=True) return dict(error='Login to Mastodon failed.') From 439dbeb1fa24fce9e51e3e3fdbd03ffa83338f98 Mon Sep 17 00:00:00 2001 From: b3yond Date: Sun, 9 Sep 2018 16:58:07 +0200 Subject: [PATCH 16/33] telegram troubleshooting and fine-tuning --- active_bots/telegrambot.py | 36 ++++++++++++++++++------------------ db.py | 1 - frontend.py | 5 ++--- user.py | 11 ++--------- 4 files changed, 22 insertions(+), 31 deletions(-) diff --git a/active_bots/telegrambot.py b/active_bots/telegrambot.py index bd833f0..ab62fa5 100644 --- a/active_bots/telegrambot.py +++ b/active_bots/telegrambot.py @@ -13,24 +13,24 @@ class TelegramBot(Bot): updates = tb.get_updates().wait() reports = [] for update in updates: - if update.message.text.lower() == "/start": - user.add_telegram_subscribers(update.message.sender.id) - tb.send_message(update.message.sender.id, "You are now \ - subscribed to report notifications.") - #TODO: /start message should be set in frontend - elif update.message.text.lower() == "/stop": - user.remove_telegram_subscribers(update.message.sender.id) - tb.send_message(update.message.sender.id, "You are now \ - unsubscribed from report notifications.") - #TODO: /stop message should be set in frontend - elif update.message.text.lower() == "/help": - tb.send_message(update.message.sender.id, "Send reports here to \ - share them with other users. Use /start and /stop to \ - be included/excluded.") - #TODO: /help message should be set in frontend - else: - reports.append(Report(update.message.sender.username, self, - update.message.text, None, update.message.date)) + try: + if update.message.text.lower() == "/start": + user.add_telegram_subscribers(update.message.sender.id) + tb.send_message(update.message.sender.id, "You are now subscribed to report notifications.") + # TODO: /start message should be set in frontend + elif update.message.text.lower() == "/stop": + user.remove_telegram_subscribers(update.message.sender.id) + tb.send_message(update.message.sender.id, "You are now unsubscribed from report notifications.") + # TODO: /stop message should be set in frontend + elif update.message.text.lower() == "/help": + tb.send_message(update.message.sender.id, "Send reports here to share them with other users. Use /start and /stop to be included/excluded.") + # TODO: /help message should be set in frontend + else: + reports.append(Report(update.message.sender.username, self, + update.message.text, None, update.message.date)) + except AttributeError: + print(updates[0], updates[1]) # Telegram API returns an Error + return reports return reports def post(self, user, report): diff --git a/db.py b/db.py index a55158e..473b24a 100644 --- a/db.py +++ b/db.py @@ -126,7 +126,6 @@ class DB(object): id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, user_id INTEGER, subscriber_id INTEGER, - active INTEGER, FOREIGN KEY(user_id) REFERENCES user(id), UNIQUE(user_id, subscriber_id) ON CONFLICT IGNORE ); diff --git a/frontend.py b/frontend.py index e6c3b91..adae5fc 100755 --- a/frontend.py +++ b/frontend.py @@ -160,11 +160,10 @@ def update_badwords(user): @post('/settings/telegram') -@view('template/settings.tpl') def register_telegram(user): apikey = request.forms['apikey'] - user.set_telegram_key(apikey) - return user.state() + user.update_telegram_key(apikey) + return city_page(user.get_city(), info="Thanks for registering Telegram!") @get('/api/state') diff --git a/user.py b/user.py index e6b0c8d..9356518 100644 --- a/user.py +++ b/user.py @@ -94,20 +94,13 @@ schlitz return True def get_telegram_credentials(self): - db.execute("""SELECT api_token + db.execute("""SELECT apikey FROM telegram_accounts WHERE user_id = ? AND active = 1;""", (self.uid,)) row = db.cur.fetchone() return row[0] - def save_telegram_credentials(self, api_token): - db.execute("""INSERT INTO telegram_accounts ( - user_id, api_token, active) VALUES(?, ?, 1);""", - (self.uid, api_token)) - db.commit() - - def get_telegram_subscribers(self): db.execute("""SELECT subscriber_id FROM telegram_subscribers @@ -272,7 +265,7 @@ schlitz (self.uid, )) return db.cur.fetchall() - def set_telegram_key(self, apikey): + def update_telegram_key(self, apikey): db.execute("UPDATE telegram_accounts SET apikey = ? WHERE user_id = ?;", (apikey, self.uid)) db.commit() From 8acbfb4569df0edc38832cda20ff310c72c0a8c2 Mon Sep 17 00:00:00 2001 From: git-sid <25916907+git-sid@users.noreply.github.com> Date: Sun, 9 Sep 2018 17:22:00 +0200 Subject: [PATCH 17/33] added offset to telegram message polling to prevent duplicated responses --- active_bots/telegrambot.py | 4 +++- db.py | 6 ++++++ user.py | 10 ++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/active_bots/telegrambot.py b/active_bots/telegrambot.py index ab62fa5..637b332 100644 --- a/active_bots/telegrambot.py +++ b/active_bots/telegrambot.py @@ -10,7 +10,8 @@ logger = logging.getLogger(__name__) class TelegramBot(Bot): def crawl(self, user): tb = Telegram(user.get_telegram_credentials()) - updates = tb.get_updates().wait() + seen_tg = user.get_seen_tg() + updates = tb.get_updates(offset=seen_tg+1).wait() reports = [] for update in updates: try: @@ -28,6 +29,7 @@ class TelegramBot(Bot): else: reports.append(Report(update.message.sender.username, self, update.message.text, None, update.message.date)) + user.save_seen_tg(update.message.id) except AttributeError: print(updates[0], updates[1]) # Telegram API returns an Error return reports diff --git a/db.py b/db.py index 473b24a..c875a2e 100644 --- a/db.py +++ b/db.py @@ -75,6 +75,12 @@ class DB(object): FOREIGN KEY(mastodon_accounts_id) REFERENCES mastodon_accounts(id) ); + CREATE TABLE IF NOT EXISTS seen_telegrams ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, + user_id INTEGER, + tg_id INTEGER, + FOREIGN KEY(user_id) REFERENCES user(id), + ); CREATE TABLE IF NOT EXISTS twitter_request_tokens ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, user_id INTEGER, diff --git a/user.py b/user.py index 9356518..a7c79df 100644 --- a/user.py +++ b/user.py @@ -174,6 +174,16 @@ schlitz (tweet_id, self.uid)) db.commit() + def get_seen_tg(self): + db.execute("SELECT tg_id FROM seen_telegrams WHERE user_id = ?;", + (self.uid,)) + return db.cur.fetchone() + + def save_seen_tg(self, tg_id): + db.execute("UPDATE seen_telegrams SET tg_id = ? WHERE user_id = ?;", + (tg_id, self.uid)) + db.commit() + def get_mailinglist(self): db.execute("SELECT email FROM mailinglist WHERE user_id = ?;", (self.uid, )) return db.cur.fetchall() From 4b953f54e596b3abd58a627603dc6cb7f6b19f2c Mon Sep 17 00:00:00 2001 From: b3yond Date: Sun, 9 Sep 2018 17:29:06 +0200 Subject: [PATCH 18/33] Making Twitter Rate Limiting intelligent #35 --- active_bots/twitterbot.py | 8 ++++++++ backend.py | 1 - db.py | 6 ++++++ user.py | 10 ++++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/active_bots/twitterbot.py b/active_bots/twitterbot.py index 37b7029..eb4e4bd 100755 --- a/active_bots/twitterbot.py +++ b/active_bots/twitterbot.py @@ -4,6 +4,7 @@ import logging import tweepy import re import requests +from time import time import report from bot import Bot @@ -28,6 +29,11 @@ class TwitterBot(Bot): :return: reports: (list of report.Report objects) """ reports = [] + try: + if user.get_last_twitter_request() + 60 > time(): + return reports + except TypeError: + user.set_last_twitter_request(time()) try: api = self.get_api(user) except Exception: @@ -39,6 +45,8 @@ class TwitterBot(Bot): mentions = api.mentions_timeline() else: mentions = api.mentions_timeline(since_id=last_mention) + user.set_last_twitter_request(time()) + print(time()) for status in mentions: text = re.sub( "(?<=^|(?<=[^a-zA-Z0-9-_\.]))@([A-Za-z]+[A-Za-z0-9-_]+)", diff --git a/backend.py b/backend.py index 77189f3..47997a2 100755 --- a/backend.py +++ b/backend.py @@ -37,7 +37,6 @@ if __name__ == '__main__': continue for bot2 in bots: bot2.post(user, status) - time.sleep(60) # twitter rate limit >.< except Exception: logger.error("Shutdown.", exc_info=True) shutdown() diff --git a/db.py b/db.py index 473b24a..b46e51a 100644 --- a/db.py +++ b/db.py @@ -90,6 +90,12 @@ class DB(object): active INTEGER, FOREIGN KEY(user_id) REFERENCES user(id) ); + CREATE TABLE IF NOT EXISTS twitter_last_request ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, + user_id INTEGER, + date INTEGER, + FOREIGN KEY(user_id) REFERENCES user(id) + ); CREATE TABLE IF NOT EXISTS telegram_accounts ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, user_id INTEGER, diff --git a/user.py b/user.py index 9356518..2e5615b 100644 --- a/user.py +++ b/user.py @@ -144,6 +144,16 @@ schlitz keys.append(row[1]) return keys + def get_last_twitter_request(self): + db.execute("SELECT date FROM twitter_last_request WHERE user_id = ?;", + (self.uid,)) + return db.cur.fetchone()[0] + + def set_last_twitter_request(self, date): + db.execute("UPDATE twitter_last_request SET date = ? WHERE user_id = ?;", + (date, self.uid)) + db.commit() + def get_seen_toot(self): db.execute("SELECT toot_id FROM seen_toots WHERE user_id = ?;", (self.uid,)) From f68a869309f4fc5bacf2713163909519e2743bf4 Mon Sep 17 00:00:00 2001 From: git-sid <25916907+git-sid@users.noreply.github.com> Date: Sun, 9 Sep 2018 17:47:38 +0200 Subject: [PATCH 19/33] added message type filtering -> only text messages get crawled --- active_bots/telegrambot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/active_bots/telegrambot.py b/active_bots/telegrambot.py index 637b332..e5db44e 100644 --- a/active_bots/telegrambot.py +++ b/active_bots/telegrambot.py @@ -11,7 +11,7 @@ class TelegramBot(Bot): def crawl(self, user): tb = Telegram(user.get_telegram_credentials()) seen_tg = user.get_seen_tg() - updates = tb.get_updates(offset=seen_tg+1).wait() + updates = tb.get_updates(offset=seen_tg+1, allowed_updates="message").wait() reports = [] for update in updates: try: From 6cac81e44440980582b8f721137a7439f8c450a1 Mon Sep 17 00:00:00 2001 From: b3yond Date: Sun, 9 Sep 2018 17:51:07 +0200 Subject: [PATCH 20/33] fixing more telegram bugs --- active_bots/telegrambot.py | 7 +++++-- db.py | 4 +++- user.py | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/active_bots/telegrambot.py b/active_bots/telegrambot.py index 637b332..65320fb 100644 --- a/active_bots/telegrambot.py +++ b/active_bots/telegrambot.py @@ -11,7 +11,10 @@ class TelegramBot(Bot): def crawl(self, user): tb = Telegram(user.get_telegram_credentials()) seen_tg = user.get_seen_tg() - updates = tb.get_updates(offset=seen_tg+1).wait() + try: + updates = tb.get_updates(offset=seen_tg+1).wait() + except TypeError: + updates = tb.get_updates().wait() reports = [] for update in updates: try: @@ -45,4 +48,4 @@ class TelegramBot(Bot): tb.send_message(subscriber_id, text).wait() except Exception: logger.error('Error telegramming: ' + user.get_city() + ': ' - + report.id, exc_info=True) + + str(report.id), exc_info=True) diff --git a/db.py b/db.py index 3fa09db..a224a0f 100644 --- a/db.py +++ b/db.py @@ -79,7 +79,7 @@ class DB(object): id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, user_id INTEGER, tg_id INTEGER, - FOREIGN KEY(user_id) REFERENCES user(id), + FOREIGN KEY(user_id) REFERENCES user(id) ); CREATE TABLE IF NOT EXISTS twitter_request_tokens ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, @@ -264,6 +264,8 @@ u\d\d? (uid, json['email'])) self.execute("""INSERT INTO telegram_accounts (user_id, apikey, active) VALUES(?, ?, ?);""", (uid, "", 1)) + self.execute("INSERT INTO seen_telegrams (user_id, tg_id) VALUES (?,?);", + (uid, 0)) self.commit() user = User(uid) self.execute("INSERT INTO seen_mail (user_id, mail_date) VALUES (?,?)", diff --git a/user.py b/user.py index e87c372..92e8657 100644 --- a/user.py +++ b/user.py @@ -104,7 +104,7 @@ schlitz def get_telegram_subscribers(self): db.execute("""SELECT subscriber_id FROM telegram_subscribers - WHERE user_id = ? AND active = 1;""", + WHERE user_id = ?;""", (self.uid,)) rows = db.cur.fetchall() return rows From 6f3c9537361e70d24606a5371a44181f3341d191 Mon Sep 17 00:00:00 2001 From: b3yond Date: Sun, 9 Sep 2018 18:06:12 +0200 Subject: [PATCH 21/33] fixed telegram spam problem!!111 --- active_bots/telegrambot.py | 4 ++-- user.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/active_bots/telegrambot.py b/active_bots/telegrambot.py index e43b5bd..722ce17 100644 --- a/active_bots/telegrambot.py +++ b/active_bots/telegrambot.py @@ -28,12 +28,12 @@ class TelegramBot(Bot): tb.send_message(update.message.sender.id, "You are now unsubscribed from report notifications.") # TODO: /stop message should be set in frontend elif update.message.text.lower() == "/help": - tb.send_message(update.message.sender.id, "Send reports here to share them with other users. Use /start and /stop to be included/excluded.") + tb.send_message(update.message.sender.id, "Send reports here to share them with other users. Use /start and /stop to get reports or not.") # TODO: /help message should be set in frontend else: reports.append(Report(update.message.sender.username, self, update.message.text, None, update.message.date)) - user.save_seen_tg(update.message.id) + user.save_seen_tg(update.update_id) except AttributeError: print(updates[0], updates[1]) # Telegram API returns an Error return reports diff --git a/user.py b/user.py index 92e8657..da7ffe5 100644 --- a/user.py +++ b/user.py @@ -187,7 +187,7 @@ schlitz def get_seen_tg(self): db.execute("SELECT tg_id FROM seen_telegrams WHERE user_id = ?;", (self.uid,)) - return db.cur.fetchone() + return db.cur.fetchone()[0] def save_seen_tg(self, tg_id): db.execute("UPDATE seen_telegrams SET tg_id = ? WHERE user_id = ?;", From a54538bceac048536f6fd2bd0ee3002a4b6ec9d7 Mon Sep 17 00:00:00 2001 From: b3yond Date: Sun, 9 Sep 2018 20:22:41 +0200 Subject: [PATCH 22/33] fixed seen_toot problem --- active_bots/mastodonbot.py | 2 ++ active_bots/telegrambot.py | 6 +++--- active_bots/twitterbot.py | 1 - db.py | 18 ++++++++---------- frontend.py | 9 ++++++--- user.py | 14 +++++++++++++- 6 files changed, 32 insertions(+), 18 deletions(-) diff --git a/active_bots/mastodonbot.py b/active_bots/mastodonbot.py index a7f735b..5127eab 100755 --- a/active_bots/mastodonbot.py +++ b/active_bots/mastodonbot.py @@ -29,6 +29,8 @@ class MastodonBot(Bot): logger.error("Unknown Mastodon API Error.", exc_info=True) return mentions for status in notifications: + if user.get_seen_toot() == None: + user.init_seen_toot(m.instance()['uri']) if (status['type'] == 'mention' and status['status']['id'] > user.get_seen_toot()): # save state diff --git a/active_bots/telegrambot.py b/active_bots/telegrambot.py index 722ce17..12a85a6 100644 --- a/active_bots/telegrambot.py +++ b/active_bots/telegrambot.py @@ -19,6 +19,7 @@ class TelegramBot(Bot): reports = [] for update in updates: try: + user.save_seen_tg(update.update_id) if update.message.text.lower() == "/start": user.add_telegram_subscribers(update.message.sender.id) tb.send_message(update.message.sender.id, "You are now subscribed to report notifications.") @@ -32,10 +33,9 @@ class TelegramBot(Bot): # TODO: /help message should be set in frontend else: reports.append(Report(update.message.sender.username, self, - update.message.text, None, update.message.date)) - user.save_seen_tg(update.update_id) + update.message.text, None, update.message.date)) except AttributeError: - print(updates[0], updates[1]) # Telegram API returns an Error + logger.error('Some Attribute Error. ', exc_info=True) return reports return reports diff --git a/active_bots/twitterbot.py b/active_bots/twitterbot.py index eb4e4bd..bf127a2 100755 --- a/active_bots/twitterbot.py +++ b/active_bots/twitterbot.py @@ -46,7 +46,6 @@ class TwitterBot(Bot): else: mentions = api.mentions_timeline(since_id=last_mention) user.set_last_twitter_request(time()) - print(time()) for status in mentions: text = re.sub( "(?<=^|(?<=[^a-zA-Z0-9-_\.]))@([A-Za-z]+[A-Za-z0-9-_]+)", diff --git a/db.py b/db.py index a224a0f..dd3a695 100644 --- a/db.py +++ b/db.py @@ -67,10 +67,10 @@ class DB(object): FOREIGN KEY(instance_id) REFERENCES mastodon_instances(id) ); CREATE TABLE IF NOT EXISTS seen_toots ( - id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, - user_id INTEGER, - mastodon_accounts_id INTEGER, - toot_id TEXT, + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE DEFAULT 1, + user_id INTEGER DEFAULT 1, + mastodon_accounts_id INTEGER DEFAULT 1, + toot_id INTEGER DEFAULT 0, FOREIGN KEY(user_id) REFERENCES user(id), FOREIGN KEY(mastodon_accounts_id) REFERENCES mastodon_accounts(id) @@ -235,8 +235,7 @@ class DB(object): self.execute("INSERT INTO user (passhash) VALUES(?);", (json['passhash'], )) uid = self.cur.lastrowid - default_triggerpatterns = """ -kontroll?e + default_triggerpatterns = """kontroll?e konti db vgn @@ -252,8 +251,7 @@ linie nuernberg nürnberg s\d -u\d\d? - """ +u\d\d?""" self.execute("""INSERT INTO triggerpatterns (user_id, patterns) VALUES(?, ?); """, (uid, default_triggerpatterns)) self.execute("INSERT INTO badwords (user_id, words) VALUES(?, ?);", @@ -266,10 +264,10 @@ u\d\d? active) VALUES(?, ?, ?);""", (uid, "", 1)) self.execute("INSERT INTO seen_telegrams (user_id, tg_id) VALUES (?,?);", (uid, 0)) + self.execute("INSERT INTO seen_mail (user_id, mail_date) VALUES (?,?);", + (uid, 0)) self.commit() user = User(uid) - self.execute("INSERT INTO seen_mail (user_id, mail_date) VALUES (?,?)", - (uid, 0)) user.set_city(city) return user diff --git a/frontend.py b/frontend.py index adae5fc..7b71ee6 100755 --- a/frontend.py +++ b/frontend.py @@ -236,16 +236,19 @@ def login_mastodon(user): # get app tokens instance_url = request.forms.get('instance_url') masto_email = request.forms.get('email') - print(masto_email) masto_pass = request.forms.get('pass') - print(masto_pass) client_id, client_secret = user.get_mastodon_app_keys(instance_url) m = Mastodon(client_id=client_id, client_secret=client_secret, api_base_url=instance_url) try: access_token = m.log_in(masto_email, masto_pass) user.save_masto_token(access_token, instance_url) - city_page(user.get_city(), info='Thanks for supporting decentralized social networks!') + + # Trying to set the seen_toot to 0, thereby initializing it. + # It should work now, but has default values. Not sure if I need them. + user.init_seen_toot(instance_url) + + return city_page(user.get_city(), info='Thanks for supporting decentralized social networks!') except Exception: logger.error('Login to Mastodon failed.', exc_info=True) return dict(error='Login to Mastodon failed.') diff --git a/user.py b/user.py index da7ffe5..49b4aef 100644 --- a/user.py +++ b/user.py @@ -154,10 +154,22 @@ schlitz (date, self.uid)) db.commit() + def init_seen_toot(self, instance_url): + db.execute("SELECT id FROM mastodon_instances WHERE instance = ?;", + (instance_url,)) + masto_instance = db.cur.fetchone()[0] + db.execute("INSERT INTO seen_toots (user_id, mastodon_accounts_id) VALUES (?,?);", + (self.uid, masto_instance)) + db.conn.commit() + return + def get_seen_toot(self): db.execute("SELECT toot_id FROM seen_toots WHERE user_id = ?;", (self.uid,)) - return db.cur.fetchone()[0] + try: + return db.cur.fetchone()[0] + except TypeError: + return None def save_seen_toot(self, toot_id): db.execute("UPDATE seen_toots SET toot_id = ? WHERE user_id = ?;", From faaf8ac5f427cfc9471c23d73aa8ba93becf4978 Mon Sep 17 00:00:00 2001 From: b3yond Date: Sun, 9 Sep 2018 20:28:13 +0200 Subject: [PATCH 23/33] default values are bad practice --- db.py | 8 ++++---- user.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/db.py b/db.py index dd3a695..7eb938b 100644 --- a/db.py +++ b/db.py @@ -67,10 +67,10 @@ class DB(object): FOREIGN KEY(instance_id) REFERENCES mastodon_instances(id) ); CREATE TABLE IF NOT EXISTS seen_toots ( - id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE DEFAULT 1, - user_id INTEGER DEFAULT 1, - mastodon_accounts_id INTEGER DEFAULT 1, - toot_id INTEGER DEFAULT 0, + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, + user_id INTEGER, + mastodon_accounts_id INTEGER, + toot_id INTEGER, FOREIGN KEY(user_id) REFERENCES user(id), FOREIGN KEY(mastodon_accounts_id) REFERENCES mastodon_accounts(id) diff --git a/user.py b/user.py index 49b4aef..2e1a550 100644 --- a/user.py +++ b/user.py @@ -158,8 +158,8 @@ schlitz db.execute("SELECT id FROM mastodon_instances WHERE instance = ?;", (instance_url,)) masto_instance = db.cur.fetchone()[0] - db.execute("INSERT INTO seen_toots (user_id, mastodon_accounts_id) VALUES (?,?);", - (self.uid, masto_instance)) + db.execute("INSERT INTO seen_toots (user_id, mastodon_accounts_id, toot_id) VALUES (?,?,?);", + (self.uid, masto_instance, 0)) db.conn.commit() return From 3e83ba95da2f1484ca8a4e784e808ff93d5ea6cc Mon Sep 17 00:00:00 2001 From: b3yond Date: Sun, 9 Sep 2018 20:32:10 +0200 Subject: [PATCH 24/33] those error messages are a bit universal. --- active_bots/telegrambot.py | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/active_bots/telegrambot.py b/active_bots/telegrambot.py index 12a85a6..e916943 100644 --- a/active_bots/telegrambot.py +++ b/active_bots/telegrambot.py @@ -18,25 +18,23 @@ class TelegramBot(Bot): updates = tb.get_updates().wait() reports = [] for update in updates: - try: - user.save_seen_tg(update.update_id) - if update.message.text.lower() == "/start": - user.add_telegram_subscribers(update.message.sender.id) - tb.send_message(update.message.sender.id, "You are now subscribed to report notifications.") - # TODO: /start message should be set in frontend - elif update.message.text.lower() == "/stop": - user.remove_telegram_subscribers(update.message.sender.id) - tb.send_message(update.message.sender.id, "You are now unsubscribed from report notifications.") - # TODO: /stop message should be set in frontend - elif update.message.text.lower() == "/help": - tb.send_message(update.message.sender.id, "Send reports here to share them with other users. Use /start and /stop to get reports or not.") - # TODO: /help message should be set in frontend - else: - reports.append(Report(update.message.sender.username, self, - update.message.text, None, update.message.date)) - except AttributeError: - logger.error('Some Attribute Error. ', exc_info=True) + if update == 404: return reports + user.save_seen_tg(update.update_id) + if update.message.text.lower() == "/start": + user.add_telegram_subscribers(update.message.sender.id) + tb.send_message(update.message.sender.id, "You are now subscribed to report notifications.") + # TODO: /start message should be set in frontend + elif update.message.text.lower() == "/stop": + user.remove_telegram_subscribers(update.message.sender.id) + tb.send_message(update.message.sender.id, "You are now unsubscribed from report notifications.") + # TODO: /stop message should be set in frontend + elif update.message.text.lower() == "/help": + tb.send_message(update.message.sender.id, "Send reports here to share them with other users. Use /start and /stop to get reports or not.") + # TODO: /help message should be set in frontend + else: + reports.append(Report(update.message.sender.username, self, + update.message.text, None, update.message.date)) return reports def post(self, user, report): From 6757e622426f2ad16fb1c6594ae7415c15827196 Mon Sep 17 00:00:00 2001 From: b3yond Date: Sun, 9 Sep 2018 20:32:10 +0200 Subject: [PATCH 25/33] those error messages are a bit universal. --- active_bots/telegrambot.py | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/active_bots/telegrambot.py b/active_bots/telegrambot.py index 12a85a6..e916943 100644 --- a/active_bots/telegrambot.py +++ b/active_bots/telegrambot.py @@ -18,25 +18,23 @@ class TelegramBot(Bot): updates = tb.get_updates().wait() reports = [] for update in updates: - try: - user.save_seen_tg(update.update_id) - if update.message.text.lower() == "/start": - user.add_telegram_subscribers(update.message.sender.id) - tb.send_message(update.message.sender.id, "You are now subscribed to report notifications.") - # TODO: /start message should be set in frontend - elif update.message.text.lower() == "/stop": - user.remove_telegram_subscribers(update.message.sender.id) - tb.send_message(update.message.sender.id, "You are now unsubscribed from report notifications.") - # TODO: /stop message should be set in frontend - elif update.message.text.lower() == "/help": - tb.send_message(update.message.sender.id, "Send reports here to share them with other users. Use /start and /stop to get reports or not.") - # TODO: /help message should be set in frontend - else: - reports.append(Report(update.message.sender.username, self, - update.message.text, None, update.message.date)) - except AttributeError: - logger.error('Some Attribute Error. ', exc_info=True) + if update == 404: return reports + user.save_seen_tg(update.update_id) + if update.message.text.lower() == "/start": + user.add_telegram_subscribers(update.message.sender.id) + tb.send_message(update.message.sender.id, "You are now subscribed to report notifications.") + # TODO: /start message should be set in frontend + elif update.message.text.lower() == "/stop": + user.remove_telegram_subscribers(update.message.sender.id) + tb.send_message(update.message.sender.id, "You are now unsubscribed from report notifications.") + # TODO: /stop message should be set in frontend + elif update.message.text.lower() == "/help": + tb.send_message(update.message.sender.id, "Send reports here to share them with other users. Use /start and /stop to get reports or not.") + # TODO: /help message should be set in frontend + else: + reports.append(Report(update.message.sender.username, self, + update.message.text, None, update.message.date)) return reports def post(self, user, report): From 91181e1cf8fc4a6ae08679e830136e86e1a665c5 Mon Sep 17 00:00:00 2001 From: b3yond Date: Sun, 9 Sep 2018 21:36:27 +0200 Subject: [PATCH 26/33] added telegram to default city page text. --- template/settings.tpl | 3 ++- user.py | 11 +++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/template/settings.tpl b/template/settings.tpl index acc2b5e..863c8fa 100644 --- a/template/settings.tpl +++ b/template/settings.tpl @@ -93,7 +93,8 @@ needs.

- You should definitely adjust the Social Media profile links. + You should definitely adjust the Social Media, E-Mail, and Telegram + profile links. Also consider adding this link to the text: Link to the mail subscription page. Your readers can use this to subscribe to mail notifications. diff --git a/user.py b/user.py index 2e1a550..0d878fa 100644 --- a/user.py +++ b/user.py @@ -62,7 +62,7 @@ class User(object): (self.uid, )) patterns = db.cur.fetchone()[0] for pattern in patterns.splitlines(): - if pattern in report.text: + if pattern in report.text.lower(): break else: # no pattern matched @@ -372,9 +372,10 @@ Aber je mehr Leute mitmachen, desto eher kannst du dir sicher sein, dass wir sie finden, bevor sie uns finden. Wenn du immer direkt gewarnt werden willst, kannst du auch die -E-Mail-Benachrichtigungen aktivieren. Gib einfach -hier deine -E-Mail-Adresse an. +Benachrichtigungen über E-Mail oder Telegram aktivieren. Gib +einfach hier deine +E-Mail-Adresse an oder subscribe dem Telegram-Bot [@ticketfrei_""" + city + \ + "_bot](https://t.me/ticketfrei_" + city + """_bot) Also, wenn du weniger Glück hast, und der erste bist, der einen Kontrolleur sieht: @@ -385,6 +386,8 @@ Ganz einfach, du schreibst es den anderen. Das geht entweder * mit Mastodon [Link zu unserem Profil](""" + masto_link + """) * über Twitter: [Link zu unserem Profil](""" + twit_link + """) +* über Telegram an [@ticketfrei_""" + city + "_bot](https://t.me/ticketfrei_" \ + + city + """_bot) * Oder per Mail an [""" + mailinglist + "](mailto:" + mailinglist + """), wenn ihr kein Social Media benutzen wollt. From c576888da53df4412530de3b9bd88acf42097f7f Mon Sep 17 00:00:00 2001 From: git-sid <25916907+git-sid@users.noreply.github.com> Date: Thu, 13 Sep 2018 17:24:19 +0200 Subject: [PATCH 27/33] small fix --- active_bots/mastodonbot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/active_bots/mastodonbot.py b/active_bots/mastodonbot.py index 5127eab..adfac48 100755 --- a/active_bots/mastodonbot.py +++ b/active_bots/mastodonbot.py @@ -29,7 +29,7 @@ class MastodonBot(Bot): logger.error("Unknown Mastodon API Error.", exc_info=True) return mentions for status in notifications: - if user.get_seen_toot() == None: + if user.get_seen_toot() is None: user.init_seen_toot(m.instance()['uri']) if (status['type'] == 'mention' and status['status']['id'] > user.get_seen_toot()): From c2ed73bafc772826fbe63a2d0e1e6211ae87ffa4 Mon Sep 17 00:00:00 2001 From: git-sid <25916907+git-sid@users.noreply.github.com> Date: Thu, 13 Sep 2018 17:33:33 +0200 Subject: [PATCH 28/33] Make code more pep8 compliant --- active_bots/mastodonbot.py | 5 +++-- active_bots/telegrambot.py | 3 ++- frontend.py | 3 ++- report.py | 1 + 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/active_bots/mastodonbot.py b/active_bots/mastodonbot.py index adfac48..5cf9cbc 100755 --- a/active_bots/mastodonbot.py +++ b/active_bots/mastodonbot.py @@ -21,7 +21,7 @@ class MastodonBot(Bot): try: m = Mastodon(*user.get_masto_credentials()) except TypeError: - #logger.error("No Mastodon Credentials in database.", exc_info=True) + # logger.error("No Mastodon Credentials in database.", exc_info=True) return mentions try: notifications = m.notifications() @@ -71,4 +71,5 @@ class MastodonBot(Bot): try: m.toot(text) except Exception: - logger.error('Error tooting: ' + user.get_city() + ': ' + report.id, exc_info=True) + logger.error('Error tooting: ' + user.get_city() + ': ' + + report.id, exc_info=True) diff --git a/active_bots/telegrambot.py b/active_bots/telegrambot.py index e916943..dc8d921 100644 --- a/active_bots/telegrambot.py +++ b/active_bots/telegrambot.py @@ -34,7 +34,8 @@ class TelegramBot(Bot): # TODO: /help message should be set in frontend else: reports.append(Report(update.message.sender.username, self, - update.message.text, None, update.message.date)) + update.message.text, None, + update.message.date)) return reports def post(self, user, report): diff --git a/frontend.py b/frontend.py index 7b71ee6..45a1981 100755 --- a/frontend.py +++ b/frontend.py @@ -123,7 +123,7 @@ def unsubscribe(token): user = db.by_city(city) user.remove_subscriber(email) return city_page(city, info="You successfully unsubscribed " + email + - " from the mail notifications.") + " from the mail notifications.") @get('/settings') @@ -145,6 +145,7 @@ def update_mail_md(user): user.set_mail_md(request.forms['mail_md']) return user.state() + @post('/settings/goodlist') @view('template/settings.tpl') def update_trigger_patterns(user): diff --git a/report.py b/report.py index 5cf2c13..dc27003 100644 --- a/report.py +++ b/report.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 + class Report(object): """ A ticketfrei report object. From 72d6798022a8b05cfbffc8509b1ea658efb06385 Mon Sep 17 00:00:00 2001 From: git-sid <25916907+git-sid@users.noreply.github.com> Date: Thu, 13 Sep 2018 19:54:51 +0200 Subject: [PATCH 29/33] WIP: #31 fix: blacklist -> blocklist. Replace all relevant instances of "blacklist" with blocklist. Untested due to OS restricitions. Please check before merge. --- README.md | 6 +++--- frontend.py | 4 ++-- static/bot.html | 10 +++++----- static/js/functions.js | 2 +- template/settings.tpl | 10 +++++----- user.py | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index da364b7..fcc2933 100644 --- a/README.md +++ b/README.md @@ -50,16 +50,16 @@ to check if something was retweeted in the last hour or something. To this date, we have never heard of this happening though. -### blacklisting +### blockisting -You also need to edit the goodlist and the blacklist. You can do this on the +You also need to edit the goodlist and the blocklist. You can do this on the website, in the settings of your bot. Just add the words to the goodlist, which you want to require. A report is only spread if it contains at least one of them. If you want to RT everything, just add a ```*```. -There is also a blacklist, which you can use to automatically sort out +There is also a blocklist, which you can use to automatically sort out malicious messages. Be careful though, our filter can't read the intention with which a word was used. Maybe you wanted it there. diff --git a/frontend.py b/frontend.py index 45a1981..ca8264a 100755 --- a/frontend.py +++ b/frontend.py @@ -153,10 +153,10 @@ def update_trigger_patterns(user): return user.state() -@post('/settings/blacklist') +@post('/settings/blocklist') @view('template/settings.tpl') def update_badwords(user): - user.set_badwords(request.forms['blacklist']) + user.set_badwords(request.forms['blocklist']) return user.state() diff --git a/static/bot.html b/static/bot.html index 9d11d2f..b2e2bf6 100644 --- a/static/bot.html +++ b/static/bot.html @@ -83,16 +83,16 @@

- +

These words are not allowed in reports. If you encounter spam, you can add more here - the bot will ignore reports which use such words. - +

-
- - + + +
diff --git a/static/js/functions.js b/static/js/functions.js index ef1fa5c..4630ae0 100644 --- a/static/js/functions.js +++ b/static/js/functions.js @@ -36,4 +36,4 @@ document.getElementById("enablebutton").innerHTML = enableButton(); document.getElementById("goodlist").innerHTML = listformat(getCookie("goodlist")); -document.getElementById("blacklist").innerHTML = listformat(getCookie("blacklist")); \ No newline at end of file +document.getElementById("blocklist").innerHTML = listformat(getCookie("blocklist")); \ No newline at end of file diff --git a/template/settings.tpl b/template/settings.tpl index acc2b5e..234128d 100644 --- a/template/settings.tpl +++ b/template/settings.tpl @@ -140,15 +140,15 @@
-

Edit the blacklist

+

Edit the blocklist

These words are not allowed in reports. If you encounter spam, you can add more here - the bot will ignore reports which use such words. - - +

+ +
diff --git a/user.py b/user.py index 2e1a550..4487f4e 100644 --- a/user.py +++ b/user.py @@ -253,7 +253,7 @@ schlitz # - markdown # - mail_md # - goodlist - # - blacklist + # - blocklist # - logged in with twitter? # - logged in with mastodon? # - enabled? From 1703eb3802f4f0224667558a29a931a9e5cf3f2a Mon Sep 17 00:00:00 2001 From: git-sid <25916907+git-sid@users.noreply.github.com> Date: Fri, 14 Sep 2018 12:45:49 +0200 Subject: [PATCH 30/33] Make code even more PEP8 compliant It could be made even more compliant, but that would actually decrease readability imo. --- active_bots/__init__.py | 1 - active_bots/mailbot.py | 5 +++-- active_bots/mastodonbot.py | 4 ++-- active_bots/telegrambot.py | 12 +++++++++--- active_bots/twitterDMs.py | 6 +++--- active_bots/twitterbot.py | 4 ++-- db.py | 23 +++++++++++------------ sendmail.py | 2 +- user.py | 7 +++---- 9 files changed, 34 insertions(+), 30 deletions(-) diff --git a/active_bots/__init__.py b/active_bots/__init__.py index eedb95f..fb28c7f 100644 --- a/active_bots/__init__.py +++ b/active_bots/__init__.py @@ -12,4 +12,3 @@ for loader, name, is_pkg in pkgutil.walk_packages(__path__): globals()[name] = value __all__.append(name) - diff --git a/active_bots/mailbot.py b/active_bots/mailbot.py index f51fb26..12945b8 100644 --- a/active_bots/mailbot.py +++ b/active_bots/mailbot.py @@ -18,7 +18,8 @@ class Mailbot(Bot): # returns a list of Report objects def crawl(self, user): reports = [] - mails = mailbox.mbox('/var/mail/test') # todo: adjust to actual mailbox + # todo: adjust to actual mailbox + mails = mailbox.mbox('/var/mail/test') for msg in mails: if get_date_from_header(msg['Date']) > user.get_seen_mail(): reports.append(make_report(msg, user)) @@ -31,7 +32,7 @@ class Mailbot(Bot): rec = rec[0] unsubscribe_text = "\n_______\nYou don't want to receive those messages? Unsubscribe with this link: " body = report.text + unsubscribe_text + config['web']['host'] + "/city/mail/unsubscribe/" \ - + db.mail_subscription_token(rec, user.get_city()) + + db.mail_subscription_token(rec, user.get_city()) if report.author != rec: try: sendmail.sendmail(rec, "Ticketfrei " + user.get_city() + diff --git a/active_bots/mastodonbot.py b/active_bots/mastodonbot.py index 5cf9cbc..6e74f3e 100755 --- a/active_bots/mastodonbot.py +++ b/active_bots/mastodonbot.py @@ -38,8 +38,8 @@ class MastodonBot(Bot): # add mention to mentions text = re.sub(r'<[^>]*>', '', status['status']['content']) text = re.sub( - "(?<=^|(?<=[^a-zA-Z0-9-_.]))@([A-Za-z]+[A-Za-z0-9-_]+)", - "", text) + "(?<=^|(?<=[^a-zA-Z0-9-_.]))@([A-Za-z]+[A-Za-z0-9-_]+)", + "", text) if status['status']['visibility'] == 'public': mentions.append(Report(status['account']['acct'], self, diff --git a/active_bots/telegrambot.py b/active_bots/telegrambot.py index dc8d921..fe3beed 100644 --- a/active_bots/telegrambot.py +++ b/active_bots/telegrambot.py @@ -23,14 +23,20 @@ class TelegramBot(Bot): user.save_seen_tg(update.update_id) if update.message.text.lower() == "/start": user.add_telegram_subscribers(update.message.sender.id) - tb.send_message(update.message.sender.id, "You are now subscribed to report notifications.") + tb.send_message( + update.message.sender.id, + "You are now subscribed to report notifications.") # TODO: /start message should be set in frontend elif update.message.text.lower() == "/stop": user.remove_telegram_subscribers(update.message.sender.id) - tb.send_message(update.message.sender.id, "You are now unsubscribed from report notifications.") + tb.send_message( + update.message.sender.id, + "You are now unsubscribed from report notifications.") # TODO: /stop message should be set in frontend elif update.message.text.lower() == "/help": - tb.send_message(update.message.sender.id, "Send reports here to share them with other users. Use /start and /stop to get reports or not.") + tb.send_message( + update.message.sender.id, + "Send reports here to share them with other users. Use /start and /stop to get reports or not.") # TODO: /help message should be set in frontend else: reports.append(Report(update.message.sender.username, self, diff --git a/active_bots/twitterDMs.py b/active_bots/twitterDMs.py index 50d2810..237d7a1 100644 --- a/active_bots/twitterDMs.py +++ b/active_bots/twitterDMs.py @@ -33,14 +33,14 @@ class TwitterBot(Bot): return reports # no twitter account for this user. last_dm = user.get_seen_dm() try: - if last_dm == None: + if last_dm is None: mentions = api.direct_messages() else: mentions = api.mentions_timeline(since_id=last_dm[0]) for status in mentions: text = re.sub( - "(?<=^|(?<=[^a-zA-Z0-9-_\.]))@([A-Za-z]+[A-Za-z0-9-_]+)", - "", status.text) + "(?<=^|(?<=[^a-zA-Z0-9-_\.]))@([A-Za-z]+[A-Za-z0-9-_]+)", + "", status.text) reports.append(report.Report(status.author.screen_name, "twitterDM", text, diff --git a/active_bots/twitterbot.py b/active_bots/twitterbot.py index bf127a2..336737b 100755 --- a/active_bots/twitterbot.py +++ b/active_bots/twitterbot.py @@ -48,8 +48,8 @@ class TwitterBot(Bot): user.set_last_twitter_request(time()) for status in mentions: text = re.sub( - "(?<=^|(?<=[^a-zA-Z0-9-_\.]))@([A-Za-z]+[A-Za-z0-9-_]+)", - "", status.text) + "(?<=^|(?<=[^a-zA-Z0-9-_\.]))@([A-Za-z]+[A-Za-z0-9-_]+)", + "", status.text) reports.append(report.Report(status.author.screen_name, self, text, diff --git a/db.py b/db.py index 7eb938b..8528938 100644 --- a/db.py +++ b/db.py @@ -162,7 +162,7 @@ class DB(object): masto_link TEXT, twit_link TEXT, FOREIGN KEY(user_id) REFERENCES user(id), - UNIQUE(user_id, city) ON CONFLICT IGNORE + UNIQUE(user_id, city) ON CONFLICT IGNORE ); CREATE TABLE IF NOT EXISTS secret ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, @@ -198,11 +198,11 @@ class DB(object): :return: """ return jwt.encode({ - 'email': email, - 'passhash': scrypt_mcf( - password.encode('utf-8') - ).decode('ascii') - }, self.secret).decode('ascii') + 'email': email, + 'passhash': scrypt_mcf( + password.encode('utf-8') + ).decode('ascii') + }, self.secret).decode('ascii') def mail_subscription_token(self, email, city): """ @@ -223,7 +223,6 @@ class DB(object): json = jwt.decode(token, self.secret) return json['email'], json['city'] - def confirm(self, token, city): from user import User try: @@ -262,10 +261,10 @@ u\d\d?""" (uid, json['email'])) self.execute("""INSERT INTO telegram_accounts (user_id, apikey, active) VALUES(?, ?, ?);""", (uid, "", 1)) - self.execute("INSERT INTO seen_telegrams (user_id, tg_id) VALUES (?,?);", - (uid, 0)) - self.execute("INSERT INTO seen_mail (user_id, mail_date) VALUES (?,?);", - (uid, 0)) + self.execute( + "INSERT INTO seen_telegrams (user_id, tg_id) VALUES (?,?);", (uid, 0)) + self.execute( + "INSERT INTO seen_mail (user_id, mail_date) VALUES (?,?);", (uid, 0)) self.commit() user = User(uid) user.set_city(city) @@ -290,7 +289,7 @@ u\d\d?""" return User(uid) def user_facing_properties(self, city): - self.execute("""SELECT city, markdown, mail_md, masto_link, twit_link + self.execute("""SELECT city, markdown, mail_md, masto_link, twit_link FROM cities WHERE city=?;""", (city, )) try: diff --git a/sendmail.py b/sendmail.py index e7e1f67..d739d78 100755 --- a/sendmail.py +++ b/sendmail.py @@ -34,7 +34,7 @@ class Mailer(object): try: context = ssl.create_default_context() self.s.starttls(context=context) - except: + except BaseException: # TODO: Amend specific exception logger.error('StartTLS failed.', exc_info=True) self.s.login(config["mail"]["user"], config["mail"]["passphrase"]) diff --git a/user.py b/user.py index 4487f4e..9064ec9 100644 --- a/user.py +++ b/user.py @@ -80,7 +80,7 @@ fag faggot nigger neger -schlitz +schlitz """ db.execute("SELECT words FROM badwords WHERE user_id=?;", (self.uid, )) @@ -345,8 +345,8 @@ schlitz return db.cur.fetchone()[0] def set_city(self, city): - masto_link = "https://example.mastodon.social/@" + city # get masto_link - twit_link = "https://example.twitter.com/" + city # get twit_link + masto_link = "https://example.mastodon.social/@" + city # get masto_link + twit_link = "https://example.twitter.com/" + city # get twit_link mailinglist = city + "@" + config['web']['host'] markdown = """# Wie funktioniert Ticketfrei? @@ -461,4 +461,3 @@ unsubscribe-link mitgeschickt. masto_link, twit_link) VALUES(?,?,?,?,?,?)""", (self.uid, city, markdown, mail_md, masto_link, twit_link)) db.commit() - From 823df7b04aa2da3778ea0a4c442218416b4ad39f Mon Sep 17 00:00:00 2001 From: b3yond Date: Fri, 14 Sep 2018 19:59:45 +0200 Subject: [PATCH 31/33] fix TypeError when updates == None --- active_bots/telegrambot.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/active_bots/telegrambot.py b/active_bots/telegrambot.py index e916943..00fb01a 100644 --- a/active_bots/telegrambot.py +++ b/active_bots/telegrambot.py @@ -17,6 +17,8 @@ class TelegramBot(Bot): except TypeError: updates = tb.get_updates().wait() reports = [] + if updates == None: + return reports for update in updates: if update == 404: return reports From e9ac7286d9e2398add1149d7e8b103d8ce882e66 Mon Sep 17 00:00:00 2001 From: b3yond Date: Sat, 15 Sep 2018 18:47:43 +0200 Subject: [PATCH 32/33] peak readability --- active_bots/mailbot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/active_bots/mailbot.py b/active_bots/mailbot.py index 12945b8..2a88ba1 100644 --- a/active_bots/mailbot.py +++ b/active_bots/mailbot.py @@ -32,7 +32,7 @@ class Mailbot(Bot): rec = rec[0] unsubscribe_text = "\n_______\nYou don't want to receive those messages? Unsubscribe with this link: " body = report.text + unsubscribe_text + config['web']['host'] + "/city/mail/unsubscribe/" \ - + db.mail_subscription_token(rec, user.get_city()) + + db.mail_subscription_token(rec, user.get_city()) if report.author != rec: try: sendmail.sendmail(rec, "Ticketfrei " + user.get_city() + From 1a793657afa4851267e7210c2922187cbd442f40 Mon Sep 17 00:00:00 2001 From: b3yond Date: Sat, 15 Sep 2018 18:50:37 +0200 Subject: [PATCH 33/33] right of = should be right of = --- active_bots/twitterDMs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/active_bots/twitterDMs.py b/active_bots/twitterDMs.py index 237d7a1..36b2562 100644 --- a/active_bots/twitterDMs.py +++ b/active_bots/twitterDMs.py @@ -39,8 +39,8 @@ class TwitterBot(Bot): mentions = api.mentions_timeline(since_id=last_dm[0]) for status in mentions: text = re.sub( - "(?<=^|(?<=[^a-zA-Z0-9-_\.]))@([A-Za-z]+[A-Za-z0-9-_]+)", - "", status.text) + "(?<=^|(?<=[^a-zA-Z0-9-_\.]))@([A-Za-z]+[A-Za-z0-9-_]+)", + "", status.text) reports.append(report.Report(status.author.screen_name, "twitterDM", text,