This commit is contained in:
parent
c9dfb6611a
commit
2a9c5c657f
|
@ -91,7 +91,7 @@ virtualenv -p python3 .
|
||||||
Install the dependencies:
|
Install the dependencies:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
pip install tweepy pytoml Mastodon.py bottle pyjwt pylibscrypt
|
pip install tweepy pytoml Mastodon.py bottle pyjwt pylibscrypt Markdown
|
||||||
```
|
```
|
||||||
|
|
||||||
Configure the bot:
|
Configure the bot:
|
||||||
|
|
31
db.py
31
db.py
|
@ -42,13 +42,13 @@ class DB(object):
|
||||||
CREATE TABLE IF NOT EXISTS triggerpatterns (
|
CREATE TABLE IF NOT EXISTS triggerpatterns (
|
||||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||||
user_id INTEGER,
|
user_id INTEGER,
|
||||||
pattern TEXT,
|
patterns TEXT,
|
||||||
FOREIGN KEY(user_id) REFERENCES user(id)
|
FOREIGN KEY(user_id) REFERENCES user(id)
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS badwords (
|
CREATE TABLE IF NOT EXISTS badwords (
|
||||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||||
user_id INTEGER,
|
user_id INTEGER,
|
||||||
word TEXT,
|
words TEXT,
|
||||||
FOREIGN KEY(user_id) REFERENCES user(id)
|
FOREIGN KEY(user_id) REFERENCES user(id)
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS mastodon_instances (
|
CREATE TABLE IF NOT EXISTS mastodon_instances (
|
||||||
|
@ -146,10 +146,29 @@ class DB(object):
|
||||||
self.execute("INSERT INTO user (passhash) VALUES(?);",
|
self.execute("INSERT INTO user (passhash) VALUES(?);",
|
||||||
(json['passhash'], ))
|
(json['passhash'], ))
|
||||||
uid = self.cur.lastrowid
|
uid = self.cur.lastrowid
|
||||||
self.execute("""
|
default_triggerpatterns = """
|
||||||
INSERT INTO triggerpatterns (user_id, pattern)
|
kontroll?e
|
||||||
VALUES(?, ?);
|
konti
|
||||||
""", (uid, '.*'))
|
db
|
||||||
|
vgn
|
||||||
|
vag
|
||||||
|
zivil
|
||||||
|
sicherheit
|
||||||
|
uniform
|
||||||
|
station
|
||||||
|
bus
|
||||||
|
bahn
|
||||||
|
tram
|
||||||
|
linie
|
||||||
|
nuernberg
|
||||||
|
nürnberg
|
||||||
|
s\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(?, ?);",
|
||||||
|
(uid, ""))
|
||||||
else:
|
else:
|
||||||
uid = json['uid']
|
uid = json['uid']
|
||||||
self.execute("INSERT INTO email (user_id, email) VALUES(?, ?);",
|
self.execute("INSERT INTO email (user_id, email) VALUES(?, ?);",
|
||||||
|
|
16
frontend.py
16
frontend.py
|
@ -38,7 +38,7 @@ def register_post():
|
||||||
return dict(error='Email address already in use.')
|
return dict(error='Email address already in use.')
|
||||||
# send confirmation mail
|
# send confirmation mail
|
||||||
try:
|
try:
|
||||||
# print(url('confirm/' + city + '/%s' % db.user_token(email, password))) # only for local testing
|
print(url('confirm/' + city + '/%s' % db.user_token(email, password))) # only for local testing
|
||||||
sendmail(
|
sendmail(
|
||||||
email,
|
email,
|
||||||
"Confirm your account",
|
"Confirm your account",
|
||||||
|
@ -93,6 +93,20 @@ def settings(user):
|
||||||
return user.state()
|
return user.state()
|
||||||
|
|
||||||
|
|
||||||
|
@post('/settings/goodlist')
|
||||||
|
@view('template/settings.tpl')
|
||||||
|
def update_trigger_patterns(user):
|
||||||
|
user.set_trigger_words(request.forms['goodlist'])
|
||||||
|
return user.state()
|
||||||
|
|
||||||
|
|
||||||
|
@post('/settings/blacklist')
|
||||||
|
@view('template/settings.tpl')
|
||||||
|
def update_badwords(user):
|
||||||
|
user.set_badwords(request.forms['blacklist'])
|
||||||
|
return user.state()
|
||||||
|
|
||||||
|
|
||||||
@get('/api/state')
|
@get('/api/state')
|
||||||
def api_enable(user):
|
def api_enable(user):
|
||||||
return user.state()
|
return user.state()
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
% rebase('template/wrapper.tpl')
|
% rebase('template/wrapper.tpl')
|
||||||
|
|
||||||
$markdown.render()
|
<%
|
||||||
|
import markdown as md
|
||||||
|
|
||||||
|
html = md.markdown(markdown)
|
||||||
|
%>
|
||||||
|
|
||||||
|
{{html}}
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
% rebase('template/wrapper.tpl')
|
% rebase('template/wrapper.tpl')
|
||||||
<a href="/logout/"><button>Logout</button></a>
|
<a href="/logout/"><button>Logout</button></a>
|
||||||
|
|
||||||
<div id="enablebutton" style="float: right; padding: 2em;">asdf</div>
|
% if enabled:
|
||||||
|
<div id="enablebutton" style="float: right; padding: 2em;">Disable</div>
|
||||||
|
% else:
|
||||||
|
<div id="enablebutton" style="float: right; padding: 2em;" color="red">Enable</div>
|
||||||
|
% end
|
||||||
|
|
||||||
<a class='button' style="padding: 1.5em;" href="/login/twitter">
|
<a class='button' style="padding: 1.5em;" href="/login/twitter">
|
||||||
<picture>
|
<picture>
|
||||||
|
@ -72,7 +76,7 @@
|
||||||
suggest:
|
suggest:
|
||||||
</p>
|
</p>
|
||||||
<form action="/settings/goodlist" method="post">
|
<form action="/settings/goodlist" method="post">
|
||||||
<textarea id="markdown" rows="20" cols="70" name="goodlist" wrap="physical">$markdown</textarea>
|
<textarea id="markdown" rows="20" cols="70" name="goodlist" wrap="physical">{{markdown}}</textarea>
|
||||||
<input name='confirm' value='Save' type='submit'/>
|
<input name='confirm' value='Save' type='submit'/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -86,7 +90,7 @@
|
||||||
</p>
|
</p>
|
||||||
<form action="/settings/goodlist" method="post">
|
<form action="/settings/goodlist" method="post">
|
||||||
<!-- find a way to display current good list. js which reads from a cookie? template? -->
|
<!-- find a way to display current good list. js which reads from a cookie? template? -->
|
||||||
<textarea id="goodlist" rows="8" cols="70" name="goodlist" wrap="physical">$triggerwords</textarea>
|
<textarea id="goodlist" rows="8" cols="70" name="goodlist" wrap="physical">{{triggerwords}}</textarea>
|
||||||
<input name='confirm' value='Submit' type='submit'/>
|
<input name='confirm' value='Submit' type='submit'/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -100,7 +104,7 @@
|
||||||
</p>
|
</p>
|
||||||
<form action="/settings/blacklist" method="post">
|
<form action="/settings/blacklist" method="post">
|
||||||
<!-- find a way to display current blacklist. js which reads from a cookie? template? -->
|
<!-- find a way to display current blacklist. js which reads from a cookie? template? -->
|
||||||
<textarea id="blacklist" rows="8" cols="70" name="blacklist" wrap="physical">$badwords</textarea>
|
<textarea id="blacklist" rows="8" cols="70" name="blacklist" wrap="physical">{{badwords}}</textarea>
|
||||||
<input name='confirm' value='Submit' type='submit'/>
|
<input name='confirm' value='Submit' type='submit'/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
228
user.py
228
user.py
|
@ -27,7 +27,7 @@ class User(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def enabled(self):
|
def enabled(self):
|
||||||
db.execute("SELECT enabled FROM user WHERE user_id=?;", (self.uid, ))
|
db.execute("SELECT enabled FROM user WHERE id=?;", (self.uid, ))
|
||||||
return bool(db.cur.fetchone()[0])
|
return bool(db.cur.fetchone()[0])
|
||||||
|
|
||||||
@enabled.setter
|
@enabled.setter
|
||||||
|
@ -59,16 +59,35 @@ class User(object):
|
||||||
def is_appropriate(self, report):
|
def is_appropriate(self, report):
|
||||||
db.execute("SELECT pattern FROM triggerpatterns WHERE user_id=?;",
|
db.execute("SELECT pattern FROM triggerpatterns WHERE user_id=?;",
|
||||||
(self.uid, ))
|
(self.uid, ))
|
||||||
for pattern, in db.cur.fetchall():
|
patterns = db.cur.fetchone()
|
||||||
|
for pattern in patterns.splitlines():
|
||||||
if pattern.search(report.text) is not None:
|
if pattern.search(report.text) is not None:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
# no pattern matched
|
# no pattern matched
|
||||||
return False
|
return False
|
||||||
|
default_badwords = """
|
||||||
|
bastard
|
||||||
|
bitch
|
||||||
|
whore
|
||||||
|
hitler
|
||||||
|
slut
|
||||||
|
hure
|
||||||
|
jude
|
||||||
|
schwuchtel
|
||||||
|
fag
|
||||||
|
faggot
|
||||||
|
nigger
|
||||||
|
neger
|
||||||
|
schlitz
|
||||||
|
"""
|
||||||
db.execute("SELECT word FROM badwords WHERE user_id=?;",
|
db.execute("SELECT word FROM badwords WHERE user_id=?;",
|
||||||
(self.uid, ))
|
(self.uid, ))
|
||||||
badwords = [word.lower() for word, in db.cur.fetchall()]
|
badwords = db.cur.fetchone()
|
||||||
for word in report.text.lower().split():
|
for word in report.text.lower().splitlines():
|
||||||
|
if word in badwords:
|
||||||
|
return False
|
||||||
|
for word in default_badwords.splitlines():
|
||||||
if word in badwords:
|
if word in badwords:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
@ -129,8 +148,22 @@ class User(object):
|
||||||
db.execute("UPDATE seen_mail SET mail_date = ? WHERE user_id = ?;",
|
db.execute("UPDATE seen_mail SET mail_date = ? WHERE user_id = ?;",
|
||||||
(mail_date, self.uid))
|
(mail_date, self.uid))
|
||||||
|
|
||||||
def get_trigger_words(self, table):
|
def set_trigger_words(self, patterns):
|
||||||
db.execute("SELECT words FROM ? WHERE user_id = ?;", (table, self.uid,))
|
db.execute("UPDATE triggerpatterns SET patterns = ? WHERE user_id = ?;",
|
||||||
|
(patterns, self.uid))
|
||||||
|
|
||||||
|
def get_trigger_words(self):
|
||||||
|
db.execute("SELECT patterns FROM triggerpatterns WHERE user_id = ?;",
|
||||||
|
(self.uid,))
|
||||||
|
return db.cur.fetchone()[0]
|
||||||
|
|
||||||
|
def set_badwords(self, words):
|
||||||
|
db.execute("UPDATE badwords SET words = ? WHERE user_id = ?;",
|
||||||
|
(words, self.uid))
|
||||||
|
|
||||||
|
def get_badwords(self):
|
||||||
|
db.execute("SELECT words FROM badwords WHERE user_id = ?;",
|
||||||
|
(self.uid,))
|
||||||
return db.cur.fetchone()[0]
|
return db.cur.fetchone()[0]
|
||||||
|
|
||||||
def state(self):
|
def state(self):
|
||||||
|
@ -142,7 +175,12 @@ class User(object):
|
||||||
# - logged in with twitter?
|
# - logged in with twitter?
|
||||||
# - logged in with mastodon?
|
# - logged in with mastodon?
|
||||||
# - enabled?
|
# - enabled?
|
||||||
return dict(foo='bar')
|
citydict = db.user_facing_properties(self.get_city())
|
||||||
|
return dict(city=citydict['city'],
|
||||||
|
markdown=citydict['markdown'],
|
||||||
|
triggerwords=self.get_trigger_words(),
|
||||||
|
badwords=self.get_badwords(),
|
||||||
|
enabled=self.enabled)
|
||||||
|
|
||||||
def save_request_token(self, token):
|
def save_request_token(self, token):
|
||||||
db.execute("INSERT INTO twitter_request_tokens(user_id, request_token, request_token_secret) VALUES(?, ?, ?);",
|
db.execute("INSERT INTO twitter_request_tokens(user_id, request_token, request_token_secret) VALUES(?, ?, ?);",
|
||||||
|
@ -191,99 +229,99 @@ class User(object):
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
def get_city(self):
|
def get_city(self):
|
||||||
db.execute("SELECT city FROM user WHERE id == ?;", (self.uid, ))
|
db.execute("SELECT city FROM cities WHERE user_id == ?;", (self.uid, ))
|
||||||
return db.cur.fetchone()[0]
|
return db.cur.fetchone()[0]
|
||||||
|
|
||||||
def set_city(self, city):
|
def set_city(self, city):
|
||||||
masto_link = "masto.social/@" + city # get masto_link
|
masto_link = "example.mastodon.social/@" + city # get masto_link
|
||||||
twit_link = "twitter.com/" + city # get twit_link
|
twit_link = "example.twitter.com/" + city # get twit_link
|
||||||
mailinglist = city + "@" + config['web']['host']
|
mailinglist = city + "@" + config['web']['host']
|
||||||
markdown = """
|
markdown = """
|
||||||
= Wie funktioniert Ticketfrei? =
|
# Wie funktioniert Ticketfrei?
|
||||||
|
|
||||||
Willst du mithelfen, Ticketkontrolleure zu überwachen?
|
Willst du mithelfen, Ticketkontrolleure zu überwachen?
|
||||||
Willst du einen Fahrscheinfreien ÖPNV erkämpfen?
|
Willst du einen Fahrscheinfreien ÖPNV erkämpfen?
|
||||||
|
|
||||||
== Ist es gerade sicher, schwarz zu fahren? ==
|
## Ist es gerade sicher, schwarz zu fahren?
|
||||||
|
|
||||||
Schau einfach auf das Profil unseres Bots: """ + twit_link + """
|
Schau einfach auf das Profil unseres Bots: """ + twit_link + """
|
||||||
|
|
||||||
Hat jemand vor kurzem etwas über Kontrolleur*innen gepostet?
|
Hat jemand vor kurzem etwas über Kontrolleur*innen gepostet?
|
||||||
* Wenn ja, dann kauf dir vllt lieber ein Ticket. In Nürnberg
|
* Wenn ja, dann kauf dir vllt lieber ein Ticket. In Nürnberg
|
||||||
haben wir die Erfahrung gemacht, dass Kontis normalerweile
|
haben wir die Erfahrung gemacht, dass Kontis normalerweile
|
||||||
ungefähr ne Woche aktiv sind, ein paar Stunden am Tag. Wenn es
|
ungefähr ne Woche aktiv sind, ein paar Stunden am Tag. Wenn es
|
||||||
also in den letzten Stunden einen Bericht gab, pass lieber
|
also in den letzten Stunden einen Bericht gab, pass lieber
|
||||||
auf.
|
auf.
|
||||||
* Wenn nicht, ist es wahrscheinlich kein Problem :)
|
* Wenn nicht, ist es wahrscheinlich kein Problem :)
|
||||||
|
|
||||||
Wir können natürlich nicht garantieren, dass es sicher ist,
|
Wir können natürlich nicht garantieren, dass es sicher ist,
|
||||||
also pass trotzdem auf, wer auf dem Bahnsteig steht.
|
also pass trotzdem auf, wer auf dem Bahnsteig steht.
|
||||||
Aber je mehr Leute mitmachen, desto eher kannst du dir sicher
|
Aber je mehr Leute mitmachen, desto eher kannst du dir sicher
|
||||||
sein, dass wir sie finden, bevor sie uns finden.
|
sein, dass wir sie finden, bevor sie uns finden.
|
||||||
|
|
||||||
Also, wenn du weniger Glück hast, und der erste bist, der einen
|
Also, wenn du weniger Glück hast, und der erste bist, der einen
|
||||||
Kontrolleur sieht:
|
Kontrolleur sieht:
|
||||||
|
|
||||||
== Was mache ich, wenn ich Kontis sehe? ==
|
## Was mache ich, wenn ich Kontis sehe?
|
||||||
|
|
||||||
Ganz einfach, du schreibst es den anderen. Das geht entweder
|
Ganz einfach, du schreibst es den anderen. Das geht entweder
|
||||||
|
|
||||||
<!-- * mit Mastodon: """ + masto_link + """ -->
|
<!-- * mit Mastodon: """ + masto_link + """ -->
|
||||||
<!-- * über Twitter: """ + twit_link + """ -->
|
<!-- * über Twitter: """ + twit_link + """ -->
|
||||||
* Oder per Mail an """ + mailinglist + """, wenn ihr kein
|
* Oder per Mail an """ + mailinglist + """, wenn ihr kein
|
||||||
Social Media benutzen wollt.
|
Social Media benutzen wollt.
|
||||||
|
|
||||||
Schreibe einfach einen Toot oder einen Tweet, der den Bot
|
Schreibe einfach einen Toot oder einen Tweet, der den Bot
|
||||||
mentioned, und gib an
|
mentioned, und gib an
|
||||||
* Wo du die Kontis gesehen hast
|
* Wo du die Kontis gesehen hast
|
||||||
* Welche Linie sie benutzen und in welche Richtung sie fahren.
|
* Welche Linie sie benutzen und in welche Richtung sie fahren.
|
||||||
|
|
||||||
Zum Beispiel so:
|
Zum Beispiel so:
|
||||||
|
|
||||||
[[https://github.com/b3yond/ticketfrei/blob/master/guides/tooting_screenshot.png|Screenshot of writing a toot]]
|
[[https://github.com/b3yond/ticketfrei/blob/master/guides/tooting_screenshot.png|Screenshot of writing a toot]]
|
||||||
|
|
||||||
[[https://github.com/b3yond/ticketfrei/blob/master/guides/toot_screenshot.png|A toot ready to be boosted]]
|
[[https://github.com/b3yond/ticketfrei/blob/master/guides/toot_screenshot.png|A toot ready to be boosted]]
|
||||||
|
|
||||||
Der Bot wird die Nachricht dann weiterverbreiten, auch zu den
|
Der Bot wird die Nachricht dann weiterverbreiten, auch zu den
|
||||||
anderen Netzwerken.
|
anderen Netzwerken.
|
||||||
Dann können andere Leute das lesen und sicher vor Kontis sein.
|
Dann können andere Leute das lesen und sicher vor Kontis sein.
|
||||||
|
|
||||||
Danke, dass du mithilfst, öffentlichen Verkehr für alle
|
Danke, dass du mithilfst, öffentlichen Verkehr für alle
|
||||||
sicherzustellen!
|
sicherzustellen!
|
||||||
|
|
||||||
== Kann ich darauf vertrauen, was random stranger from the
|
## Kann ich darauf vertrauen, was random stranger from the
|
||||||
Internet mir da erzählen? ==
|
Internet mir da erzählen?
|
||||||
|
|
||||||
Aber natürlich! Wir haben Katzenbilder!
|
Aber natürlich! Wir haben Katzenbilder!
|
||||||
|
|
||||||
[[https://lorempixel.com/550/300/cats|Katzenbilder...]]
|
[[https://lorempixel.com/550/300/cats|Katzenbilder...]]
|
||||||
|
|
||||||
Glaubt besser nicht, wenn jemand postet, dass die Luft da und
|
Glaubt besser nicht, wenn jemand postet, dass die Luft da und
|
||||||
da gerade rein ist.
|
da gerade rein ist.
|
||||||
Das ist vielleicht sogar gut gemeint - aber klar könnte die
|
Das ist vielleicht sogar gut gemeint - aber klar könnte die
|
||||||
VAG sich hinsetzen und einfach lauter Falschmeldungen posten.
|
VAG sich hinsetzen und einfach lauter Falschmeldungen posten.
|
||||||
|
|
||||||
Aber Falschmeldungen darüber, dass gerade Kontis i-wo unterwegs
|
Aber Falschmeldungen darüber, dass gerade Kontis i-wo unterwegs
|
||||||
sind?
|
sind?
|
||||||
Das macht keinen Sinn.
|
Das macht keinen Sinn.
|
||||||
Im schlimmsten Fall kauft jmd mal eine Fahrkarte mehr - aber
|
Im schlimmsten Fall kauft jmd mal eine Fahrkarte mehr - aber
|
||||||
kann sonst immer schwarz fahren.
|
kann sonst immer schwarz fahren.
|
||||||
|
|
||||||
Also ja - es macht Sinn, uns zu vertrauen, wenn wir sagen, wo
|
Also ja - es macht Sinn, uns zu vertrauen, wenn wir sagen, wo
|
||||||
gerade Kontis sind.
|
gerade Kontis sind.
|
||||||
|
|
||||||
== Was ist Mastodon und warum sollte ich es benutzen? ==
|
## Was ist Mastodon und warum sollte ich es benutzen?
|
||||||
|
|
||||||
Mastodon ist ein dezentrales soziales Netzwerk - so wie
|
Mastodon ist ein dezentrales soziales Netzwerk - so wie
|
||||||
Twitter, nur ohne Monopol und Zentralismus.
|
Twitter, nur ohne Monopol und Zentralismus.
|
||||||
Ihr könnt Kurznachrichten (Toots) über alles mögliche
|
Ihr könnt Kurznachrichten (Toots) über alles mögliche
|
||||||
schreiben, und euch mit anderen austauschen.
|
schreiben, und euch mit anderen austauschen.
|
||||||
|
|
||||||
Mastodon ist Open Source, Privatsphäre-freundlich und relativ
|
Mastodon ist Open Source, Privatsphäre-freundlich und relativ
|
||||||
sicher vor Zensur.
|
sicher vor Zensur.
|
||||||
|
|
||||||
Um Mastodon zu benutzen, besucht diese Seite:
|
Um Mastodon zu benutzen, besucht diese Seite:
|
||||||
https://joinmastodon.org/
|
https://joinmastodon.org/
|
||||||
"""
|
"""
|
||||||
db.execute("""INSERT INTO cities(user_id, city, markdown, masto_link,
|
db.execute("""INSERT INTO cities(user_id, city, markdown, masto_link,
|
||||||
twit_link) VALUES(?,?,?,?,?)""",
|
twit_link) VALUES(?,?,?,?,?)""",
|
||||||
|
|
Loading…
Reference in a new issue