Skip to content

Commit

Permalink
feat(server): initial support for satori adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Sep 15, 2023
1 parent e79bda0 commit 49ae476
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 0 deletions.
2 changes: 2 additions & 0 deletions adapters/satori/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.DS_Store
tsconfig.tsbuildinfo
31 changes: 31 additions & 0 deletions adapters/satori/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "@satorijs/adapter-satori",
"description": "Satori Adapter for Satorijs",
"version": "0.1.0",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"files": [
"lib"
],
"author": "Shigma <[email protected]>",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/satorijs/satori.git",
"directory": "adapters/satori"
},
"bugs": {
"url": "https://github.com/satorijs/satori/issues"
},
"homepage": "https://koishi.chat/plugins/adapter/satori.html",
"keywords": [
"bot",
"protocol",
"client",
"chatbot",
"satori"
],
"peerDependencies": {
"@satorijs/satori": "^3.0.0-alpha.1"
}
}
5 changes: 5 additions & 0 deletions adapters/satori/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# [@satorijs/adapter-satori](https://koishi.chat/plugins/adapter/satori.html)

Satori adapter for [Satori](https://github.com/satorijs/satori).

- [Documentation](https://koishi.chat/plugins/adapter/satori.html)
48 changes: 48 additions & 0 deletions adapters/satori/src/bot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Bot, camelize, Context, Quester, Schema, Universal } from '@satorijs/satori'
import { WsClient } from './ws'

export function camelizeKeys<T>(source: T): T {
if (!source || typeof source !== 'object') return source
if (Array.isArray(source)) return source.map(camelizeKeys) as any
return Object.fromEntries(Object.entries(source).map(([k, v]) => [camelize(k), camelizeKeys(v)])) as any
}

export class SatoriBot extends Bot<SatoriBot.Config> {
public http: Quester

constructor(ctx: Context, config: SatoriBot.Config) {
super(ctx, config)
this.platform = 'discord'
this.http = ctx.http.extend(config)
// TODO: Internal
// this.internal = new Internal(this.http)
ctx.plugin(WsClient, this)
}
}

for (const [key, method] of Object.entries(Universal.Methods)) {
SatoriBot.prototype[key] = function (this: SatoriBot, ...args: any[]) {
const payload = {}
for (const key of method.fields) {
payload[key] = args.shift()
}
this.http.post('/' + key, payload)
}
}

export namespace SatoriBot {
export interface Config extends Bot.Config, WsClient.Config {
slash?: boolean
endpoint: string
}

export const Config: Schema<Config> = Schema.intersect([
Schema.object({
endpoint: Schema.string().description('API endpoint.').required(),
}),
Schema.object({
slash: Schema.boolean().description('是否启用斜线指令。').default(true),
}).description('功能设置'),
WsClient.Config,
])
}
6 changes: 6 additions & 0 deletions adapters/satori/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { SatoriBot } from './bot'

export * from './bot'
export * from './ws'

export default SatoriBot
90 changes: 90 additions & 0 deletions adapters/satori/src/ws.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { Adapter, Logger, Schema, Time, Universal } from '@satorijs/satori'
import { camelizeKeys, SatoriBot } from './bot'

const logger = new Logger('discord')

type Message = Message.Heartbeat | Message.Dispatch | Message.Ready

namespace Message {
export interface Base {
op: string
seq?: number
}

export interface Heartbeat extends Base {
op: 'heartbeat'
}

export interface Ready extends Base {
op: 'ready'
data: {
user: Universal.User
}
}

export interface Dispatch extends Base {
op: 'dispatch'
data: any
}
}

export class WsClient extends Adapter.WsClient<SatoriBot> {
_seq = 0
_ses?: string
_ping: NodeJS.Timeout

async prepare() {
const { url } = await this.bot.internal.getGatewayBot()
return this.bot.http.ws(url + '/?v=10&encoding=json')
}

accept() {
this.bot.socket.send(JSON.stringify({
op: 'identify',
d: {
seq: this._ses,
},
}))

this._ping = setInterval(() => {
this.bot.socket.send(JSON.stringify({
op: 'heartbeat',
}))
}, Time.second * 10)

this.bot.socket.addEventListener('message', async ({ data }) => {
let parsed: Message
try {
parsed = JSON.parse(data.toString())
} catch (error) {
return logger.warn('cannot parse message', data)
}
if (parsed.seq) {
this._seq = parsed.seq
}

if (parsed.op === 'ready') {
logger.debug('ready')
Object.assign(this.bot, camelizeKeys(parsed.data.user))
return this.bot.online()
}

if (parsed.op === 'dispatch') {
const session = this.bot.session(camelizeKeys(parsed.data))
this.bot.dispatch(session)
}
})

this.bot.socket.addEventListener('close', () => {
clearInterval(this._ping)
})
}
}

export namespace WsClient {
export interface Config extends Adapter.WsClient.Config {}

export const Config: Schema<Config> = Schema.intersect([
Adapter.WsClient.Config,
] as const)
}
10 changes: 10 additions & 0 deletions adapters/satori/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.base",
"compilerOptions": {
"outDir": "lib",
"rootDir": "src",
},
"include": [
"src",
],
}

0 comments on commit 49ae476

Please sign in to comment.