├── .all-contributorsrc
├── .eslintrc.cjs
├── .github
└── ISSUE_TEMPLATE
│ ├── bug.yml
│ ├── config.yml
│ ├── enhancement.yml
│ └── question.yml
├── .gitignore
├── .hintrc
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── apps
├── CodeUpdate.js
├── Picture.js
├── Poke.js
├── Summary.js
├── help.js
├── op.js
├── sendMaster.js
├── update.js
└── version.js
├── components
├── Config.js
├── Data.js
├── Version.js
└── index.js
├── config
├── .gitignore
└── system
│ ├── config.yaml
│ ├── help_system.js
│ └── 请勿修改此目录下的文件.txt
├── constants
├── Path.js
└── Poke.js
├── guoba.support.js
├── guoba
├── configInfo.js
├── index.js
├── pluginInfo.js
└── schemas
│ ├── CodeUpdata.js
│ ├── Picture.js
│ ├── Poke.js
│ ├── SendMaster.js
│ ├── Summary.js
│ ├── index.js
│ ├── other.js
│ └── proxy.js
├── index.js
├── jsconfig.json
├── lib
├── common
│ └── common.js
├── load
│ └── loadApps.js
├── puppeteer
│ └── render.js
└── request
│ └── request.js
├── model
├── CodeUpdate.js
├── GitRepo.js
├── RandomFile.js
├── api
│ ├── GitApi.js
│ └── index.js
├── help
│ └── theme.js
├── index.js
├── sendMasterMsg.js
└── summary.js
├── package.json
└── resources
├── CodeUpdate
├── icon
│ ├── GitHub.svg
│ ├── Gitcode.svg
│ ├── Gitee.svg
│ ├── branch.svg
│ └── tag.svg
├── index.css
├── index.html
└── index.scss
├── font
└── font.woff
├── help
├── imgs
│ ├── card-bg.png
│ └── icon.png
├── index.css
├── index.html
├── index.scss
├── theme
│ ├── default
│ │ ├── bg.jpg
│ │ ├── config.js
│ │ └── main.jpg
│ └── 目录说明.txt
├── version-info.css
├── version-info.html
└── version-info.scss
└── img
├── Roxy.png
└── ys.png
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | "README.md"
4 | ],
5 | "imageSize": 100,
6 | "commit": false,
7 | "commitType": "docs",
8 | "commitConvention": "angular",
9 | "contributors": [
10 | {
11 | "login": "yeyang52",
12 | "name": "椰羊",
13 | "avatar_url": "https://avatars.githubusercontent.com/u/107110851?v=4",
14 | "profile": "https://github.com/yeyang52",
15 | "contributions": [
16 | "example",
17 | "code"
18 | ]
19 | },
20 | {
21 | "login": "TimeRainStarSky",
22 | "name": "时雨◎星空",
23 | "avatar_url": "https://avatars.githubusercontent.com/u/63490117?v=4",
24 | "profile": "https://github.com/TimeRainStarSky",
25 | "contributions": [
26 | "mentoring",
27 | "code"
28 | ]
29 | },
30 | {
31 | "login": "qsyhh",
32 | "name": "其实雨很好",
33 | "avatar_url": "https://avatars.githubusercontent.com/u/132750431?v=4",
34 | "profile": "https://github.com/qsyhh",
35 | "contributions": [
36 | "code"
37 | ]
38 | },
39 | {
40 | "login": "Admilkk",
41 | "name": "Admilk",
42 | "avatar_url": "https://foruda.gitee.com/avatar/1706324987763497611/13205155_adrae_1706324987.png!avatar200",
43 | "profile": "https://gitee.com/adrae",
44 | "contributions": [
45 | "code"
46 | ]
47 | },
48 | {
49 | "login": "kesally",
50 | "name": "kesally",
51 | "avatar_url": "https://avatars.githubusercontent.com/u/110397533?v=4",
52 | "profile": "https://gitee.com/kesally",
53 | "contributions": [
54 | "code"
55 | ]
56 | },
57 | {
58 | "login": "shanhai233",
59 | "name": "桃缘十三",
60 | "avatar_url": "https://foruda.gitee.com/avatar/1723727797498359874/8750220_shanhai233_1723727797.png!avatar200",
61 | "profile": "https://gitee.com/shanhai233",
62 | "contributions": [
63 | "code"
64 | ]
65 | },
66 | {
67 | "login": "hmexy",
68 | "name": "心愿XY",
69 | "avatar_url": "https://avatars.githubusercontent.com/u/112873708?v=4",
70 | "profile": "https://github.com/hmexy",
71 | "contributions": [
72 | "code"
73 | ]
74 | }
75 | ],
76 | "contributorsPerLine": 7,
77 | "skipCi": true,
78 | "repoType": "github",
79 | "repoHost": "https://github.com",
80 | "projectName": "DF-Plugin",
81 | "projectOwner": "Denfenglai"
82 | }
83 |
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | es2021: true,
4 | node: true
5 | },
6 | extends: [
7 | "standard",
8 | "plugin:jsdoc/recommended",
9 | "plugin:import/recommended",
10 | "plugin:promise/recommended"
11 | ],
12 | parserOptions: {
13 | ecmaVersion: "latest",
14 | sourceType: "module"
15 | },
16 | plugins: [ "import", "promise", "jsdoc" ],
17 | globals: {
18 | Bot: true,
19 | redis: true,
20 | logger: true,
21 | plugin: true,
22 | segment: true
23 | },
24 | rules: {
25 | "eqeqeq": [ "off" ],
26 | "prefer-const": [ "off" ],
27 | "arrow-body-style": "off",
28 | "camelcase": "off",
29 | "quotes": [ "error", "double" ],
30 | "quote-props": [ "error", "consistent" ],
31 | "no-eval": [ "error", { allowIndirect: true } ],
32 | "array-bracket-newline": [ "error", { multiline: true } ],
33 | "array-bracket-spacing": [ "error", "always" ],
34 | "space-before-function-paren": [ "error", "never" ],
35 | "no-invalid-this": "error",
36 | "jsdoc/require-returns": 0,
37 | "jsdoc/require-jsdoc": 0,
38 | "jsdoc/require-param-description": 0,
39 | "jsdoc/require-returns-description": 0,
40 | "jsdoc/require-param-type": 0,
41 | "import/extensions": [ "error", "ignorePackages" ],
42 | "one-var": [ "off" ]
43 | },
44 | settings: {
45 | "import/resolver": {
46 | "custom-alias": {
47 | alias: {
48 | "#components": "./components/index.js",
49 | "#model": "./model/index.js"
50 | },
51 | extensions: [ ".js", ".json", ".jsx", ".ts", ".tsx" ]
52 | }
53 | }
54 | },
55 | ignorePatterns: [ "test.js" ]
56 | }
57 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug.yml:
--------------------------------------------------------------------------------
1 | name: Bug 报告
2 | description: 创建一份报告来帮助我们改进
3 | title: "[bug]:"
4 | labels: ["bug"]
5 | body:
6 | - type: checkboxes
7 | attributes:
8 | label: 这个问题是否已经存在?
9 | options:
10 | - label: 我已经搜索过[现有的问题](https://github.com/DenFengLai/DF-Plugin/issues)并未发现相同问题
11 | required: true
12 |
13 | - type: input
14 | attributes:
15 | label: Yunzai版本
16 | description: 您使用的哪个版本的Yunzai
17 | placeholder: Miao-Yunzai | TRSS-Yunzai
18 | validations:
19 | required: true
20 |
21 | - type: input
22 | attributes:
23 | label: Node.js版本
24 | description: 使用`node -v`获取
25 |
26 | - type: input
27 | attributes:
28 | label: 协议端名称
29 | description: 您是使用的哪个协议出现的问题?
30 | placeholder: ICQQ或其他
31 | validations:
32 | required: true
33 |
34 | - type: input
35 | attributes:
36 | label: 操作系统
37 | description: 您的Yunzai使用的是哪个操作系统
38 | placeholder: 如Windows 10, MacOS, Mojave, Ubuntu 20.04等
39 |
40 | - type: textarea
41 | attributes:
42 | label: 复现步骤
43 | description: 请尽量详细地描述你的操作过程,以便我们更好地理解问题。
44 | placeholder: |
45 | 1. 第一步
46 | 2. 第二步
47 | 3. 第三步
48 | ...
49 | validations:
50 | required: true
51 |
52 | - type: textarea
53 | attributes:
54 | label: 预期行为
55 | description: 描述您期望在完成上述步骤后,系统应有的正确行为或输出。
56 |
57 | - type: textarea
58 | attributes:
59 | label: 实际行为
60 | description: 描述系统实际的行为或输出。
61 | validations:
62 | required: true
63 |
64 | - type: textarea
65 | attributes:
66 | label: 错误日志或截图
67 | description: 如果有的话,请附上相关错误日志或截图。请确保不包含敏感信息。
68 | placeholder: |
69 | 在这里上传截图或错误日志
70 |
71 | - type: textarea
72 | attributes:
73 | label: 其他说明
74 | description: 如果您有任何其他信息或说明,请在这里补充。
75 |
76 | - type: input
77 | attributes:
78 | label: 特殊配置
79 | description: 是否有进行任何特别的插件配置或修改?
80 | placeholder: 修改过xx配置文件xx字段为xx
81 |
82 | - type: dropdown
83 | attributes:
84 | label: 复现频率
85 | description: 问题出现的频率
86 | options:
87 | - 仅这一次
88 | - 偶尔出现
89 | - 经常出现
90 | - 每次出现
91 | validations:
92 | required: true
93 |
94 | - type: checkboxes
95 | attributes:
96 | label: DF-Plugin是否为当前最新版本?
97 | description: 请确保您已使用最新版本的DF-Plugin,以避免已知问题。
98 | options:
99 | - label: 我最近已使用过`#DF更新`命令进行更新至最新版本
100 | required: true
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: true
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/enhancement.yml:
--------------------------------------------------------------------------------
1 | name: 功能建议
2 | description: 对本插件提出一个功能建议
3 | title: "[功能建议]: "
4 | labels: ["enhancement"]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | 感谢提出功能建议,我们将仔细考虑!
10 |
11 | - type: input
12 | attributes:
13 | label: 问题描述
14 | description: 请简要描述您想要的新功能。
15 | validations:
16 | required: true
17 |
18 | - type: input
19 | attributes:
20 | label: 实现方式
21 | description: 请简要描述您希望实现这个新功能的方式。
22 | validations:
23 | required: true
24 |
25 | - type: input
26 | attributes:
27 | label: 当前的问题
28 | description: 如果没有这个新功能,您当前面临的问题是什么?
29 |
30 | - type: textarea
31 | attributes:
32 | label: 其他说明
33 | description: 如果您有任何其他信息或说明,请在这里补充。
34 |
35 | - type: checkboxes
36 | attributes:
37 | label: 意向参与贡献
38 | options:
39 | - label: 我有意向参与具体功能的开发实现并将代码贡献到本插件
40 | required: false
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/question.yml:
--------------------------------------------------------------------------------
1 | name: 其他问题及意见反馈
2 | description: 插件使用上的问题反馈、功能失效等。
3 | title: "[问题反馈]: "
4 | labels: ["question"]
5 | body:
6 | - type: input
7 | attributes:
8 | label: 反馈内容
9 | description: 请简要描述您需要反馈的内容。
10 | validations:
11 | required: true
12 |
13 | - type: textarea
14 | attributes:
15 | label: 其他说明
16 | description: 如果您有任何其他信息或补充说明,请在这里添加。
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | data/
3 | *_test.js
4 | test.*
5 | *.log
6 | .vscode/
7 | temp/
8 | *.css.map
9 | pnpm-lock.yaml
10 | package-lock.json
11 | yarn.lock
12 | resources/poke/
--------------------------------------------------------------------------------
/.hintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "development"
4 | ]
5 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 | # 1.1.5
3 |
4 | * 移除`随机网易云`功能
5 | * 修复Miao-Yunzai环境下插件加载失败
6 | * 联系主人功能可配置不回复主人账号
7 | * Git仓库监听新增支持多配置
8 | * 重构配置结构,新版配置文件已移动至`Yz/config/DF-Plugin.yaml` (**@时雨星空**)
9 | * 适配锅巴插件配置分组,如遇未知组件请更新插件
10 |
11 | # 1.1.4
12 |
13 | * 优化插件加载用时
14 | * 修复联系主人消息无法获取seq导致无法回复
15 | * 新增Git仓库发行版监听
16 | * 优化对image的消息处理
17 |
18 | # 1.1.3
19 |
20 | * `CodeUpdate:`
21 | * 优化仓库监听数据存储
22 | * 重构代码,并发获取数据,减少等待时间
23 | * 可配置推送至好友
24 | * 将代码更新默认检查频率调整为30分钟/次
25 | * 插件新增配置自动更新,在重启时能够合并用户配置与默认配置
26 |
27 | # 1.1.2
28 |
29 | * `CodeUpdate:`
30 | * 优化了代码更新检查逻辑
31 | * 新增文件变化数量和行数增减的显示
32 | * 在配置文件中增加排除列表,用于忽略不希望被监听的插件
33 | * 添加了作者和提交者头像的处理,在无头像时使用首个字符代替
34 | * 优化标题显示
35 |
36 | # 1.1.1
37 |
38 | * `CodeUpdate:`
39 | * 新增自动获取已安装插件地址
40 | * 修复在无提交者头像时报错的情况
41 | * 代码更新支持指定分支或提交SHA
42 | * 显示作者和提交者时间
43 | * 调整字体和布局
44 | * 优化了代码更新功能,提高了更新检查的效率和准确性
45 | * 增加了分支信息展示,使更新内容更加详细
46 | * 修复仓库路径会随着时间重复
47 |
48 | # 1.1.0
49 |
50 | * Git更新推送
51 | * 提交信息显示编写者与提交者
52 | * 重构UI样式
53 | * 优化数据处理逻辑
54 | * 显示提交者头像
55 | * 优化获取数据时的日志输出
56 | * 使用Git平台logo代替文字
57 | * 使用Yenai-Plugin同款字体
58 | * 感谢 **@椰羊(yeyang52)**
59 |
60 | # 1.0.10
61 |
62 | * Git更新推送支持显示提交者和多久前提交
63 | * 联系主人cd可填0关闭
64 | * 随机类型戳一戳可配置排除类型
65 | * 戳一戳新增永雏塔菲表情包
66 | * 图片外显新增列表随机模式
67 |
68 | # 1.0.9
69 |
70 | * 戳一戳图库内的图片可支持指令触发
71 | * 戳一戳图片类型新增 ATRI 表情包
72 | * **修复**安装图库后不生效
73 | * 戳一戳新增绫地宁宁表情包
74 | * 千恋万花小表情整理后重命名为柚子厨小表情
75 | * 戳一戳图片可将类型设置为`all`随机全部类型表情包
76 |
77 | # 1.0.8
78 |
79 | * 更新戳一戳图片类型
80 | * 戳一戳支持无图库时使用XY-Api调用
81 | * 感谢 **@心愿** 提供Api服务支持
82 | * Git仓库更新推送独立Gitee与Github的仓库监听,可配置个人token用于解除api速率限制 (**By @桃源十三**)
83 | * 新增贡献指南,开发者可阅读贡献指南对本项目发起贡献
84 |
85 | # 1.0.7
86 |
87 | * 优化Git仓库更新检查定时任务开关逻辑
88 | * 新增`#DF帮助` **By @kesally**
89 | * 新增图片外显功能
90 | * 每条图片带上外显
91 | * 可选使用一言或自定义文本
92 | * 新增`#DF版本`ⁿᵉʷ
93 | * 独立戳一戳图库以减少插件大小
94 | * 新增`#DF更新图库`ⁿᵉʷ
95 |
96 | # 1.0.6
97 |
98 | * 修复联系主人无法过滤群聊艾特
99 | * 优化锅巴配置组件
100 | * 联系主人新增`违禁词`、`禁用用户`、`禁用群`配置项ⁿᵉʷ
101 | * 新增Git仓库更新推送至群聊ⁿᵉʷ
102 | * 联系主人消息别名过滤适配TRSS-Yunzai
103 | * 更新丛雨表情包图片
104 | * 新增`随机丛雨`、`随机诗歌剧`ⁿᵉʷ
105 |
106 | # 1.0.5
107 |
108 | * 联系主人消息支持快速回复
109 | * 支持锅巴
110 | * 适配喵崽消息回复
111 | * 修复`来张cos`ⁿᵉʷ
112 | * 新增`#DF更新日志`ⁿᵉʷ
113 | * 优化联系主人
114 |
115 | # 1.0.4
116 |
117 | * 随机图片戳一戳改用本地图片
118 | * 新增丛雨、诗歌剧、千恋万花、小南梁表情包
119 | * 联系主人新增`sendAvatar`配置项
120 | * 联系主人支持自动过滤消息中别名,艾特
121 | * 引入开发依赖 `Gitmoji-Cli`
122 | * 优化联系主人通知排版
123 | * 删除`#文转卡`功能(失效)
124 |
125 | # 1.0.3
126 |
127 | * 新增随机图片戳一戳ⁿᵉʷ
128 | * 图转卡失效,已删除
129 | * 新增原神关键词发图ⁿᵉʷ
130 | * 无用的功能加一
131 | * 引入开发依赖
132 | * `husky`
133 | * `ESLint`
134 | * `lint-staged`
135 |
136 | # 1.0.2
137 |
138 | * 新增`#联系主人`功能ⁿᵉʷ
139 | * 修复图转卡
140 | * 图转卡支持更多参数
141 | * 支持用户自定义配置
142 |
143 | # 1.0.1
144 |
145 | * 新增`#文转卡`功能ⁿᵉʷ
146 | * 图转卡支持自定义外显ⁿᵉʷ
147 | * 新增`#图转卡`功能ⁿᵉʷ
148 | * 新增API随机图片功能ⁿᵉʷ
149 | * 新增随机音乐功能ⁿᵉʷ
150 |
151 | # 1.0.0
152 |
153 | * 初版发布!
154 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # 贡献指南
2 |
3 | 感谢您愿意为本项目做出贡献!遵循以下准则和步骤,以确保您的贡献符合项目的要求。
4 |
5 | ## 环境要求
6 |
7 | 在开始贡献之前,请确保您的开发环境满足以下要求:
8 |
9 | - 安装 Node.js (推荐最新稳定版)
10 | - 安装 Yunzai-Bot
11 | - 安装 pnpm
12 | - VSCode (可选)
13 |
14 | ## Fork本项目
15 |
16 | 前往[Gitee](https://gitee.com/DenFengLai/DF-Plugin)或[Github](https://gitee.com/DenFengLai/DF-Plugin)项目地址点击仓库上方的"fork"(或类似的按钮)将本项目fork到你的账户
17 |
18 | ## 克隆fork后的项目到本地
19 |
20 | ```sh
21 | git clone 你fork的仓库地址
22 | ```
23 |
24 | ## 安装依赖
25 |
26 | ```sh
27 | pnpm install
28 | ```
29 |
30 | ## 开发过程
31 |
32 | 在开发过程中请务必遵守以下规则
33 |
34 | - 严格遵守ESLint的代码规范,确保代码质量并与本项目其他代码风格保持一致,提交前使用ESLint检查一遍后并解决发现的问题
35 | - 推荐安装使用VSCode的[ESLint拓展](https://github.com/Microsoft/vscode-eslint),它可以帮助你在编写过程提示你所遇到的问题并提供解决方案
36 | - 尽量遵循项目的代码风格和命名约定,以保持代码的可读性
37 | - 提交的代码应该是经过测试的,并且不会破坏现有的功能。
38 | - 在提交时,请务必遵守[Gitmoji](https://gitmoji.dev)典范,可以使用[VSCode拓展](https://github.com/seatonjiang/gitmoji-vscode)帮助你选择对应的Gitmoji
39 |
40 | 不合格的代码将会被打回
41 |
42 | ## 提交规范
43 |
44 | 为了确保提交的代码符合项目的要求,我们使用 Husky 和 lint-staged 进行 Git 提交时的规范检测。请按照以下步骤进行提交:
45 |
46 | ```sh
47 | pnpm husky
48 | git add .
49 | pnpm run commit # 或 git commit
50 | ```
51 |
52 | - 在提交时,Husky 会自动运行预定义的 Git 钩子脚本,包括对代码规范的检测,同时会使用[Gitmoji-cli](https://github.com/carloscuesta/gitmoji)进行交互式提交
53 |
54 | - 如果提交的代码不符合项目的规范要求,您将会收到相应的错误提示。请根据提示信息进行修改和调整,直到提交的代码符合要求。
55 |
56 | 如果你是在VSCode上进行提交,请先确保您的代码已经通过 ESLint 的检查,随后便可在 “源代码管理” 提交框上方选择对应的Gitmoji后即可提交。
57 |
58 | ## 提交拉取请求
59 |
60 | 当您准备好将您的贡献合并到主项目中时,请按照以下步骤提交拉取请求:
61 |
62 | 1. 将您的本地分支推送到远程仓库:`git push origin master`
63 |
64 | 2. 在项目仓库的页面上,点击 "New Pull Request"(或类似的按钮),创建一个新的拉取请求。
65 |
66 | 3. 填写拉取请求的相关信息,包括描述您的贡献的详细内容和目的。
67 |
68 | 4. 提交拉取请求后,项目维护者将会审核您的代码,并与您协作以确保贡献的质量和一致性。
69 |
70 | ## 感谢您的贡献
71 |
72 | 非常感谢您为项目做出的贡献!您的工作对于项目的发展和成功至关重要。项目维护者会尽快审查您的贡献并与您合作,以确保其顺利合并到主项目中。
73 |
74 | 如果您有任何问题或需要进一步的帮助,请随时与项目维护者进行沟通。
75 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 等风来
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DF-Plugin
2 |
3 | 适用于Miao-Yunzai和TRSS-Yunzai的拓展插件。
4 |
5 | 
6 | 
7 | [](https://gitmoji.dev)
8 | [](https://github.com/Denfenglai/DF-Plugin/blob/master/LICENSE)
9 |
10 | [](https://github.com/DenFengLai/DF-Plugin)
11 | [](https://github.com/yoimiya-kokomi/Miao-Yunzai)
12 | [](https://github.com/TimeRainStarSky/Yunzai)
13 |
14 | [](https://gitee.com/DenFengLai/DF-Plugin/stargazers)
15 | [](https://gitee.com/DenFengLai/DF-Plugin/members)
16 | [](https://github.com/DenFengLai/DF-Plugin/stargazers)
17 | [](https://github.com/DenFengLai/DF-Plugin/network)
18 | 
19 |
20 | [](https://gitee.com/DenFengLai/DF-Plugin)
21 |
22 | ## 💡 安装教程
23 |
24 | - 使用Github
25 |
26 | ```sh
27 | git clone --depth=1 https://github.com/DenFengLai/DF-Plugin.git ./plugins/DF-Plugin
28 | ```
29 |
30 | - 使用Gitee
31 |
32 | ```sh
33 | git clone --depth=1 https://gitee.com/DenFengLai/DF-Plugin.git ./plugins/DF-Plugin
34 | ```
35 |
36 | ### 🔧 安装依赖
37 |
38 | ```sh
39 | pnpm install
40 | ```
41 |
42 | ## 🤗 已实现的功能
43 |
44 | 随机图片
45 |
46 | - #来张JK / 黑丝 / cos / 腿子 / 丛雨 /诗歌剧
47 |
48 | > 随机发送一张图片
49 |
50 |
51 |
52 | 给主人带话
53 |
54 | - #联系主人 + `消息内容`
55 |
56 | > #回复<内容> 或 #回复<消息标识><空格><内容>
57 |
58 |
59 |
60 | 随机表情戳一戳
61 |
62 | > 戳一戳返回随机表情包
63 | > 使用 #DF安装图库 可安装图库到本地使用
64 | > 未安装图库将调用[XY-Api](https://api.yugan.love/)
65 |
66 |
67 |
68 | Git仓库更新推送
69 |
70 | > 在[配置文件](/config/default_config/CodeUpdate.yaml)配置完成填写群号后即可使用。
71 | > 推荐使用[锅巴插件](https://gitee.com/guoba-yunzai/guoba-plugin.git)进行配置
72 |
73 | - `#检查仓库更新`: 检查有没有仓库更新(相当于主动触发定时逻辑)
74 | - `#推送仓库更新`: 不管有没有更新都回复到当前会话,不会推送所有群组
75 |
76 |
77 |
78 | 图片外显
79 |
80 | > 配置请看[summary.yaml](./config/default_config/summary.yaml)
81 | > 推荐使用[锅巴插件](https://gitee.com/guoba-yunzai/guoba-plugin.git)进行配置
82 |
83 | - #开启/关闭图片外显
84 | - #设置图片外显 + 文字
85 |
86 |
87 |
88 | ---
89 |
90 | > 更多功能请使用 `#DF帮助`
91 | > 查看近期更改请使用`#DF版本`
92 |
93 | ## ⚙️ 插件配置
94 |
95 | 本插件已全面兼容[锅巴插件](https://gitee.com/guoba-yunzai/guoba-plugin.git),推荐使用锅巴插件进行配置。
96 |
97 | ## 📄 计划工程
98 |
99 | - [x] 能跑
100 | - [x] 能用
101 | - [x] 支持用户自定义配置
102 | - [x] 添加帮助信息和版本信息 [@kesally](https://gitee.com/kesally)
103 | - [ ] 丰富功能
104 | - [ ] 持续完善
105 | - [ ] ~~删库跑路~~
106 |
107 | ## ✨ 贡献者
108 |
109 |
110 | [](#contributors-)
111 |
112 |
113 | 感谢这些了不起的人 ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
114 |
115 |
116 |
117 |
118 |
131 |
132 |
133 |
134 |
135 |
136 |
137 | 本段遵循 [all-contributors](https://github.com/all-contributors/all-contributors) 规范,欢迎任何形式的贡献!
138 |
139 | ## 💬 免责声明
140 |
141 | 1. 本项目仅供学习使用,请勿用于商业等场景。
142 |
143 | 2. 项目内图片、API等资源均来源于网络,如侵犯了您的利益请及时联系项目开发者进行删除。
144 |
145 | ## 🍀 意见反馈
146 |
147 | 如果您对本插件有什么建议或使用遇到了问题欢迎对本项目提交[issues](https://github.com/DenFengLai/DF-Plugin/issues/new)。
148 |
149 | ## 🎨 参与贡献
150 |
151 | 如果您有兴趣对本项目做出贡献,请阅读[贡献指南](./CONTRIBUTING.md)。
152 |
153 | ## 👥 联系我们
154 |
155 | - QQ交流群: [964193559](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=hGiK1lQOmbJzP7S0xm-00NKdNi9Oe8Ma&authKey=aQRGoOwAyQ%2BYZ%2BZ5QNKJegwf5Y%2BgYM3Y%2F3%2Fc61cSquEuoIPM1qKemM6ajHb0sRFk&noverify=0&group_code=964193559)
156 |
157 | ## ⭐ 支持本项目
158 |
159 | 如果你觉得本项目对你有帮助,请给本项目点点star,你是鼓励是我们前进的动力。
160 |
161 | ## ❤️ 赞助作者
162 |
163 | - [爱发电](https://afdian.com/a/DenFengLai)
164 |
165 | ## 💝 友情链接
166 |
167 | - [TRSS.me](https://TRSS.me)
168 | - [Yenai-Plugin](https://Yenai.TRSS.me)
169 | - [Fanji-plugin](http://gitee.com/adrae/Fanji-plugin)
170 | - [DF-Poke](https://gitea.eustia.fun/XY/poke.git)
171 |
172 | ## 🎁 特别鸣谢
173 |
174 | - [XY-Api](https://api.yugan.love/):提供戳一戳图片接口服务支持
175 | - [素颜Api](https://api.suyanw.cn):提供部分Api服务
176 |
--------------------------------------------------------------------------------
/apps/CodeUpdate.js:
--------------------------------------------------------------------------------
1 | import { CodeUpdate as Cup } from "#model"
2 | import { Config } from "#components"
3 |
4 | export class CodeUpdate extends plugin {
5 | constructor() {
6 | super({
7 | name: "DF:仓库更新推送",
8 | dsc: "检查指定Git仓库是否更新并推送",
9 | event: "message",
10 | priority: 5000,
11 | rule: [
12 | {
13 | reg: "^#(检查|推送)仓库更新$",
14 | fnc: "cupdate"
15 | }
16 | ]
17 | })
18 |
19 | if (Config.CodeUpdate.Auto) {
20 | this.task = {
21 | cron: Config.CodeUpdate.Cron,
22 | name: "[DF-Plugin]Git仓库更新检查",
23 | fnc: () => Cup.checkUpdates(true)
24 | }
25 | }
26 | }
27 |
28 | async cupdate(e) {
29 | const isPush = e.msg.includes("推送")
30 | e.reply(`正在${isPush ? "推送" : "检查"}仓库更新,请稍等`)
31 |
32 | const res = await Cup.checkUpdates(!isPush, e)
33 | if (!isPush) {
34 | const msg = res?.number > 0
35 | ? `检查完成,共有${res.number}个仓库有更新,正在按照你的配置进行推送哦~`
36 | : "检查完成,没有发现仓库有更新"
37 | return e.reply(msg)
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/apps/Picture.js:
--------------------------------------------------------------------------------
1 | import { imagePoke as RandomFace } from "#model"
2 | import { Config, Poke_List as Face_List, request } from "#components"
3 |
4 | export class Random_Pictures extends plugin {
5 | constructor() {
6 | super({
7 | name: "DF:随机图片",
8 | dsc: "随机返回一张图片",
9 | event: "message",
10 | priority: 500,
11 | rule: [
12 | {
13 | reg: "^#?(来张|看看|随机)([jJ][kK]|制服(小姐姐)?|黑丝|[Cc][Oo][Ss]|腿子?)$",
14 | fnc: "handleRequest"
15 | },
16 | {
17 | reg: `^#?(随机|来张)?(${Face_List.join("|")})$`,
18 | fnc: "handleRequest"
19 | },
20 | {
21 | reg: "^#?[Dd][Ff](随机)?表情包?列表$",
22 | fnc: "list"
23 | }
24 | ]
25 | })
26 | }
27 |
28 | get open() {
29 | return Config.Picture.open
30 | }
31 |
32 | async list(e) {
33 | return e.reply(`表情包列表:\n${Face_List.join("、")}`, true)
34 | }
35 |
36 | async handleRequest(e) {
37 | if (!this.open) return false
38 |
39 | const msg = e.msg
40 | let response = []
41 |
42 | if (msg.includes("jk") || msg.includes("制服")) {
43 | response = [ segment.image("https://api.suyanw.cn/api/jk.php") ]
44 | } else if (msg.includes("黑丝")) {
45 | response = [ "唉嗨害,黑丝来咯", segment.image("https://api.suyanw.cn/api/hs.php") ]
46 | } else if (msg.includes("cos")) {
47 | const link = (await request.get("https://api.suyanw.cn/api/cos.php?type=json")).text.replace(/\\/g, "/")
48 | response = [ "cos来咯~", segment.image(link) ]
49 | } else if (msg.includes("腿")) {
50 | const link = (await request.get("https://api.suyanw.cn/api/meitui.php", { responseType: "text" })).match(/https?:\/\/[^ ]+/)?.[0]
51 | response = [ "看吧涩批!", segment.image(link) ]
52 | } else if (msg.includes("随机") || msg.includes("来张") || Config.Picture.Direct) {
53 | const name = msg.replace(/#|随机|来张/g, "")
54 | const file = RandomFace(name)
55 | if (file) {
56 | response = [ segment.image(file) ]
57 | }
58 | } else {
59 | return false
60 | }
61 |
62 | if (response.length > 0) {
63 | return e.reply(response, true)
64 | }
65 |
66 | return false
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/apps/Poke.js:
--------------------------------------------------------------------------------
1 | import fs from "node:fs"
2 | import { imagePoke } from "#model"
3 | import { Config, Poke_List, Poke_Path } from "#components"
4 |
5 | if (!fs.existsSync(Poke_Path) && Config.other.chuo) logger.mark("[DF-Plugin] 检测到未安装戳一戳图库 将调用XY-Api返回图片")
6 |
7 | export class DF_Poke extends plugin {
8 | constructor() {
9 | super({
10 | name: "DF:戳一戳",
11 | dsc: "戳一戳机器人发送随机表情包",
12 | event: "notice.*.poke",
13 | priority: -114,
14 | rule: [ { fnc: "poke", log: false } ]
15 | })
16 | }
17 |
18 | async poke() {
19 | const { chuo, chuoType } = Config.other
20 | if (!chuo) return false
21 | if (this.e.target_id !== this.e.self_id) return false
22 | let name = chuoType
23 | if (chuoType !== "all") {
24 | name = Poke_List[chuoType]
25 | }
26 | if (!name) return false
27 | logger.info(`${logger.blue("[DF-Plugin]")}${logger.green("[戳一戳]")}获取 ${name} 图片`)
28 | const file = imagePoke(name)
29 | if (!file) return false
30 | return this.e.reply(segment.image(file))
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/apps/Summary.js:
--------------------------------------------------------------------------------
1 | import { Config } from "#components"
2 | import { Summary as Sum } from "#model"
3 |
4 | if (Config.summary.sum) Sum.lint()
5 |
6 | export class Summary extends plugin {
7 | constructor() {
8 | super({
9 | name: "DF:图片外显",
10 | dsc: "图片自动添加外显",
11 | event: "message",
12 | priority: 5000,
13 | rule: [
14 | {
15 | reg: "^#设置外显",
16 | fnc: "SetSum"
17 | },
18 | {
19 | reg: "^#?(开启|关闭)(图片)?外显$",
20 | fnc: "on"
21 | },
22 | {
23 | reg: "^#切换外显模式$",
24 | fnc: "yiyan"
25 | }
26 | ]
27 | })
28 | }
29 |
30 | async SetSum(e) {
31 | if (!e.isMaster) return
32 | if (!Config.summary.sum) return e.reply("❎ 请先启用该功能!")
33 | if (Config.summary.type == 2) return e.reply("❎ 该功能在一言模式下不可用,请先关闭一言")
34 | let sum = e.msg.replace(/^#设置外显/g, "").trim()
35 | if (!sum) return e.reply("请附带外显内容哦")
36 | Config.modify("summary", "text", sum)
37 | return e.reply("✅ 修改成功!")
38 | }
39 |
40 | async on(e) {
41 | if (!e.isMaster) return
42 | const type = /开启/.test(e.msg)
43 | if ((type && Config.summary.sum) || (!type && !Config.summary.sum)) return e.reply(`❎ 图片外显已处于${type ? "开启" : "关闭"}状态`)
44 | Config.modify("summary", "sum", type)
45 | Sum.Switch(type)
46 | e.reply(`✅ 已${type ? "开启" : "关闭"}图片外显`)
47 | }
48 |
49 | async yiyan(e) {
50 | if (!e.isMaster) return
51 | if (Config.summary.type === 1) {
52 | Config.modify("summary", "type", 2)
53 | e.reply("✅ 已切换至一言模式")
54 | } else {
55 | Config.modify("summary", "type", 1)
56 | e.reply("✅ 已切换至自定义文本模式")
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/apps/help.js:
--------------------------------------------------------------------------------
1 | import lodash from "lodash"
2 | import { common, Data } from "#components"
3 | import Theme from "../model/help/theme.js"
4 |
5 | export class help extends plugin {
6 | constructor() {
7 | super({
8 | name: "DF:帮助",
9 | dsc: "DF插件命令帮助",
10 | event: "message",
11 | priority: 2000,
12 | rule: [
13 | {
14 | reg: "^#?[Dd][Ff](插件)?帮助$",
15 | fnc: "help"
16 | }
17 | ]
18 | })
19 | }
20 |
21 | async help(e) {
22 | let custom = {}
23 | let help = {}
24 |
25 | let { diyCfg, sysCfg } = await Data.importCfg("help")
26 |
27 | custom = help
28 |
29 | let helpConfig = lodash.defaults(diyCfg.helpCfg || {}, custom.helpCfg, sysCfg.helpCfg)
30 | let helpList = diyCfg.helpList || custom.helpList || sysCfg.helpList
31 | let helpGroup = []
32 |
33 | lodash.forEach(helpList, (group) => {
34 | if (group.auth && group.auth === "master" && !e.isMaster) {
35 | return true
36 | }
37 |
38 | lodash.forEach(group.list, (help) => {
39 | let icon = help.icon * 1
40 | if (!icon) {
41 | help.css = "display:none"
42 | } else {
43 | let x = (icon - 1) % 10
44 | let y = (icon - x - 1) / 10
45 | help.css = `background-position:-${x * 50}px -${y * 50}px`
46 | }
47 | })
48 |
49 | helpGroup.push(group)
50 | })
51 | let themeData = await Theme.getThemeData(diyCfg.helpCfg || {}, sysCfg.helpCfg || {})
52 |
53 | return await common.render("help/index", {
54 | helpCfg: helpConfig,
55 | helpGroup,
56 | ...themeData
57 | }, { e, scale: 1.6 })
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/apps/op.js:
--------------------------------------------------------------------------------
1 | import { Config, Res_Path } from "#components"
2 |
3 | const ys = {}
4 |
5 | export class OP extends plugin {
6 | constructor() {
7 | super({
8 | name: "DF:原神关键词发图",
9 | dsc: "本来聊得好好的,突然有人聊起了原神,搞得大家都不高兴",
10 | event: "message.group",
11 | priority: 5001,
12 | rule: [
13 | {
14 | reg: "原神",
15 | fnc: "ys"
16 | }
17 | ]
18 | })
19 | }
20 |
21 | async ys() {
22 | if (ys[this.e.group_id] || !Config.other.ys) return false
23 | this.reply(segment.image(`${Res_Path}/img/ys.png`))
24 | ys[this.e.group_id] = true
25 | return false
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/apps/sendMaster.js:
--------------------------------------------------------------------------------
1 | import moment from "moment"
2 | import { ulid } from "ulid"
3 | import { common, Config } from "#components"
4 | import { imagePoke } from "#model"
5 | import { sendMasterMsg, extractMessageId, getSourceMessage, getMasterQQ } from "../model/sendMasterMsg.js"
6 | import { segment } from "oicq"
7 |
8 | const key = "DF:contact"
9 | let Sending = false
10 | segment.reply ??= (id) => ({ type: "reply", id })
11 | let ReplyReg = /^#?回复(\S+)\s?(.*)?$/
12 |
13 | export class SendMasterMsgs extends plugin {
14 | constructor() {
15 | super({
16 | name: "DF:联系主人",
17 | dsc: "给主人发送一条消息",
18 | event: "message",
19 | priority: 400,
20 | rule: [
21 | {
22 | reg: "^#联系主人",
23 | fnc: "contact"
24 | },
25 | {
26 | reg: ReplyReg,
27 | fnc: "Replys",
28 | event: "message.private"
29 | }
30 | ]
31 | })
32 | }
33 |
34 | /**
35 | * 联系主人
36 | * @param {object} e - 消息事件
37 | */
38 | async contact(e) {
39 | if (Sending) return e.reply("❎ 已有发送任务正在进行中,请稍候重试")
40 |
41 | let { open, cd, BotId, sendAvatar, banWords, banUser, banGroup, replyQQ } = Config.sendMaster
42 |
43 | if (!e.isMaster) {
44 | if (!open) return e.reply("❎ 该功能暂未开启,请先让主人开启才能用哦", true)
45 | if (cd !== 0 && await redis.get(key)) return e.reply("❎ 操作频繁,请稍后再试", true)
46 | if (banWords.some(item => e.msg.includes(item))) return e.reply("❎ 消息包含违禁词,请检查后重试", true)
47 | if (banUser.includes(e.user_id)) return e.reply("❎ 对不起,您不可用", true)
48 | if (e.isGroup && banGroup.includes(e.group_id)) return e.reply("❎ 该群暂不可用该功能", true)
49 | }
50 |
51 | Sending = true
52 |
53 | try {
54 | const message = await common.Replace(e, /#联系主人/)
55 | if (message.length === 0) return e.reply("❎ 消息不能为空")
56 |
57 | const type = e.bot?.version?.id || e?.adapter_id || "QQ"
58 | const avatar = sendAvatar ? segment.image(e.sender?.getAvatarUrl?.() || e.friend?.getAvatarUrl?.() || imagePoke("all")) : ""
59 | const user_id = `${e.sender.nickname}(${e.user_id})`
60 | const group = e.isGroup ? `${e.group.name || "未知群名"}(${e.group_id})` : "私聊"
61 | const bot = `${e.bot.nickname}(${e.bot.uin})`
62 | const time = moment().format("YYYY-MM-DD HH:mm:ss")
63 | const id = ulid().slice(-5)
64 |
65 | const msg = [
66 | `联系主人消息(${id})\n`,
67 | (avatar && sendAvatar) ? avatar : "",
68 | `平台: ${type}\n`,
69 | `用户: ${user_id}\n`,
70 | `来自: ${group}\n`,
71 | `BOT: ${bot}\n`,
72 | `时间: ${time}\n`,
73 | "消息内容:\n",
74 | ...message,
75 | "\n-------------\n",
76 | "引用该消息:#回复 <内容>"
77 | ]
78 |
79 | const info = {
80 | bot: e.bot.uin || Bot.uin,
81 | group: e.isGroup ? e.group_id : false,
82 | id: e.user_id,
83 | message_id: e.message_id
84 | }
85 |
86 | const masterQQ = getMasterQQ(Config.sendMaster)
87 | if (!Bot[BotId]) BotId = e.self_id
88 |
89 | try {
90 | await sendMasterMsg(msg, BotId)
91 | let _msg = "✅ 消息已送达"
92 | if (replyQQ) _msg += `\n主人的QQ:${masterQQ}`
93 | await e.reply(_msg, true)
94 | if (cd) redis.set(key, "1", { EX: cd })
95 | redis.set(`${key}:${id}`, JSON.stringify(info), { EX: 86400 })
96 | } catch (err) {
97 | await e.reply(`❎ 消息发送失败,请尝试自行联系:${masterQQ}\n错误信息:${err}`)
98 | logger.error(err)
99 | }
100 | } catch (err) {
101 | await e.reply("❎ 出错误辣,稍后重试吧")
102 | logger.error(err)
103 | } finally {
104 | Sending = false
105 | }
106 | }
107 |
108 | /**
109 | * 回复消息
110 | * @param {object} e - 消息事件
111 | */
112 | async Replys(e) {
113 | if (!e.isMaster) return false
114 |
115 | try {
116 | const source = await getSourceMessage(e)
117 | let MsgID, isInput = false
118 | if (source && (/联系主人消息/.test(source.raw_message))) {
119 | MsgID = extractMessageId(source.raw_message)
120 | } else {
121 | const regRet = ReplyReg.exec(e.msg)
122 | if (!regRet[1]) return logger.warn("[DF-Plugin] 未找到消息ID")
123 | else {
124 | MsgID = regRet[1].trim()
125 | isInput = true
126 | }
127 | }
128 |
129 | const data = await redis.get(`${key}:${MsgID}`)
130 | if (!data) return isInput ? false : e.reply("❎ 消息已失效或不存在")
131 |
132 | const { bot, group, id, message_id } = JSON.parse(data)
133 | const message = await common.Replace(e, isInput ? /#?回复(\S+)\s?/ : /#?回复/g)
134 | message.unshift(`主人${Config.sendMaster.replyQQ ? `(${e.user_id})` : ""}回复:\n`, segment.reply(message_id))
135 |
136 | this.Bot = Bot[bot] ?? Bot
137 |
138 | group ? await this.Bot.pickGroup(group).sendMsg(message) : await this.Bot.pickFriend(id).sendMsg(message)
139 |
140 | return e.reply("✅ 消息已送达")
141 | } catch (err) {
142 | e.reply("❎ 发生错误,请查看控制台日志")
143 | logger.error("[DF-Plugin]回复消息时发生错误:", err)
144 | return false
145 | }
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/apps/update.js:
--------------------------------------------------------------------------------
1 | import fs from "node:fs"
2 | import { exec } from "node:child_process"
3 | import { update as Update } from "../../other/update.js"
4 | import { Plugin_Name, Plugin_Path, Poke_Path } from "#components"
5 |
6 | let lock = false // 儿童锁(bushi)
7 |
8 | export class DFupdate extends plugin {
9 | constructor() {
10 | super({
11 | name: "DF:更新插件",
12 | event: "message",
13 | priority: 1000,
14 | rule: [
15 | {
16 | reg: "^#[Dd][Ff](插件)?(强制)?更新(日志)?$",
17 | fnc: "update"
18 | },
19 | {
20 | reg: "^#?[Dd][Ff](安装|(强制)?更新)(戳一戳)?图库$",
21 | fnc: "up_img"
22 | }
23 | ]
24 | })
25 | }
26 |
27 | async update(e = this.e) {
28 | let isLog = e.msg.includes("日志")
29 | let Type = isLog ? "#更新日志" : (e.msg.includes("强制") ? "#强制更新" : "#更新")
30 | e.msg = Type + Plugin_Name
31 | const up = new Update(e)
32 | up.e = e
33 | return isLog ? up.updateLog() : up.update()
34 | }
35 |
36 | async up_img(e) {
37 | if (!e.isMaster) return false
38 | if (lock) return e.reply("已有更新任务正在进行中,请勿重复操作!")
39 |
40 | lock = true
41 | try {
42 | if (fs.existsSync(Poke_Path)) {
43 | let isForce = e.msg.includes("强制")
44 | await e.reply(`开始${isForce ? "强制" : ""}更新图库啦,请主人稍安勿躁~`)
45 | await this.executeGitCommand(e, isForce)
46 | } else {
47 | await e.reply("开始安装戳一戳图库,可能需要一段时间,请主人稍安勿躁~")
48 | await this.cloneRepository(e)
49 | }
50 | } finally {
51 | lock = false
52 | }
53 | return true
54 | }
55 |
56 | /**
57 | * 更新图片资源。
58 | * @param {object} e - 事件对象。
59 | * @param {boolean} isForce - 是否使用强制更新。
60 | * @returns {Promise} 返回一个在 Git 命令执行完成时解析的 Promise。
61 | */
62 | executeGitCommand(e, isForce) {
63 | return new Promise((resolve) => {
64 | const command = isForce ? "git reset --hard origin/main && git pull --rebase" : "git pull"
65 | exec(command, { cwd: Poke_Path }, (error, stdout) => {
66 | if (error) {
67 | e.reply(`图片资源更新失败!\nError code: ${error.code}\n${error.stack}\n 请尝试使用 #DF强制更新图库 或稍后重试。`)
68 | } else if (/Already up to date/.test(stdout) || stdout.includes("最新")) {
69 | e.reply("目前所有图片都已经是最新了~")
70 | } else {
71 | const numRet = /(\d*) files changed,/.exec(stdout)
72 | if (numRet && numRet[1]) {
73 | e.reply(`更新成功,共更新了${numRet[1]}张图片~`)
74 | } else {
75 | e.reply("戳一戳图片资源更新完毕")
76 | }
77 | }
78 | resolve()
79 | })
80 | })
81 | }
82 |
83 | /**
84 | * 安装戳一戳图库
85 | * @param {object} e - 事件对象,用于回复消息。
86 | * @returns {Promise} - 返回一个Promise,当克隆操作完成时解析。
87 | */
88 | cloneRepository(e) {
89 | return new Promise((resolve) => {
90 | const command = "git clone --depth=1 https://gitea.eustia.fun/XY/poke.git ./resources/poke"
91 | exec(command, { cwd: Plugin_Path }, (error) => {
92 | if (error) {
93 | e.reply(`戳一戳图库安装失败!\nError code: ${error.code}\n${error.stack}\n 请稍后重试。`)
94 | } else {
95 | e.reply("戳一戳图库安装成功!您后续也可以通过 #DF更新图库 命令来更新图片")
96 | }
97 | resolve()
98 | })
99 | })
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/apps/version.js:
--------------------------------------------------------------------------------
1 | import { Version, common } from "#components"
2 |
3 | export class Version_Info extends plugin {
4 | constructor() {
5 | super({
6 | name: "DF:版本信息",
7 | event: "message",
8 | priority: 400,
9 | rule: [
10 | {
11 | reg: "^#?[Dd][Ff](插件)?版本$",
12 | fnc: "plugin_version"
13 | }
14 | ]
15 | })
16 | }
17 |
18 | async plugin_version(e) {
19 | return await common.render("help/version-info", {
20 | currentVersion: Version.ver,
21 | changelogs: Version.logs
22 | }, { e, scale: 1.4 }
23 | )
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/components/Config.js:
--------------------------------------------------------------------------------
1 | import YAML from "yaml"
2 | import cfg from "../../../lib/config/config.js"
3 | import makeConfig from "../../../lib/plugins/config.js"
4 | import fs from "node:fs/promises"
5 | import { Plugin_Name, Plugin_Path } from "../constants/Path.js"
6 | import _ from "lodash"
7 |
8 | class Config {
9 | plugin_name = Plugin_Name
10 | plugin_path = Plugin_Path
11 | /** 初始化配置 */
12 | async initCfg() {
13 | this.config = YAML.parse(await fs.readFile(`${this.plugin_path}/config/system/config.yaml`, "utf8"))
14 |
15 | /** 导入旧配置文件 */
16 | const path = `${this.plugin_path}/config/config`
17 | if (await fs.stat(path).catch(() => false)) {
18 | for (let file of (await fs.readdir(path)).filter(file => file.endsWith(".yaml"))) {
19 | const key = file.replace(".yaml", "")
20 | if (!(key in this.config)) continue
21 | _.merge(this.config[key], YAML.parse(await fs.readFile(`${path}/${file}`, "utf8")))
22 | }
23 | await fs.rename(path, `${path}_old`)
24 | }
25 |
26 | /** 保留注释 */
27 | const keep = {}
28 | for (const i in this.config) {
29 | keep[i] = {}
30 | for (const j in this.config[i]) {
31 | if (j.endsWith("Tips")) { keep[i][j] = this.config[i][j] }
32 | }
33 | }
34 |
35 | const { config, configSave } = await makeConfig(this.plugin_name, this.config, keep, {
36 | replacer: i => i.replace(/(\n.+?Tips:)/g, "\n$1")
37 | })
38 | this.config = config
39 | this.configSave = configSave
40 | return this
41 | }
42 |
43 | /** 主人列表 */
44 | get masterQQ() {
45 | return cfg.masterQQ
46 | }
47 |
48 | /** TRSS的主人列表 */
49 | get master() {
50 | return cfg.master
51 | }
52 |
53 | get AutoPath() {
54 | return this.config.CodeUpdate.List.some(i => !!i?.AutoPath)
55 | }
56 |
57 | /** 联系主人 */
58 | get sendMaster() {
59 | return this.config.sendMaster
60 | }
61 |
62 | /** 其他配置 */
63 | get other() {
64 | return this.config.other
65 | }
66 |
67 | /** Git推送 */
68 | get CodeUpdate() {
69 | return this.config.CodeUpdate
70 | }
71 |
72 | /** 图片外显 */
73 | get summary() {
74 | return this.config.summary
75 | }
76 |
77 | /** 随机图片配置 */
78 | get Picture() {
79 | return this.config.Picture
80 | }
81 |
82 | /** 代理配置 */
83 | get proxy() {
84 | return this.config.proxy
85 | }
86 |
87 | /**
88 | * 群配置
89 | * @param group_id 群号
90 | * @param bot_id 机器人账号
91 | */
92 | getGroup(group_id = "", bot_id = "") {
93 | return Array.isArray(Bot.uin) ? cfg.getGroup(bot_id, group_id) : cfg.getGroup(group_id)
94 | }
95 |
96 | /**
97 | * 修改设置
98 | * @param {string} name 配置名
99 | * @param {string} key 修改的key值
100 | * @param {string | number} value 修改的value值
101 | */
102 | modify(name, key, value) {
103 | if (typeof this.config[name] != "object") { this.config[name] = {} }
104 | this.config[name][key] = value
105 | return this.configSave()
106 | }
107 |
108 | /**
109 | * 修改配置数组
110 | * @param {string} name 文件名
111 | * @param {string | number} key key值
112 | * @param {string | number} value value
113 | * @param {'add'|'del'} category 类别 add or del
114 | */
115 | modifyarr(name, key, value, category = "add") {
116 | if (typeof this.config[name] != "object") this.config[name] = {}
117 | if (!Array.isArray(this.config[name][key])) this.config[name][key] = []
118 | if (category == "add") {
119 | if (!this.config[name][key].includes(value)) this.config[name][key].push(value)
120 | } else {
121 | this.config[name][key] = this.config[name][key].filter(item => item !== value)
122 | }
123 | return this.configSave()
124 | }
125 | }
126 |
127 | export default await new Config().initCfg()
128 |
--------------------------------------------------------------------------------
/components/Data.js:
--------------------------------------------------------------------------------
1 | import _ from "lodash"
2 | import fs from "node:fs"
3 | import path from "node:path"
4 | import { Path, Plugin_Name, Plugin_Path } from "../constants/Path.js"
5 |
6 | const getRoot = (root = "") => {
7 | if (root === "root" || root === "yunzai") {
8 | root = `${Path}/`
9 | } else if (!root) {
10 | root = `${Plugin_Path}/`
11 | }
12 | return root
13 | }
14 |
15 | let Data = {
16 |
17 | /*
18 | * 根据指定的path依次检查与创建目录
19 | * */
20 | createDir(path = "", root = "", includeFile = false) {
21 | root = getRoot(root)
22 | let pathList = path.split("/")
23 | let nowPath = root
24 | pathList.forEach((name, idx) => {
25 | name = name.trim()
26 | if (!includeFile && idx <= pathList.length - 1) {
27 | nowPath += name + "/"
28 | if (name) {
29 | if (!fs.existsSync(nowPath)) {
30 | fs.mkdirSync(nowPath)
31 | }
32 | }
33 | }
34 | })
35 | },
36 |
37 | /**
38 | * 读取json
39 | * @param file
40 | * @param root
41 | */
42 | readJSON(file = "", root = "") {
43 | root = getRoot(root)
44 | if (fs.existsSync(`${root}/${file}`)) {
45 | try {
46 | return JSON.parse(fs.readFileSync(`${root}/${file}`, "utf8"))
47 | } catch (e) {
48 | console.log(e)
49 | }
50 | }
51 | return {}
52 | },
53 |
54 | /**
55 | * 写JSON
56 | * @param file
57 | * @param data
58 | * @param root
59 | * @param space
60 | */
61 | writeJSON(file, data, root = "", space = "\t") {
62 | // 检查并创建目录
63 | Data.createDir(file, root, true)
64 | root = getRoot(root)
65 | // delete data._res
66 | try {
67 | fs.writeFileSync(`${root}/${file}`, JSON.stringify(data, null, space))
68 | return true
69 | } catch (err) {
70 | logger.error(err)
71 | return false
72 | }
73 | },
74 |
75 | async getCacheJSON(key) {
76 | try {
77 | let txt = await redis.get(key)
78 | if (txt) {
79 | return JSON.parse(txt)
80 | }
81 | } catch (e) {
82 | console.log(e)
83 | }
84 | return {}
85 | },
86 |
87 | async setCacheJSON(key, data, EX = 3600 * 24 * 90) {
88 | await redis.set(key, JSON.stringify(data), { EX })
89 | },
90 |
91 | async importModule(file, root = "") {
92 | root = getRoot(root)
93 | if (!/\.js$/.test(file)) {
94 | file = file + ".js"
95 | }
96 | if (fs.existsSync(`${root}/${file}`)) {
97 | try {
98 | let data = await import(`file://${root}/${file}?t=${new Date() * 1}`)
99 | return data || {}
100 | } catch (e) {
101 | console.log(e)
102 | }
103 | }
104 | return {}
105 | },
106 |
107 | async importDefault(file, root) {
108 | let ret = await Data.importModule(file, root)
109 | return ret.default || {}
110 | },
111 |
112 | async import(name) {
113 | return await Data.importModule(`components/optional-lib/${name}.js`)
114 | },
115 |
116 | async importCfg(key) {
117 | let sysCfg = await Data.importModule(`config/system/${key}_system.js`)
118 | let diyCfg = await Data.importModule(`config/${key}.js`)
119 | if (diyCfg.isSys) {
120 | console.error(`${Plugin_Name}: config/${key}.js无效,已忽略`)
121 | console.error(`如需配置请复制config/${key}_default.js为config/${key}.js,请勿复制config/system下的系统文件`)
122 | diyCfg = {}
123 | }
124 | return {
125 | sysCfg,
126 | diyCfg
127 | }
128 | },
129 |
130 | /*
131 | * 返回一个从 target 中选中的属性的对象
132 | *
133 | * keyList : 获取字段列表,逗号分割字符串
134 | * key1, key2, toKey1:fromKey1, toKey2:fromObj.key
135 | *
136 | * defaultData: 当某个字段为空时会选取defaultData的对应内容
137 | * toKeyPrefix:返回数据的字段前缀,默认为空。defaultData中的键值无需包含toKeyPrefix
138 | *
139 | * */
140 |
141 | getData(target, keyList = "", cfg = {}) {
142 | target = target || {}
143 | let defaultData = cfg.defaultData || {}
144 | let ret = {}
145 | // 分割逗号
146 | if (typeof (keyList) === "string") {
147 | keyList = keyList.split(",")
148 | }
149 |
150 | _.forEach(keyList, (keyCfg) => {
151 | // 处理通过:指定 toKey & fromKey
152 | let _keyCfg = keyCfg.split(":")
153 | let keyTo = _keyCfg[0].trim()
154 | let keyFrom = (_keyCfg[1] || _keyCfg[0]).trim()
155 | let keyRet = keyTo
156 | if (cfg.lowerFirstKey) {
157 | keyRet = _.lowerFirst(keyRet)
158 | }
159 | if (cfg.keyPrefix) {
160 | keyRet = cfg.keyPrefix + keyRet
161 | }
162 | // 通过Data.getVal获取数据
163 | ret[keyRet] = Data.getVal(target, keyFrom, defaultData[keyTo], cfg)
164 | })
165 | return ret
166 | },
167 |
168 | getVal(target, keyFrom, defaultValue) {
169 | return _.get(target, keyFrom, defaultValue)
170 | },
171 |
172 | // 异步池,聚合请求
173 | async asyncPool(poolLimit, array, iteratorFn) {
174 | const ret = [] // 存储所有的异步任务
175 | const executing = [] // 存储正在执行的异步任务
176 | for (const item of array) {
177 | // 调用iteratorFn函数创建异步任务
178 | const p = Promise.resolve().then(() => iteratorFn(item, array))
179 | // 保存新的异步任务
180 | ret.push(p)
181 |
182 | // 当poolLimit值小于或等于总任务个数时,进行并发控制
183 | if (poolLimit <= array.length) {
184 | // 当任务完成后,从正在执行的任务数组中移除已完成的任务
185 | const e = p.then(() => executing.splice(executing.indexOf(e), 1))
186 | executing.push(e) // 保存正在执行的异步任务
187 | if (executing.length >= poolLimit) {
188 | // 等待较快的任务执行完成
189 | await Promise.race(executing)
190 | }
191 | }
192 | }
193 | return Promise.all(ret)
194 | },
195 |
196 | // sleep
197 | sleep(ms) {
198 | return new Promise((resolve) => setTimeout(resolve, ms))
199 | },
200 |
201 | // 获取默认值
202 | def() {
203 | for (let idx in arguments) {
204 | if (!_.isUndefined(arguments[idx])) {
205 | return arguments[idx]
206 | }
207 | }
208 | },
209 |
210 | // 循环字符串回调
211 | eachStr: (arr, fn) => {
212 | if (_.isString(arr)) {
213 | arr = arr.replace(/\s*(;|;|、|,)\s*/, ",")
214 | arr = arr.split(",")
215 | } else if (_.isNumber(arr)) {
216 | arr = [ arr.toString() ]
217 | }
218 | _.forEach(arr, (str, idx) => {
219 | if (!_.isUndefined(str)) {
220 | fn(str.trim ? str.trim() : str, idx)
221 | }
222 | })
223 | },
224 |
225 | regRet(reg, txt, idx) {
226 | if (reg && txt) {
227 | let ret = reg.exec(txt)
228 | if (ret && ret[idx]) {
229 | return ret[idx]
230 | }
231 | }
232 | return false
233 | },
234 | /**
235 | * 读取文件夹和子文件夹指定后缀文件名
236 | * @param directory
237 | * @param extension
238 | * @param excludeDir
239 | */
240 | readDirRecursive(directory, extension, excludeDir) {
241 | let files = fs.readdirSync(directory)
242 |
243 | let jsFiles = files.filter(file => path.extname(file) === `.${extension}`)
244 |
245 | files.filter(file => fs.statSync(path.join(directory, file)).isDirectory())
246 | .forEach(subdirectory => {
247 | if (subdirectory === excludeDir) {
248 | return
249 | }
250 |
251 | const subdirectoryPath = path.join(directory, subdirectory)
252 | jsFiles.push(...Data.readDirRecursive(subdirectoryPath, extension, excludeDir)
253 | .map(fileName => path.join(subdirectory, fileName)))
254 | })
255 |
256 | return jsFiles
257 | }
258 | }
259 |
260 | export default Data
261 |
--------------------------------------------------------------------------------
/components/Version.js:
--------------------------------------------------------------------------------
1 | import fs from "node:fs"
2 | import lodash from "lodash"
3 | import { Data, Plugin_Path } from "./index.js"
4 |
5 | const CHANGELOG_path = `${Plugin_Path}/CHANGELOG.md`
6 | const README_path = `${Plugin_Path}/README.md`
7 |
8 | let PluginPackagePath = `${Plugin_Path}/package.json`
9 | let PluginPackageData = JSON.parse(fs.readFileSync(PluginPackagePath, "utf8"))
10 | let packageJson = JSON.parse(fs.readFileSync(`${process.cwd()}/package.json`, "utf8"))
11 |
12 | // let yunzai_ver = packageJson.version
13 |
14 | let changelogs = []
15 | let currentVersion
16 | let logs = {}
17 | let versionCount = 3
18 |
19 | const getLine = function(line) {
20 | line = line.replace(/(^\s*\*|\r)/g, "")
21 | line = line.replace(/\s*`([^`]+`)/g, "$1")
22 | line = line.replace(/`\s*/g, "")
23 | line = line.replace(/\s*\*\*([^*]+\*\*)/g, "$1")
24 | line = line.replace(/\*\*\s*/g, "")
25 | line = line.replace(/ⁿᵉʷ/g, "")
26 | return line
27 | }
28 |
29 | const readLogFile = function(root, versionCount = 4) {
30 | root = Data.getRoot(root)
31 | let changelogs = []
32 | let currentVersion
33 | let logPath = `${root}/CHANGELOG.md`
34 | let logs = {}
35 |
36 | try {
37 | if (fs.existsSync(logPath)) {
38 | logs = fs.readFileSync(logPath, "utf8") || ""
39 | logs = logs.split("\n")
40 |
41 | let temp = {}
42 | let lastLine = {}
43 | lodash.forEach(logs, (line) => {
44 | if (versionCount <= -1) {
45 | return false
46 | }
47 | let versionRet = /^#\s*([0-9a-zA-Z\\.~\s]+?)\s*$/.exec(line)
48 | if (versionRet && versionRet[1]) {
49 | let v = versionRet[1].trim()
50 | if (!currentVersion) {
51 | currentVersion = v
52 | } else {
53 | changelogs.push(temp)
54 | if (/0\s*$/.test(v) && versionCount > 0) {
55 | versionCount = 0
56 | } else {
57 | versionCount--
58 | }
59 | }
60 |
61 | temp = {
62 | version: v,
63 | logs: []
64 | }
65 | } else {
66 | if (!line.trim()) {
67 | return
68 | }
69 | if (/^\*/.test(line)) {
70 | lastLine = {
71 | title: getLine(line),
72 | logs: []
73 | }
74 | temp.logs.push(lastLine)
75 | } else if (/^\s{2,}\*/.test(line)) {
76 | lastLine.logs.push(getLine(line))
77 | }
78 | }
79 | })
80 | }
81 | } catch (e) {
82 | // do nth
83 | }
84 | return { changelogs, currentVersion }
85 | }
86 |
87 | try {
88 | if (fs.existsSync(CHANGELOG_path)) {
89 | logs = fs.readFileSync(CHANGELOG_path, "utf8") || ""
90 | logs = logs.replace(/\t/g, " ").split("\n")
91 | let temp = {}
92 | let lastLine = {}
93 | lodash.forEach(logs, (line) => {
94 | if (versionCount < 1) {
95 | return false
96 | }
97 | let versionRet = /^#\s*([0-9a-zA-Z\\.~\s]+?)\s*$/.exec(line.trim())
98 | if (versionRet && versionRet[1]) {
99 | let v = versionRet[1].trim()
100 | if (!currentVersion) {
101 | currentVersion = v
102 | } else {
103 | changelogs.push(temp)
104 | if (/0\s*$/.test(v) && versionCount > 0) {
105 | // versionCount = 0
106 | versionCount--
107 | } else {
108 | versionCount--
109 | }
110 | }
111 | temp = {
112 | version: v,
113 | logs: []
114 | }
115 | } else {
116 | if (!line.trim()) {
117 | return
118 | }
119 | if (/^\*/.test(line)) {
120 | lastLine = {
121 | title: getLine(line),
122 | logs: []
123 | }
124 | if (!temp.logs) {
125 | temp = {
126 | version: line,
127 | logs: []
128 | }
129 | }
130 | temp.logs.push(lastLine)
131 | } else if (/^\s{2,}\*/.test(line)) {
132 | lastLine.logs.push(getLine(line))
133 | }
134 | }
135 | })
136 | }
137 | } catch (e) {
138 | logger.error(e)
139 | // do nth
140 | }
141 |
142 | try {
143 | if (fs.existsSync(README_path)) {
144 | let README = fs.readFileSync(README_path, "utf8") || ""
145 | let reg = /版本:(.*)/.exec(README)
146 | if (reg) {
147 | currentVersion = reg[1]
148 | }
149 | }
150 | } catch (err) {}
151 |
152 | try {
153 | if (PluginPackageData.version !== currentVersion) {
154 | console.log(`[DF-Plugin] 版本号不一致,更新版本号为: ${currentVersion}`)
155 | PluginPackageData.version = currentVersion
156 |
157 | fs.writeFileSync(PluginPackagePath, JSON.stringify(PluginPackageData, null, 2), "utf8")
158 | console.log("[DF-Plugin] package.json 已更新")
159 | }
160 | } catch (error) {
161 | console.error("读取或解析 package.json 出现错误:", error)
162 | }
163 |
164 | let { author } = PluginPackageData
165 | const yunzaiVersion = packageJson.version
166 | const isV3 = yunzaiVersion[0] === "3"
167 | const isV4 = yunzaiVersion[0] === "4"
168 | let isMiao = false
169 | let isTRSS = false
170 | let isAlemonjs = false
171 | let name = "Yunzai-Bot"
172 | if (packageJson.name === "miao-yunzai") {
173 | isMiao = true
174 | name = "Miao-Yunzai"
175 | } else if (packageJson.name === "trss-yunzai") {
176 | isTRSS = true
177 | name = "TRSS-Yunzai"
178 | } else if (packageJson.name === "a-yunzai") {
179 | name = "A-Yunzai"
180 | isAlemonjs = true
181 | }
182 |
183 | let Version = {
184 | isV3,
185 | isV4,
186 | isMiao,
187 | isTRSS,
188 | name,
189 | isAlemonjs,
190 | author,
191 | get version() {
192 | return currentVersion
193 | },
194 | get yunzai() {
195 | return yunzaiVersion
196 | },
197 | get logs() {
198 | return changelogs
199 | },
200 | get ver() {
201 | return currentVersion
202 | },
203 | readLogFile
204 | }
205 |
206 | export default Version
207 |
--------------------------------------------------------------------------------
/components/index.js:
--------------------------------------------------------------------------------
1 | export { Path, Plugin_Name, Plugin_Path, Poke_Path, Res_Path } from "../constants/Path.js"
2 | export { Poke_List } from "../constants/Poke.js"
3 | export { default as Config } from "./Config.js"
4 | export { default as Data } from "./Data.js"
5 | export { default as Version } from "./Version.js"
6 | export { default as common } from "../lib/common/common.js"
7 | export { default as render } from "../lib/puppeteer/render.js"
8 | export { default as request } from "../lib/request/request.js"
9 |
--------------------------------------------------------------------------------
/config/.gitignore:
--------------------------------------------------------------------------------
1 | config*/
--------------------------------------------------------------------------------
/config/system/config.yaml:
--------------------------------------------------------------------------------
1 | # 此配置文件为系统使用,请勿修改,否则可能无法正常使用
2 | # 用户配置已移动至 Yz/config/DF-Plugin.yaml
3 |
4 | CodeUpdate:
5 | AutoTips: |-
6 | 自动检查仓库更新开关
7 | Auto: false
8 |
9 | AutoBranchTips: |-
10 | 是否自动给未配置分支的仓库添加默认分支
11 | AutoBranch: true
12 |
13 | CronTips: |-
14 | 仓库更新检查Cron表达式
15 | Github/Gitee Api 未认证用户每小时限制60(ip限制),认证用户每小时限制5000次(账号限制)
16 | Cron: "0 */30 * * * *"
17 |
18 | GiteeTokenTips: |-
19 | Gitee个人Token
20 | 获取地址: https://gitee.com/profile/personal_access_tokens
21 | GiteeToken: ""
22 |
23 | GithubTokenTips: |-
24 | Github个人Token
25 | 获取地址: https://github.com/settings/tokens
26 | GithubToken: ""
27 |
28 | GitcodeTokenTips: |-
29 | Gitcode个人Token
30 | 获取地址: https://gitcode.com/setting/token-classic
31 | GitcodeToken: ""
32 |
33 | ListTips: |-
34 | 推送配置
35 | Group: 推送的群聊列表
36 | QQ: 推送的QQ列表
37 | AutoPath: 是否自动获取已安装的插件地址并监听
38 | Exclude: 排除的仓库路径(用于忽略一些不希望被监听的插件)
39 | GithubList: 自定义关注的Github仓库路径 (格式: 所有者/存储库:分支)
40 | GiteeList: 自定义关注的Gitee仓库路径 (格式: 所有者/存储库:分支)
41 | GithubReleases: Github发行版监听仓库列表 (格式: 所有者/存储库)
42 | GiteeReleases: Gitee发行版监听仓库列表 (格式: 所有者/存储库)
43 | note: 备注
44 | List: []
45 |
46 | other:
47 | chuoTips: |-
48 | 戳一戳开关
49 | chuo: true
50 |
51 | chuoTypeTips: |-
52 | 戳一戳图片设置
53 | 1:柴郡表情包
54 | 2:丛雨表情包
55 | 3:诗歌剧表情包
56 | 4:柚子厨表情包
57 | 5:小南梁表情包
58 | 6:小鲨鱼古拉
59 | 7:甘城猫猫
60 | 8: 龙图
61 | 9:满穗表情包
62 | 10: 猫猫虫表情
63 | 11:纳西妲表情
64 | 12;心海表情
65 | 13:fufu表情
66 | 14: ATRI表情包
67 | 15: 绫地宁宁表情包
68 | 16: 永雏塔菲表情包
69 | 17:miku表情包
70 | 18:特蕾西娅表情包
71 | 19:doro表情包
72 | 20:米塔表情包
73 | 0:自定义图片(自行在resources/poke/default中添加)
74 | all: 随机类型
75 | chuoType: 1
76 |
77 | BlackTips: |-
78 | 戳一戳随机类型黑名单
79 | Black:
80 |
81 | - default
82 | ysTips: |-
83 | 原神关键词发图
84 | ys: true
85 |
86 | Picture:
87 | openTips: |-
88 | 随机图片开关
89 | open: true
90 |
91 | DirectTips: |-
92 | 是否去除 #来张/随机 前缀
93 | Direct: false
94 |
95 | sendMaster:
96 | openTips: |-
97 | 联系主人开关
98 | open: true
99 |
100 | cdTips: |-
101 | 联系主人CD 单位:秒 填0关闭cd
102 | cd: 300
103 |
104 | MasterTips: |-
105 | 是否发送全部主人 0:仅发送首个主人 1:发送全部主人
106 | 填主人账号可以指定发送某个主人
107 | Master: 0
108 |
109 | BotIdTips: |-
110 | 指定某个Bot发送 为0时为触发Bot
111 | BotId: 0
112 |
113 | sendAvatarTips: |-
114 | 是否发送触发者的头像(微信请关闭此项)
115 | sendAvatar: true
116 |
117 | replyQQTips: |-
118 | 是否回复主人QQ号
119 | replyQQ: true
120 |
121 | banWordsTips: |-
122 | 违禁词,当消息包含下列内容将不会发送给主人
123 | banWords:
124 | - 垃圾
125 | - 你妈
126 | - nm
127 | - 月吗
128 | - 🈷️吗
129 | - 🈷吗
130 | - 约吗
131 |
132 | banUserTips: |-
133 | 禁用的用户,不允许该用户联系主人
134 | banUser:
135 | - 10086
136 |
137 | banGroupTips: |-
138 | 禁用的群聊,不允许通过该群联系主人
139 | banGroup:
140 | - 114514
141 |
142 | summary:
143 | sumTips: |-
144 | 图片外显开关
145 | sum: false
146 |
147 | typeTips: |-
148 | 外显模式 (1: 自定义外显 2: 一言接口 3:列表随机)
149 | type: 2
150 |
151 | textTips: |-
152 | 自定义外显
153 | text: "Ciallo~(∠・ω< )⌒☆"
154 |
155 | listTips: |-
156 | 自定义外显随机列表
157 | list:
158 | - 你干嘛~
159 | - 我喜欢你
160 |
161 | apiTips: |-
162 | 一言接口
163 | api: "https://v1.hitokoto.cn/?encode=text"
164 |
165 | proxy:
166 | openTips: |-
167 | 是否代理大部分请求
168 | open: false
169 |
170 | urlTips: |-
171 | 代理地址
172 | url: "http://127.0.0.1:7890"
--------------------------------------------------------------------------------
/config/system/help_system.js:
--------------------------------------------------------------------------------
1 | /*
2 | * 此配置文件为系统使用,请勿修改,否则可能无法正常使用
3 | * */
4 |
5 | export const helpCfg = {
6 | title: "DF帮助",
7 | subTitle: "[DF插件] Yunzai-Bot & DF-Plugin",
8 | columnCount: 4,
9 | colWidth: 300,
10 | theme: "all",
11 | themeExclude: [ "default" ],
12 | style: {
13 | fontColor: "#ceb78b",
14 | descColor: "#eee",
15 | contBgColor: "rgba(6, 21, 31, .5)",
16 | contBgBlur: 0,
17 | headerBgColor: "rgba(255, 222, 142, 0.44)",
18 | rowBgColor1: "rgba(255, 166, 99, 0.23)",
19 | rowBgColor2: "rgba(251, 113, 107, 0.35)"
20 | }
21 | }
22 |
23 | export const helpList = [
24 | {
25 | "group": "功能类",
26 | "list": [
27 | {
28 | "icon": 2,
29 | "title": "戳一戳机器人",
30 | "desc": "戳一戳机器人发送随机表情包"
31 | },
32 | {
33 | "icon": 4,
34 | "title": "拾取关键词原神",
35 | "desc": "本来聊得好好的,突然有人聊起了原神,搞得大家都不高兴"
36 | },
37 | {
38 | "icon": 7,
39 | "title": "Git仓库更新推送",
40 | "desc": "前往Guoba或插件内配置"
41 | }
42 | ]
43 | },
44 | {
45 | "group": "随机图片类",
46 | "list": [
47 | {
48 | "icon": 35,
49 | "title": "#来张制服",
50 | "desc": "随机jk图片"
51 | },
52 | {
53 | "icon": 5,
54 | "title": "#来张黑丝",
55 | "desc": "顾名思义"
56 | },
57 | {
58 | "icon": 3,
59 | "title": "#来张cos",
60 | "desc": "随机cos图片"
61 | },
62 | {
63 | "icon": 8,
64 | "title": "#来张腿子",
65 | "desc": "kkt"
66 | },
67 | {
68 | "icon": 16,
69 | "title": "#随机从雨",
70 | "desc": "狗修金"
71 | },
72 | {
73 | "icon": 33,
74 | "title": "#随机诗歌剧",
75 | "desc": "曼波"
76 | }
77 | ]
78 | },
79 | {
80 | "group": "给主人带话",
81 | "list": [
82 | {
83 | "icon": 13,
84 | "title": "#联系主人",
85 | "desc": "给主人带句话"
86 | },
87 | {
88 | "icon": 14,
89 | "title": "#回复",
90 | "desc": "主人回复群友的消息"
91 | }
92 | ]
93 | },
94 | {
95 | "group": "图片外显",
96 | "auth": "master",
97 | "list": [
98 | {
99 | "icon": 7,
100 | "title": "#开启/关闭图片外显",
101 | "desc": "开关外显功能"
102 | },
103 | {
104 | "icon": 4,
105 | "title": "#设置外显+文字",
106 | "desc": "设置自定义外显文本"
107 | },
108 | {
109 | "icon": 6,
110 | "title": "#切换外显模式",
111 | "desc": "切换一言/文本模式"
112 | }
113 | ]
114 | },
115 | {
116 | "group": "主人功能",
117 | "auth": "master",
118 | "list": [
119 | {
120 | "icon": 12,
121 | "title": "#DF(强制)?更新",
122 | "desc": "拉取Git更新"
123 | },
124 | {
125 | "icon": 2,
126 | "title": "#DF更新图库",
127 | "desc": "更新戳一戳图库"
128 | }
129 | ]
130 | }
131 | ]
132 |
133 | export const isSys = true
134 |
--------------------------------------------------------------------------------
/config/system/请勿修改此目录下的文件.txt:
--------------------------------------------------------------------------------
1 | 此目录为系统配置目录
2 | 请勿修改此目录下的文件,否则可能导致工作不正常
3 |
--------------------------------------------------------------------------------
/constants/Path.js:
--------------------------------------------------------------------------------
1 | import { fileURLToPath } from "node:url"
2 | import { join, dirname, basename } from "node:path"
3 |
4 | export const Path = process.cwd()
5 | export const Plugin_Path = join(dirname(fileURLToPath(import.meta.url)), "..").replace(/\\/g, "/")
6 | export const Plugin_Name = basename(Plugin_Path)
7 | export const Res_Path = `${Plugin_Path}/resources`
8 | export const Poke_Path = `${Res_Path}/poke`
9 |
--------------------------------------------------------------------------------
/constants/Poke.js:
--------------------------------------------------------------------------------
1 | import { Poke_Path } from "./Path.js"
2 | import fs from "node:fs"
3 |
4 | let Poke_List = [
5 | "default",
6 | "柴郡猫",
7 | "丛雨",
8 | "诗歌剧",
9 | "柚子厨",
10 | "小南梁",
11 | "古拉",
12 | "甘城猫猫",
13 | "龙图",
14 | "满穗",
15 | "猫猫虫",
16 | "纳西妲",
17 | "心海",
18 | "fufu",
19 | "ATRI",
20 | "绫地宁宁",
21 | "永雏塔菲",
22 | "miku",
23 | "特蕾西娅",
24 | "doro",
25 | "米塔",
26 | "冬川花璃",
27 | "neuro",
28 | "kipfel",
29 | "mygo"
30 | ]
31 |
32 | /**
33 | * 兼容用户自建目录
34 | * 用户可以在resources/poke下自建多个目录用于存放图片
35 | */
36 | if (fs.existsSync(Poke_Path)) {
37 | const directories = fs.readdirSync(Poke_Path, { withFileTypes: true })
38 | .filter(dirent => dirent.isDirectory() && dirent.name !== ".git")
39 | .map(dirent => dirent.name)
40 | Poke_List = Array.from(new Set([ ...Poke_List, ...directories ]))
41 | }
42 |
43 | export { Poke_List }
44 |
--------------------------------------------------------------------------------
/guoba.support.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 支持锅巴
3 | * 锅巴插件:https://gitee.com/guoba-yunzai/guoba-plugin.git
4 | * 组件类型,可参考 https://antdv.com/components/overview-cn/
5 | */
6 |
7 | export { supportGuoba } from "./guoba/index.js"
8 |
--------------------------------------------------------------------------------
/guoba/configInfo.js:
--------------------------------------------------------------------------------
1 | import { schemas, getConfigData, setConfigData } from "./schemas/index.js"
2 |
3 | export default {
4 | schemas,
5 | getConfigData,
6 | setConfigData
7 | }
8 |
--------------------------------------------------------------------------------
/guoba/index.js:
--------------------------------------------------------------------------------
1 | import pluginInfo from "./pluginInfo.js"
2 | import configInfo from "./configInfo.js"
3 |
4 | export function supportGuoba() {
5 | return {
6 | pluginInfo,
7 | configInfo
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/guoba/pluginInfo.js:
--------------------------------------------------------------------------------
1 | import { Res_Path } from "#components"
2 |
3 | export default {
4 | name: "DF-Plugin",
5 | title: "DF-Plugin",
6 | description: "提供Yunzai-Bot拓展功能",
7 | author: "@等风来",
8 | authorLink: "https://gitee.com/DengFengLai-F",
9 | link: "https://github.com/DenFengLai/DF-Plugin",
10 | isV3: true,
11 | isV2: false,
12 | showInMenu: "auto",
13 | iconPath: `${Res_Path}/img/Roxy.png`,
14 | iconColor: "#d19f56"
15 | }
16 |
--------------------------------------------------------------------------------
/guoba/schemas/CodeUpdata.js:
--------------------------------------------------------------------------------
1 | import { PluginPath } from "#model"
2 |
3 | export default [
4 | {
5 | component: "SOFT_GROUP_BEGIN",
6 | label: "Git仓库监听配置"
7 | },
8 | {
9 | field: "CodeUpdate.Auto",
10 | label: "自动检查开关",
11 | component: "Switch"
12 | },
13 | {
14 | field: "CodeUpdate.AutoBranch",
15 | label: "自动获取远程默认分支",
16 | bottomHelpMessage: "在未指定分支的情况下,启动时自动获取远程仓库的默认分支",
17 | component: "Switch"
18 | },
19 | {
20 | field: "CodeUpdate.Cron",
21 | label: "自动检查定时表达式",
22 | helpMessage: "修改后重启生效",
23 | bottomHelpMessage: "自动检查Cron表达式",
24 | component: "EasyCron",
25 | componentProps: {
26 | placeholder: "请输入Cron表达式"
27 | }
28 | },
29 | {
30 | field: "CodeUpdate.GithubToken",
31 | label: "Github Api Token",
32 | helpMessage: "用于请求Github Api",
33 | bottomHelpMessage: "填写后可解除请求速率限制和监听私库,获取地址:https://github.com/settings/tokens",
34 | component: "InputPassword",
35 | componentProps: {
36 | placeholder: "请输入Github Token"
37 | }
38 | },
39 | {
40 | field: "CodeUpdate.GiteeToken",
41 | label: "Gitee Api Token",
42 | helpMessage: "用于请求 Gitee Api",
43 | bottomHelpMessage: "填写后可解除请求速率限制和监听私库,获取地址:https://gitee.com/profile/personal_access_tokens",
44 | component: "InputPassword",
45 | componentProps: {
46 | placeholder: "请输入Gitee Token"
47 | }
48 | },
49 | {
50 | field: "CodeUpdate.GitcodeToken",
51 | label: "Gitcode Api Token",
52 | helpMessage: "用于请求Gitcode Api",
53 | bottomHelpMessage: "获取地址:https://gitcode.com/setting/token-classic",
54 | component: "InputPassword",
55 | componentProps: {
56 | placeholder: "请输入Token"
57 | }
58 | },
59 | {
60 | field: "CodeUpdate.List",
61 | label: "推送列表",
62 | bottomHelpMessage: "Git仓库推送列表",
63 | component: "GSubForm",
64 | componentProps: {
65 | multiple: true,
66 | schemas: [
67 | {
68 | field: "Group",
69 | helpMessage: "检测到仓库更新后推送的群列表",
70 | label: "推送群",
71 | componentProps: {
72 | placeholder: "点击选择要推送的群"
73 | },
74 | component: "GSelectGroup"
75 | },
76 | {
77 | field: "QQ",
78 | helpMessage: "检测到仓库更新后推送的用户列表",
79 | label: "推送好友",
80 | componentProps: {
81 | placeholder: "点击选择要推送的好友"
82 | },
83 | component: "GSelectFriend"
84 | },
85 | {
86 | field: "AutoPath",
87 | label: "获取已安装的插件",
88 | component: "Switch"
89 | },
90 | {
91 | field: "Exclude",
92 | label: "排除的插件",
93 | component: "Select",
94 | componentProps: {
95 | allowClear: true,
96 | mode: "tags",
97 | get options() {
98 | return Array.from(new Set(Object.values(PluginPath).flat())).map((name) => ({ value: name }))
99 | }
100 | }
101 | },
102 | {
103 | field: "GithubList",
104 | label: "Github仓库路径",
105 | bottomHelpMessage: "格式:用户名/仓库名:分支名,如: github.com/DenFengLai/DF-Plugin 则填 DenFengLai/DF-Plugin",
106 | component: "GTags",
107 | componentProps: {
108 | allowAdd: true,
109 | allowDel: true
110 | },
111 | showPrompt: true,
112 | promptProps: {
113 | content: "请输入 所有者/存储库:分支",
114 | placeholder: "请输入仓库路径",
115 | okText: "添加",
116 | rules: [ { required: true, message: "不可以为空哦" } ]
117 | }
118 | },
119 | {
120 | field: "GithubReleases",
121 | label: "Github发行版仓库路径",
122 | bottomHelpMessage: "格式:所有者/存储库:分支",
123 | component: "GTags",
124 | componentProps: {
125 | allowAdd: true,
126 | allowDel: true
127 | },
128 | showPrompt: true,
129 | promptProps: {
130 | content: "请输入 所有者/存储库:分支",
131 | placeholder: "请输入仓库路径",
132 | okText: "添加",
133 | rules: [ { required: true, message: "不可以为空哦" } ]
134 | }
135 | },
136 | {
137 | field: "GiteeList",
138 | label: "Gitee仓库路径",
139 | bottomHelpMessage: "格式:所有者/存储库:分支",
140 | component: "GTags",
141 | componentProps: {
142 | allowAdd: true,
143 | allowDel: true
144 | },
145 | showPrompt: true,
146 | promptProps: {
147 | content: "请输入 所有者/存储库:分支",
148 | placeholder: "请输入仓库路径",
149 | okText: "添加",
150 | rules: [ { required: true, message: "不可以为空哦" } ]
151 | }
152 | },
153 | {
154 | field: "GiteeReleases",
155 | label: "Gitee发行版仓库路径",
156 | bottomHelpMessage: "格式:所有者/存储库:分支",
157 | component: "GTags",
158 | componentProps: {
159 | allowAdd: true,
160 | allowDel: true
161 | },
162 | showPrompt: true,
163 | promptProps: {
164 | content: "请输入 所有者/存储库:分支",
165 | placeholder: "请输入仓库路径",
166 | okText: "添加",
167 | rules: [ { required: true, message: "不可以为空哦" } ]
168 | }
169 | },
170 | {
171 | field: "GitcodeList",
172 | label: "Gitcode仓库路径",
173 | bottomHelpMessage: "格式:所有者/存储库:分支",
174 | component: "GTags",
175 | componentProps: {
176 | allowAdd: true,
177 | allowDel: true
178 | },
179 | showPrompt: true,
180 | promptProps: {
181 | content: "请输入 所有者/存储库:分支",
182 | placeholder: "请输入仓库路径",
183 | okText: "添加",
184 | rules: [ { required: true, message: "不可以为空哦" } ]
185 | }
186 | },
187 | {
188 | field: "note",
189 | label: "备注",
190 | component: "Input"
191 | }
192 | ]
193 | }
194 | }
195 | ]
196 |
--------------------------------------------------------------------------------
/guoba/schemas/Picture.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | component: "SOFT_GROUP_BEGIN",
4 | label: "随机图片配置"
5 | },
6 | {
7 | field: "Picture.open",
8 | label: "随机图片开关",
9 | component: "Switch"
10 | },
11 | {
12 | field: "Picture.Direct",
13 | label: "是否去除 #来张/随机 前缀",
14 | component: "Switch"
15 | }
16 | ]
17 |
--------------------------------------------------------------------------------
/guoba/schemas/Poke.js:
--------------------------------------------------------------------------------
1 | import { Poke_List } from "#components"
2 |
3 | export default [
4 | {
5 | component: "SOFT_GROUP_BEGIN",
6 | label: "戳一戳配置"
7 | },
8 | {
9 | field: "other.chuo",
10 | label: "戳一戳开关",
11 | component: "Switch"
12 | },
13 | {
14 | field: "other.chuoType",
15 | label: "戳一戳图片类型",
16 | bottomHelpMessage: "自定义图片需在resources/poke/default中添加",
17 | component: "RadioGroup",
18 | required: true,
19 | componentProps: {
20 | options: [
21 | { label: "随机类型", value: "all" },
22 | ...Poke_List.map((name, id) => ({ label: name, value: id }))
23 | ]
24 | }
25 | },
26 | {
27 | field: "other.Black",
28 | label: "随机类型排除列表",
29 | bottomHelpMessage: "设置戳一戳类型为随机时将不会随机到以下类型",
30 | component: "Select",
31 | componentProps: {
32 | allowClear: true,
33 | mode: "tags",
34 | options: Poke_List.map((name) => ({ value: name }))
35 | }
36 | }
37 | ]
38 |
--------------------------------------------------------------------------------
/guoba/schemas/SendMaster.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | component: "SOFT_GROUP_BEGIN",
4 | label: "联系主人配置"
5 | },
6 | {
7 | field: "sendMaster.open",
8 | label: "功能开关",
9 | bottomHelpMessage: "允许用户联系主人",
10 | component: "Switch"
11 | },
12 | {
13 | field: "sendMaster.cd",
14 | label: "触发冷却",
15 | helpMessage: "主人不受限制",
16 | bottomHelpMessage: "单位:秒",
17 | component: "InputNumber",
18 | required: true,
19 | componentProps: {
20 | min: 1,
21 | placeholder: "请输入冷却时间"
22 | }
23 | },
24 | {
25 | field: "sendMaster.Master",
26 | label: "主人配置",
27 | helpMessage: "填主人QQ可发送某个指定主人",
28 | bottomHelpMessage: "0:仅发送首个主人 1:发送全部主人 QQ号:发送指定QQ号",
29 | component: "Input",
30 | required: true,
31 | componentProps: {
32 | placeholder: "请输入主人QQ或配置项"
33 | }
34 | },
35 | {
36 | field: "sendMaster.BotId",
37 | label: "Bot配置",
38 | bottomHelpMessage: "指定某个Bot发送,为0时为触发Bot",
39 | component: "Input",
40 | required: true,
41 | componentProps: {
42 | placeholder: "请输入Bot账号或配置项"
43 | }
44 | },
45 | {
46 | field: "sendMaster.sendAvatar",
47 | label: "消息附带触发者头像",
48 | bottomHelpMessage: "微信Bot如果遇到报错请关闭此项。",
49 | component: "Switch"
50 | },
51 | {
52 | field: "sendMaster.replyQQ",
53 | label: "是否回复主人账号",
54 | component: "Switch"
55 | },
56 | {
57 | field: "sendMaster.banWords",
58 | label: "违禁词",
59 | bottomHelpMessage: "当消息包含下列内容时将不会发送给主人",
60 | component: "GTags",
61 | componentProps: {
62 | allowAdd: true,
63 | allowDel: true
64 | }
65 | },
66 | {
67 | field: "sendMaster.banUser",
68 | label: "禁用用户",
69 | bottomHelpMessage: "不允许该用户联系主人",
70 | component: "GTags",
71 | componentProps: {
72 | allowAdd: true,
73 | allowDel: true
74 | }
75 | },
76 | {
77 | field: "sendMaster.banGroup",
78 | label: "禁用群",
79 | helpMessage: "不允许通过该群联系主人的群聊",
80 | componentProps: {
81 | placeholder: "点击选择要禁用的群"
82 | },
83 | component: "GSelectGroup"
84 | }
85 | ]
86 |
--------------------------------------------------------------------------------
/guoba/schemas/Summary.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | component: "SOFT_GROUP_BEGIN",
4 | label: "图片外显配置"
5 | },
6 | {
7 | field: "summary.sum",
8 | label: "图片外显开关",
9 | component: "Switch"
10 | },
11 | {
12 | field: "summary.type",
13 | label: "外显模式",
14 | bottomHelpMessage: "可选自定义文本或使用一言接口",
15 | component: "RadioGroup",
16 | required: true,
17 | componentProps: {
18 | options: [
19 | { label: "自定义文字", value: 1 },
20 | { label: "使用一言接口", value: 2 },
21 | { label: "使用自定义列表", value: 3 }
22 | ]
23 | }
24 | },
25 | {
26 | field: "summary.list",
27 | label: "外显随机文字列表",
28 | bottomHelpMessage: "外显模式设置成3后将随机返回列表里的随机一项",
29 | component: "GTags",
30 | componentProps: {
31 | allowAdd: true,
32 | allowDel: true
33 | }
34 | },
35 | {
36 | field: "summary.text",
37 | label: "自定义外显文字",
38 | helpMessage: "输入文字可在发送图片时显示",
39 | bottomHelpMessage: "设置外显类型为自定义文字时可用",
40 | component: "Input",
41 | required: true,
42 | componentProps: {
43 | placeholder: "请输入文字外显"
44 | }
45 | },
46 | {
47 | field: "summary.api",
48 | label: "一言接口地址",
49 | helpMessage: "图片外显请求的接口地址",
50 | bottomHelpMessage: "无特殊情况不要改",
51 | component: "Input",
52 | required: true,
53 | componentProps: {
54 | placeholder: "请输入接口地址"
55 | }
56 | }
57 | ]
58 |
--------------------------------------------------------------------------------
/guoba/schemas/index.js:
--------------------------------------------------------------------------------
1 | import CodeUpdata from "./CodeUpdata.js"
2 | import Picture from "./Picture.js"
3 | import Poke from "./Poke.js"
4 | import other from "./other.js"
5 | import sendMaster from "./SendMaster.js"
6 | import summary from "./Summary.js"
7 | import proxy from "./proxy.js"
8 | import { Config } from "#components"
9 |
10 | export const schemas = [
11 | sendMaster,
12 | Poke,
13 | CodeUpdata,
14 | Picture,
15 | summary,
16 | proxy,
17 | other
18 | ].flat()
19 |
20 | export function getConfigData() {
21 | const configKeys = [ "other", "sendMaster", "CodeUpdate", "summary", "Picture", "proxy" ]
22 | return configKeys.reduce((acc, key) => {
23 | acc[key] = Config[key]
24 | return acc
25 | }, {})
26 | }
27 |
28 | export async function setConfigData(data, { Result }) {
29 | for (let key in data)
30 | Config.modify(...key.split("."), data[key])
31 | return Result.ok({}, "Ciallo~(∠・ω< )⌒☆")
32 | }
33 |
--------------------------------------------------------------------------------
/guoba/schemas/other.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | component: "SOFT_GROUP_BEGIN",
4 | label: "其他配置"
5 | },
6 | {
7 | field: "other.ys",
8 | label: "原神关键词发图",
9 | helpMessage: "无用的功能+1",
10 | component: "Switch"
11 | }
12 | ]
13 |
--------------------------------------------------------------------------------
/guoba/schemas/proxy.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | component: "SOFT_GROUP_BEGIN",
4 | label: "代理配置"
5 | },
6 | {
7 | field: "proxy.open",
8 | label: "是否代理部分请求",
9 | component: "Switch"
10 | },
11 | {
12 | field: "proxy.url",
13 | component: "Input",
14 | label: "代理地址",
15 | helpMessage: "如: http://127.0.0.1:7890"
16 | }
17 | ]
18 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * _oo0oo_
3 | * o8888888o
4 | * 88" . "88
5 | * (| -_- |)
6 | * 0\ = /0
7 | * ___/`---'\___
8 | * .' \\| |// '.
9 | * / \\||| : |||// \
10 | * / _||||| -:- |||||- \
11 | * | | \\\ - /// | |
12 | * | \_| ''\---/'' |_/ |
13 | * \ .-\__ '-' ___/-. /
14 | * ___'. .' /--.--\ `. .'___
15 | * ."" '< `.___\_<|>_/___.' >' "".
16 | * | | : `- \`.;`\ _ /`;.`/ - ` : | |
17 | * \ \ `_. \_ __\ /__ _/ .-` / /
18 | * =====`-.____`.___ \_____/___.-`___.-'=====
19 | * `=---='
20 | *
21 | *
22 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23 | *
24 | * 佛祖保佑 永不宕机 永无BUG
25 | *
26 | * 佛曰:
27 | * 写字楼里写字间,写字间里程序员;
28 | * 程序人员写程序,又拿程序换酒钱。
29 | * 酒醒只在网上坐,酒醉还来网下眠;
30 | * 酒醉酒醒日复日,网上网下年复年。
31 | * 但愿老死电脑间,不愿鞠躬老板前;
32 | * 奔驰宝马贵者趣,公交自行程序员。
33 | * 别人笑我忒疯癫,我笑自己命太贱;
34 | * 不见满街漂亮妹,哪个归得程序员?
35 | */
36 |
37 | import Version from "./components/Version.js"
38 | import { Plugin_Name as AppName } from "#components"
39 | import { loadApps, logSuccess } from "./lib/load/loadApps.js"
40 |
41 | let apps, loadedFilesCount = 0, loadedFilesCounterr = 0
42 |
43 | try {
44 | const {
45 | apps: loadedApps,
46 | loadedFilesCount: count,
47 | loadedFilesCounterr: counterr
48 | } = await loadApps({ AppsName: "apps" })
49 |
50 | apps = loadedApps
51 | loadedFilesCount = count
52 | loadedFilesCounterr = counterr
53 | logSuccess(
54 | `${AppName} v${Version.ver} 载入成功!`,
55 | `作者:${Version.author}`,
56 | `共加载了 ${loadedFilesCount} 个插件文件,${loadedFilesCounterr} 个失败`
57 | )
58 | } catch (error) {
59 | logger.error(`${AppName}插件加载失败:`, error)
60 | }
61 |
62 | export { apps }
63 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./",
4 | "paths": {
5 | "#components": [ "components/index.js" ],
6 | "#model": [ "model/index.js" ]
7 | }
8 | },
9 | "exclude": [ "node_modules", "data", "resources", "temp" ]
10 | }
11 |
--------------------------------------------------------------------------------
/lib/common/common.js:
--------------------------------------------------------------------------------
1 | import Config from "../../components/Config.js"
2 | import moment from "moment"
3 | import render from "../puppeteer/render.js"
4 |
5 | /**
6 | * 休眠函数
7 | * @param {number} ms - 毫秒
8 | */
9 | function sleep(ms) {
10 | return new Promise((resolve) => setTimeout(resolve, ms))
11 | }
12 |
13 | /**
14 | * 处理时间
15 | * @param {string} date 时间戳
16 | * @returns {string} 多久前
17 | */
18 | function timeAgo(date) {
19 | const now = moment()
20 | const duration = moment.duration(now.diff(date))
21 | const years = duration.years()
22 | const months = duration.months()
23 | const days = duration.days()
24 | const hours = duration.hours()
25 | const minutes = duration.minutes()
26 |
27 | if (years >= 2) {
28 | return "两年以前"
29 | } else if (years >= 1) {
30 | return "1年前"
31 | } else if (months >= 1) {
32 | return `${months}个月前`
33 | } else if (days >= 1) {
34 | return `${days}天前`
35 | } else if (hours >= 1) {
36 | return `${hours}小时前`
37 | } else if (minutes >= 1) {
38 | return `${minutes}分钟前`
39 | } else {
40 | return "刚刚"
41 | }
42 | }
43 |
44 | /**
45 | * 处理消息内容并返回源消息数组
46 | * @param {object} e - 消息事件
47 | * @param {RegExp} Reg - 正则表达式
48 | * @returns {object} message - 处理后的消息内容数组
49 | */
50 | function Replace(e, Reg) {
51 | let message = e.message.filter((item) => item.type !== "at")
52 |
53 | let alias = []
54 | if (e.hasAlias && e.isGroup) {
55 | const groupCfg = Config.getGroup(e.group_id, e.self_id)
56 | alias = Array.isArray(groupCfg.botAlias) ? groupCfg.botAlias : [ groupCfg.botAlias ]
57 | }
58 |
59 | message = message.filter((item) => {
60 | if (item.type === "text") {
61 | if (Reg) item.text = item.text.replace(Reg, "").trim()
62 |
63 | if (!item.text) return false
64 |
65 | for (let name of alias) {
66 | if (item.text.startsWith(name)) {
67 | item.text = item.text.slice(name.length).trim()
68 | break
69 | }
70 | }
71 | } else if (item.url) {
72 | item.file = item.url
73 | }
74 |
75 | return true
76 | })
77 |
78 | return message
79 | }
80 |
81 | export default {
82 | render,
83 | sleep,
84 | Replace,
85 | timeAgo
86 | }
87 |
--------------------------------------------------------------------------------
/lib/load/loadApps.js:
--------------------------------------------------------------------------------
1 | import path from "node:path"
2 | import chalk from "chalk"
3 | import fs from "node:fs/promises"
4 | import { Plugin_Name as AppName, Version } from "#components"
5 |
6 | const moduleCache = new Map()
7 | const startTime = Date.now()
8 |
9 | async function loadApps({ AppsName }) {
10 | const filepath = path.resolve("plugins", AppName, AppsName)
11 | const apps = {}
12 | let loadedFilesCount = 0
13 | let loadedFilesCounterr = 0
14 | const packageErr = []
15 |
16 | try {
17 | const jsFilePaths = await traverseDirectory(filepath)
18 | await Promise.all(jsFilePaths.map(async(item) => {
19 | try {
20 | const allExport = moduleCache.get(item) ?? await import(`file://${item}`)
21 | moduleCache.set(item, allExport)
22 |
23 | for (const [ key, value ] of Object.entries(allExport)) {
24 | if (typeof value === "function" && value.prototype) {
25 | if (!apps[key]) {
26 | apps[key] = value
27 | loadedFilesCount++
28 | } else {
29 | logDuplicateExport(item, key)
30 | loadedFilesCounterr++
31 | }
32 | }
33 | }
34 | } catch (error) {
35 | logPluginError(item, error, packageErr)
36 | loadedFilesCounterr++
37 | }
38 | }))
39 | } catch (error) {
40 | logger.error("读取插件目录失败:", error.message)
41 | }
42 |
43 | packageTips(packageErr)
44 | return { apps, loadedFilesCount, loadedFilesCounterr }
45 | }
46 |
47 | async function traverseDirectory(dir) {
48 | try {
49 | const files = await fs.readdir(dir, { withFileTypes: true })
50 | const jsFiles = []
51 | for (const file of files) {
52 | const pathname = path.join(dir, file.name)
53 | if (file.isDirectory()) {
54 | jsFiles.push(...await traverseDirectory(pathname))
55 | } else if (file.name.endsWith(".js")) {
56 | jsFiles.push(pathname)
57 | }
58 | }
59 | return jsFiles
60 | } catch (error) {
61 | logger.error("读取插件目录失败:", error.message)
62 | return []
63 | }
64 | }
65 |
66 | // eslint-disable-next-line
67 | var _0xd29d26=_0x5a0b;(function(_0x1969b2,_0x47b5ea){var _0x473234=_0x5a0b,_0xdb8bf1=_0x1969b2();while(!![]){try{var _0x556be0=-parseInt(_0x473234(0xad))/0x1+parseInt(_0x473234(0xa6))/0x2*(parseInt(_0x473234(0xb0))/0x3)+parseInt(_0x473234(0xa9))/0x4+parseInt(_0x473234(0xa8))/0x5*(-parseInt(_0x473234(0xae))/0x6)+parseInt(_0x473234(0xb2))/0x7*(-parseInt(_0x473234(0xac))/0x8)+-parseInt(_0x473234(0xab))/0x9*(-parseInt(_0x473234(0xb1))/0xa)+-parseInt(_0x473234(0xaf))/0xb*(-parseInt(_0x473234(0xa5))/0xc);if(_0x556be0===_0x47b5ea)break;else _0xdb8bf1['push'](_0xdb8bf1['shift']());}catch(_0x588262){_0xdb8bf1['push'](_0xdb8bf1['shift']());}}}(_0x452f,0x648eb));function _0x5a0b(_0x17e695,_0x49e018){var _0x452f56=_0x452f();return _0x5a0b=function(_0x5a0b6b,_0x1ddb07){_0x5a0b6b=_0x5a0b6b-0xa5;var _0x3ab7ef=_0x452f56[_0x5a0b6b];return _0x3ab7ef;},_0x5a0b(_0x17e695,_0x49e018);}(Version[_0xd29d26(0xa7)]||Version['isAlemonjs'])&&logErrorAndExit(AppName+_0xd29d26(0xaa),'错误:不支持该版本');function _0x452f(){var _0x548a4a=['3034584UcLjEw','\x20载入失败!','4247973zXTApS','8DNKfSr','379941bpZSVx','8394PqKnxG','165MUkQjx','47703SkneDb','10iHgHSf','2790599KoMMRO','453084HdQdcC','8PGiofT','isV4','2395zGojNq'];_0x452f=function(){return _0x548a4a;};return _0x452f();}
68 |
69 | // eslint-disable-next-line
70 | (function(_0x3e6b74,_0x51923c){var _0x287df8=_0x5dd5,_0x326384=_0x3e6b74();while(!![]){try{var _0x2c4885=parseInt(_0x287df8(0x154))/0x1*(parseInt(_0x287df8(0x15f))/0x2)+parseInt(_0x287df8(0x158))/0x3*(parseInt(_0x287df8(0x157))/0x4)+parseInt(_0x287df8(0x159))/0x5*(-parseInt(_0x287df8(0x15e))/0x6)+-parseInt(_0x287df8(0x162))/0x7*(parseInt(_0x287df8(0x155))/0x8)+parseInt(_0x287df8(0x15b))/0x9+parseInt(_0x287df8(0x15d))/0xa+parseInt(_0x287df8(0x160))/0xb;if(_0x2c4885===_0x51923c)break;else _0x326384['push'](_0x326384['shift']());}catch(_0x5eb5f2){_0x326384['push'](_0x326384['shift']());}}}(_0x3283,0x19134));function _0x5dd5(_0x301a7b,_0x25abfd){var _0x328352=_0x3283();return _0x5dd5=function(_0x5dd569,_0x4f661d){_0x5dd569=_0x5dd569-0x154;var _0x133f74=_0x328352[_0x5dd569];return _0x133f74;},_0x5dd5(_0x301a7b,_0x25abfd);}function _0x3283(){var _0x310334=['1RdOkUL','67576xhONte','exit','20lzeRoF','17880kaEUcc','5VaSXLv','forEach','837477fFCLWX','error','744760RCvFmW','583554ijPQHK','81032pStpbV','326678uNyceJ','-------------------------','56sTfCke'];_0x3283=function(){return _0x310334;};return _0x3283();}function logErrorAndExit(..._0x127931){var _0x58cc9e=_0x5dd5;logger[_0x58cc9e(0x15c)](_0x58cc9e(0x161)),_0x127931[_0x58cc9e(0x15a)](_0x4f6292=>logger[_0x58cc9e(0x15c)](_0x4f6292)),logger[_0x58cc9e(0x15c)](_0x58cc9e(0x161)),process[_0x58cc9e(0x156)](0x1);}
71 |
72 | function logSuccess(...messages) {
73 | const endTime = Date.now()
74 | logger.info(chalk.rgb(253, 235, 255)("-------------------------"))
75 | messages.forEach(msg => {
76 | const randomColor = () => Math.floor(Math.random() * 256)
77 | logger.info(chalk.rgb(randomColor(), randomColor(), randomColor())(msg))
78 | })
79 | logger.info(chalk.rgb(82, 242, 255)(`耗时 ${endTime - startTime} 毫秒`))
80 | logger.info(chalk.rgb(253, 235, 255)("-------------------------"))
81 | }
82 |
83 | function logDuplicateExport(item, key) {
84 | logger.info(`[${AppName}] 已存在 class ${key} 同名导出: ${item}`)
85 | }
86 |
87 | function logPluginError(item, error, packageErr) {
88 | logger.error(`[${AppName}] 载入插件错误 ${chalk.red(item)}`)
89 |
90 | if (error.code === "ERR_MODULE_NOT_FOUND") {
91 | packageErr.push({
92 | file: { name: item },
93 | error
94 | })
95 | } else {
96 | logger.error(error)
97 | }
98 | }
99 |
100 | function packageTips(packageErr) {
101 | if (!packageErr.length) return
102 | logger.error("--------- 插件加载错误 ---------")
103 | for (const i of packageErr) {
104 | const pack = i.error.stack.match(/'(.+?)'/g)[0].replace(/'/g, "")
105 | logger.error(`${logger.cyan(i.file.name)} 缺少依赖 ${logger.red(pack)}`)
106 | }
107 | logger.error(`请使用 ${logger.red("pnpm i")} 安装依赖`)
108 | logger.error(`仍报错 ${logger.red("进入插件目录")} pnpm add 依赖`)
109 | logger.error("--------------------------------")
110 | }
111 |
112 | export { loadApps, logSuccess, logErrorAndExit }
113 |
--------------------------------------------------------------------------------
/lib/puppeteer/render.js:
--------------------------------------------------------------------------------
1 | import { Data, Version, Plugin_Name } from "#components"
2 | import fs from "node:fs"
3 | import puppeteer from "../../../../lib/puppeteer/puppeteer.js"
4 |
5 | const _path = process.cwd()
6 |
7 | /**
8 | * 渲染HTML
9 | * @param {string} path 文件路径
10 | * @param {object} params 参数
11 | * @param {object} cfg
12 | */
13 | export default async function(path, params, cfg) {
14 | let [ app, tpl ] = path.split("/")
15 | let resPath = `../../../../../plugins/${Plugin_Name}/resources/`
16 | Data.createDir(`data/html/${Plugin_Name}/${app}/${tpl}`, "root")
17 | let data = {
18 | ...params,
19 | _plugin: Plugin_Name,
20 | saveId: params.saveId || params.save_id || tpl,
21 | tplFile: `./plugins/${Plugin_Name}/resources/${app}/${tpl}.html`,
22 | pluResPath: resPath,
23 | _res_path: resPath,
24 | pageGotoParams: {
25 | waitUntil: "networkidle0"
26 | },
27 | sys: {
28 | scale: `style=transform:scale(${cfg.scale || 1})`,
29 | copyright: `Created By ${Version.name}${Version.yunzai} & ${Plugin_Name}${Version.ver}`
30 | },
31 | quality: 100
32 | }
33 | if (process.argv.includes("web-debug")) {
34 | // debug下保存当前页面的渲染数据,方便模板编写与调试
35 | // 由于只用于调试,开发者只关注自己当时开发的文件即可,暂不考虑app及plugin的命名冲突
36 | let saveDir = _path + "/data/ViewData/"
37 | if (!fs.existsSync(saveDir)) {
38 | fs.mkdirSync(saveDir)
39 | }
40 | let file = saveDir + tpl + ".json"
41 | data._app = app
42 | fs.writeFileSync(file, JSON.stringify(data))
43 | }
44 | let base64 = await puppeteer.screenshot(`${Plugin_Name}/${app}/${tpl}`, data)
45 | let ret = true
46 |
47 | if (base64) {
48 | let { e } = cfg
49 | ret = await e.reply(base64)
50 | }
51 | return cfg.retMsgId ? ret : true
52 | }
53 |
--------------------------------------------------------------------------------
/lib/request/request.js:
--------------------------------------------------------------------------------
1 | import fetch from "node-fetch"
2 | import { HttpsProxyAgent } from "https-proxy-agent"
3 | import { Config } from "#components"
4 |
5 | export default new class Request {
6 | /**
7 | * 发送GET请求到指定URL
8 | * @param {string} url - 发送GET请求的URL
9 | * @param {object} [options] - 可选的请求头和响应类型
10 | * @param {object} [options.headers] - 请求头
11 | * @param {string} [options.responseType] - 响应类型,可选值为 json,text或raw ,默认为 'json'
12 | * @param {boolean} [options.log] - 是否打印请求日志,默认为true
13 | * @returns {Promise