const puppeteer = require('puppeteer'); const fs = require("fs") const iPhonex = puppeteer.devices['iPhone X']; const fetch = require('node-fetch'); // const RND_SLEEP_PAD_MAX = 10; const RND_SLEEP_PAD_MAX = 1; const MAIN_URL = 'https://rewards.bing.com/?lang=en-US'; let sleep = (sec) => { return new Promise(resolve => setTimeout(resolve, sec * 1000)); } async function rndSleep(sec, pad = null) { if (!pad) pad = Math.random() * RND_SLEEP_PAD_MAX; await sleep(sec + pad); } let rndSample = (arr, n) => { const shuffled = arr.sort(() => 0.5 - Math.random()); return shuffled.slice(0, n); } let shuffleArray = (array) => { for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } } module.exports = class BingAuto2 { constructor(id, pw, headless) { this.id = id; this.pw = pw; this.headless = headless; this.browser = null; this.mainPage = null; this.keywords = null; } async startBrowser() { let id = this.id.split('@')[0]; let tmp_dir = './chrome_cache_' + id; if (!fs.existsSync(tmp_dir)) fs.mkdirSync(tmp_dir); // await page.setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'); let option = { headless: this.headless, userDataDir: tmp_dir, args: ["--proxy-server='direct://'", '--proxy-bypass-list=*', '--disable-site-isolation-trials', '--no-sandbox'] }; this.browser = await puppeteer.launch(option); // await browser.close(); } async close() { this.browser.close(); } async newPage(agent = 'chrome') { let browser = this.browser; const page = await browser.newPage(); await page.setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'); // await page.setCacheEnabled(false); // await page.setDefaultNavigationTimeout(0); if (agent === 'mobile') await page.emulate(iPhonex); else await page.setViewport({height: 900, width: 1200}); if (agent === 'edge') { await page.setUserAgent('"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML like Gecko) Chrome/83.0.2704.79 Safari/537.36 Edge/14.14931"') } await page.goto(MAIN_URL); return page; } async newMainPage(login = true) { this.mainPage = await this.newPage(); if (login) await sleep(1); await this.login(); } async login() { let page = this.mainPage; let elem, elems; await page.waitForSelector('body'); elem = await page.$('div.info-columns mee-rewards-counter-animation span'); if (elem) return; console.log('> Logging In...'); // elem = await page.$('#raf-signin-link-id'); // console.log('> Logging In...0'); // if (elem == null) // return; // // await elem.click(); // await page.waitForNavigation({waitUntil: 'networkidle0'}); elem = await page.$('[name="loginfmt"]'); await elem.type(this.id); await elem.press('Enter'); await sleep(2); elem = await page.$('[name="KMSI"]'); if (elem) elem.click(); await sleep(1); elem = await page.$('[name="passwd"]'); await elem.type(this.pw); await elem.press('Enter'); await sleep(3); elem = await page.$('#iLooksGood'); if (elem) { await elem.click(); await sleep(1); await page.waitForNavigation(); } elem = await page.$('#KmsiCheckboxField'); if (elem) { elem.click(); elem = await page.$('#idSIButton9'); elem.click(); await sleep(4); } // check language error elem = await page.$('#error-links > li > a'); if (elem) elem.click(); console.log('> Logged In'); } async getRewardPoint() { if (this.mainPage.url() !== MAIN_URL) { await this.mainPage.goto(MAIN_URL); } await sleep(5); try { let point = await this.getText(this.mainPage, 'div.info-columns mee-rewards-counter-animation span'); console.log('> Current point :', point); } catch (e) { console.log('Failed to get point'); return 0; } return parseInt(point.replace(',', '')); } async doDailyReward() { console.log('> Daily Reward'); if (this.mainPage.url() !== MAIN_URL) await this.mainPage.goto(MAIN_URL); let cards = await this.mainPage.$$('#daily-sets mee-card'); cards.splice(3); cards = cards.concat(await this.mainPage.$$('#more-activities mee-card')); // console.log(cards.length + ' cards'); for (let card of cards) { let newPage = null; try { if (!await card.$('.mee-icon-AddMedium') && !await card.$('.mee-icon-HourGlass')) continue; let title = await this.getText(this.mainPage, '.contentContainer > h3', card); console.log('Card: [' + title + ']'); let elem = await card.$('div.actionLink'); await this.click(this.mainPage, elem); await sleep(3); let pages = await this.browser.pages(); if (pages.length === 2) // no new page continue newPage = pages[pages.length - 1]; await newPage.setViewport({height: 900, width: 1200}); await sleep(2); await newPage.waitForSelector('body'); if (await newPage.$('.wk_paddingBtm')) await this.doABCQuiz(newPage); else if (await newPage.$('#rqStartQuiz')) await this.doSelectQuiz(newPage); else if (await newPage.$('.rqOption,.b_slidebar')) await this.doSelectQuiz(newPage); else { console.log('Unknown card type'); } } catch (e) { console.log(e); } finally { if (newPage && !newPage.isClosed()) await newPage.close(); } await rndSleep(2); console.log(''); } } async getText(page, selector, elem = null) { if (elem) { return await page.evaluate((selector, elem) => elem.querySelector(selector).textContent, selector, elem); } else { return await page.evaluate((selector) => document.querySelector(selector).textContent, selector); } } async checkRemainDailyReward() { let elems = await this.mainPage.$$('#daily-sets mee-rewards-points .mee-icon-AddMedium'); let daily_remains = elems.length; console.log(`Daily set : ${daily_remains} remains`); elems = await this.mainPage.$$('#more-activities mee-card .mee-icon-AddMedium'); let more_remains = elems.length; console.log(`More activities : ${more_remains} remains`); return [daily_remains, more_remains]; } async doABCQuiz(page) { console.log('> ABC Quiz'); while (true) { let answers = await page.$$('.wk_paddingBtm'); if (answers.length === 0) { console.log('ERROR: Failed to get ABC quiz answer elements'); return; } let rnd_answer = Math.floor(Math.random() * answers.length); await answers[rnd_answer].click(); await rndSleep(2); let elem = await page.$('input[value="Next question"]'); if (elem) { await this.click(page, elem); } else { elem = await page.$('input[value="Get your score"]'); if (elem) await this.click(page, elem); break; } await rndSleep(2); } } async doSelectQuiz(page) { console.log('> Select Quiz'); await sleep(5); let elem = await page.$('#rqStartQuiz'); if (elem) await this.click(page, elem); await rndSleep(4); elem = await page.$('.rqDragOption'); if (elem) { // order type quiz console.log('Order-type quiz'); while (true) { await rndSleep(5); await this.insertDragDropFunc(page); await rndSleep(1); elem = await page.$('#quizCompleteContainer > div:nth-child(1) > div.headerMessage'); if (elem) { console.log('Done'); break; } let all_answers = await page.$$('.rqDragOption'); let correct_ans = await page.$$('.rqDragOption.correctAnswer'); let answers = []; for (let an of all_answers) { if (!correct_ans.includes(an)) answers.push(an); } let switchAns = rndSample(answers, 2); let ans1 = await switchAns[0].getProperty('id'); let ans2 = await switchAns[1].getProperty('id'); let js = 'triggerDragAndDrop("#","#")' await page.evaluate((id1, id2) => { triggerDragAndDrop('#' + id1, '#' + id2); }, ans1, ans2); } } else { // select quiz // poll if (await page.$('#btPollOverlay')) { console.log('Daily Poll'); let opt = await page.$('#btPollOverlay .btOption'); if (opt) { opt.click(); await rndSleep(3); } return; } //else const quizTypeData = [{ 'type': 0, 'name': 'basic', 'tester': '#QuizContainerWrapper .rqOption', 'title': '.rqQuestion', 'btn': '#QuizContainerWrapper .rqOption' }, { 'type': 1, 'name': 'many', 'tester': '#QuizContainerWrapper .b_slidebar', 'title': '.rqQuestion > .bt_queText', 'btn': '#QuizContainerWrapper .bt_lstcl_card' }, { 'type': 2, 'name': 'thisthat', 'tester': '#QuizContainerWrapper .btOptionCard', 'title': '#QuizContainerWrapper .bt_Quefooter', 'btn': '#QuizContainerWrapper .btOptionCard' }]; let elem, elems; let quizType = -1; for (let item of quizTypeData) { elems = await page.$$(item['tester']); if (elems.length !== 0) { quizType = item['type']; break; } } if (quizType === -1) { console.log('ERROR: Can\'t get select quiz type'); return; } let quizData = quizTypeData[quizType]; console.log('Quiz Type:', quizData['name']); let btns = await page.$$(quizData['btn']); let answerCnt = btns.length; let intervalTime = 5; if (quizType === 1) answerCnt = 8; else if (quizType === 2) intervalTime = 10; console.log('# of answers:', answerCnt); while (true) { await rndSleep(intervalTime); elem = await page.$('#quizCompleteContainer > div:nth-child(1) > div.headerMessage'); if (elem) break; elem = await page.$('.headerMessage_Refresh'); if (elem) break; let answerIds = [...Array(answerCnt).keys()]; shuffleArray(answerIds); let i = 0; let oldTitle = await this.getText(page, quizData['title']); while (i < answerCnt && i < 10) { elems = await page.$$(quizData['btn']); console.log('- Selecting answer#', answerIds[i]); await this.elemCoordinateClick(page, elems[answerIds[i]]); await rndSleep(intervalTime); let newTitle = await this.getText(page, quizData['title']); if (oldTitle !== newTitle) { break; } i += 1; } } } } async click(page, elem) { await page.evaluate((elem) => elem.click(), elem); } async insertDragDropFunc(page) { let js = 'window.triggerDragAndDrop=function(e,o){var t=function(e,o,t,r){var n=document.createEvent("MouseEvents");n.initMouseEvent(e,!0,!0,window,1,1,1,t,r,!1,!1,!1,!1,0,o),o.dispatchEvent(n)},r=document.querySelector(e),n=document.querySelector(o);if(!r||!n)return!1;var u=r.getBoundingClientRect(),a=Math.floor((u.left+u.right)/2),d=Math.floor((u.top+u.bottom)/2);u=n.getBoundingClientRect();var m=Math.floor((u.left+u.right)/2),g=Math.floor((u.top+u.bottom)/2);return t("mousemove",r,a,d),t("mouseenter",r,a,d),t("mouseover",r,a,d),t("mousedown",r,a,d),t("dragstart",r,a,d),t("drag",r,a,d),t("mousemove",r,a,d),t("drag",r,m,g),t("mousemove",n,m,g),t("mouseenter",n,m,g),t("dragenter",n,m,g),t("mouseover",n,m,g),t("dragover",n,m,g),t("drop",n,m,g),t("dragend",r,m,g),t("mouseup",r,m,g),!0};'; await page.addScriptTag({content: js}); } async elemCoordinateClick(page, elem) { await page.evaluate((elem) => { let rect = elem.getBoundingClientRect(); let obj = document.elementFromPoint(rect.x + 10, rect.y + 10); obj.click(); }, elem); } async getSearchKeywords() { let getKeyword = (url, regex) => { return new Promise(resolve => { let args = {headers: {'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_0_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36'}}; fetch(url, args) .then(res => res.text()) .then(html => { let matches = html.matchAll(regex); let keywords = []; for (let item of matches) keywords.push(item[1]); resolve(keywords); }); }); } if (this.keywords !== null) return this.keywords; let key1 = await getKeyword('https://datalab.naver.com/keyword/realtimeList.naver', '(.*?)'); let key2 = await getKeyword('https://trends.google.com/trends/trendingsearches/daily/rss?geo=US', '(.*?)'); let keywords = key1.concat(key2); if (keywords.length < 80) { let mixes = []; for (let i = keywords.length; i < 80; i++) { let joins = rndSample(keywords, 2); mixes.push(joins.join(' ')); } keywords = keywords.concat(mixes); } this.keywords = keywords; return keywords; } async doSearch(searchCnt = 30, page = null) { let keywords = await this.getSearchKeywords(); if (!page) page = this.mainPage; await page.goto('https://www.bing.com/'); shuffleArray(keywords); searchCnt += Math.floor(Math.random() * 10); for (let i = 0; i < searchCnt; i++) { try { console.log(`Search ${i + 1}/${searchCnt} : ${keywords[i]}`); let searchBar = await page.$('#sb_form_q'); // clear await searchBar.click({clickCount: 3}); await searchBar.press('Backspace'); await searchBar.type(keywords[i], {delay: 200}); await searchBar.press('Enter'); await rndSleep(2); // await rndSleep(5, 10); } catch (e) { console.log(e); } } } async checkSearchStatus() { // await this.mainPage.waitForNavigation({waitUntil: "networkidle2"} ); await this.mainPage.goto('https://rewards.bing.com/pointsbreakdown'); await sleep(2); let elems = await this.mainPage.$$('div.cardContainer p.pointsDetail'); let status = {}; let statusOrder = ['chrome', 'mobile', 'edge']; if (elems.length === 2) statusOrder.splice(1, 1); for (let i = 0; i < elems.length; i++) { let elem = elems[i]; let text = await this.mainPage.evaluate((elem) => elem.textContent, elem); text = text.split('/'); status[statusOrder[i]] = [parseInt(text[0]), parseInt(text[1])]; } return status; } async doAllSearch() { let searchStatus = await this.checkSearchStatus(); console.log('> Search status:', searchStatus); for (let searchType in searchStatus) { let searchCnt = Math.floor((searchStatus[searchType][1] - searchStatus[searchType][0]) / 5); if (searchCnt !== 0) { console.log('>', searchType, 'search'); let page = await this.newPage(searchType); await this.doSearch(searchCnt, page); await page.close(); } } } }