· 5 years ago · Feb 26, 2021, 02:32 PM
1/*
2 List of includes
3*/
4const queryParser = require('query-string');
5var express = require('express');
6var http = require('http');
7var path = require('path');
8var bodyParser = require('body-parser');
9
10//////////////////////////////////////////////////////////////////////////////////////////////
11
12function between(min, max) {
13 return Math.floor(
14 Math.random() * (max - min) + min
15 )
16}
17
18function getPhoneNumberFromURL(queryString) {
19 return queryParser.parse(queryString.split('?')[1]).number;
20}
21
22
23///////////////////////////////////////////////////////////////////////////////////////////////
24
25////////////////////////////////////////////
26// SELENIUM AUTOMATED
27////////////////////////////////////////////
28
29///////////////////////////////////////////////////////////////////////////////////////////////
30
31// [!] Warning!
32// You have to register your name
33// and your password manually
34// in Twilio website first to input this.
35// More info at: https://www.twilio.com/try-twilio
36
37const email = process.env.TWILIO_USERNAME; // Your email goes here.
38const pass = process.env.TWILIO_PASSWORD; // Your password goes here.
39
40// Calling Selenium webdriver.
41// Using Firefox's.
42const {Builder, By, Key, until} = require('selenium-webdriver');
43let driver = new Builder().forBrowser('firefox').build();
44
45// Action mapper.
46const actionMap = {
47 'https://www.twilio.com/console/phone-numbers/verify/message' : 'Text',
48 'https://www.twilio.com/console/phone-numbers/verified' : 'Call',
49 'https://www.twilio.com/console/phone-numbers/verify/message/validate': 'Verify'
50};
51
52// List of pending verifications
53let listOfPendingVerifications = [];
54let someoneVerifying = '';
55
56// keypress():
57// Wait for any keypress to finish
58const keypress = async () => {
59 console.log("[i] Press any key to continue...");
60 process.stdin.setRawMode(true);
61 return new Promise(resolve => process.stdin.once('data', () => {
62 process.stdin.setRawMode(false);
63 resolve();
64 }))
65}
66
67async function waitTillLoadNewPage() {
68 // Get HTML content for async input.
69 var page = await driver.findElement(By.css('html'));
70
71 // Wait until the old page expires
72 await driver.wait(until.stalenessOf(page));
73
74 // Wait for new page arrives
75 let pageState = await driver.executeScript('return document.readyState');
76 while (pageState != 'complete')
77 pageState = await driver.executeScript('return document.readyState');
78}
79
80////////////////////////////////////////////////////////////////////////////////////////////////////
81
82/*
83 inputEmail():
84 Enter email into Twilio's website.
85*/
86async function inputEmail() {
87 // Send whatever value to input box then press Enter.
88 await driver.findElement(By.css('input')).sendKeys(email, Key.ENTER);
89
90 // Wait for new page to show up
91 await waitTillLoadNewPage();
92}
93
94/*
95 inputPass():
96 Enter password into Twilio's website.
97 There is a human verify form, so we need to manually enter that...
98*/
99async function inputPass() {
100 // Find all input boxes in here
101 let inputBoxes = await driver.findElements(By.css('input'));
102
103 // Loop until input box type is 'password'...
104 for (i in inputBoxes) {
105 if (await inputBoxes[i].getAttribute('type') == 'password') {
106 await inputBoxes[i].sendKeys(pass);
107 await inputBoxes[i].sendKeys(Key.ENTER);
108 break;
109 }
110 }
111
112 // Wait for new page to show up
113 await waitTillLoadNewPage();
114}
115
116/*
117 login():
118 Automatically enter login details and let human
119 do robot check-up if exists one.
120*/
121async function login() {
122 // Navigate to login page
123 console.log("[i] Open https://www.twilio.com/login..." );
124 await driver.get('https://www.twilio.com/login');
125
126 // Inputing...
127 console.log("[i] Input email..."); await inputEmail();
128 console.log("[i] Input password..."); await inputPass();
129}
130
131/////////////////////////////////////////////////////////////////////////////////////////////////////
132
133/*
134 openVerifyCallersTab():
135 Open verify caller tab.
136*/
137async function openVerifyCallersTab() {
138 console.log("[i] Open https://www.twilio.com/console/phone-numbers/verified...");
139 await driver.get('https://www.twilio.com/console/phone-numbers/verified');
140}
141
142async function getVerifyButton() {
143 let buttons = await driver.findElements(By.css('a'));
144 for (i in buttons) {
145 if (await buttons[i].getAttribute('tooltip') == 'Add new Number') {
146 return buttons[i];
147 }
148
149 if (await buttons[i].getAttribute('data-analytics-title') == 'verify-number') {
150 return buttons[i];
151 }
152 }
153}
154
155async function waitForVerifyForm(verifyType='') {
156 while (true) {
157 // Wait for the form to show up
158 let forms = await driver.findElements(By.css('form'));
159 for (i in forms) {
160 if (await forms[i].getAttribute('id') == 'verify-number') {
161 // Return verify type if argument is empty
162 if (verifyType == '')
163 return actionMap[await forms[i].getAttribute('action')];
164
165 // Else, keep loop until verify type matches our desired action.
166 while (actionMap[await forms[i].getAttribute('action')] != verifyType) {}
167
168 return verifyType;
169 }
170 }
171 }
172}
173
174async function openVerifyDialogBox() {
175 // Get and push verify button...
176 console.log('[i] Pushing verify button...');
177 let verifyButton = await getVerifyButton();
178 await verifyButton.click();
179
180 // Get verify type...
181 let verifyType = await waitForVerifyForm();
182 console.log('[i] Verify type: ' + verifyType);
183
184 // Change to 'text' if verify type is 'Call'
185 if (verifyType == 'Call') {
186 console.log('[i] Change verify type to \'Text\'...');
187
188 // Press button for message verification
189 let buttons = await driver.findElements(By.css('a'));
190 for (i in buttons) {
191 if (await buttons[i].getAttribute('href') == 'https://www.twilio.com/console/phone-numbers/verify/message') {
192 await buttons[i].click();
193 break;
194 }
195 }
196
197 await waitForVerifyForm('Text');
198 }
199}
200
201async function waitForVerificationDialog() {
202 let isDialogClose;
203 do {
204 isDialogClose = true;
205 let forms = await driver.findElements(By.css('form'));
206
207 for (i in forms) {
208 try {
209 if (await forms[i].getAttribute('id') == 'verify-number') {
210 let alertDivs = await forms[i].findElements(By.className('alert alert-danger alert-static'));
211 if (alertDivs.length != 0)
212 return 'Wrong Input';
213
214 isDialogClose = false;
215 break;
216 }
217 } catch (error) {
218 return 'Closed';
219 }
220 }
221 } while (!isDialogClose);
222
223 return 'Closed';
224}
225
226async function closeVerifyDialogBox() {
227 console.log("[i] Closing verify box...");
228 try {
229 // Wait for the form to show up
230 let forms = await driver.findElements(By.css('form'));
231 for (i in forms) {
232 if (await forms[i].getAttribute('id') == 'verify-number') {
233 await (await (await forms[i].findElement(By.className('modal-header'))).findElement(By.css('button'))).click();
234 break;
235 }
236 }
237 } catch (error) {
238
239 }
240
241 // Wait until dialog closes
242 await waitForVerificationDialog();
243}
244
245async function inputVerifyCode(verifyCode) {
246 let forms = await driver.findElements(By.css('form'));
247 for (i in forms) {
248 if (await forms[i].getAttribute('id') == 'verify-number') {
249 let inputBoxes = await forms[i].findElements(By.css('input'));
250 for (j in inputBoxes) {
251 if (await inputBoxes[j].getAttribute('id') == 'VerificationCode') {
252 await inputBoxes[j].sendKeys(Key.BACK_SPACE, Key.BACK_SPACE, Key.BACK_SPACE, Key.BACK_SPACE, Key.BACK_SPACE, Key.BACK_SPACE, verifyCode, Key.ENTER);
253 return;
254 }
255 }
256 }
257 }
258}
259
260async function inputNumber(phoneNumber) {
261 while (true) {
262 // Wait for the form to show up
263 let forms = await driver.findElements(By.css('form'));
264 for (i in forms) {
265 if (await forms[i].getAttribute('id') == 'verify-number') {
266 let divs = await forms[i].findElements(By.className('input-group'));
267
268 for (j in divs) {
269 if (await (await divs[j].findElement(By.css('span'))).getAttribute('id') == 'verify-country-code-sms') {
270 await (await divs[j].findElement(By.css('input'))).sendKeys(phoneNumber, Key.ENTER);
271 return;
272 }
273 }
274 }
275 }
276 }
277}
278
279async function addNumberToVerify(phoneNumber) {
280 console.log(`[i] Add ${phoneNumber} to the list of verified numbers...`);
281 listOfPendingVerifications.push(phoneNumber);
282}
283
284async function handlePhoneVerification() {
285 while (true) {
286 if (listOfPendingVerifications.length != 0 && someoneVerifying == '') {
287 // Set state of verifying to `true`...
288 someoneVerifying = listOfPendingVerifications[0];
289
290 await openVerifyDialogBox(); // Open verify dialog box, sets to 'Text' mode
291 await inputNumber(someoneVerifying); // Input number into input box
292 await waitForVerifyForm('Verify'); // Wait for verify form to show up
293
294 // Remove an item from list...
295 listOfPendingVerifications.splice(0, 1);
296 }
297 }
298}
299
300///////////////////////////////////////////////////////////////////////////////////////////////
301
302////////////////////////////////////////////
303// TWILIO'S API
304////////////////////////////////////////////
305
306///////////////////////////////////////////////////////////////////////////////////////////////
307
308// Download the helper library from https://www.twilio.com/docs/node/install
309// Your Account Sid and Auth Token from twilio.com/console
310// and set the environment variables. See http://twil.io/secure
311const accountSid = process.env.TWILIO_ACCOUNT_SID;
312const authToken = process.env.TWILIO_AUTH_TOKEN;
313const phoneNumber = process.env.TWILIO_PHONENUMBER;
314
315// Authenticate the client
316const client = require('twilio')(accountSid, authToken);
317
318///////////////////////////////////////////////////////////////////////////////////////////////
319
320////////////////////////////////////////////
321// SERVER
322////////////////////////////////////////////
323
324///////////////////////////////////////////////////////////////////////////////////////////////
325
326// IP and Port of server
327const host = '192.168.38.104';
328const port = 8000;
329
330// List of OTPs availible
331var OTPs = {};
332
333// For initiating server
334var app = express(); // Create express application
335var server = http.createServer(app); // Create server
336app.use(express.static(path.join(__dirname,'public'))); // Path to HTML file
337app.use(bodyParser.urlencoded({ extended: true })); // For parsing HTTP request
338
339////////////////////////////////////////////////////////////////////////////////////////////////
340
341/*
342 app.get('/'):
343 Handle GET requests when
344 user types URL to the bar.
345*/
346app.get('/', async (req, res) => {
347 res.render('index.html')
348})
349
350////////////////////////////////////////////////////////////////////////////////////////////////
351
352/*
353 app.post('/submit_telephone'):
354 Handles POST request when user types
355 their phone number.
356*/
357app.post('/submit_telephone', async (req, res) => {
358
359 console.log(`[i] Receive a request from ${req.body.number}`);
360
361 // If the number has already requesting OTP, cut it off...
362 if (OTPs[req.body.number]) {
363 console.log(`[i] Trying to request ${req.body.number}, but the number has already in use.`)
364 res.redirect('/in_use.html');
365 return;
366 }
367
368 // Assigns an OTP to a phone number
369 OTPs[req.body.number] = between(100000, 999999);
370
371 // Send message to Twilio's API
372 await client.messages
373
374 // Create a message, send it to the number requested
375 .create({
376 body: OTPs[req.body.number],
377 from: phoneNumber,
378 to: '+84' + req.body.number
379 })
380
381 // If send message to server is successful
382 .then(message => {
383 console.log(`[i] Sent code ${OTPs[req.body.number]} to number ${req.body.number}...`);
384 console.log(`[i] Redirect to /submit_otp.html...`);
385
386 // Redirect to submit OTP website...
387 res.redirect(`/submit_otp.html?number=${req.body.number}`);
388 })
389
390 // If the number is not verified...
391 .catch(async (error) => {
392 console.log(`[!] Error! Number ${req.body.number} has not been verified! Redirect to /submit_verify.html...`);
393
394 // Add a number to list of verified numbers
395 await addNumberToVerify(req.body.number);
396
397 // Redirect to submit verify website...
398 res.redirect(`/submit_verify.html?number=${req.body.number}`);
399
400 });
401
402
403})
404
405////////////////////////////////////////////////////////////////////////////////////////////////
406
407/*
408 app.post('/submit_verify'):
409 Submit verify code if the phone number
410 has not been verified yet.
411*/
412app.post('/submit_verify', async (req, res) =>{
413
414 let requestNumber = getPhoneNumberFromURL(req.headers.referer);
415 console.log(`[!] User ${requestNumber} submits code to /submit_verify.html...`);
416 if (requestNumber != someoneVerifying) {
417 console.log(`[!] User ${requestNumber} submits code, but ${someoneVerifying} has already using the service. Return.`);
418 return;
419 }
420
421 // Send verification code
422 await inputVerifyCode(req.body.verifyCode);
423
424 // Get verification status
425 let verificationStatus = await waitForVerificationDialog();
426 if (verificationStatus == 'Wrong Input') {
427 console.log(`[!] User ${requestNumber} submit wrong code to /submit_verify.html...`);
428 await closeVerifyDialogBox();
429 res.redirect(`/submit_verify_wrong.html`);
430 }
431 else if (verificationStatus == 'Closed') {
432 res.redirect('/');
433 }
434
435 // Remove OTP...
436 delete OTPs[requestNumber];
437
438 // Set the state of verifying to false...
439 someoneVerifying = '';
440
441})
442
443////////////////////////////////////////////////////////////////////////////////////////////////
444
445/*
446 app.post('/submit_otp'):
447 Submit OTP code for a phone number.
448*/
449app.post('/submit_otp', async (req, res) =>{
450
451 let requestNumber = getPhoneNumberFromURL(req.headers.referer);
452
453 if (req.body.OTP == OTPs[requestNumber]) {
454 console.log(`[i] User ${requestNumber} entered correct OTP...`);
455 res.redirect('/right_otp.html');
456 } else {
457 console.log(`[i] OTP is ${OTPs[requestNumber]}, but user ${requestNumber} entered ${req.body.OTP}...`);
458 res.redirect('/wrong_otp.html');
459 }
460
461 // Removes OTP from a number
462 delete OTPs[requestNumber];
463
464})
465
466////////////////////////////////////////////////////////////////////////////////////////////////
467
468// Tell server to listen at host:port
469server.listen(port,host, () => {
470 console.log(`Server is running on http://${host}:${port}`);
471});
472
473// Open Twilio website through
474(async function main() {
475 try {
476 await login();
477 await openVerifyCallersTab();
478 await handlePhoneVerification();
479 } finally {
480 driver.quit();
481 }
482
483})();
484
485