From 5a987b38b3561cf2b34616a826aa52d354aaa55e Mon Sep 17 00:00:00 2001 From: Shigma Date: Fri, 3 Jan 2025 20:21:11 +0800 Subject: [PATCH] feat(satori): support referrer --- adapters/satori/src/bot.ts | 15 ++++----------- adapters/satori/src/ws.ts | 4 ++-- packages/core/src/bot.ts | 10 +++++----- packages/core/src/index.ts | 4 ++-- packages/core/src/message.ts | 2 +- packages/core/src/session.ts | 2 ++ packages/protocol/src/index.ts | 30 +++++++++++++++++++++--------- packages/server/src/index.ts | 26 +++++++++----------------- 8 files changed, 46 insertions(+), 47 deletions(-) diff --git a/adapters/satori/src/bot.ts b/adapters/satori/src/bot.ts index 703747ba..d74b06b9 100644 --- a/adapters/satori/src/bot.ts +++ b/adapters/satori/src/bot.ts @@ -1,14 +1,5 @@ import { Bot, camelCase, Context, h, HTTP, JsonForm, snakeCase, Universal } from '@satorijs/core' -export function transformKey(source: any, callback: (key: string) => string) { - if (!source || typeof source !== 'object') return source - if (Array.isArray(source)) return source.map(value => transformKey(value, callback)) - return Object.fromEntries(Object.entries(source).map(([key, value]) => { - if (key.startsWith('_')) return [key, value] - return [callback(key), transformKey(value, callback)] - })) -} - function createInternal(bot: SatoriBot, prefix = '') { return new Proxy(() => {}, { apply(target, thisArg, args) { @@ -76,13 +67,15 @@ for (const [key, method] of Object.entries(Universal.Methods)) { session.elements = await session.transform(h.normalize(args[index])) if (await session.app.serial(session, 'before-send', session, args[3] ?? {})) return payload[field.name] = session.elements.join('') + } else if (field.name === 'referrer') { + payload[field.name] = args[index] } else { - payload[field.name] = transformKey(args[index], snakeCase) + payload[field.name] = Universal.transformKey(args[index], snakeCase) } } } this.logger.debug('[request]', key, payload) const result = await this.http.post('/v1/' + key, payload) - return transformKey(result, camelCase) + return Universal.transformKey(result, camelCase) } } diff --git a/adapters/satori/src/ws.ts b/adapters/satori/src/ws.ts index 49fb6be3..8ff8862f 100644 --- a/adapters/satori/src/ws.ts +++ b/adapters/satori/src/ws.ts @@ -1,5 +1,5 @@ import { Adapter, camelize, Context, HTTP, Logger, Schema, Time, Universal } from '@satorijs/core' -import { SatoriBot, transformKey } from './bot' +import { SatoriBot } from './bot' export class SatoriAdapter extends Adapter.WsClientBase> { static schema = true as any @@ -91,7 +91,7 @@ export class SatoriAdapter extends Adapter.WsClient let parsed: Universal.ServerPayload data = data.toString() try { - parsed = transformKey(JSON.parse(data), camelize) + parsed = Universal.transformKey(JSON.parse(data), camelize) } catch (error) { return this.logger.warn('cannot parse message', data) } diff --git a/packages/core/src/bot.ts b/packages/core/src/bot.ts index 8a3ad637..dfef71b7 100644 --- a/packages/core/src/bot.ts +++ b/packages/core/src/bot.ts @@ -23,7 +23,7 @@ export interface Bot extends Methods { export abstract class Bot { static reusable = true - static MessageEncoder?: new (bot: Bot, channelId: string, guildId?: string, options?: SendOptions) => MessageEncoder + static MessageEncoder?: new (bot: Bot, channelId: string, referrer?: any, options?: SendOptions) => MessageEncoder public [Service.tracker] = { associate: 'bot', @@ -191,13 +191,13 @@ export abstract class Bot { } } - async createMessage(channelId: string, content: h.Fragment, guildId?: string, options?: SendOptions) { + async createMessage(channelId: string, content: h.Fragment, referrer?: any, options?: SendOptions) { const { MessageEncoder } = this.constructor as typeof Bot - return new MessageEncoder(this, channelId, guildId, options).send(content) + return new MessageEncoder(this, channelId, referrer, options).send(content) } - async sendMessage(channelId: string, content: h.Fragment, guildId?: string, options?: SendOptions) { - const messages = await this.createMessage(channelId, content, guildId, options) + async sendMessage(channelId: string, content: h.Fragment, referrer?: any, options?: SendOptions) { + const messages = await this.createMessage(channelId, content, referrer, options) return messages.map(message => message.id) } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 80103deb..6fb023b1 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -4,7 +4,7 @@ import { Bot } from './bot' import { ExtractParams, InternalRequest, InternalRouter, JsonForm } from './internal' import { Session } from './session' import { HTTP } from '@cordisjs/plugin-http' -import { Response, SendOptions } from '@satorijs/protocol' +import { Meta, Response, SendOptions } from '@satorijs/protocol' import h from '@satorijs/element' h.warn = new Logger('element').warn @@ -277,7 +277,7 @@ export class Satori extends Service { return response } - toJSON(meta = false) { + toJSON(meta = false): Meta { return { logins: meta ? undefined : this.bots.map(bot => bot.toJSON()), proxyUrls: [...this.proxyUrls], diff --git a/packages/core/src/message.ts b/packages/core/src/message.ts index 82cdfbad..28861e20 100644 --- a/packages/core/src/message.ts +++ b/packages/core/src/message.ts @@ -14,7 +14,7 @@ export abstract class MessageEncoder { @@ -187,3 +188,4 @@ defineAccessor(Session.prototype, 'messageId', ['event', 'message', 'id']) defineAccessor(Session.prototype, 'operatorId', ['event', 'operator', 'id']) defineAccessor(Session.prototype, 'roleId', ['event', 'role', 'id']) defineAccessor(Session.prototype, 'quote', ['event', 'message', 'quote']) +defineAccessor(Session.prototype, 'referrer', ['event', 'referrer']) diff --git a/packages/protocol/src/index.ts b/packages/protocol/src/index.ts index 00246cfa..108ee40e 100644 --- a/packages/protocol/src/index.ts +++ b/packages/protocol/src/index.ts @@ -44,7 +44,7 @@ export const Methods: Dict = { 'channel.delete': Method('deleteChannel', ['channel_id']), 'channel.mute': Method('muteChannel', ['channel_id', 'guild_id', 'enable']), - 'message.create': Method('createMessage', ['channel_id', 'content']), + 'message.create': Method('createMessage', ['channel_id', 'content', 'referrer']), 'message.update': Method('editMessage', ['channel_id', 'message_id', 'content']), 'message.delete': Method('deleteMessage', ['channel_id', 'message_id']), 'message.get': Method('getMessage', ['channel_id', 'message_id']), @@ -100,8 +100,8 @@ export type Order = 'asc' | 'desc' export interface Methods { // message - createMessage(channelId: string, content: Element.Fragment, guildId?: string, options?: SendOptions): Promise - sendMessage(channelId: string, content: Element.Fragment, guildId?: string, options?: SendOptions): Promise + createMessage(channelId: string, content: Element.Fragment, referrer?: any, options?: SendOptions): Promise + sendMessage(channelId: string, content: Element.Fragment, referrer?: any, options?: SendOptions): Promise sendPrivateMessage(userId: string, content: Element.Fragment, guildId?: string, options?: SendOptions): Promise getMessage(channelId: string, messageId: string): Promise getMessageList(channelId: string, next?: string, direction?: Direction, limit?: number, order?: Order): Promise> @@ -214,13 +214,13 @@ export interface User { isBot?: boolean } -export interface Resource { +export interface Resource { attrs: (keyof K)[] children: (keyof K)[] content?: keyof K } -export function Resource(attrs: (keyof K)[], children: (keyof K)[] = [], content?: keyof K): Resource { +export function Resource(attrs: (keyof K)[] = [], children: (keyof K)[] = [], content?: keyof K): Resource { return { attrs, children, content } } @@ -270,6 +270,15 @@ export namespace Resource { } } +export function transformKey(source: any, callback: (key: string) => string) { + if (!source || typeof source !== 'object') return source + if (Array.isArray(source)) return source.map(value => transformKey(value, callback)) + return Object.fromEntries(Object.entries(source).map(([key, value]) => { + if (key.startsWith('_') || key === 'referrer') return [key, value] + return [callback(key), transformKey(value, callback)] + })) +} + export interface GuildMember { user?: User name?: string @@ -380,6 +389,7 @@ export interface Event { role?: GuildRole user?: User button?: Button + referrer: any _type?: string _data?: any /** @deprecated */ @@ -388,6 +398,11 @@ export interface Event { subsubtype?: string } +export interface Meta { + logins: Login[] + proxyUrls: string[] +} + export type MessageLike = Message | Event export const enum Opcode { @@ -420,10 +435,7 @@ export interface GatewayBody { token?: string sn?: number } - [Opcode.READY]: { - logins: Login[] - proxyUrls: string[] - } + [Opcode.READY]: Meta [Opcode.META]: { proxyUrls: string[] } diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index ba448327..020aa469 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -18,15 +18,6 @@ class Client { authorized = false } -function transformKey(source: any, callback: (key: string) => string) { - if (!source || typeof source !== 'object') return source - if (Array.isArray(source)) return source.map(value => transformKey(value, callback)) - return Object.fromEntries(Object.entries(source).map(([key, value]) => { - if (key.startsWith('_')) return [key, value] - return [callback(key), transformKey(value, callback)] - })) -} - const FILTER_HEADERS = [ 'host', 'authorization', @@ -99,10 +90,11 @@ class SatoriServer extends Service { const json = koa.request.body const args = method.fields.map(({ name }) => { - return transformKey(json[name], camelCase) + if (name === 'referrer') return json[name] + return Universal.transformKey(json[name], camelCase) }) const result = await bot[method.name](...args) - koa.body = transformKey(result, snakeCase) + koa.body = Universal.transformKey(result, snakeCase) koa.status = 200 }) @@ -171,13 +163,13 @@ class SatoriServer extends Service { ctx.server.post(path + '/v1/meta', async (koa) => { if (checkAuth(koa)) return - koa.body = transformKey(ctx.satori.toJSON(), snakeCase) + koa.body = Universal.transformKey(ctx.satori.toJSON(), snakeCase) koa.status = 200 }) ctx.server.post(path + '/v1/meta/webhook.create', async (koa) => { if (checkAuth(koa)) return - const webhook: SatoriServer.Webhook = transformKey(koa.request.body, camelCase) + const webhook: SatoriServer.Webhook = Universal.transformKey(koa.request.body, camelCase) const index = config.webhooks.findIndex(({ url }) => url === webhook.url) if (index === -1) { config.webhooks.push(webhook) @@ -230,12 +222,12 @@ class SatoriServer extends Service { client.authorized = true socket.send(JSON.stringify({ op: Universal.Opcode.READY, - body: transformKey(ctx.satori.toJSON(), snakeCase), + body: Universal.transformKey(ctx.satori.toJSON(), snakeCase), })) if (!payload.body?.sn) return for (const session of buffer) { if (session.id <= payload.body.sn) continue - dispatch(socket, transformKey(session.toJSON(), snakeCase)) + dispatch(socket, Universal.transformKey(session.toJSON(), snakeCase)) } } else if (payload.op === Universal.Opcode.PING) { socket.send(JSON.stringify({ @@ -272,12 +264,12 @@ class SatoriServer extends Service { } ctx.on('internal/session', (session) => { - const body = transformKey(session.toJSON(), snakeCase) + const body = Universal.transformKey(session.toJSON(), snakeCase) sendEvent(Universal.Opcode.EVENT, body) }) ctx.on('satori/meta', () => { - const body = transformKey(ctx.satori.toJSON(true), snakeCase) + const body = Universal.transformKey(ctx.satori.toJSON(true), snakeCase) sendEvent(Universal.Opcode.META, body) }) }