Skip to content

Instantly share code, notes, and snippets.

@maxhawkins
Last active March 12, 2020 04:14
Show Gist options
  • Save maxhawkins/e9ada815e27ff3dfcc63f316f6f1e74c to your computer and use it in GitHub Desktop.
Save maxhawkins/e9ada815e27ff3dfcc63f316f6f1e74c to your computer and use it in GitHub Desktop.
Random Twitter feed

I wrote this script to wrest some control over the content I see from the Twitter algorithm.

It mutes everyone I follow and un-mutes 30 random people.

You can run it as a scheduled Firebase Cloud Function so you have a new feed every day.

@maxhawkins

// First you need to generate a pair of OAuth client tokens that give
// you access to your Twitter account.
//
// 1. Create a Twitter developer application
// 2. Get the oauth key pair
// 3. Enter it in the OAuth constructor below
// 4. Run the script
//
// Read more here: https://developer.twitter.com/en/docs/basics/authentication/oauth-1-0a/obtaining-user-access-tokens
const { OAuth } = require("oauth");
const { createInterface } = require("readline");
const client = new OAuth(
"https://api.twitter.com/oauth/request_token",
"https://api.twitter.com/oauth/access_token",
"<oauth_consumer_key>",
"<oauth_consumer_secret>",
"1.0A",
null,
"HMAC-SHA1"
);
const fetchToken = () => {
const stdin = createInterface(process.stdin);
client.getOAuthRequestToken(
{ oauth_callback: "oob" },
(error, token, secret) => {
if (error) {
console.error(error);
stdin.close();
return;
}
const url = "https://twitter.com/oauth/authenticate?oauth_token=" + token;
console.log("Go to " + url + " and enter the code here:");
stdin.question("> ", verifier => {
client.getOAuthAccessToken(
token,
secret,
verifier,
(err, userToken, userSecret) => {
if (err) {
console.error(err);
stdin.close();
return;
}
console.log(`userToken = "${userToken}"`);
console.log(`userSecret = "${userSecret}"`);
stdin.close();
}
);
});
}
);
};
fetchToken();
// Run this script with the user credentials generated in the previous step.
// It will unmute 30 random people you follow and mute everyone else.
const { OAuth } = require("oauth");
const client = new OAuth(
"https://api.twitter.com/oauth/request_token",
"https://api.twitter.com/oauth/access_token",
"<twitter oauth token>",
"<twitter oauth secret>",
"1.0A",
null,
"HMAC-SHA1"
);
const yourTwitterHandle = "<your twitter handle without the @>";
const numToUnmute = 30;
const userToken = "<client token from previous step>";
const userSecret = "<client secret from previous step>";
// Twitter API methods
const send = (method, url) => {
return new Promise((resolve, reject) => {
client.getProtectedResource(
"https://api.twitter.com/1.1" + url,
method,
userToken,
userSecret,
(err, data) => {
if (err) {
reject(err);
} else {
resolve(JSON.parse(data.toString()));
}
}
);
});
};
const getMuted = () => send("GET", "/mutes/users/ids.json");
const getFollowing = screenName =>
send("GET", "/friends/ids.json?screen_name=" + screenName);
const mute = id => send("POST", "/mutes/users/create.json?user_id=" + id);
const unmute = id => send("POST", "/mutes/users/destroy.json?user_id=" + id);
// Back-off / retry logic
const sleep = duration => new Promise(resolve => setTimeout(resolve, duration));
const withRetry = async (func, maxRetries = 25) => {
let retries = 0;
while (retries < maxRetries) {
try {
await func();
return;
} catch (error) {
if (error.statusCode !== 429) {
throw error;
}
retries++;
}
const backoff = Math.min(Math.pow(2, retries), 30) + Math.random();
await sleep(backoff * 1000);
}
throw new Error('timed out');
};
// This is the main function
const switchTwitterMutes = async () => {
console.log("fetching followers for", yourTwitterHandle);
let { ids: following } = await getFollowing(yourTwitterHandle);
const { ids: muted } = await getMuted();
// Shuffle list of people you follow
following = following.sort(() => Math.random() - 0.5);
// Choose 30 random people to un-mute
const unmuted = [];
while (unmuted.length < numToUnmute) {
const id = following.pop();
try {
await withRetry(() => unmute(id));
unmuted.push(id);
console.log("unmuted userid", id);
} catch (err) {
console.warn("error unmuting userid", id, ":", err.statusCode);
}
await sleep(1000);
}
// Mute everyone else
const toMute = following.filter(
id => !muted.includes(id) && !unmuted.includes(id)
);
for (const id of toMute) {
try {
await withRetry(() => mute(id));
console.log("muted userid", id);
} catch (error) {
console.warn("error: muting userid", id, ":", error.statusCode);
}
await sleep(1000);
}
console.log("Done!");
};
switchTwitterMutes().catch(error => console.error(error));
// To run the script every day, add this code to a Firebase Cloud Function
// then comment the previous line and uncomment the following lines:
//
// const functions = require("firebase-functions");
//
// export const onCheckTwitter = functions.pubsub
// .schedule("2 20 * * *") // run every day at 2:20am
// .onRun(() => switchTwitterMutes());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment