feat: 全新的客户端协议库!

This commit is contained in:
CrescentLeaf
2025-11-09 01:00:01 +08:00
parent 3e5fc722e6
commit f0ca0fbbd4
9 changed files with 378 additions and 9 deletions

229
client-protocol/Chat.ts Normal file
View File

@@ -0,0 +1,229 @@
import BaseClientObject from "./BaseClientObject.ts"
import BaseChatSettingsBean from "./bean/BaseChatSettingsBean.ts"
import ChatBean from "./bean/ChatBean.ts"
import JoinRequestBean from "./bean/JoinRequestBean.ts"
import MessageBean from "./bean/MessageBean.ts"
import CallbackError from "./CallbackError.ts"
import JoinRequest from "./JoinRequest.ts"
import LingChairClient from "./LingChairClient.ts"
import Message from "./Message.ts"
export default class Chat extends BaseClientObject {
declare bean: ChatBean
constructor(client: LingChairClient, bean: ChatBean) {
super(client)
this.bean = bean
}
/*
* ================================================
* 实例化方法
* ================================================
*/
static async getById(client: LingChairClient, id: string) {
try {
return await this.getByIdOrThrow(client, id)
} catch (_) {
return null
}
}
static async getByIdOrThrow(client: LingChairClient, id: string) {
const re = await client.invoke("Chat.getInfo", {
token: client.access_token,
target: id,
})
if (re.code == 200)
return new Chat(client, re.data as unknown as ChatBean)
throw new CallbackError(re)
}
/**
* ================================================
* 创建对话 (另类实例化方法)
* ================================================
*/
static async getOrCreatePrivateChat(client: LingChairClient, user_id: string) {
try {
return await this.getOrCreatePrivateChatOrThrow(client, user_id)
} catch (_) {
return null
}
}
static async getOrCreatePrivateChatOrThrow(client: LingChairClient, user_id: string) {
const re = await client.invoke("Chat.getIdForPrivate", {
token: client.access_token,
target: user_id,
})
if (re.code != 200) throw new CallbackError(re)
return new Chat(client, re.data as unknown as ChatBean)
}
static async createGroup(client: LingChairClient, title: string, name?: string) {
try {
return await this.createGroupOrThrow(client, title, name)
} catch (_) {
return null
}
}
static async createGroupOrThrow(client: LingChairClient, title: string, name?: string) {
const re = await client.invoke("Chat.createGroup", {
token: client.access_token,
title,
name,
})
if (re.code != 200) throw new CallbackError(re)
return new Chat(client, re.data as unknown as ChatBean)
}
/**
* ================================================
* 对话消息
* ================================================
*/
async getMessages(page: number = 0) {
return (await this.getMessageBeans(page)).map((v) => new Message(this.client, v))
}
async getMessagesOrThrow(page: number = 0) {
return (await this.getMessageBeansOrThrow(page)).map((v) => new Message(this.client, v))
}
async getMessageBeans(page: number = 0) {
try {
return await this.getMessageBeansOrThrow(page)
} catch (_) {
return []
}
}
async getMessageBeansOrThrow(page: number = 0) {
const re = await this.client.invoke("Chat.getMessageHistory", {
token: this.client.access_token,
page,
target: this.bean.id,
})
if (re.code == 200) return re.data!.messages as MessageBean[]
throw new CallbackError(re)
}
async sendMessage(text: string) {
try {
return await this.sendMessageOrThrow(text)
} catch (_) {
return null
}
}
async sendMessageOrThrow(text: string) {
const re = await this.client.invoke("Chat.sendMessage", {
token: this.client.access_token,
text,
target: this.bean.id,
})
if (re.code == 200)
return new Message(this.client, re.data!.message as MessageBean)
throw new CallbackError(re)
}
/**
* ================================================
* 加入对话申请
* ================================================
*/
async getJoinRequests() {
try {
return await this.getJoinRequestsOrThrow()
} catch (_) {
return []
}
}
async getJoinRequestsOrThrow() {
const join_requests = await this.getJoinRequestBeansOrThrow()
return join_requests.map((jr) => new JoinRequest(this.client, jr, this.bean.id))
}
async getJoinRequestBeans() {
try {
return await this.getJoinRequestBeansOrThrow()
} catch (_) {
return []
}
}
async getJoinRequestBeansOrThrow() {
const re = await this.client.invoke("Chat.getJoinRequests", {
token: this.client.access_token
})
if (re.code == 200)
return re.data!.join_requests as JoinRequestBean[]
throw new CallbackError(re)
}
/**
* ================================================
* 对话信息
* ================================================
*/
async setAvatarFileHash(file_hash: string) {
try {
await this.setAvatarFileHashOrThrow(file_hash)
return true
} catch (_) {
return false
}
}
async setAvatarFileHashOrThrow(file_hash: string) {
const re = await this.client.invoke("Chat.setAvatar", {
token: this.client.access_token,
file_hash,
target: this.bean.id,
})
if (re.code != 200) throw new CallbackError(re)
this.bean.avatar_file_hash = file_hash
}
async updateSettings(args: BaseChatSettingsBean) {
try {
await this.updateSettingsOrThrow(args)
return true
} catch (_) {
return false
}
}
async updateSettingsOrThrow(args: BaseChatSettingsBean) {
const re = await this.client.invoke("Chat.updateSettings", {
token: this.client.access_token,
target: this.bean.id,
settings: args
})
if (re.code != 200) throw new CallbackError(re)
this.bean.settings = args
}
async getTheOtherUserId() {
try {
return await this.getTheOtherUserIdOrThrow()
} catch (_) {
return null
}
}
async getTheOtherUserIdOrThrow() {
const re = await this.client.invoke("Chat.updateSettings", {
token: this.client.access_token,
target: this.bean.id,
})
if (re.code != 200) throw new CallbackError(re)
return re.data!.user_id as string
}
/*
* ================================================
* 基本 Bean
* ================================================
*/
getId() {
return this.bean.id
}
getTitle() {
return this.bean.title
}
getType() {
return this.bean.type
}
isMember() {
return this.bean.is_member
}
isAdmin() {
return this.bean.is_admin
}
getAvatarFileHash() {
return this.bean.avatar_file_hash
}
getSettings() {
return this.bean.settings
}
}

View File

@@ -0,0 +1,66 @@
import BaseClientObject from "./BaseClientObject.ts"
import JoinRequestBean from "./bean/JoinRequestBean.ts"
import CallbackError from "./CallbackError.ts"
import LingChairClient from "./LingChairClient.ts"
import JoinRequestAction from "./type/JoinRequestAction.ts"
export default class JoinRequest extends BaseClientObject {
declare bean: JoinRequestBean
declare chat_id: string
constructor(client: LingChairClient, bean: JoinRequestBean, chat_id: string) {
super(client)
this.bean = bean
this.chat_id = chat_id
}
/*
* ================================================
* 操作
* ================================================
*/
async accept() {
return await this.process('accept')
}
async acceptOrThrow() {
return await this.processOrThrow('accept')
}
async remove() {
return await this.process('remove')
}
async removOrThrow() {
return await this.processOrThrow('remove')
}
async process(action: JoinRequestAction) {
try {
await this.processOrThrow(action)
return true
} catch (_) {
return false
}
}
async processOrThrow(action: JoinRequestAction) {
const re = await this.client.invoke("Chat.processJoinRequest", {
token: this.client.access_token,
chat_id: this.chat_id,
user_id: this.bean.user_id,
action,
})
if (re.code != 200) throw new CallbackError(re)
}
/*
* ================================================
* 基本 Bean
* ================================================
*/
getAvatarFileHash() {
return this.bean.avatar_file_hash
}
getUserId() {
return this.bean.user_id
}
getNickName() {
return this.bean.title
}
getReason() {
return this.bean.reason
}
}

View File

@@ -6,9 +6,11 @@ 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,
}

View File

@@ -0,0 +1,39 @@
import BaseClientObject from "./BaseClientObject.ts"
import MessageBean from "./bean/MessageBean.ts"
import LingChairClient from "./LingChairClient.ts"
import Chat from "./Chat.ts"
import User from "./User.ts"
export default class Message extends BaseClientObject {
declare bean: MessageBean
constructor(client: LingChairClient, bean: MessageBean) {
super(client)
this.bean = bean
}
/*
* ================================================
* 基本 Bean
* ================================================
*/
getId() {
return this.bean.id
}
getChatId() {
return this.bean.chat_id
}
async getChat() {
return await Chat.getById(this.client, this.bean.chat_id as string)
}
getText() {
return this.bean.text
}
getUserId() {
return this.bean.user_id
}
async getUser() {
return await User.getById(this.client, this.bean.user_id as string)
}
getTime() {
return this.bean.time
}
}

View File

@@ -1,5 +1,5 @@
import BaseChatSettingsBean from "./BaseChatSettingsBean.ts"
import ChatType from "./ChatType.ts"
import ChatType from "../type/ChatType.ts"
export default class ChatBean {
declare type: ChatType

View File

@@ -1,7 +1,7 @@
export default class JoinRequestBean {
declare user_id: string
declare title: string
declare avatar?: string
declare nickname: string
declare avatar_file_hash?: string
declare reason?: string
[key: string]: unknown

View File

@@ -1,6 +1,7 @@
export default class MessageBean {
declare id: number
declare text: string
declare user_id: string
declare user_id?: string
declare chat_id?: string
declare time: string
}

View File

@@ -1,4 +1,4 @@
import LingChairClient, { User, UserMySelf } from "./LingChairClient.ts"
import LingChairClient, { Chat, UserMySelf } from "./LingChairClient.ts"
import OnMessageData from "./type/OnMessageData.ts"
const client = new LingChairClient({
@@ -9,7 +9,10 @@ await client.auth({
account: '满月',
password: '满月',
})
client.on('Client.onMessage', (data: OnMessageData) => {
console.log(data)
client.on('Client.onMessage', async (data: OnMessageData) => {
const chat = await Chat.getById(client, data.chat)
const regexp = /^test (.*)/g.exec(data.msg.text)
if (regexp?.[0] != null) {
chat?.sendMessage(`Hello, ${regexp[1]}`)
}
})
console.log(await (await UserMySelf.getMySelf(client))?.getMyRecentChatBeans())

View File

@@ -76,7 +76,7 @@ export default class ChatApi extends BaseApi {
code: 200,
msg: "成功",
data: {
msg: m,
message: m,
}
}
})
@@ -196,6 +196,8 @@ export default class ChatApi extends BaseApi {
return {
user_id: user?.bean.id,
reason: v.reason,
nickname: user!.getNickName(),
// TODO: 这个得删掉, 应该用 nickname
title: user!.getNickName(),
avatar_file_hash: user!.getAvatarFileHash() ? user!.getAvatarFileHash() : null,
}
@@ -342,7 +344,18 @@ export default class ChatApi extends BaseApi {
code: 200,
msg: '成功',
data: {
// TODO: 移除这个, 将本方法重命名为 getOrCreatePrivateChat
// 并重构原 Web 客户端所引用的内容
chat_id: chat.bean.id,
id: chat.bean.id,
name: chat.bean.name,
type: chat.bean.type,
title: chat.getTitle(user),
avatar_file_hash: chat.getAvatarFileHash(user) ? chat.getAvatarFileHash(user) : undefined,
settings: JSON.parse(chat.bean.settings),
is_member: true,
is_admin: true,
}
}
})
@@ -384,7 +397,23 @@ export default class ChatApi extends BaseApi {
code: 200,
msg: '成功',
data: {
// TODO: 移除这个
// 并重构原 Web 客户端所引用的内容
chat_id: chat.bean.id,
id: chat.bean.id,
name: chat.bean.name,
type: chat.bean.type,
title: chat.getTitle(),
avatar_file_hash: chat.getAvatarFileHash() ? chat.getAvatarFileHash() : undefined,
settings: {
...JSON.parse(chat.bean.settings),
// 下面两个比较特殊, 用于群设置
group_name: chat.bean.name,
group_title: chat.getTitle(),
},
is_member: UserChatLinker.checkUserIsLinkedToChat(token.author, chat!.bean.id),
is_admin: chat.checkUserIsAdmin(token.author),
}
}
})