更改项目结构

main
落雨楓 1 year ago
parent 140b705324
commit 6336a89b7f

File diff suppressed because it is too large Load Diff

@ -1,13 +1,13 @@
import App from "../App";
import { CommonReceivedMessage } from "../message/Message";
import { CommandInputArgs, MessagePriority, PluginController, PluginEvent } from "../PluginManager";
import App from "#ibot/App";
import { CommonReceivedMessage } from "#ibot/message/Message";
import { CommandInputArgs, MessagePriority, PluginController, PluginEvent } from "#ibot/PluginManager";
import { encode as gptEncode } from 'gpt-3-encoder';
import got, { OptionsOfTextResponseBody } from "got/dist/source";
import { HttpsProxyAgent } from 'hpagent';
import { ProxyAgent } from 'undici';
import { FetchEventSourceInit, fetchEventSource } from '@waylaidwanderer/fetch-event-source';
import { RandomMessage } from "../utils/RandomMessage";
import { MessageTypingSimulator } from "../utils/MessageTypingSimulator";
import { RandomMessage } from "#ibot/utils/RandomMessage";
import { MessageTypingSimulator } from "#ibot/utils/MessageTypingSimulator";
import OpenCC from 'opencc';
@ -177,7 +177,7 @@ export default class ChatGPTController implements PluginController {
this.event.registerCommand({
command: 'ai',
name: '开始对话',
}, (args, message, resolve) => {
}, async (args, message, resolve) => {
resolve();
return this.handleChatGPTAPIChat(args, message, true, 'saved', true);
@ -195,7 +195,7 @@ export default class ChatGPTController implements PluginController {
this.event.registerCommand({
command: '重置对话',
name: '重置对话',
}, (args, message, resolve) => {
}, async (args, message, resolve) => {
resolve();
return Promise.all([

@ -0,0 +1,125 @@
import { CommonReceivedMessage } from "#ibot/message/Message";
import App from "#ibot/App";
import { CommandInputArgs, PluginController, PluginEvent } from "#ibot/PluginManager";
export default class DiceController implements PluginController {
public event!: PluginEvent;
public app: App;
public id = 'dice';
public name = 'DND骰子';
public description = '骰一个DND骰子格式1d6+3';
private config!: Awaited<ReturnType<typeof this.getDefaultConfig>>;
constructor(app: App) {
this.app = app;
}
async getDefaultConfig() {
return {
messages: {
diceFormatError: [
'骰子格式错误:{{{error}}}',
'输入错误:{{{error}}}',
'错误的骰子格式:{{{error}}}',
]
}
};
}
public async initialize(config: any): Promise<void> {
await this.updateConfig(config);
this.event.init(this);
this.event.registerCommand({
command: 'roll',
name: 'DND骰子',
alias: ['r'],
help: '格式:|骰子数量|d|面数|(+|加成|)示例1d20、1d6+3'
}, (args, message, resolved) => {
resolved();
this.rollDice(args, message);
});
this.event.registerCommand({
command: 'random',
name: '随机数',
alias: ['rand'],
help: '格式:“最大值”或“最小值 最大值”示例1 100'
}, (args, message, resolved) => {
resolved();
this.randomNumber(args, message);
});
}
async updateConfig(config: any) {
this.config = config;
}
private async rollDice(args: CommandInputArgs, message: CommonReceivedMessage) {
await message.markRead();
let matches = args.param.trim().match(/^(?<diceNum>\d+)d(?<diceType>\d+)(\+(?<bonus>\d+))?/);
if (!matches) {
await message.sendReply('骰子格式错误');
return;
}
let diceNum = parseInt(matches.groups!.diceNum);
let diceType = parseInt(matches.groups!.diceType);
let bonus = parseInt(matches.groups!.bonus ?? '0');
if (diceNum > 20) {
await message.sendReply('骰子数量不能超过20');
return;
}
let results: number[] = [];
for (let i = 0; i < diceNum; i++) {
let roll = Math.floor(Math.random() * diceType) + 1;
results.push(roll);
}
let total = results.reduce((a, b) => a + b) + bonus;
let replyMsg = '骰子结果:'
replyMsg += results.join('、') + '\n';
if (bonus > 0) {
replyMsg += `加成:${bonus}\n`;
}
if (results.length >= 2) {
replyMsg += `总计:${total}`;
}
await message.sendReply(replyMsg);
}
private async randomNumber(args: CommandInputArgs, message: CommonReceivedMessage) {
await message.markRead();
let argv = args.param.trim().split(' ');
if (argv.length == 0) {
await message.sendReply('参数错误,请提供最大值或者最大值和最小值');
return;
}
let maxNum = 0;
let minNum = 0;
if (argv.length == 1) {
maxNum = parseInt(argv[0]);
} else if (argv.length == 2) {
minNum = parseInt(argv[0]);
maxNum = parseInt(argv[1]);
}
if (isNaN(maxNum) || isNaN(minNum)) {
await message.sendReply('参数错误,请提供正确的数字');
return;
}
let result = Math.floor(Math.random() * (maxNum - minNum + 1)) + minNum;
await message.sendReply(`随机数:${result}`);
}
}

@ -1,11 +1,11 @@
import App from "../App";
import { CommonReceivedMessage } from "../message/Message";
import { MessagePriority, PluginController, PluginEvent } from "../PluginManager";
import App from "#ibot/App";
import { AddReplyMode, CommonReceivedMessage } from "#ibot/message/Message";
import { CommandInputArgs, MessagePriority, PluginController, PluginEvent } from "../PluginManager";
import got from "got/dist/source";
import { RandomMessage } from "../utils/RandomMessage";
import { QQForwardingMessage } from "../robot/adapter/qq/Message";
import QQRobot from "../robot/adapter/QQRobot";
import { GroupSender } from "../message/Sender";
import { RandomMessage } from "#ibot/utils/RandomMessage";
import { QQForwardingMessage } from "#ibot/robot/adapter/qq/Message";
import QQRobot from "#ibot/robot/adapter/QQRobot";
import { GroupSender } from "#ibot/message/Sender";
import { Robot } from "#ibot/robot/Robot";
export type IsekaiBBSQuicklyPostConfig = {
@ -79,6 +79,21 @@ export default class IsekaiBBSQuicklyPost implements PluginController {
this.event.init(this);
this.event.registerCommand({
command: '绑定快速发布',
name: '绑定快速发布账号',
}, async (args, message, resolve) => {
let groupId = message.sender.groupId;
if (!groupId) return;
let groupConfig = this.config.groups[groupId];
if (!groupConfig) return;
resolve();
return this.bindAccount(args, message, groupConfig);
});
this.event.on('message/group', async (message, resolved) => {
if (message.type !== 'reference') return;
@ -117,6 +132,41 @@ export default class IsekaiBBSQuicklyPost implements PluginController {
return username.substring(0, maskOffset) + '_'.repeat(maskLen) + username.substring(maskOffset + maskLen);
}
async bindAccount(args: CommandInputArgs, message: CommonReceivedMessage, groupConfig: IsekaiBBSQuicklyPostConfig) {
message.markRead();
let bindingCodeStr = args.param.trim();
if (!bindingCodeStr) {
await message.sendReply('请输入绑定码。', false);
return;
}
try {
const res = await got.post(groupConfig.api_endpoint + '/api/isekai-quicklypost/server-api/qq/verify-binding', {
json: {
account: message.sender?.userId,
binding_code: bindingCodeStr,
},
headers: {
authorization: `Bearer ${groupConfig.token}`,
},
}).json<any>();
if (res.error) {
if (res.error === 'BINDING_CODE_INVALID') {
await message.sendReply(`验证码错误或验证码已过期`, AddReplyMode.IGNORE_PRIVATE);
return;
}
throw new Error(res.message);
}
} catch (err: any) {
this.app.logger.error("绑定BBS账号失败" + err.message, err);
console.error(err);
await message.sendReply(`绑定账号失败:${err.message}`, false);
}
}
async messageToMarkdown(message: CommonReceivedMessage) {
let markdownBuilder: string[] = [];
message.content.forEach(messageChunk => {
@ -179,7 +229,7 @@ export default class IsekaiBBSQuicklyPost implements PluginController {
}],
} as IsekaiQuicklyPostBody;
const res = await got.post(groupConfig.api_endpoint, {
const res = await got.post(groupConfig.api_endpoint + '/api/isekai-quicklypost/server-api/post', {
json: postData,
headers: {
authorization: `Bearer ${groupConfig.token}`,

@ -59,8 +59,7 @@ export default class RWKVRolePlayingController implements PluginController {
private globalDefaultCharacter: string = '';
private chatGenerating = false;
private messageGroup: Record<string, RandomMessage> = {}
private botSentMessageIds = new ItemLimitedList<string>(1000);
private messageGroup: Record<string, RandomMessage> = {};
constructor(app: App) {
this.app = app;
@ -135,7 +134,7 @@ export default class RWKVRolePlayingController implements PluginController {
command: '重开',
alias: ['重置聊天', 'remake'],
name: '重置聊天',
}, (args, message, resolve) => {
}, async (args, message, resolve) => {
resolve();
return this.handleResetCurrentCharacter(args, message);
@ -144,17 +143,20 @@ export default class RWKVRolePlayingController implements PluginController {
this.event.registerCommand({
command: '切换人物',
name: '切换人物',
}, (args, message, resolve) => {
}, async (args, message, resolve) => {
resolve();
return this.handleChangeCharacter(args, message);
});
this.event.on('message/focused', (message, resolve) => {
if (message.repliedId && message.id && !this.botSentMessageIds.includes(message.id)) {
this.event.on('message/focused', async (message, resolve) => {
if (message.repliedId && message.id) {
let repliedMessage = await message.getRepliedMessage();
if (!repliedMessage?.extra?.isRWKVReply) {
// Don't reply message from other controllers
return;
}
}
resolve();
@ -501,9 +503,6 @@ export default class RWKVRolePlayingController implements PluginController {
let sentMessage = await message.sendReply(replyRes, true, {
isRWKVReply: true
});
if (sentMessage?.id) {
this.botSentMessageIds.addOne(sentMessage.id);
}
} catch (err: any) {
this.app.logger.error('RWKV error', err);
console.error(err);

@ -37,7 +37,7 @@ export default class SystemController implements PluginController {
let replyMsg = message.createReplyMessage();
replyMsg.type = 'help';
replyMsg.extra.controllers = subscribedControllers;
replyMsg._context.controllers = subscribedControllers;
let helpBuilder: string[] = [];

@ -1,7 +1,7 @@
import got from "got";
import App from "../../App";
import { CommonReceivedMessage } from "../../message/Message";
import { PluginEvent } from "../../PluginManager";
import App from "#ibot/App";
import { CommonReceivedMessage } from "#ibot/message/Message";
import { PluginEvent } from "#ibot/PluginManager";
export class WikiMisc {
public event!: PluginEvent;

@ -88,6 +88,9 @@ export class CommonMessage {
/** 附加信息 */
extra: any = reactive({});
/** 临时上下文信息,不会保存到数据库 */
_context: any = {};
private _contentText?: string;
public get contentText() {
@ -235,7 +238,7 @@ export class CommonSendMessage extends CommonMessage {
receiver: ChatIdentity;
/** 回复的消息 */
repliedMessage?: Reactive<CommonReceivedMessage>;
repliedMessage?: CommonSendMessage | CommonReceivedMessage;
/** 发送时间 */
time: Date = new Date();
@ -268,6 +271,10 @@ export class CommonSendMessage extends CommonMessage {
extra: this.extra,
};
}
public async getRepliedMessage(): Promise<CommonSendMessage | CommonReceivedMessage | null> {
return this.repliedMessage ?? null;
}
}
export type SessionStoreGroup = {
@ -302,6 +309,9 @@ export class CommonReceivedMessage extends CommonMessage {
},
}) as any;
/** 回复的消息 */
private _repliedMessage?: CommonSendMessage | CommonReceivedMessage | null;
constructor(receiver: Robot, sender: IMessageSender, messageId?: string) {
super();
@ -370,6 +380,18 @@ export class CommonReceivedMessage extends CommonMessage {
return this.receiver.getSession(this.sender.identity, type);
}
public async getRepliedMessage(): Promise<CommonSendMessage | CommonReceivedMessage | null> {
if (this._repliedMessage === undefined) {
if (this.repliedId && this.receiver.storages) {
this._repliedMessage = await this.receiver.storages.message.get<CommonSendMessage | CommonReceivedMessage>(this.repliedId);
} else {
this._repliedMessage = null;
}
}
return this._repliedMessage;
}
public toDBObject(): MessageDataType {
const chatIdentity = this.sender.identity;
return {

@ -81,6 +81,13 @@ export default class QQRobot implements RobotAdapter {
}
async initRestfulApi(router: RestfulRouter) {
router.get('/event', (ctx: FullRestfulContext, next: koa.Next) => {
ctx.body = JSON.stringify({
status: 0,
message: 'Please use POST method.'
});
next();
});
router.post(`/event`, this.handlePostEvent.bind(this));
}
@ -104,7 +111,9 @@ export default class QQRobot implements RobotAdapter {
}
}
ctx.body = 'OK';
ctx.body = JSON.stringify({
status: 1,
});
await next();
}
@ -114,7 +123,7 @@ export default class QQRobot implements RobotAdapter {
this.infoProvider.getGroupUsersInfo(userIds, groupId, rootGroupId);
async parseHelpMessage(message: CommonSendMessage) {
const controllers = message.extra.controllers as PluginController[];
const controllers = (message._context.controllers ?? []) as PluginController[];
let helpBuilder: string[] = [];
if (this.description) {
@ -290,9 +299,13 @@ export default class QQRobot implements RobotAdapter {
async markRead(message: CommonReceivedMessage): Promise<boolean> {
if (message.id) {
try {
await this.callRobotApi('mark_msg_as_read', {
message_id: message.id
});
} catch(err) {
this.app.logger.warn("[QQRobot] 当前API不支持markRead");
}
}
return true;
}

@ -186,7 +186,6 @@ export async function parseQQMessageChunk(bot: QQRobot, messageData: any[], mess
let jsonData = JSON.parse(chunkData.data.data);
switch (jsonData.app) {
case 'com.tencent.multimsg':
console.log('forwarding message', chunkData.data.data);
if (jsonData.meta?.detail?.resid) {
message.content.push({
type: ['reference', 'qqforwarding'],

@ -140,7 +140,13 @@ const emojiMapString = `
186: 🌰
187: 👻
188: 🥚
200: 🙏
201: 👍
202: 😉
203: 🤗
212: 🧐
265: 🤔
277: 😏
`;

@ -2,7 +2,7 @@ import App from "../App";
import { StorageConfig } from "../Config";
import { ModelRegistry } from "../DatabaseManager";
import { ItemLimitedList } from "../utils/ItemLimitedList";
import { CommonMessage } from "../message/Message";
import { CommonMessage, CommonSendMessage } from "../message/Message";
import { RobotStorage } from "./RobotStorage";
import { Reactive, reactive } from "../utils/reactive";
import { debounce } from "throttle-debounce";
@ -31,9 +31,9 @@ export class MessageStorage {
this.models = this.storages.models;
}
public async get(messageId: string): Promise<CommonMessage | null> {
public async get<T extends CommonMessage = CommonMessage>(messageId: string): Promise<T | null> {
// from cache
let messageObj: CommonMessage | null | undefined = this.cache.find((msg) => msg && msg.id === messageId);
let messageObj: T | null = this.cache.find((msg) => msg && msg.id === messageId) as any;
if (messageObj) {
return messageObj;
}
@ -47,7 +47,7 @@ export class MessageStorage {
if (doc) {
const robot = this.storages.robot;
if (robot) {
messageObj = await robot.parseDBMessage?.(doc);
messageObj = await robot.parseDBMessage?.(doc) as any;
return messageObj;
} else {
this.app.logger.error(`无法找到机器人配置:${this.storages.robotId}`);

@ -12,6 +12,11 @@ export type Reactive<T extends Object = any> = T & {
export function reactive<T extends Object>(obj: T): Reactive<T> {
const eventEmitter = new EventEmitter();
// 防止嵌套
if ((obj as Reactive<T>)._isReactive) {
return obj as Reactive<T>;
}
// 递归监听子对象
for (let key of Object.getOwnPropertyNames(obj)) {
if (key.startsWith('_')) continue;

@ -29,7 +29,8 @@
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
"paths": {
"#ibot/*": ["./src/*"],
"#ibot/*": ["./src/server/*"],
"#ibot-api/*": ["./src/devkit/*"],
}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */

Loading…
Cancel
Save