Skip to content

Commit

Permalink
feat: captcha support
Browse files Browse the repository at this point in the history
  • Loading branch information
daidr committed Nov 5, 2022
1 parent d28e74d commit 2a6d064
Show file tree
Hide file tree
Showing 10 changed files with 655 additions and 246 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@
"dev": "npm run clear && cross-env NODE_ENV=development run-p dev:*",
"dev:prepare": "esno scripts/prepare.ts",
"dev:web": "vite",
"dev:js": "npm run build:js -- --mode development",
"dev:bg": "tsup --watch ./src",
"build": "cross-env NODE_ENV=production run-s clear build:web build:prepare build:bg",
"build": "cross-env NODE_ENV=production run-s clear build:web build:prepare build:js build:bg",
"build:prepare": "esno scripts/prepare.ts",
"build:web": "vite build",
"build:js": "vite build --config vite.config.content.ts",
"build:bg": "tsup",
"pack": "cross-env NODE_ENV=production run-p pack:*",
"pack:zip": "rimraf extension.zip && jszip-cli add extension/* -o ./extension.zip",
Expand Down
5 changes: 5 additions & 0 deletions shim.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { ProtocolWithReturn } from 'webext-bridge'
import type { ICaptchaResponse } from '~/types'

declare module 'webext-bridge' {
export interface ProtocolMap {
Expand All @@ -11,5 +12,9 @@ declare module 'webext-bridge' {
'set_role_alert_status': { uid: string; status: boolean }
'delete_role_request': ProtocolWithReturn<{ uid: string }, boolean>
'request_cookie_read': ProtocolWithReturn<{ oversea: boolean }, number>
'create_verification': ProtocolWithReturn<{ uid: string }, ICaptchaResponse | false>
'get_selected_role': ProtocolWithReturn<{}, string>
'finish_captcha': ProtocolWithReturn<{ geetest: ICaptchaRequest; uid: string; tabId: number }, boolean>
'request_captcha': { verification: ICaptchaResponse; uid: string; tabId: number }
}
}
105 changes: 100 additions & 5 deletions src/background/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { onMessage } from 'webext-bridge'
import { action, alarms, cookies, i18n, notifications, runtime, storage } from 'webextension-polyfill'
import { onMessage, sendMessage } from 'webext-bridge'
import { action, alarms, cookies, i18n, notifications, runtime, storage, tabs } from 'webextension-polyfill'
import type { Cookies, Notifications } from 'webextension-polyfill'
import type { IAlertSetting, IAlertStatus, IRoleDataItem, IUserData, IUserDataItem } from '~/types'
import { getRoleDataByCookie, getRoleInfoByCookie } from '~/utils'
import { createVerification, getRoleDataByCookie, getRoleInfoByCookie, verifyVerification } from '~/utils'

// 一分钟
const INTERVAL_TIME = 1
const INTERVAL_TIME = 3

// 角色的默认提醒设定
const defaultAlertSetting: IAlertSetting = {
Expand Down Expand Up @@ -171,6 +171,10 @@ const targetPages = [
'https://api-takumi.mihoyo.com/binding/api/getUserGameRolesByCookie?game_biz=hk4e_cn&asource=paimon',
'https://bbs-api-os.hoyolab.com/game_record/app/genshin/api/dailyNote?asource=paimon*',
'https://api-takumi-record.mihoyo.com/game_record/app/genshin/api/dailyNote?asource=paimon*',
'https://api-takumi-record.mihoyo.com/game_record/app/card/wapi/createVerification?asource=paimon*',
'https://api-takumi-record.mihoyo.com/game_record/app/card/wapi/verifyVerification?asource=paimon*',
'https://bbs-api-os.hoyolab.com/game_record/app/card/wapi/createVerification?asource=paimon*',
'https://bbs-api-os.hoyolab.com/game_record/app/card/wapi/verifyVerification?asource=paimon*',
]

let currentCookie = ''
Expand Down Expand Up @@ -408,7 +412,19 @@ const refreshData = async function (uiOnly = false) {
for (const role of enabledRoleList) {
const data = uiOnly ? role.data : await getRoleDataByCookie(role.serverType === 'os', role.cookie, role.uid, role.serverRegion, setCookie)

if (data) {
if (Number.isInteger(data)) {
// error code
switch (data) {
case 1034:
// risk control
// 获取失败,写入错误信息
role.isError = true
role.errorMessage = '触发风控'
role.updateTimestamp = Date.now()
break
}
}
else if (data && typeof data === 'object') {
// 更新 roleList
const roleIndex = originRoleList.findIndex((item) => {
return item.uid === role.uid
Expand Down Expand Up @@ -581,3 +597,82 @@ onMessage<{ oversea: boolean }, 'request_cookie_read'>('request_cookie_read', as
return -1
}
})

onMessage<{ uid: string }, 'create_verification'>('create_verification', async ({ data: { uid } }) => {
const originRoleList = await readDataFromStorage<IUserDataItem[]>('roleList', [])
const index = originRoleList.findIndex((item) => {
return item.uid === uid
})
const cookie = originRoleList[index].cookie
const oversea = originRoleList[index].serverType === 'os'

const setCookie = async (cookie: string) => {
currentCookie = cookie
await updateRules()
}

return await createVerification(oversea, cookie, setCookie)
})

onMessage<{ uid: string }, 'request_captcha_bg'>('request_captcha_bg', async ({ data: { uid } }) => {
// open captcha tab
const curtab = await tabs.create({
url: 'https://paimon-webext.daidr.me/captcha.html',
})

// wait for curtab loaded
await new Promise((resolve, reject) => {
const check = async () => {
if (curtab.id === undefined) {
reject(new Error('tab id is undefined'))
return
}

const tab = await tabs.get(curtab.id)
if (tab.status === 'complete')
resolve(true)
else
setTimeout(check, 100)
}
check()
})

console.log('tab loaded')

// send message to captcha tab
const originRoleList = await readDataFromStorage<IUserDataItem[]>('roleList', [])
const index = originRoleList.findIndex((item) => {
return item.uid === uid
})
const cookie = originRoleList[index].cookie
const oversea = originRoleList[index].serverType === 'os'

const setCookie = async (cookie: string) => {
currentCookie = cookie
await updateRules()
}

const verification = await createVerification(oversea, cookie, setCookie)
if (verification && curtab.id)
await sendMessage('request_captcha', { verification, uid, tabId: curtab.id }, { tabId: curtab.id, context: 'content-script' })
})

onMessage('finish_captcha', async ({ data: { tabId, uid, geetest } }) => {
const originRoleList = await readDataFromStorage<IUserDataItem[]>('roleList', [])
const index = originRoleList.findIndex((item) => {
return item.uid === uid
})

const cookie = originRoleList[index].cookie
const oversea = originRoleList[index].serverType === 'os'
const setCookie = async (cookie: string) => {
currentCookie = cookie
await updateRules()
}
const result = await verifyVerification(oversea, cookie, geetest, setCookie)

tabs.remove(tabId)
getRoleInfoByCookie(oversea, cookie, setCookie)
// refreshData()
return result
})
50 changes: 50 additions & 0 deletions src/contentScripts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* eslint-disable */
import { onMessage, sendMessage } from 'webext-bridge'
import { ICaptchaRequest } from '~/types'

(() => {
const Config = {
uid: "",
tabId: -1,
}

console.info('[paimon-webext] init')

// eslint-disable-next-line
window.postMessage({
direction: 'from-content-script',
type: 'init',
}, '*')

onMessage('request_captcha', ({ data }) => {
console.info('[paimon-webext] request_captcha', data)
const { uid, tabId } = data
Config.uid = uid
Config.tabId = tabId
window.postMessage({
direction: 'from-content-script',
type: 'request_captcha',
payload: JSON.stringify(data),
}, '*')
})

window.addEventListener('message', (event) => {
if (event.source === window
&& event.data.direction
&& event.data.direction === 'from-page-script') {
console.log('Received message from page script: ', event.data)
if (event.data.type === 'finish_captcha') {
const data = JSON.parse(event.data.payload)
sendCaptchaResult(data)
}
}
})

function sendCaptchaResult(data: ICaptchaRequest) {
sendMessage('finish_captcha', {
geetest: data,
uid: Config.uid,
tabId: Config.tabId,
})
}
})()
7 changes: 7 additions & 0 deletions src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ export async function getManifest() {
background: {
service_worker: './dist/background/index.mjs',
},
content_scripts: [
{
matches: ['https://paimon-webext.daidr.me/captcha.html'],
js: ['./dist/contentScripts/index.global.js'],
},
],
options_ui: {
page: './dist/options/index.html',
open_in_tab: false,
Expand All @@ -38,6 +44,7 @@ export async function getManifest() {

if (isDev) {
// add dev-only features here
delete manifest.content_scripts
manifest.permissions?.push('webNavigation')
}

Expand Down
Loading

0 comments on commit 2a6d064

Please sign in to comment.