diff --git a/README.md b/README.md index ddd35f3..336cb1e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Ticketfrei social bot -Ticketfrei is a mastodon/twitter/mail bot to dodge ticket controllers in public +Ticketfrei is a mastodon/mail bot to dodge ticket controllers in public transport systems. ## Mission @@ -28,9 +28,9 @@ your tweet and others can read the info and think twice whether they want to buy a ticket or not. If enough people, a critical mass, participate for the bot to become reliable, you have positive self-reinforcing dynamics. -Today, you can use a Twitter, Mastodon, Telegram, and Mail with the account. +Today, you can use a Mastodon, Telegram, and Mail with the account. They will communicate with each other; if someone warns others via Mail, -Telegram, Twitter and Mastodon users will also see the message. And vice versa. +Telegram and Mastodon users will also see the message. And vice versa. In version 2, this repository contains a web application. On this website, people can register an own bot for their city - the website manages multiple @@ -51,7 +51,6 @@ running. * Register on the ticketfrei site * Optionally: register bots: - * Register a Twitter account * Register a Mastodon account * Register a Telegram bot * Configure account @@ -119,7 +118,7 @@ vim config.toml ``` This configuration is only for the admin. Moderators can log into -twitter/mastodon/mail and configure their personal bot on the settings page. +mastodon/mail and configure their personal bot on the settings page. Set up LetsEncrypt: @@ -221,7 +220,7 @@ less /var/log/exim4/mainlog ### Development Install -If you want to install it locally to develop on it, note that twitter and mail +If you want to install it locally to develop on it, note that mail will probably not work. You should test them on a server instead. ```shell @@ -252,7 +251,7 @@ vim config.toml ``` This configuration is only for the admin. Users can log into -twitter/mastodon/mail and configure their personal bot on the settings page. +mastodon/mail and configure their personal bot on the settings page. ```shell # create folder for socket & database @@ -272,7 +271,7 @@ sudo chown $USER:$USER -R /var/log/ticketfrei ## Version 1 - more of less hacked together during a mate-fueled weekend -- backend-only, twitter & mastodon +- backend-only, mastodon - just a script, which crawled & retweeted tweets, if they match a whitelist & blocklist - whitelist & blocklist were just 2 files @@ -281,7 +280,7 @@ sudo chown $USER:$USER -R /var/log/ticketfrei Reasons for the rewrite: - user management: Users should be able to run a Ticketfrei bot in their city - without needing a server, without needing command line skills -- more networks; not only Twitter & Mastodon, also Email & Telegram +- more networks; not only Mastodon, also Email & Telegram 2 processes: backend & frontend. The two Processes talk via a database. diff --git a/active_bots/twitterbot.py b/active_bots/twitterbot.py deleted file mode 100755 index 4d1e283..0000000 --- a/active_bots/twitterbot.py +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env python3 - -import logging -import tweepy -import re -import requests -from time import time -import report -from bot import Bot - - -logger = logging.getLogger("main") - - -class TwitterBot(Bot): - - def get_api(self, user): - keys = user.get_twitter_credentials() - auth = tweepy.OAuthHandler(consumer_key=keys[0], - consumer_secret=keys[1]) - auth.set_access_token(keys[2], # access_token_key - keys[3]) # access_token_secret - return tweepy.API(auth, wait_on_rate_limit=True) - - def crawl(self, user): - """ - crawls all Tweets which mention the bot from the twitter rest API. - - :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 TypeError: - # When there is no twitter account for this bot, we want to - # seamlessly continue. - #logger.error("Error Authenticating Twitter", exc_info=True) - return reports - last_mention = user.get_seen_tweet() - try: - if last_mention == 0: - mentions = api.mentions_timeline() - else: - mentions = api.mentions_timeline(since_id=last_mention) - user.set_last_twitter_request(time()) - for status in mentions: - if status._json['in_reply_to_status_id'] == None: - text = re.sub( - "(?<=^|(?<=[^a-zA-Z0-9-_\.]))@([A-Za-z]+[A-Za-z0-9-_]+)", - "", status.text) - reports.append(report.Report(status.author.screen_name, - self, - text, - status.id, - status.created_at)) - user.save_seen_tweet(status.id) - return reports - except tweepy.RateLimitError: - logger.error("Twitter API Error: Rate Limit Exceeded", - exc_info=True) - # :todo implement rate limiting - except requests.exceptions.ConnectionError: - logger.error("Twitter API Error: Bad Connection", exc_info=True) - except tweepy.TweepError: - logger.error("Twitter API Error: General Error. User: " + str(user.uid), exc_info=True) - return [] - - def post(self, user, report): - try: - api = self.get_api(user) - except TypeError: - return # no twitter account for this user. - try: - if report.source == self: - api.retweet(report.id) - else: - text = report.text - if len(text) > 280: - text = text[:280 - 4] + u' ...' - api.update_status(status=text) - except requests.exceptions.ConnectionError: - logger.error("Twitter API Error: Bad Connection", - exc_info=True) - except tweepy.error.TweepError: - logger.error("Twitter API Error", exc_info=True) diff --git a/config.py b/config.py index e0ba420..81670c9 100755 --- a/config.py +++ b/config.py @@ -12,18 +12,6 @@ def load_env(): with open('config.toml.example') as defaultconf: configdict = toml.load(defaultconf) - try: - if os.environ['CONSUMER_KEY'] != "": - configdict['twitter']['consumer_key'] = os.environ['CONSUMER_KEY'] - except KeyError: - pass - - try: - if os.environ['CONSUMER_SECRET'] != "": - configdict['twitter']['consumer_secret'] = os.environ['CONSUMER_SECRET'] - except KeyError: - pass - try: if os.environ['HOST'] != "": configdict['web']['host'] = os.environ['HOST'] diff --git a/config.toml.example b/config.toml.example index eb519db..fbdf4cb 100644 --- a/config.toml.example +++ b/config.toml.example @@ -1,9 +1,3 @@ -[twitter] -# You get those keys when you follow these steps: -# https://developer.twitter.com/en/docs/basics/authentication/guides/access-tokens -consumer_key = "your_consumer_key" -consumer_secret = "your_consumer_secret" - [web] host = "0.0.0.0" # will be used by bottle as a host. port = 80 diff --git a/db.py b/db.py index 6c2c4ca..2102d1c 100644 --- a/db.py +++ b/db.py @@ -91,21 +91,6 @@ class DB(object): 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, - request_token TEXT, - request_token_secret TEXT, - FOREIGN KEY(user_id) REFERENCES user(id) - ); - CREATE TABLE IF NOT EXISTS twitter_accounts ( - id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, - user_id INTEGER, - client_id TEXT, - client_secret TEXT, - active 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, @@ -113,21 +98,6 @@ class DB(object): active INTEGER, FOREIGN KEY(user_id) REFERENCES user(id) ); - CREATE TABLE IF NOT EXISTS seen_tweets ( - id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, - user_id INTEGER, - tweet_id INTEGER, - FOREIGN KEY(user_id) REFERENCES user(id) - ); - CREATE TABLE IF NOT EXISTS seen_dms ( - id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, - user_id INTEGER, - twitter_accounts_id INTEGER, - message_id TEXT, - FOREIGN KEY(user_id) REFERENCES user(id) - FOREIGN KEY(twitter_accounts_id) - REFERENCES twitter_accounts(id) - ); CREATE TABLE IF NOT EXISTS telegram_subscribers ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, user_id INTEGER, @@ -147,12 +117,6 @@ class DB(object): mail_date REAL, 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 cities ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, user_id INTEGER, @@ -269,8 +233,6 @@ 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.execute("INSERT INTO seen_tweets (user_id, tweet_id) VALUES (?, ?)", (uid, 0)) - self.execute("INSERT INTO twitter_last_request (user_id, date) VALUES (?, ?)", (uid, 0)) self.commit() user = User(uid) user.set_city(city) diff --git a/frontend.py b/frontend.py index 7fbf975..ee27faa 100755 --- a/frontend.py +++ b/frontend.py @@ -4,7 +4,6 @@ from bottle import get, post, redirect, request, response, view from config import config from db import db import logging -import tweepy from sendmail import sendmail from session import SessionPlugin from mastodon import Mastodon @@ -202,45 +201,6 @@ def logout(): # :todo show info "Logout successful." redirect('/') - -@get('/login/twitter') -def login_twitter(user): - """ - Starts the twitter OAuth authentication process. - :return: redirect to twitter. - """ - consumer_key = config["twitter"]["consumer_key"] - consumer_secret = config["twitter"]["consumer_secret"] - callback_url = url("login/twitter/callback") - auth = tweepy.OAuthHandler(consumer_key, consumer_secret, callback_url) - try: - redirect_url = auth.get_authorization_url() - except tweepy.TweepError: - logger.error('Twitter OAuth Error: Failed to get request token.', - exc_info=True) - return dict(error="Failed to get request token.") - user.save_request_token(auth.request_token) - redirect(redirect_url) - - -@get('/login/twitter/callback') -def twitter_callback(user): - """ - Gets the callback - :return: - """ - # twitter passes the verifier/oauth token secret in a GET request. - verifier = request.query['oauth_verifier'] - consumer_key = config["twitter"]["consumer_key"] - consumer_secret = config["twitter"]["consumer_secret"] - auth = tweepy.OAuthHandler(consumer_key, consumer_secret) - request_token = user.get_request_token() - auth.request_token = request_token - auth.get_access_token(verifier) - user.save_twitter_token(auth.access_token, auth.access_token_secret) - redirect("/settings") - - @post('/login/mastodon') def login_mastodon(user): """ diff --git a/report.py b/report.py index dc27003..66722c0 100644 --- a/report.py +++ b/report.py @@ -14,7 +14,7 @@ class Report(object): Constructor of a ticketfrei report :param author: username of the author - :param source: mastodon, twitter, or email bot object + :param source: mastodon or email bot object :param text: the text of the report :param id: id in the network :param timestamp: time of the report diff --git a/static/bot.html b/static/bot.html index b2e2bf6..7b27e13 100644 --- a/static/bot.html +++ b/static/bot.html @@ -13,15 +13,6 @@
asdf
- - - - - - - Log in with Twitter - -

Log in with Mastodon

diff --git a/template/settings.tpl b/template/settings.tpl index 66cf873..b7b8398 100644 --- a/template/settings.tpl +++ b/template/settings.tpl @@ -7,15 +7,6 @@

Enable
% end - - - - - - - Log in with Twitter - -

Log in with Mastodon

diff --git a/user.py b/user.py index 2cff416..ae95c0d 100644 --- a/user.py +++ b/user.py @@ -107,16 +107,6 @@ schlitz logger.info("Valid report: " + report.text + " | username: " + report.author) return True - 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_telegram_credentials(self): db.execute("""SELECT apikey FROM telegram_accounts @@ -170,27 +160,6 @@ schlitz (toot_uri, self.uid)) db.commit() - def get_seen_tweet(self): - db.execute("SELECT tweet_id FROM seen_tweets WHERE user_id = ?;", - (self.uid,)) - return db.cur.fetchone()[0] - - def save_seen_tweet(self, tweet_id): - if tweet_id > self.get_seen_tweet(): - db.execute("UPDATE seen_tweets SET tweet_id = ? WHERE user_id = ?;", - (tweet_id, self.uid)) - db.commit() - - def get_seen_dm(self): - db.execute("SELECT message_id FROM seen_dms WHERE user_id = ?;", - (self.uid,)) - return db.cur.fetchone() - - def save_seen_dm(self, tweet_id): - db.execute("UPDATE seen_dms SET message_id = ? WHERE user_id = ?;", - (tweet_id, self.uid)) - db.commit() - def get_seen_tg(self): db.execute("SELECT tg_id FROM seen_telegrams WHERE user_id = ?;", (self.uid,)) @@ -250,7 +219,6 @@ schlitz # - goodlist # - blocklist # - csrf - # - logged in with twitter? # - logged in with mastodon? # - enabled? citydict = db.user_facing_properties(self.get_city()) @@ -262,48 +230,6 @@ schlitz enabled=self.enabled, csrf=self.get_csrf()) - 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.commit() - - def get_request_token(self): - 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.commit() - 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 seen_tweets(user_id, tweet_id) VALUES (?, ?);""", - (self.uid, 0)) - db.commit() - - def get_twitter_token(self): - db.execute("SELECT client_id, client_secret FROM twitter_accounts WHERE user_id = ?;", - (self.uid, )) - return db.cur.fetchone() - - def get_twitter_credentials(self): - keys = [config['twitter']['consumer_key'], - config['twitter']['consumer_secret']] - row = self.get_twitter_token() - keys.append(row[0]) - keys.append(row[1]) - return keys - def update_telegram_key(self, apikey): db.execute("UPDATE telegram_accounts SET apikey = ? WHERE user_id = ?;", (apikey, self.uid)) db.commit() @@ -353,7 +279,6 @@ schlitz 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 mailinglist = city + "@" + config['web']['host'] markdown = """# Wie funktioniert Ticketfrei? @@ -395,7 +320,6 @@ Kontrolleur sieht: 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