tootbot/tootbot.py
2019-06-25 15:05:16 +02:00

135 lines
4.4 KiB
Python
Executable File

import os.path
import sys
import re
import sqlite3
from datetime import datetime, timedelta
import feedparser
from mastodon import Mastodon
import requests
if len(sys.argv) < 4:
print("Usage: python3 tootbot.py twitter_account mastodon_login mastodon_passwd mastodon_instance [max_days [footer_tags [delay]]]") # noqa
sys.exit(1)
# sqlite db to store processed tweets (and corresponding toots ids)
sql = sqlite3.connect('tootbot.db')
db = sql.cursor()
db.execute('''CREATE TABLE IF NOT EXISTS tweets (tweet text, toot text,
twitter text, mastodon text, instance text)''')
if len(sys.argv) > 4:
instance = sys.argv[4]
else:
instance = 'amicale.net'
if len(sys.argv) > 5:
days = int(sys.argv[5])
else:
days = 1
if len(sys.argv) > 6:
tags = sys.argv[6]
else:
tags = None
if len(sys.argv) > 7:
delay = int(sys.argv[7])
else:
delay = 0
source = sys.argv[1]
mastodon = sys.argv[2]
passwd = sys.argv[3]
mastodon_api = None
if source[:4] == 'http':
d = feedparser.parse(source)
twitter = None
else:
d = feedparser.parse('http://twitrss.me/twitter_user_to_rss/?user='+source)
twitter = source
for t in reversed(d.entries):
# check if this tweet has been processed
db.execute('SELECT * FROM tweets WHERE tweet = ? AND twitter = ? and mastodon = ? and instance = ?', (t.id, source, mastodon, instance)) # noqa
last = db.fetchone()
dt = t.published_parsed
age = datetime.now()-datetime(dt.tm_year, dt.tm_mon, dt.tm_mday,
dt.tm_hour, dt.tm_min, dt.tm_sec)
# process only unprocessed tweets less than 1 day old, after delay
if last is None and age < timedelta(days=days) and age > timedelta(days=delay):
if mastodon_api is None:
# Create application if it does not exist
if not os.path.isfile(instance+'.secret'):
if Mastodon.create_app(
'tootbot',
api_base_url='https://'+instance,
to_file=instance+'.secret'
):
print('tootbot app created on instance '+instance)
else:
print('failed to create app on instance '+instance)
sys.exit(1)
try:
mastodon_api = Mastodon(
client_id=instance+'.secret',
api_base_url='https://'+instance
)
mastodon_api.log_in(
username=mastodon,
password=passwd,
scopes=['read', 'write'],
to_file=mastodon+".secret"
)
except:
print("ERROR: First Login Failed!")
sys.exit(1)
c = t.title
if twitter and t.author.lower() != ('(@%s)' % twitter).lower():
c = ("RT https://twitter.com/%s\n" % t.author[2:-1]) + c
toot_media = []
# get the pictures...
for p in re.finditer(r"https://pbs.twimg.com/[^ \xa0\"]*", t.summary):
media = requests.get(p.group(0))
media_posted = mastodon_api.media_post(media.content, mime_type=media.headers.get('content-type'))
toot_media.append(media_posted['id'])
# replace short links by original URL
m = re.search(r"http[^ \xa0]*", c)
if m is not None:
l = m.group(0)
r = requests.get(l, allow_redirects=False)
if r.status_code in {301, 302}:
c = c.replace(l, r.headers.get('Location'))
# remove pic.twitter.com links
m = re.search(r"pic.twitter.com[^ \xa0]*", c)
if m is not None:
l = m.group(0)
c = c.replace(l, ' ')
# remove ellipsis
c = c.replace('\xa0', ' ')
if twitter is None:
c = c + '\nSource: '+ t.authors[0].name +'\n\n' + t.link
if tags:
c = c + '\n' + tags
if toot_media is not None:
toot = mastodon_api.status_post(c, in_reply_to_id=None,
media_ids=toot_media,
sensitive=False,
visibility='public',
spoiler_text=None)
if "id" in toot:
db.execute("INSERT INTO tweets VALUES ( ? , ? , ? , ? , ? )",
(t.id, toot["id"], source, mastodon, instance))
sql.commit()