Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions src/block.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { ConfigManager } from "./config/configManager";
import { logger } from "./logger";
import { revive } from "./utils/utils";
import { fmtDate } from "./utils/utils_string";

export class BlockInfo {
static validKeys: (keyof BlockInfo)[] = ['reason', 'time'];
reason: string;
time: number;

constructor() {
this.reason = '';
this.time = 0;
}
}

export class BlockManager {
static blockList: { [id: string]: BlockInfo } = {};

static initBlockList() {
try {
const data = JSON.parse(ConfigManager.ext.storageGet('blacklist') || '{}');
if (typeof data !== 'object') throw new Error('blacklist不是对象');

for (const key in data) {
if (data.hasOwnProperty(key)) {
this.blockList[key] = revive(BlockInfo, data[key]);
}
}
} catch (error) {
logger.error(`从数据库中获取blacklist失败:`, error);
}
}

static saveBlockList() {
ConfigManager.ext.storageSet('blacklist', JSON.stringify(this.blockList));
}

static addBlock(id: string, reason: string) {
const info = new BlockInfo();
info.reason = reason;
info.time = Date.now();

this.blockList[id] = info;
this.saveBlockList();
}

static removeBlock(id: string) {
if (this.blockList.hasOwnProperty(id)) {
delete this.blockList[id];
this.saveBlockList();
return true;
}
return false;
}

static checkBlock(id: string): string | null {
if (this.blockList.hasOwnProperty(id)) {
return this.blockList[id].reason;
}
return null;
}

static getListText(): string {
const ids = Object.keys(this.blockList);
if (ids.length === 0) {
return '黑名单为空';
}
return ids.map(id => {
const info = this.blockList[id];
return `${id}: ${info.reason} (拉黑时间: ${fmtDate(Math.floor(info.time / 1000))})`;
}).join('\n');
}
}
139 changes: 138 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { aliasToCmd } from "./utils/utils";
import { knowledgeMM } from "./AI/memory";
import { HELPMAP, CQTYPESALLOW } from "./config/config";
import { ImageManager } from "./AI/image";
import { BlockManager } from "./block";

function main() {
ConfigManager.registerConfig();
Expand All @@ -22,6 +23,7 @@ function main() {
TimerManager.init();
PrivilegeManager.reviveCmdPriv();
knowledgeMM.init();
BlockManager.initBlockList();

const ext = ConfigManager.ext;

Expand All @@ -42,6 +44,7 @@ function main() {
【.ai memo】AI的记忆相关
【.ai tool】AI的工具相关
【.ai ign】AI的忽略名单相关
【.ai block】AI的黑名单相关
【.ai tk】AI的token相关
【.ai shut】终止AI当前流式输出`;
cmdAI.allowDelegate = true;
Expand Down Expand Up @@ -1037,6 +1040,77 @@ ${images.map(img => img.CQCode).join('\n')}`));
}
}
}
case 'block': {
const mctx = seal.getCtxProxyFirst(ctx, cmdArgs);
const muid = cmdArgs.amIBeMentionedFirst ? epId : mctx.player.userId;

const val2 = cmdArgs.getArgN(2);
switch (aliasToCmd(val2)) {
case 'add': {
let targetId = '';
let reason = '';
const arg3 = cmdArgs.getArgN(3);
const arg4 = cmdArgs.getArgN(4);

if (cmdArgs.at.length > 0) {
targetId = muid;
reason = arg4;
} else if (arg3 && (arg3.startsWith('QQ:') || arg3.startsWith('QQ-Group:'))) {
targetId = arg3;
reason = arg4;
} else {
seal.replyToSender(ctx, msg, '参数缺失,【.ai block add <统一ID/@xxx> <原因>】添加黑名单');
return ret;
}

if (!reason || reason.trim() === '') {
reason = `未填写原因`;
}

if (BlockManager.checkBlock(targetId)) {
seal.replyToSender(ctx, msg, '已经在黑名单中');
return ret;
}

BlockManager.addBlock(targetId, reason);
seal.replyToSender(ctx, msg, `已将<${targetId}>加入黑名单,原因: ${reason}`);
return ret;
}
case 'remove': {
let targetId = '';
const arg3 = cmdArgs.getArgN(3);

if (cmdArgs.at.length > 0) {
targetId = muid;
} else if (arg3 && (arg3.startsWith('QQ:') || arg3.startsWith('QQ-Group:'))) {
targetId = arg3;
} else {
seal.replyToSender(ctx, msg, '参数缺失,【.ai block rm <统一ID/@xxx>】移除黑名单');
return ret;
}

if (BlockManager.removeBlock(targetId)) {
seal.replyToSender(ctx, msg, `已将<${targetId}>移出黑名单`);
} else {
seal.replyToSender(ctx, msg, `不在黑名单中`);
}
return ret;
}
case 'list': {
seal.replyToSender(ctx, msg, BlockManager.getListText());
return ret;
}
default: {
seal.replyToSender(ctx, msg, `帮助:
【.ai block add <统一ID/@xxx> <原因>】添加黑名单
【.ai block rm <统一ID/@xxx>】移除黑名单
【.ai block list】查看黑名单列表

被拉黑的对象无法触发AI对话`);
return ret;
}
}
}
case 'token': {
const val2 = cmdArgs.getArgN(2);
switch (aliasToCmd(val2)) {
Expand Down Expand Up @@ -1431,6 +1505,22 @@ ${images.map(img => img.CQCode).join('\n')}`));
ext.cmdMap['ai'] = cmdAI;

ext.onPoke = (ctx, event) => {
const uid = event.senderId;
const blockReason = BlockManager.checkBlock(uid);
if (blockReason) {
logger.info(`用户<${uid}>在黑名单中,原因: ${blockReason},忽略戳一戳`);
return;
}

if (!event.isPrivate) {
const gid = event.groupId;
const groupBlockReason = BlockManager.checkBlock(gid);
if (groupBlockReason) {
logger.info(`群组<${gid}>在黑名单中,原因: ${groupBlockReason},忽略戳一戳`);
return;
}
}

const msg = createMsg(event.isPrivate ? 'private' : 'group', event.senderId, event.groupId);
msg.message = `[CQ:poke,qq=${event.targetId.replace(/^.+:/, '')}]`;
if (event.senderId === ctx.endPoint.userId) ext.onMessageSend(ctx, msg);
Expand All @@ -1440,12 +1530,28 @@ ${images.map(img => img.CQCode).join('\n')}`));
//接受非指令消息
ext.onNotCommandReceived = (ctx, msg): void | Promise<void> => {
try {
// 黑名单用户消息不接收不处理
const uid = ctx.player.userId;
const blockReason = BlockManager.checkBlock(uid);
if (blockReason) {
logger.info(`用户<${uid}>在黑名单中,原因: ${blockReason},忽略消息`);
return;
}

if (!ctx.isPrivate) {
const gid = ctx.group.groupId;
const groupBlockReason = BlockManager.checkBlock(gid);
if (groupBlockReason) {
logger.info(`群组<${gid}>在黑名单中,原因: ${groupBlockReason},忽略消息`);
return;
}
}

const { disabledInPrivate, globalStandby, triggerRegex, ignoreRegex, triggerCondition } = ConfigManager.received;
if (ctx.isPrivate && disabledInPrivate) {
return;
}

const uid = ctx.player.userId;
const gid = ctx.group.groupId;
const sid = ctx.isPrivate ? uid : gid;
const ai = AIManager.getAI(sid);
Expand Down Expand Up @@ -1532,6 +1638,22 @@ ${images.map(img => img.CQCode).join('\n')}`));
//接受的指令
ext.onCommandReceived = (ctx, msg, cmdArgs) => {
try {
const uid = ctx.player.userId;
const blockReason = BlockManager.checkBlock(uid);
if (blockReason) {
logger.info(`用户<${uid}>在黑名单中,原因: ${blockReason},忽略指令`);
return;
}

if (!ctx.isPrivate) {
const gid = ctx.group.groupId;
const groupBlockReason = BlockManager.checkBlock(gid);
if (groupBlockReason) {
logger.info(`群组<${gid}>在黑名单中,原因: ${groupBlockReason},忽略指令`);
return;
}
}

if (ToolManager.cmdArgs === null) {
ToolManager.cmdArgs = cmdArgs;
}
Expand Down Expand Up @@ -1566,6 +1688,21 @@ ${images.map(img => img.CQCode).join('\n')}`));
ext.onMessageSend = (ctx, msg) => {
try {
const uid = ctx.player.userId;
const blockReason = BlockManager.checkBlock(uid);
if (blockReason) {
logger.info(`用户<${uid}>在黑名单中,原因: ${blockReason},忽略发送消息`);
return;
}

if (!ctx.isPrivate) {
const gid = ctx.group.groupId;
const groupBlockReason = BlockManager.checkBlock(gid);
if (groupBlockReason) {
logger.info(`群组<${gid}>在黑名单中,原因: ${groupBlockReason},忽略发送消息`);
return;
}
}

const gid = ctx.group.groupId;
const sid = ctx.isPrivate ? uid : gid;
const ai = AIManager.getAI(sid);
Expand Down
8 changes: 8 additions & 0 deletions src/privilege.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,14 @@ export const defaultCmdPriv: CmdPriv = {
list: { priv: U }
}
},
block: {
priv: M, args: {
add: { priv: M },
remove: { priv: M },
list: { priv: M },
help: { priv: U }
}
},
token: {
priv: S, args: {
list: { priv: U },
Expand Down
2 changes: 2 additions & 0 deletions src/tool/tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { registerSetTrigger } from "./tool_trigger"
import { registerMusicPlay } from "./tool_music"
import { registerMeme } from "./tool_meme"
import { registerRender } from "./tool_render"
import { registerBlockTool } from "./tool_block"
import { logger } from "../logger"
import { Image } from "../AI/image";
import { fixJsonString } from "../utils/utils_string";
Expand Down Expand Up @@ -205,6 +206,7 @@ export class ToolManager {
registerMusicPlay();
registerMeme();
registerRender();
registerBlockTool();
}

/**
Expand Down
89 changes: 89 additions & 0 deletions src/tool/tool_block.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { Tool } from "./tool";
import { BlockManager } from "../block";
import { ConfigManager } from "../config/configManager";

export function registerBlockTool() {
const toolBlock = new Tool({
type: 'function',
function: {
name: 'block_user',
description: '拉黑指定用户,使其无法触发AI',
parameters: {
type: 'object',
properties: {
name: {
type: 'string',
description: '用户名称' + (ConfigManager.message.showNumber ? '或纯数字QQ号' : '')
},
reason: {
type: 'string',
description: '拉黑原因'
}
},
required: ['name', 'reason']
}
}
});
toolBlock.solve = async (ctx, _, ai, args) => {
const { name, reason } = args;

const ui = await ai.context.findUserInfo(ctx, name);
if (ui === null) return { content: `未找到<${name}>`, images: [] };

if (BlockManager.checkBlock(ui.id)) {
return { content: `用户<${name}>已经在黑名单中`, images: [] };
}

BlockManager.addBlock(ui.id, reason);
ctx.notice(`AI已将用户<${name}>(${ui.id})加入黑名单,原因: ${reason}`);
return { content: `已将<${name}>加入黑名单,原因: ${reason}`, images: [] };
}

// 不确定是否给ai
// const toolUnblock = new Tool({
// type: 'function',
// function: {
// name: 'unblock_user',
// description: '移除黑名单中的用户',
// parameters: {
// type: 'object',
// properties: {
// name: {
// type: 'string',
// description: '用户名称' + (ConfigManager.message.showNumber ? '或纯数字QQ号' : '')
// }
// },
// required: ['name']
// }
// }
// });
// toolUnblock.solve = async (ctx, _, ai, args) => {
// const { name } = args;

// const ui = await ai.context.findUserInfo(ctx, name);
// if (ui === null) return { content: `未找到<${name}>`, images: [] };

// if (BlockManager.removeBlock(ui.id)) {
// return { content: `已将<${name}>移出黑名单`, images: [] };
// } else {
// return { content: `用户<${name}>不在黑名单中`, images: [] };
// }
// }

// const toolList = new Tool({
// type: 'function',
// function: {
// name: 'get_block_list',
// description: '获取AI黑名单列表',
// parameters: {
// type: 'object',
// properties: {},
// required: []
// }
// }
// });
// toolList.solve = async (_, __, ___, ____) => {
// const list = BlockManager.getListText();
// return { content: list, images: [] };
// }
}