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