commit 068037620a8c7ef40075ab622664ac274a7f937d Author: Matei Adriel Date: Fri Sep 3 15:48:19 2021 +0300 typescript(globalbot): feat: implemented basic fetching Signed-off-by: prescientmoon diff --git a/typescript/globalbot/.gitignore b/typescript/globalbot/.gitignore new file mode 100644 index 0000000..6ed48a9 --- /dev/null +++ b/typescript/globalbot/.gitignore @@ -0,0 +1,2 @@ +.env +node_modules diff --git a/typescript/globalbot/package.json b/typescript/globalbot/package.json new file mode 100644 index 0000000..948e1c7 --- /dev/null +++ b/typescript/globalbot/package.json @@ -0,0 +1,14 @@ +{ + "type": "module", + "name": "global-bot", + "scripts": {}, + "dependencies": { + "dotenv": "^10.0.0", + "twitter-api-v2": "^1.4.1" + }, + "devDependencies": { + "@types/node": "^16.7.10", + "ts-node-dev": "^1.1.8", + "typescript": "^4.4.2" + } +} diff --git a/typescript/globalbot/pnpm-lock.yaml b/typescript/globalbot/pnpm-lock.yaml new file mode 100644 index 0000000..ad8ab88 --- /dev/null +++ b/typescript/globalbot/pnpm-lock.yaml @@ -0,0 +1,395 @@ +dependencies: + dotenv: 10.0.0 + twitter-api-v2: 1.4.1 +devDependencies: + '@types/node': 16.7.10 + ts-node-dev: 1.1.8_typescript@4.4.2 + typescript: 4.4.2 +lockfileVersion: 5.1 +packages: + /@types/node/16.7.10: + dev: true + resolution: + integrity: sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA== + /@types/strip-bom/3.0.0: + dev: true + resolution: + integrity: sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I= + /@types/strip-json-comments/0.0.30: + dev: true + resolution: + integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== + /anymatch/3.1.2: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.0 + dev: true + engines: + node: '>= 8' + resolution: + integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + /arg/4.1.3: + dev: true + resolution: + integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + /balanced-match/1.0.2: + dev: true + resolution: + integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + /binary-extensions/2.2.0: + dev: true + engines: + node: '>=8' + resolution: + integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + /brace-expansion/1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + resolution: + integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + /braces/3.0.2: + dependencies: + fill-range: 7.0.1 + dev: true + engines: + node: '>=8' + resolution: + integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + /buffer-from/1.1.2: + dev: true + resolution: + integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + /chokidar/3.5.2: + dependencies: + anymatch: 3.1.2 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.1 + normalize-path: 3.0.0 + readdirp: 3.6.0 + dev: true + engines: + node: '>= 8.10.0' + optionalDependencies: + fsevents: 2.3.2 + resolution: + integrity: sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== + /concat-map/0.0.1: + dev: true + resolution: + integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + /create-require/1.1.1: + dev: true + resolution: + integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + /diff/4.0.2: + dev: true + engines: + node: '>=0.3.1' + resolution: + integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + /dotenv/10.0.0: + dev: false + engines: + node: '>=10' + resolution: + integrity: sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== + /dynamic-dedupe/0.3.0: + dependencies: + xtend: 4.0.2 + dev: true + resolution: + integrity: sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE= + /fill-range/7.0.1: + dependencies: + to-regex-range: 5.0.1 + dev: true + engines: + node: '>=8' + resolution: + integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + /fs.realpath/1.0.0: + dev: true + resolution: + integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + /fsevents/2.3.2: + dev: true + engines: + node: ^8.16.0 || ^10.6.0 || >=11.0.0 + optional: true + os: + - darwin + resolution: + integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + /function-bind/1.1.1: + dev: true + resolution: + integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + /glob-parent/5.1.2: + dependencies: + is-glob: 4.0.1 + dev: true + engines: + node: '>= 6' + resolution: + integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + /glob/7.1.7: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.0.4 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + resolution: + integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + /has/1.0.3: + dependencies: + function-bind: 1.1.1 + dev: true + engines: + node: '>= 0.4.0' + resolution: + integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + /inflight/1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + resolution: + integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + /inherits/2.0.4: + dev: true + resolution: + integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + /is-binary-path/2.1.0: + dependencies: + binary-extensions: 2.2.0 + dev: true + engines: + node: '>=8' + resolution: + integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + /is-core-module/2.6.0: + dependencies: + has: 1.0.3 + dev: true + resolution: + integrity: sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ== + /is-extglob/2.1.1: + dev: true + engines: + node: '>=0.10.0' + resolution: + integrity: sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + /is-glob/4.0.1: + dependencies: + is-extglob: 2.1.1 + dev: true + engines: + node: '>=0.10.0' + resolution: + integrity: sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + /is-number/7.0.0: + dev: true + engines: + node: '>=0.12.0' + resolution: + integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + /make-error/1.3.6: + dev: true + resolution: + integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + /minimatch/3.0.4: + dependencies: + brace-expansion: 1.1.11 + dev: true + resolution: + integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + /minimist/1.2.5: + dev: true + resolution: + integrity: sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + /mkdirp/1.0.4: + dev: true + engines: + node: '>=10' + hasBin: true + resolution: + integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + /normalize-path/3.0.0: + dev: true + engines: + node: '>=0.10.0' + resolution: + integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + /once/1.4.0: + dependencies: + wrappy: 1.0.2 + dev: true + resolution: + integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + /path-is-absolute/1.0.1: + dev: true + engines: + node: '>=0.10.0' + resolution: + integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + /path-parse/1.0.7: + dev: true + resolution: + integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + /picomatch/2.3.0: + dev: true + engines: + node: '>=8.6' + resolution: + integrity: sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== + /readdirp/3.6.0: + dependencies: + picomatch: 2.3.0 + dev: true + engines: + node: '>=8.10.0' + resolution: + integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + /resolve/1.20.0: + dependencies: + is-core-module: 2.6.0 + path-parse: 1.0.7 + dev: true + resolution: + integrity: sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + /rimraf/2.7.1: + dependencies: + glob: 7.1.7 + dev: true + hasBin: true + resolution: + integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + /source-map-support/0.5.19: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + resolution: + integrity: sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + /source-map/0.6.1: + dev: true + engines: + node: '>=0.10.0' + resolution: + integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + /strip-bom/3.0.0: + dev: true + engines: + node: '>=4' + resolution: + integrity: sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + /strip-json-comments/2.0.1: + dev: true + engines: + node: '>=0.10.0' + resolution: + integrity: sha1-PFMZQukIwml8DsNEhYwobHygpgo= + /to-regex-range/5.0.1: + dependencies: + is-number: 7.0.0 + dev: true + engines: + node: '>=8.0' + resolution: + integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + /tree-kill/1.2.2: + dev: true + hasBin: true + resolution: + integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== + /ts-node-dev/1.1.8_typescript@4.4.2: + dependencies: + chokidar: 3.5.2 + dynamic-dedupe: 0.3.0 + minimist: 1.2.5 + mkdirp: 1.0.4 + resolve: 1.20.0 + rimraf: 2.7.1 + source-map-support: 0.5.19 + tree-kill: 1.2.2 + ts-node: 9.1.1_typescript@4.4.2 + tsconfig: 7.0.0 + typescript: 4.4.2 + dev: true + engines: + node: '>=0.8.0' + hasBin: true + peerDependencies: + node-notifier: '*' + typescript: '*' + peerDependenciesMeta: + node-notifier: + optional: true + resolution: + integrity: sha512-Q/m3vEwzYwLZKmV6/0VlFxcZzVV/xcgOt+Tx/VjaaRHyiBcFlV0541yrT09QjzzCxlDZ34OzKjrFAynlmtflEg== + /ts-node/9.1.1_typescript@4.4.2: + dependencies: + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + source-map-support: 0.5.19 + typescript: 4.4.2 + yn: 3.1.1 + dev: true + engines: + node: '>=10.0.0' + hasBin: true + peerDependencies: + typescript: '>=2.7' + resolution: + integrity: sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== + /tsconfig/7.0.0: + dependencies: + '@types/strip-bom': 3.0.0 + '@types/strip-json-comments': 0.0.30 + strip-bom: 3.0.0 + strip-json-comments: 2.0.1 + dev: true + resolution: + integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw== + /twitter-api-v2/1.4.1: + dev: false + resolution: + integrity: sha512-jaUPQ2dpdog1iCoS7ExZbOZt+A7duHVt2I/zuaEkStyHpkppJ1Z9AJ5pY0Nxe6FjNOFqtIk4sCvYaKt0qTQHIA== + /typescript/4.4.2: + dev: true + engines: + node: '>=4.2.0' + hasBin: true + resolution: + integrity: sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ== + /wrappy/1.0.2: + dev: true + resolution: + integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + /xtend/4.0.2: + dev: true + engines: + node: '>=0.4' + resolution: + integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + /yn/3.1.1: + dev: true + engines: + node: '>=6' + resolution: + integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== +specifiers: + '@types/node': ^16.7.10 + dotenv: ^10.0.0 + ts-node-dev: ^1.1.8 + twitter-api-v2: ^1.4.1 + typescript: ^4.4.2 diff --git a/typescript/globalbot/src/countries.json b/typescript/globalbot/src/countries.json new file mode 100644 index 0000000..da92986 --- /dev/null +++ b/typescript/globalbot/src/countries.json @@ -0,0 +1,15 @@ +{ + "aliases": { + "USA": "US", + "The Netherlands": "Netherlands" + }, + + "countries": [ + "US", + "Netherlands", + "France", + "Denmark", + "Switzerland", + "Canada" + ] +} diff --git a/typescript/globalbot/src/index.ts b/typescript/globalbot/src/index.ts new file mode 100644 index 0000000..7fcdfe9 --- /dev/null +++ b/typescript/globalbot/src/index.ts @@ -0,0 +1,90 @@ +import TwitterApi, { TweetV2 } from "twitter-api-v2"; +import { config } from "dotenv"; + +config(); + +const api = new TwitterApi(process.env.TOKEN); + +const luke = "GlobeGames1"; +const latest = "1433459713035935749"; +const command = "!vote"; +const prefixes = [command, `@${luke}`]; + +const client = api.readOnly; + +const { aliases, countries } = require("./countries.json"); + +const getCoutntryName = (message: string): string | null => { + const trimmed = message.trim().toLowerCase(); + + for (const prefix of prefixes) + if (trimmed.startsWith(prefix.toLowerCase())) + return getCoutntryName(trimmed.substr(prefix.length)); + + for (const alias in aliases) { + if (trimmed.startsWith(alias.toLowerCase())) return aliases[alias]; + } + + for (const country of countries) { + if (trimmed.startsWith(country.toLowerCase())) return country; + } + + return null; +}; + +const main = async () => { + const data: TweetV2[] = []; + + let token: string | undefined; + let fetches = 0; + + do { + const tweets = await client.v2.search( + `to:${luke} conversation_id:${latest}`, + { + "tweet.fields": ["text", "author_id", "referenced_tweets"], + next_token: token, + } + ); + + data.push(...tweets.data.data); + token = tweets.meta.next_token; + fetches++; + } while (token); + + console.log(`Fetched the twitter api ${fetches} times`); + + const votes = new Map(); + + for (const vote of data) { + if ( + !vote.referenced_tweets.some( + (tweet) => tweet.type === "replied_to" && tweet.id === latest + ) + ) + continue; + + const country = getCoutntryName(vote.text); + + if (country === null) continue; + + votes.set(vote.author_id, country); + } + + const voteResults = new Map(); + + for (const [_author, country] of votes) { + const previousAmount = voteResults.get(country); + + voteResults.set(country, (previousAmount ?? 0) + 1); + } + + const sortedResults = [...voteResults.entries()]; + sortedResults.sort((a, b) => b[1] - a[1]); + + console.log(Object.fromEntries(sortedResults)); +}; + +main().catch((e) => { + process.exit(1); +}); diff --git a/typescript/globalbot/tsconfig.json b/typescript/globalbot/tsconfig.json new file mode 100644 index 0000000..c985888 --- /dev/null +++ b/typescript/globalbot/tsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "module": "CommonJS", + "moduleResolution": "Node", + "target": "ES2017" + } +}