finish pipe

This commit is contained in:
锦恢 2024-06-01 02:14:46 +08:00
parent a13a6ba34f
commit c27bb27ec3
8 changed files with 487 additions and 147 deletions

View File

@ -14,7 +14,7 @@ import * as Lagrange from '../type';
* @param message * @param message
* @param auto_escape CQ message * @param auto_escape CQ message
*/ */
export function sendPrivateMsg(user_id: number, message: Lagrange.Message, auto_escape: boolean = false) { export function sendPrivateMsg(user_id: number, message: string | Lagrange.Send.Default[], auto_escape: boolean = false) {
return { return {
action: 'send_private_msg', action: 'send_private_msg',
params: { user_id, message, auto_escape } params: { user_id, message, auto_escape }
@ -27,7 +27,7 @@ export function sendPrivateMsg(user_id: number, message: Lagrange.Message, auto_
* @param message * @param message
* @param auto_escape CQ message * @param auto_escape CQ message
*/ */
export function sendGroupMsg(group_id: number, message: Lagrange.Message, auto_escape: boolean = false) { export function sendGroupMsg(group_id: number, message: string | Lagrange.Send.Default[], auto_escape: boolean = false) {
return { return {
action: 'send_group_msg', action: 'send_group_msg',
params: { group_id, message, auto_escape } params: { group_id, message, auto_escape }
@ -42,7 +42,7 @@ export function sendGroupMsg(group_id: number, message: Lagrange.Message, auto_e
* @param message * @param message
* @param auto_escape CQ message * @param auto_escape CQ message
*/ */
export function sendMsg(message_type: string, user_id: number, group_id: number, message: Lagrange.Message, auto_escape: boolean = false) { export function sendMsg(message_type: string, user_id: number, group_id: number, message: string | Lagrange.Send.Default[], auto_escape: boolean = false) {
return { return {
action: 'send_msg', action: 'send_msg',
params: { message_type, user_id, group_id, message, auto_escape } params: { message_type, user_id, group_id, message, auto_escape }

View File

@ -1,24 +1,33 @@
import type * as Lagrange from './type';
import lagrangeMapper from './lagrange-mapping'; import lagrangeMapper from './lagrange-mapping';
function runPipe(message: Lagrange.Message) { import type * as Lagrange from './type';
import type { LagrangeContext } from './context';
class Pipe {
context: LagrangeContext | undefined;
send: Lagrange.SendApi | undefined;
public injectContext(context: LagrangeContext) {
this.context = context;
this.send = context.send.bind(context);
}
public run(message: Lagrange.Message) {
switch (message.post_type) { switch (message.post_type) {
case 'message': messagePipe(message); break; case 'message': this.messagePipe(message); break;
case 'notice': noticePipe(message); break; case 'notice': this.noticePipe(message); break;
case 'request': requestPipe(message); break; case 'request':this.requestPipe(message); break;
default: break; default: break;
} }
} }
// 处理 message 类型的 post_type 的 // 处理 message 类型的 post_type 消息
function messagePipe(message: Lagrange.MessagePostType) { public messagePipe(message: Lagrange.MessagePostType) {
switch (message.message_type) { switch (message.message_type) {
case 'private': case 'private':
lagrangeMapper.resolvePrivateUser(message, this.send);
break; break;
case 'group': case 'group':
lagrangeMapper.resolveGroup(message, this.send);
break; break;
default: default:
break; break;
@ -26,21 +35,25 @@ function messagePipe(message: Lagrange.MessagePostType) {
} }
// 处理 notice 类型的 post_type 消息 // 处理 notice 类型的 post_type 消息
function noticePipe(message: Lagrange.NoticePostType) { public noticePipe(message: Lagrange.NoticePostType) {
} }
// 处理 request 类型的 post_type 消息 // 处理 request 类型的 post_type 消息
function requestPipe(message: Lagrange.RequestPostType) { public requestPipe(message: Lagrange.RequestPostType) {
} }
}
export const pipe = new Pipe();
export function onMessage(event: Buffer) { export function onMessage(event: Buffer) {
const messageBuffer = event.toString('utf-8'); const messageBuffer = event.toString('utf-8');
const messageJson = JSON.parse(messageBuffer) as Lagrange.Message; const messageJson = JSON.parse(messageBuffer) as Lagrange.Message;
// 忽略系统 message // 忽略系统 message
if (messageJson.post_type !== 'meta_event') { if (messageJson.post_type !== 'meta_event') {
runPipe(messageJson); console.log('进入 runPipe');
pipe.run(messageJson);
} }
} }

View File

@ -1,13 +1,15 @@
import lagrangeMapper from './lagrange-mapping'; import lagrangeMapper from './lagrange-mapping';
import { apiQueryVecdb } from './api/vecdb';
import type * as Lagrange from './type'; import type * as Lagrange from './type';
class Impl { export class Impl {
@lagrangeMapper.onPrivateUser({ user_id: 1193466151 }) @lagrangeMapper.onPrivateUser(1193466151)
async handleJinhui(message: Lagrange.PrivateMessage) { async handleJinhui(c: Lagrange.PrivateUserInvokeContext) {
console.log('raw message:' + c.message.raw_message);
} }
} }

View File

@ -1,8 +1,9 @@
import assert from 'assert'; import assert from 'assert';
import type * as Lagrange from './type'; import type * as Lagrange from './type';
type PrivateUserInvoker = (message: Lagrange.PrivateMessage) => void; type PrivateUserInvoker = (context: Lagrange.PrivateUserInvokeContext) => Lagrange.Thenable<undefined | void | string | Lagrange.Send.Default>;
type GroupUserInvoker = (message: Lagrange.GroupMessage) => void; type GroupUserInvoker = (context: Lagrange.GroupUserInvokeContext) => Lagrange.Thenable<undefined | void | string | Lagrange.Send.Default>;
type MessageInvoker = PrivateUserInvoker | GroupUserInvoker; type MessageInvoker = PrivateUserInvoker | GroupUserInvoker;
@ -17,7 +18,7 @@ interface CustomDescriptor<T extends MessageInvoker> {
interface MessageInvokerStorage<T extends MessageInvoker> { interface MessageInvokerStorage<T extends MessageInvoker> {
invoker: T; invoker: T;
config: Partial<Lagrange.CommonMessage> config?: Partial<Lagrange.CommonMessage>
} }
class LagrangeMapper { class LagrangeMapper {
@ -37,24 +38,33 @@ class LagrangeMapper {
return this._groupStorage; return this._groupStorage;
} }
public matchPrivateUser(message: Lagrange.PrivateMessage) { public resolvePrivateUser(message: Lagrange.PrivateMessage, send: Lagrange.SendApi) {
const user_id = message.user_id; const user_id = message.user_id;
const userStorage = this._privateUserStorage.get(user_id); const userStorage = this._privateUserStorage.get(user_id);
console.log(user_id);
console.log(userStorage);
if (userStorage) { if (userStorage) {
userStorage.invoker(message); userStorage.invoker({ message, send });
} }
} }
public onPrivateUser(config: Partial<Lagrange.CommonMessage>) { public resolveGroup(message: Lagrange.GroupMessage, send: Lagrange.SendApi) {
assert(config.user_id, 'onPrivateUser 中 user_id 不能为空'); const group_id = message.group_id;
const groupStorage = this._groupStorage.get(group_id);
if (groupStorage) {
groupStorage.invoker({ message, send });
}
}
public onPrivateUser(user_id: number) {
const _this = this; const _this = this;
return function(target: any, propertyKey: string, descriptor: CustomDescriptor<PrivateUserInvoker>) { return function(target: any, propertyKey: string, descriptor: CustomDescriptor<PrivateUserInvoker>) {
const user_id = config.user_id; if (_this._privateUserStorage.has(user_id)) {
if (_this.privateUserStorage.has(user_id)) {
console.warn(`${propertyKey} -> 用户 ${user_id} 已经被注册过了,该操作将覆盖原本的!`); console.warn(`${propertyKey} -> 用户 ${user_id} 已经被注册过了,该操作将覆盖原本的!`);
} }
const invoker = descriptor.value; const invoker = descriptor.value;
_this.privateUserStorage.set(user_id, { invoker, config }); _this._privateUserStorage.set(user_id, { invoker });
} }
} }

View File

@ -1,35 +1,14 @@
import * as fs from 'fs'; import * as fs from 'fs';
import WebSocket from 'ws'; import lagServer from './context';
import './impl';
import { onMessage, onClose } from './event'; const buffer = fs.readFileSync('./app/publish/appsettings.json', 'utf-8');
import { apiQueryVecdb } from './api/vecdb'; const config = JSON.parse(buffer);
const impl = config.Implementations[0];
const lagrangeBuffer = fs.readFileSync('./app/publish/appsettings.json', 'utf-8'); lagServer.run({
const lagrangeConfig = JSON.parse(lagrangeBuffer);
const impl = lagrangeConfig.Implementations[0];
const connectionParam = {
host: impl.Host, host: impl.Host,
port: impl.Port, port: impl.Port,
path: impl.Suffix path: impl.Suffix
};
const socket = new WebSocket.Server(connectionParam);
socket.on('connection', (ws: WebSocket) => {
console.log('完成 ws 连接,启动参数如下');
console.table(connectionParam);
ws.on('message', onMessage);
ws.on('close', onClose);
const testMsg = {
action: 'send_private_msg',
params: {
user_id: 1193466151,
message: '你好'
}
}
ws.send(JSON.stringify(testMsg));
}); });

View File

@ -1,4 +1,5 @@
import type * as Lagrange from './type'; import * as Lagrange from './type';
class Plugins { class Plugins {
registeredPlugins: Map<string, Function>; registeredPlugins: Map<string, Function>;

View File

@ -38,14 +38,23 @@ export interface Sender {
title?: string title?: string
} }
export interface MsgText { // 参考文档: https://github.com/botuniverse/onebot-11/blob/master/message/segment.md
export namespace Receive {
export interface Text {
type: 'text', type: 'text',
data: { data: {
text: string text: string
} }
} }
export interface MsgImage { export interface Face {
type: 'face',
data: {
id: string
}
}
export interface Image {
type: 'image', type: 'image',
data: { data: {
file: string, file: string,
@ -55,20 +64,332 @@ export interface MsgImage {
} }
} }
export interface MsgFace { export interface Audio {
type: 'record',
data: {
file: string,
magic: 0 | 1,
url: string
}
}
export interface Video {
type: 'video',
data: {
file: string,
url: string
}
}
export interface At {
type: 'at',
data: {
qq: string
}
}
// 猜拳魔法表情
export interface FingerGuess {
type: 'rps',
data: {}
}
// 掷骰子魔法表情
export interface Dice {
type: 'dice',
data: {}
}
// 窗口抖动(戳一戳)
export interface WindowJitter {
type: 'shake',
data: {}
}
// 戳一戳
export interface Poke {
type: 'poke',
data: {
type: string,
id: string,
name: string
}
}
export interface Link {
type: 'share',
data: {
// URL
url: string,
// 标题
title: string,
// 发送时可选,内容描述
content?: string,
// 发送时可选,图片 URL
image?: string
}
}
export interface RecommendFriend {
type: 'contact',
data: {
type: 'qq',
// 被推荐人的 QQ 号
id: string
}
}
export interface RecommendGroup {
type: 'contact',
data: {
type: 'group',
// 被推荐群的群号
id: string
}
}
export interface Location {
type: 'location',
data: {
// 纬度
lat: string,
// 经度
lon: string,
// 发送时可选,标题
title?: string,
// 发送时可选,内容描述
content?: string
}
}
export interface Reply {
type: 'reply',
data: {
id: string
}
}
export interface Forward {
type: 'forward',
data: {
id: string
}
}
export interface XML {
type: 'xml',
data: {
// XML 内容
data: string
}
}
export interface JSON {
type: 'json',
data: {
data: string
}
}
export type Default = Text | Face | Image | Audio | Video | At | FingerGuess | Dice | WindowJitter | Poke | Link | RecommendFriend | RecommendGroup | Location | Reply | Forward | XML | JSON;
}
export namespace Send {
export interface Text {
type: 'text',
data: {
text: string
}
}
export interface Face {
type: 'face', type: 'face',
data: { data: {
id: string id: string
} }
} }
export interface MsgAt { export interface Image {
type: 'image',
data: {
/**
* file
* 1. file:///C:\\Users\Richard\Pictures\1.png
* 2. URL http://i1.piimg.com/567571/fdd6e7b6d93f1ef0.jpg
* Base64 base64://iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAIAAADJt1n/AAAAKElEQVQ4EWPk5+RmIBcwkasRpG9UM4mhNxpgowFGMARGEwnBIEJVAAAdBgBNAZf+QAAAAABJRU5ErkJggg==
*/
file: string,
// 只在通过网络 URL 发送时有效,表示是否使用已缓存的文件,默认 1
cache: 0 | 1,
// 只在通过网络 URL 发送时有效,表示是否通过代理下载文件(需通过环境变量或配置文件配置代理),默认 1
proxy: 0 | 1,
// 只在通过网络 URL 发送时有效,单位秒,表示下载网络文件的超时时间,默认不超时
timeout: number
}
}
export interface Audio {
type: 'record',
data: {
file: string,
magic: 0 | 1,
cache: 0 | 1,
proxy: 0 | 1,
timeout: number
}
}
export interface Video {
type: 'video',
data: {
file: string,
cache: 0 | 1,
proxy: 0 | 1,
timeout: number
}
}
export interface At {
type: 'at', type: 'at',
data: { data: {
qq: string qq: string
} }
} }
export interface FingerGuess {
type: 'rps',
data: {}
}
export interface Dice {
type: 'dice',
data: {}
}
export interface WindowJitter {
type: 'shake',
data: {}
}
// 戳一戳
export interface Poke {
type: 'poke',
data: {
type: string,
id: string,
}
}
export interface Anonymous {
type: 'anonymous',
data: {}
}
export interface Link {
type: 'share',
data: {
// URL
url: string,
// 标题
title: string,
// 发送时可选,内容描述
content?: string,
// 发送时可选,图片 URL
image?: string
}
}
export interface RecommendFriend {
type: 'contact',
data: {
type: 'qq',
// 被推荐人的 QQ 号
id: string
}
}
export interface RecommendGroup {
type: 'contact',
data: {
type: 'group',
// 被推荐群的群号
id: string
}
}
export interface Location {
type: 'location',
data: {
// 纬度
lat: string,
// 经度
lon: string,
// 发送时可选,标题
title?: string,
// 发送时可选,内容描述
content?: string
}
}
export interface MusicShare {
type: 'music',
data: {
// 分别表示使用 QQ 音乐、网易云音乐、虾米音乐
type: 'qq' | '163' | 'xm',
// 歌曲 ID
id: string
}
}
export interface CustomMusicShare {
type: 'music',
data: {
type: 'custom',
url: string,
audio: string,
title: string,
content: string,
image: string
}
}
export interface Reply {
type: 'reply',
data: {
id: string
}
}
export interface ForwardNode {
type: 'node',
data: {
id: string
}
}
export interface XML {
type: 'xml',
data: {
// XML 内容
data: string
}
}
export interface JSON {
type: 'json',
data: {
data: string
}
}
export type Default = Text | Face | Image | Audio | Video | At | FingerGuess | Dice | WindowJitter | Poke | Anonymous | Link | RecommendFriend | RecommendGroup | Location | MusicShare | CustomMusicShare | Reply | ForwardNode | XML | JSON;
}
export interface MsgFile { export interface MsgFile {
// 一般是 '' // 一般是 ''
id: string, id: string,
@ -103,7 +424,7 @@ export interface CommonMessage {
// 是否为匿名发言,一般都是 null // 是否为匿名发言,一般都是 null
anonymous?: null | boolean, anonymous?: null | boolean,
// 消息内容(结构化) // 消息内容(结构化)
message?: MsgText | MsgImage | MsgFace | MsgAt, message?: Receive.Default,
// 消息内容(纯文本) // 消息内容(纯文本)
raw_message?: string, raw_message?: string,
// 发送的时间戳 // 发送的时间戳
@ -125,7 +446,7 @@ export interface PrivateMessage {
// 发消息的人的 QQ 号 // 发消息的人的 QQ 号
user_id: number, user_id: number,
// 消息内容(结构化) // 消息内容(结构化)
message: MsgText | MsgImage | MsgFace | MsgAt, message: Receive.Default,
// 消息内容(纯文本) // 消息内容(纯文本)
raw_message: string, raw_message: string,
// 发送的时间戳 // 发送的时间戳
@ -136,6 +457,7 @@ export interface PrivateMessage {
font?: number font?: number
} }
export interface GroupMessage { export interface GroupMessage {
// 事件类型 // 事件类型
post_type: 'message', post_type: 'message',
@ -152,7 +474,7 @@ export interface GroupMessage {
// 是否为匿名发言,一般都是 null // 是否为匿名发言,一般都是 null
anonymous: null | boolean, anonymous: null | boolean,
// 消息内容(结构化) // 消息内容(结构化)
message: MsgText | MsgImage | MsgFace | MsgAt, message: Receive.Default,
// 消息内容(纯文本) // 消息内容(纯文本)
raw_message: string, raw_message: string,
// 发送的时间戳 // 发送的时间戳
@ -205,3 +527,16 @@ export type Message = MetaMessage | PrivateMessage | GroupMessage | FileMessage
export type MessagePostType = PrivateMessage | GroupMessage; export type MessagePostType = PrivateMessage | GroupMessage;
export type NoticePostType = FileMessage | ApproveMessage; export type NoticePostType = FileMessage | ApproveMessage;
export type RequestPostType = AddMessage; export type RequestPostType = AddMessage;
export type Thenable<T> = T | Promise<T>;
export type SendApi = (msg: string | Send.Default[]) => Thenable<void | Error>;
export interface InvokerContext<M = Message> {
message: M,
send: SendApi
}
export type PrivateUserInvokeContext = InvokerContext<PrivateMessage>;
export type GroupUserInvokeContext = InvokerContext<GroupMessage>;

View File

@ -96,11 +96,11 @@ for el in soup.find_all('h2'):
} }
if param['type'] == 'message': if param['type'] == 'message':
param['type'] = 'Lagrange.Message' param['type'] = 'string | Lagrange.Send.Default[]'
params.append(param) params.append(param)
t1 = function_desc t1 = function_desc.strip()
t2 = '\n' t2 = '\n'
for param in params: for param in params:
t2 += ' * @param {} {}\n'.format(param['name'], param['desc']) t2 += ' * @param {} {}\n'.format(param['name'], param['desc'])