forked from plorry/VegAssist
-
Notifications
You must be signed in to change notification settings - Fork 1
/
5m5v-bot.js
127 lines (104 loc) · 3.62 KB
/
5m5v-bot.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/env node
require('dotenv').config();
if ([
'TWITTER_EMAIL',
'TWITTER_USERNAME',
'TWITTER_PASSWORD',
'TWEETS_API_ENDPOINT',
'TWEETS_API_KEY',
].some(key => !process.env[key])) {
console.error('One or more required environment variables are missing. Exiting.');
process.exit(1);
}
const { Rettiwt } = require('rettiwt-api');
const axios = require('axios');
const TweetFilter = require('./lib/filter');
const pollingIntervalMs = 21 * 60 * 1000;
const retryLoginDelayMs = 5 * 60 * 1000;
const isDryRun = process.argv[2] === '--dry-run';
const languageKeys = {
english: 'en',
french: 'fr',
spanish: 'es',
german: 'de',
};
const tweetFilter = new TweetFilter([], Object.keys(languageKeys));
const streamFilter = {
includeWords: [
'"vegan" OR "végétalien" OR "végétalienne" OR "végane" OR "vegano" OR "vegana"',
'"I want to" OR "I would like" OR "thinking of" OR "I should try" OR "plan on" OR "I wish" OR "devenir" OR "je veux" OR "j\'ai envie" OR "je voudrais" OR "j\'aimerais" OR "Quiero" OR "Deseo" OR "Estoy pensando en" OR "Voy a" OR "Ich will" OR "Ich möchte" OR "Ich beabsichtige" OR "Ich habe vor"',
],
};
let twitterApiKey = null;
(async () => {
twitterApiKey = await loginToTwitter();
while (true) {
const rettiwt = new Rettiwt({ apiKey: twitterApiKey });
console.log(isDryRun ? 'Looking for new tweets (dry run)...' : 'Looking for new tweets...');
console.log('Search filter:', streamFilter);
try {
for await (const tweet of rettiwt.tweet.stream(streamFilter, pollingIntervalMs)) {
const matchingLanguages = tweetFilter.matches(tweet) || [];
console.log('Found tweet', tweet.id, tweet.fullText, matchingLanguages);
for (const language of matchingLanguages) {
try {
if (!isDryRun) {
await axios.post(process.env.TWEETS_API_ENDPOINT, {
lang: languageKeys[language],
tweets: [buildTweetPayload(tweet)],
}, {
headers: {
'X-API-KEY': process.env.TWEETS_API_KEY,
},
});
}
console.log(`Sent tweet ${tweet.id} in ${language}:\n${tweet.fullText}`);
} catch (error) {
console.error(`Unable to send tweet ${tweet.id} in ${language}: ${error.message}`);
}
}
}
} catch (error) {
console.error(`Error while streaming tweets: ${error.message}`);
if (error.constructor.name === 'RettiwtError' && error.code === 32) {
twitterApiKey = await loginToTwitter();
}
}
}
})();
async function loginToTwitter() {
while (true) {
try {
console.log('Logging in...');
const apiKey = await new Rettiwt().auth.login(
process.env.TWITTER_EMAIL,
process.env.TWITTER_USERNAME,
process.env.TWITTER_PASSWORD,
);
if (!apiKey) {
throw new Error('No API key returned');
}
console.log('Logged in!');
return apiKey;
} catch (e) {
console.error(`Unable to log in: ${e.message}`);
await new Promise(resolve => setTimeout(resolve, retryLoginDelayMs));
}
}
}
function buildTweetPayload(tweet) {
return {
id: tweet.id,
date: tweet.createdAt,
text: tweet.fullText,
from_user_name: tweet.tweetBy.userName,
from_full_name: tweet.tweetBy.fullName,
from_profile_image: tweet.tweetBy.profileImage,
view_count: ~~tweet.viewCount,
like_count: ~~tweet.likeCount,
reply_count: ~~tweet.replyCount,
retweet_count: ~~tweet.retweetCount,
quote_count: ~~tweet.quoteCount,
media: (tweet.media ?? []).map(media => ({ ...media })),
};
}