Преглед изворни кода

feat(经济系统): 添加经济系统支持,包括金币管理和相关命令

新增经济系统配置文件、用户数据接口和操作函数,支持金币的添加和移除。同时添加了相关装饰器和命令处理逻辑,确保经济系统与现有插件系统无缝集成。
枫林 пре 3 месеци
родитељ
комит
7d26fe5504

+ 3 - 1
.gitignore

@@ -80,4 +80,6 @@ test/
 src/plugins/*
 !src/plugins/PluginsFile.ts
 !src/plugins/test.ts
-!src/plugins/log.ts
+!src/plugins/log.ts
+
+src/data

+ 12 - 0
data/economy/2180323481.json

@@ -0,0 +1,12 @@
+{
+    "userId": "2180323481",
+    "coins": 10000,
+    "logs": [
+        {
+            "type": "add",
+            "amount": 10000,
+            "reason": "添加金币",
+            "date": "2025-05-23T23:50:39.034Z"
+        }
+    ]
+}

+ 2 - 1
src/config/bot.yml

@@ -1,3 +1,4 @@
+# bot 配置文件,用于配置 bot 的相关信息,如协议、主机地址、端口号、访问令牌等。
 bot:
   protocol: "ws"  # 协议 ws/wss
   host: "192.168.100.249"  # 主机地址
@@ -9,4 +10,4 @@ bot:
     attempts: 10
     delay: 5000
 cmd:
- prefix: "#"
+ prefix: "#" # 命令前缀

+ 8 - 0
src/config/economy.yml

@@ -0,0 +1,8 @@
+# 经济系统配置
+name: "金币" # 名称
+enable: true # 是否启用经济系统
+currency: 元 # 货币单位
+decimal: 2 # 小数位数
+data:
+  path: "/Users/fenglin/Desktop/botQQ/src/data" # 数据路径
+  defaultCoins: 0 # 默认金额

+ 1 - 0
src/config/load.yml

@@ -1,3 +1,4 @@
+# 记录插件上传转化的文件
 isuplad: false
 name: test.ts
 id: 1814872986

+ 17 - 0
src/interface/economy.ts

@@ -0,0 +1,17 @@
+export interface UserData {
+    userId: string; 
+    coins: number;
+    logs: Economylogs[];
+}
+export interface Economylogs {
+    type: 'add' | 'remove';
+    amount: number;
+    reason: string;
+    date: Date;
+}
+export interface EconomyCommands {
+    name: string; 
+    type: 'add' | 'remove';
+    amount: number;
+    reason: string;
+}

+ 18 - 3
src/lib/Plugins.ts

@@ -13,13 +13,14 @@ import { Command, ParamMetadata, Plugin, } from '../interface/plugin.js';
 import * as fs from 'fs'
 import * as path from 'path'
 // 获取指令前缀
-import { Botconfig as config, load, PermissionConfig, saveConfig } from './config.js'
+import { Botconfig as config, economy, load, PermissionConfig, saveConfig } from './config.js'
 import { ImageSegment, ReplySegment, TextSegment } from "node-napcat-ts/dist/Structs.js";
 import { fileURLToPath } from 'node:url';
 import { qqBot } from "../app.js";
 import { IsPermission } from "./Permission.js";
 import { download } from "./download.js";
-import { commandList, paramMetadata } from "./decorators.js";
+import { commandList, economyCommands, paramMetadata } from "./decorators.js";
+import { addCoins, removeCoins } from "./economy.js";
 
 //WSSendParam
 const CMD_PREFIX = config?.cmd?.prefix ?? '#';
@@ -436,7 +437,7 @@ async function handleCommand(context: PrivateFriendMessage | PrivateGroupMessage
             command: command.cmd,
             args: parsedArgs.slice(0, -1) // 不显示 context 对象
         }));
-
+        await cost(context, command);
         // 执行命令
         const pluginInstance = new (command.class)();
         const result = await command.fn.apply(pluginInstance, parsedArgs);
@@ -649,6 +650,20 @@ export function runcod(cmd: string | string[], desc: string): MethodDecorator {
     };
 }
 
+// 指令花费金币
+export async function cost( context: PrivateFriendMessage | PrivateGroupMessage | GroupMessage,command: Command){
+    if (economy.enable == false) {
+        return;
+    }
+    const ecocmd = economyCommands.get(command.pluginId + "." + command.fnName);
+    if (ecocmd) {
+      if (ecocmd.type ==='add') {
+        addCoins(context.sender.user_id.toString(), ecocmd.amount, ecocmd.reason);
+      }else{
+        removeCoins(context.sender.user_id.toString(), ecocmd.amount, ecocmd.reason);
+      }
+    }
+}
 
 // 修改参数解析函数
 async function parseCommandParams(message: string, context: PrivateFriendMessage | PrivateGroupMessage | GroupMessage, command: Command,easycmd:boolean): Promise<any[]> {

+ 1 - 0
src/lib/config.ts

@@ -16,3 +16,4 @@ export function saveConfig(file: string, data: any): void {
 export const Botconfig = await loadConfig('bot');
 export const PermissionConfig = await loadConfig('permission');
 export const load = await loadConfig('load')
+export const economy = await loadConfig('economy')

+ 24 - 0
src/lib/decorators.ts

@@ -6,12 +6,15 @@ const CMD_PREFIX = config?.cmd?.prefix ?? '#';
 import { fileURLToPath } from 'node:url';
 import { Command, CommandConfig, ParamMetadata, ParamType, PluginConfig } from '../interface/plugin.js';
 import { Plugin } from '../interface/plugin.js';
+import { EconomyCommands } from '../interface/economy.js';
 
 
 
 
 // 存储参数元数据
 export const paramMetadata = new Map<string, ParamMetadata[]>();
+// 存储金币相关的命令
+export const economyCommands= new Map<string, EconomyCommands>();
 // 存储命令的数组
 export const commandList: Plugin[] = [];
 
@@ -268,3 +271,24 @@ export function schedule(cron: string): MethodDecorator {
         return descriptor;
     };
 }
+
+// 添加金币相关的fn装饰器
+/**
+ * 金币相关的fn装饰器
+ * @param amount - 金币数量
+ * @param type - 操作类型: 'add' 或 'remove'
+ * @param reason - 操作原因
+ */
+export function coins(amount: number, type: 'add' | 'remove' ,reason: string = "未知原因") {
+    return function (target: any, propertyKey: string | symbol | undefined): void {
+        const actualPropertyKey = propertyKey!;
+        const fnName = `${target.constructor.name}.${actualPropertyKey.toString()}`;
+        const EconomyCommand: EconomyCommands = {
+            amount: amount,
+            type: type,
+            reason: reason,
+            name: ''
+        };
+        economyCommands.set(fnName,{...EconomyCommand})
+    };
+}

+ 53 - 0
src/lib/economy.ts

@@ -0,0 +1,53 @@
+
+import { UserData } from "../interface/economy.js";
+import fs from 'fs';
+import { economy } from "./config.js";
+
+
+export function addCoins(userId: string, amount: number, reason: string): void {
+    const userData = getUserData(userId);
+    userData.coins += amount;
+    userData.logs.push({
+        type: 'add',
+        amount: amount,
+        reason: reason,
+        date: new Date()
+    });
+    saveUserData(userId, userData);
+}
+export function removeCoins(userId: string, amount: number, reason: string): void {
+    const userData = getUserData(userId);
+    if (userData.coins < amount) {
+        throw new Error(`${economy.name}不足,需要${amount}${economy.currency},拥有${userData.coins}${economy.currency}`);
+    }
+    userData.coins -= amount;
+    userData.logs.push({
+        type: 'remove',
+        amount: amount,
+        reason: reason,
+        date: new Date()
+    });
+     saveUserData(userId, userData);
+}
+function getUserData(userId: string): UserData {
+    if (!fs.existsSync(`${economy.data.path}`)) {
+        throw new Error(`未找到用户数据目录,请检查配置文件`);
+    }
+    if (!fs.existsSync(`${economy.data.path}/${userId}.json`)) {
+        const newUserData: UserData = {
+            userId: userId,
+            coins: economy.data.defaultCoins,
+            logs: []
+        };
+        fs.writeFileSync(`${economy.data.path}/${userId}.json`, JSON.stringify(newUserData, null, 4));
+        return newUserData;
+    }
+    const userData = JSON.parse(fs.readFileSync(`${economy.data.path}/${userId}.json`, 'utf-8')) as UserData;
+    return userData;
+}
+function  saveUserData(userId: string, userData: UserData): void {
+    if (!fs.existsSync(`${economy.data.path}`)) {
+        throw new Error(`未找到用户数据目录,请检查配置文件`);
+    }
+    fs.writeFileSync(`${economy.data.path}/${userId}.json`, JSON.stringify(userData, null, 4));
+}

+ 23 - 2
src/plugins/test.ts

@@ -1,6 +1,6 @@
 //PLUGIN test.ts
 
-import { param, plugins, runcod, schedule } from '../lib/decorators.js';
+import { coins, param, plugins, runcod, schedule } from '../lib/decorators.js';
 import path from 'path';
 import 'reflect-metadata';
 import { fileURLToPath } from 'node:url';
@@ -36,7 +36,10 @@ export class test {
         })
         botlogger.info("测试插件加载成功")
     }
-    @runcod(["param"], "参数实例")//命令装饰器,用于注册命令
+    @runcod(
+        ["param"], //命令名称,用于触发命令
+        "参数实例" //命令描述,用于显示在默认菜单中
+    )//命令装饰器,用于注册命令
     async param(
         @param("参数1", ParamType.String) param1: string,//参数装饰器,用于解析参数
         @param("参数2", ParamType.Number,999,true) param2: number,//参数装饰器,用于解析参数
@@ -68,6 +71,24 @@ export class test {
         };
     }
 
+    @runcod(["add"], "添加金币")//命令装饰器,用于注册命令
+    @coins(10000,//金币数量
+        'add',//类别 add为增加金币,remove为减少金币
+        "添加金币"//原因,用于记录日志
+    )
+    async add(){
+        return `添加成功`;
+    }
+    //remove coins from user
+    @runcod(["remove"], "移除金币")//命令装饰器,用于注册命令
+    @coins(
+        10000,//金币数量
+        'remove',//类别 add为增加金币,remove为减少金币
+        "移除金币"//原因,用于记录日志
+    ) //经济修饰词,用于减少金币
+    async remove(){
+        return `移除成功`;
+    }
     @schedule('* */30 * * * *') // 每30分钟执行一次
     async testschedule() {
         // botlogger.info("定时任务测试")