TeknoCerdas.com – Salam cerdas untuk kita semua. WhatsApp adalah salah satu aplikasi yang paling sering digunakan saat ini, hampir menggantikan SMS. Dengan unofficial WhatsApp API Gateway dari API KirimWA.id kita dapat menjadikan WhatsApp memiliki lebih banyak fungsi. Salah satunya adalah menjadikannya sebuah Bot WhatsApp. Tulisan kali ini akan membahas bagaimana membuat WhatsApp Bot untuk melihat trending topics Twitter dengan API KirimWA.id.
Bot atau Chatbot adalah sebuah layanan yang otomatis melakukan respon terhadap chat yang dikirimkan pengguna. Chatbot banyak dimanfaatkan sebagai layanan Customer Service pelanggan. Namun semua itu tergantung dari pengembang bot untuk menginterpretasikan setiap chat yang masuk akan diproses sedemikian rupa.
Baca Juga
Kirim WhatsApp dengan API KirimWA.id dan Node.js
Seri Tutorial WhatsApp API Gateway dengan API KirimWA.id
Tujuan tutorial kali ini adalah kita akan membuat bot yang akan merespon WhatsApp chat atau perintah berikut:
twitter /trends
Jika nomor WhatsApp menerima pesan dengan format tersebut maka otomatis pesan balik yang dikirimkan adalah daftar trending topics dari Twitter.
Daftar Isi
- 0. Persiapan
- 1. Install Dependencies
- 2. Menulis Kode Bot WhatsApp Twitter
- 3. Expose Localhost ke Internet
- 4. Menambahkan Webhook
- 5. Tes Bot WhatsApp
0. Persiapan
Terdapat beberapa hal yang perlu dipenuhi sebelum mengikuti tutorial ini.
- Sudah memiliki API Token dari API KirimWA.id jika belum maka silahkan daftar di https://developer.kirimwa.id/
- Sudah memiliki instalasi Node.js minimal v14.x
- Memiliki HP dan Nomor WhatsApp yang berfungsi
- Telah mengikuti tutorial Kirim WhatsApp dengan API KirimWA.id dan Node.js.
- Ngrok atau Cloudflared untuk membuka localhost ke internet agar webhook dapat diakses server API KirimWA.id.
1. Install Dependencies
Kita akan membuat direktori baru untuk menyimpan kode tutorial yang ada pada artikel ini.
$ mkdir bot-twitter
$ cd bot-twitter
Buat sebuah package.json
baru untuk menginstal Express framework dan Puppeteer.
{
"dependencies": {
"express": "^4.17.1",
"puppeteer": "^10.2.0"
}
}
Kemudian lanjutkan dengan menginstall dependencies atau ketergantungan pustaka.
$ npm install
Express digunakan sebagai webhook yang akan dipanggil oleh API KirimWA.id ketika sebuah pesan masuk. Sedangkan pustaka Puppeter digunakan untuk melakukan scraping Twitter dengan memanfaatkan headless Chromium.
2. Menulis Kode Bot WhatsApp Twitter
Saatnya bagian yang menyenangkan yaitu menulis kode utama bot untuk mendapatkan trending topics Twitter dan mengirimkannya ke WhatsApp pengirim.
Pastikan berada pada direktori kerja bot-twitter
kemudian kita akan membuat index.js
. Ini adalah satu-satunya file yang harus dibuat. Ini adalah bot sederhana jadi cukup satu file saja.
const puppeteer = require('puppeteer');
const express = require('express');
const app = express();
const port = process.env.APP_PORT || 4000;
const caches = {};
const https = require('http');
/**
* @param Object browser
* @return Promise
*/
async function getTwitterTrendingTopics(browser) {
const br = await browser;
const page = await br.newPage();
await page.setUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1');
await page.setViewport({
width: 375,
height: 667,
deviceScaleFactor: 1,
isMobile: true
})
console.log('Opening /i/tends/ page...');
await page.goto('https://mobile.twitter.com/i/trends');
console.log('Waiting content be ready...');
await page.waitForFunction("document.body.innerText.indexOf('Trending in') > -1");
console.log('Trying to scroll...');
await page.evaluate(async () => {
const delay = 1000;
const wait = (ms) => new Promise(res => setTimeout(res, ms));
const count = async () => document.querySelectorAll('div[data-testid="trend"][role="link"]').length;
const scrollDown = async () => {
const last = document.querySelectorAll('div[data-testid="trend"][role="link"]').length;
if (last) {
document.querySelectorAll('div[data-testid="trend"][role="link"]')[last - 1]
.scrollIntoView({ behavior: 'smooth', block: 'end', inline: 'end' });
}
}
let preCount = 0;
let postCount = 0;
do {
preCount = await count();
await scrollDown();
await wait(delay);
postCount = await count();
} while (postCount > preCount);
await wait(delay);
});
console.log('Collecting trending elements...');
const trendingElements = await page.$$eval('div[data-testid="trend"][role="link"]', (divs) => {
let tmp = [];
for (let i=0; i -1) {
return parseFloat(n.split(' ')[0]) * 1000;
}
if (n.indexOf('m') > -1) {
return parseFloat(n) * 1000000;
}
return parseFloat(n);
};
tmp.push({
trending_location: spans[0] ? spans[0].innerText : '?',
text: spans[1] ? spans[1].innerText : '?',
tweets: numberOfTweets(spans),
tweets_raw: getRawTweets(numberOfTweets(spans))
});
}
tmp.sort(function compare(a, b) {
if (a.tweets_raw < b.tweets_raw) { return 1 }
if (a.tweets_raw > b.tweets_raw) { return -1 }
return 0;
})
return tmp;
});
return trendingElements;
}
/**
* @param Object browser
* @return Promise
*/
async function getTwitterTrendingTopicsFromCache(browser) {
let trendings, needToFetch;
const now = Math.floor(Date.now() / 1000);
needToFetch = caches.hasOwnProperty('trendings') === false;
if (caches.hasOwnProperty('trendings') === true) {
const cacheMaxAge = 300;
const cacheAge = now - caches.trendings.timestamp;
if (cacheAge > cacheMaxAge) {
needToFetch = true;
}
}
if (needToFetch) {
trendings = await getTwitterTrendingTopics(browser);
caches.trendings = {
data: trendings,
timestamp: now
}
return trendings;
}
return caches.trendings.data;
}
/**
* Make a request to API KirimWA.id
*
* @param Object params
* @return Promise
*/
function apiKirimWaRequest(params)
{
return new Promise((resolve, reject) => {
let responseBody = '';
const options = {
method: params.method || 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${params.token}`
}
}
const payloadBuffer = Buffer.from(params.payload);
if (params.method === 'POST') {
options.headers['Content-Length'] = payloadBuffer.length;
}
const req = https.request(params.url, options, function(res) {
res.on('data', function concatBody(chunk) {
responseBody += chunk;
});
res.on('end', () => {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve({ body: responseBody, response: res });
return;
}
// Non 2XX response
reject({ body: responseBody, response: res });
});
});
if (params.method === 'POST') {
req.write(params.payload);
}
req.end();
}); // Promise
}
const browser = puppeteer.launch({
headless: true
});
app.use(express.json());
app.post('/webhook', async (req, res) => {
const cmdPrefix = 'twitter /trends';
const skipMessage = req.body.payload.from_me === false ||
req.body.payload.is_group_message === true ||
req.body.payload.text !== cmdPrefix;
if (skipMessage) {
res.send({ 'message': 'SKIP '});
return;
}
const trendings = await getTwitterTrendingTopicsFromCache(browser);
let trendingMessage = `${cmdPrefix} response:\n\n`;
for (item of trendings) {
trendingMessage += item.trending_location + "\n" +
`*${item.text}*` + "\n" +
`${item.tweets}` + "\n" +
'--' + "\n"
}
const url = new URL(process.env.API_BASE_URL + '/messages');
const message = {
message: trendingMessage,
phone_number: req.body.payload.sender,
message_type: 'text',
device_id: req.body.payload.device_id
};
const reqParams = {
token: process.env.API_TOKEN,
url: url,
method: 'POST',
payload: JSON.stringify(message)
};
try {
const { body, response } = await apiKirimWaRequest(reqParams);
console.log(body);
} catch (error) {
console.error(error);
console.error('Something went wrong', {
body: error.body,
statusCode: error.response.statusCode
});
}
res.send({ 'message': 'OK '});
});
app.listen(port, function() {
console.log(`Twitter API running at http://localhost:${port}`);
});
Simpan file tersebut dengan nama index.js
.
Fungsi getTwitterTrendingTopics
mensimulasikan sebuah browser bernavigasi ke https://mobile.twitter.com/i/trends/ kemudian menunggu konten untuk muncul dan melakukan scroll ke bawah hingga semua trending topics muncul.
Fungsi getTwitterTrendingTopicsFromCache
berguna untuk melakukan cache dari hasil fungsi sebelumnya karena fungsi tersebut membutuhkan waktu beberapa detik. Jadi hasil akan dicache selama 5 menit.
Fungsi apiKirimWaRequest
berguna untuk melakukan HTTP request ke server API KirimWA.id. Fungsi ini melakukan abstraksi pemanggilan dan akan melemparkan error ketika hasil tidak berstatus 2XX.
Endpoint Express app.post('/webhook', async (req, res)
berfungsi untuk untuk memproses webhook call dari API KirimWA.id. Webhook ini akan menerima data pesan yang masuk, jika pesan yang masuk adalah sebuah pesan dengan format twitter /trends
maka panggil fungsi untuk mendapatkan trending topics diatas kemudian kembalikan hasilnya kepada pengirim.
Jalankan script Node.js yang akan berjalan pada port 4000 dengan perintah berikut:
$ API_TOKEN=TOKEN_API_KIRIMWA_ANDA \
API_BASE_URL=https://api.kirimwa.id/v1 \
node index.js
Twitter API running at http://localhost:4000
3. Expose Localhost ke Internet
Saat ini webhook masih berjalan dilocalhost sehingga belum bisa dihubungi oleh API KirimWA.id dari internet. Untuk itu kita perlu melakukan expose localhost ke internet. Anda bisa menggunakan ngrok atau Cloudflare Tunnel. Pada contoh ini saya menggunakan Cloudflared Tunnel dan menghubungkannya dengan domain teknocerdas.com
Perintah yang saya gunakan untuk melakukan expose localhost port 4000 ke internet adalah:
$ cloudflared tunnel --hostname localhost.teknocerdas.com --url http://localhost:4000
Untuk mencobanya silahkan lakukan POST request ke URL https://localhost.teknocerdas.com/webhook.
$ curl -XPOST https://localhost.teknocerdas.com/webhook \
-H "Content-Type: application/json" \
-d '{ "Hello": "World" }'
Lihat pada terminal Window yang menjalankan script Node.js tersebut harusnya terdapat log body yang dikirim.
Baca Juga
Mengakses localhost dari Internet dengan SSH Tunneling
4. Menambahkan Webhook
Agar API KirimWA.id mengirimkan data pesan masuk ke webhook yang berlokasi di https://localhost.teknocerdas.com/webhook maka URL tersebut perlu didaftarkan.
Gunakan perintah berikut untuk mendaftarkan Webhook.
$ curl -XPOST https://api.kirimwa.id/v1/webhooks \
-H "Content-Type: application/json" \
-H "Authorization: Bearer API_TOKEN_ANDA" \
-d '{ "webhook_url": "https://localhost.teknocerdas.com/webhook" }'
Jika berhasil maka respon yang dikembalikan kurang lebih seperti berikut.
{
"id": "whu-YOUR@EMAIL",
"status": "active",
"data": "https://localhost.teknocerdas.com/webhook",
"created_at": "2021-06-20T17:17:06.333Z",
"meta": {
"location": "https://api.kirimwa.id/v1/webhooks"
}
}
5. Tes Bot WhatsApp
Untuk mengetes Bot yang telah dibuat anda dapat mengirimkan pesan ke diri sendiri. Pada perangkat HP anda buka browser dan buka https://kirimwa.id/ kemudian ketikkan nomor anda sendiri.
Setelah chat WhatsApp terbuka maka ketikkan pesan twitter /trends
lalu kirim. Selang beberapa detik maka harusnya otomatis akan mendapat balasan berupa daftar trending topik di Twitter.