diff --git a/client/packages/tailchat-client-sdk/package.json b/client/packages/tailchat-client-sdk/package.json index f6f214a2..f09f09a0 100644 --- a/client/packages/tailchat-client-sdk/package.json +++ b/client/packages/tailchat-client-sdk/package.json @@ -1,6 +1,6 @@ { "name": "tailchat-client-sdk", - "version": "1.0.5", + "version": "1.0.6", "description": "", "main": "lib/index.js", "scripts": { @@ -16,9 +16,12 @@ "@types/node": "^18.16.1", "jest": "27.5.1", "ts-jest": "27.1.4", + "tailchat-types": "*", "typescript": "^4.9.5" }, "dependencies": { - "axios": "^1.3.2" + "axios": "^1.3.2", + "socket.io-client": "^4.7.1", + "socket.io-msgpack-parser": "^3.0.2" } } diff --git a/client/packages/tailchat-client-sdk/src/openapi/client.ts b/client/packages/tailchat-client-sdk/src/openapi/client/base.ts similarity index 88% rename from client/packages/tailchat-client-sdk/src/openapi/client.ts rename to client/packages/tailchat-client-sdk/src/openapi/client/base.ts index 05c86b71..ada652be 100644 --- a/client/packages/tailchat-client-sdk/src/openapi/client.ts +++ b/client/packages/tailchat-client-sdk/src/openapi/client/base.ts @@ -1,7 +1,7 @@ import axios, { AxiosInstance } from 'axios'; import crypto from 'crypto'; -export class TailchatClient { +export class TailchatBaseClient { request: AxiosInstance; jwt: string | null = null; userId: string | null = null; @@ -58,16 +58,20 @@ export class TailchatClient { } } + async waitingForLogin(): Promise { + await Promise.resolve(this.loginP); + } + async call(action: string, params = {}) { try { - await Promise.resolve(this.loginP); + await this.waitingForLogin(); console.log('Calling:', action); const { data } = await this.request.post( '/api/' + action.replace(/\./g, '/'), params ); - return data; + return data.data; } catch (err: any) { console.error('Service Call Failed:', err); const data: string = err?.response?.data; @@ -84,7 +88,18 @@ export class TailchatClient { } } - async whoami() { + async whoami(): Promise<{ + userAgent: string; + language: string; + user: { + _id: string; + nickname: string; + email: string; + avatar: string; + }; + token: string; + userId: string; + }> { return this.call('user.whoami'); } diff --git a/client/packages/tailchat-client-sdk/src/openapi/client/http.ts b/client/packages/tailchat-client-sdk/src/openapi/client/http.ts new file mode 100644 index 00000000..e472b74d --- /dev/null +++ b/client/packages/tailchat-client-sdk/src/openapi/client/http.ts @@ -0,0 +1,3 @@ +import { TailchatBaseClient } from './base'; + +export class TailchatHTTPClient extends TailchatBaseClient {} diff --git a/client/packages/tailchat-client-sdk/src/openapi/client/index.ts b/client/packages/tailchat-client-sdk/src/openapi/client/index.ts new file mode 100644 index 00000000..6e93e903 --- /dev/null +++ b/client/packages/tailchat-client-sdk/src/openapi/client/index.ts @@ -0,0 +1,8 @@ +export { + /** + * @deprecated please rename to TailchatHTTPClient + */ + TailchatHTTPClient as TailchatClient, + TailchatHTTPClient, +} from './http'; +export { TailchatWsClient } from './ws'; diff --git a/client/packages/tailchat-client-sdk/src/openapi/client/ws.ts b/client/packages/tailchat-client-sdk/src/openapi/client/ws.ts new file mode 100644 index 00000000..291b4e87 --- /dev/null +++ b/client/packages/tailchat-client-sdk/src/openapi/client/ws.ts @@ -0,0 +1,103 @@ +import { TailchatBaseClient } from './base'; +import io, { Socket } from 'socket.io-client'; +import * as msgpackParser from 'socket.io-msgpack-parser'; +import type { ChatMessage } from 'tailchat-types'; + +export class TailchatWsClient extends TailchatBaseClient { + public socket: Socket | null = null; + + connect(): Promise { + return new Promise(async (resolve, reject) => { + await this.waitingForLogin(); + + const token = this.jwt; + + const socket = (this.socket = io(this.url, { + transports: ['websocket'], + auth: { + token, + }, + forceNew: true, + parser: msgpackParser, + })); + + socket.once('connect', () => { + // 连接成功 + this.emit('chat.converse.findAndJoinRoom') + .then((res) => { + console.log('Joined rooms', res.data); + resolve(socket); + }) + .catch((err) => { + reject(err); + }); + }); + socket.once('error', () => { + reject(); + }); + + socket.on('disconnect', (reason) => { + console.log(`disconnect due to ${reason}`); + this.socket = null; + }); + + socket.onAny((ev) => { + console.log('onAny', ev); + }); + }); + } + + disconnect() { + if (!this.socket) { + console.warn('You should call it after connect'); + return; + } + + this.socket.disconnect(); + this.socket = null; + } + + emit(eventName: string, eventData: any = {}) { + if (!this.socket) { + console.warn('You should call it after connect'); + throw new Error('You should call it after connect'); + } + + return this.socket.emitWithAck(eventName, eventData); + } + + on(eventName: string, callback: (payload: any) => void) { + if (!this.socket) { + console.warn('You should call it after connect'); + return; + } + + this.socket.on(eventName, callback); + } + + once(eventName: string, callback: (payload: any) => void) { + if (!this.socket) { + console.warn('You should call it after connect'); + return; + } + + this.socket.once(eventName, callback); + } + + off(eventName: string, callback: (payload: any) => void) { + if (!this.socket) { + console.warn('You should call it after connect'); + return; + } + + this.socket.off(eventName, callback); + } + + onMessage(callback: (messagePayload: ChatMessage) => void) { + this.on('notify:chat.message.add', callback); + } + + onMessageUpdate(callback: (messagePayload: ChatMessage) => void) { + this.on('notify:chat.message.update', callback); + } +} diff --git a/client/packages/tailchat-client-sdk/test/index.ts b/client/packages/tailchat-client-sdk/test/index.ts new file mode 100644 index 00000000..7a20d34e --- /dev/null +++ b/client/packages/tailchat-client-sdk/test/index.ts @@ -0,0 +1,20 @@ +import { TailchatWsClient } from '../src'; + +const HOST = process.env.HOST; +const APPID = process.env.APPID; +const APPSECRET = process.env.APPSECRET; + +if (!HOST || !APPID || !APPSECRET) { + console.log('require env: HOST, APPID, APPSECRET'); + process.exit(1); +} + +const client = new TailchatWsClient(HOST, APPID, APPSECRET); + +client.connect().then(async () => { + console.log('Login Success!'); + + client.onMessage((message) => { + console.log('Receive message', message); + }); +}); diff --git a/client/packages/tailchat-client-sdk/tsconfig.json b/client/packages/tailchat-client-sdk/tsconfig.json index 1907cc05..52a14ef3 100644 --- a/client/packages/tailchat-client-sdk/tsconfig.json +++ b/client/packages/tailchat-client-sdk/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { "target": "esnext", "lib": ["ESNext"], + "skipLibCheck": true, "outDir": "lib", "declaration": true, "declarationMap": true, diff --git a/client/shared/model/message.ts b/client/shared/model/message.ts index 13c2d562..5d66ecc8 100644 --- a/client/shared/model/message.ts +++ b/client/shared/model/message.ts @@ -1,31 +1,7 @@ import { request } from '../api/request'; +import type { ChatMessageReaction, ChatMessage } from 'tailchat-types'; -export interface ChatMessageReaction { - name: string; - author: string; -} - -export interface ChatMessage { - _id: string; - - content: string; - - author?: string; - - groupId?: string; - - converseId: string; - - reactions?: ChatMessageReaction[]; - - hasRecall?: boolean; - - meta?: Record; - - createdAt?: string; - - updatedAt?: string; -} +export { ChatMessageReaction, ChatMessage }; export interface SimpleMessagePayload { groupId?: string; diff --git a/packages/types/package.json b/packages/types/package.json index 63a26612..0bec33a1 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "tailchat-types", - "version": "1.0.1", + "version": "1.0.2", "description": "Tailchat model types", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 6da6ce21..4c44ec99 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,2 +1,3 @@ export * from './model/inbox'; export * from './model/user'; +export * from './model/message'; diff --git a/packages/types/src/model/message.ts b/packages/types/src/model/message.ts new file mode 100644 index 00000000..4bcf1546 --- /dev/null +++ b/packages/types/src/model/message.ts @@ -0,0 +1,26 @@ +export interface ChatMessageReaction { + name: string; + author: string; +} + +export interface ChatMessage { + _id: string; + + content: string; + + author?: string; + + groupId?: string; + + converseId: string; + + reactions?: ChatMessageReaction[]; + + hasRecall?: boolean; + + meta?: Record; + + createdAt?: string; + + updatedAt?: string; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c6612b18..e6343ba2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -490,6 +490,12 @@ importers: axios: specifier: ^1.3.2 version: 1.3.2 + socket.io-client: + specifier: ^4.7.1 + version: 4.7.1 + socket.io-msgpack-parser: + specifier: ^3.0.2 + version: 3.0.2 devDependencies: '@types/jest': specifier: ^29.5.1 @@ -500,6 +506,9 @@ importers: jest: specifier: 27.5.1 version: 27.5.1(ts-node@10.9.1) + tailchat-types: + specifier: '*' + version: link:../../../packages/types ts-jest: specifier: 27.1.4 version: 27.1.4(@babel/core@7.21.0)(@types/jest@29.5.1)(esbuild@0.15.18)(jest@27.5.1)(typescript@4.9.4) @@ -17612,10 +17621,29 @@ packages: - utf-8-validate dev: false + /engine.io-client@6.5.1: + resolution: {integrity: sha512-hE5wKXH8Ru4L19MbM1GgYV/2Qo54JSMh1rlJbfpa40bEWkCKNo3ol2eOtGmowcr+ysgbI7+SGL+by42Q3pt/Ng==} + dependencies: + '@socket.io/component-emitter': 3.1.0 + debug: 4.3.4(supports-color@9.2.2) + engine.io-parser: 5.1.0 + ws: 8.11.0 + xmlhttprequest-ssl: 2.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: false + /engine.io-parser@5.0.6: resolution: {integrity: sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==} engines: {node: '>=10.0.0'} + /engine.io-parser@5.1.0: + resolution: {integrity: sha512-enySgNiK5tyZFynt3z7iqBR+Bto9EVVVvDFuTT0ioHCGbzirZVGDGiQjZzEp8hWl6hd5FSVytJGuScX1C1C35w==} + engines: {node: '>=10.0.0'} + dev: false + /engine.io@6.2.0: resolution: {integrity: sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==} engines: {node: '>=10.0.0'} @@ -30531,6 +30559,20 @@ packages: - utf-8-validate dev: false + /socket.io-client@4.7.1: + resolution: {integrity: sha512-Qk3Xj8ekbnzKu3faejo4wk2MzXA029XppiXtTF/PkbTg+fcwaTw1PlDrTrrrU4mKoYC4dvlApOnSeyLCKwek2w==} + engines: {node: '>=10.0.0'} + dependencies: + '@socket.io/component-emitter': 3.1.0 + debug: 4.3.4(supports-color@9.2.2) + engine.io-client: 6.5.1 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: false + /socket.io-msgpack-parser@3.0.2: resolution: {integrity: sha512-1e76bJ1PCKi9H+JiYk+S29PBJvknHjQWM7Mtj0hjF2KxDA6b6rQxv3rTsnwBoz/haZOhlCDIMQvPATbqYeuMxg==} dependencies: @@ -31554,7 +31596,7 @@ packages: hark: 1.2.3 lodash: 4.17.21 mediasoup-client: 3.6.57 - socket.io-client: 4.6.2 + socket.io-client: 4.7.1 transitivePeerDependencies: - bufferutil - supports-color diff --git a/server/mixins/socketio.mixin.ts b/server/mixins/socketio.mixin.ts index 6ff27766..83635283 100644 --- a/server/mixins/socketio.mixin.ts +++ b/server/mixins/socketio.mixin.ts @@ -218,7 +218,7 @@ export const TcSocketIOService = ( // 检测是否允许调用 if (checkBlacklist(eventName)) { - const message = '不允许的请求'; + const message = 'Not allowed request'; this.logger.warn('[SocketIO]', '=>', message); cb({ result: false,