Skip to content

Commit

Permalink
Filter tweets using GPT (#8)
Browse files Browse the repository at this point in the history
* Install OpenAI API

* Use GPT 3.5 to filter tweets

* Fix typo

* Attempt to fix the dollar cutoff bug
  • Loading branch information
Jeto143 committed May 8, 2024
1 parent 4a9a798 commit 0acee9c
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 28 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ env:
SERVICE_NAME: 5m5v-bot
DEPLOY_HOST: 206.189.96.198
DEPLOY_USER: deploy
BOT_CONFIG: ${{ secrets.BOT_CONFIG }}

jobs:
deploy:
Expand Down Expand Up @@ -53,7 +54,7 @@ jobs:
ssh -o StrictHostKeyChecking=no ${DEPLOY_USER}@${DEPLOY_HOST} -C
"
mkdir -p \${HOME}/${SERVICE_NAME} &&
echo \"${{ secrets.BOT_CONFIG }}\" > \${HOME}/${SERVICE_NAME}/.env &&
echo \"${BOT_CONFIG}\" > \${HOME}/${SERVICE_NAME}/.env &&
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin &&
docker pull ${{ steps.meta.outputs.tags }} &&
docker rm -f ${SERVICE_NAME} &&
Expand Down
69 changes: 42 additions & 27 deletions 5m5v-bot.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,22 @@
require('dotenv').config();

if ([
'TWEETS_API_ENDPOINT',
'TWEETS_API_KEY',
'TWITTER_EMAIL',
'TWITTER_USERNAME',
'TWITTER_PASSWORD',
'TWEETS_API_ENDPOINT',
'TWEETS_API_KEY',
'OPENAI_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 { OpenAI } = require('openai');
const axios = require('axios');

const TweetFilter = require('./lib/filter');
const getAiPrompt = require('./data/prompt');
const util = require('./lib/util');

const pollingIntervalMs = 21 * 60 * 1000;
Expand All @@ -30,15 +32,15 @@ const languageKeys = {
german: 'de',
};

const tweetFilter = new TweetFilter([], Object.keys(languageKeys));

const streamFilter = {
includeWords: [
util.trackedTerms.map(term => `"${term}"`).join(' OR '),
'"want to" OR "would like" OR "thinking of" OR "should try" OR "planning on" OR "souhaite" OR "veux" OR "ai envie de" OR "aspire à" OR "espère" OR "quiero" OR "deseo" OR "tengo ganas de" OR "aspiro a" OR "will" OR "möchte" OR "begehre" OR "verlange" OR "sehne mich nach" OR "erwünsche"',
'"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 "planning on" OR "I wish" OR "Je souhaite" OR "Je veux" OR "J\'ai envie de" OR "J\'espère" OR "Quiero" OR "Deseo" OR "Tengo ganas de" OR "Estoy pensando en" OR "He decidido" OR "Planeo" OR "Me estoy planteando" OR "Voy a" OR "Mi intención es" OR "Ich will" OR "Ich möchte" OR "Ich beabsichtige" OR "Ich habe vor"',
],
};

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

let twitterApiKey = null;

(async () => {
Expand All @@ -52,27 +54,29 @@ let twitterApiKey = null;

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}`);
const lang = await findMatchingLanguageKey(tweet.fullText);

if (lang === null) {
continue;
}

console.log('Found tweet', tweet.id, tweet.fullText, lang);

try {
if (!isDryRun) {
await axios.post(process.env.TWEETS_API_ENDPOINT, {
lang,
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) {
Expand Down Expand Up @@ -111,6 +115,17 @@ async function loginToTwitter() {
}
}

async function findMatchingLanguageKey(tweetText) {
const completion = await openai.chat.completions.create({
messages: [{ role: 'user', content: getAiPrompt(tweetText) }],
model: 'gpt-3.5-turbo',
});

const response = completion.choices[0].message.content.trim();

return ['en', 'fr', 'es', 'de'].includes(response) ? response : null;
}

function buildTweetPayload(tweet) {
return {
id: tweet.id,
Expand Down
9 changes: 9 additions & 0 deletions data/prompt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = (tweetText) => `
Does the author, in ANY part of their message, state their personal intention/desire/inclination to go vegan?
- If so, return the tweet's language in two letters ('en' for English, 'es' for Spanish, 'fr' for French, 'de' for German).
- In any other case, or if the author clearly expressed that they won't go vegan, return 'IGNORE'.
---
${tweetText}
`;
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"dependencies": {
"axios": "^1.6.8",
"dotenv": "^16.4.5",
"openai": "^4.40.0",
"rettiwt-api": "2.7.1"
},
"devDependencies": {
Expand Down
125 changes: 125 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -137,18 +137,54 @@
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"

"@types/node-fetch@^2.6.4":
version "2.6.11"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.11.tgz#9b39b78665dae0e82a08f02f4967d62c66f95d24"
integrity sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==
dependencies:
"@types/node" "*"
form-data "^4.0.0"

"@types/node@*":
version "20.12.8"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.8.tgz#35897bf2bfe3469847ab04634636de09552e8256"
integrity sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==
dependencies:
undici-types "~5.26.4"

"@types/node@^18.11.18":
version "18.19.31"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.31.tgz#b7d4a00f7cb826b60a543cebdbda5d189aaecdcd"
integrity sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA==
dependencies:
undici-types "~5.26.4"

"@types/validator@^13.11.8":
version "13.11.9"
resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.11.9.tgz#adfe96520b437a0eaa798a475877bf2f75ee402d"
integrity sha512-FCTsikRozryfayPuiI46QzH3fnrOoctTjvOYZkho9BTFLCOZ2rgZJHMOVgCOfttjPJcgOx52EpkY0CMfy87MIw==

abort-controller@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==
dependencies:
event-target-shim "^5.0.0"

agent-base@^7.0.2:
version "7.1.0"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434"
integrity sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==
dependencies:
debug "^4.3.4"

agentkeepalive@^4.2.1:
version "4.5.0"
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923"
integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==
dependencies:
humanize-ms "^1.2.1"

ajv@^6.12.3:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
Expand Down Expand Up @@ -534,6 +570,11 @@ esprima@^4.0.0:
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==

event-target-shim@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==

events-to-array@^1.0.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/events-to-array/-/events-to-array-1.1.2.tgz#2d41f563e1fe400ed4962fe1a4d5c6a7539df7f6"
Expand Down Expand Up @@ -603,6 +644,11 @@ forever-agent@~0.6.1:
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=

[email protected]:
version "1.7.2"
resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040"
integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==

[email protected], form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
Expand All @@ -621,6 +667,14 @@ form-data@~2.3.2:
combined-stream "^1.0.6"
mime-types "^2.1.12"

formdata-node@^4.3.2:
version "4.4.1"
resolved "https://registry.yarnpkg.com/formdata-node/-/formdata-node-4.4.1.tgz#23f6a5cb9cb55315912cbec4ff7b0f59bbd191e2"
integrity sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==
dependencies:
node-domexception "1.0.0"
web-streams-polyfill "4.0.0-beta.3"

fs-exists-cached@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs-exists-cached/-/fs-exists-cached-1.0.0.tgz#cf25554ca050dc49ae6656b41de42258989dcbce"
Expand Down Expand Up @@ -734,6 +788,13 @@ [email protected]:
agent-base "^7.0.2"
debug "4"

humanize-ms@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed"
integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==
dependencies:
ms "^2.0.0"

imurmurhash@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
Expand Down Expand Up @@ -1029,11 +1090,28 @@ [email protected]:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==

ms@^2.0.0:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==

nested-error-stacks@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz#26c8a3cee6cc05fbcf1e333cd2fc3e003326c0b5"
integrity sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==

[email protected]:
version "1.0.0"
resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==

node-fetch@^2.6.7:
version "2.7.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
dependencies:
whatwg-url "^5.0.0"

nodeunit@^0.11.3:
version "0.11.3"
resolved "https://registry.yarnpkg.com/nodeunit/-/nodeunit-0.11.3.tgz#313afae26cd11b407b731ff774b8e35e5d6f9568"
Expand Down Expand Up @@ -1095,6 +1173,20 @@ once@^1.3.0:
dependencies:
wrappy "1"

openai@^4.40.0:
version "4.40.0"
resolved "https://registry.yarnpkg.com/openai/-/openai-4.40.0.tgz#6cf11702ca009ead10b61061d9e0a1635c6ac307"
integrity sha512-ofh9qMxRPDSZTWYvifScusMfnyIwEQL3w+fv3ucQGn3cIn0W6Zw4vXSUod8DwYfcX/hkAx9/ZvWrdkFYnVXlmQ==
dependencies:
"@types/node" "^18.11.18"
"@types/node-fetch" "^2.6.4"
abort-controller "^3.0.0"
agentkeepalive "^4.2.1"
form-data-encoder "1.7.2"
formdata-node "^4.3.2"
node-fetch "^2.6.7"
web-streams-polyfill "^3.2.1"

opener@^1.5.1:
version "1.5.2"
resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598"
Expand Down Expand Up @@ -1638,6 +1730,11 @@ tough-cookie@~2.5.0:
psl "^1.1.28"
punycode "^2.1.1"

tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==

trivial-deferred@^1.0.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/trivial-deferred/-/trivial-deferred-1.1.2.tgz#6b07aa1eb045f6128b8b30673b040f99bfe64a2e"
Expand Down Expand Up @@ -1676,6 +1773,11 @@ typescript@^3.3.3:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8"
integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==

undici-types@~5.26.4:
version "5.26.5"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==

unicode-length@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/unicode-length/-/unicode-length-1.0.3.tgz#5ada7a7fed51841a418a328cf149478ac8358abb"
Expand Down Expand Up @@ -1723,6 +1825,29 @@ [email protected]:
core-util-is "1.0.2"
extsprintf "^1.2.0"

[email protected]:
version "4.0.0-beta.3"
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz#2898486b74f5156095e473efe989dcf185047a38"
integrity sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==

web-streams-polyfill@^3.2.1:
version "3.3.3"
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b"
integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==

webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==

whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"

which-module@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
Expand Down

0 comments on commit 0acee9c

Please sign in to comment.