Skip to content

Commit

Permalink
Handle login + auto re-login when bot account gets logged out (#5)
Browse files Browse the repository at this point in the history
* Handle login and re-logins

* Update README

* Fix log being in the wrong place

* Fix dry run case

* Hide API key from logs

* CS

* Check that config is valid

* Simplify condition

* Switch back to base package
  • Loading branch information
Jeto143 authored Apr 25, 2024
1 parent 76ea1cc commit 7cd4ef6
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 37 deletions.
67 changes: 51 additions & 16 deletions 5m5v-bot.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const fs = require('fs');
const yaml = require('js-yaml');
const { Rettiwt } = require('@jeto314/rettiwt-api');
const { Rettiwt } = require('rettiwt-api');
const TweetFilter = require('./lib/filter');
const util = require('./lib/util');

Expand All @@ -13,34 +13,69 @@ try {
throw `Unable to load config file: ${error.message}`;
}

if ((config?.users || []).length === 0) {
throw 'No users defined in config file';
}

if (!config.users.every(user => ['language', 'email', 'username', 'password'].every(key => key in (user ?? [])))) {
throw 'At least one user is missing required fields in config file';
}

const languages = config.users.map(user => user.language);
const pollingIntervalMs = 40 * 1000;
const retweetDelayMs = config.delaytime || 2 * 60 * 1000;
const isDryRun = process.argv[2] === '--dry-run';

const tweetFilter = new TweetFilter(config.exclude, languages);
const rettiwt = new Rettiwt({ apiKey: config.users[0].api_key });

console.log(isDryRun ? 'Looking for new tweets (dry run)...' : 'Looking for new tweets...');
async function getApiKey(user) {
if (!user.apiKey) {
console.log(`Logging in as ${user.username}...`);

user.apiKey = await new Rettiwt().auth.login(user.email, user.username, user.password);

console.log('Logged in!');
}

return user.apiKey;
}

(async () => {
for await (const tweet of rettiwt.tweet.stream({ includeWords: [util.trackedTerms.map(term => `"${term}"`).join(' OR ')] }, pollingIntervalMs)) {
const matchingLanguages = tweetFilter.matches(tweet) || [];
while (true) {
const rettiwt = new Rettiwt({ apiKey: await getApiKey(config.users[0]) });

console.log(isDryRun ? 'Looking for new tweets (dry run)...' : 'Looking for new tweets...');

try {
for await (const tweet of rettiwt.tweet.stream({ includeWords: [util.trackedTerms.map(term => `"${term}"`).join(' OR ')] }, pollingIntervalMs)) {
const matchingLanguages = tweetFilter.matches(tweet) || [];

for (const language of matchingLanguages) {
await new Promise(resolve => setTimeout(resolve, retweetDelayMs));
for (const language of matchingLanguages) {
await new Promise(resolve => setTimeout(resolve, retweetDelayMs));

try {
if (!isDryRun) {
const apiKey = config.users.find(user => user.language === language)?.api_key ?? null;
const rettiwt = new Rettiwt({ apiKey });
const user = config.users.find(user => user.language === language);

await rettiwt.tweet.retweet(tweet.id);
try {
if (!isDryRun) {
const rettiwt = new Rettiwt({ apiKey: await getApiKey(user) });

await rettiwt.tweet.retweet(tweet.id);
}

console.log(`Retweeted tweet ${tweet.id} in ${language}:\n${tweet.fullText}`);
} catch (error) {
console.error(`Unable to retweet ${tweet.id} in ${language}: ${error.message}`);

if (error.constructor.name === 'RettiwtError' && error.code === 32) {
user.apiKey = null;
}
}
}
}
} catch (error) {
console.error(`Error while streaming tweets: ${error.message}`);

console.log(`Retweeted tweet ${tweet.id} in ${language}:\n${tweet.fullText}`);
} catch (error) {
console.error(`Unable to retweet ${tweet.id} in ${language}: ${error.message}`);
if (error.constructor.name === 'RettiwtError' && error.code === 32) {
config.users[0].apiKey = null;
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions 5m5v-config.yaml.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
users:
- api_key: Aa0Aa0Aa0Aa0Aa0Aa0Aa0Aa0Aa0Aa0Aa0Aa0Aa0Aa0Aa0
language: english
- language: 'english'
username: 'user1'
email: '[email protected]'
password: 'password123#@!'
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,14 @@ The configuration is loaded from `5m5v-config.yaml` in the working directory, wh

#### `users`
A list containing all users to retweet with. The first user listed will be used to search tweets with.
#### `users.api_key`
To obtain the API key for a given Twitter user, follow the [instructions from the Rettiwt-API package](https://github.com/Rishikant181/Rettiwt-API#authentication).
#### `users.language`
The language that this user will be retweeting matches from.
#### `users.username`
The Twitter username of that user (without the @).
#### `users.email`
The email address of the Twitter account.
#### `users.password`
The password of the Twitter account.
#### `exclude` *(optional)*
A list of keywords that if found in a Tweet will exclude that Tweet from being retweeted.
#### `delaytime` *(optional)*
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"main": "5m5v-bot.js",
"dependencies": {
"js-yaml": "^3.13.1",
"@jeto314/rettiwt-api": "1.0.0"
"rettiwt-api": "2.7.1"
},
"devDependencies": {
"nodeunit": "^0.11.3"
Expand Down
32 changes: 16 additions & 16 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -105,18 +105,6 @@
"@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0"

"@jeto314/[email protected]":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@jeto314/rettiwt-api/-/rettiwt-api-1.0.0.tgz#bff3544713df4a5f1e4190ee7c8545ff80b81791"
integrity sha512-8efcjpB+wBXdOrWcBdz/m58LTYC8JQ5PQqPMGEoQ1z2CGNmr6YUP/+as8cauPHd131CdtIROzOtwhkg46mytNQ==
dependencies:
axios "1.6.3"
class-validator "0.14.1"
commander "11.1.0"
https-proxy-agent "7.0.2"
rettiwt-auth "2.1.0"
rettiwt-core "3.4.0-alpha.2"

"@jridgewell/gen-mapping@^0.3.2":
version "0.3.3"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098"
Expand Down Expand Up @@ -1318,6 +1306,18 @@ resolve@^1.10.0:
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"

[email protected]:
version "2.7.1"
resolved "https://registry.yarnpkg.com/rettiwt-api/-/rettiwt-api-2.7.1.tgz#8721606d08222d7034652782f7a09be53d28ad49"
integrity sha512-Q0757mFJTY70jFCFjLjzy1Dz/DIPR+HhnQaFBxz9HUZ4Dp9CStC/4OvEq6I3P8UlXTA3HVexTlevxRp9PHrnqw==
dependencies:
axios "1.6.3"
class-validator "0.14.1"
commander "11.1.0"
https-proxy-agent "7.0.2"
rettiwt-auth "2.1.0"
rettiwt-core "3.4.0"

[email protected]:
version "2.1.0"
resolved "https://registry.yarnpkg.com/rettiwt-auth/-/rettiwt-auth-2.1.0.tgz#43fedd40cab5b775b92a1bef740d7b2a36553463"
Expand All @@ -1328,10 +1328,10 @@ [email protected]:
cookiejar "2.1.4"
https-proxy-agent "7.0.2"

[email protected]-alpha.2:
version "3.4.0-alpha.2"
resolved "https://registry.yarnpkg.com/rettiwt-core/-/rettiwt-core-3.4.0-alpha.2.tgz#d3033d0db7a05f503e3b0aef5981d33897f0d08b"
integrity sha512-775cDTPWZXhY+znEOsjupIgj/Mmi8shKhLveShm1KnXEX1Qc5SjTEnpQv1dHmDaxiV9n6BIQ73E7K9AucBl+Mg==
[email protected]:
version "3.4.0"
resolved "https://registry.yarnpkg.com/rettiwt-core/-/rettiwt-core-3.4.0.tgz#515aa168a5a64effa6cb2340debafebf5d4ee8a1"
integrity sha512-7N2wV+AB5fJVMzyE0s6FS9jZ/4z/eXIM1DlhOqeWous36PZSDPprINIcdPM8LVmfMEpq4aanplHkqCheNGLXBw==
dependencies:
axios "1.6.3"
class-validator "0.14.1"
Expand Down

0 comments on commit 7cd4ef6

Please sign in to comment.