jarvis-tasks/jarvis_tasks/tasks/twitter.py
2022-02-19 14:24:41 -07:00

81 lines
3 KiB
Python

"""JARVIS Twitter sync."""
import asyncio
from datetime import datetime, timedelta
import tweepy
from dis_snek import Snake
from jarvis_core.db import q
from jarvis_core.db.models import TwitterAccount, TwitterFollow
from jarvis_tasks.config import TaskConfig
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 <t:{timestamp}:f>: {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()