From 87880184b5ec8d62d3682ba5dcb220b11cf20828 Mon Sep 17 00:00:00 2001 From: yema <1304119519@qq.com> Date: Tue, 13 Jun 2023 21:39:23 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E9=80=A0=E5=89=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- electron/main/gpt/batchApplication.ts | 99 +-------- electron/main/gpt/getLink.ts | 8 +- electron/main/gpt/index.ts | 5 +- electron/main/gpt/login.ts | 100 --------- electron/main/gpt/validate.ts | 7 +- electron/main/index.ts | 6 +- electron/main/login.ts | 282 +++++++++++++++++++++++++- electron/main/poe/getLink.ts | 17 +- electron/main/poe/hao.txt | 0 electron/main/poe/index.js | 4 - electron/main/poe/link_7day.ts | 154 ++++++++++---- electron/main/poe/login.ts | 107 ---------- electron/main/poe/openBrowser.js | 8 - electron/main/poe/validate.js | 21 +- electron/main/tools.ts | 67 +++++- package.json | 1 + src/App.vue | 2 +- 17 files changed, 480 insertions(+), 408 deletions(-) delete mode 100644 electron/main/gpt/login.ts delete mode 100644 electron/main/poe/hao.txt delete mode 100644 electron/main/poe/login.ts delete mode 100644 electron/main/poe/openBrowser.js diff --git a/electron/main/gpt/batchApplication.ts b/electron/main/gpt/batchApplication.ts index 30a227d..3bbc28d 100644 --- a/electron/main/gpt/batchApplication.ts +++ b/electron/main/gpt/batchApplication.ts @@ -1,8 +1,9 @@ import type { Browser, Page } from 'puppeteer' import puppeteer from 'puppeteer-extra' import StealthPlugin from 'puppeteer-extra-plugin-stealth' -import { clog } from "../poe/login"; +import { clog } from "../tools"; import { awaitWrap, randomNum } from '../tools'; +import login from '../login' import Mock from 'mockjs' import axios from 'axios' import { SocksProxyAgent } from 'socks-proxy-agent' @@ -37,98 +38,6 @@ function getProxy(options = {} as any) { }) } -// getProxy()//.then(res => console.log(res.data)) - - -export const browsers = new Map() -// 登录 -async function login(options = {} as any): Promise { - const { user, pass } = options - - const log = clog(options) - - // const [pErr, proxy] = await awaitWrap(getProxy(options)) - // if (pErr) { - // log('获取代理失败') - // return - // } - // // console.log('获取代理成功', proxy) - // // // const proxyUrl = await proxyChain.anonymizeProxy(`socks5://${proxy.ip}:${proxy.port}`); - // const proxyUrl = `socks5://${proxy.ip}:${proxy.port}` - // console.log('proxyUrl', proxyUrl) - // console.log(`curl --socks5 ${proxy.ip}:${proxy.port} https://jd.com`) - // // // const agent = new SocksProxyAgent(`socks5://${proxy.user}:${proxy.pass}@${proxy.ip}:${proxy.port}`); - - // // // console.log('proxy', proxy) - - log('启动浏览器') - - const browser = await puppeteer.launch({ - headless: false, - args: [ - '--no-sandbox', - '--disable-setuid-sandbox', - // `--proxy-server=${proxyUrl}`, - ] - }) - log('创建新页面') - const page = await browser.newPage() - // await page.authenticate({ - // username: proxy.user, - // password: proxy.pass - // }) - browsers.set(user, browser) - - // await page.setRequestInterception(true); - // page.on('request', (request) => { - // request.continue({ - // agent - // }); - // }); - - await page.setExtraHTTPHeaders({ - 'accept-language': 'en-US,en;q=0.9,hy;q=0.8' - }) - - // page.goto('https://ip.900cha.com/') - - // return - - // 进入 - log('准备进入 gpt 登录') - await page.goto('https://platform.openai.com') - - // const [err, res] = await awaitWrap(page.waitForNavigation({ timeout: 10000 })) - // if (err) throw err - - log('等待出现输入框') - await page.waitForSelector('#username', { visible: true, timeout: 10000 }) - - // 输入账号 - log('输入账号') - await page.type('#username', user) - await Promise.all([ - page.waitForNavigation(), - page.keyboard.press('Enter') - ]) - - log('等待出现密码输入框') - await page.waitForSelector('#password', { visible: true }) - log('输入密码') - await page.type('#password', pass) - - - log('准备登录') - await Promise.all([ - page.waitForNavigation({ timeout: 10000 }), - page.keyboard.press('Enter') - ]) - - log('登录成功') - - return page -} - // 获取组织id async function getOrgId(page: Page, options: any) { const log = clog(options) @@ -228,7 +137,7 @@ export async function batchApplication(options) { const log = clog(options) log('开始', { ident: 'gpt-batch-4.0' }) - const [error, page] = await awaitWrap(login(options)) + const [error, [page, browser]] = await awaitWrap(login.openai(options)) if (error) return log('登录失败', { error, ident: 'gpt-batch-4.0' }) await page.waitForSelector('.ovr-section') @@ -238,4 +147,6 @@ export async function batchApplication(options) { console.log('orgId', orgId) await application(page, options) + + browser.close() } diff --git a/electron/main/gpt/getLink.ts b/electron/main/gpt/getLink.ts index c2185e4..2d14f80 100644 --- a/electron/main/gpt/getLink.ts +++ b/electron/main/gpt/getLink.ts @@ -1,10 +1,10 @@ -import { clog } from "../poe/login"; -import { login } from "./login"; +import { clog } from "../tools"; +import login from "../login"; export async function getLink(options) { const log = clog(options) log('开始', { ident: 'gpt-link' }) - const page = await login(options) + const [page, browser] = await login.chatgpt(options) // await page.waitForTimeout(500) @@ -36,6 +36,8 @@ export async function getLink(options) { if (response.ok()) { const url = response._request._frame._url log('获取链接成功', { result: url, type: 'success' }) + browser.close() return url } + browser.close() } diff --git a/electron/main/gpt/index.ts b/electron/main/gpt/index.ts index 4a98e50..1bd5f39 100644 --- a/electron/main/gpt/index.ts +++ b/electron/main/gpt/index.ts @@ -1,8 +1,7 @@ import { ipcMain } from 'electron' import { getLink } from './getLink' import { validate } from './validate' -import { browser } from './login' -import { batchApplication, browsers } from './batchApplication' +import { batchApplication } from './batchApplication' const parseAccount = text => text.split('\n').filter(Boolean).map(v => { v = v.split(/(——|-)+/).filter(v => !['-', '——'].includes(v)) @@ -24,7 +23,6 @@ ipcMain.handle('gpt-link', async (event, arg) => { }) console.log('process', i, user, link) } - browser && browser.close() }) ipcMain.handle('gpt-result', async (event, arg) => { @@ -86,5 +84,4 @@ export async function runActions(action: keyof typeof actions, options: any) { }) console.log('process', i, user, link) } - browsers.forEach(browser => browser.close()) } diff --git a/electron/main/gpt/login.ts b/electron/main/gpt/login.ts deleted file mode 100644 index 54774d1..0000000 --- a/electron/main/gpt/login.ts +++ /dev/null @@ -1,100 +0,0 @@ -import type { Browser, Page } from 'puppeteer' -import puppeteer from 'puppeteer-extra' -import StealthPlugin from 'puppeteer-extra-plugin-stealth' -import { randomNum } from '../tools' -import { win } from '../index' -import { clog } from '../poe/login' -puppeteer.use(StealthPlugin()) - -let id -export let browser: Browser | undefined -/** - * 登录google - */ -export async function login(options, tryCount = 1): Promise { - let resolve, reject - const log = clog(options) - const p = new Promise((res, rej) => { - resolve = res - reject = rej - }) - id = options.id - const env = { - GUSER: options.user, - GPASS: options.pass - } - - if (browser) await browser.close() - - browser = await puppeteer.launch({ headless: false }) - const page = await browser.newPage() - await page.setExtraHTTPHeaders({ - 'accept-language': 'en-US,en;q=0.9,hy;q=0.8' - }) - - log('开始访问 gpt') - await page.goto('https://chat.openai.com/auth/login') - await page.waitForSelector('button') - - // log('准备进入 google 登录页') - const [response] = await Promise.all([ - page.waitForNavigation({ timeout: 10000 }), - page.click('button') - ]) - // log('已进入 google 登录页') - - if (!response.ok()) return log('进入 google 登录页失败') - - log('准备输入账号') - await page.type('#username', env.GUSER) - await Promise.all([ - page.waitForNavigation(), - page.keyboard.press('Enter') - ]) - - log('已输入账号,准备输入密码') - await page.waitForSelector('#password', { visible: true }) - await page.type('#password', env.GPASS) - log('已输入密码,开始登录') - - await Promise.all([ - page.waitForNavigation({ timeout: 10000 }), - page.keyboard.press('Enter') - ]) - log('登录成功') - resolve(page) - - // if (response.ok()) { - // await page.waitForSelector('input[type="email"]') - // log('准备输入账号') - // await page.type('input[type="email"]', env.GUSER) - // await Promise.all([ - // page.waitForNavigation(), - // await page.keyboard.press('Enter') - // ]) - // log('已输入账号,准备输入密码') - // await page.waitForSelector('input[type="password"]', { visible: true }) - // await page.type('input[type="password"]', env.GPASS) - // log('已输入密码,开始登录') - // await Promise.all([ - // page.waitForFunction(() => location.href === 'https://poe.com/'), - // await page.keyboard.press('Enter') - // ]) - - // log('登录成功,准备进入 poe') - - // return page - // } - - // if (tryCount < 4) { - // log(`google 登录失败,准备 ${tryCount} 次重试`) - // await page.close() - // return await loginGoogle(options, tryCount + 1) - // } else { - // log('google 登录失败,重试次数已达上限') - // reject('google 登录失败') - // } - - return p - // return Promise.reject('google 登录失败') -} diff --git a/electron/main/gpt/validate.ts b/electron/main/gpt/validate.ts index 922b63a..8887b39 100644 --- a/electron/main/gpt/validate.ts +++ b/electron/main/gpt/validate.ts @@ -1,10 +1,10 @@ -import { clog } from "../poe/login"; -import { login } from "./login"; +import { clog } from "../tools"; +import login from "../login"; export async function validate(options) { const log = clog(options) log('开始', { ident: 'gpt-validate' }) - const page = await login(options) + const [page, browser] = await login.chatgpt(options) await page.waitForSelector('body > div.absolute.inset-0') await page.evaluate(() => { @@ -24,4 +24,5 @@ export async function validate(options) { log('获取链接成功', { result: isSuccess ? '充值成功😘' : '充值失败😭', type: isSuccess ? 'success' : 'fail' }) console.log(isSuccess) + browser.close() } diff --git a/electron/main/index.ts b/electron/main/index.ts index 4bd7b91..c8228cf 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -3,9 +3,7 @@ import { release } from 'node:os' import { join } from 'node:path' import './poe/index' import './gpt/index' -import { browser as poeBrowser } from './poe/login' -import { browser as gptBrowser } from './gpt/login' -import { browsers } from './gpt/batchApplication' +import { browsers } from './tools' // The built directory structure // @@ -126,8 +124,6 @@ ipcMain.handle('open-win', (_, arg) => { }) ipcMain.handle('stop', async (event, arg) => { - poeBrowser && poeBrowser.close() - gptBrowser && gptBrowser.close() browsers.forEach(browser => browser.close()) return true }) diff --git a/electron/main/login.ts b/electron/main/login.ts index 4740adb..1ef651e 100644 --- a/electron/main/login.ts +++ b/electron/main/login.ts @@ -1,14 +1,16 @@ -import { clog } from './poe/login' -import { browserAndPage } from './tools' +import { Page } from 'puppeteer' +import { browsers, clog, elCheck } from './tools' +import { awaitWrap, browserAndPage, randomNum } from './tools' +import { Browser } from 'puppeteer' const login = { // poe 邮箱登录 - async poe_email(options) { + async poe_email(options, getCodeFn: Function): Promise<[Page, Browser]> { const log = clog(options) log('启动浏览器') - const { page } = await browserAndPage(options) + const { browser, page } = await browserAndPage(options) log('正在进入登录页面') await page.goto('https://poe.com/login') @@ -27,9 +29,281 @@ const login = { log('开始输入邮箱') await page.waitForSelector('input[type="email"]') await page.type('input[type="email"]', options.user) + await page.waitForTimeout(1000) + // elCheck() await page.keyboard.press('Enter') log('开始输入密码') + + await page.waitForSelector('input[class^="VerificationCodeInput_verificationCodeInput"]') + const code = await getCodeFn(options, { page, browser }) + + await page.type('input[class^=VerificationCodeInput_verificationCodeInput]', code) + await page.keyboard.press('Enter') + + await page.waitForNavigation() + + return [page, browser] + }, + + async poe_google(options, tryCount = 1): Promise<[Page, Browser]> { + let resolve: (value: [Page, Browser] | PromiseLike<[Page, Browser]>) => void, + reject + const log = clog(options) + const p = new Promise<[Page, Browser]>((res, rej) => { + resolve = res + reject = rej + }) + + const env = { + GUSER: options.user, + GPASS: options.pass + } + const { browser, page } = await browserAndPage(options) + + log('开始访问 poe') + await page.goto('https://poe.com') + log('已进入 poe') + await page.waitForTimeout(randomNum(1000, 2600)) + + log('准备进入 google 登录页') + + page.click('.ContinueWithGoogleButton_buttonContentWrapper__Mrp0W') + const [err, response] = await awaitWrap(page.waitForNavigation({ timeout: 10000 })) + if (err) { + reject({ text: '登录报错', try: true }) + return p + } + log('已进入 google 登录页') + + if (response.ok()) { + await page.waitForSelector('input[type="email"]') + log('准备输入账号') + await page.type('input[type="email"]', env.GUSER) + await Promise.all([ + page.waitForNavigation(), + await page.keyboard.press('Enter') + ]) + log('已输入账号,准备输入密码') + if (await isError(page)) { + reject({ text: '登录报错', try: true }) + return p + } + + await page.waitForSelector('input[type="password"]', { visible: true }) + await page.type('input[type="password"]', env.GPASS) + log('已输入密码,开始登录') + await Promise.all([ + page.waitForFunction(() => location.href === 'https://poe.com/'), + await page.keyboard.press('Enter') + ]) + if (await isError(page)) { + reject({ text: '登录报错', try: true }) + return p + } + + log('登录成功,准备进入 poe') + resolve([page, browser]) + + return p + } + + if (tryCount < 4) { + log(`google 登录失败,准备 ${tryCount} 次重试`) + await page.close() + return await login.poe_google(options, tryCount + 1) + } else { + log('google 登录失败,重试次数已达上限') + reject('google 登录失败') + } + + return p + }, + + async openai(options): Promise<[Page, Browser]> { + const { user, pass } = options + const log = clog(options) + + log('启动浏览器') + + const { browser, page } = await browserAndPage(options) + + log('准备进入 gpt 登录') + await page.goto('https://platform.openai.com') + + + log('等待出现输入框') + await page.waitForSelector('#username', { visible: true, timeout: 10000 }) + + // 输入账号 + log('输入账号') + await page.type('#username', user) + await Promise.all([ + page.waitForNavigation(), + page.keyboard.press('Enter') + ]) + + log('等待出现密码输入框') + await page.waitForSelector('#password', { visible: true }) + log('输入密码') + await page.type('#password', pass) + + + log('准备登录') + await Promise.all([ + page.waitForNavigation({ timeout: 10000 }), + page.keyboard.press('Enter') + ]) + + log('登录成功') + + return [page, browser] + }, + + /** + * 登录邮箱并获取验证码 + */ + async mail_get_code(options): Promise<[Page, Browser, { + code?: string, + validateCode: () => Promise + }]> { + const { emailText = 'Poe' } = options + const log = clog(options) + const { browser, page } = await browserAndPage({ ...options }) + + log('准备进入邮箱登录页') + const [error] = await awaitWrap(page.goto('https://www.mail.com/', { waitUntil: 'domcontentloaded' })) + + await page.waitForSelector('.header-bar .button-login') + await page.click('.header-bar .button-login') + + log('输入帐密') + await page.waitForTimeout(500) + await page.waitForSelector('input#login-email') + await page.waitForTimeout(500) + await page.type('input#login-email', options.user) + await page.type('input#login-password', options.pass) + await page.keyboard.press('Enter') + log('开始登录') + + async function validateCode() { + log('开始获取code') + let $iframe = await page.$('#thirdPartyFrame_home') + let frame = await $iframe?.contentFrame() + + if (!frame) { + await page.reload() + return await validateCode() + } + + await frame.waitForSelector('.ico.sync') + await frame.click('.ico.sync') + await frame.waitForTimeout(1000) + + await frame.waitForSelector('ul.inbox-container li') + log('查找邮箱') + const $li = await frame.evaluate((emailText) => { + const $li: any = Array.from(document.querySelectorAll('ul.inbox-container li')).find($li => { + let sender = $li.querySelector('.sender')?.textContent || '' + return sender.includes(emailText) + }) + + if ($li) $li.click() + return $li + }, emailText) + + if (!$li) { + log('未找到邮箱') + return '' + } else { + log('等待出现验证码 iframe') + await page.waitForSelector('#thirdPartyFrame_mail') + log('出现了验证码 iframe,寻找他的 iframe') + $iframe = await page.$('#thirdPartyFrame_mail') + frame = await $iframe?.contentFrame() + + await frame.waitForSelector('iframe#mail-detail') + log('出现验证码 iframe') + + $iframe = await frame.$('iframe#mail-detail') + frame = await $iframe?.contentFrame() + + log('等待验证码') + await frame.waitForSelector('table table table ') + const code = (await frame.$eval('table table table tr:nth-of-type(5)', el => el.textContent)) || '' + return code.trim() + } + } + + await awaitWrap(page.waitForNavigation({ waitUntil: 'load' })) + const code = await validateCode() + + log(`邮箱验证码 ${code}`) + return [page, browser, { code, validateCode }] + }, + + async chatgpt(options, tryCount = 1): Promise<[Page, Browser]> { + let resolve: (value: [Page, Browser] | PromiseLike<[Page, Browser]>) => void, + reject + const log = clog(options) + const p = new Promise<[Page, Browser]>((res, rej) => { + resolve = res + reject = rej + }) + + const env = { + GUSER: options.user, + GPASS: options.pass + } + + const { browser, page } = await browserAndPage(options) + log('开始访问 gpt') + await page.goto('https://chat.openai.com/auth/login') + await page.waitForSelector('button') + + // log('准备进入 google 登录页') + const [response] = await Promise.all([ + page.waitForNavigation({ timeout: 10000 }), + page.click('button') + ]) + // log('已进入 google 登录页') + + if (!response.ok()) { + log('进入 google 登录页失败') + reject('进入 google 登录页失败') + return p + } + + log('准备输入账号') + await page.type('#username', env.GUSER) + await Promise.all([ + page.waitForNavigation(), + page.keyboard.press('Enter') + ]) + + log('已输入账号,准备输入密码') + await page.waitForSelector('#password', { visible: true }) + await page.type('#password', env.GPASS) + log('已输入密码,开始登录') + + await Promise.all([ + page.waitForNavigation({ timeout: 10000 }), + page.keyboard.press('Enter') + ]) + log('登录成功') + resolve([page, browser]) + + return p + }, + + closeAll() { + browsers.map(b => b.close()) } } export default login + +async function isError(page: Page) { + const text = await page.evaluate(() => (document.querySelector('p')?.textContent || '')); + // const text = await page.evaluate('p', element => element.textContent); + return text && text.includes('error') +} diff --git a/electron/main/poe/getLink.ts b/electron/main/poe/getLink.ts index 919eb0d..3622d5c 100644 --- a/electron/main/poe/getLink.ts +++ b/electron/main/poe/getLink.ts @@ -1,8 +1,7 @@ -import { clog, loginGoogle } from './login' -import type { Browser, Page } from 'puppeteer' -import { readFileSync, writeFileSync } from 'fs' +import { clog } from '../tools' +import type { Page } from 'puppeteer' import path from 'path' -import { EOL } from 'os' +import login from '../login' function existDialog(page: Page) { return page.evaluate((selector, searchText) => { @@ -17,7 +16,7 @@ function existDialog(page: Page) { export async function getLink(options) { const log = clog(options) log('开始', { ident: 'poe-link' }) - return loginGoogle(options).then(async (page: Page) => { + return login.poe_google(options).then(async ([page, browser]) => { await page.waitForTimeout(1000) const isExistDialog = await existDialog(page) @@ -51,14 +50,10 @@ export async function getLink(options) { if (response.ok()) { const url = response._request._frame._url log('获取链接成功', { result: url, type: 'success' }) + browser.close() return url - - // if (options.index === 0) { - // writeFileSync(resolve('./hao.txt'), '', 'utf8') - // } - // const fileContent = readFileSync(resolve('./hao.txt'), 'utf8') - // writeFileSync(resolve('./hao.txt'), fileContent + `${EOL}${options.index + 1} ${options.user}${EOL}${url}`, 'utf8') } + browser.close() }).catch(error => { console.log('error ->', error.try, error.text, error) if (error?.try) { diff --git a/electron/main/poe/hao.txt b/electron/main/poe/hao.txt deleted file mode 100644 index e69de29..0000000 diff --git a/electron/main/poe/index.js b/electron/main/poe/index.js index 73081b3..34d383b 100644 --- a/electron/main/poe/index.js +++ b/electron/main/poe/index.js @@ -1,7 +1,6 @@ import { getLink } from './getLink' import { validate } from './validate' import { ipcMain } from 'electron' -import { browser } from './login' import { link_7day } from './link_7day' // import './openBrowser' @@ -32,7 +31,6 @@ ipcMain.handle('getLink', async (event, arg) => { }) console.log('process', i, user, link) } - browser && browser.close() return links }) @@ -54,7 +52,6 @@ ipcMain.handle('get-poe-link-7day', async (event, arg) => { }) console.log('process', i, user, link) } - // browser && browser.close() return links }) @@ -77,6 +74,5 @@ ipcMain.handle('poe-result', async (event, arg) => { }) console.log('process', i, user, link) } - browser && browser.close() return links }) diff --git a/electron/main/poe/link_7day.ts b/electron/main/poe/link_7day.ts index b45bcd2..138f6bb 100644 --- a/electron/main/poe/link_7day.ts +++ b/electron/main/poe/link_7day.ts @@ -1,50 +1,120 @@ import type { Browser, Page } from 'puppeteer' import puppeteer from 'puppeteer-extra' import StealthPlugin from 'puppeteer-extra-plugin-stealth' -import { clog } from "./login" +import { browserAndPage, clog, createPromise } from "../tools" +import login from '../login' puppeteer.use(StealthPlugin()) -async function login (options) { - const log = clog(options) - - log('启动浏览器') - - const browser = await puppeteer.launch({ - headless: false, - args: [ - '--no-sandbox', - '--disable-setuid-sandbox', - // `--proxy-server=http://192.168.1.80:7890`, - ] - }) - - const page = await browser.newPage() - await page.setExtraHTTPHeaders({ - 'accept-language': 'en-US,en;q=0.9,hy;q=0.8' - }) - - log('正在进入登录页面') - await page.goto('https://poe.com/login') - - log('设置登录方式') - await page.evaluate((selector, searchText) => { - const elements = Array.from(document.querySelectorAll(selector)); - const target = elements.find(el => el.textContent.trim() === searchText); - - if (!target) return false - - const text = target.nextElementSibling?.textContent || '' - if (text.includes('email')) target.nextElementSibling.click() - }, 'button', 'Go') - - await page.waitForSelector('input[type="email"]') - await page.type('input[type="email"]', options.user) - await page.keyboard.press('Enter') - -} - -export async function link_7day (options) { +export async function link_7day(options) { const log = clog(options) log('开始', { ident: 'link_7day' }) - await login(options) + + let { p, resolve, reject } = createPromise() + login.mail_get_code(options).then(([page, browser, options]) => { + if (options.code) { + resolve(options.code) + browser.close() + } else { + log('未获取验证码,重试一次', { ident: 'link_7day' }) + setTimeout(async () => { + const code = await options.validateCode() + if (code) { + browser.close() + resolve(code) + }else { + setTimeout(async () => { + const code = await options.validateCode() + if (code) { + browser.close() + resolve(code) + }else { + resolve('') + } + }, 3000); + } + }, 3000); + } + }) + const [page, browser] = await login.poe_email(options, async () => { + const code = await p + console.log('获取验证码结果', code) + if (!code) { + log('获取验证码失败', { ident: 'link_7day' }) + reject('获取验证码失败') + throw '获取验证码失败' + } + + return code + }) + + await getLink(options, [page, browser]) + + browser.close() + // await page.waitForTimeout(2000) + // browser.close() + // const { page } = await login.poe_email(options) +} + +function existDialog(page: Page) { + return page.evaluate((selector, searchText) => { + const elements = Array.from(document.querySelectorAll(selector)); + const target = elements.find(el => el.textContent.trim() === searchText); + target && target.click() + + return !!target + }, 'button', 'Start free trial') +} + +export async function getLink(options, [page, browser]: [Page, Browser]) { + const log = clog(options) + log('开始', { ident: 'poe-link' }) + await page.waitForTimeout(1000) + const isExistDialog = await existDialog(page) + + isExistDialog && log('检测到充值弹窗,无需前往设置页') + + if (!isExistDialog) { + await Promise.all([ + page.waitForNavigation(), + page.goto('https://poe.com/settings') + ]) + log('已进入设置页面, 检查中') + + const existMange = await page.evaluate(() => { + const mange = document.querySelector('[class*="SettingsSubscriptionSection_manageSubscription"]') + if (mange) return true + return false + }) + + if (existMange) { + log('已经订阅') + return + } + await page.waitForSelector('[class*=SettingsSubscriptionSection_subscribeButton]') + + page.waitForTimeout(500) + log('点击显示订阅套餐按钮') + await page.click('[class*=SettingsSubscriptionSection_subscribeButton]') + } + + // log('显示更多套餐') + // await page.waitForSelector('[class*=WebSubscriptionFreeTrial_viewAllPlansButton]') + // await page.click('[class*=WebSubscriptionFreeTrial_viewAllPlansButton]') + // log('点击最后一个套餐') + // await page.waitForSelector('[class*=WebSubscriptionPaywall_plans]') + // await page.click('[class*=WebSubscriptionPaywall_plans] > button:last-child') + + // 点击订阅 + log('4, 开始点击订阅') + await page.waitForSelector('[class*=WebSubscriptionPaywall_button]') + page.click('[class*=WebSubscriptionPaywall_button]') + const [response] = await Promise.all([ + page.waitForNavigation({ waitUntil: 'domcontentloaded' }), + ]) + + if (response.ok()) { + const url = response._request._frame._url + log('获取链接成功', { result: url, type: 'success' }) + return url + } } diff --git a/electron/main/poe/login.ts b/electron/main/poe/login.ts deleted file mode 100644 index da8502b..0000000 --- a/electron/main/poe/login.ts +++ /dev/null @@ -1,107 +0,0 @@ -import type { Browser, Page } from 'puppeteer' -import puppeteer from 'puppeteer-extra' -import StealthPlugin from 'puppeteer-extra-plugin-stealth' -import { randomNum, awaitWrap } from '../tools' -import { win } from '../index' -puppeteer.use(StealthPlugin()) - - -let id -export let browser: Browser -/** - * 登录google - */ -export async function loginGoogle(options, tryCount = 1) { - let resolve, reject - const log = clog(options) - const p = new Promise((res, rej) => { - resolve = res - reject = rej - }) - id = options.id - const env = { - GUSER: options.user, - GPASS: options.pass - } - - if (browser) await browser.close() - - browser = await puppeteer.launch({ headless: false }) - const page = await browser.newPage() - await page.setExtraHTTPHeaders({ - 'accept-language': 'en-US,en;q=0.9,hy;q=0.8' - }) - - log('开始访问 poe') - await page.goto('https://poe.com') - log('已进入 poe') - await page.waitForTimeout(randomNum(1000, 2600)) - - log('准备进入 google 登录页') - - page.click('.ContinueWithGoogleButton_buttonContentWrapper__Mrp0W') - const [err, response] = await awaitWrap(page.waitForNavigation({ timeout: 10000 })) - if (err) { - reject({ text: '登录报错', try: true }) - return p - } - log('已进入 google 登录页') - - if (response.ok()) { - await page.waitForSelector('input[type="email"]') - log('准备输入账号') - await page.type('input[type="email"]', env.GUSER) - await Promise.all([ - page.waitForNavigation(), - await page.keyboard.press('Enter') - ]) - log('已输入账号,准备输入密码') - if (await isError(page)) { - reject({ text: '登录报错', try: true }) - return p - } - - await page.waitForSelector('input[type="password"]', { visible: true }) - await page.type('input[type="password"]', env.GPASS) - log('已输入密码,开始登录') - await Promise.all([ - page.waitForFunction(() => location.href === 'https://poe.com/'), - await page.keyboard.press('Enter') - ]) - if (await isError(page)) { - reject({ text: '登录报错', try: true }) - return p - } - - log('登录成功,准备进入 poe') - - return page - } - - if (tryCount < 4) { - log(`google 登录失败,准备 ${tryCount} 次重试`) - await page.close() - return await loginGoogle(options, tryCount + 1) - } else { - log('google 登录失败,重试次数已达上限') - reject('google 登录失败') - } - - return p - // return Promise.reject('google 登录失败') -} - -async function isError(page: Page) { - const text = await page.evaluate(() => (document.querySelector('p')?.textContent || '')); - // const text = await page.evaluate('p', element => element.textContent); - return text && text.includes('error') -} - - -export function clog(options) { - return (info, data = {}) => { - if (win) { - win.webContents.send('progress', { ...options, info, ...data }) - } - } -} diff --git a/electron/main/poe/openBrowser.js b/electron/main/poe/openBrowser.js deleted file mode 100644 index 2ea0089..0000000 --- a/electron/main/poe/openBrowser.js +++ /dev/null @@ -1,8 +0,0 @@ -import puppeteer from 'puppeteer-extra' -import StealthPlugin from 'puppeteer-extra-plugin-stealth' -puppeteer.use(StealthPlugin()) - -;(async () => { - const browser = await puppeteer.launch({ headless: false }) - browser.newPage() -})() diff --git a/electron/main/poe/validate.js b/electron/main/poe/validate.js index 8061e71..6711a2a 100644 --- a/electron/main/poe/validate.js +++ b/electron/main/poe/validate.js @@ -1,12 +1,11 @@ -import { clog, loginGoogle } from './login' -import { readFileSync, writeFileSync } from 'fs' +import login from '../login' import path from 'path' -import { EOL } from 'os' +import { clog } from '../tools' export async function validate (options) { const log = clog(options) log('开始', { ident: 'poe-validate' }) - const page = await loginGoogle(options) + const [page, browser] = await login.poe_google(options) await Promise.all([ page.waitForNavigation(), @@ -16,22 +15,10 @@ export async function validate (options) { log('已进入设置页面, 检查中', { ident: 'poe-validate' }) const length = await page.$$eval('.SettingsSubscriptionSection_botLimitSection__j4mSO > div:first-child > div', (doms) => doms.length) - // page.close() - - // if (options.index === 0) { - // writeFileSync(resolve('./validate.txt'), '', 'utf8') - // } - // const fileContent = readFileSync(resolve('./validate.txt'), 'utf8') const resultTxt = length === 1 ? '失败😭' : '成功😘' - // writeFileSync(resolve('./validate.txt'), fileContent + `${options.index + 1} ${options.user}----${resultTxt}${EOL}`, 'utf8') - log(resultTxt, { type: length === 1 ? 'fail' : 'success', result: length === 1 ? '失败😭' : '成功😘' }) - // // 不成功 - // if (length === 1) { - // } else { - // log('充值成功😘') - // } + browser.close() } function resolve (url) { diff --git a/electron/main/tools.ts b/electron/main/tools.ts index 6b9732f..32aaa70 100644 --- a/electron/main/tools.ts +++ b/electron/main/tools.ts @@ -1,6 +1,8 @@ import type { Page } from "puppeteer" import puppeteer from 'puppeteer-extra' import StealthPlugin from 'puppeteer-extra-plugin-stealth' +import chromeLauncher from 'chrome-launcher' +import { win } from './index' puppeteer.use(StealthPlugin()) //生成从minNum到maxNum的随机数 @@ -17,6 +19,7 @@ export function awaitWrap(promise: Promise): Promise<[U | null, T // 设置页面请求 export function pageRequest (page: Page) { + page.setRequestInterception(true) page.on('request', (request) => { if (['stylesheet', 'font'].indexOf(request.resourceType()) !== -1) { request.abort() @@ -26,9 +29,10 @@ export function pageRequest (page: Page) { }) } -const browsers = [] +export const browsers = [] -export async function browserAndPage (options) { +export async function browserAndPage (options = {}) { + const { request } = options const browser = await puppeteer.launch({ headless: false, args: [ @@ -37,10 +41,11 @@ export async function browserAndPage (options) { // `--proxy-server=http:// ] }) + browsers.push(browser) const close = browser.close - browser.close = async () => { + browser.close = async function () { let i = browsers.find(b => b === browser) - await close() + await close.call(this) if (i !== -1) browsers.slice(i, 1) } @@ -49,7 +54,59 @@ export async function browserAndPage (options) { 'accept-language': 'en-US,en;q=0.9,hy;q=0.8' }) - pageRequest(page) + if (request) pageRequest(page) return { browser, page } } + +/** + * 记录日志 + */ +export function clog(options) { + return (info, data = {}) => { + if (win) { + win.webContents.send('progress', { ...options, info, ...data }) + } + } +} + +/** + * 创建一个 promise + */ +export function createPromise () { + let resolve: (value: T | PromiseLike) => void, + reject + const p = new Promise((res, rej) => { + resolve = res + reject = rej + }) + return { resolve, reject, p } +} + +async function findElement (page: Page, selector: string, searchText: string) { + return page.evaluate((selector, searchText) => { + const elements = Array.from(document.querySelectorAll(selector)); + const target = elements.find(el => el.textContent.trim() === searchText); + return target + }, selector, searchText) +} + +/** + * 元素检查,被点击或按下按键后,检查是否成功,不成功测重试或其他操作 + */ +export async function elCheck(operationsFn: Function, checkFn: Function, tryCount = 1) { + const { p, resolve, reject } = createPromise() + await operationsFn() + const pass = await checkFn() + if (pass) { + resolve(true) + } else { + if (tryCount > 1) { + return await elCheck(operationsFn, checkFn, tryCount - 1) + } else { + resolve(false) + } + } + + return p +} diff --git a/package.json b/package.json index ea8fce7..2bb48a9 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "dependencies": { "@vueuse/core": "^10.1.2", "axios": "^1.4.0", + "chrome-launcher": "^0.15.2", "mockjs": "^1.1.0", "naive-ui": "^2.34.4", "proxy-chain": "^2.3.0", diff --git a/src/App.vue b/src/App.vue index 034b34c..1537a05 100644 --- a/src/App.vue +++ b/src/App.vue @@ -2,7 +2,7 @@ import { ipcRenderer } from 'electron' import { NButton } from 'naive-ui' import { useClipboard } from '@vueuse/core' -const input = ref('r0tg74y71ophdadi2r@newgmail.icu----7rtupH27r') +const input = ref('pollcribracacom@mail.com-----XAxeEgy34j') // const input = ref('126vdsjmgyanpgqrvb@ddmvp.icu----EOJ2NgPfS') // const input = ref('traceetakashi6274@gmail.com----kedaraditi0214----kedaraditi4760@hotmail.com')