From ef0ce8f9f11d72d5c309ba70bf1ad82d3dd18f00 Mon Sep 17 00:00:00 2001 From: b3yond Date: Wed, 8 Aug 2018 10:55:51 +0200 Subject: [PATCH 1/6] 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 2/6] 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 3/6] 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 4/6] 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 5/6] 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 6/6] 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() +