You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

107 lines
3.8 KiB

  1. import os.path
  2. import sys
  3. import feedparser
  4. from mastodon import Mastodon
  5. import json
  6. import requests
  7. import re
  8. import sqlite3
  9. from datetime import datetime, date, time, timedelta
  10. if len(sys.argv) < 4:
  11. print("Usage: python3 tootbot.py twitter_account mastodon_login mastodon_passwd mastodon_instance")
  12. sys.exit(1)
  13. # sqlite db to store processed tweets (and corresponding toots ids)
  14. sql = sqlite3.connect('tootbot.db')
  15. db = sql.cursor()
  16. db.execute('''CREATE TABLE IF NOT EXISTS tweets (tweet text, toot text, twitter text, mastodon text, instance text)''')
  17. if len(sys.argv)>4:
  18. instance = sys.argv[4]
  19. else:
  20. instance = 'amicale.net'
  21. if len(sys.argv)>5:
  22. days = int(sys.argv[5])
  23. else:
  24. days = 1
  25. twitter = sys.argv[1]
  26. mastodon = sys.argv[2]
  27. passwd = sys.argv[3]
  28. mastodon_api = None
  29. d = feedparser.parse('http://twitrss.me/twitter_user_to_rss/?user='+twitter)
  30. for t in reversed(d.entries):
  31. # check if this tweet has been processed
  32. db.execute('SELECT * FROM tweets WHERE tweet = ? AND twitter = ? and mastodon = ? and instance = ?',(t.id, twitter, mastodon, instance))
  33. last = db.fetchone()
  34. # process only unprocessed tweets less than 1 day old
  35. if last is None and (datetime.now()-datetime(t.published_parsed.tm_year, t.published_parsed.tm_mon, t.published_parsed.tm_mday, t.published_parsed.tm_hour, t.published_parsed.tm_min, t.published_parsed.tm_sec) < timedelta(days=days)):
  36. if mastodon_api is None:
  37. # Create application if it does not exist
  38. if not os.path.isfile(instance+'.secret'):
  39. if Mastodon.create_app(
  40. 'tootbot',
  41. api_base_url='https://'+instance,
  42. to_file = instance+'.secret'
  43. ):
  44. print('tootbot app created on instance '+instance)
  45. else:
  46. print('failed to create app on instance '+instance)
  47. sys.exit(1)
  48. try:
  49. mastodon_api = Mastodon(
  50. client_id=instance+'.secret',
  51. api_base_url='https://'+instance
  52. )
  53. mastodon_api.log_in(
  54. username=mastodon,
  55. password=passwd,
  56. scopes=['read', 'write'],
  57. to_file=mastodon+".secret"
  58. )
  59. except:
  60. print("ERROR: First Login Failed!")
  61. sys.exit(1)
  62. #h = BeautifulSoup(t.summary_detail.value, "html.parser")
  63. c = t.title
  64. if t.author != '(%s)' % twitter:
  65. c = ("RT %s\n" % t.author[1:-1]) + c
  66. toot_media = []
  67. # get the pictures...
  68. for p in re.finditer(r"https://pbs.twimg.com/[^ \xa0\"]*", t.summary):
  69. media = requests.get(p.group(0))
  70. media_posted = mastodon_api.media_post(media.content, mime_type=media.headers.get('content-type'))
  71. toot_media.append(media_posted['id'])
  72. # replace t.co link by original URL
  73. m = re.search(r"http[^ \xa0]*", c)
  74. if m != None:
  75. l = m.group(0)
  76. r = requests.get(l, allow_redirects=False)
  77. if r.status_code in {301,302}:
  78. c = c.replace(l,r.headers.get('Location'))
  79. # remove pic.twitter.com links
  80. m = re.search(r"pic.twitter.com[^ \xa0]*", c)
  81. if m != None:
  82. l = m.group(0)
  83. c = c.replace(l,' ')
  84. # remove ellipsis
  85. c = c.replace('\xa0…',' ')
  86. if toot_media is not None:
  87. toot = mastodon_api.status_post(c, in_reply_to_id=None, media_ids=toot_media, sensitive=False, visibility='public', spoiler_text=None)
  88. if "id" in toot:
  89. db.execute("INSERT INTO tweets VALUES ( ? , ? , ? , ? , ? )",
  90. (t.id, toot["id"], twitter, mastodon, instance))
  91. sql.commit()