// deno-lint-ignore-file no-explicit-any import { io, ManagerOptions, Socket, SocketOptions } from 'socket.io-client' import crypto from 'node:crypto' import { CallMethod, ClientEvent } from './ApiDeclare.ts' import ApiCallbackMessage from './ApiCallbackMessage.ts' import User from "./User.ts" import UserMySelf from "./UserMySelf.ts" import CallbackError from "./CallbackError.ts" import Chat from "./Chat.ts" export { User, Chat, UserMySelf, } export default class LingChairClient { declare client: Socket declare access_token: string declare refresh_token?: string constructor(args: { server_url: string device_id: string, io?: Partial }) { this.client = io(args.server_url, { transports: ["polling", "websocket", "webtransport"], ...args.io, auth: { ...args.io?.auth, device_id: args.device_id, session_id: crypto.randomUUID(), }, }) this.client.on("The_White_Silk", (name: string, data: unknown, _callback: (ret: unknown) => void) => { try { if (name == null || data == null) return this.events[name]?.forEach((v) => v(data)) } catch (e) { console.error(e) } }) } connect() { this.client.connect() } disconnect() { this.client.disconnect() } reconnect() { this.disconnect() this.connect() } invoke(method: CallMethod, args: object = {}, timeout: number = 10000): Promise { return new Promise((resolve) => { this.client!.timeout(timeout).emit("The_White_Silk", method, args, (err: Error, res: ApiCallbackMessage) => { // 错误处理 if (err) return resolve({ code: -1, msg: err.message, }) resolve(res) }) }) } events: { [key: string]: ((data: any) => void)[] } = {} on(eventName: ClientEvent, func: (data: any) => void) { if (this.events[eventName] == null) this.events[eventName] = [] if (this.events[eventName].indexOf(func) == -1) this.events[eventName].push(func) } off(eventName: ClientEvent, func: (data: any) => void) { if (this.events[eventName] == null) this.events[eventName] = [] const index = this.events[eventName].indexOf(func) if (index != -1) this.events[eventName].splice(index, 1) } async auth(args: { refresh_token?: string, access_token?: string, account?: string, password?: string, }) { try { await this.authOrThrow(args) return true } catch (_) { return false } } async authOrThrow(args: { refresh_token?: string, access_token?: string, account?: string, password?: string, }) { if ((!args.access_token && !args.refresh_token) && (!args.account && !args.password)) throw new Error('Access/Refresh token or account & password required') this.refresh_token = args.refresh_token let access_token = args.access_token if (!access_token && args.refresh_token) { const re = await this.invoke('User.refreshAccessToken', { refresh_token: args.refresh_token, }) if (re.code == 200) access_token = re.data!.access_token as string | undefined else throw new CallbackError(re) } if (!access_token && (args.account && args.password)) { const re = await this.invoke('User.login', { account: args.account, password: crypto.createHash('sha256').update(args.password).digest('hex'), }) if (re.code == 200) access_token = re.data!.access_token as string | undefined else throw new CallbackError(re) } const re = await this.invoke('User.auth', { access_token: access_token }) if (re.code == 200) this.access_token = access_token as string else throw new CallbackError(re) } async register(args: { nickname: string, username?: string, password: string, }) { try { await this.registerOrThrow(args) return true } catch (_) { return false } } async registerOrThrow({ nickname, username, password, }: { nickname: string, username?: string, password: string, }) { const re = await this.invoke('User.register', { nickname, username, password, }) if (re.code != 200) throw new CallbackError(re) } }