mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2026-04-22 21:59:49 +08:00
♻️ 请求重构
This commit is contained in:
@@ -122,7 +122,6 @@ import showGeetest from "@comp/func/geetest.js";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import ToGameLogin from "@comp/pageConfig/tco-gameLogin.vue";
|
||||
import Mys from "@Mys/index.js";
|
||||
import TSUserAccount from "@Sqlite/modules/userAccount.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, ref, shallowRef } from "vue";
|
||||
@@ -132,6 +131,7 @@ import { useUserStore } from "@/store/modules/user.js";
|
||||
import TGLogger from "@/utils/TGLogger.js";
|
||||
import BBSApi from "@/web/request/bbsReq.js";
|
||||
import PassportApi from "@/web/request/passportReq.js";
|
||||
import passportReq from "@/web/request/passportReq.js";
|
||||
import TakumiApi from "@/web/request/takumiReq.js";
|
||||
|
||||
const { isLogin } = storeToRefs(useAppStore());
|
||||
@@ -398,13 +398,13 @@ async function confirmCopyCookie(): Promise<void> {
|
||||
}
|
||||
|
||||
async function tryGetCaptcha(phone: string, aigis?: string): Promise<string | false> {
|
||||
const captchaResp = await Mys.User.getCaptcha(phone, aigis);
|
||||
const captchaResp = await passportReq.captcha.create(phone, aigis);
|
||||
if ("retcode" in captchaResp) {
|
||||
if (!captchaResp.data || captchaResp.data === "") {
|
||||
showSnackbar.error(`[${captchaResp.retcode}] ${captchaResp.message}`);
|
||||
return false;
|
||||
}
|
||||
const aigisResp: TGApp.Plugins.Mys.CaptchaLogin.CaptchaAigis = JSON.parse(captchaResp.data);
|
||||
const aigisResp: TGApp.BBS.CaptchaLogin.CaptchaAigis = JSON.parse(captchaResp.data);
|
||||
const resp = await showGeetest(JSON.parse(aigisResp.data));
|
||||
const aigisStr = `${aigisResp.session_id};${btoa(JSON.stringify(resp))}`;
|
||||
return await tryGetCaptcha(phone, aigisStr);
|
||||
@@ -417,14 +417,14 @@ async function tryLoginByCaptcha(
|
||||
captcha: string,
|
||||
actionType: string,
|
||||
aigis?: string,
|
||||
): Promise<TGApp.Plugins.Mys.CaptchaLogin.LoginData | false> {
|
||||
const loginResp = await Mys.User.login(phone, captcha, actionType, aigis);
|
||||
): Promise<TGApp.BBS.CaptchaLogin.LoginRes | false> {
|
||||
const loginResp = await passportReq.captcha.login(phone, captcha, actionType, aigis);
|
||||
if ("retcode" in loginResp) {
|
||||
if (!loginResp.data || loginResp.data === "") {
|
||||
showSnackbar.error(`[${loginResp.retcode}] ${loginResp.message}`);
|
||||
return false;
|
||||
}
|
||||
const aigisResp: TGApp.Plugins.Mys.CaptchaLogin.CaptchaAigis = JSON.parse(loginResp.data);
|
||||
const aigisResp: TGApp.BBS.CaptchaLogin.CaptchaAigis = JSON.parse(loginResp.data);
|
||||
const resp = await showGeetest(JSON.parse(aigisResp.data));
|
||||
const aigisStr = `${aigisResp.session_id};${btoa(JSON.stringify(resp))}`;
|
||||
return await tryLoginByCaptcha(phone, captcha, actionType, aigisStr);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<span>{{ showNew ? "查看当前祈愿" : "查看后续祈愿" }}</span>
|
||||
</template>
|
||||
<template #default>
|
||||
<!-- todo 组件化 -->
|
||||
<div class="pool-grid">
|
||||
<div v-for="pool in poolSelect" :key="pool.postId" class="pool-card">
|
||||
<div class="pool-cover" @click="createPost(pool.postId, pool.title)">
|
||||
@@ -58,20 +59,41 @@
|
||||
import TItembox, { type TItemBoxData } from "@comp/app/t-itemBox.vue";
|
||||
import TMiImg from "@comp/app/t-mi-img.vue";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import Mys from "@Mys/index.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, onMounted, onUnmounted, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
import THomeCard from "./ph-comp-card.vue";
|
||||
|
||||
import { AppCharacterData } from "@/data/index.js";
|
||||
import { useHomeStore } from "@/store/modules/home.js";
|
||||
import { createPost, createTGWindow } from "@/utils/TGWindow.js";
|
||||
import { stamp2LastTime } from "@/utils/toolFunc.js";
|
||||
import postReq from "@/web/request/postReq.js";
|
||||
import takumiReq from "@/web/request/takumiReq.js";
|
||||
|
||||
type TPoolEmits = (e: "success") => void;
|
||||
type PoolStat = "future" | "now" | "past"; // 未开始 | 进行中 | 已结束
|
||||
type PoolItem = TGApp.Plugins.Mys.Gacha.RenderCard & { timeRest: number; stat: PoolStat };
|
||||
type AvatarRender = {
|
||||
icon: string;
|
||||
url: string;
|
||||
info?: TGApp.App.Character.WikiBriefInfo;
|
||||
};
|
||||
type PoolCard = {
|
||||
id: number;
|
||||
title: string;
|
||||
cover: string;
|
||||
postId: number;
|
||||
characters: Array<AvatarRender>;
|
||||
time: {
|
||||
str: string;
|
||||
startStamp: number;
|
||||
endStamp: number;
|
||||
totalStamp: number;
|
||||
};
|
||||
timeRest: number;
|
||||
stat: PoolStat;
|
||||
};
|
||||
|
||||
const emits = defineEmits<TPoolEmits>();
|
||||
const { poolCover } = storeToRefs(useHomeStore());
|
||||
@@ -80,43 +102,91 @@ const router = useRouter();
|
||||
let timer: NodeJS.Timeout | null = null;
|
||||
|
||||
const showNew = ref<boolean>(false);
|
||||
const poolCards = ref<Array<PoolItem>>([]);
|
||||
const poolCards = ref<Array<PoolCard>>([]);
|
||||
const hasNew = computed<boolean>(
|
||||
() => poolCards.value.find((pool) => pool.stat === "future") !== undefined,
|
||||
);
|
||||
const poolSelect = computed<Array<PoolItem>>(() => {
|
||||
const poolSelect = computed<Array<PoolCard>>(() => {
|
||||
if (!hasNew.value) return poolCards.value;
|
||||
if (showNew.value) return poolCards.value.filter((pool) => pool.stat === "future");
|
||||
return poolCards.value.filter((pool) => pool.stat !== "future");
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
const gachaData = await Mys.Gacha.get();
|
||||
let cards: Array<TGApp.Plugins.Mys.Gacha.RenderCard>;
|
||||
if (!checkCover(gachaData)) {
|
||||
cards = await Mys.Gacha.card(gachaData);
|
||||
const coverData: Record<string, string> = {};
|
||||
poolCards.value.map((pool) => {
|
||||
coverData[pool.id] = pool.cover;
|
||||
return pool;
|
||||
});
|
||||
poolCover.value = coverData;
|
||||
const gachaData = await takumiReq.obc.gacha();
|
||||
if (checkCover(gachaData)) {
|
||||
poolCards.value = await getGachaCard(gachaData, poolCover.value);
|
||||
} else {
|
||||
cards = await Mys.Gacha.card(gachaData, poolCover.value);
|
||||
}
|
||||
for (const pool of cards) {
|
||||
const timeRest = pool.time.endStamp - Date.now();
|
||||
poolCards.value.push({
|
||||
...pool,
|
||||
stat: timeRest > pool.time.totalStamp ? "future" : timeRest > 0 ? "now" : "past",
|
||||
timeRest: timeRest,
|
||||
});
|
||||
poolCards.value = await getGachaCard(gachaData);
|
||||
const coverData: Record<string, string> = {};
|
||||
for (const pool of poolCards.value) coverData[pool.id] = pool.cover;
|
||||
poolCover.value = coverData;
|
||||
}
|
||||
if (timer !== null) clearInterval(timer);
|
||||
timer = setInterval(poolTimeout, 1000);
|
||||
emits("success");
|
||||
});
|
||||
|
||||
async function getGachaItemCard(
|
||||
data: TGApp.BBS.Obc.GachaItem,
|
||||
poolCover?: string,
|
||||
): Promise<PoolCard | null> {
|
||||
let cover = "/source/UI/empty.webp";
|
||||
const postId: number | undefined = Number(data.activity_url.split("/").pop()) || undefined;
|
||||
if (postId === undefined || isNaN(postId)) return null;
|
||||
if (poolCover !== undefined) {
|
||||
cover = poolCover;
|
||||
} else {
|
||||
const postResp = await postReq.post(postId);
|
||||
if ("retcode" in postResp) {
|
||||
showSnackbar.error(`[${postResp.retcode}] ${postResp.message}`);
|
||||
return null;
|
||||
}
|
||||
cover = postResp.cover?.url ?? postResp.post.images[0];
|
||||
}
|
||||
const timeStr = `${data.start_time} ~ ${data.end_time}`;
|
||||
const characters: Array<AvatarRender> = [];
|
||||
for (const character of data.pool) {
|
||||
const item: AvatarRender = { icon: character.icon, url: character.url };
|
||||
const contentId = character.url.match(/(?<=content\/)\d+/)?.[0];
|
||||
if (contentId) {
|
||||
const itemF = AppCharacterData.find((item) => item.contentId.toString() === contentId);
|
||||
if (itemF) item.info = itemF;
|
||||
}
|
||||
characters.push(item);
|
||||
}
|
||||
const endTs = new Date(data.end_time).getTime();
|
||||
const totalTs = endTs - new Date(data.start_time).getTime();
|
||||
const restTs = endTs - Date.now();
|
||||
return {
|
||||
id: data.id,
|
||||
title: data.title,
|
||||
cover,
|
||||
postId,
|
||||
characters,
|
||||
time: {
|
||||
str: timeStr,
|
||||
startStamp: new Date(data.start_time).getTime(),
|
||||
endStamp: endTs,
|
||||
totalStamp: totalTs,
|
||||
},
|
||||
timeRest: restTs,
|
||||
stat: restTs > totalTs ? "future" : restTs > 0 ? "now" : "past",
|
||||
};
|
||||
}
|
||||
|
||||
async function getGachaCard(
|
||||
gachaData: Array<TGApp.BBS.Obc.GachaItem>,
|
||||
poolCover?: Record<number, string>,
|
||||
): Promise<Array<PoolCard>> {
|
||||
const gachaCard: Array<PoolCard> = [];
|
||||
for (const data of gachaData) {
|
||||
const item = await getGachaItemCard(data, poolCover?.[Number(data.id)]);
|
||||
if (item !== null) gachaCard.push(item);
|
||||
}
|
||||
return gachaCard;
|
||||
}
|
||||
|
||||
function poolTimeout(): void {
|
||||
for (const pool of poolCards.value) {
|
||||
if (pool.stat === "past") {
|
||||
@@ -139,7 +209,7 @@ function poolTimeout(): void {
|
||||
}
|
||||
}
|
||||
|
||||
function checkCover(data: Array<TGApp.Plugins.Mys.Gacha.Data>): boolean {
|
||||
function checkCover(data: Array<TGApp.BBS.Obc.GachaItem>): boolean {
|
||||
if (poolCover.value === undefined || Object.keys(poolCover.value).length === 0) return false;
|
||||
let checkList = data.length;
|
||||
Object.entries(poolCover.value).forEach(([key, value]: [string, unknown]) => {
|
||||
@@ -149,10 +219,7 @@ function checkCover(data: Array<TGApp.Plugins.Mys.Gacha.Data>): boolean {
|
||||
return checkList === 0;
|
||||
}
|
||||
|
||||
async function toOuter(
|
||||
character: TGApp.Plugins.Mys.Gacha.RenderItem,
|
||||
title: string,
|
||||
): Promise<void> {
|
||||
async function toOuter(character: AvatarRender, title: string): Promise<void> {
|
||||
if (character.info !== undefined) {
|
||||
await router.push({ name: "角色图鉴", params: { id: character.info.id } });
|
||||
return;
|
||||
|
||||
@@ -10,47 +10,64 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import PhPositionCard from "@comp/pageHome/ph-position-card.vue";
|
||||
import Mys from "@Mys/index.js";
|
||||
import { onMounted, onUnmounted, ref } from "vue";
|
||||
|
||||
import THomeCard from "./ph-comp-card.vue";
|
||||
|
||||
import takumiReq from "@/web/request/takumiReq.js";
|
||||
|
||||
type TPositionEmits = (e: "success") => void;
|
||||
type PositionStat = "past" | "now" | "future" | "unknown"; // 已结束 | 进行中 | 未开始 | 未知
|
||||
export type PositionItem = TGApp.Plugins.Mys.Position.RenderCard & {
|
||||
export type PositionCard = {
|
||||
title: string;
|
||||
postId: number;
|
||||
link: string;
|
||||
icon: string;
|
||||
abstract: string;
|
||||
time: { startStamp: number; endStamp: number; totalStamp: number };
|
||||
timeRest: number;
|
||||
stat: PositionStat;
|
||||
};
|
||||
const emits = defineEmits<TPositionEmits>();
|
||||
// eslint-disable-next-line no-undef
|
||||
let timer: NodeJS.Timeout | null = null;
|
||||
const positionCards = ref<Array<PositionItem>>([]);
|
||||
const positionCards = ref<Array<PositionCard>>([]);
|
||||
|
||||
onMounted(async () => {
|
||||
const positionData = await Mys.Position.get();
|
||||
console.log(positionData);
|
||||
const cards = Mys.Position.card(positionData);
|
||||
for (const position of cards) {
|
||||
if (position.time.endStamp === 0 || position.time.totalStamp < 0) {
|
||||
positionCards.value.push({
|
||||
...position,
|
||||
timeRest: 0,
|
||||
stat: "unknown",
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const timeRest = position.time.endStamp - Date.now();
|
||||
positionCards.value.push({
|
||||
...position,
|
||||
timeRest,
|
||||
stat: timeRest > position.time.totalStamp ? "future" : timeRest > 0 ? "now" : "past",
|
||||
});
|
||||
}
|
||||
const resp = await takumiReq.obc.position();
|
||||
console.log("positionCards", resp);
|
||||
positionCards.value = getPositionCard(resp);
|
||||
if (timer !== null) clearInterval(timer);
|
||||
timer = setInterval(getPositionTimer, 1000);
|
||||
emits("success");
|
||||
});
|
||||
|
||||
function getPositionCard(list: Array<TGApp.BBS.Obc.PositionItem>): Array<PositionCard> {
|
||||
const res: Array<PositionCard> = [];
|
||||
for (const position of list) {
|
||||
let link = position.url;
|
||||
if (position.url === "" && position.content_id !== 0) {
|
||||
link = `https://bbs.mihoyo.com/ys/obc/content/${position.content_id}/detail?bbs_presentation_style=no_header`;
|
||||
}
|
||||
const startTs = new Date(position.create_time).getTime();
|
||||
const endTs = Number(position.end_time);
|
||||
const totalTs = endTs - startTs;
|
||||
const restTs = endTs === 0 || totalTs < 0 ? 0 : endTs - Date.now();
|
||||
const card: PositionCard = {
|
||||
title: position.title,
|
||||
postId: position.url !== "" ? Number(position.url.split("/").pop()) : position.content_id,
|
||||
link: link,
|
||||
icon: position.icon,
|
||||
abstract: position.abstract,
|
||||
time: { startStamp: startTs, endStamp: endTs, totalStamp: totalTs },
|
||||
timeRest: restTs,
|
||||
stat: restTs === 0 ? "unknown" : restTs > totalTs ? "future" : restTs > 0 ? "now" : "past",
|
||||
};
|
||||
res.push(card);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function getPositionTimer(): void {
|
||||
for (const position of positionCards.value) {
|
||||
if (position.stat === "unknown") continue;
|
||||
|
||||
@@ -40,16 +40,16 @@
|
||||
<script lang="ts" setup>
|
||||
import TMiImg from "@comp/app/t-mi-img.vue";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import type { PositionItem } from "@comp/pageHome/ph-comp-position.vue";
|
||||
import type { PositionCard } from "@comp/pageHome/ph-comp-position.vue";
|
||||
|
||||
import { parseLink } from "@/utils/linkParser.js";
|
||||
import { createPost } from "@/utils/TGWindow.js";
|
||||
import { stamp2LastTime, timestampToDate } from "@/utils/toolFunc.js";
|
||||
|
||||
type PhPositionCardProps = { position: PositionItem };
|
||||
type PhPositionCardProps = { position: PositionCard };
|
||||
const props = defineProps<PhPositionCardProps>();
|
||||
|
||||
async function openPosition(card: TGApp.Plugins.Mys.Position.RenderCard): Promise<void> {
|
||||
async function openPosition(card: PositionCard): Promise<void> {
|
||||
const res = await parseLink(card.link);
|
||||
if (res === "post") {
|
||||
await createPost(card.postId, card.title);
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import { getEmojis } from "@Mys/request/getEmojis.js";
|
||||
import { onMounted, ref, shallowRef, StyleValue, toRaw } from "vue";
|
||||
|
||||
import { parseLink, parsePost } from "@/utils/linkParser.js";
|
||||
import { isColorSimilar, decodeRegExp } from "@/utils/toolFunc.js";
|
||||
import bbsReq from "@/web/request/bbsReq.js";
|
||||
|
||||
export type TpText = {
|
||||
insert: string;
|
||||
@@ -114,7 +114,7 @@ async function toLink(): Promise<void> {
|
||||
function getEmojiUrl(): string {
|
||||
if (localEmojis.value === null || !JSON.parse(localEmojis.value)[getEmojiName()]) {
|
||||
console.warn("tpEmoji unknown", getEmojiName());
|
||||
getEmojis().then((res) => {
|
||||
bbsReq.emojis().then((res) => {
|
||||
if ("retcode" in res) {
|
||||
console.error(res);
|
||||
showSnackbar.error("获取表情包失败!");
|
||||
|
||||
@@ -19,36 +19,8 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import showGeetest from "@comp/func/geetest.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
|
||||
import { useUserStore } from "@/store/modules/user.js";
|
||||
import miscReq from "@/web/request/miscReq.js";
|
||||
|
||||
const { cookie } = storeToRefs(useUserStore());
|
||||
|
||||
async function test(): Promise<void> {
|
||||
if (!cookie.value) return;
|
||||
const ck: Record<string, string> = {
|
||||
stoken: cookie.value.stoken,
|
||||
stuid: cookie.value.stuid,
|
||||
mid: cookie.value.mid,
|
||||
};
|
||||
const resp = await miscReq.create(ck);
|
||||
if ("retcode" in resp) {
|
||||
showSnackbar.error(`[${resp.retcode}] ${resp.message}`);
|
||||
return;
|
||||
}
|
||||
const gtRes = await showGeetest(resp);
|
||||
console.log(gtRes);
|
||||
if (gtRes === false) return;
|
||||
const verifyResp = await miscReq.verify(gtRes, ck);
|
||||
if ("retcode" in verifyResp) {
|
||||
showSnackbar.error(`[${verifyResp.retcode}] ${verifyResp.message}`);
|
||||
return;
|
||||
}
|
||||
showSnackbar.success("验证成功");
|
||||
console.log("test");
|
||||
}
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
/**
|
||||
* @file plugins/Mys/index.ts
|
||||
* @description Mys plugin index
|
||||
* @since Beta v0.7.1
|
||||
*/
|
||||
|
||||
import { doCaptchaLogin, getCaptcha } from "./request/doCaptchaLogin.js";
|
||||
import { getGachaData, getPositionData } from "./request/obcReq.js";
|
||||
import { getGachaCard } from "./utils/getGachaCard.js";
|
||||
import getPositionCard from "./utils/getPositionCard.js";
|
||||
|
||||
const Mys = {
|
||||
Gacha: { get: getGachaData, card: getGachaCard },
|
||||
Position: { get: getPositionData, card: getPositionCard },
|
||||
User: { getCaptcha, login: doCaptchaLogin },
|
||||
};
|
||||
|
||||
export default Mys;
|
||||
@@ -1,137 +0,0 @@
|
||||
/**
|
||||
* @file plugins/Mys/request/doCaptchaLogin.ts
|
||||
* @description 通过短信验证码登录账号获取 stoken
|
||||
* @since Beta v0.7.1
|
||||
*/
|
||||
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import { JSEncrypt } from "jsencrypt";
|
||||
|
||||
import TGBbs from "@/utils/TGBbs.js";
|
||||
import TGHttp from "@/utils/TGHttp.js";
|
||||
import { getDeviceInfo } from "@/utils/toolFunc.js";
|
||||
|
||||
const PUB_KEY_STR: Readonly<string> = `-----BEGIN PUBLIC KEY-----
|
||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDvekdPMHN3AYhm/vktJT+YJr7cI5DcsNKqdsx5DZX0gDuWFuIjzdwButrIYPNmRJ1G8ybDIF7oDW2eEpm5sMbL9zs
|
||||
9ExXCdvqrn51qELbqj0XxtMTIpaCHFSI50PfPpTFV9Xt/hmyVwokoOXFlAEgCn+Q
|
||||
CgGs52bFoYMtyi+xEQIDAQAB
|
||||
-----END PUBLIC KEY-----`;
|
||||
const encrypt = new JSEncrypt();
|
||||
encrypt.setPublicKey(PUB_KEY_STR);
|
||||
|
||||
/**
|
||||
* @description rsa 加密
|
||||
* @since Beta v0.5.1
|
||||
* @param {string} data - 待加密数据
|
||||
* @returns {string} 加密后数据
|
||||
*/
|
||||
function rsaEncrypt(data: string): string {
|
||||
const res = encrypt.encrypt(data.toString());
|
||||
if (res === false) {
|
||||
showSnackbar.error("RSA 加密失败");
|
||||
return "";
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取短信验证码
|
||||
* @since Beta v0.7.1
|
||||
* @param {string} phone - 手机号
|
||||
* @param {string} [aigis] - 验证数据
|
||||
* @returns {Promise<TGApp.Plugins.Mys.CaptchaLogin.CaptchaData | TGApp.BBS.Response.Base>}
|
||||
*/
|
||||
export async function getCaptcha(
|
||||
phone: string,
|
||||
aigis?: string,
|
||||
): Promise<TGApp.Plugins.Mys.CaptchaLogin.CaptchaData | TGApp.BBS.Response.BaseWithData<string>> {
|
||||
const url = "https://passport-api.mihoyo.com/account/ma-cn-verifier/verifier/createLoginCaptcha";
|
||||
const device_fp = getDeviceInfo("device_fp");
|
||||
const device_name = getDeviceInfo("device_name");
|
||||
const device_id = getDeviceInfo("device_id");
|
||||
const device_model = getDeviceInfo("product");
|
||||
const body = { area_code: rsaEncrypt("+86"), mobile: rsaEncrypt(phone) };
|
||||
const header: Record<string, string> = {
|
||||
"x-rpc-aigis": aigis || "",
|
||||
"x-rpc-app_version": TGBbs.version,
|
||||
"x-rpc-client_type": "2",
|
||||
"x-rpc-app_id": "bll8iq97cem8",
|
||||
"x-rpc-device_fp": device_fp,
|
||||
"x-rpc-device_name": device_name,
|
||||
"x-rpc-device_id": device_id,
|
||||
"x-rpc-device_model": device_model,
|
||||
"user-agent": TGBbs.ua,
|
||||
"content-type": "application/json",
|
||||
referer: "https://user.miyoushe.com/",
|
||||
"x-rpc-game_biz": "hk4e_cn",
|
||||
};
|
||||
const resp = await TGHttp<
|
||||
TGApp.Plugins.Mys.CaptchaLogin.CaptchaResponse | TGApp.BBS.Response.Base
|
||||
>(
|
||||
url,
|
||||
{
|
||||
method: "POST",
|
||||
headers: header,
|
||||
body: JSON.stringify(body),
|
||||
},
|
||||
true,
|
||||
);
|
||||
const data = await resp.data;
|
||||
if (data.retcode !== 0) {
|
||||
return <TGApp.BBS.Response.BaseWithData<string>>{
|
||||
retcode: data.retcode,
|
||||
message: data.message,
|
||||
data: resp.resp.headers.get("x-rpc-aigis"),
|
||||
};
|
||||
}
|
||||
return <TGApp.Plugins.Mys.CaptchaLogin.CaptchaData>data.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 通过短信验证码登录
|
||||
* @since Beta v0.5.1
|
||||
* @param {string} phone - 手机号
|
||||
* @param {string} captcha - 验证码
|
||||
* @param {string} action_type - 操作类型
|
||||
* @param {string} [aigis] - 验证数据
|
||||
* @returns {Promise<TGApp.Plugins.Mys.CaptchaLogin.LoginData | TGApp.BBS.Response.Base>}
|
||||
*/
|
||||
export async function doCaptchaLogin(
|
||||
phone: string,
|
||||
captcha: string,
|
||||
action_type: string,
|
||||
aigis?: string,
|
||||
): Promise<TGApp.Plugins.Mys.CaptchaLogin.LoginData | TGApp.BBS.Response.Base> {
|
||||
const url = "https://passport-api.mihoyo.com/account/ma-cn-passport/app/loginByMobileCaptcha";
|
||||
const device_fp = getDeviceInfo("device_fp");
|
||||
const device_name = getDeviceInfo("device_name");
|
||||
const device_id = getDeviceInfo("device_id");
|
||||
const device_model = getDeviceInfo("product");
|
||||
const body = {
|
||||
area_code: rsaEncrypt("+86"),
|
||||
mobile: rsaEncrypt(phone),
|
||||
action_type,
|
||||
captcha,
|
||||
};
|
||||
const header = {
|
||||
"x-rpc-aigis": aigis || "",
|
||||
"x-rpc-app_version": TGBbs.version,
|
||||
"x-rpc-client_type": "2",
|
||||
"x-rpc-app_id": "bll8iq97cem8",
|
||||
"x-rpc-device_fp": device_fp,
|
||||
"x-rpc-device_name": device_name,
|
||||
"x-rpc-device_id": device_id,
|
||||
"x-rpc-device_model": device_model,
|
||||
"user-agent": TGBbs.ua,
|
||||
};
|
||||
const resp = await TGHttp<TGApp.Plugins.Mys.CaptchaLogin.LoginResponse | TGApp.BBS.Response.Base>(
|
||||
url,
|
||||
{
|
||||
method: "POST",
|
||||
headers: header,
|
||||
body: JSON.stringify(body),
|
||||
},
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* @file plugins/Mys/request/getEmojis.ts
|
||||
* @description Mys 表情包请求函数集合
|
||||
* @since Beta v0.5.0
|
||||
*/
|
||||
|
||||
import TGHttp from "@/utils/TGHttp.js";
|
||||
|
||||
/**
|
||||
* @description 获取表情包列表
|
||||
* @since Beta v0.5.0
|
||||
* @return {Promise<Record<string,string>|TGApp.BBS.Response.Base>}
|
||||
*/
|
||||
export async function getEmojis(): Promise<Record<string, string> | TGApp.BBS.Response.Base> {
|
||||
const url = "https://bbs-api-static.miyoushe.com/misc/api/emoticon_set";
|
||||
const resp = await TGHttp<TGApp.Plugins.Mys.Emoji.Response | TGApp.BBS.Response.Base>(url, {
|
||||
method: "GET",
|
||||
});
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
const emojis: Record<string, string> = {};
|
||||
for (const series of resp.data.list) {
|
||||
for (const emoji of series.list) {
|
||||
emojis[emoji.name] = emoji.icon;
|
||||
}
|
||||
}
|
||||
return emojis;
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/**
|
||||
* @file plugins/Mys/request/obcReq.ts
|
||||
* @description 观测枢相关请求
|
||||
* @since Beta v0.6.3
|
||||
*/
|
||||
|
||||
import TGHttp from "@/utils/TGHttp.js";
|
||||
|
||||
const obcApi: Readonly<string> = "https://api-takumi.mihoyo.com/common/blackboard/ys_obc/v1/";
|
||||
|
||||
/**
|
||||
* @description 获取卡池信息
|
||||
* @since Beta v0.6.3
|
||||
* @return {Promise<TGApp.Plugins.Mys.Gacha.Data[]>}
|
||||
*/
|
||||
export async function getGachaData(): Promise<TGApp.Plugins.Mys.Gacha.Data[]> {
|
||||
const resp = await TGHttp<TGApp.Plugins.Mys.Gacha.Response>(`${obcApi}gacha_pool`, {
|
||||
method: "GET",
|
||||
query: { app_sn: "ys_obc" },
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
return resp.data.list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取热点追踪信息
|
||||
* @since Beta v0.6.3
|
||||
* @return {Promise<TGApp.Plugins.Mys.Position.Data[]>}
|
||||
*/
|
||||
export async function getPositionData(): Promise<TGApp.Plugins.Mys.Position.Data[]> {
|
||||
const resp = await TGHttp<TGApp.Plugins.Mys.Position.Response>(`${obcApi}home/position`, {
|
||||
method: "GET",
|
||||
query: { app_sn: "ys_obc" },
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
const data = resp.data.list;
|
||||
return DfsObc(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 深度优先遍历
|
||||
* @since Alpha v0.2.1
|
||||
* @param {TGApp.Plugins.Mys.Position.ObcItem[]} list 列表
|
||||
* @returns {TGApp.Plugins.Mys.Position.Data[]} 返回列表
|
||||
*/
|
||||
function DfsObc(list: TGApp.Plugins.Mys.Position.ObcItem[]): TGApp.Plugins.Mys.Position.Data[] {
|
||||
const res: TGApp.Plugins.Mys.Position.Data[] = [];
|
||||
for (const item of list) {
|
||||
if (item.name === "近期活动") {
|
||||
res.push(...item.list);
|
||||
}
|
||||
if (item.children) {
|
||||
res.push(...DfsObc(<TGApp.Plugins.Mys.Position.ObcItem[]>item.children));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
100
src/plugins/Mys/types/Gacha.d.ts
vendored
100
src/plugins/Mys/types/Gacha.d.ts
vendored
@@ -1,100 +0,0 @@
|
||||
/**
|
||||
* @file plugins/Mys/types/Gacha.d.ts
|
||||
* @description Mys 插件卡池类型定义文件
|
||||
* @since Beta v0.6.6
|
||||
*/
|
||||
|
||||
declare namespace TGApp.Plugins.Mys.Gacha {
|
||||
/**
|
||||
* @description 获取卡池信息返回
|
||||
* @since Alpha v0.2.1
|
||||
* @interface Response
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {Data[]} data.list 卡池数据
|
||||
* @return Response
|
||||
*/
|
||||
interface Response extends TGApp.BBS.Response.BaseWithData {
|
||||
data: {
|
||||
list: Data[];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 卡池信息
|
||||
* @since Beta v0.4.4
|
||||
* @interface Data
|
||||
* @property {string} id 卡池ID
|
||||
* @property {string} title 卡池标题
|
||||
* @property {string} activity_url 卡池对应帖子
|
||||
* @property {string} content_before_act 卡池内容
|
||||
* @property {Array<{icon: string; url: string}>} pool 卡池角色头像
|
||||
* @property {string} voice_icon 卡池角色语音头像
|
||||
* @property {string} voice_url 卡池角色语音URL
|
||||
* @property {string} voice_status 卡池角色语音状态
|
||||
* @description 如下时间示例:2023-03-21 17:59:59
|
||||
* @property {string} start_time 卡池开始时间
|
||||
* @property {string} end_time 卡池结束时间
|
||||
* @return Data
|
||||
*/
|
||||
interface Data {
|
||||
id: number;
|
||||
title: string;
|
||||
activity_url: string;
|
||||
content_before_act: string;
|
||||
pool: Array<{
|
||||
icon: string;
|
||||
url: string;
|
||||
}>;
|
||||
voice_icon: string;
|
||||
voice_url: string;
|
||||
voice_status: string;
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 用于渲染的卡池数据
|
||||
* @since Beta v0.6.6
|
||||
* @interface RenderCard
|
||||
* @property {string} id 卡池ID
|
||||
* @property {string} title 卡池标题
|
||||
* @property {string} subtitle 卡池副标题
|
||||
* @property {string} cover 卡池封面
|
||||
* @property {number} postId 卡池对应帖子ID
|
||||
* @property {Array<RenderItem>} characters 卡池角色头像
|
||||
* @property {string} time.str 卡池时间字符串
|
||||
* @property {string} time.startStamp 卡池开始时间戳
|
||||
* @property {string} time.endStamp 卡池结束时间戳
|
||||
* @property {string} time.totalStamp 卡池持续时间戳
|
||||
* @return RenderCard
|
||||
*/
|
||||
interface RenderCard {
|
||||
id: number;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
cover: string;
|
||||
postId: number;
|
||||
characters: RenderItem[];
|
||||
time: {
|
||||
str: string;
|
||||
startStamp: number;
|
||||
endStamp: number;
|
||||
totalStamp: number;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 用于渲染的卡池角色头像
|
||||
* @since Beta v0.4.4
|
||||
* @interface RenderItem
|
||||
* @property {string} icon 角色头像
|
||||
* @property {string} url 角色详情页URL
|
||||
* @property {TGApp.App.Character.WikiBriefInfo} info 角色简略信息
|
||||
* @return RenderItem
|
||||
*/
|
||||
interface RenderItem {
|
||||
icon: string;
|
||||
url: string;
|
||||
info?: TGApp.App.Character.WikiBriefInfo;
|
||||
}
|
||||
}
|
||||
50
src/plugins/Mys/types/Obc.d.ts
vendored
50
src/plugins/Mys/types/Obc.d.ts
vendored
@@ -1,50 +0,0 @@
|
||||
/**
|
||||
* @file plugins/Mys/types/Obc.d.ts
|
||||
* @description Mys obc 类型定义文件
|
||||
* @since Alpha v0.2.1
|
||||
*/
|
||||
|
||||
/**
|
||||
* @description Mys obc 类型
|
||||
* @since Alpha v0.2.1
|
||||
* @namespace TGApp.Plugins.Mys.Obc
|
||||
* @memberof TGApp.Plugins.Mys
|
||||
*/
|
||||
declare namespace TGApp.Plugins.Mys.Obc {
|
||||
/**
|
||||
* @description Mys obc 返回数据
|
||||
* @since Beta v0.3.6
|
||||
* @interface Response
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {Data[]} data.list obc 列表
|
||||
* @return Response
|
||||
*/
|
||||
interface Response extends TGApp.BBS.Response.BaseWithData {
|
||||
data: {
|
||||
list: Data[];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Mys obc 层级结构
|
||||
* @since Alpha v0.2.1
|
||||
* @interface Data
|
||||
* @property {number} id ID
|
||||
* @property {string} name 名称
|
||||
* @property {number} parent_id 父ID
|
||||
* @property {number} depth 深度
|
||||
* @property {string} ch_ext 结构化扩展信息
|
||||
* @property {Obc[]} children 子节点,可以递归
|
||||
* @property {unknown[]} list 列表
|
||||
* @return Data
|
||||
*/
|
||||
interface Data {
|
||||
id: number;
|
||||
name: string;
|
||||
parent_id: number;
|
||||
depth: number;
|
||||
ch_ext: string;
|
||||
children: Data[];
|
||||
list: unknown[];
|
||||
}
|
||||
}
|
||||
96
src/plugins/Mys/types/Position.d.ts
vendored
96
src/plugins/Mys/types/Position.d.ts
vendored
@@ -1,96 +0,0 @@
|
||||
/**
|
||||
* @file plugins/Mys/types/Position.d.ts
|
||||
* @description Mys 插件热点追踪接口
|
||||
* @since Beta v0.6.6
|
||||
*/
|
||||
|
||||
declare namespace TGApp.Plugins.Mys.Position {
|
||||
/**
|
||||
* @description 热点追踪信息的返回类型
|
||||
* @since Alpha v0.2.1
|
||||
* @interface Response
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {ObcItem[]} data.list obc 列表
|
||||
* @return Response
|
||||
*/
|
||||
interface Response extends TGApp.BBS.Response.BaseWithData {
|
||||
data: {
|
||||
list: ObcItem[];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 热点追踪层级结构
|
||||
* @since Alpha v0.2.1
|
||||
* @interface ObcItem
|
||||
* @extends TGApp.Plugins.Mys.Obc.Data
|
||||
* @property {Data[]} list 列表
|
||||
* @return ObcItem
|
||||
*/
|
||||
interface ObcItem extends TGApp.Plugins.Mys.Obc.Data {
|
||||
list: Data[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 热点追踪信息
|
||||
* @since Alpha v0.2.1
|
||||
* @interface Data
|
||||
* @property {number} recommend_id 推荐ID
|
||||
* @property {number} content_id 内容ID
|
||||
* @property {string} title 标题
|
||||
* @property {string} ext 扩展信息
|
||||
* @property {number} type 类型
|
||||
* @property {string} url 链接
|
||||
* @property {string} icon 图标
|
||||
* @property {string} abstract 摘要
|
||||
* @property {string} article_user_name 作者
|
||||
* @property {string} avatar_url 头像
|
||||
* @property {string} article_time 时间
|
||||
* @property {string} create_time 创建时间 // 2023-03-31 11:16:57
|
||||
* @property {string} end_time 结束时间 // 1680465599000
|
||||
* @return Data
|
||||
*/
|
||||
interface Data {
|
||||
recommend_id: number;
|
||||
content_id: number;
|
||||
title: string;
|
||||
ext: string;
|
||||
type: number;
|
||||
url: string;
|
||||
icon: string;
|
||||
abstract: string;
|
||||
article_user_name: string;
|
||||
avatar_url: string;
|
||||
article_time: string;
|
||||
create_time: string;
|
||||
end_time: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 渲染用的热点追踪信息
|
||||
* @since Beta v0.6.6
|
||||
* @interface RenderCard
|
||||
* @property {string} title 标题
|
||||
* @property {number} postId 帖子ID
|
||||
* @property {string} link 链接
|
||||
* @property {string} icon 图标
|
||||
* @property {string} abstract 摘要
|
||||
* @property time 时间
|
||||
* @property {number} time.startStamp 开始时间戳
|
||||
* @property {number} time.endStamp 结束时间戳
|
||||
* @property {number} time.totalStamp 总时间戳
|
||||
* @return RenderCard
|
||||
*/
|
||||
interface RenderCard {
|
||||
title: string;
|
||||
postId: number;
|
||||
link: string;
|
||||
icon: string;
|
||||
abstract: string;
|
||||
time: {
|
||||
startStamp: number;
|
||||
endStamp: number;
|
||||
totalStamp: number;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/**
|
||||
* @file plugins/Mys/utils/getGachaCard.ts
|
||||
* @description Mys 插件抽卡工具
|
||||
* @since Beta v0.6.3
|
||||
*/
|
||||
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
|
||||
import { AppCharacterData } from "@/data/index.js";
|
||||
import postReq from "@/web/request/postReq.js";
|
||||
|
||||
/**
|
||||
* @description 根据单个卡池信息转为渲染用的卡池信息
|
||||
* @since Beta v0.6.6
|
||||
* @param {TGApp.Plugins.Mys.Gacha.Data} data 卡池信息
|
||||
* @param {string} poolCover 卡池封面
|
||||
* @returns {Promise<TGApp.Plugins.Mys.Gacha.RenderCard>}
|
||||
*/
|
||||
async function getGachaItemCard(
|
||||
data: TGApp.Plugins.Mys.Gacha.Data,
|
||||
poolCover?: string,
|
||||
): Promise<TGApp.Plugins.Mys.Gacha.RenderCard | null> {
|
||||
let cover = "/source/UI/empty.webp";
|
||||
const postId: number | undefined = Number(data.activity_url.split("/").pop()) || undefined;
|
||||
if (postId === undefined || isNaN(postId)) return null;
|
||||
if (poolCover !== undefined) {
|
||||
cover = poolCover;
|
||||
} else {
|
||||
const postResp = await postReq.post(postId);
|
||||
if ("retcode" in postResp) {
|
||||
showSnackbar.error(`[${postResp.retcode}] ${postResp.message}`);
|
||||
return null;
|
||||
}
|
||||
cover = postResp.cover?.url ?? postResp.post.images[0];
|
||||
}
|
||||
const timeStr = `${data.start_time} ~ ${data.end_time}`;
|
||||
const characters: TGApp.Plugins.Mys.Gacha.RenderItem[] = [];
|
||||
for (const character of data.pool) {
|
||||
const item: TGApp.Plugins.Mys.Gacha.RenderItem = { icon: character.icon, url: character.url };
|
||||
const contentId = character.url.match(/(?<=content\/)\d+/)?.[0];
|
||||
if (contentId) {
|
||||
const itemF = AppCharacterData.find((item) => item.contentId.toString() === contentId);
|
||||
if (itemF) item.info = itemF;
|
||||
}
|
||||
characters.push(item);
|
||||
}
|
||||
return {
|
||||
id: data.id,
|
||||
title: data.title,
|
||||
subtitle: data.content_before_act,
|
||||
cover,
|
||||
postId,
|
||||
characters,
|
||||
time: {
|
||||
str: timeStr,
|
||||
startStamp: new Date(data.start_time).getTime(),
|
||||
endStamp: new Date(data.end_time).getTime(),
|
||||
totalStamp: new Date(data.end_time).getTime() - new Date(data.start_time).getTime(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 根据卡池信息转为渲染用的卡池信息
|
||||
* @since Beta v0.6.3
|
||||
* @param {TGApp.Plugins.Mys.Gacha.Data[]} gachaData 卡池信息
|
||||
* @param {Record<number, string>} poolCover 卡池封面
|
||||
* @returns {Promise<TGApp.Plugins.Mys.Gacha.RenderCard[]>}
|
||||
*/
|
||||
export async function getGachaCard(
|
||||
gachaData: TGApp.Plugins.Mys.Gacha.Data[],
|
||||
poolCover?: Record<number, string>,
|
||||
): Promise<TGApp.Plugins.Mys.Gacha.RenderCard[]> {
|
||||
const gachaCard: TGApp.Plugins.Mys.Gacha.RenderCard[] = [];
|
||||
for (const data of gachaData) {
|
||||
const item = await getGachaItemCard(data, poolCover?.[Number(data.id)]);
|
||||
if (item !== null) gachaCard.push(item);
|
||||
}
|
||||
return gachaCard;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/**
|
||||
* @file plugins/Mys/utils/getPositionCard.ts
|
||||
* @description Mys 插件热点追踪工具
|
||||
* @since Beta v0.6.6
|
||||
*/
|
||||
|
||||
/**
|
||||
* @description 根据热点追踪信息转为渲染用的数据
|
||||
* @since Beta v0.6.6
|
||||
* @param {TGApp.Plugins.Mys.Position.Data[]} positionData 列表
|
||||
* @returns {TGApp.Plugins.Mys.Position.RenderCard[]} 返回列表
|
||||
*/
|
||||
function getPositionCard(
|
||||
positionData: TGApp.Plugins.Mys.Position.Data[],
|
||||
): TGApp.Plugins.Mys.Position.RenderCard[] {
|
||||
const res: TGApp.Plugins.Mys.Position.RenderCard[] = [];
|
||||
for (const position of positionData) {
|
||||
let link = position.url;
|
||||
if (position.url === "" && position.content_id !== 0) {
|
||||
link = `https://bbs.mihoyo.com/ys/obc/content/${position.content_id}/detail?bbs_presentation_style=no_header`;
|
||||
}
|
||||
const startTs = new Date(position.create_time).getTime();
|
||||
const endTs = Number(position.end_time);
|
||||
const card: TGApp.Plugins.Mys.Position.RenderCard = {
|
||||
title: position.title,
|
||||
postId: position.url !== "" ? Number(position.url.split("/").pop()) : position.content_id,
|
||||
link: link,
|
||||
icon: position.icon,
|
||||
abstract: position.abstract,
|
||||
time: { startStamp: startTs, endStamp: endTs, totalStamp: endTs - startTs },
|
||||
};
|
||||
res.push(card);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
export default getPositionCard;
|
||||
@@ -1,46 +1,34 @@
|
||||
/**
|
||||
* @file plugins/Mys/types/CaptchaLogin.d.ts
|
||||
* @description Mys 插件 Captcha 登录类型定义文件
|
||||
* @since Beta v0.5.1
|
||||
* @file types/BBS/CaptchaLogin.d.ts
|
||||
* @description 验证码登录类型声明
|
||||
* @since Beta v0.7.2
|
||||
*/
|
||||
|
||||
/**
|
||||
* @description Mys 插件 Captcha 登录类型
|
||||
* @since Beta v0.5.1
|
||||
* @namespace TGApp.Plugins.Mys.CaptchaLogin
|
||||
* @memberof TGApp.Plugins.Mys
|
||||
*/
|
||||
declare namespace TGApp.Plugins.Mys.CaptchaLogin {
|
||||
declare namespace TGApp.BBS.CaptchaLogin {
|
||||
/**
|
||||
* @description 获取短信验证码返回数据
|
||||
* @since Beta v0.5.1
|
||||
* @interface CaptchaResponse
|
||||
* @since Beta v0.7.2
|
||||
* @interface CaptchaResp
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {CaptchaData} data 数据
|
||||
* @property {CaptchaRes} data 数据
|
||||
* @return GetCaptchaResponse
|
||||
*/
|
||||
interface CaptchaResponse extends TGApp.BBS.Response.BaseWithData {
|
||||
data: CaptchaData;
|
||||
}
|
||||
type CaptchaResp = TGApp.BBS.Response.BaseWithData<CaptchaRes>;
|
||||
|
||||
/**
|
||||
* @description 获取短信验证码返回数据
|
||||
* @since Beta v0.5.1
|
||||
* @interface CaptchaData
|
||||
* @since Beta v0.7.2
|
||||
* @interface CaptchaRes
|
||||
* @property {string} sent_new 是否发送新验证码
|
||||
* @property {string} countdown 倒计时
|
||||
* @property {string} action_type 操作类型
|
||||
* @return CaptchaData
|
||||
* @return CaptchaRes
|
||||
*/
|
||||
interface CaptchaData {
|
||||
sent_new: string;
|
||||
countdown: string;
|
||||
action_type: string;
|
||||
}
|
||||
type CaptchaRes = { sent_new: string; countdown: string; action_type: string };
|
||||
|
||||
/**
|
||||
* @description 触发验证的序列化数据
|
||||
* @since Beta v0.5.1
|
||||
* @since Beta v0.7.2
|
||||
* @interface CaptchaAigis
|
||||
* @property {string} session_id 会话 id
|
||||
* @property {number} mmt_type mmt 类型
|
||||
@@ -48,28 +36,22 @@ declare namespace TGApp.Plugins.Mys.CaptchaLogin {
|
||||
* @property {string} data 数据,为上面的序列化数据
|
||||
* @return CaptchaBody
|
||||
*/
|
||||
interface CaptchaAigis {
|
||||
session_id: string;
|
||||
mmt_type: number;
|
||||
data: string;
|
||||
}
|
||||
type CaptchaAigis = { session_id: string; mmt_type: number; data: string };
|
||||
|
||||
/**
|
||||
* @description 短信验证码登录返回数据
|
||||
* @since Beta v0.5.1
|
||||
* @interface LoginResponse
|
||||
* @since Beta v0.7.2
|
||||
* @interface LoginResp
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {LoginData} data 数据
|
||||
* @return LoginResponse
|
||||
* @property {LoginRes} data 数据
|
||||
* @return LoginResp
|
||||
*/
|
||||
interface LoginResponse extends TGApp.BBS.Response.BaseWithData {
|
||||
data: LoginData;
|
||||
}
|
||||
type LoginResp = TGApp.BBS.Response.BaseWithData<LoginRes>;
|
||||
|
||||
/**
|
||||
* @description 短信验证码登录返回数据
|
||||
* @since Beta v0.5.1
|
||||
* @interface LoginData
|
||||
* @since Beta v0.7.2
|
||||
* @interface LoginRes
|
||||
* @property {Token} token token数据
|
||||
* @property {UserInfo} user_info 用户信息
|
||||
* @property {ReactivateInfo} reactivate_info 重新激活信息
|
||||
@@ -78,9 +60,9 @@ declare namespace TGApp.Plugins.Mys.CaptchaLogin {
|
||||
* @property {RealnameInfo} realname_info 实名信息
|
||||
* @property {boolean} need_realperson 是否需要实名认证
|
||||
* @property {string} oauth_hw_open_id 华为 open id
|
||||
* @return LoginData
|
||||
* @return LoginRes
|
||||
*/
|
||||
interface LoginData {
|
||||
type LoginRes = {
|
||||
token: Token;
|
||||
user_info: UserInfo;
|
||||
reactivate_info: ReactivateInfo;
|
||||
@@ -89,24 +71,21 @@ declare namespace TGApp.Plugins.Mys.CaptchaLogin {
|
||||
realname_info: RealnameInfo;
|
||||
need_realperson: boolean;
|
||||
oauth_hw_open_id: string;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description token 数据
|
||||
* @since Beta v0.5.1
|
||||
* @since Beta v0.7.2
|
||||
* @interface Token
|
||||
* @property {number} token_type token 类型
|
||||
* @property {string} token token
|
||||
* @return Token
|
||||
*/
|
||||
interface Token {
|
||||
token_type: number;
|
||||
token: string;
|
||||
}
|
||||
type Token = { token_type: number; token: string };
|
||||
|
||||
/**
|
||||
* @description 用户信息
|
||||
* @since Beta v0.5.1
|
||||
* @since Beta v0.7.2
|
||||
* @interface UserInfo
|
||||
* @property {string} aid 账号 id
|
||||
* @property {string} mid mid
|
||||
@@ -128,7 +107,7 @@ declare namespace TGApp.Plugins.Mys.CaptchaLogin {
|
||||
* @property {number} unmasked_email_type 邮箱类型
|
||||
* @return UserInfo
|
||||
*/
|
||||
interface UserInfo {
|
||||
type UserInfo = {
|
||||
aid: string;
|
||||
mid: string;
|
||||
account_name: string;
|
||||
@@ -147,33 +126,26 @@ declare namespace TGApp.Plugins.Mys.CaptchaLogin {
|
||||
country: string;
|
||||
unmasked_email: string;
|
||||
unmasked_email_type: number;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 重新激活信息
|
||||
* @since Beta v0.5.1
|
||||
* @since Beta v0.7.2
|
||||
* @interface ReactivateInfo
|
||||
* @property {boolean} required 是否需要重新激活
|
||||
* @property {string} ticket 重新激活 ticket
|
||||
* @return ReactivateInfo
|
||||
*/
|
||||
interface ReactivateInfo {
|
||||
required: boolean;
|
||||
ticket: string;
|
||||
}
|
||||
type ReactivateInfo = { required: boolean; ticket: string };
|
||||
|
||||
/**
|
||||
* @description 实名信息
|
||||
* @since Beta v0.5.1
|
||||
* @since Beta v0.7.2
|
||||
* @interface RealnameInfo
|
||||
* @property {boolean} required 是否需要实名认证
|
||||
* @property {string} action_type 操作类型
|
||||
* @property {string} action_ticket 操作 ticket
|
||||
* @return RealnameInfo
|
||||
*/
|
||||
interface RealnameInfo {
|
||||
required: boolean;
|
||||
action_type: string;
|
||||
action_ticket: string;
|
||||
}
|
||||
type RealnameInfo = { required: boolean; action_type: string; action_ticket: string };
|
||||
}
|
||||
@@ -1,35 +1,34 @@
|
||||
/**
|
||||
* @file plugins/Mys/types/Emoji.d.ts
|
||||
* @description Mys 表情包类型声明文件
|
||||
* @since Beta v0.3.0
|
||||
* @file types/BBS/Emoji.d.ts
|
||||
* @description 表情包类型声明文件
|
||||
* @since Beta v0.7.2
|
||||
*/
|
||||
|
||||
/**
|
||||
* @description Mys 表情包类型
|
||||
* @since Beta v0.3.0
|
||||
* @namespace TGApp.Plugins.Mys.Emoji
|
||||
* @memberof TGApp.Plugins.Mys
|
||||
*/
|
||||
declare namespace TGApp.Plugins.Mys.Emoji {
|
||||
declare namespace TGApp.BBS.Emoji {
|
||||
/**
|
||||
* @description 获取表情包列表返回
|
||||
* @since Beta v0.3.0
|
||||
* @interface Response
|
||||
* @since Beta v0.7.2
|
||||
* @interface Resp
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {Series[]} data.list 表情包列表
|
||||
* @property {unknown} data.recently_emoticon 最近使用的表情包
|
||||
* @return Response
|
||||
* @return Resp
|
||||
*/
|
||||
interface Response extends TGApp.BBS.Response.BaseWithData {
|
||||
data: {
|
||||
list: Series[];
|
||||
recently_emoticon: unknown;
|
||||
};
|
||||
}
|
||||
type Resp = TGApp.BBS.Response.BaseWithData<Res>;
|
||||
|
||||
/**
|
||||
* @description 获取表情包列表返回
|
||||
* @since Beta v0.7.2
|
||||
* @interface Res
|
||||
* @property {Array<Series>} list 表情包列表
|
||||
* @property {unknown} recently_emoticon 最近使用的表情包
|
||||
* @return Res;
|
||||
*/
|
||||
type Res = { list: Array<Series>; recently_emoticon: unknown };
|
||||
|
||||
/**
|
||||
* @description 表情包系列
|
||||
* @since Beta v0.3.0
|
||||
* @since Beta v0.7.2
|
||||
* @interface Series
|
||||
* @property {number} id 表情包系列 ID
|
||||
* @property {string} name 表情包系列名称
|
||||
@@ -37,27 +36,27 @@ declare namespace TGApp.Plugins.Mys.Emoji {
|
||||
* @property {number} sort_order 表情包系列排序
|
||||
* @property {number} num 表情包系列数量
|
||||
* @property {string} status 表情包系列状态
|
||||
* @property {EmojiItem[]} list 表情包系列列表
|
||||
* @property {Item[]} list 表情包系列列表
|
||||
* @property {number} updated_at 表情包系列更新时间
|
||||
* @property {boolean} is_available 表情包系列是否可用
|
||||
* @return Series
|
||||
*/
|
||||
interface Series {
|
||||
type Series = {
|
||||
id: number;
|
||||
name: string;
|
||||
icon: string;
|
||||
sort_order: number;
|
||||
num: number;
|
||||
status: string;
|
||||
list: EmojiItem[];
|
||||
list: Array<Item>;
|
||||
updated_at: number;
|
||||
is_available: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 表情包
|
||||
* @since Beta v0.3.0
|
||||
* @interface EmojiItem
|
||||
* @since Beta v0.7.2
|
||||
* @interface Item
|
||||
* @property {number} id 表情包 ID
|
||||
* @property {string} name 表情包名称
|
||||
* @property {string} icon 表情包图标
|
||||
@@ -67,9 +66,9 @@ declare namespace TGApp.Plugins.Mys.Emoji {
|
||||
* @property {boolean} is_available 表情包是否可用
|
||||
* @property {string} status 表情包状态
|
||||
* @property {unknown[]} keywords 表情包关键词
|
||||
* @return EmojiItem
|
||||
* @return Item
|
||||
*/
|
||||
interface EmojiItem {
|
||||
type Item = {
|
||||
id: number;
|
||||
name: string;
|
||||
icon: string;
|
||||
@@ -78,6 +77,6 @@ declare namespace TGApp.Plugins.Mys.Emoji {
|
||||
updated_at: number;
|
||||
is_available: boolean;
|
||||
status: string;
|
||||
keywords: unknown[];
|
||||
}
|
||||
keywords: Array<unknown>;
|
||||
};
|
||||
}
|
||||
143
src/types/BBS/Obc.d.ts
vendored
Normal file
143
src/types/BBS/Obc.d.ts
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* @file types/BBS/Obc.d.ts
|
||||
* @description 观测枢相关请求类型
|
||||
* @since Beta v0.7.2
|
||||
*/
|
||||
|
||||
declare namespace TGApp.BBS.Obc {
|
||||
/**
|
||||
* @description 卡池返回响应
|
||||
* @since Beta v0.7.2
|
||||
* @interface GachaResp
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {GachaRes} data
|
||||
* @return GachaResp
|
||||
*/
|
||||
type GachaResp = TGApp.BBS.Response.BaseWithData<GachaRes>;
|
||||
|
||||
/**
|
||||
* @description 卡池返回数据
|
||||
* @since Beta v0.7.2
|
||||
* @interface GachaRes
|
||||
* @property {Array<GachaItem>} list 卡池列表
|
||||
* @return GachaRes
|
||||
*/
|
||||
type GachaRes = { list: Array<GachaItem> };
|
||||
|
||||
/**
|
||||
* @description 卡池信息
|
||||
* @since Beta v0.7.2
|
||||
* @interface GachaItem
|
||||
* @property {string} id 卡池ID
|
||||
* @property {string} title 卡池标题
|
||||
* @property {string} activity_url 卡池对应帖子
|
||||
* @property {string} content_before_act 卡池内容
|
||||
* @property {Array<GachaPool>} pool 卡池角色头像
|
||||
* @property {string} voice_icon 卡池角色语音头像
|
||||
* @property {string} voice_url 卡池角色语音URL
|
||||
* @property {string} voice_status 卡池角色语音状态
|
||||
* @description 如下时间示例:2023-03-21 17:59:59
|
||||
* @property {string} start_time 卡池开始时间
|
||||
* @property {string} end_time 卡池结束时间
|
||||
* @return GachaItem
|
||||
*/
|
||||
type GachaItem = {
|
||||
id: number;
|
||||
title: string;
|
||||
activity_url: string;
|
||||
content_before_act: string;
|
||||
pool: Array<GachaPool>;
|
||||
voice_icon: string;
|
||||
voice_url: string;
|
||||
voice_status: string;
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 卡池数据
|
||||
* @since Beta v0.7.2
|
||||
* @interface GachaPool
|
||||
* @property {string} icon 头像
|
||||
* @property {string} url 链接
|
||||
* @return GachaPool
|
||||
*/
|
||||
type GachaPool = { icon: string; url: string };
|
||||
|
||||
/**
|
||||
* @description 活动返回响应
|
||||
* @since Beta v0.7.2
|
||||
* @interface PositionResp
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {PositionRes} data
|
||||
* @return PositionResp
|
||||
*/
|
||||
type PositionResp = TGApp.BBS.Response.BaseWithData<PositionRes>;
|
||||
|
||||
/**
|
||||
* @description 活动返回数据
|
||||
* @since Beta v0.7.2
|
||||
* @interface PositionRes
|
||||
* @property {Array<PositionItem>} list 活动列表
|
||||
* @return PositionRes
|
||||
*/
|
||||
type PositionRes = { list: Array<ObcItem<PositionItem>> };
|
||||
|
||||
/**
|
||||
* @description 观测枢通用接口
|
||||
* @since Beta v0.7.2
|
||||
* @interface ObcItem
|
||||
* @template T
|
||||
* @property {number} id ID
|
||||
* @property {string} name 名称
|
||||
* @property {number} parent_id 父ID
|
||||
* @property {number} depth 深度
|
||||
* @property {string} ch_ext 结构化扩展信息
|
||||
* @property {Array<ObcItem>} children 子项递归
|
||||
* @property {Array<T>} list 列表
|
||||
*/
|
||||
type ObcItem<T = unknown> = {
|
||||
id: number;
|
||||
name: string;
|
||||
parent_id: number;
|
||||
depth: number;
|
||||
ch_ext: string;
|
||||
children: Array<ObcItem<T>>;
|
||||
list: Array<T>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 热点追踪信息
|
||||
* @since Beta v0.7.2
|
||||
* @interface PositionItem
|
||||
* @property {number} recommend_id 推荐ID
|
||||
* @property {number} content_id 内容ID
|
||||
* @property {string} title 标题
|
||||
* @property {string} ext 扩展信息
|
||||
* @property {number} type 类型
|
||||
* @property {string} url 链接
|
||||
* @property {string} icon 图标
|
||||
* @property {string} abstract 摘要
|
||||
* @property {string} article_user_name 作者
|
||||
* @property {string} avatar_url 头像
|
||||
* @property {string} article_time 时间
|
||||
* @property {string} create_time 创建时间 // 2023-03-31 11:16:57
|
||||
* @property {string} end_time 结束时间 // 1680465599000
|
||||
* @return PositionItem
|
||||
*/
|
||||
type PositionItem = {
|
||||
recommend_id: number;
|
||||
content_id: number;
|
||||
title: string;
|
||||
ext: string;
|
||||
type: number;
|
||||
url: string;
|
||||
icon: string;
|
||||
abstract: string;
|
||||
article_user_name: string;
|
||||
avatar_url: string;
|
||||
article_time: string;
|
||||
create_time: string;
|
||||
end_time: string;
|
||||
};
|
||||
}
|
||||
@@ -1,12 +1,32 @@
|
||||
/**
|
||||
* @file web/request/bbsReq.ts
|
||||
* @description BBS 请求模块
|
||||
* @since Beta v0.6.3
|
||||
* @since Beta v0.7.2
|
||||
*/
|
||||
|
||||
import TGHttp from "@/utils/TGHttp.js";
|
||||
import { getRequestHeader } from "@/web/utils/getRequestHeader.js";
|
||||
|
||||
/**
|
||||
* @description 获取表情包列表
|
||||
* @since Beta v0.7.2
|
||||
* @return {Promise<Record<string,string>|TGApp.BBS.Response.Base>}
|
||||
*/
|
||||
async function getEmoticonSet(): Promise<Record<string, string> | TGApp.BBS.Response.Base> {
|
||||
const resp = await TGHttp<TGApp.BBS.Emoji.Resp>(
|
||||
"https://bbs-api-static.miyoushe.com/misc/api/emoticon_set",
|
||||
{ method: "GET" },
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
const emojis: Record<string, string> = {};
|
||||
for (const series of resp.data.list) {
|
||||
for (const emoji of series.list) {
|
||||
emojis[emoji.name] = emoji.icon;
|
||||
}
|
||||
}
|
||||
return emojis;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 根据 cookie 获取用户信息
|
||||
* @since Beta v0.5.0
|
||||
@@ -46,6 +66,10 @@ async function getOtherUserInfo(
|
||||
return resp.data.user_info;
|
||||
}
|
||||
|
||||
const BBSApi = { userInfo: getUserFullInfo, otherUserInfo: getOtherUserInfo };
|
||||
const BBSApi = {
|
||||
emojis: getEmoticonSet,
|
||||
userInfo: getUserFullInfo,
|
||||
otherUserInfo: getOtherUserInfo,
|
||||
};
|
||||
|
||||
export default BBSApi;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file web/request/otherReq.ts
|
||||
* @description Other API
|
||||
* @since Beta v0.6.8
|
||||
* @since Beta v0.7.2
|
||||
*/
|
||||
|
||||
import TGBbs from "@/utils/TGBbs.js";
|
||||
@@ -11,7 +11,7 @@ import { getInitDeviceInfo } from "@/utils/toolFunc.js";
|
||||
|
||||
/**
|
||||
* @description 获取设备指纹
|
||||
* @since Beta v0.5.5
|
||||
* @since Beta v0.7.2
|
||||
* @param {TGApp.App.Device.DeviceInfo} Info - 设备信息
|
||||
* @returns {Promise<TGApp.App.Device.DeviceInfo>} 设备指纹
|
||||
*/
|
||||
@@ -91,8 +91,9 @@ async function getDeviceFp(
|
||||
"x-requested-with": "com.mihoyo.hyperion",
|
||||
Referer: "https://webstatic.mihoyo.com/",
|
||||
};
|
||||
type ResType = { device_fp: string; code: number; msg: string };
|
||||
try {
|
||||
const resp = await TGHttp<DeviceFpResp>(
|
||||
const resp = await TGHttp<TGApp.BBS.Response.BaseWithData<ResType>>(
|
||||
"https://public-data-api.mihoyo.com/device-fp/api/getFp",
|
||||
{ method: "POST", body: JSON.stringify(data), headers: header },
|
||||
);
|
||||
@@ -125,8 +126,3 @@ async function refreshCode(
|
||||
const OtherApi = { code: refreshCode, fp: getDeviceFp };
|
||||
|
||||
export default OtherApi;
|
||||
|
||||
/// 一些类型 ///
|
||||
type DeviceFpResp = TGApp.BBS.Response.BaseWithData & {
|
||||
data: { device_fp: string; code: number; msg: string };
|
||||
};
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
/**
|
||||
* @file web/request/passportReq.ts
|
||||
* @description Passport 相关请求
|
||||
* @since Beta v0.6.8
|
||||
* @since Beta v0.7.2
|
||||
*/
|
||||
import { JSEncrypt } from "jsencrypt";
|
||||
|
||||
import TGBbs from "@/utils/TGBbs.js";
|
||||
import TGHttp from "@/utils/TGHttp.js";
|
||||
import { getDeviceInfo } from "@/utils/toolFunc.js";
|
||||
import { getRequestHeader } from "@/web/utils/getRequestHeader.js";
|
||||
@@ -13,6 +16,26 @@ const pAbu: Readonly<string> = "https://passport-api.mihoyo.com/";
|
||||
const p4Abu: Readonly<string> = "https://passport-api-v4.mihoyo.com/";
|
||||
// HoyoLauncherVersion
|
||||
const hlv: Readonly<string> = "1.3.3.182";
|
||||
// 加密密钥
|
||||
const PUB_KEY_STR: Readonly<string> = `-----BEGIN PUBLIC KEY-----
|
||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDvekdPMHN3AYhm/vktJT+YJr7cI5DcsNKqdsx5DZX0gDuWFuIjzdwButrIYPNmRJ1G8ybDIF7oDW2eEpm5sMbL9zs
|
||||
9ExXCdvqrn51qELbqj0XxtMTIpaCHFSI50PfPpTFV9Xt/hmyVwokoOXFlAEgCn+Q
|
||||
CgGs52bFoYMtyi+xEQIDAQAB
|
||||
-----END PUBLIC KEY-----`;
|
||||
const encrypt = new JSEncrypt();
|
||||
encrypt.setPublicKey(PUB_KEY_STR);
|
||||
|
||||
/**
|
||||
* @description rsa 加密
|
||||
* @since Beta v0.5.1
|
||||
* @param {string} data - 待加密数据
|
||||
* @returns {string} 加密后数据
|
||||
*/
|
||||
function rsaEncrypt(data: string): string {
|
||||
const res = encrypt.encrypt(data.toString());
|
||||
if (res === false) return "";
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取登录ticket
|
||||
@@ -35,7 +58,8 @@ async function createAuthTicketByGameBiz(
|
||||
"x-rpc-client_type": "3",
|
||||
"x-rpc-app_id": "ddxf5dufpuyo",
|
||||
};
|
||||
const resp = await TGHttp<AuthTicketByGameBizResp>(
|
||||
type ResType = { ticket: string };
|
||||
const resp = await TGHttp<TGApp.BBS.Response.BaseWithData<ResType>>(
|
||||
`${pAbu}account/ma-cn-verifier/app/createAuthTicketByGameBiz`,
|
||||
{ method: "POST", headers: headers, query: params },
|
||||
);
|
||||
@@ -43,6 +67,48 @@ async function createAuthTicketByGameBiz(
|
||||
return resp.data.ticket;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取短信验证码
|
||||
* @since Beta v0.7.2
|
||||
* @param {string} phone - 手机号
|
||||
* @param {string} [aigis] - 验证数据
|
||||
* @returns {Promise<TGApp.BBS.CaptchaLogin.CaptchaRes | TGApp.BBS.Response.BaseWithData<string>>}
|
||||
*/
|
||||
async function createLoginCaptcha(
|
||||
phone: string,
|
||||
aigis?: string,
|
||||
): Promise<TGApp.BBS.CaptchaLogin.CaptchaRes | TGApp.BBS.Response.BaseWithData<string>> {
|
||||
const body = { area_code: rsaEncrypt("+86"), mobile: rsaEncrypt(phone) };
|
||||
const header: Record<string, string> = {
|
||||
"x-rpc-aigis": aigis || "",
|
||||
"x-rpc-app_version": TGBbs.version,
|
||||
"x-rpc-client_type": "2",
|
||||
"x-rpc-app_id": "bll8iq97cem8",
|
||||
"x-rpc-device_fp": getDeviceInfo("device_fp"),
|
||||
"x-rpc-device_name": getDeviceInfo("device_name"),
|
||||
"x-rpc-device_id": getDeviceInfo("device_id"),
|
||||
"x-rpc-device_model": getDeviceInfo("product"),
|
||||
"user-agent": TGBbs.ua,
|
||||
"content-type": "application/json",
|
||||
referer: "https://user.miyoushe.com/",
|
||||
"x-rpc-game_biz": "hk4e_cn",
|
||||
};
|
||||
const resp = await TGHttp<TGApp.BBS.CaptchaLogin.CaptchaResp>(
|
||||
`${pAbu}account/ma-cn-verifier/verifier/createLoginCaptcha`,
|
||||
{ method: "POST", headers: header, body: JSON.stringify(body) },
|
||||
true,
|
||||
);
|
||||
const data = await resp.data;
|
||||
if (data.retcode !== 0) {
|
||||
return <TGApp.BBS.Response.BaseWithData<string>>{
|
||||
retcode: data.retcode,
|
||||
message: data.message,
|
||||
data: resp.resp.headers.get("x-rpc-aigis"),
|
||||
};
|
||||
}
|
||||
return <TGApp.BBS.CaptchaLogin.CaptchaRes>data.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 创建登录二维码
|
||||
* @since Beta v0.6.8
|
||||
@@ -78,7 +144,8 @@ async function getCookieAccountInfoBySToken(
|
||||
): Promise<string | TGApp.BBS.Response.Base> {
|
||||
const ck = { stoken: cookie.stoken, mid: cookie.mid };
|
||||
const params = { stoken: cookie.stoken };
|
||||
const resp = await TGHttp<GetCookieTokenBySTokenResp | TGApp.BBS.Response.Base>(
|
||||
type ResType = { uid: string; cookie_token: string };
|
||||
const resp = await TGHttp<TGApp.BBS.Response.BaseWithData<ResType>>(
|
||||
`${pAbu}account/auth/api/getCookieAccountInfoBySToken`,
|
||||
{ method: "GET", headers: getRequestHeader(ck, "GET", params), query: params },
|
||||
);
|
||||
@@ -97,7 +164,8 @@ async function getLTokenBySToken(
|
||||
): Promise<string | TGApp.BBS.Response.Base> {
|
||||
const ck = { mid: cookie.mid, stoken: cookie.stoken };
|
||||
const params = { stoken: cookie.stoken };
|
||||
const resp = await TGHttp<GetLTokenBySTokenResp | TGApp.BBS.Response.Base>(
|
||||
type ResType = { ltoken: string };
|
||||
const resp = await TGHttp<TGApp.BBS.Response.BaseWithData<ResType>>(
|
||||
`${pAbu}account/auth/api/getLTokenBySToken`,
|
||||
{ method: "GET", headers: getRequestHeader(ck, "GET", params), query: params },
|
||||
);
|
||||
@@ -105,6 +173,46 @@ async function getLTokenBySToken(
|
||||
return resp.data.ltoken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 通过短信验证码登录
|
||||
* @since Beta v0.5.1
|
||||
* @param {string} phone - 手机号
|
||||
* @param {string} captcha - 验证码
|
||||
* @param {string} action_type - 操作类型
|
||||
* @param {string} [aigis] - 验证数据
|
||||
* @returns {Promise<TGApp.BBS.CaptchaLogin.LoginRes | TGApp.BBS.Response.Base>}
|
||||
*/
|
||||
async function loginByMobileCaptcha(
|
||||
phone: string,
|
||||
captcha: string,
|
||||
action_type: string,
|
||||
aigis?: string,
|
||||
): Promise<TGApp.BBS.CaptchaLogin.LoginRes | TGApp.BBS.Response.Base> {
|
||||
const body = {
|
||||
area_code: rsaEncrypt("+86"),
|
||||
mobile: rsaEncrypt(phone),
|
||||
action_type,
|
||||
captcha,
|
||||
};
|
||||
const header = {
|
||||
"x-rpc-aigis": aigis || "",
|
||||
"x-rpc-app_version": TGBbs.version,
|
||||
"x-rpc-client_type": "2",
|
||||
"x-rpc-app_id": "bll8iq97cem8",
|
||||
"x-rpc-device_fp": getDeviceInfo("device_fp"),
|
||||
"x-rpc-device_name": getDeviceInfo("device_name"),
|
||||
"x-rpc-device_id": getDeviceInfo("device_id"),
|
||||
"x-rpc-device_model": getDeviceInfo("product"),
|
||||
"user-agent": TGBbs.ua,
|
||||
};
|
||||
const resp = await TGHttp<TGApp.BBS.CaptchaLogin.LoginResp>(
|
||||
`${pAbu}account/ma-cn-passport/app/loginByMobileCaptcha`,
|
||||
{ method: "POST", headers: header, body: JSON.stringify(body) },
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取登录状态
|
||||
* @since Beta v0.6.8
|
||||
@@ -142,35 +250,7 @@ async function verifyLToken(
|
||||
): Promise<string | TGApp.BBS.Response.Base> {
|
||||
const ck = { ltoken: cookie.ltoken, ltuid: cookie.ltuid };
|
||||
const data = { ltoken: cookie.ltoken };
|
||||
const resp = await TGHttp<VerifyLtokenResp>(`${p4Abu}account/ma-cn-session/web/verifyLtoken`, {
|
||||
method: "POST",
|
||||
headers: getRequestHeader(ck, "POST", data),
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data.user_info.mid;
|
||||
}
|
||||
|
||||
const PassportApi = {
|
||||
authTicket: createAuthTicketByGameBiz,
|
||||
cookieToken: getCookieAccountInfoBySToken,
|
||||
lToken: { get: getLTokenBySToken, verify: verifyLToken },
|
||||
qrLogin: { create: createQrLogin, query: queryLoginStatus },
|
||||
};
|
||||
|
||||
export default PassportApi;
|
||||
|
||||
/// 一些类型 ///
|
||||
type AuthTicketByGameBizResp = TGApp.BBS.Response.BaseWithData & { data: { ticket: string } };
|
||||
|
||||
type GetCookieTokenBySTokenResp = TGApp.BBS.Response.BaseWithData & {
|
||||
data: { uid: string; cookie_token: string };
|
||||
};
|
||||
|
||||
type GetLTokenBySTokenResp = TGApp.BBS.Response.BaseWithData & { data: { ltoken: string } };
|
||||
|
||||
type VerifyLtokenResp = TGApp.BBS.Response.BaseWithData & {
|
||||
data: {
|
||||
type ResType = {
|
||||
realname_info: unknown;
|
||||
need_realperson: boolean;
|
||||
user_info: {
|
||||
@@ -189,4 +269,24 @@ type VerifyLtokenResp = TGApp.BBS.Response.BaseWithData & {
|
||||
links: Array<unknown>;
|
||||
};
|
||||
};
|
||||
const resp = await TGHttp<TGApp.BBS.Response.BaseWithData<ResType>>(
|
||||
`${p4Abu}account/ma-cn-session/web/verifyLtoken`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: getRequestHeader(ck, "POST", data),
|
||||
body: JSON.stringify(data),
|
||||
},
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data.user_info.mid;
|
||||
}
|
||||
|
||||
const PassportApi = {
|
||||
authTicket: createAuthTicketByGameBiz,
|
||||
cookieToken: getCookieAccountInfoBySToken,
|
||||
lToken: { get: getLTokenBySToken, verify: verifyLToken },
|
||||
qrLogin: { create: createQrLogin, query: queryLoginStatus },
|
||||
captcha: { create: createLoginCaptcha, login: loginByMobileCaptcha },
|
||||
};
|
||||
|
||||
export default PassportApi;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file web/request/takumiReq.ts
|
||||
* @description Takumi 相关请求函数
|
||||
* @since Beta v0.7.0
|
||||
* @since Beta v0.7.2
|
||||
*/
|
||||
import TGBbs from "@/utils/TGBbs.js";
|
||||
import TGHttp from "@/utils/TGHttp.js";
|
||||
@@ -57,20 +57,20 @@ async function getSTokenByGameToken(
|
||||
|
||||
/**
|
||||
* @description 根据stoken获取action_ticket
|
||||
* @since Beta v0.6.3
|
||||
* @since Beta v0.7.2
|
||||
* @param {TGApp.App.Account.Cookie} cookie Cookie
|
||||
* @param {TGApp.Sqlite.Account.Game} user 用户
|
||||
* @param {string} actionType 动作类型
|
||||
* @returns {Promise<ActionTicketByStokenResp>}
|
||||
* @returns {Promise<TGApp.BBS.Response.Base>}
|
||||
*/
|
||||
async function getActionTicketBySToken(
|
||||
cookie: TGApp.App.Account.Cookie,
|
||||
user: TGApp.Sqlite.Account.Game,
|
||||
actionType: string,
|
||||
): Promise<ActionTicketByStokenResp> {
|
||||
): Promise<TGApp.BBS.Response.Base> {
|
||||
const ck = { stoken: cookie.stoken, mid: cookie.mid };
|
||||
const params = { action_type: actionType, stoken: cookie.stoken, uid: user.gameUid };
|
||||
return await TGHttp<ActionTicketByStokenResp>(`${taBu}auth/api/getActionTicketBySToken`, {
|
||||
return await TGHttp<TGApp.BBS.Response.Base>(`${taBu}auth/api/getActionTicketBySToken`, {
|
||||
method: "GET",
|
||||
headers: getRequestHeader(ck, "GET", params, "K2"),
|
||||
query: params,
|
||||
@@ -127,7 +127,7 @@ async function genAuthKey2(
|
||||
|
||||
/**
|
||||
* @description 通过cookie获取游戏账号
|
||||
* @since Beta v0.6.5
|
||||
* @since Beta v0.7.2
|
||||
* @param {TGApp.App.Account.Cookie} cookie cookie
|
||||
* @returns {Promise<TGApp.BBS.Account.GameAccount[]|TGApp.BBS.Response.Base>}
|
||||
*/
|
||||
@@ -136,58 +136,76 @@ async function getUserGameRolesByCookie(
|
||||
): Promise<TGApp.BBS.Account.GameAccount[] | TGApp.BBS.Response.Base> {
|
||||
const ck = { account_id: cookie.account_id, cookie_token: cookie.cookie_token };
|
||||
const params = { game_biz: "hk4e_cn" };
|
||||
const resp = await TGHttp<GameAccountsResp>(`${taBu}binding/api/getUserGameRolesByCookie`, {
|
||||
method: "GET",
|
||||
headers: getRequestHeader(ck, "GET", params),
|
||||
query: params,
|
||||
});
|
||||
type ResType = { list: Array<TGApp.BBS.Account.GameAccount> };
|
||||
const resp = await TGHttp<TGApp.BBS.Response.BaseWithData<ResType>>(
|
||||
`${taBu}binding/api/getUserGameRolesByCookie`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: getRequestHeader(ck, "GET", params),
|
||||
query: params,
|
||||
},
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data.list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取卡池信息
|
||||
* @since Beta v0.7.2
|
||||
* @return {Promise<Array<TGApp.BBS.Obc.GachaItem>>}
|
||||
*/
|
||||
async function getObcGachaPool(): Promise<Array<TGApp.BBS.Obc.GachaItem>> {
|
||||
const resp = await TGHttp<TGApp.BBS.Obc.GachaResp>(
|
||||
`${taBu}common/blackboard/ys_obc/v1/gacha_pool`,
|
||||
{
|
||||
method: "GET",
|
||||
query: { app_sn: "ys_obc" },
|
||||
headers: { "Content-Type": "application/json" },
|
||||
},
|
||||
);
|
||||
return resp.data.list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 深度优先遍历
|
||||
* @since Beta v0.7.2
|
||||
* @param {Array<TGApp.BBS.Obc.ObcItem<TGApp.BBS.Obc.PositionItem>>} list 列表
|
||||
* @returns {Array<TGApp.BBS.Obc.PositionItem>} 返回列表
|
||||
*/
|
||||
function DfsObc(
|
||||
list: Array<TGApp.BBS.Obc.ObcItem<TGApp.BBS.Obc.PositionItem>>,
|
||||
): Array<TGApp.BBS.Obc.PositionItem> {
|
||||
const res: Array<TGApp.BBS.Obc.PositionItem> = [];
|
||||
for (const item of list) {
|
||||
if (item.name === "近期活动") res.push(...item.list);
|
||||
if (item.children) res.push(...DfsObc(item.children));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取热点追踪信息
|
||||
* @since Beta v0.7.2
|
||||
* @return {Promise<Array<TGApp.BBS.Obc.PositionItem>>}
|
||||
*/
|
||||
async function getObcHomePosition(): Promise<Array<TGApp.BBS.Obc.PositionItem>> {
|
||||
const resp = await TGHttp<TGApp.BBS.Obc.PositionResp>(
|
||||
`${taBu}common/blackboard/ys_obc/v1/home/position`,
|
||||
{
|
||||
method: "GET",
|
||||
query: { app_sn: "ys_obc" },
|
||||
headers: { "Content-Type": "application/json" },
|
||||
},
|
||||
);
|
||||
const data = resp.data.list;
|
||||
return DfsObc(data);
|
||||
}
|
||||
|
||||
const TakumiApi = {
|
||||
auth: { actionTicket: getActionTicketBySToken },
|
||||
bind: { authKey: genAuthKey, authKey2: genAuthKey2, gameRoles: getUserGameRolesByCookie },
|
||||
game: { stoken: getSTokenByGameToken },
|
||||
obc: { gacha: getObcGachaPool, position: getObcHomePosition },
|
||||
};
|
||||
|
||||
export default TakumiApi;
|
||||
|
||||
/// 一些类型 ///
|
||||
type ActionTicketByStokenResp = TGApp.BBS.Response.BaseWithData & {
|
||||
data: {
|
||||
ticket: string;
|
||||
is_verified: boolean;
|
||||
account_info: {
|
||||
is_realname: boolean;
|
||||
mobile: string;
|
||||
safe_mobile: string;
|
||||
account_id: string;
|
||||
account_name: string;
|
||||
email: string;
|
||||
is_email_verify: boolean;
|
||||
area_code: string;
|
||||
safe_area_code: string;
|
||||
real_name: string;
|
||||
identity_code: string;
|
||||
create_time: string;
|
||||
create_ip: string;
|
||||
change_pwd_time: string;
|
||||
nickname: string;
|
||||
user_icon_id: number;
|
||||
safe_level: number;
|
||||
black_endtime: string;
|
||||
black_note: string;
|
||||
gender: number;
|
||||
real_stat: number;
|
||||
apple_name: string;
|
||||
sony_name: string;
|
||||
tap_name: string;
|
||||
reactivate_ticket: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
type GameAccountsResp = TGApp.BBS.Response.BaseWithData & {
|
||||
data: { list: Array<TGApp.BBS.Account.GameAccount> };
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user