diff --git a/electron-builder.json5 b/electron-builder.json5 index cc2fca2..3cebd07 100644 --- a/electron-builder.json5 +++ b/electron-builder.json5 @@ -4,7 +4,7 @@ { "$schema": "https://raw.githubusercontent.com/electron-userland/electron-builder/master/packages/app-builder-lib/scheme.json", "appId": "YourAppID", - "asar": true, + "asar": false, "directories": { "output": "release/${version}" }, diff --git a/electron/main/gpt/batchApplication.ts b/electron/main/gpt/batchApplication.ts new file mode 100644 index 0000000..6a9880b --- /dev/null +++ b/electron/main/gpt/batchApplication.ts @@ -0,0 +1,152 @@ +import type { Browser, Page } from 'puppeteer' +import puppeteer from 'puppeteer-extra' +import StealthPlugin from 'puppeteer-extra-plugin-stealth' +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' +import proxyChain from 'proxy-chain' +// const proxyChain = require('proxy-chain'); + +puppeteer.use(StealthPlugin()) + + +function getProxy(options = {} as any) { + const log = clog(options) + log('开始获取代理ip') + return axios.get('http://api.proxy.ipidea.io/getBalanceProxyIp?num=1&return_type=json&lb=1&sb=0&flow=1®ions=&protocol=socks5', { + // return axios.get('https://www.miyaip.com/api/ProxyGenerage/PublicGenerateProxy?country=us&city=jaffrey®ion=nh&num=1&apiSwitch=0&mealType=2&genType=2&username=nmfk549724@163.com&secret=E3BCBmiyaipC23358C250F5', { + // return axios.get('http://api.tianqiip.com/getip?secret=loy0r7fpmnlshm8l&num=1&type=json&port=3&time=3&mr=1&sign=5f73ab58ad7ab40346311014bef59b79', { + timeout: 10 * 1000, + }).then(res => { + + // // const [user, pass] = res.data.split(':') + // // return { user, pass, ip, port } + // // // return axios.get('https://www.miyaip.com/api/ProxyGenerage/PublicGenerateProxy?country=us&city=cairo®ion=ny&num=1&apiSwitch=0&mealType=2&genType=2&username=nmfk549724@163.com&secret=E3BCBmiyaipC23358C250F5').then(res => { + // const [ip, port] = res.data.replace(/(\n|\r\s)/g, '').split(':') + // console.log({ ip, port, }) + // // const [ip, pory, user, pass] = res.data.split(':') + return res.data.data[0] + }) + return Promise.resolve({ + ip: '43.130.10.70', + port: '22993', + // user: '1E783B07miyaip489C251B3FA7', + // pass: '1g7E3M4U5w03vO' + }) +} + +// 获取组织id +async function getOrgId(page: Page, options: any) { + const log = clog(options) + log('准备进入组织页面') + await page.goto('https://platform.openai.com/account/org-settings') + await page.waitForSelector('input') + log('开始获取组织id') + const orgId = await page.$$eval('input', (inputs: HTMLInputElement[]) => inputs?.[1].value || '') + log('获取组织id成功', { orgId }) + return orgId +} + +// 申请 +export async function application(page: Page, options = {} as any) { + const log = clog(options) + + log('准备进入申请页面') + await page.goto('https://openai.com/waitlist/gpt-4-api') + + log('等待出现输入框') + await page.waitForSelector('input') + + log('开始申请') + + const f = Mock.mock('@first') + const l = Mock.mock('@last') + console.log(f, l); + + await page.type('#firstname', f) + await page.type('#lastname', l) + await page.type('#email', options.user) + await page.type('#organizationId', options.orgId) + + const i = randomNum(1, 4) + + log('选择申请类型', { i }) + page.evaluate((i) => { + document.querySelector('#primaryUse').nextSibling.childNodes[i].childNodes[1].click() + }, i) + + log('等待 gpt 生成描述') + const desc = await generateDescription(i) + await page.type('#ideas', desc) + + log('点击提交申请') + const isSuccess = await page.evaluate((selector, searchText) => { + const elements = Array.from(document.querySelectorAll(selector)); + const target = elements.find(el => el.textContent.trim() === searchText); + + if (target) target.click() + return !!target + }, 'button', 'Join waitlist') + + if (isSuccess) { + log('提交成功,等待 gpt 返回结果') + console.log('isSuccess', isSuccess) + await page.waitForSelector('button[type="submit"] + .ui-richtext') + const text = await page.evaluate(() => document.querySelector('button[type="submit"] + .ui-richtext').textContent) + log('gpt 返回已结果', { result: text }) + } + + + // await page.click('form[data-gtm-form-interact-id] button[type="submit"]') +} + + +// 生成描述 +export async function generateDescription(desIndex = 0): Promise { + const types = { + 1: 'Build a new product', + 2: 'Integrate into an existing product', + 3: 'General exploration of capabilities', + 4: 'Academic research', + } + + return axios({ + url: 'https://api.openai-proxy.com/v1/chat/completions', + method: 'post', + headers: { + Authorization: `Bearer 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` + }, + data: { + max_tokens: 1024, + model: 'gpt-3.5-turbo', + messages: [ + { "role": "user", "content": `我在申请gpt4.0,我的类型是 ${types[desIndex]},不要解释,给我一段申请的理由,以我的身份,以英语形式发给我,结尾不要带任何名字或位置,三四句话就行` }, + ] + }, + timeout: 1000 * 30, + }).then(res => { + console.log('gpt 生成描述', res.data.choices[0].message.content) + return res.data.choices[0].message.content + }) +} + +export async function batchApplication(options) { + const log = clog(options) + log('开始', { ident: 'gpt-batch-4.0' }) + + const [error, [page, browser]] = await awaitWrap(login.openai(options)) + if (error) return log('登录失败', { error, ident: 'gpt-batch-4.0' }) + + await page.waitForSelector('.ovr-section') + + const orgId = await getOrgId(page, options) + options.orgId = orgId + console.log('orgId', orgId) + + await application(page, options) + + browser.close() +} diff --git a/electron/main/gpt/getLink.ts b/electron/main/gpt/getLink.ts new file mode 100644 index 0000000..b12e0ce --- /dev/null +++ b/electron/main/gpt/getLink.ts @@ -0,0 +1,48 @@ +import { clog } from "../tools"; +import login from "../login"; + +export async function getLink(options) { + const log = clog(options) + log('开始', { ident: 'gpt-link', ...options }) + const [page, browser] = await login.chatgpt({ ...options, changeUS: false }) + + // await page.waitForTimeout(500) + + await page.waitForSelector('body > div.absolute.inset-0', { timeout: 0 }) + await page.evaluate(() => { + const $mark = document.querySelector("body > div.absolute.inset-0") + if ($mark) $mark.hidden = true + document.body.style.pointerEvents = 'all' + }) + + log('隐藏欢迎页面') + + await page.waitForSelector('.gold-new-button', { visible: true }) + log('等待升级plus按钮出现') + await page.waitForTimeout(500) + + await page.evaluate(() => { + const $mark = document.querySelector("body > div.absolute.inset-0") + if ($mark) $mark.remove() + }) + + log('开始点击升级plus按钮') + await page.click('.gold-new-button') + + const [response] = await Promise.all([ + page.waitForNavigation({ waitUntil: 'domcontentloaded', timeout: 0 }), + page.evaluate((selector, searchText) => { + const elements = Array.from(document.querySelectorAll(selector)); + const target = elements.find(el => el.textContent.trim() === searchText); + target && target.click() + }, 'button', 'Upgrade plan') + ]) + + 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 new file mode 100644 index 0000000..9b51c58 --- /dev/null +++ b/electron/main/gpt/index.ts @@ -0,0 +1,93 @@ +import { ipcMain } from 'electron' +import { getLink } from './getLink' +import { validate } from './validate' +import { batchApplication } from './batchApplication' +import { chunk } from 'lodash' +import { sleep } from '../tools' + +const parseAccount = text => text.split('\n').filter(Boolean).map(v => { + v = v.split(/(——|-)+/).filter(v => !['-', '——'].includes(v)) + return v +}) + +ipcMain.handle('gpt-link', async (event, arg) => { + const { text } = arg + // 进程数 + const processNum = 2 + const totalArr = parseAccount(text) + const accounts = chunk(totalArr, Math.ceil(totalArr.length / processNum)) + + async function run (accounts) { + for(let i = 0; i < accounts.length; i++) { + const [user, pass] = accounts[i] + const link = await getLink({ user, pass, index: i, id: user, ...arg }) + console.log('process', i, user, link) + } + } + const links = [] + for(let i = 0; i < accounts.length; i++) { + if (i !== 0) await sleep(2000) + run(accounts[i]) + } +}) + +ipcMain.handle('gpt-result', async (event, arg) => { + const { text } = arg + const accounts = parseAccount(text) + + const links = [] + for(let i = 0; i < accounts.length; i++) { + const [user, pass] = accounts[i] + const link = await validate({ user, pass, index: i, id: user }) + links.push({ + i, + user, + link + }) + console.log('process', i, user, link) + } + // browser && browser.close() +}) + + +ipcMain.handle('gpt-batch-4.0', async (event, arg) => { + const { text } = arg + const accounts = parseAccount(text) + + const links = [] + for(let i = 0; i < accounts.length; i++) { + const [user, pass] = accounts[i] + const link = await batchApplication({ user, pass, index: i, id: user }) + links.push({ + i, + user, + link + }) + console.log('process', i, user, link) + } + // browsers.forEach(browser => browser.close()) +}) + + +const actions = { + 'gpt-link': getLink, + 'gpt-result': validate, + 'gpt-batch-4.0': batchApplication +} + +export async function runActions(action: keyof typeof actions, options: any) { + const { text } = options + const accounts = parseAccount(text) + + const links = [] + for(let i = 0; i < accounts.length; i++) { + const [user, pass] = accounts[i] + const link = await batchApplication({ user, pass, index: i, id: user }) + links.push({ + i, + user, + link + }) + console.log('process', i, user, link) + } +} diff --git a/electron/main/gpt/validate.ts b/electron/main/gpt/validate.ts new file mode 100644 index 0000000..8887b39 --- /dev/null +++ b/electron/main/gpt/validate.ts @@ -0,0 +1,28 @@ +import { clog } from "../tools"; +import login from "../login"; + +export async function validate(options) { + const log = clog(options) + log('开始', { ident: 'gpt-validate' }) + const [page, browser] = await login.chatgpt(options) + + await page.waitForSelector('body > div.absolute.inset-0') + await page.evaluate(() => { + const $mark = document.querySelector("body > div.absolute.inset-0") + if ($mark) $mark.hidden = true + document.body.style.pointerEvents = 'all' + }) + log('隐藏欢迎页面,开始检测') + + await page.waitForTimeout(2000) + + const isSuccess = await page.evaluate((selector, searchText) => { + const elements = Array.from(document.querySelectorAll(selector)); + const target = elements.find(el => el.textContent.trim() === searchText); + return !!target + }, 'button', 'GPT-4') + 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 a1bed6c..161275a 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -1,6 +1,24 @@ import { app, BrowserWindow, shell, ipcMain } from 'electron' import { release } from 'node:os' import { join } from 'node:path' +import './poe/index' +import './gpt/index' +import { browsers, test1 } from './tools' + +import puppeteer from 'puppeteer-extra' +import StealthPlugin from 'puppeteer-extra-plugin-stealth' +puppeteer.use(StealthPlugin()) + + +// test1() +// puppeteer.launch({ +// headless: false, +// args: [ +// '--no-sandbox', +// '--disable-setuid-sandbox', +// '--proxy-server=socks5://127.0.0.1:40000' +// ] +// }) // The built directory structure // @@ -34,7 +52,7 @@ if (!app.requestSingleInstanceLock()) { // Read more on https://www.electronjs.org/docs/latest/tutorial/security // process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true' -let win: BrowserWindow | null = null +export let win: BrowserWindow | null = null // Here, you can also use other preload const preload = join(__dirname, '../preload/index.js') const url = process.env.VITE_DEV_SERVER_URL @@ -43,6 +61,8 @@ const indexHtml = join(process.env.DIST, 'index.html') async function createWindow() { win = new BrowserWindow({ title: 'Main window', + width: 1000, + height: 800, icon: join(process.env.PUBLIC, 'favicon.ico'), webPreferences: { preload, @@ -56,8 +76,10 @@ async function createWindow() { if (process.env.VITE_DEV_SERVER_URL) { // electron-vite-vue#298 win.loadURL(url) + console.log('process.env.VITE_DEV_SERVER_URL', process.env.VITE_DEV_SERVER_URL); + // Open devTool if the app is not packaged - win.webContents.openDevTools() + // win.webContents.openDevTools() } else { win.loadFile(indexHtml) } @@ -115,3 +137,8 @@ ipcMain.handle('open-win', (_, arg) => { childWindow.loadFile(indexHtml, { hash: arg }) } }) + +ipcMain.handle('stop', async (event, arg) => { + browsers.forEach(browser => browser.close()) + return true +}) diff --git a/electron/main/login.ts b/electron/main/login.ts new file mode 100644 index 0000000..56c27a3 --- /dev/null +++ b/electron/main/login.ts @@ -0,0 +1,373 @@ +import type { Page, Browser } from 'puppeteer' +import { browsers, clog, statusCheck } from './tools' +import { awaitWrap, browserAndPage, randomNum } from './tools' + +const login = { + // poe 邮箱登录 + async poe_email(options, getCodeFn: Function, tryCount = 1): Promise<[Page, Browser]> { + const log = clog(options) + + if (tryCount > 2) { + log('重试次数已达上限') + throw '重试次数已达上限' + return + } + + log('启动浏览器') + + const { browser, page } = await browserAndPage({ ...options, proxy: true }) + + 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') + + log('开始输入邮箱') + await page.waitForSelector('input[type="email"]') + await page.type('input[type="email"]', options.user) + await page.waitForTimeout(1000) + await statusCheck( + async () => await page.keyboard.press('Enter'), + async () => await page.$('.LoadingDots_wrapper__lXyQd'), // LoadingDots_wrapper__lXyQd + ) + + // 检查是否有错误 + const isOk = await Promise.race([ + page.waitForSelector('.InfoText_error__OQwmg').then(() => false), + page.waitForSelector('input[class^="VerificationCodeInput_verificationCodeInput"]').then(() => true) + ]) + + console.log('isOk', isOk) + if (!isOk) { + const reason = await page.$eval('.InfoText_error__OQwmg', (el: HTMLElement) => el.textContent.trim()) + if (reason.startsWith('Something')) { + await page.waitForTimeout(3482) + await statusCheck( + async () => await page.keyboard.press('Enter'), + async () => await page.$('.LoadingDots_wrapper__lXyQd'), // LoadingDots_wrapper__lXyQd + ) + } else { + log('', { result: `登录失败: ${reason}` }) + browser.close() + throw '登录失败' + } + // return login.poe_email(options, getCodeFn, tryCount + 1) + } + + // await page.keyboard.press('Enter') + log('开始输入密码') + + 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, changeUS: false }) + + 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('开始登录') + + /** + * + * @param reload 是否点击刷新验证码按钮 + */ + async function validateCode() { + log('开始获取code') + let $iframe = await page.$('#thirdPartyFrame_home') + let frame = await $iframe?.contentFrame() + + if (!frame) { + log('未找到页面,刷新重试中...') + await page.reload() + return await validateCode() + } + + const [err] = await awaitWrap(frame.waitForSelector('ul.inbox-container li', { timeout: 4000 })) + if (err) { + console.log(err) + console.log('有报错') + await frame.waitForSelector('.ico.sync') + const sync = await page.$('.ico.sync') + if (!sync) { + console.log('未找到刷新按钮') + log('未找到刷新按钮') + return '' + } + await frame.click('.ico.sync') + await frame.waitForTimeout(4000) + return await validate() + } + + return await validate() + + + // console.log('type', type) + // if (type === 'list') { + // return await validate() + // } + + + await frame.waitForSelector('.ico.sync') + await frame.click('.ico.sync') + await frame.waitForTimeout(1000) + + await frame.waitForSelector('ul.inbox-container li') + log('查找邮箱') + + async function validate() { + 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, changeUS: false }) + 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: 0 }), + 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({ timeout: 0 }), + 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, waitUntil: 'domcontentloaded' }), + 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/cookie.ts b/electron/main/poe/cookie.ts new file mode 100644 index 0000000..46b86fb --- /dev/null +++ b/electron/main/poe/cookie.ts @@ -0,0 +1,193 @@ +import type { Browser, Page } from 'puppeteer' +import puppeteer from 'puppeteer-extra' +import StealthPlugin from 'puppeteer-extra-plugin-stealth' +import { browserAndPage, browsers, clog, createPromise, statusCheck } from "../tools" +import login from '../login' +puppeteer.use(StealthPlugin()) + +export async function getCookie(options) { + console.log('options', options); + const log = clog(options) + log('开始', { ident: 'link_7day' }) + + let { p, resolve, reject } = createPromise() + + login.mail_get_code({ ...options, changeUS: false }).then(async ([page, browser, options]) => { + const isOk = await statusCheck( + () => options.code, + async () => { + log('未获取验证码,重试一次', { ident: 'link_7day' }) + options.code = await options.validateCode() + return options.code + }, + { interval: 4000 } + ) + resolve(options.code) + browser.close() + }) + + 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 + }).catch(() => '' as any) + + // 登录失败了 + if (!page) { + console.log('登录失败了') + browsers.map(b => b.close()) + return + } + + const cookies = await page.cookies() + const token = cookies.find(v => v.name === 'p-b')?.value + + log('获取token', { result: token }) + console.log('cookies', cookies) + // browser.close() + // await page.waitForTimeout(2000) + browser.close() + // const { page } = await login.poe_email(options) +} + +async function recharge(options: { + browser: Browser, + page: Page, + user: string, + liao: any, +}) { + const { browser, page, liao, ...args } = options + const log = clog(args) + log('开始充值') + await page.waitForSelector('#cardNumber') + + await page.evaluate(() => { + const btn: HTMLElement = document.querySelector('.AddressAutocomplete-manual-entry .Link') + if (btn) btn.click() + }) + + await page.evaluate((email: string) => { + const el: HTMLInputElement = document.querySelector('#email') + if (el) el.value = email + }, options.user) + + await page.type('#cardNumber', liao.bank) + await page.type('#cardExpiry', liao.date) + await page.type('#cardCvc', liao.cvc) + await page.type('#billingName', liao.name) + await page.select('#billingCountry', liao.nation) + await page.type('#billingAddressLine1', liao.address) + await page.type('#billingLocality', liao.city) + await page.type('#billingPostalCode', liao.postalCode) + await page.waitForTimeout(1000) + await page.click('.SubmitButton-IconContainer') + + // await page.waitForTimeout(9000) + + // await page.waitForSelector('iframe') + // let $frame = await page.$('iframe') + // let frame = await $frame.contentFrame() + + // console.log('$frame', $frame) + // // console.log('frame', frame) + // await $frame.waitForSelector('iframe') + // $frame = await frame.$('iframe') + // frame = await $frame.contentFrame() + // await frame.waitForSelector('iframe') + // $frame = await frame.$('iframe') + // frame = await $frame.contentFrame() + + // await frame.waitForSelector('#checkbox', { timeout: 0 }) + // await frame.click('#checkbox') + // await page.waitForNavigation() + console.log('充值成功') + log('充值成功') +} + +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]', { timeout: 0 }) + + page.waitForTimeout(500) + log('点击显示订阅套餐按钮') + // await page.click('[class*=SettingsSubscriptionSection_subscribeButton]') + + const disabled = await page.$eval('[class*=SettingsSubscriptionSection_subscribeButton]', (el: HTMLButtonElement) => el.disabled) + if (disabled) { + log('订阅按钮不可用,地区不可用') + return + } + + await statusCheck( + async () => { + return page.evaluate(() => { + const $el = document.querySelector("[class*=SettingsSubscriptionSection_subscribeButton]") + console.log('$el', $el) + $el.click(); + }) + }, + // async () => page.click('[class*=SettingsSubscriptionSection_subscribeButton]'), + async () => page.$('.Modal_modal__SxITf'), + ) + } + + // 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]', { timeout: 0 }) + 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/getLink.ts b/electron/main/poe/getLink.ts new file mode 100644 index 0000000..3622d5c --- /dev/null +++ b/electron/main/poe/getLink.ts @@ -0,0 +1,69 @@ +import { clog } from '../tools' +import type { Page } from 'puppeteer' +import path from 'path' +import login from '../login' + +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) { + const log = clog(options) + log('开始', { ident: 'poe-link' }) + return login.poe_google(options).then(async ([page, browser]) => { + 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('已进入设置页面, 检查中') + await page.waitForSelector('[class*=SettingsSubscriptionSection_subscribeButton]') + 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, 开始点击订阅') + 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' }) + browser.close() + return url + } + browser.close() + }).catch(error => { + console.log('error ->', error.try, error.text, error) + if (error?.try) { + return getLink(options) + } + }) + +} + + +function resolve(url) { + return path.resolve(__dirname, '../src/poe/', url) +} diff --git a/electron/main/poe/index.js b/electron/main/poe/index.js new file mode 100644 index 0000000..a33a6cf --- /dev/null +++ b/electron/main/poe/index.js @@ -0,0 +1,102 @@ +import { getLink } from './getLink' +import { validate } from './validate' +import { ipcMain } from 'electron' +import { link_7day } from './link_7day' +import { getCookie } from './cookie' +import { browserAndPage } from '../tools' + +export const parseAccount = text => text.split('\n').filter(Boolean).map(v => { + v = v.split(/(——|-)+/).filter(v => !['-', '——'].includes(v)) + return v +}) + +ipcMain.handle('getLink', async (event, arg) => { + const { text } = arg + const accounts = parseAccount(text) + + const links = [] + for (let i = 0; i < accounts.length; i++) { + const [user, pass, auxiliary] = accounts[i] + const link = await getLink({ user, pass, auxiliary, index: i, id: user }) + // .catch(err => { + // console.log('error ->', err) + // }) + links.push({ + i, + user, + link + }) + console.log('process', i, user, link) + } + return links +}) + +ipcMain.handle('get-poe-link-7day', async (event, arg) => { + const { text, liao } = arg + const accounts = parseAccount(text) + + const links = [] + for (let i = 0; i < accounts.length; i++) { + const [user, pass, auxiliary] = accounts[i] + const link = await link_7day({ user, pass, auxiliary, index: i, id: user, liao }) + // .catch(err => { + // console.log('error ->', err) + // }) + links.push({ + i, + user, + link + }) + console.log('process', i, user, link) + } + return links +}) + + + +ipcMain.handle('get-poe-cookie', async (event, arg) => { + const { text, liao } = arg + const accounts = parseAccount(text) + + const links = [] + for (let i = 0; i < accounts.length; i++) { + const [user, pass, auxiliary] = accounts[i] + const link = await getCookie({ user, pass, auxiliary, index: i, id: user, liao }) + // .catch(err => { + // console.log('error ->', err) + // }) + links.push({ + i, + user, + link + }) + console.log('process', i, user, link) + } + return links +}) + + +ipcMain.handle('poe-result', async (event, arg) => { + const { text } = arg + const accounts = parseAccount(text) + + const links = [] + for (let i = 0; i < accounts.length; i++) { + const [user, pass, auxiliary] = accounts[i] + const link = await validate({ user, pass, auxiliary, index: i, id: user }) + // .catch(err => { + // console.log('error ->', err) + // }) + links.push({ + i, + user, + link + }) + console.log('process', i, user, link) + } + return links +}) + +ipcMain.handle('start-one-chrom', async (event, arg) => { + browserAndPage({ proxy: true }) +}) diff --git a/electron/main/poe/link_7day.ts b/electron/main/poe/link_7day.ts new file mode 100644 index 0000000..de5e8be --- /dev/null +++ b/electron/main/poe/link_7day.ts @@ -0,0 +1,217 @@ +import type { Browser, Page } from 'puppeteer' +import puppeteer from 'puppeteer-extra' +import StealthPlugin from 'puppeteer-extra-plugin-stealth' +import { awaitWrap, browserAndPage, browsers, clog, createPromise, statusCheck } from "../tools" +import login from '../login' +puppeteer.use(StealthPlugin()) + +export async function link_7day(options) { + console.log('options', options); + const log = clog(options) + log('开始', { ident: 'link_7day' }) + + let { p, resolve, reject } = createPromise() + + login.mail_get_code({ ...options, changeUS: false }).then(async ([page, browser, options]) => { + const isOk = await statusCheck( + () => options.code, + async () => { + log('未获取验证码,重试一次', { ident: 'link_7day' }) + options.code = await options.validateCode() + return options.code + }, + { interval: 4000 } + ) + resolve(options.code) + browser.close() + }) + + 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 + }).catch(() => '' as any) + + // 登录失败了 + if (!page) { + console.log('登录失败了') + browsers.map(b => b.close()) + return + } + + const url = await getLink(options, [page, browser]) + + return url + if (url && options.liao) { + await recharge({ ...options, page, browser }) + } + + // browser.close() + // await page.waitForTimeout(2000) + // browser.close() + // const { page } = await login.poe_email(options) +} + +async function test() { + const url = 'https://checkout.stripe.com/c/pay/cs_live_a1MXhrEYuI3qJEjw85zmuIxjFsgswafv0xlcxUGAOIeyiGJBGLA56mvRto#fidkdWxOYHwnPyd1blppbHNgWjxITDJsdEROY3Y1NjZpNTc8Q1RMU3ZTNicpJ2N3amhWYHdzYHcnP3F3cGApJ2lkfGpwcVF8dWAnPyd2bGtiaWBabHFgaCcpJ2BrZGdpYFVpZGZgbWppYWB3dic%2FcXdwYHgl' + const liao = {"bank":"4833160230060672","cvc":"727","date":"04/26","name":"Zackary Wais","address":"1200LakeshoreaveApt8G","city":"Oakland","postalCode":"94606","nation":"US"} + const user = 'neletegcongder@mail.com' + + const { page, browser } = await browserAndPage() + await page.goto(url, { waitUntil: 'domcontentloaded' }) + await recharge({ browser, page, user, liao }) +} + +// test() + +async function recharge(options: { + browser: Browser, + page: Page, + user: string, + liao: any, +}) { + const { browser, page, liao, ...args } = options + const log = clog(args) + log('开始充值') + await page.waitForSelector('#cardNumber') + + await page.evaluate(() => { + const btn: HTMLElement = document.querySelector('.AddressAutocomplete-manual-entry .Link') + if (btn) btn.click() + }) + + const isEmail = await page.evaluate((email: string) => { + const el: HTMLInputElement = document.querySelector('#email') + return !!el + }, options.user) + if (isEmail) await page.type('#email', options.user) + + + await page.type('#cardNumber', liao.bank) + await page.type('#cardExpiry', liao.date) + await page.type('#cardCvc', liao.cvc) + await page.type('#billingName', liao.name) + await page.select('#billingCountry', liao.nation) + await page.type('#billingAddressLine1', liao.address) + await page.type('#billingLocality', liao.city) + await page.type('#billingPostalCode', liao.postalCode) + await page.waitForTimeout(1000) + await page.click('.SubmitButton-IconContainer') + + // await page.waitForTimeout(8000) + + // const [error, config] = await awaitWrap(page.solveRecaptchas()) + // if (error) { + // log('充值失败', { result: '充值失败', }) + // } else { + // console.log(config) + // log('充值成功', { result: '充值成功', }) + // } + + // await page.waitForSelector('iframe') + // let $frame = await page.$('iframe') + // let frame = await $frame.contentFrame() + + // console.log('$frame', $frame) + // // console.log('frame', frame) + // await frame.waitForSelector('iframe') + // $frame = await frame.$('iframe') + // frame = await $frame.contentFrame() + // await frame.waitForSelector('iframe') + // $frame = await frame.$('iframe') + // frame = await $frame.contentFrame() + + // await frame.waitForSelector('#checkbox', { timeout: 0 }) + // await frame.click('#checkbox') + await page.waitForNavigation() + console.log('充值成功') + log('充值成功') +} + +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]', { timeout: 0 }) + + page.waitForTimeout(500) + log('点击显示订阅套餐按钮') + // await page.click('[class*=SettingsSubscriptionSection_subscribeButton]') + + const disabled = await page.$eval('[class*=SettingsSubscriptionSection_subscribeButton]', (el: HTMLButtonElement) => el.disabled) + if (disabled) { + log('订阅按钮不可用,地区不可用') + return + } + + await statusCheck( + async () => { + return page.evaluate(() => { + const $el = document.querySelector("[class*=SettingsSubscriptionSection_subscribeButton]") + console.log('$el', $el) + $el.click(); + }) + }, + // async () => page.click('[class*=SettingsSubscriptionSection_subscribeButton]'), + async () => page.$('.Modal_modal__SxITf'), + ) + } + + // 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]', { timeout: 0 }) + 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/validate.js b/electron/main/poe/validate.js new file mode 100644 index 0000000..6711a2a --- /dev/null +++ b/electron/main/poe/validate.js @@ -0,0 +1,26 @@ +import login from '../login' +import path from 'path' +import { clog } from '../tools' + +export async function validate (options) { + const log = clog(options) + log('开始', { ident: 'poe-validate' }) + const [page, browser] = await login.poe_google(options) + + await Promise.all([ + page.waitForNavigation(), + page.goto('https://poe.com/settings') + ]) + + log('已进入设置页面, 检查中', { ident: 'poe-validate' }) + + const length = await page.$$eval('.SettingsSubscriptionSection_botLimitSection__j4mSO > div:first-child > div', (doms) => doms.length) + const resultTxt = length === 1 ? '失败😭' : '成功😘' + log(resultTxt, { type: length === 1 ? 'fail' : 'success', result: length === 1 ? '失败😭' : '成功😘' }) + + browser.close() +} + +function resolve (url) { + return path.resolve(__dirname, '../src/poe/', url) +} diff --git a/electron/main/tools.ts b/electron/main/tools.ts new file mode 100644 index 0000000..082122b --- /dev/null +++ b/electron/main/tools.ts @@ -0,0 +1,236 @@ +import type { Page } from "puppeteer" +import puppeteer from 'puppeteer-extra' +import StealthPlugin from 'puppeteer-extra-plugin-stealth' +import RecaptchaPlugin from 'puppeteer-extra-plugin-recaptcha' +import chromeLauncher from 'chrome-launcher' +import { win } from './index' +import fakeUa from 'fake-useragent' +import axios from "axios" +puppeteer.use(StealthPlugin()) +puppeteer.use(RecaptchaPlugin({ + provider: { + id: '2captcha', + token: '25bd4a3638f3836b0d119007cbc21954' + }, + visualFeedback: true +})) + + +export function test1() { + // puppeteer-extra is a drop-in replacement for puppeteer, + // it augments the installed puppeteer with plugin functionality + const puppeteer = require('puppeteer-extra') + + // add recaptcha plugin and provide it your 2captcha token (= their apiKey) + // 2captcha is the builtin solution provider but others would work as well. + // Please note: You need to add funds to your 2captcha account for this to work + const RecaptchaPlugin = require('puppeteer-extra-plugin-recaptcha') + puppeteer.use( + RecaptchaPlugin({ + provider: { + id: '2captcha', + token: '25bd4a3638f3836b0d119007cbc21954' // REPLACE THIS WITH YOUR OWN 2CAPTCHA API KEY ⚡ + }, + visualFeedback: true // colorize reCAPTCHAs (violet = detected, green = solved) + }) + ) + + // puppeteer usage as normal + puppeteer.launch({ headless: false }).then(async browser => { + const page = await browser.newPage() + await page.goto('https://www.google.com/recaptcha/api2/demo') + + // That's it, a single line of code to solve reCAPTCHAs 🎉 + const { + captchas, + filtered, + solutions, + solved, + error + } = await page.solveRecaptchas() + + console.log({ captchas, filtered, solutions, solved, error }) + + await Promise.all([ + page.waitForNavigation(), + page.click(`#recaptcha-demo-submit`) + ]) + await page.screenshot({ path: 'response.png', fullPage: true }) + await browser.close() + }) +} + +//生成从minNum到maxNum的随机数 +export function randomNum (min, max) { + return parseInt(Math.random() * (max - min + 1) + min) +} + +// promise 错误处理 +export function awaitWrap(promise: Promise): Promise<[U | null, T | null]> { + return promise + .then<[null, T]>((data: T) => [null, data]) + .catch<[U, null]>(err => [err, null]) +} + +// 设置页面请求 +export function pageRequest (page: Page) { + page.setRequestInterception(true) + page.on('request', (request) => { + if (['stylesheet', 'font'].indexOf(request.resourceType()) !== -1) { + request.abort() + } else { + request.continue() + } + }) +} + +export const browsers = [] + +export async function browserAndPage (options: any = {}) { + const { request, changeUS = true, proxy = false } = options + + async function proxyCommand () { + // const proxy = (await axios.get('http://127.0.0.1:10101/api/get_ip_list?num=1&country=US&state=all&city=all&zip=all&isp=all&t=1&port=40000&ip_time=1')).data + console.log('proxy', proxy); + + // return proxy ? + return `--proxy-server=socks5://127.0.0.1:40000` + } + + const args = [ + '--no-sandbox', + '--disable-setuid-sandbox', + '--disable-web-security', + '--disable-features=IsolateOrigins,site-per-process,SitePerProcess', + '--flag-switches-begin --disable-site-isolation-trials --flag-switches-end' + ] + + proxy ? args.push(await proxyCommand()) : null + + // proxy ? args.push(await proxyCommand()) : '' + + const browser = await puppeteer.launch({ + headless: false, + args + }) + + browsers.push(browser) + const close = browser.close + browser.close = async function () { + let i = browsers.find(b => b === browser) + await close.call(this) + if (i !== -1) browsers.slice(i, 1) + } + + const page = await browser.newPage() + console.log('fakeUa', fakeUa()); + + changeUS && page.setUserAgent(fakeUa()) + await page.setExtraHTTPHeaders({ + 'accept-language': 'en-US,en;q=0.9,hy;q=0.8' + }) + + if (request) pageRequest(page) + + return { browser, page } +} + +function getProxy () { + return axios({ + url: 'http://api.tianqiip.com/getip?secret=loy0r7fpmnlshm8l&num=1&type=json&port=3&time=5&mr=1&sign=5f73ab58ad7ab40346311014bef59b79', + method: 'get', + }).then(res => { + console.log('proxy', res.data.data[0]) + return res.data.data[0] + }).catch(err => { + console.log(err) + }) +} + + +/** + * 记录日志 + */ +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 statusCheck( + operationsFn: Function, + checkFn: Function, + options: {interval?: number, tryCount?: number} = {} +) { + options.interval = options.interval ?? 1000 + options.tryCount = options.tryCount ?? 1 + let { interval, tryCount } = options + const { p, resolve, reject } = createPromise() + await operationsFn() + const pass = await checkFn() + console.log('检查状态', !!pass) + if (pass) { + resolve(true) + } else { + if (tryCount <= 5) { + await sleep(interval) + options.tryCount++ + return await statusCheck(operationsFn, checkFn, options) + } else { + resolve(false) + } + } + + return p +} + +export function sleep (time = 1000) { + return new Promise((resolve) => { + setTimeout(resolve, time) + }) +} + +/** + * 将方法重试n次 + */ +export function retry(fn: Function, tryCount = 1) { + return async function (...args) { + try { + return await fn(...args) + } catch (error) { + if (tryCount <= 5) { + await sleep(1000) + tryCount++ + return await retry(fn, tryCount)(...args) + } else { + throw error + } + } + } +} diff --git a/package.json b/package.json index 1209e0c..40e397a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "electron-vue-vite", - "version": "2.0.0", + "name": "poe_gpt", + "version": "1.0.0", "main": "dist-electron/main/index.js", "description": "Really simple Electron + Vue + Vite boilerplate.", "author": "草鞋没号 <308487730@qq.com>", @@ -15,23 +15,45 @@ ], "debug": { "env": { - "VITE_DEV_SERVER_URL": "http://127.0.0.1:3344/" + "VITE_DEV_SERVER_URL": "http://127.0.0.1:3200/" } }, "scripts": { "dev": "vite", - "build": "vue-tsc --noEmit && vite build && electron-builder", + "build": "vite build && electron-builder", + "build:64": "vite build && electron-builder --win --x64", + "build:32": "vite build && electron-builder --win --x32", "preview": "vite preview" }, "devDependencies": { "@vitejs/plugin-vue": "^4.1.0", + "@vitejs/plugin-vue-jsx": "^3.0.1", "electron": "^25.0.1", "electron-builder": "^23.6.0", "typescript": "^5.0.2", + "unplugin-auto-import": "^0.16.4", + "unplugin-vue-components": "^0.25.0", "vite": "^4.1.4", "vite-plugin-electron": "^0.11.2", "vite-plugin-electron-renderer": "^0.14.1", "vue": "^3.2.47", "vue-tsc": "^1.2.0" + }, + "dependencies": { + "@vueuse/core": "^10.1.2", + "axios": "^1.4.0", + "chrome-launcher": "^0.15.2", + "fake-useragent": "^1.0.1", + "lodash": "^4.17.21", + "mockjs": "^1.1.0", + "naive-ui": "^2.34.4", + "proxy-chain": "^2.3.0", + "puppeteer": "^13.3.2", + "puppeteer-extra": "^3.3.6", + "puppeteer-extra-plugin-recaptcha": "^3.6.8", + "puppeteer-extra-plugin-stealth": "^2.11.2", + "sass": "^1.62.1", + "socks-proxy-agent": "^8.0.1", + "unocss": "^0.52.7" } } diff --git a/response.png b/response.png new file mode 100644 index 0000000..b7f193d Binary files /dev/null and b/response.png differ diff --git a/src/App.vue b/src/App.vue index 3210bf6..13f0d16 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,51 +1,390 @@ -