2
0

3 Коммиты d3ad0eb25f ... daaca9e618

Автор SHA1 Сообщение Дата
  枫林 daaca9e618 创建我的世界服务器白名单管理 1 месяц назад
  枫林 39dc6a2734 修改提交文件 1 месяц назад
  枫林 9287e75cc1 模版自定义模版高度自适配 1 месяц назад
9 измененных файлов с 265 добавлено и 2 удалено
  1. 4 0
      .gitignore
  2. 1 1
      botQQ_screenshots/test.json
  3. 16 0
      package-lock.json
  4. 1 0
      package.json
  5. 15 0
      pnpm-lock.yaml
  6. 4 0
      src/config/mc.yml
  7. 1 0
      src/lib/config.ts
  8. 219 0
      src/plugins/mc.ts
  9. 4 1
      src/plugins/test.ts

+ 4 - 0
.gitignore

@@ -85,5 +85,9 @@ src/plugins/*
 !src/plugins/prop.ts
 !src/plugins/Permission.ts
 !src/plugins/wiki.ts
+!src/plugins/mc.ts
 
 /data
+
+/src/plugins/mc.ts
+/src/config/mc.yml

+ 1 - 1
botQQ_screenshots/test.json

@@ -1 +1 @@
-{"":{"x1":""},"好友":{"x1":567,"y1":868},"添加好友":{"x1":183,"y1":472},"同意添加":{"x1":1642,"y1":405},"搜索好友":{"x1":643,"y1":204},"返回":{"x1":112,"y1":58},"确认搜索":{"x1":1634,"y1":183},"搜索好友添加":{"x1":1744,"y1":346},"干员":{"x1":1700,"y1":500},"干员翻页":{"type":"slide","x1":650,"y1":400,"x2":250,"y2":400},"助战干员":{"type":"cilik","x1":170,"y1":640},"助战干员1":{"type":"cilik","x1":500,"y1":550},"助战":{"type":"cilik","x1":1350,"y1":400},"确认干员":{"type":"cilik","x1":1528,"y1":1069}}
+{"":{"x1":"","plId":"","updatetime":1751182890059},"好友":{"x1":567,"y1":868},"添加好友":{"x1":183,"y1":472},"同意添加":{"x1":1642,"y1":405},"搜索好友":{"x1":643,"y1":204},"返回":{"x1":112,"y1":58},"确认搜索":{"x1":1634,"y1":183},"搜索好友添加":{"x1":1744,"y1":346},"干员":{"x1":1700,"y1":500},"干员翻页":{"type":"slide","x1":650,"y1":400,"x2":250,"y2":400},"助战干员":{"type":"cilik","x1":170,"y1":640},"助战干员1":{"type":"cilik","x1":500,"y1":550},"助战":{"type":"cilik","x1":1350,"y1":400},"确认干员":{"type":"cilik","x1":1528,"y1":1069},"明日方舟":{"type":"cilik","x1":800,"y1":300},"开始游戏":{"type":"cilik","x1":950,"y1":600},"关闭公告":{"type":"cilik","x1":100,"y1":1800},"仓库":{"type":"cilik","x1":1800,"y1":950}}

+ 16 - 0
package-lock.json

@@ -17,6 +17,7 @@
         "node-cron": "^3.0.3",
         "node-napcat-ts": "^0.4.0",
         "puppeteer": "^23.9.0",
+        "rcon-client": "^4.2.5",
         "reflect-metadata": "^0.2.2",
         "tesseract.js": "^6.0.1",
         "winston": "^3.17.0",
@@ -2869,6 +2870,15 @@
         "node": ">=18"
       }
     },
+    "node_modules/rcon-client": {
+      "version": "4.2.5",
+      "resolved": "https://registry.npmjs.org/rcon-client/-/rcon-client-4.2.5.tgz",
+      "integrity": "sha512-AnX1GU/ZTlwtYup3H6h0J1hwfP3OYltXVe+8ReBzmNEepX3xGH8nDg7gYqT5Y9rpAS/LmQ48h0BKINt1YGd8bA==",
+      "license": "MIT",
+      "dependencies": {
+        "typed-emitter": "^0.1.0"
+      }
+    },
     "node_modules/readable-stream": {
       "version": "3.6.2",
       "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
@@ -3392,6 +3402,12 @@
       "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
       "license": "0BSD"
     },
+    "node_modules/typed-emitter": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-0.1.0.tgz",
+      "integrity": "sha512-Tfay0l6gJMP5rkil8CzGbLthukn+9BN/VXWcABVFPjOoelJ+koW8BuPZYk+h/L+lEeIp1fSzVRiWRPIjKVjPdg==",
+      "license": "MIT"
+    },
     "node_modules/typed-query-selector": {
       "version": "2.12.0",
       "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz",

+ 1 - 0
package.json

@@ -21,6 +21,7 @@
     "node-cron": "^3.0.3",
     "node-napcat-ts": "^0.4.0",
     "puppeteer": "^23.9.0",
+    "rcon-client": "^4.2.5",
     "reflect-metadata": "^0.2.2",
     "tesseract.js": "^6.0.1",
     "winston": "^3.17.0",

+ 15 - 0
pnpm-lock.yaml

@@ -32,6 +32,9 @@ importers:
       puppeteer:
         specifier: ^23.9.0
         version: 23.11.1(typescript@4.9.5)
+      rcon-client:
+        specifier: ^4.2.5
+        version: 4.2.5
       reflect-metadata:
         specifier: ^0.2.2
         version: 0.2.2
@@ -1001,6 +1004,9 @@ packages:
     engines: {node: '>=18'}
     hasBin: true
 
+  rcon-client@4.2.5:
+    resolution: {integrity: sha512-AnX1GU/ZTlwtYup3H6h0J1hwfP3OYltXVe+8ReBzmNEepX3xGH8nDg7gYqT5Y9rpAS/LmQ48h0BKINt1YGd8bA==}
+
   readable-stream@3.6.2:
     resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
     engines: {node: '>= 6'}
@@ -1181,6 +1187,9 @@ packages:
     resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==}
     engines: {node: '>= 0.8.0'}
 
+  typed-emitter@0.1.0:
+    resolution: {integrity: sha512-Tfay0l6gJMP5rkil8CzGbLthukn+9BN/VXWcABVFPjOoelJ+koW8BuPZYk+h/L+lEeIp1fSzVRiWRPIjKVjPdg==}
+
   typed-query-selector@2.12.0:
     resolution: {integrity: sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==}
 
@@ -2325,6 +2334,10 @@ snapshots:
       - typescript
       - utf-8-validate
 
+  rcon-client@4.2.5:
+    dependencies:
+      typed-emitter: 0.1.0
+
   readable-stream@3.6.2:
     dependencies:
       inherits: 2.0.4
@@ -2517,6 +2530,8 @@ snapshots:
     dependencies:
       prelude-ls: 1.1.2
 
+  typed-emitter@0.1.0: {}
+
   typed-query-selector@2.12.0: {}
 
   typescript@4.9.5: {}

+ 4 - 0
src/config/mc.yml

@@ -0,0 +1,4 @@
+#连接配置
+host: localhost
+port: 25575
+password: fenglin123

+ 1 - 0
src/lib/config.ts

@@ -18,3 +18,4 @@ export const Botconfig = await loadConfig('bot');
 export const PermissionConfig = await loadConfig('permission');
 export const load = await loadConfig('load')
 export const economy = await loadConfig('economy')
+export const mccfg = await loadConfig('mc')

+ 219 - 0
src/plugins/mc.ts

@@ -0,0 +1,219 @@
+import { param, plugins, runcod } from "../lib/decorators.js";
+import { Rcon } from "rcon-client"
+import { Permission } from '../lib/Permission.js';
+import { GroupMessage, PrivateFriendMessage, PrivateGroupMessage, Receive } from "node-napcat-ts";
+import { mccfg } from "../lib/config.js";
+import path from "path";
+import { fileURLToPath } from "url";
+import fs from 'fs'
+import { qqBot } from "../app.js";
+
+@plugins({
+    easycmd: true,
+    name: "我的世界工具箱",
+    version: "0.0.1",
+    describe: "这是一个我的世界工具箱,用来管理你的服务器",
+    author: "枫叶秋林",
+    help: {
+        enabled: true,
+        description: "查看帮助信息"
+    }
+})
+export class mc {
+    rcon: Rcon
+    constructor() {
+        this.rcon = new Rcon({
+            host: mccfg.host,
+            port: mccfg.port,
+            password: mccfg.password
+        })
+        qqBot.on('notice.group_decrease', async (context) => {
+            if (!context.user_id) {
+                return
+            }
+            const plId = await this.readpl(context.user_id)
+            if (plId?.plId) {
+                try {
+                    const rcon = await this.rcon.connect()
+                    if (context.sub_type === 'kick') {
+                        const res = await rcon.send(`ban ${plId?.plId}`)
+                        await this.deletepl(context.user_id)
+                        qqBot.send_group_msg({
+                            group_id: context.group_id,
+                            message: [{
+                                type: "text",
+                                data: {
+                                    text: `群友:${context.user_id}被踢出,玩家${plId.plId}已被封禁!:\n
+                                           指令执行结果:${res}`
+                                }
+                            }]
+                        })
+                        return
+                    }
+                    if (context.sub_type === 'leave') {
+                        const res = await rcon.send(`whitelist remove ${plId?.plId}`)
+                        if (res.includes(`Removed ${plId?.plId} from the whitelist`)) {
+                            await this.deletepl(context.user_id)
+                            qqBot.send_group_msg({
+                                group_id: context.group_id,
+                                message: [{
+                                    type: "text",
+                                    data: {
+                                        text: `群友:${context.user_id}离开群了,玩家${plId.plId}已移除白名单!`
+                                    }
+                                }]
+                            })
+                        }
+                    }
+                    rcon.end()
+                } catch (error) {
+                    console.log(error)
+                }
+            } else {
+                qqBot.send_group_msg({
+                    group_id: context.group_id,
+                    message: [{
+                        type: "text",
+                        data: {
+                            text: `群友:${context.user_id}离开群了,但是玩家${plId}不在白名单!`
+                        }
+                    }]
+                })
+            }
+        })
+    }
+
+    @runcod(["list", "在线玩家"], "查看在线玩家") //命令描述,用于显示在默认菜单中
+    async list() {
+        try {
+            const rcon = await this.rcon.connect()
+            const res = await rcon.send("list")
+            const players = res.split(":");
+            rcon.end()
+            return players[1]
+        } catch (error) {
+            return "连接失败"
+        }
+    }
+    @Permission('Admin')
+    @runcod(["指令", "cmd"], "运行指令") //命令描述,用于显示在默认菜单中
+    async cmd(@param("执行指令", 'text') runcod: Receive["text"]) {//参数装饰器,用于解析参数) {
+        try {
+            const rcon = await this.rcon.connect()
+            runcod.data.text = runcod.data.text?.replace(/,/g, ' ')
+            const res = await rcon.send(runcod?.data?.text ?? '')
+            rcon.end()
+            return res
+        } catch (error) {
+            return "连接失败"
+        }
+    }
+    @runcod(["绑定", ""], "绑定玩家") //命令描述,用于显示在默认菜单中
+    async bindPl(@param("Id", 'text') pId: Receive["text"],
+        context: PrivateFriendMessage | PrivateGroupMessage | GroupMessage) {
+        const seedId = context?.sender?.user_id ?? null
+        const plId = pId?.data?.text ?? null
+        if (!seedId || !plId) {
+            return "绑定失败"
+        }
+        try {
+            const rcon = await this.rcon.connect()
+            const res = await rcon.send(`whitelist add ${plId}`)
+            //Added feng_linH to the whitelist
+            if (res.includes(`Added ${plId} to the whitelist`)) {
+                await this.savepl(seedId, plId)
+                return "绑定成功!"
+            }
+            rcon.end()
+            return res
+        } catch (error: any) {
+            return `绑定失败:${error.message}`
+        }
+    }
+
+    @runcod(["解绑", ""], "解绑玩家") //命令描述,用于显示在默认菜单中
+    async unBindPl(
+        context: PrivateFriendMessage | PrivateGroupMessage | GroupMessage) {
+        const seedId = context?.sender?.user_id ?? null
+        if (!seedId) {
+            return "解绑失败"
+        }
+        try {
+            const plId = await this.readpl(seedId)
+            if (!plId) {
+                return "解绑失败"
+            }
+            const rcon = await this.rcon.connect()
+            const res = await rcon.send(`whitelist remove ${plId}`)
+            //Removed feng_linH from the whitelist
+            if (res.includes(`Removed ${plId} from the whitelist`)) {
+                await this.savepl(seedId, '')
+                return "解绑成功!"
+            }
+            rcon.end()
+            return res
+        } catch (error: any) {
+            return `解绑失败:${error.message}`
+        }
+    }
+
+
+
+    private async savepl(seedId: number, plId: string): Promise<void> {
+        const __dirname = path.dirname(fileURLToPath(import.meta.url));
+        //json
+        const filePath = path.join(__dirname, '..', '..', 'botQQ_screenshots', 'mcData.json');
+        let data: any = {};
+        if (fs.existsSync(filePath)) {
+            const fileContent = fs.readFileSync(filePath, 'utf-8');
+            data = JSON.parse(fileContent);
+        } else {
+            fs.writeFileSync(filePath, JSON.stringify(data));
+        }
+        if (data[seedId]) {
+            data[seedId].plId = plId;
+            data[seedId].updatetime = new Date().getTime()
+        } else {
+            data[seedId] = {
+                plId,
+                createtime: new Date().getTime(),
+                updatetime: new Date().getTime(),
+            }
+        }
+        fs.writeFileSync(filePath, JSON.stringify(data));
+        return;
+    }
+
+    private async readpl(seedId: number): Promise<{ plId: string, createtime: number, updatetime: number } | undefined> {
+        const __dirname = path.dirname(fileURLToPath(import.meta.url));
+        const filePath = path.join(__dirname, '..', '..', 'botQQ_screenshots', 'mcData.json');
+        let data: any = {};
+        if (fs.existsSync(filePath)) {
+            const fileContent = fs.readFileSync(filePath, 'utf-8');
+            data = JSON.parse(fileContent);
+            if (data[seedId]) {
+                return data[seedId];
+            }
+        }
+        if (data[seedId]) {
+            return data[seedId];
+        }
+    }
+    //deletepl
+    private async deletepl(seedId: number): Promise<void> {
+        const __dirname = path.dirname(fileURLToPath(import.meta.url));
+        const filePath = path.join(__dirname, '..', '..', 'botQQ_screenshots', 'mcData.json');
+        let data: any = {};
+        if (fs.existsSync(filePath)) {
+            const fileContent = fs.readFileSync(filePath, 'utf-8');
+            data = JSON.parse(fileContent);
+            if (data[seedId]) {
+                delete data[seedId];
+                fs.writeFileSync(filePath, JSON.stringify(data));
+            }
+        }
+    }
+
+
+
+}

+ 4 - 1
src/plugins/test.ts

@@ -67,17 +67,20 @@ export class test {
             param1,//参数1,用于显示在菜单中
             param2,//参数2,用于显示在菜单中
             param3,//参数3,用于显示在菜单中
+            //渲染优先级 url渲染 > 简易渲染 > 模版渲染
             template: { // 模板配置,用于发送图片内容
                 enabled: true,//是否启用模板,启用将发送图片内容
                 sendText: false,//是否发送文本,启用将发送文本内容,如果都启用则发送两条消息
                 path: path.resolve(__dirname, '..', 'resources', 'test', 'param.html'),//模版路径,推荐按规范放置在resources目录下
+                html: `<div>简约自定义html渲染内容</div>`,//简易渲染,填写html内容
                 render: {//浏览器默认参数设置,用于打开浏览器的设置
                     width: 600, // 模板宽度
                     height: 1, // 模板高度
                     type: 'png',// 模板类型
                     quality: 100,// 模板质量
                     fullPage: false,// 是否全屏
-                    background: true// 是否背景
+                    background: true,// 是否背景
+                    url: 'http://www.baidu.com'// 直接使用网站截图渲染支持90%的网站,需要自行测试
                 }
             },
             toString() { //重写toString方法,用于返回文本内容,启用sendText时将发送文本内容,不启用时将发送图片内容,图片发送失败时发送文字内容