Skip to content

Commit

Permalink
feat: cmd adapter and loader modules
Browse files Browse the repository at this point in the history
  • Loading branch information
BIYUEHU committed Oct 6, 2023
1 parent 5d11c5f commit f2b656a
Show file tree
Hide file tree
Showing 33 changed files with 442 additions and 9,835 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

---

基于**NodeJS**+**TypeScript****go-cqhttp**的SDK和QQ机器人框架实现,相比于其它的go-cqhttp的NodeJS实现,Kotori-Bot的最大特点便是完全由纯**TypeScript**语言开发
基于**NodeJS**+**TypeScript****go-cqhttp**的SDK和QQ机器人框架实现,相比于其它的go-cqhttp的NodeJS实现,KotoriBot的最大特点便是完全由纯**TypeScript**语言开发

**Kotori**是一个罗马字,在日语中是**ことり(小鳥)**的意思,该名字取自于[Key](http://key.visualarts.gr.jp/)公式游戏[《Rewrite》](https://bgm.tv/subject/4022)及其衍生作品中的主要女性角色之一的**[神户小鸟](https://bgm.tv/character/12063)**(神戸(かんべ) 小鳥(ことり))。

Expand All @@ -28,6 +28,7 @@ kotori是一个**快捷,轻便,跨平台**的BOT框架,去繁化简只为打造

> [变更日志](CHANGELOG.md)
<!--
## kotori支持的连接模式
- [x] 正向 WebSocket
Expand Down Expand Up @@ -94,3 +95,4 @@ npm run build
```
> 该方式下运行不会读取项目下的配置文件,需在实例化时传入配置参数,详细说明参考
-->
6 changes: 4 additions & 2 deletions kotori.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,7 @@ adapter:
# reverse-port: 8080 # 反向Http端口
# retry: 10 # 连接断开或失败时尝试重连间隔时间 单位:秒

# cmd-test:
# master:
cmd-test:
extend: "cmd"
master: 0720
lang: 'zh_CN'
4 changes: 2 additions & 2 deletions modules/adapter-cmd/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@kotori-bot/plugdin-adapter-qq",
"name": "@kotori-bot/plugin-adapter-cmd",
"version": "1.0.0",
"description": "Adapter For QQ",
"description": "Adapter For Cmd",
"main": "src/index.ts",
"license": "GPL-3.0",
"author": "Hotaru <biyuehuya@gmail.com>",
Expand Down
98 changes: 88 additions & 10 deletions modules/adapter-cmd/src/adapter.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,95 @@
import { Adapter, Api } from '@kotori-bot/kotori';
import QQApi from './api';
/*
* @Author: Hotaru biyuehuya@gmail.com
* @Blog: https://hotaru.icu
* @Date: 2023-09-29 14:31:09
* @LastEditors: Hotaru biyuehuya@gmail.com
* @LastEditTime: 2023-10-06 15:59:37
*/
import { Adapter, AdapterConfig, Events, Msg, eventDataMsgSender, isObj } from '@kotori-bot/kotori';
import CmdApi from './api';

export default class QQAdapter extends Adapter {
protected Api: Api;
interface Iconfig extends AdapterConfig {
nickname: string;
age: number;
sex: eventDataMsgSender['sex'];
}

function checkConfig(config: unknown): config is Iconfig {
if (!isObj(config)) return false;
if (typeof config.nickname !== 'string') return false;
if (typeof config.age !== 'number') return false;
if (config.sex !== 'male' && config.sex !== 'female' && config.sex !== 'unknown') return false;
return true;
}

export default class CmdAdapter extends Adapter<CmdApi> {
private messageId = 0;

public api: CmdApi = new CmdApi(this);

public readonly platform: string = 'cmd';

public declare config: Iconfig;

public constructor(config: object) {
super(config);
this.Api = new QQApi(this);
public constructor(config: AdapterConfig, identity: string) {
const defaultConfig = {
nickname: 'Kotarou',
age: 18,
sex: 'male',
};
const newConfig = Object.assign(defaultConfig, config);
super(newConfig, identity);
if (!checkConfig(newConfig)) throw new Error(`Bot '${identity}' config format error`);
this.config = newConfig;
process.stdin.on('data', data => this.handle(data));
}

public handle = () => this;
public handle = (data: Buffer) => {
if (this.status.value !== 'online') return;
const message = data.toString();
if (message === '\n' || message === '\r\n') return;
Adapter.emit({
type: 'private_msg',
messageId: this.messageId,
message,
userId: this.config.master,
sender: {
nickname: this.config.nickname,
sex: this.config.sex,
age: this.config.age,
},
send: (message: Msg) => {
this.api.send_private_msg(message, this.config.master);
},
api: this.api,
locale: this.locale,
});
this.messageId += 1;
};

public start = () => this;
public start = () => {
this.send = (action, params?) => {
if (this.status.value !== 'online' || action !== 'send_private_msg' || !params) return;
if (typeof (params as { message: string }).message !== 'string') return;
if ((params as { user_id: unknown }).user_id !== this.config.master) return;
process.stdout.write(`> ${(params as { message: string }).message} \r\n`);
};
Events.emit({
type: 'connect',
adapter: this,
normal: true,
info: `start cmd-line listen`,
});
this.online();
};

public stop = () => this;
public stop = () => {
Events.emit({
type: 'disconnect',
adapter: this,
normal: true,
info: `stop cmd-line listen`,
});
this.offline();
};
}
118 changes: 20 additions & 98 deletions modules/adapter-cmd/src/api.ts
Original file line number Diff line number Diff line change
@@ -1,113 +1,35 @@
/*
* @Author: Hotaru biyuehuya@gmail.com
* @Blog: https://hotaru.icu
* @Date: 2023-09-29 14:31:13
* @LastEditors: Hotaru biyuehuya@gmail.com
* @LastEditTime: 2023-10-06 15:57:29
*/
import { Api, Msg } from '@kotori-bot/kotori';

export default class QQApi extends Api {
export default class CmdApi extends Api {
public send_private_msg = (message: Msg, userId: number) => {
this.send('send_private_msg', { user_id: userId, message, auto_escape: false });
/* handled msg... */
this.adapter.send('send_private_msg', { user_id: userId, message });
};

/**
* @description: 发送私聊消息
* @param {Msg} message 要发送的内容
* @param {groupId} groupId 群号
* @return {void}
*/
public send_group_msg = (message: Msg, groupId: number): void => {
this.send('send_group_msg', { group_id: groupId, message, auto_escape: false });
};
public send_group_msg = () => this.adapter.send('');

/**
* @description: 撤回消息
* @param {number} messageId 消息id
* @return {void}
*/
public delete_msg = (messageId: number): void => {
this.send('delete_msg', { messageId });
};
public delete_msg = () => this.adapter.send('');

/**
* @description: 设置群名
* @param {number} groupId 群号
* @param {string} groupName 新群名
* @return {void}
*/
public set_group_name = (groupId: number, groupName: string): void => {
this.send('set_group_name', { group_id: groupId, group_name: groupName });
};
public set_group_name = () => this.adapter.send('');

/**
* @description: 设置群头像
* @param {number} groupId 群号
* @param {string} image 图片路径
* @return {void}
*/
public set_group_avatar = (groupId: number, image: string): void => {
this.send('set_group_portrait', { group_id: groupId, file: image, cache: false });
};
public set_group_avatar = () => this.adapter.send('');

/**
* @description: 设置群管理员
* @param {number} groupId 群号
* @param {number} userId 要设置的管理员的QQ号
* @param {boolean} enable true为设置,false取消,默认true
* @return {void}
*/
public set_group_admin = (groupId: number, userId: number, enable: boolean = true): void => {
this.send('set_group_admin', { group_id: groupId, user_id: userId, enable });
};
public set_group_admin = () => this.adapter.send('');

/**
* @description: 设置群名片(群备注)
* @param {number} groupId 群号
* @param {number} userId 要设置的QQ号
* @param {string} card 群名片内容,不填或空字符串表示删除群名片
* @return {void}
*/
public set_group_card = (groupId: number, userId: number, card: string): void => {
this.send('set_group_card', { group_id: groupId, user_id: userId, card });
};
public set_group_card = () => this.adapter.send('');

/**
* @description: 群禁言
* @param {number} groupId 群号
* @param {number} userId 要禁言的QQ号,不填则为群禁言
* @param {number} time 禁言时长,单位秒,0表示取消禁言
* @return {void}
*/
public set_group_ban = (groupId: number, userId?: number, time: number = 0): void => {
if (userId) {
this.send('set_group_ban', { group_id: groupId, user_id: userId, duration: time });
} else {
this.send('set_group_whole_ban', { group_id: groupId, enable: !!time });
}
};
public set_group_ban = () => this.adapter.send('');

/**
* @description: 发送群公告
* @param {number} groupId 群号
* @param {string} content 公告内容
* @param {string} image 图片路径(可选)
* @return {void}
*/
public send_group_notice = (groupId: number, content: string, image?: string): void => {
this.send('_send_group_notice', { group_id: groupId, content, image });
};
public send_group_notice = () => this.adapter.send('');

/**
* @description: 群组踢人
* @param {number} groupId 群号
* @param {number} userId 要踢的QQ号
* @return {void}
*/
public set_group_kick = (groupId: number, userId: number): void => {
this.send('set_group_kick', { group_id: groupId, user_id: userId, reject_add_request: false });
};
public set_group_kick = () => this.adapter.send('');

/**
* @description: 退出群组
* @param {number} groupId 群号
* @return {void}
*/
public set_group_leave = (groupId: number): void => {
this.send('set_group_leave', { group_id: groupId, is_dismiss: false });
};
public set_group_leave = () => this.adapter.send('');
}
3 changes: 3 additions & 0 deletions modules/adapter-cmd/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import QQAdapter from './adapter';

export default QQAdapter;
8 changes: 4 additions & 4 deletions modules/adapter-qq/src/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
* @Blog: https://hotaru.icu
* @Date: 2023-09-29 14:31:09
* @LastEditors: Hotaru biyuehuya@gmail.com
* @LastEditTime: 2023-10-05 19:21:31
* @LastEditTime: 2023-10-06 15:25:14
*/
import { Adapter, AdapterConfig, Events, Msg, isObj } from '@kotori-bot/kotori';
import WebSocket from 'ws';
import QQApi from './api';
import WsServer from './services/wsserver';
import { EventDataType, Iconfig, IconfigWs } from './type';

function checkConfig(config: any): config is Iconfig {
function checkConfig(config: unknown): config is Iconfig {
if (!isObj(config)) return false;
if (typeof config.port !== 'number') return false;
if (config.mode === 'ws') {
Expand Down Expand Up @@ -54,7 +54,7 @@ export default class QQAdapter extends Adapter<QQApi> {
groupId: data.group_id,
...this.func(data),
});
// this.status.receivedMsg += 1;
this.status.receivedMsg += 1;
} else if (data.post_type === 'message' && data.message_type === 'group') {
Events.emit({
type: 'group_msg',
Expand All @@ -69,7 +69,7 @@ export default class QQAdapter extends Adapter<QQApi> {
groupId: data.group_id!,
...this.func(data),
});
// this.status.receivedMsg += 1;
this.status.receivedMsg += 1;
} else if (data.post_type === 'notice' && data.notice_type === 'private_recall') {
Events.emit({
type: 'private_recall',
Expand Down
4 changes: 4 additions & 0 deletions modules/adapter-qq/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { Api, Msg } from '@kotori-bot/kotori';

export default class QQApi extends Api {
public send_private_msg = (message: Msg, userId: number) => {
this.adapter.status.lastMsgTime = new Date();
this.adapter.status.sendMsg += 1;
this.adapter.send('send_private_msg', { user_id: userId, message, auto_escape: false });
};

Expand All @@ -19,6 +21,8 @@ export default class QQApi extends Api {
* @return {void}
*/
public send_group_msg = (message: Msg, groupId: number): void => {
this.adapter.status.lastMsgTime = new Date();
this.adapter.status.sendMsg += 1;
this.adapter.send('send_group_msg', { group_id: groupId, message, auto_escape: false });
};

Expand Down
11 changes: 11 additions & 0 deletions modules/testing/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "@kotori-bot/plugin-testing",
"version": "1.0.0",
"description": "testing plugin",
"main": "src/index.ts",
"license": "GPL-3.0",
"author": "Hotaru <biyuehuya@gmail.com>",
"peerDependencies": {
"kotori-bot": "workspace:^"
}
}
5 changes: 5 additions & 0 deletions modules/testing/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Kotori from '@kotori-bot/kotori';

Kotori.command('echo <content>').action(data => {
console.log(data);
});

0 comments on commit f2b656a

Please sign in to comment.