diff --git a/adapters/dingtalk/src/bot.ts b/adapters/dingtalk/src/bot.ts index 28a2764b..74b1a261 100644 --- a/adapters/dingtalk/src/bot.ts +++ b/adapters/dingtalk/src/bot.ts @@ -7,7 +7,7 @@ import { Internal } from './internal' const logger = new Logger('dingtalk') // https://open.dingtalk.com/document/orgapp/enterprise-created-chatbot -export class DingtalkBot extends Bot { +export class DingtalkBot extends Bot { static MessageEncoder = DingtalkMessageEncoder public oldHttp: Quester @@ -15,7 +15,7 @@ export class DingtalkBot extends Bot { public internal: Internal private refreshTokenTimer: NodeJS.Timeout - constructor(ctx: Context, config: DingtalkBot.Config) { + constructor(ctx: C, config: DingtalkBot.Config) { super(ctx, config) this.platform = 'dingtalk' this.selfId = config.appkey diff --git a/adapters/dingtalk/src/http.ts b/adapters/dingtalk/src/http.ts index f3016061..d574349a 100644 --- a/adapters/dingtalk/src/http.ts +++ b/adapters/dingtalk/src/http.ts @@ -4,14 +4,10 @@ import crypto from 'node:crypto' import { Message } from './types' import { decodeMessage } from './utils' -export class HttpServer extends Adapter { +export class HttpServer extends Adapter> { logger = new Logger('dingtalk') - constructor(ctx: Context, bot: DingtalkBot) { - super() - } - - async connect(bot: DingtalkBot) { + async connect(bot: DingtalkBot) { await bot.refreshToken() await bot.getLogin() diff --git a/adapters/dingtalk/src/message.ts b/adapters/dingtalk/src/message.ts index b50f0725..c2b8bb19 100644 --- a/adapters/dingtalk/src/message.ts +++ b/adapters/dingtalk/src/message.ts @@ -1,4 +1,4 @@ -import { Dict, h, MessageEncoder } from '@satorijs/satori' +import { Context, Dict, h, MessageEncoder } from '@satorijs/satori' import { DingtalkBot } from './bot' import FormData from 'form-data' import { SendMessageData } from './types' @@ -14,7 +14,7 @@ export const unescape = (val: string) => val .replace(/\u200b([\*_~`])/g, '$1') -export class DingtalkMessageEncoder extends MessageEncoder { +export class DingtalkMessageEncoder extends MessageEncoder> { buffer = '' /** diff --git a/adapters/dingtalk/src/utils.ts b/adapters/dingtalk/src/utils.ts index 52c87f19..702bee1e 100644 --- a/adapters/dingtalk/src/utils.ts +++ b/adapters/dingtalk/src/utils.ts @@ -1,8 +1,8 @@ -import { h, Session } from '@satorijs/satori' +import { Context, h } from '@satorijs/satori' import { Message } from './types' import { DingtalkBot } from './bot' -export async function decodeMessage(bot: DingtalkBot, body: Message): Promise { +export async function decodeMessage(bot: DingtalkBot, body: Message) { const session = bot.session() session.type = 'message' session.messageId = body.msgId diff --git a/adapters/dingtalk/src/ws.ts b/adapters/dingtalk/src/ws.ts index 6be9f177..2e375a75 100644 --- a/adapters/dingtalk/src/ws.ts +++ b/adapters/dingtalk/src/ws.ts @@ -1,8 +1,8 @@ -import { Adapter, Schema } from '@satorijs/satori' +import { Adapter, Context, Schema } from '@satorijs/satori' import { DingtalkBot } from './bot' import { decodeMessage } from './utils' -export class WsClient extends Adapter.WsClient { +export class WsClient extends Adapter.WsClient> { async prepare() { await this.bot.refreshToken() await this.bot.getLogin() diff --git a/adapters/discord/src/bot.ts b/adapters/discord/src/bot.ts index 359be4dd..f3414874 100644 --- a/adapters/discord/src/bot.ts +++ b/adapters/discord/src/bot.ts @@ -9,7 +9,7 @@ import { version } from '../package.json' const logger = new Logger('discord') -export class DiscordBot extends Bot { +export class DiscordBot extends Bot { static MessageEncoder = DiscordMessageEncoder public http: Quester @@ -18,7 +18,7 @@ export class DiscordBot extends Bot { public webhookLock: Record> = {} public commands: Universal.Command[] = [] - constructor(ctx: Context, config: DiscordBot.Config) { + constructor(ctx: C, config: DiscordBot.Config) { super(ctx, config) this.platform = 'discord' this.http = ctx.http.extend({ diff --git a/adapters/discord/src/message.ts b/adapters/discord/src/message.ts index e71c896b..fb4e04c8 100644 --- a/adapters/discord/src/message.ts +++ b/adapters/discord/src/message.ts @@ -1,4 +1,4 @@ -import { Dict, h, Logger, MessageEncoder, Quester, Schema, Universal } from '@satorijs/satori' +import { Context, Dict, h, Logger, MessageEncoder, Quester, Schema, Universal } from '@satorijs/satori' import FormData from 'form-data' import { DiscordBot } from './bot' import { ActionRow, Button, ButtonStyles, Channel, ComponentType, Message } from './types' @@ -18,7 +18,7 @@ class State { constructor(public type: 'message' | 'forward') { } } -export class DiscordMessageEncoder extends MessageEncoder { +export class DiscordMessageEncoder extends MessageEncoder> { private stack: State[] = [new State('message')] private buffer: string = '' private addition: Dict = {} diff --git a/adapters/discord/src/utils.ts b/adapters/discord/src/utils.ts index dd142200..f11689a2 100644 --- a/adapters/discord/src/utils.ts +++ b/adapters/discord/src/utils.ts @@ -1,4 +1,4 @@ -import { Dict, h, pick, Session, Universal, valueMap } from '@satorijs/satori' +import { Context, Dict, h, pick, Session, Universal, valueMap } from '@satorijs/satori' import { DiscordBot } from './bot' import * as Discord from './types' @@ -170,7 +170,7 @@ function setupReaction(session: Session, data: ReactionEvent) { session.content = id ? `${name}:${id}` : name } -export async function adaptSession(bot: DiscordBot, input: Discord.Gateway.Payload) { +export async function adaptSession(bot: DiscordBot, input: Discord.Gateway.Payload) { const session = bot.session() session.setInternal('discord', input) if (input.t === 'MESSAGE_CREATE') { diff --git a/adapters/discord/src/ws.ts b/adapters/discord/src/ws.ts index 6659792e..dd9ff498 100644 --- a/adapters/discord/src/ws.ts +++ b/adapters/discord/src/ws.ts @@ -1,11 +1,11 @@ -import { Adapter, Logger, Schema } from '@satorijs/satori' +import { Adapter, Context, Logger, Schema } from '@satorijs/satori' import { Gateway } from './types' import { adaptSession, decodeUser } from './utils' import { DiscordBot } from './bot' const logger = new Logger('discord') -export class WsClient extends Adapter.WsClient { +export class WsClient extends Adapter.WsClient> { _d = 0 _ping: NodeJS.Timeout _sessionId = '' diff --git a/adapters/kook/src/bot.ts b/adapters/kook/src/bot.ts index 68c18c36..491ba174 100644 --- a/adapters/kook/src/bot.ts +++ b/adapters/kook/src/bot.ts @@ -6,13 +6,13 @@ import { WsClient } from './ws' import { HttpServer } from './http' import { isDirectChannel, KookMessageEncoder } from './message' -export class KookBot extends Bot { +export class KookBot extends Bot { static MessageEncoder = KookMessageEncoder http: Quester internal: Kook.Internal - constructor(ctx: Context, config: T) { + constructor(ctx: C, config: T) { super(ctx, config) this.platform = 'kook' this.http = ctx.http.extend({ diff --git a/adapters/kook/src/http.ts b/adapters/kook/src/http.ts index 0f78b982..2541fcdf 100644 --- a/adapters/kook/src/http.ts +++ b/adapters/kook/src/http.ts @@ -4,8 +4,8 @@ import { adaptSession } from './utils' const logger = new Logger('kook') -export class HttpServer extends Adapter> { - constructor(ctx: Context, bot: KookBot) { +export class HttpServer extends Adapter> { + constructor(ctx: C, bot: KookBot) { super() let { path } = bot.config as HttpServer.Config path = sanitize(path) diff --git a/adapters/kook/src/message.ts b/adapters/kook/src/message.ts index cd2625ff..b2184b85 100644 --- a/adapters/kook/src/message.ts +++ b/adapters/kook/src/message.ts @@ -1,4 +1,4 @@ -import { h, MessageEncoder, Schema } from '@satorijs/satori' +import { Context, h, MessageEncoder, Schema } from '@satorijs/satori' import FormData from 'form-data' import { KookBot } from './bot' import * as Kook from './types' @@ -10,7 +10,7 @@ export function isDirectChannel(channelId: string) { return channelId.length > 30 } -export class KookMessageEncoder extends MessageEncoder { +export class KookMessageEncoder extends MessageEncoder> { private path: string private params = {} as Partial private additional = {} as Partial diff --git a/adapters/kook/src/utils.ts b/adapters/kook/src/utils.ts index 6388fd71..82e206bb 100644 --- a/adapters/kook/src/utils.ts +++ b/adapters/kook/src/utils.ts @@ -1,4 +1,4 @@ -import { Bot, h, hyphenate, isNullable, Session, Universal } from '@satorijs/satori' +import { Bot, Context, h, hyphenate, isNullable, Session, Universal } from '@satorijs/satori' import * as Kook from './types' export * from './types' @@ -147,7 +147,7 @@ function adaptReaction(body: Kook.NoticeBody, session: Session) { session['emoji'] = body.emoji.id } -export function adaptSession(bot: Bot, input: any) { +export function adaptSession(bot: Bot, input: any) { const session = bot.session() session.setInternal('kook', input) if (input.type === Kook.Type.system) { diff --git a/adapters/kook/src/ws.ts b/adapters/kook/src/ws.ts index d60ef335..ca83be2a 100644 --- a/adapters/kook/src/ws.ts +++ b/adapters/kook/src/ws.ts @@ -1,4 +1,4 @@ -import { Adapter, Logger, Schema, Time, Universal } from '@satorijs/satori' +import { Adapter, Context, Logger, Schema, Time, Universal } from '@satorijs/satori' import { KookBot } from './bot' import { adaptSession } from './utils' import { Payload, Signal } from './types' @@ -7,7 +7,7 @@ const logger = new Logger('kook') const heartbeatIntervals = [6, 2, 4] -export class WsClient extends Adapter.WsClient> { +export class WsClient extends Adapter.WsClient> { _sn = 0 _ping: NodeJS.Timeout _heartbeat: NodeJS.Timeout diff --git a/adapters/lark/src/bot.ts b/adapters/lark/src/bot.ts index 7930ba2d..1ff9a016 100644 --- a/adapters/lark/src/bot.ts +++ b/adapters/lark/src/bot.ts @@ -7,7 +7,7 @@ import * as Utils from './utils' const logger = new Logger('lark') -export class LarkBot extends Bot { +export class LarkBot extends Bot { static MessageEncoder = LarkMessageEncoder _token?: string @@ -16,7 +16,7 @@ export class LarkBot extends Bot { assetsQuester: Quester internal: Internal - constructor(ctx: Context, config: LarkBot.Config) { + constructor(ctx: C, config: LarkBot.Config) { super(ctx, config) // lark bot needs config.selfUrl to be set as it should be serve on a public url diff --git a/adapters/lark/src/http.ts b/adapters/lark/src/http.ts index d0b32670..ad63f1a9 100644 --- a/adapters/lark/src/http.ts +++ b/adapters/lark/src/http.ts @@ -8,10 +8,10 @@ import { adaptSession, Cipher } from './utils' const logger = new Logger('lark') -export class HttpServer extends Adapter { +export class HttpServer extends Adapter> { private ciphers: Record = {} - fork(ctx: Context, bot: FeishuBot) { + fork(ctx: C, bot: FeishuBot) { super.fork(ctx, bot) this._refreshCipher() diff --git a/adapters/lark/src/message.ts b/adapters/lark/src/message.ts index 6d964c77..284bad27 100644 --- a/adapters/lark/src/message.ts +++ b/adapters/lark/src/message.ts @@ -1,7 +1,7 @@ import { createReadStream } from 'fs' import internal from 'stream' -import { h, MessageEncoder, Quester } from '@satorijs/satori' +import { Context, h, MessageEncoder, Quester } from '@satorijs/satori' import FormData from 'form-data' import { LarkBot } from './bot' @@ -13,7 +13,7 @@ export interface Addition { type: MessageType } -export class LarkMessageEncoder extends MessageEncoder { +export class LarkMessageEncoder extends MessageEncoder> { private quote: string | undefined private content = '' private addition: Addition diff --git a/adapters/lark/src/utils.ts b/adapters/lark/src/utils.ts index dc47af59..e73d0780 100644 --- a/adapters/lark/src/utils.ts +++ b/adapters/lark/src/utils.ts @@ -1,6 +1,6 @@ import crypto from 'crypto' import { Channel, Guild, Message, User } from '@satorijs/protocol' -import { h, Session, trimSlash } from '@satorijs/satori' +import { Context, h, Session, trimSlash } from '@satorijs/satori' import { FeishuBot, LarkBot } from './bot' import { AllEvents, Events, Lark, Message as LarkMessage, MessageContentType, MessageType } from './types' @@ -68,7 +68,7 @@ export function adaptMessage(bot: FeishuBot, data: Events['im.message.receive_v1 return session } -export function adaptSession(bot: FeishuBot, body: AllEvents): Session { +export function adaptSession(bot: FeishuBot, body: AllEvents) { const session = bot.session() session.setInternal('lark', body) diff --git a/adapters/line/src/bot.ts b/adapters/line/src/bot.ts index 2108e690..6a8ab1d1 100644 --- a/adapters/line/src/bot.ts +++ b/adapters/line/src/bot.ts @@ -5,13 +5,13 @@ import { LineMessageEncoder } from './message' const logger = new Logger('line') -export class LineBot extends Bot { +export class LineBot extends Bot { static MessageEncoder = LineMessageEncoder public http: Quester public contentHttp: Quester public internal: Internal - constructor(ctx: Context, config: LineBot.Config) { + constructor(ctx: C, config: LineBot.Config) { super(ctx, config) if (!ctx.root.config.selfUrl) { logger.warn('selfUrl is not set, some features may not work') diff --git a/adapters/line/src/http.ts b/adapters/line/src/http.ts index e9c18abe..b5b15937 100644 --- a/adapters/line/src/http.ts +++ b/adapters/line/src/http.ts @@ -5,14 +5,14 @@ import { WebhookRequestBody } from './types' import { adaptSessions } from './utils' import internal from 'stream' -export class HttpServer extends Adapter { +export class HttpServer extends Adapter> { logger = new Logger('line') - constructor(ctx: Context, bot: LineBot) { + constructor(ctx: C, bot: LineBot) { super() } - async connect(bot: LineBot) { + async connect(bot: LineBot) { bot.ctx.router.post('/line', async (ctx) => { const sign = ctx.headers['x-line-signature']?.toString() const parsed = ctx.request.body as WebhookRequestBody diff --git a/adapters/line/src/message.ts b/adapters/line/src/message.ts index 7a15ad3f..8e7cc442 100644 --- a/adapters/line/src/message.ts +++ b/adapters/line/src/message.ts @@ -1,4 +1,4 @@ -import { Dict, h, MessageEncoder } from '@satorijs/satori' +import { Context, Dict, h, MessageEncoder } from '@satorijs/satori' import { LineBot } from './bot' import * as Line from './types' @@ -10,7 +10,7 @@ export const unescape = (val: string) => val .replace(/\u200b([\*_~`])/g, '$1') -export class LineMessageEncoder extends MessageEncoder { +export class LineMessageEncoder extends MessageEncoder> { buffer = '' blocks: Line.Message[] = [] sender: Line.Sender = {} diff --git a/adapters/mail/src/bot.ts b/adapters/mail/src/bot.ts index 714277b1..0bc4d763 100644 --- a/adapters/mail/src/bot.ts +++ b/adapters/mail/src/bot.ts @@ -1,57 +1,19 @@ -import { Bot, Context, Logger, Schema, Universal } from '@satorijs/satori' -import { ParsedMail } from 'mailparser' +import { Bot, Context, Schema } from '@satorijs/satori' import { IMAP, SMTP } from './mail' import { MailMessageEncoder } from './message' -import { dispatchSession } from './utils' -const logger = new Logger('adapter-mail') - -export class MailBot extends Bot { +export class MailBot extends Bot { static MessageEncoder = MailMessageEncoder - imap: IMAP - smtp: SMTP + internal: SMTP - constructor(ctx: Context, config: MailBot.Config) { + constructor(ctx: C, config: MailBot.Config) { super(ctx, config) this.selfId = config.selfId || config.username this.platform = 'mail' - } - - async start() { this.user.name = this.config.username - await super.start() - this.imap = new IMAP( - this.config, - this.online.bind(this), - this.onClose.bind(this), - this.onMail.bind(this), - this.onError.bind(this), - ) - this.smtp = new SMTP(this.config) - } - - async stop() { - await super.stop() - this.imap.stop() - } - - onError(error: Error) { - logger.error(error) - } - - onMail(mail: ParsedMail) { - dispatchSession(this, mail) - } - - onClose() { - if (!this.isActive) return - logger.info('IMAP disconnected, will reconnect in 3s...') - this.status = Universal.Status.RECONNECT - setTimeout(() => { - if (!this.isActive) return - this.imap.connect() - }, 3000) + this.internal = new SMTP(this.config) + this.ctx.plugin(IMAP) } } diff --git a/adapters/mail/src/mail.ts b/adapters/mail/src/mail.ts index 37bb579c..8e283aef 100644 --- a/adapters/mail/src/mail.ts +++ b/adapters/mail/src/mail.ts @@ -1,34 +1,44 @@ import NodeIMAP from 'node-imap' import { createTransport, Transporter } from 'nodemailer' -import { ParsedMail, simpleParser } from 'mailparser' +import { simpleParser } from 'mailparser' import { MailBot } from './bot' +import { Adapter, Context, Logger, Universal } from '@satorijs/satori' +import { dispatchSession } from './utils' -export class IMAP { - imap: NodeIMAP +const logger = new Logger('mail') - constructor( - public config: MailBot.Config, - public onReady: () => void, - public onClose: () => void, - public onMail: (mail: ParsedMail) => void, - public onError: (error: Error) => void, - ) { - this.connect() - } +export class IMAP extends Adapter> { + static reusable = true + + imap: NodeIMAP - connect() { + constructor(ctx: C, public bot: MailBot) { + super() this.imap = new NodeIMAP({ - user: this.config.username, - password: this.config.password, - host: this.config.imap.host, - port: this.config.imap.port, - tls: this.config.imap.tls, + user: bot.config.username, + password: bot.config.password, + host: bot.config.imap.host, + port: bot.config.imap.port, + tls: bot.config.imap.tls, }) + this.imap.on('error', (error) => { + logger.error(error) + }) + } + + async connect(bot: MailBot) { this.imap.on('ready', () => { this.imap.openBox('INBOX', false, this.inbox.bind(this)) }) - this.imap.on('error', this.onError) - this.imap.on('close', this.onClose) + this.imap.on('close', () => { + if (!bot.isActive) return + logger.info('IMAP disconnected, will reconnect in 3s...') + bot.status = Universal.Status.RECONNECT + setTimeout(() => { + if (!bot.isActive) return + this.imap.connect() + }, 3000) + }) this.imap.connect() } @@ -38,10 +48,10 @@ export class IMAP { inbox(error: Error) { if (error) { - this.onError(error) + logger.error(error) return } - this.onReady() + this.bot.online() this.scan() this.imap.on('mail', this.scan.bind(this)) } @@ -49,13 +59,13 @@ export class IMAP { scan() { this.imap.search(['UNSEEN'], (error, uids) => { if (error) { - this.onError(error) + logger.error(error) return } if (uids.length === 0) return this.imap.setFlags(uids, ['\\SEEN'], (error) => { - if (error) this.onError(error) + if (error) logger.error(error) }) // markSeen doesn't work @@ -64,10 +74,10 @@ export class IMAP { message.once('body', (stream) => { simpleParser(stream, (error, mail) => { if (error) { - this.onError(error) + logger.error(error) return } - this.onMail(mail) + dispatchSession(this.bot, mail) }) }) }) diff --git a/adapters/mail/src/message.ts b/adapters/mail/src/message.ts index bea0aa1d..c0b96471 100644 --- a/adapters/mail/src/message.ts +++ b/adapters/mail/src/message.ts @@ -1,4 +1,4 @@ -import { Element, MessageEncoder } from '@satorijs/satori' +import { Context, Element, MessageEncoder } from '@satorijs/satori' import { MailBot } from './bot' import { Attachment } from './mail' @@ -8,7 +8,7 @@ export function randomId() { return Array(8).fill(0).map(() => letters[Math.floor(Math.random() * letters.length)]).join('') } -export class MailMessageEncoder extends MessageEncoder { +export class MailMessageEncoder extends MessageEncoder> { buffer = '' reply: string attachments: Attachment[] = [] @@ -16,7 +16,7 @@ export class MailMessageEncoder extends MessageEncoder { async flush() { if (!this.buffer && this.attachments.length === 0) return - const messageId = await this.bot.smtp.send({ + const messageId = await this.bot.internal.send({ to: this.session.channelId, html: `
${this.buffer}
`, attachments: this.attachments, diff --git a/adapters/matrix/src/bot.ts b/adapters/matrix/src/bot.ts index c97ec91c..c4b49490 100644 --- a/adapters/matrix/src/bot.ts +++ b/adapters/matrix/src/bot.ts @@ -4,7 +4,7 @@ import { MatrixMessageEncoder } from './message' import * as Matrix from './types' import { adaptMessage, decodeUser, dispatchSession } from './utils' -export class MatrixBot extends Bot { +export class MatrixBot extends Bot { static MessageEncoder = MatrixMessageEncoder http: Quester @@ -13,7 +13,7 @@ export class MatrixBot extends Bot { rooms: string[] = [] internal: Matrix.Internal - constructor(ctx: Context, config: MatrixBot.Config) { + constructor(ctx: C, config: MatrixBot.Config) { super(ctx, config) this.id = config.id this.platform = 'matrix' diff --git a/adapters/matrix/src/http.ts b/adapters/matrix/src/http.ts index 4dc76e85..d8119c75 100644 --- a/adapters/matrix/src/http.ts +++ b/adapters/matrix/src/http.ts @@ -6,10 +6,10 @@ import { ClientEvent, M_ROOM_MEMBER } from './types' const logger = new Logger('matrix') -export class HttpAdapter extends Adapter { +export class HttpAdapter extends Adapter> { private txnId: string = null - public constructor(ctx: Context) { + public constructor(ctx: C) { super() ctx.router.all('/(.*)', (koaCtx, next) => { const match = this.bots.filter(bot => koaCtx.path.startsWith(bot.config.path + '/')) diff --git a/adapters/matrix/src/message.ts b/adapters/matrix/src/message.ts index 50063239..9a7818e2 100644 --- a/adapters/matrix/src/message.ts +++ b/adapters/matrix/src/message.ts @@ -1,7 +1,7 @@ -import { MessageEncoder, segment, Universal } from '@satorijs/satori' +import { Context, MessageEncoder, segment, Universal } from '@satorijs/satori' import { MatrixBot } from './bot' -export class MatrixMessageEncoder extends MessageEncoder { +export class MatrixMessageEncoder extends MessageEncoder> { private buffer: string = '' private reply: Universal.Message = null diff --git a/adapters/qq/src/bot/guild.ts b/adapters/qq/src/bot/guild.ts index 04ec9507..76e4bd16 100644 --- a/adapters/qq/src/bot/guild.ts +++ b/adapters/qq/src/bot/guild.ts @@ -10,14 +10,14 @@ export namespace QQGuildBot { } } -export class QQGuildBot extends Bot { +export class QQGuildBot extends Bot { declare parent: QQBot hidden = true public internal: GuildInternal public http: Quester static MessageEncoder = QQGuildMessageEncoder - constructor(ctx: Context, config: QQGuildBot.Config) { + constructor(ctx: C, config: QQGuildBot.Config) { super(ctx, config) this.parent = config.parent this.parent.guildBot = this diff --git a/adapters/qq/src/bot/index.ts b/adapters/qq/src/bot/index.ts index ba9b2553..2dee4c76 100644 --- a/adapters/qq/src/bot/index.ts +++ b/adapters/qq/src/bot/index.ts @@ -10,9 +10,9 @@ interface GetAppAccessTokenResult { expires_in: number } -export class QQBot extends Bot { +export class QQBot extends Bot { static MessageEncoder = QQMessageEncoder - public guildBot: QQGuildBot + public guildBot: QQGuildBot internal: GroupInternal groupHttp: Quester @@ -21,7 +21,7 @@ export class QQBot extends Bot { private _token: string private _timer: NodeJS.Timeout - constructor(ctx: Context, config: QQBot.Config) { + constructor(ctx: C, config: QQBot.Config) { super(ctx, config) this.platform = 'qq' let endpoint = config.endpoint diff --git a/adapters/qq/src/message.ts b/adapters/qq/src/message.ts index d8f74437..ea6716e2 100644 --- a/adapters/qq/src/message.ts +++ b/adapters/qq/src/message.ts @@ -1,5 +1,5 @@ import * as QQ from './types' -import { Dict, h, MessageEncoder } from '@satorijs/satori' +import { Context, Dict, h, MessageEncoder } from '@satorijs/satori' import { QQBot } from './bot' import FormData from 'form-data' import { escape } from '@satorijs/element' @@ -9,7 +9,7 @@ export const escapeMarkdown = (val: string) => val .replace(/([\\`*_[\*_~`\]\-(#!>])/g, '\\$&') -export class QQGuildMessageEncoder extends MessageEncoder { +export class QQGuildMessageEncoder extends MessageEncoder> { private content: string = '' private file: Buffer private filename: string @@ -146,7 +146,7 @@ export class QQGuildMessageEncoder extends MessageEncoder { const MSG_TIMEOUT = 5 * 60 * 1000 - 2000// 5 mins -export class QQMessageEncoder extends MessageEncoder { +export class QQMessageEncoder extends MessageEncoder> { private content: string = '' private useMarkdown = false private rows: QQ.Button[][] = [] diff --git a/adapters/qq/src/utils.ts b/adapters/qq/src/utils.ts index b6bf0477..271315a8 100644 --- a/adapters/qq/src/utils.ts +++ b/adapters/qq/src/utils.ts @@ -1,4 +1,4 @@ -import { Bot, h, Session, Universal } from '@satorijs/satori' +import { Bot, Context, h, Session, Universal } from '@satorijs/satori' import * as QQ from './types' import { QQBot } from './bot' import { unescape } from '@satorijs/element' @@ -106,7 +106,7 @@ export function setupReaction(session: Session, data: QQ.MessageReaction) { return session } -export async function adaptSession(bot: QQBot, input: QQ.DispatchPayload) { +export async function adaptSession(bot: QQBot, input: QQ.DispatchPayload) { let session = bot.session() if (!['GROUP_AT_MESSAGE_CREATE', 'C2C_MESSAGE_CREATE', 'FRIEND_ADD', 'FRIEND_DEL', diff --git a/adapters/qq/src/ws.ts b/adapters/qq/src/ws.ts index ebede986..57c4f6c5 100644 --- a/adapters/qq/src/ws.ts +++ b/adapters/qq/src/ws.ts @@ -1,11 +1,11 @@ -import { Adapter, Logger, Schema } from '@satorijs/satori' +import { Adapter, Context, Logger, Schema } from '@satorijs/satori' import { QQBot } from './bot' import { Opcode, Payload } from './types' import { adaptSession, decodeUser } from './utils' const logger = new Logger('qq') -export class WsClient extends Adapter.WsClient { +export class WsClient extends Adapter.WsClient> { _sessionId = '' _s: number = null _ping: NodeJS.Timeout diff --git a/adapters/satori/src/bot.ts b/adapters/satori/src/bot.ts index c833d829..c6fe78ef 100644 --- a/adapters/satori/src/bot.ts +++ b/adapters/satori/src/bot.ts @@ -23,11 +23,11 @@ function createInternal(bot: SatoriBot, prefix = '') { }) } -export class SatoriBot extends Bot { +export class SatoriBot extends Bot { public http: Quester public internal = createInternal(this) - constructor(ctx: Context, config: Universal.Login) { + constructor(ctx: C, config: Universal.Login) { super(ctx, config) Object.assign(this, config) } diff --git a/adapters/satori/src/ws.ts b/adapters/satori/src/ws.ts index 0edd07b7..39b25a0c 100644 --- a/adapters/satori/src/ws.ts +++ b/adapters/satori/src/ws.ts @@ -3,7 +3,7 @@ import { SatoriBot, transformKey } from './bot' const logger = new Logger('satori') -export class SatoriAdapter extends Adapter.WsClientBase { +export class SatoriAdapter extends Adapter.WsClientBase> { static schema = true static reusable = true @@ -13,7 +13,7 @@ export class SatoriAdapter extends Adapter.WsClientBase { private sequence?: number private timeout?: NodeJS.Timeout - constructor(public ctx: Context, public config: SatoriAdapter.Config) { + constructor(public ctx: C, public config: SatoriAdapter.Config) { super(ctx, config) this.http = ctx.http.extend({ endpoint: config.endpoint, diff --git a/adapters/slack/src/bot.ts b/adapters/slack/src/bot.ts index 4456c804..2056b6d3 100644 --- a/adapters/slack/src/bot.ts +++ b/adapters/slack/src/bot.ts @@ -7,12 +7,12 @@ import { GenericMessageEvent, SlackChannel, SlackTeam, SlackUser } from './types import FormData from 'form-data' import { Internal, Token } from './types/internal' -export class SlackBot extends Bot { +export class SlackBot extends Bot { static MessageEncoder = SlackMessageEncoder public http: Quester public internal: Internal - constructor(ctx: Context, config: T) { + constructor(ctx: C, config: T) { super(ctx, config) this.platform = 'slack' this.http = ctx.http.extend(config) diff --git a/adapters/slack/src/http.ts b/adapters/slack/src/http.ts index ec9af821..f02100a2 100644 --- a/adapters/slack/src/http.ts +++ b/adapters/slack/src/http.ts @@ -1,13 +1,13 @@ -import { Adapter, Logger, Schema } from '@satorijs/satori' +import { Adapter, Context, Logger, Schema } from '@satorijs/satori' import { SlackBot } from './bot' import crypto from 'node:crypto' import { EnvelopedEvent, SlackEvent, SocketEvent } from './types' import { adaptSession } from './utils' -export class HttpServer extends Adapter { +export class HttpServer extends Adapter> { logger = new Logger('slack') - async connect(bot: SlackBot) { + async connect(bot: SlackBot) { const { signing } = bot.config await bot.getLogin() bot.ctx.router.post('/slack', async (ctx) => { diff --git a/adapters/slack/src/message.ts b/adapters/slack/src/message.ts index 20f393ef..6238f67d 100644 --- a/adapters/slack/src/message.ts +++ b/adapters/slack/src/message.ts @@ -1,4 +1,4 @@ -import { h, MessageEncoder } from '@satorijs/satori' +import { Context, h, MessageEncoder } from '@satorijs/satori' import { SlackBot } from './bot' import FormData from 'form-data' import { adaptMessage, adaptSentAsset } from './utils' @@ -21,7 +21,7 @@ export const unescape = (val: string) => .replace(/@\u200Beveryone/g, () => '@everyone') .replace(/@\u200Bhere/g, () => '@here') -export class SlackMessageEncoder extends MessageEncoder { +export class SlackMessageEncoder extends MessageEncoder> { buffer = '' thread_ts = null elements: any[] = [] diff --git a/adapters/slack/src/utils.ts b/adapters/slack/src/utils.ts index ce583d27..53464f21 100644 --- a/adapters/slack/src/utils.ts +++ b/adapters/slack/src/utils.ts @@ -1,4 +1,4 @@ -import { Element, h, Session, Universal } from '@satorijs/satori' +import { Context, Element, h, Session, Universal } from '@satorijs/satori' import { SlackBot } from './bot' // eslint-disable-next-line max-len import { EnvelopedEvent, GenericMessageEvent, MessageChangedEvent, MessageDeletedEvent, ReactionAddedEvent, ReactionRemovedEvent, RichText, RichTextBlock, SlackEvent, SlackUser } from './types/events' @@ -160,7 +160,7 @@ function setupReaction(session: Session, data: EnvelopedEvent) { +export async function adaptSession(bot: SlackBot, payload: EnvelopedEvent) { const session = bot.session() // https://api.slack.com/events if (payload.event.type === 'message') { diff --git a/adapters/slack/src/ws.ts b/adapters/slack/src/ws.ts index 4478ba7e..a4a88173 100644 --- a/adapters/slack/src/ws.ts +++ b/adapters/slack/src/ws.ts @@ -1,11 +1,11 @@ -import { Adapter, Logger, Schema } from '@satorijs/satori' +import { Adapter, Context, Logger, Schema } from '@satorijs/satori' import { SlackBot } from './bot' import { adaptSession } from './utils' import { SocketEvent } from './types/events' const logger = new Logger('slack') -export class WsClient extends Adapter.WsClient> { +export class WsClient extends Adapter.WsClient> { async prepare() { await this.bot.getLogin() const data = await this.bot.request('POST', '/apps.connections.open', {}, {}, true) diff --git a/adapters/telegram/src/bot.ts b/adapters/telegram/src/bot.ts index 4c2362f8..85a36cc8 100644 --- a/adapters/telegram/src/bot.ts +++ b/adapters/telegram/src/bot.ts @@ -26,7 +26,7 @@ export interface TelegramResponse { result: any } -export class TelegramBot extends Bot { +export class TelegramBot extends Bot { static MessageEncoder = TelegramMessageEncoder http: Quester @@ -35,7 +35,7 @@ export class TelegramBot exte local?: boolean server?: string - constructor(ctx: Context, config: T) { + constructor(ctx: C, config: T) { super(ctx, config) this.platform = 'telegram' this.selfId = config.token.split(':')[0] diff --git a/adapters/telegram/src/message.ts b/adapters/telegram/src/message.ts index c59a693e..2a5181e3 100644 --- a/adapters/telegram/src/message.ts +++ b/adapters/telegram/src/message.ts @@ -1,4 +1,4 @@ -import { Dict, h, MessageEncoder, Universal } from '@satorijs/satori' +import { Context, Dict, h, MessageEncoder, Universal } from '@satorijs/satori' import FormData from 'form-data' import { TelegramBot } from './bot' import * as Telegram from './utils' @@ -29,13 +29,13 @@ async function appendAsset(bot: TelegramBot, form: FormData, element: h): Promis const supportedElements = ['b', 'strong', 'i', 'em', 'u', 'ins', 's', 'del', 'a'] -export class TelegramMessageEncoder extends MessageEncoder { +export class TelegramMessageEncoder extends MessageEncoder> { private asset: h = null private payload: Dict private mode: RenderMode = 'default' private rows: Telegram.InlineKeyboardButton[][] = [] - constructor(bot: TelegramBot, channelId: string, guildId?: string, options?: Universal.SendOptions) { + constructor(bot: TelegramBot, channelId: string, guildId?: string, options?: Universal.SendOptions) { super(bot, channelId, guildId, options) const chat_id = guildId || channelId this.payload = { chat_id, parse_mode: 'html', caption: '' } diff --git a/adapters/telegram/src/polling.ts b/adapters/telegram/src/polling.ts index ba9df414..71787603 100644 --- a/adapters/telegram/src/polling.ts +++ b/adapters/telegram/src/polling.ts @@ -1,16 +1,16 @@ -import { Adapter, Logger, Quester, Schema, Time, Universal } from '@satorijs/satori' +import { Adapter, Context, Logger, Quester, Schema, Time, Universal } from '@satorijs/satori' import { TelegramBot } from './bot' import { handleUpdate } from './utils' const logger = new Logger('telegram') -export class HttpPolling extends Adapter { +export class HttpPolling extends Adapter> { static reusable = true private offset = 0 private timeout: NodeJS.Timeout - async connect(bot: TelegramBot) { + async connect(bot: TelegramBot) { bot.initialize(async () => { let _retryCount = 0 let _initial = true diff --git a/adapters/telegram/src/server.ts b/adapters/telegram/src/server.ts index e2d0b43d..99bed39b 100644 --- a/adapters/telegram/src/server.ts +++ b/adapters/telegram/src/server.ts @@ -1,12 +1,12 @@ -import { Adapter, Logger, sanitize, Schema, trimSlash } from '@satorijs/satori' +import { Adapter, Context, Logger, sanitize, Schema, trimSlash } from '@satorijs/satori' import { TelegramBot } from './bot' import { handleUpdate } from './utils' import * as Telegram from './types' const logger = new Logger('telegram') -export class HttpServer extends Adapter { - async connect(bot: TelegramBot) { +export class HttpServer extends Adapter> { + async connect(bot: TelegramBot) { let { token, path, selfUrl } = bot.config path = sanitize(path || '/telegram') if (selfUrl) { diff --git a/adapters/wechat-official/src/bot.ts b/adapters/wechat-official/src/bot.ts index 1188c10b..dda64281 100644 --- a/adapters/wechat-official/src/bot.ts +++ b/adapters/wechat-official/src/bot.ts @@ -3,13 +3,14 @@ import { HttpServer } from './http' import { WechatOfficialMessageEncoder } from './message' // import { Internal } from './types/internal' -export class WechatOfficialBot extends Bot { +export class WechatOfficialBot extends Bot { static MessageEncoder = WechatOfficialMessageEncoder http: Quester // internal: Internal refreshTokenTimer: NodeJS.Timeout logger = new Logger('wo') - constructor(ctx: Context, config: WechatOfficialBot.Config) { + + constructor(ctx: C, config: WechatOfficialBot.Config) { super(ctx, config) this.platform = 'wechat-official' this.selfId = config.account diff --git a/adapters/wechat-official/src/http.ts b/adapters/wechat-official/src/http.ts index d9138a2a..cc5dd9b1 100644 --- a/adapters/wechat-official/src/http.ts +++ b/adapters/wechat-official/src/http.ts @@ -1,11 +1,11 @@ -import { Adapter } from '@satorijs/satori' +import { Adapter, Context } from '@satorijs/satori' import { WechatOfficialBot } from './bot' import xml2js from 'xml2js' import { Message } from './types' import { decodeMessage } from './utils' import { decrypt, encrypt, getSignature } from '@wecom/crypto' -export class HttpServer extends Adapter { +export class HttpServer extends Adapter> { constructor() { super() } diff --git a/adapters/wechat-official/src/message.ts b/adapters/wechat-official/src/message.ts index f8fc9f38..cd70a3f7 100644 --- a/adapters/wechat-official/src/message.ts +++ b/adapters/wechat-official/src/message.ts @@ -1,11 +1,11 @@ -import { h, MessageEncoder } from '@satorijs/satori' +import { Context, h, MessageEncoder } from '@satorijs/satori' import { WechatOfficialBot } from './bot' import FormData from 'form-data' import xml2js from 'xml2js' import { SendMessage } from './types' // https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Passive_user_reply_message.html -export class WechatOfficialMessageEncoder extends MessageEncoder { +export class WechatOfficialMessageEncoder extends MessageEncoder> { buffer = '' sent = false diff --git a/adapters/wechat-official/src/utils.ts b/adapters/wechat-official/src/utils.ts index 2a91c3b4..d61aa6b1 100644 --- a/adapters/wechat-official/src/utils.ts +++ b/adapters/wechat-official/src/utils.ts @@ -1,8 +1,8 @@ import { Message } from './types' import { WechatOfficialBot } from './bot' -import { h } from '@satorijs/satori' +import { Context, h } from '@satorijs/satori' -export async function decodeMessage(bot: WechatOfficialBot, message: Message) { +export async function decodeMessage(bot: WechatOfficialBot, message: Message) { const session = bot.session() // https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html session.timestamp = message.CreateTime * 1000 diff --git a/adapters/wecom/src/bot.ts b/adapters/wecom/src/bot.ts index 40806db7..c6a8d958 100644 --- a/adapters/wecom/src/bot.ts +++ b/adapters/wecom/src/bot.ts @@ -2,7 +2,7 @@ import { Bot, Context, Logger, Quester, Schema, Universal } from '@satorijs/sato import { HttpServer } from './http' import { WecomMessageEncoder } from './message' -export class WecomBot extends Bot { +export class WecomBot extends Bot { static MessageEncoder = WecomMessageEncoder http: Quester @@ -10,7 +10,7 @@ export class WecomBot extends Bot { refreshTokenTimer: NodeJS.Timeout logger = new Logger('wecom') - constructor(ctx: Context, config: WecomBot.Config) { + constructor(ctx: C, config: WecomBot.Config) { super(ctx, config) this.selfId = config.agentId this.platform = 'wecom' diff --git a/adapters/wecom/src/http.ts b/adapters/wecom/src/http.ts index 124b2a60..99d16484 100644 --- a/adapters/wecom/src/http.ts +++ b/adapters/wecom/src/http.ts @@ -1,11 +1,11 @@ -import { Adapter } from '@satorijs/satori' +import { Adapter, Context } from '@satorijs/satori' import { WecomBot } from './bot' import xml2js from 'xml2js' import { Message } from './types' import { decodeMessage } from './utils' import { decrypt, getSignature } from '@wecom/crypto' -export class HttpServer extends Adapter { +export class HttpServer extends Adapter> { constructor() { super() } diff --git a/adapters/wecom/src/message.ts b/adapters/wecom/src/message.ts index 145b3c3e..f1aaddbe 100644 --- a/adapters/wecom/src/message.ts +++ b/adapters/wecom/src/message.ts @@ -1,10 +1,10 @@ -import { h, MessageEncoder } from '@satorijs/satori' +import { Context, h, MessageEncoder } from '@satorijs/satori' import { WecomBot } from './bot' import FormData from 'form-data' /** https://developer.work.weixin.qq.com/document/path/90236#%E6%94%AF%E6%8C%81%E7%9A%84markdown%E8%AF%AD%E6%B3%95 */ -export class WecomMessageEncoder extends MessageEncoder { +export class WecomMessageEncoder extends MessageEncoder> { buffer = '' upsertSend(msgId: string) { const session = this.bot.session() diff --git a/adapters/wecom/src/utils.ts b/adapters/wecom/src/utils.ts index e69763a0..83a5dff8 100644 --- a/adapters/wecom/src/utils.ts +++ b/adapters/wecom/src/utils.ts @@ -1,8 +1,8 @@ import { Message } from './types' import { WecomBot } from './bot' -import { h } from '@satorijs/satori' +import { Context, h } from '@satorijs/satori' -export async function decodeMessage(bot: WecomBot, message: Message) { +export async function decodeMessage(bot: WecomBot, message: Message) { const session = bot.session() session.timestamp = message.CreateTime * 1000 // session.wechatOfficial = message diff --git a/adapters/whatsapp/src/adapter.ts b/adapters/whatsapp/src/adapter.ts index 465fe0de..4f359a27 100644 --- a/adapters/whatsapp/src/adapter.ts +++ b/adapters/whatsapp/src/adapter.ts @@ -92,13 +92,11 @@ class HttpServer { } } -export class WhatsAppAdapter extends Adapter { +export class WhatsAppAdapter extends Adapter> { static schema = true static reusable = true - public bots: WhatsAppBot[] = [] - - constructor(ctx: Context, public config: WhatsAppAdapter.Config) { + constructor(ctx: C, public config: WhatsAppAdapter.Config) { super() ctx.plugin(HttpServer, this) diff --git a/adapters/whatsapp/src/bot.ts b/adapters/whatsapp/src/bot.ts index 73218cf8..474e32e9 100644 --- a/adapters/whatsapp/src/bot.ts +++ b/adapters/whatsapp/src/bot.ts @@ -2,16 +2,12 @@ import { Bot, Context, Quester } from '@satorijs/satori' import { WhatsAppMessageEncoder } from './message' import { Internal } from './internal' -export class WhatsAppBot extends Bot { +export class WhatsAppBot extends Bot { static MessageEncoder = WhatsAppMessageEncoder public internal: Internal public http: Quester - - constructor(ctx: Context, config: {}) { - super(ctx, config) - this.platform = 'whatsapp' - } + public platform = 'whatsapp' async createReaction(channelId: string, messageId: string, emoji: string): Promise { await this.internal.messageReaction(this.selfId, channelId, messageId, emoji) diff --git a/adapters/whatsapp/src/message.ts b/adapters/whatsapp/src/message.ts index 8c2acacd..cb4856c2 100644 --- a/adapters/whatsapp/src/message.ts +++ b/adapters/whatsapp/src/message.ts @@ -1,4 +1,4 @@ -import { Dict, h, MessageEncoder } from '@satorijs/satori' +import { Context, Dict, h, MessageEncoder } from '@satorijs/satori' import { WhatsAppBot } from './bot' import FormData from 'form-data' import { SendMessage } from './types' @@ -25,7 +25,7 @@ const SUPPORTED_MEDIA = [ 'video/3gpp', ] -export class WhatsAppMessageEncoder extends MessageEncoder { +export class WhatsAppMessageEncoder extends MessageEncoder> { private buffer = '' quoteId: string = null diff --git a/adapters/zulip/src/bot.ts b/adapters/zulip/src/bot.ts index ff4cad49..487cdd78 100644 --- a/adapters/zulip/src/bot.ts +++ b/adapters/zulip/src/bot.ts @@ -6,13 +6,13 @@ import { ZulipMessageEncoder } from './message' import { version } from '../package.json' import { decodeGuild, decodeMessage, decodeUser } from './utils' -export class ZulipBot extends Bot { +export class ZulipBot extends Bot { static MessageEncoder = ZulipMessageEncoder public http: Quester public logger: Logger public internal: Internal - constructor(ctx: Context, config: ZulipBot.Config) { + constructor(ctx: C, config: ZulipBot.Config) { super(ctx, config) this.platform = 'zulip' diff --git a/adapters/zulip/src/message.ts b/adapters/zulip/src/message.ts index 787a8d3f..595e0040 100644 --- a/adapters/zulip/src/message.ts +++ b/adapters/zulip/src/message.ts @@ -1,4 +1,4 @@ -import { h, MessageEncoder } from '@satorijs/satori' +import { Context, h, MessageEncoder } from '@satorijs/satori' import { ZulipBot } from './bot' import FormData from 'form-data' import { by_stream_topic_url, encodeHashComponent } from './utils' @@ -13,7 +13,7 @@ export const unescape = (val: string) => .replace(/^( )+/g, (match) => Array(match.length + 1).join(' ')) .replace(/\u200b([\*_~`\->[\](#!@])/g, '$1') -export class ZulipMessageEncoder extends MessageEncoder { +export class ZulipMessageEncoder extends MessageEncoder> { buffer: string = '' async flush() { diff --git a/adapters/zulip/src/polling.ts b/adapters/zulip/src/polling.ts index c4d64c18..d67e366b 100644 --- a/adapters/zulip/src/polling.ts +++ b/adapters/zulip/src/polling.ts @@ -1,8 +1,8 @@ -import { Adapter, Quester, Schema, Time, Universal } from '@satorijs/satori' +import { Adapter, Context, Quester, Schema, Time, Universal } from '@satorijs/satori' import { ZulipBot } from './bot' import { adaptSession } from './utils' -export class HttpPolling extends Adapter { +export class HttpPolling extends Adapter> { static reusable = true private timeout: NodeJS.Timeout diff --git a/packages/core/src/adapter.ts b/packages/core/src/adapter.ts index c10483a4..29cdd76b 100644 --- a/packages/core/src/adapter.ts +++ b/packages/core/src/adapter.ts @@ -7,15 +7,15 @@ import Logger from 'reggol' const logger = new Logger('adapter') -export abstract class Adapter { +export abstract class Adapter = Bot> { static schema = false - public bots: T[] = [] + public bots: B[] = [] - async connect(bot: T) {} - async disconnect(bot: T) {} + async connect(bot: B) {} + async disconnect(bot: B) {} - fork(ctx: Context, bot: T) { + fork(ctx: Context, bot: B) { bot.adapter = this this.bots.push(bot) ctx.on('dispose', () => { @@ -37,7 +37,7 @@ export namespace Adapter { retryLazy: Schema.natural().role('ms').description('连接关闭后的重试时间间隔。').default(Time.minute), }).description('连接设置') - export abstract class WsClientBase extends Adapter { + export abstract class WsClientBase> extends Adapter { protected socket: WebSocket protected abstract prepare(): Awaitable @@ -102,11 +102,11 @@ export namespace Adapter { } } - export abstract class WsClient> extends WsClientBase { + export abstract class WsClient> extends WsClientBase { static reusable = true static Config = WsClientConfig - constructor(ctx: Context, public bot: T) { + constructor(ctx: Context, public bot: B) { super(ctx, bot.config) bot.adapter = this } @@ -120,11 +120,11 @@ export namespace Adapter { this.bot.error = error } - async connect(bot: T) { + async connect(bot: B) { this.start() } - async disconnect(bot: T) { + async disconnect(bot: B) { this.stop() } } diff --git a/packages/core/src/bot.ts b/packages/core/src/bot.ts index b355761d..aa7686f5 100644 --- a/packages/core/src/bot.ts +++ b/packages/core/src/bot.ts @@ -2,7 +2,7 @@ import { Dict, pick, remove } from 'cosmokit' import { Context, Fragment } from '.' import { Adapter } from './adapter' import { MessageEncoder } from './message' -import { defineAccessor, Session } from './session' +import { defineAccessor } from './session' import { Event, List, Login, Methods, SendOptions, Status, User } from '@satorijs/protocol' const eventAliases = [ @@ -15,23 +15,23 @@ export interface Bot extends Methods { internal: any } -export abstract class Bot implements Login { +export abstract class Bot implements Login { static reusable = true - static filter = false static MessageEncoder?: new (bot: Bot, channelId: string, guildId?: string, options?: SendOptions) => MessageEncoder public user = {} as User public isBot = true public hidden = false public platform: string - public adapter?: Adapter + public adapter?: Adapter public error?: Error public callbacks: Dict = {} + // Same as `this.ctx`, but with a more specific type. protected context: Context protected _status: Status = Status.OFFLINE - constructor(public ctx: Context, public config: T) { + constructor(public ctx: C, public config: T) { this.internal = null this.context = ctx ctx.bots.push(this) @@ -44,7 +44,7 @@ export abstract class Bot implements Login { ctx.on('dispose', () => this.dispose()) - ctx.on('interaction/button', (session: Session) => { + ctx.on('interaction/button', (session: C[typeof Context.session]) => { const cb = this.callbacks[session.event.button.id] if (cb) cb(session) }) @@ -129,11 +129,12 @@ export abstract class Bot implements Login { return `${this.platform}:${this.selfId}` } - session(event: Partial = {}) { + session(event: Partial = {}): C[typeof Context.session] { + const { Session } = this.ctx.constructor as typeof Context return new Session(this, event) } - dispatch(session: Session) { + dispatch(session: C[typeof Context.session]) { if (!this.ctx.lifecycle.isActive) return let events = [session.type] for (const aliases of eventAliases) { @@ -165,11 +166,11 @@ export abstract class Bot implements Login { return this.sendMessage(id, content, null, options) } - async supports(name: string, session: Partial = {}) { + async supports(name: string, session: Partial = {}) { return !!this[Methods[name]?.name] } - async checkPermission(name: string, session: Partial) { + async checkPermission(name: string, session: Partial) { if (name.startsWith('bot.')) { return this.supports(name.slice(4), session) } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index f6f1fedc..d9e94605 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -5,7 +5,7 @@ import { Session } from './session' import Schema from 'schemastery' import Logger from 'reggol' import Quester from 'cordis-axios' -import { SendOptions } from '@satorijs/protocol' +import { Event, SendOptions } from '@satorijs/protocol' import h from '@satorijs/element' h.warn = new Logger('element').warn @@ -46,7 +46,7 @@ Quester.createConfig = function createConfig(this, endpoint) { }).description('请求设置') } -export type Component = h.Render, Session> +export type Component = h.Render, S> export namespace Component { export interface Options { @@ -54,37 +54,37 @@ export namespace Component { } } -type EventCallback = (this: Session, session: Session, ...args: R) => T +export type GetSession = C[typeof Context.session] export interface Events extends cordis.Events { - 'internal/session'(session: Session): void - 'interaction/command'(session: Session): void - 'interaction/button'(session: Session): void - 'message'(session: Session): void - 'message-created'(session: Session): void - 'message-deleted'(session: Session): void - 'message-updated'(session: Session): void - 'message-pinned'(session: Session): void - 'message-unpinned'(session: Session): void - 'guild-added'(session: Session): void - 'guild-removed'(session: Session): void - 'guild-updated'(session: Session): void - 'guild-member-added'(session: Session): void - 'guild-member-removed'(session: Session): void - 'guild-member-updated'(session: Session): void - 'guild-role-created'(session: Session): void - 'guild-role-deleted'(session: Session): void - 'guild-role-updated'(session: Session): void - 'reaction-added'(session: Session): void - 'reaction-removed'(session: Session): void - 'login-added'(session: Session): void - 'login-removed'(session: Session): void - 'login-updated'(session: Session): void - 'friend-request'(session: Session): void - 'guild-request'(session: Session): void - 'guild-member-request'(session: Session): void - 'before-send': EventCallback, [SendOptions]> - 'send'(session: Session): void + 'internal/session'(session: GetSession): void + 'interaction/command'(session: GetSession): void + 'interaction/button'(session: GetSession): void + 'message'(session: GetSession): void + 'message-created'(session: GetSession): void + 'message-deleted'(session: GetSession): void + 'message-updated'(session: GetSession): void + 'message-pinned'(session: GetSession): void + 'message-unpinned'(session: GetSession): void + 'guild-added'(session: GetSession): void + 'guild-removed'(session: GetSession): void + 'guild-updated'(session: GetSession): void + 'guild-member-added'(session: GetSession): void + 'guild-member-removed'(session: GetSession): void + 'guild-member-updated'(session: GetSession): void + 'guild-role-created'(session: GetSession): void + 'guild-role-deleted'(session: GetSession): void + 'guild-role-updated'(session: GetSession): void + 'reaction-added'(session: GetSession): void + 'reaction-removed'(session: GetSession): void + 'login-added'(session: GetSession): void + 'login-removed'(session: GetSession): void + 'login-updated'(session: GetSession): void + 'friend-request'(session: GetSession): void + 'guild-request'(session: GetSession): void + 'guild-member-request'(session: GetSession): void + 'before-send'(session: GetSession, options: SendOptions): Awaitable + 'send'(session: GetSession): void /** @deprecated use `login-added` instead */ 'bot-added'(client: Bot): void /** @deprecated use `login-removed` instead */ @@ -98,11 +98,14 @@ export interface Events extends cordis.Events { export interface Context { [Context.config]: Context.Config [Context.events]: Events + [Context.session]: Session http: Quester } export class Context extends cordis.Context { static readonly session = Symbol('session') + // remove generic type to loosen the constraint + static readonly Session = Session as new (bot: Bot, event: Partial) => Session public bots = new Proxy([] as Bot[] & Dict, { get(target, prop) { @@ -136,7 +139,7 @@ export class Context extends cordis.Context { return new Logger(name) } - component(name: string, component: Component, options: Component.Options = {}) { + component(name: string, component: Component, options: Component.Options = {}) { const render: Component = async (attrs, children, session) => { if (options.session && session.type === 'send') { throw new Error('interactive components is not available outside sessions') diff --git a/packages/core/src/message.ts b/packages/core/src/message.ts index 1a5cd686..5a85868d 100644 --- a/packages/core/src/message.ts +++ b/packages/core/src/message.ts @@ -1,7 +1,7 @@ import { Bot } from './bot' import { Channel, Message, SendOptions } from '@satorijs/protocol' -import { Session } from './session' import h from '@satorijs/element' +import { Context } from '.' class AggregateError extends Error { constructor(public errors: Error[], message = '') { @@ -9,10 +9,10 @@ class AggregateError extends Error { } } -export abstract class MessageEncoder { +export abstract class MessageEncoder = Bot> { public errors: Error[] = [] public results: Message[] = [] - public session: Session + public session: C[typeof Context.session] constructor(public bot: B, public channelId: string, public guildId?: string, public options: SendOptions = {}) { } diff --git a/packages/core/src/session.ts b/packages/core/src/session.ts index 605e6afe..3c0ecea3 100644 --- a/packages/core/src/session.ts +++ b/packages/core/src/session.ts @@ -27,23 +27,23 @@ export interface Session { quote: Message } -export class Session { +export class Session { static counter = 0 public id: number - public bot: Bot - public app: Context['root'] + public bot: Bot + public app: C['root'] public event: Event public locales: string[] = [] - constructor(bot: Bot, payload: Partial) { - payload.selfId ??= bot.selfId - payload.platform ??= bot.platform - payload.timestamp ??= Date.now() - this.event = payload as Event + constructor(bot: Bot, event: Partial) { + event.selfId ??= bot.selfId + event.platform ??= bot.platform + event.timestamp ??= Date.now() + this.event = event as Event this.id = ++Session.counter - Object.assign(this, payload) - for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(payload))) { + Object.assign(this, event) + for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(event))) { if (descriptor.enumerable) continue Object.defineProperty(this, key, descriptor) } diff --git a/packages/satori/src/index.ts b/packages/satori/src/index.ts index 5a391f98..3ce8210b 100644 --- a/packages/satori/src/index.ts +++ b/packages/satori/src/index.ts @@ -1,6 +1,5 @@ -import { Context, Logger, Quester, Schema } from '@satorijs/core' -import { defineProperty, trimSlash } from 'cosmokit' -import { listen } from './listen' +import { Context, Quester, Schema } from '@satorijs/core' +import { defineProperty } from 'cosmokit' export * from '@satorijs/core' export * from 'cosmokit' @@ -37,29 +36,3 @@ Context.Config.list.unshift(Context.Config.Network) Context.Config.list.push(Schema.object({ request: Quester.Config, })) - -const logger = new Logger('app') - -const start = Context.prototype.start -Context.prototype.start = async function (this: Context, ...args) { - if (this.root.config.selfUrl) { - this.root.config.selfUrl = trimSlash(this.root.config.selfUrl) - } - - if (this.root.config.port) { - const { host } = this.root.config - this.router.host = host - this.router.port = await listen(this.root.config) - this.router._http.listen(this.router.port, host) - logger.info('server listening at %c', this.router.selfUrl) - this.on('dispose', () => { - logger.info('http server closing') - this.router._ws?.close() - this.router._http?.close() - }) - } - - this.decline(['selfUrl', 'host', 'port', 'maxPort']) - - return start.call(this, ...args) -} diff --git a/packages/satori/src/router.ts b/packages/satori/src/router.ts index 31907e9f..4d1f6b2e 100644 --- a/packages/satori/src/router.ts +++ b/packages/satori/src/router.ts @@ -1,11 +1,14 @@ -import { Context } from '@satorijs/core' -import { MaybeArray, remove } from 'cosmokit' +import { Context, Logger } from '@satorijs/core' +import { MaybeArray, remove, trimSlash } from 'cosmokit' import { createServer, IncomingMessage, Server } from 'http' import { pathToRegexp } from 'path-to-regexp' import parseUrl from 'parseurl' import WebSocket from 'ws' import KoaRouter from '@koa/router' import Koa from 'koa' +import { listen } from './listen' + +const logger = new Logger('app') declare module 'koa' { // koa-bodyparser @@ -79,6 +82,26 @@ export class Router extends KoaRouter { } socket.close() }) + + ctx.root.decline(['selfUrl', 'host', 'port', 'maxPort']) + + if (ctx.root.config.selfUrl) { + ctx.root.config.selfUrl = trimSlash(ctx.root.config.selfUrl) + } + + ctx.on('ready', async () => { + const { host, port } = ctx.root.config + if (!port) return + ctx.router.host = host + ctx.router.port = await listen(ctx.root.config) + ctx.router._http.listen(ctx.router.port, host) + logger.info('server listening at %c', ctx.router.selfUrl) + ctx.on('dispose', () => { + logger.info('http server closing') + ctx.router._ws?.close() + ctx.router._http?.close() + }) + }, true) } get selfUrl() {