From c7671744393f4995bf416f8ac987be9a4f6f7f57 Mon Sep 17 00:00:00 2001 From: zevaryx Date: Sat, 19 Feb 2022 13:52:28 -0700 Subject: [PATCH] Add twitter task --- jarvis_tasks/tasks/twitter.py | 84 +++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 jarvis_tasks/tasks/twitter.py diff --git a/jarvis_tasks/tasks/twitter.py b/jarvis_tasks/tasks/twitter.py new file mode 100644 index 0000000..850db97 --- /dev/null +++ b/jarvis_tasks/tasks/twitter.py @@ -0,0 +1,84 @@ +"""JARVIS Twitter sync.""" +import asyncio +from datetime import datetime, timedelta +from typing import TYPE_CHECKING + +import tweepy +from jarvis_core.db import q +from jarvis_core.db.models import TwitterAccount, TwitterFollow + +from jarvis_tasks.config import TaskConfig + +if TYPE_CHECKING: + from dis_snek import Snake + +config = TaskConfig.from_yaml() + + +async def twitter(self, bot: "Snake") -> None: + """ + Sync tweets in the background. + + Args: + bot: Snake instance + """ + auth = tweepy.AppAuthHandler(config.twitter["consumer_key"], config.twitter["consumer_secret"]) + api = tweepy.API(auth) + while True: + # Only check once a minute + asyncio.sleep(60) + + accounts = TwitterAccount.find() + accounts_to_delete = [] + + # Go through all actively followed accounts + async for account in accounts: + # Check if account needs updated (handle changes) + if account.last_sync + timedelta(hours=1) <= datetime.utcnow(): + user = api.get_user(id=account.twitter_id) + account.update(q(handle=user.screen_name, last_sync=datetime.utcnow())) + + # Get new tweets + if tweets := api.user_timeline(id=account.twitter_id, since_id=account.last_tweet): + tweets = sorted(tweets, key=lambda x: x.id) + follows = TwitterFollow.find(q(twitter_id=account.twitter_id)) + follows_to_delete = [] + num_follows = 0 + + # Go through follows and send tweet if necessary + async for follow in follows: + num_follows += 1 + guild = await bot.fetch_guild(follow.guild_id) + if not guild: + follows_to_delete.append(follow) + continue + channel = await guild.fetch_channel(follow.channel_id) + if not channel: + follows_to_delete.append(follow) + continue + for tweet in tweets: + retweet = "retweeted_status" in tweet.__dict__ + if retweet and not follow.retweets: + continue + + timestamp = int(tweet.created_at.timestamp()) + url = f"https://twitter.com/{account.handle}/status/{tweet.id}" + mod = "re" if retweet else "" + await channel.send( + f"`@{account.handle}` {mod}tweeted this at : {url}" + ) + + # Delete invalid follows + for follow in follows_to_delete: + await follow.delete() + + if num_follows == 0: + accounts_to_delete.append(account) + else: + newest = tweets[0] + account.update(q(last_tweet=newest.id)) + await account.commit() + + # Delete invalid accounts (no follows) + for account in accounts_to_delete: + await account.delete()