Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building Malaysia Vaccine Tracker Twitter Bot

Building Malaysia Vaccine Tracker Twitter Bot

In this session, you will learn how to make the Malaysia vaccination data easier to understand by building a Twitter bot (Malaysia Vaccine Tracker, twitter.com/MYVaccineCount) with the help of some cool Google technologies like Google Cloud Vision API, Google Sheets API, Firebase and more.

Henry Lim

July 10, 2021
Tweet

More Decks by Henry Lim

Other Decks in Programming

Transcript

  1. Building Malaysia Vaccine Tracker Twitter Bot Henry Lim 󰞦 Google

    Developer Expert, Web Technologies Creator of Remote for Slides Last modified: 10 July 2021
  2. covidbucketbbc.s3-ap-southeast-1.amazonaws.com/heatdata.json { "data": [ { "nme": "Malaysia", "regtotal": "13046616", "pop_18":

    "24259200", "vakdose1": 2835178, "vakdose2": 1267758, "vakdosecomplete": 2835178 }, { "nme": "Johor", "regtotal": "1621696", "pop_18": "2838600", "vakdose1": "265985", "vakdose2": "117725", "vakdosecomplete": "265985" }, ... ], "updated": 1623340740000 }
  3. Search Tweet(s) const q = { "query": "from:JKJAVMY \"Total cumulative

    dose administered as\"", "start_time": "2021-06-11T00:00:00.000Z", "expansions": "attachments.media_keys", "tweet.fields": "attachments", "media.fields": "url", } const query = Object.keys(q).map((key) => `${key}=${encodeURIComponent(q[key])}`).join("&"); // Get Bearer Token from developer.twitter.com/en/portal/dashboard const headers = {Authorization: this.config.twitterToken}; // Call "search/recent" API // developer.twitter.com/en/docs/twitter-api/tweets/search/introduction const baseUrl = "https://api.twitter.com/2/tweets/search/recent"; const tweetReq = await fetch(`${baseUrl}?${query}`, {headers}); const tweetRes = await tweetReq.json(); return tweetRes;
  4. Detect Text using Google Cloud Vision API const Vision =

    require("@google-cloud/vision"); const vision = new Vision.ImageAnnotatorClient(); const imageReq = await fetch(imageUrl); const imageBuffer = await imageReq.buffer(); const imgContent = imageBuffer.toString("base64"); const [textDetections] = await vision.textDetection({ image: { content: imgContent }, imageContext: { languageHints: ["en", "ms"] }, }); return textDetections.textAnnotations.map((e) => ({ vertices: e.boundingPoly.vertices, text: e.description, }));
  5. Send Tweet const Twitter = require("twitter-lite"); this.twitterClient = new Twitter({

    subdomain: "api" , version: "1.1", consumer_key: config.consumerKey , consumer_secret: config.consumerSecret, access_token_key: config.accessTokenKey , access_token_secret: config.accessTokenSecret }); const text = ` 1st dose: 21.372% (+0.634 p.p.) ▓▓▓▓░░░░░░░░░░░░░░░░ 2nd dose: 9.273% (+0.513 p.p.) ▓░░░░░░░░░░░░░░░░░░░ 1st dose: 6,999,554 (+207,795) 2nd dose: 3,036,807 (+168,047) Total doses: 10,036,361 (+375,842) (As of 07/07/21, 23:59) `; await this.twitterClient.post("statuses/update", {status: text});
  6. Send Message to Telegram const FormData = require("form-data"); const sendMsgToTelegram

    = async (text) => { const form = new FormData(); form.append("chat_id", "<telegram_chat_id>"); form.append("text", text); const url = `https://api.telegram.org/<telegram_token>/sendMessage` return await fetch(url, { method: "POST", body: form }); } exports.someCuteNameHere = functions.https.onRequest(async (req, res) => { try { // um ... write some code here ? sendMsgToTelegram("2021-07-08: OK"); return res.send(200); } catch (e) { sendMsgToTelegram(`🚨🚨🚨 ERROR 🚨🚨🚨\n\n${e.stack}`); return res.send(500); } })
  7. const {createCanvas, loadImage} = require("canvas"); // 1. Draw the background

    image (syringe): const img = await loadImage(path.resolve(__dirname, "icon.png")); const canvas = createCanvas(400, 400); const ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0, 400, 400); // 2. Calculate the math for the ring: const radius = 200 - (28 / 2); const angle = Math.PI * 1.5; const offset = ((28 / 2) / (Math.PI * 400) * 360) * (Math.PI / 180); ctx.lineWidth = 28; ctx.lineCap = "round"; // 3. Outer Ring: ctx.beginPath(); ctx.arc(200, 200, radius, angle, (Math.PI * 2) + angle, false); ctx.strokeStyle = "#2c41a4"; ctx.stroke(); // 4. First Dose Ring: ctx.beginPath(); ↓ 1st dose % ctx.arc(200, 200, radius, angle + offset, (21.372 / 100 * Math.PI * 2) + angle - offset, false); ctx.strokeStyle = "#c0f4b8"; ctx.stroke(); // 5. Second Dose Ring: ctx.beginPath(); ↓ 2nd dose % ctx.arc(200, 200, radius, angle + offset, (9.273 / 100 * Math.PI * 2) + angle - offset, false); ctx.strokeStyle = "#34cf1c"; ctx.stroke(); // 6. Convert to base64 and POST account/update_profile_image: await this.twitterClient.post("account/update_profile_image", {image: canvas.toDataURL().split(",")[1]});
  8. Resources MY Vaccine Tracker Twitter: twitter.com/MYVaccineCount MY Vaccine Tracker Google

    Sheets: bit.ly/malaysia-vaccine-data CITF GitHub: github.com/CITF-Malaysia/citf-public Terrasect: github.com/tesseract-ocr/tesseract Firebase: firebase.google.com Cloud Vision API: cloud.google.com/vision twitter-lite: npmjs.com/package/twitter-lite chartjs-node-canvas: npmjs.com/package/chartjs-node-canvas google-spreadsheet: npmjs.com/package/google-spreadsheet chartjs-node-canvas: npmjs.com/package/chartjs-node-canvas cron-job.org: cron-job.org Telegram Bot API: core.telegram.org/bots/api