my-kaiqiuwang/src/utils.ts
2026-01-28 17:00:31 +09:00

163 lines
5.6 KiB
TypeScript

// import { fetch } from 'bun';
import type { IEventInfo, Player } from "./types";
import * as cheerio from "cheerio";
import fs from 'fs';
import path from 'path';
import { chunk } from 'lodash';
import type { XCXProfile } from "./types/profile";
import type { XCXTag } from "./types/tag";
const BASE_URL = `https://kaiqiuwang.cc`;
const XCX_BASE_URL = `${BASE_URL}/xcx/public/index.php`;
/**
* token:
XX-Device-Type: wxapp
content-type: application/json
Accept-Encoding: gzip,compress,br,deflate
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.68(0x1800442a) NetType/WIFI Language/zh_CN
Referer: https://servicewechat.com/wxff09fb0e92aa456a/464/page-frame.html
*/
const xcxDefaultHeaders = {
'token': 'e72b91bb-a690-44fe-9274-6a4a251f611b',
'XX-Device-Type': 'wxapp',
'content-type': 'application/json',
'Accept-Encoding': 'gzip,compress,br,deflate',
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.68(0x1800442a) NetType/WIFI Language/zh_CN',
'Referer': 'https://servicewechat.com/wxff09fb0e92aa456a/464/page-frame.html',
};
/**
* @param tagid 俱乐部 ID
*/
export async function listEvent(tagid: string): Promise<IEventInfo[]> {
return parseEventList(await fetchEventListHTML(tagid));
return parseEventList(
fs.readFileSync(
path.resolve(__dirname, '..', '__test__', 'data', 'view-event.html')
).toString()
);
}
/**
* @param tagid 俱乐部 ID
* @return HTML
*/
export async function fetchEventListHTML(tagid: string) {
const url = `${BASE_URL}/home/space.php?do=mtag&tagid=${tagid}&view=event`;
const resp = await fetch(url);
return resp.text() ?? '';
}
export function parseEventList(html: string) {
const $ = cheerio.load(html);
const blockList = $('div.event_list > ol > li');
const list: IEventInfo[] = [];
for (const block of blockList) {
const titleEl = $(block).find('.event_title');
const title = titleEl.text();
const url = $(titleEl).find('a').attr('href') ?? '';
const startDate = $(block).find('ul li:nth-of-type(1)').text().replace('比赛开始: \\t', '').trim();
const place = $(block).find('ul li:nth-of-type(2)').text().replace('比赛地点: \\t', '').trim();
const event: IEventInfo = {
title,
info: [startDate, place],
url: `${BASE_URL}/home/${url}`,
matchId: /\S+-(\d+).html$/.exec(url)?.[1] ?? '',
}
list.push(event);
}
return list;
}
/**
*
* @param matchId 比赛 ID
* @returns HTML
*/
export async function fetchEventContentHTML(matchId: string) {
const url = `${BASE_URL}/home/space.php?do=event&id=${matchId}&view=member&status=2`;
const resp = await fetch(url);
return resp.text() ?? ''
}
export function parseEventInfo(html: string) {
const $ = cheerio.load(html);
const title = $('h2.title a').text();
const itemHref = $('.sub_menu a.active').attr('href') ?? '';
const itemId = /\S+item_id=(\d+)$/.exec(itemHref)?.[1] ?? '';
const players: Player[] = [];
const playersEl = $('.thumb');
for (const player of playersEl) {
const img = $(player).find('.image img').attr('src') ?? '';
const name = $(player).find('h6').text().trim();
const uid = /space-(?<uid>\d+).html/.exec($(player).find('h6 a').attr('href') ?? '')?.groups?.uid ?? '';
const info = $(player).find('p:nth-of-type(2)').text().replace(/\s/g, '');
const score = Number(/^.*?\b(\d+)\b/.exec(info)?.[1]);
players.push({ name, avatar: img, score, info, uid });
}
return {
itemId,
title,
players,
}
}
export async function getMatchInfo(matchId: string) {
return parseEventInfo(await fetchEventContentHTML(matchId));
return parseEventInfo(
fs
.readFileSync(
path.resolve(__dirname, '..', '__test__', 'data', 'view-event-content.html')
)
.toString()
);
}
export function sneckGroup(size: number, groupLen: number) {
const indexArray = new Array<number>(size).fill(0).map((_, i) => i);
const chunckSize = Math.round((size / groupLen));
const chunckedGroup = chunk(indexArray, groupLen);
const reversedGroup = chunckedGroup.map((e, i) => {
if (i % 2 === 0) return e;
return e.toReversed();
});
const newGroups: number[][] = [];
for (let groupIndex = 0; groupIndex < groupLen; groupIndex++) {
const group: number[] = [];
for (let colIndex = 0; colIndex < chunckSize; colIndex++) {
const data = reversedGroup[colIndex]?.[groupIndex];
group.push(data === undefined ? NaN : data);
}
newGroups.push(group);
}
return newGroups;
}
export async function getAdvProfile(uid: string) {
// return JSON.parse(fs.readFileSync(
// path.resolve(__dirname, '..', '__test__', 'data', 'profile.json'),
// ).toString()).data;
if (!/^\d+$/.test(uid)) return null;
if (!uid) return null;
const resp = await fetch(`${XCX_BASE_URL}/api/User/adv_profile?uid=${uid}`, {
headers: xcxDefaultHeaders,
});
const data = await resp.json();
if (data.code !== 1) return null;
return data.data as XCXProfile;
}
export async function getPlayerTags(uid: string) {
// return JSON.parse(fs.readFileSync(
// path.resolve(__dirname, '..', '__test__', 'data', 'profile.json'),
// ).toString()).data;
if (!/^\d+$/.test(uid)) return null;
if (!uid) return null;
const resp = await fetch(`${XCX_BASE_URL}/api/User/get_tags?uid=${uid}&limitByCount=50&getNegative=true`, {
headers: xcxDefaultHeaders,
});
const data = await resp.json();
if (data.code !== 1) return null;
return (data.data as XCXTag[]).filter(e => Number(e.count) > 0);
}