Retweet Be Gone Revisited
Published: Updated:
Note: This project wasn’t on my GitHub profile until I wrote this article. I’m including this in the My Git Revisited series of posts because I’ve worked on it for months.
When I was still on Twitter, I hated retweets since they drowned the voices of the people I followed with random nonsense instead. Thankfully, Twitter lets you hide retweets from a user. However, you had to do it for each individual user which is hard when you follow 100+ people. Since I can program and Twitter’s API lets me hide tweets from a user through the friendships/update
endpoint, I made a script retweet-be-gone to do this for me.
I had one restriction while making the app–I had to write it from in scratch without a Twitter library. I would love to claim that this was to challenge myself but the real reason was that I was coding on an iPhone. I used the Python IDE Pythonista which is great except for its bad third-party package management. They used to have a Twitter library, but that no longer works since Apple removed Twitter integration from iOS.
To use Twitter’s API, you need to authorize requests using OAuth 1. It’s a lot of fun, especially if you don’t have a library to do the work for you. Just look at the imports
from base64 import urlsafe_b64encode
from hashlib import sha1
import hmac
import json
import pprint
import secrets
from urllib.parse import quote_plus, quote
from time import time
import notification #Pythonista Specific Library
import requests
As confusing as they are, I needed all of them. After all, I didn’t have a library to do all the hard work for me. After all the helper functions, the functionality to delete retweets ended up like this
def get_friends():
url = 'https://api.twitter.com/1.1/friends/ids.json'
method = "GET"
oauth = make_oauth_obj()
sign_string = make_sign_str(method, url, oauth, {})
oauth["oauth_signature"] = signrequest(sign_string)
headers = {"Authorization":make_auth_header(oauth)}
r = requests.get(url, headers=headers)
ids = r.json()['ids']
return ids
def get_friends_with_hidden_retweets():
url = 'https://api.twitter.com/1.1/friendships/no_retweets/ids.json'
method = "GET"
oauth = make_oauth_obj()
sign_string = make_sign_str(method, url, oauth, {})
oauth["oauth_signature"] = signrequest(sign_string)
headers = {"Authorization":make_auth_header(oauth)}
r = requests.get(url, headers=headers)
ids = r.json()
return ids
def remove_retweets(id):
url = "https://api.twitter.com/1.1/friendships/update.json"
method = "POST"
oauth = make_oauth_obj()
args = {"user_id":id, "retweets":False}
sign_string = make_sign_str(method, url, oauth, args)
oauth["oauth_signature"] = signrequest(sign_string)
headers = {"Authorization":make_auth_header(oauth)}
r = requests.post(url, data=args, headers=headers)
return r
def main():
friends = get_friends()
unretweeted = get_friends_with_hidden_retweets()
retweeded = list(filter(lambda x:x not in unretweeted, friends))
log = []
print(retweeded)
try:
for user in retweeded:
out = remove_retweets(user)
log.append(out)
except Exception as e:
print("Something went Wrong")
raise(e)
print("All done!")
Once I finished this, James Gunn got exposed for some nasty old tweets. Fearing that something like this would happen to me, I modified the script so I could mass delete my tweets and liked tweets1. It wasn’t too much work since I unintentionally decoupled my code once I realized that the OAuth workflow was similar for every API endpoint. I guess I discovered software engineering methodology from first principles. As I added other tasks, I forgot to put the initial functionality in its own function. So to use it, I’d have to comment out the irrelevant code to actually use the script.
In all, this was a rather fun project although I don’t need it anymore since I don’t have Twitter anymore. Also, I doubt this script follows Twitter’s guidelines against scripted data. Given what I learned about manually implementing OAuth for Twitter, I might write a post on how it to it.
It’s hard to delete things from the internet. Besides the technical challenges involved with deleting things [VIDEO], you still have screenshots and archives to worry about. ↩︎