Skip to content

Instantly share code, notes, and snippets.

@Angelin01
Created May 2, 2022 16:14
Show Gist options
  • Save Angelin01/694353ac5e446fa002e516ca7370c938 to your computer and use it in GitHub Desktop.
Save Angelin01/694353ac5e446fa002e516ca7370c938 to your computer and use it in GitHub Desktop.
Twitter 2 Discord
# Old script I made and had laying around, it's not really adequate corpared to something I'd create
# these days, but it works
# Simple script to poll Twitter for tweets and route them to a Discord webhook
# Run it periodically, using something like Cron
# Requires the following environment variables, can also be read from a file `.env` in the same
# directory as this one:
# USERNAME: Case insensitive twitter handle without the @
# LAST_TWEET: File name to store the last tweet ID, must have read/write permissions
# BEARER: Bearer token for twitter API
# WEBHOOK: Discord's webhook URL
from os.path import join, dirname
from time import sleep
from os import getenv
from dotenv import load_dotenv
from requests import post, get
from json import loads
from sys import exit
def get_tweets(user, bearer, since_id=None, count=20, include_rts=False):
"""
Gets tweets from user API
:param user: The handle of the tweeter user, without the @
:param bearer: The twitter bearer token, load from environment
:param since_id: Returns results with an ID greater than this paramter
:param count: Maximum number of tweets to get
:param include_rts: Include retweets if true
:return: A list of tweets in JSON format
"""
payload = {
'screen_name': user,
'count': count,
'include_rts': include_rts,
'trim_user': False,
'tweet_mode': 'extended'
}
if since_id:
payload['since_id'] = since_id
response = get('https://api.twitter.com/1.1/statuses/user_timeline.json',
headers={'Authorization': 'Bearer ' + bearer},
params=payload)
if response.status_code != 200:
raise ValueError('Non 200 response from API, something is wrong with the request')
return loads(response.text)
def build_payload(tweet):
"""
Builds a payload (to use as json) to send to Discord
:param tweet: The tweet object properly loaded with Python types and formats
:return: A dictionary in JSON format to use with the POST request as a payload
"""
from time import strftime, strptime
payload = {
'username': 'JefTweets',
'embeds': [
{
'title': '{user} tweeted'.format(user=tweet['user']['name']),
'url': 'https://twitter.com/{user}/status/{id}'.format(user=tweet['user']['screen_name'], id=tweet['id']),
'thumbnail': {
'url': tweet['user']['profile_image_url_https']
},
'timestamp': strftime('%Y-%m-%dT%H:%M:%SZ', strptime(tweet['created_at'], '%a %b %d %H:%M:%S %z %Y')),
'description': tweet['full_text']
}
]
}
if 'extended_entities' in tweet and tweet['extended_entities']['media']:
payload['embeds'][0]['image'] = {
'url': tweet['extended_entities']['media'][0]['media_url_https']
}
if len(tweet['extended_entities']['media']) > 1:
payload['embeds'][0]['footer'] = {
'text': 'There are more images in the tweet'
}
return payload
def send_to_discord(user, last_tweet, bearer, webhook):
"""
Sends to a Discord webhook tweets from someone
:param user: The handle of the tweeter user, without the @
:param last_tweet: The file in which to store the last tweet's ID. If it doesn't exist, it will be created
:param webhook: The Discord webhook URL
:param bearer: The twitter bearer token, load from environment
:return: Exit code, 0 is fine, 1 indicates some form of error in the request
"""
# Get the last known tweet's ID from the file
try:
with open(last_tweet) as last_tweet_file:
last_tweet_id = last_tweet_file.readline().strip() or None
except FileNotFoundError:
last_tweet_id = None
# If there's no ID, simply get the tweets and set the last one as the last known ID
if not last_tweet_id:
try:
for tweet in get_tweets(user, bearer):
if not tweet['retweeted']:
with open(last_tweet, 'w') as last_tweet_file:
last_tweet_file.write(str(tweet['id']))
return 0
except ValueError:
return 1
try:
yet_to_post = get_tweets(user, bearer, since_id=last_tweet_id)
except ValueError:
return 1
# If the list is empty, there are no new IDs, so just return
if not yet_to_post:
return 0
# Send the Tweets to Discord
# Last one in list is the oldest, so send it reversed
for tweet in reversed(yet_to_post):
discord_response = post(webhook, json=build_payload(tweet))
sleep(0.5)
# Write the new last known ID
with open(last_tweet, 'w') as last_tweet_file:
last_tweet_file.write(str(yet_to_post[0]['id']))
return 0
if __name__ == '__main__':
load_dotenv(join(dirname(__file__), '.env'))
USERNAME = getenv('USERNAME')
LAST_TWEET = getenv('LAST_TWEET_FILE')
BEARER = getenv('TWITTER_BEARER')
WEBHOOK = getenv('WEBHOOK')
exit(send_to_discord(USERNAME, LAST_TWEET, BEARER, WEBHOOK))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment