run.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. from mastodon import Mastodon
  2. from datetime import datetime, timedelta
  3. import pytz
  4. from environs import Env
  5. from jinja2 import Template
  6. import logging
  7. import sys
  8. import time
  9. class Bot(object):
  10. def __init__(self, access_token, instance_url, msg_template='', debug_mode=True, account_id=1, max_chars=500):
  11. self.max_chars = max_chars
  12. self.instance_url = instance_url
  13. self.msg_template = Template(msg_template)
  14. self.client = Mastodon(
  15. access_token=access_token,
  16. api_base_url=self.instance_url
  17. )
  18. self.debug_mode = debug_mode
  19. #start by sending welcome to accounts that have registered after the bot has started
  20. self.newest_account_creation_date = pytz.utc.localize(datetime.now())
  21. #used to update self.newest_account_creation_date
  22. self.newest_account_creation_date_record = self.newest_account_creation_date
  23. self.log = logging.getLogger()
  24. if self.debug_mode:
  25. self.log.setLevel(logging.DEBUG)
  26. else:
  27. self.log.setLevel(logging.INFO)
  28. ch = logging.StreamHandler(sys.stdout)
  29. self.log.addHandler(ch)
  30. self.account_id = account_id
  31. def _should_get_msg(self, account):
  32. if self.debug_mode:
  33. self.log.debug("--Filtering {}--".format(account.username))
  34. self.log.debug("Account url: {}".format(account.url))
  35. self.log.debug("Created at: {}".format(account.created_at))
  36. self.log.debug("Newest account date {}".format(self.newest_account_creation_date))
  37. self.log.debug("")
  38. if account.created_at > self.newest_account_creation_date_record:
  39. self.newest_account_creation_date_record = account.created_at
  40. return account.url.startswith(self.instance_url) and account.created_at > self.newest_account_creation_date and not account.locked
  41. def get_users(self):
  42. users_to_msg = []
  43. def get_prev(users):
  44. followers = []
  45. try:
  46. followers = self.client.fetch_previous(users)
  47. except:
  48. self.log.error("Cannot fetch followers.")
  49. filtered = [follower for follower in followers if self._should_get_msg(follower)]
  50. users_to_msg.extend(filtered)
  51. # this assumes that followers are returned in descending order by follow date
  52. if len(filtered) == 0:
  53. return users_to_msg
  54. return get_prev(followers)
  55. followers = []
  56. try:
  57. followers = self.client.account_followers(self.account_id, limit=80)
  58. except Exception as e:
  59. self.log.error("Cannot fetch followers.")
  60. self.log.exception(e)
  61. except:
  62. self.log.error("Error")
  63. filtered = [follower for follower in followers if self._should_get_msg(follower)]
  64. users_to_msg.extend(filtered)
  65. if len(filtered) == 0:
  66. return users_to_msg
  67. return get_prev(followers)
  68. def send_msg(self, account):
  69. msg = self.msg_template.render(account)
  70. toots = msg.split('\n\n\n')
  71. for toot in toots:
  72. reply_id = None
  73. if len(toot) < self.max_chars:
  74. reply_id = self.client.status_post(toot, visibility='direct', in_reply_to_id=reply_id).id
  75. if self.debug_mode:
  76. self.log.debug("Replying to {}".format(reply_id))
  77. else:
  78. self.log.error("Make sure to correctly split the toot into parts that can be sent individually")
  79. def go(self):
  80. users_to_msg = self.get_users()
  81. self.newest_account_creation_date = self.newest_account_creation_date_record
  82. for u in users_to_msg:
  83. self.send_msg(u)
  84. if self.debug_mode:
  85. self.log.debug('{} toots sent'.format(len(users_to_msg)))
  86. def create_bot():
  87. env = Env()
  88. env.read_env()
  89. access_token = env('ACCESS_TOKEN')
  90. max_chars = env.int('INSTANCE_MAX_CHARS', 500)
  91. instance_base_url = env('INSTANCE_BASE_URL')
  92. msg_template = ''
  93. with open('message.template', 'r') as file:
  94. msg_template = file.read()
  95. debug_mode = env.bool('DEBUG', False)
  96. account_id = env.int('ACCOUNT_ID')
  97. bot = Bot(access_token,
  98. instance_url=instance_base_url,
  99. msg_template=msg_template,
  100. debug_mode=debug_mode,
  101. account_id=account_id,
  102. max_chars=max_chars)
  103. return bot
  104. def debug_shell(bot):
  105. cmd = input('debug command> ').strip()
  106. if cmd == 'c' or cmd == 'continue':
  107. return False
  108. elif cmd == 'update date':
  109. bot.newest_account_creation_date = pytz.utc.localize(datetime.now())
  110. bot.newest_account_creation_date_record = bot.newest_account_creation_date
  111. elif cmd.startswith('before'):
  112. bot.newest_account_creation_date = pytz.utc.localize(datetime.now() - timedelta(hours=int(cmd.split(' ')[1])))
  113. bot.newest_account_creation_date_record = bot.newest_account_creation_date
  114. elif cmd == 'print date':
  115. bot.log.debug(bot.newest_account_creation_date)
  116. return True
  117. def run():
  118. bot = create_bot()
  119. while True:
  120. if bot.debug_mode:
  121. while debug_shell(bot):
  122. pass
  123. else:
  124. time.sleep(30) # 30 secs
  125. bot.go()
  126. if __name__ == '__main__':
  127. run()