import type { Browser } from 'patchright' import type { EmailVerificationProvider } from '../../utils/temkakit/email-verification.ts' import { writeFile } from 'node:fs/promises' import { faker } from '@faker-js/faker' import { sleep } from '@fuman/utils' import { load } from 'cheerio' import { Cookie, CookieJar } from 'tough-cookie' import { ffetch as ffetchBase } from '../../utils/fetch.ts' import { AnymessageEmailVerificationProvider } from '../../utils/temkakit/anymessage.ts' import { solveKasadaSalamoonder } from '../../utils/temkakit/kasada-solver.ts' import { createLibcurlFetch } from '../../utils/temkakit/libcurl.ts' // half broken, unfinished function getProxy() { // return { // user: 'JaTjXK', // pass: 'WYsU4C', // host: '38.152.247.16', // port: 9785, // } return { user: '', pass: '', host: '127.0.0.1', port: 7891, } } function proxyToUrl(proxy: { user: string, pass: string, host: string, port: number }) { return `http://${proxy.user}:${proxy.pass}@${proxy.host}:${proxy.port}` } const THREADS = 1 const ACCOUNTS_COUNT = 2 const TWITCH_PJS = 'https://k.twitchcdn.net/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/p.js' async function twitchAutoreg(options: { // browser: Browser emailProvider: EmailVerificationProvider log?: (format: string, ...args: any[]) => void proxy?: string }) { const { // browser, proxy, emailProvider, log = (fmt, ...args) => console.log(fmt, ...args), } = options const jar = new CookieJar() log('proxy', proxy) const ffetch = ffetchBase.extend({ cookies: jar, fetch: createLibcurlFetch({ proxy }), }) log('fetching main page') const mainPage = await ffetch('https://www.twitch.tv/').text() const twilightBuildId = mainPage.match(/window.__twilightBuildID="([^"]+)"/)?.[1] if (!twilightBuildId) { throw new Error('failed to get twilightBuildId') } await jar.setCookie(new Cookie({ key: 'api_token', value: `twilight.${faker.string.hexadecimal({ length: 32 })}`, domain: 'twitch.tv', path: '/', secure: true, sameSite: 'None', hostOnly: false, expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365), }), 'https://www.twitch.tv') await jar.setCookie(new Cookie({ key: 'experiment_overrides', value: encodeURIComponent(JSON.stringify({ experiments: {}, disabled: [] })), domain: 'twitch.tv', path: '/', secure: true, sameSite: 'None', hostOnly: false, expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365), }), 'https://www.twitch.tv') const deviceId = faker.string.alphanumeric({ length: 32 }) const sessionId = faker.string.hexadecimal({ length: 16 }) log('generating integrity token') // const kasadaSolver = await createKasadaSolver({ // pageUrl: 'https://www.twitch.tv/', // scriptUrl: '', // browser, // beforePageLoad: async (page) => { // await syncCookiesIntoBrowser(jar, page.context()) // }, // requests: [ // { // protocol: 'https', // method: 'POST', // domain: 'gql.twitch.tv', // path: '/integrity', // }, // { // protocol: 'https', // method: 'POST', // domain: 'passport.twitch.tv', // path: '/integrity', // }, // { // protocol: 'https', // method: 'POST', // domain: 'passport.twitch.tv', // path: '/protected_register', // }, // { // protocol: 'https', // method: 'POST', // domain: 'passport.twitch.tv', // path: '/protected_login', // }, // ], // }) const commonHeaders: Record = { 'X-Device-Id': deviceId, 'Client-Id': 'kimne78kx3ncx6brgo4mv6wki5h1ko', 'Client-Request-Id': faker.string.alphanumeric({ length: 32 }), 'Client-Session-Id': sessionId, 'Client-Version': twilightBuildId, } const kasadaSolution = await solveKasadaSalamoonder({ pjs: TWITCH_PJS }) // const integrityToken = await kasadaSolver.request({ // url: 'https://gql.twitch.tv/integrity', // method: 'POST', // headers: commonHeaders, // }) as { token: string } const integrityToken = await ffetch('https://gql.twitch.tv/integrity', { method: 'POST', headers: { ...commonHeaders, ...kasadaSolution, }, }).json() as { token: string } const ffetchGql = ffetchBase.extend({ headers: { 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Site': 'same-site', ...commonHeaders, 'Client-Integrity': integrityToken.token, }, }) // await syncCookiesFromBrowser(kasadaSolver.page.context(), jar) let username while (true) { username = faker.internet.username().toLowerCase().replace(/[^a-z0-9]/gi, '') log('checking username', username) const r = await ffetchGql.post('https://gql.twitch.tv/gql', { json: [ { operationName: 'UsernameValidator_User', variables: { username }, extensions: { persistedQuery: { version: 1, sha256Hash: 'fd1085cf8350e309b725cf8ca91cd90cac03909a3edeeedbd0872ac912f3d660', }, }, }, ], }).json() as any if (r[0].errors) { throw new Error(`failed to check username:${JSON.stringify(r[0].errors)}`) } if (r[0].data.isUsernameAvailable) { log('username is available: %s', username) break } await sleep(1000) } log('ordering email') const email = await emailProvider.getEmail() log('got email: %s, registering', email) const password = faker.internet.password({ length: 16, pattern: /[a-z0-9]/ }) const birthday = faker.date.birthdate({ min: 18, max: 25, mode: 'age' }) const registerBody: Record = { username, password, email, birthday: { day: birthday.getDate(), month: birthday.getMonth() + 1, year: birthday.getFullYear(), isOver18: true, }, email_marketing_opt_in: false, client_id: 'kimne78kx3ncx6brgo4mv6wki5h1ko', is_password_guide: 'nist', } for (let i = 0; i < 5; i++) { // const r1 = await kasadaSolver.request({ // url: 'https://passport.twitch.tv/protected_register', // method: 'POST', // body: JSON.stringify(registerBody), // headers: { // 'Content-Type': 'text/plain;charset=UTF-8', // 'Accept': '*/*', // }, // credentials: 'include', // }) as { error_code: number } log('solving kasada') const kasadaSolution = await solveKasadaSalamoonder({ pjs: TWITCH_PJS }) const r1 = await ffetch.post('https://passport.twitch.tv/protected_register', { validateResponse: false, json: registerBody, headers: { ...kasadaSolution, }, }).json() as { error_code: number } log('r1', r1) if (i < 4 && r1.error_code === 5025) { log('integrity failed, retrying...') continue } if (r1.error_code !== 2026) { await emailProvider.dispose() throw new Error(`failed to register: ${JSON.stringify(r1)}`) } break } log('waiting for code') const message = await emailProvider.waitForMessage({ timeout: 90_000 }) const message$ = load(message) const code = message$('center p[style^=background]').text() // what the fuck is this selector // const code = await question('code: ') if (!code.match(/^\d{6}$/)) { log('❌ invalid code parsed: %s', code) log(message) await emailProvider.dispose() throw new Error(`invalid code parsed:${code}`) } log('code: %s', code) registerBody.email_verification_code = code for (let i = 0; i < 5; i++) { // const r2 = await kasadaSolver.request({ // url: 'https://passport.twitch.tv/protected_register', // method: 'POST', // body: JSON.stringify(registerBody), // headers: { // 'Content-Type': 'text/plain;charset=UTF-8', // }, // credentials: 'include', // }) as { error_code: number } log('solving kasada') const kasadaSolution = await solveKasadaSalamoonder({ pjs: TWITCH_PJS }) const r2 = await ffetch.post('https://passport.twitch.tv/protected_register', { json: registerBody, validateResponse: false, headers: { ...kasadaSolution, }, }).json() as { error_code: number } if (i < 4 && r2.error_code === 5025) { log('integrity failed, retrying...') continue } if (r2.error_code) { await emailProvider.dispose() throw new Error(`❌ failed to register:${r2.error_code}`) } break } // await syncCookiesFromBrowser(kasadaSolver.page.context(), jar) log('авторег работает!') await emailProvider.dispose() return { username, password, email, cookies: await jar.store.getAllCookies(), } } let started = 0 let completed = 0 await Promise.all(Array.from({ length: THREADS }).map(async (_, idx) => { const emailProvider = new AnymessageEmailVerificationProvider({ site: 'twitch.tv', domain: 'hotmail.com', }) let browser: Browser | null = null while (true) { if (started >= ACCOUNTS_COUNT) { break } started++ const log = (fmt: string, ...args: any[]) => console.log(`[worker ${idx}] ${fmt}`, ...args) try { const proxy = getProxy() // browser = await chromium.launch({ // channel: 'chrome', // headless: false, // env: { // TZ: 'Europe/Amsterdam', // }, // proxy: { // server: `http://${proxy.host}:${proxy.port}`, // username: proxy.user, // password: proxy.pass, // }, // }) const acct = await twitchAutoreg({ // browser, proxy: proxyToUrl(proxy), emailProvider, log, }) await writeFile('assets/twitch-accs.txt', `${JSON.stringify(acct)}\n`, { flag: 'a' }) completed++ log('completed: %d/%d', completed, ACCOUNTS_COUNT) } catch (e) { log('autoreg error: %s', e) // await browser?.close() browser = null started-- } } }))