karin-plugin-mys-core 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1031 -76
- package/lib/apps/MiHoYoLogin.js +19 -18
- package/lib/apps/MysDevice.js +16 -17
- package/lib/apps/UIDManage.js +17 -16
- package/lib/{array-DuqoS2wx.d.ts → array-CCexQ14j.d.ts} +1 -1
- package/lib/{chunk-KN5SHBVI.js → chunk-7ZQQ76WC.js} +1 -1
- package/lib/chunk-AY34PPJT.js +72 -0
- package/lib/{chunk-COKAJ7V6.js → chunk-BH4S574A.js} +1 -1
- package/lib/{chunk-UVMX6WUC.js → chunk-BNSHDFFN.js} +4 -4
- package/lib/{chunk-3BIQN6QA.js → chunk-BWFWWHCT.js} +20 -1
- package/lib/{chunk-JKZ5NRPX.js → chunk-BXA3VAQC.js} +3 -2
- package/lib/{chunk-RWBRS7SA.js → chunk-DJTJKSNN.js} +162 -169
- package/lib/{chunk-5NA7ZPDS.js → chunk-EAVXU4Q2.js} +3 -0
- package/lib/{chunk-7WESXE4X.js → chunk-HTH25YE2.js} +1 -1
- package/lib/chunk-UX2BDLJF.js +6 -0
- package/lib/chunk-WCX7WZAQ.js +0 -0
- package/lib/{chunk-HNODD2IL.js → chunk-ZZ5BYFNK.js} +83 -6
- package/lib/{define-BWuQ7acM.d.ts → define-B9r20dK6.d.ts} +4 -5
- package/lib/exports/config/array.d.ts +1 -1
- package/lib/exports/config/config.d.ts +1 -1
- package/lib/exports/config/config.js +1 -1
- package/lib/exports/config/index.d.ts +1 -1
- package/lib/exports/config/index.js +1 -1
- package/lib/exports/database/database.js +1 -1
- package/lib/exports/database/dbs/base.js +1 -1
- package/lib/exports/database/dbs/index.js +2 -2
- package/lib/exports/database/dbs/sqlite3.js +1 -1
- package/lib/exports/database/dbs/table.d.ts +3 -0
- package/lib/exports/database/dbs/table.js +2 -2
- package/lib/exports/database/index.js +12 -13
- package/lib/exports/database/tables/index.js +10 -11
- package/lib/exports/database/tables/mysAccountInfo.js +3 -3
- package/lib/exports/database/tables/mysDeviceInfo.js +5 -6
- package/lib/exports/database/tables/mysUserInfo.js +3 -3
- package/lib/exports/database/types/index.js +3 -3
- package/lib/exports/database/types/tables/index.js +3 -3
- package/lib/exports/mys/api/apis.d.ts +5 -5
- package/lib/exports/mys/api/apis.js +14 -15
- package/lib/exports/mys/api/app.js +7 -8
- package/lib/exports/mys/api/define.d.ts +1 -1
- package/lib/exports/mys/api/define.js +14 -15
- package/lib/exports/mys/api/index.d.ts +2 -2
- package/lib/exports/mys/api/index.js +17 -18
- package/lib/exports/mys/index.d.ts +5 -3
- package/lib/exports/mys/index.js +20 -22
- package/lib/exports/mys/types/api/define.d.ts +1 -1
- package/lib/exports/mys/types/api/index.d.ts +1 -1
- package/lib/exports/mys/types/index.d.ts +4 -2
- package/lib/exports/mys/types/index.js +1 -7
- package/lib/exports/mys/types/user/game.d.ts +17 -4
- package/lib/exports/mys/types/user/index.d.ts +4 -2
- package/lib/exports/mys/types/user/index.js +1 -7
- package/lib/exports/mys/types/user/userInfo.d.ts +40 -8
- package/lib/exports/mys/types/user/userInfo.js +1 -7
- package/lib/exports/mys/user/deviceInfo.js +8 -9
- package/lib/exports/mys/user/game.d.ts +16 -3
- package/lib/exports/mys/user/game.js +5 -3
- package/lib/exports/mys/user/index.d.ts +5 -3
- package/lib/exports/mys/user/index.js +20 -19
- package/lib/exports/mys/user/userInfo.d.ts +49 -6
- package/lib/exports/mys/user/userInfo.js +14 -15
- package/lib/exports/render/index.d.ts +3 -0
- package/lib/exports/render/index.js +13 -0
- package/lib/exports/render/render.d.ts +38 -0
- package/lib/exports/render/render.js +9 -0
- package/lib/exports/utils/common.d.ts +33 -1
- package/lib/exports/utils/index.d.ts +1 -2
- package/lib/exports/utils/index.js +1 -5
- package/package.json +11 -2
- package/resources/styles/karin-plugin-mys-core.css +1059 -0
- package/lib/chunk-HVYNK5BN.js +0 -45
- package/lib/chunk-IVZICDGW.js +0 -19
- package/lib/common-Dw5l6Uud.d.ts +0 -33
- package/lib/exports/utils/render.d.ts +0 -21
- package/lib/exports/utils/render.js +0 -7
- package/lib/index-DEEWbZ0V.d.ts +0 -102
- package/resources/template/ShowBindAccount/index.css +0 -1
- package/resources/template/ShowBindAccount/index.html +0 -65
- package/resources/template/ShowBindDevice/index.html +0 -20
- package/resources/template/ShowBindDevice/inex.css +0 -1
- package/resources/template/layout/default.css +0 -1
- package/resources/template/layout/default.html +0 -27
- /package/lib/{chunk-ARLTXJFJ.js → chunk-UC6INRYV.js} +0 -0
package/README.md
CHANGED
|
@@ -1,123 +1,1078 @@
|
|
|
1
|
-
|
|
1
|
+
<h1 align="center"><font color="#3f7dd1">karin-plugin-mys-core</font></h1>
|
|
2
2
|
|
|
3
3
|
## 📖 目录
|
|
4
4
|
|
|
5
5
|
- [前言](#前言)
|
|
6
|
-
- [
|
|
7
|
-
- [
|
|
8
|
-
- [
|
|
6
|
+
- [快速安装](#快速安装)
|
|
7
|
+
- [游戏模块](#游戏模块)
|
|
8
|
+
- [API 文档](#api-文档)
|
|
9
|
+
- [配置模块 (config)](#配置模块-config)
|
|
10
|
+
- [数据库模块 (database)](#数据库模块-database)
|
|
11
|
+
- [米游社模块 (mys)](#米游社模块-mys)
|
|
12
|
+
- [渲染模块 (render)](#渲染模块-render)
|
|
13
|
+
- [工具模块 (utils)](#工具模块-utils)
|
|
9
14
|
- [贡献与反馈](#贡献与反馈)
|
|
10
15
|
|
|
11
16
|
---
|
|
12
17
|
|
|
13
|
-
##
|
|
18
|
+
## 快速安装
|
|
14
19
|
|
|
15
|
-
|
|
20
|
+
- 本插件已加入插件商店,可在插件商店中一键下载安装。
|
|
16
21
|
|
|
17
|
-
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 游戏模块
|
|
25
|
+
> [!TIP]
|
|
26
|
+
> 本插件并不直接提供完整的游戏功能
|
|
27
|
+
|
|
28
|
+
- 自行编写请查看[karin-plugin-mys-template](https://github.com/Karin-Mys-Plugins/karin-plugin-mys-template)
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## API 文档
|
|
33
|
+
|
|
34
|
+
### 配置模块 (config)
|
|
35
|
+
|
|
36
|
+
配置模块提供了强大的配置文件管理功能,包括配置类和增强数组类。
|
|
37
|
+
|
|
38
|
+
#### 导入方式
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { Config } from 'karin-plugin-mys-core/config'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
#### Config 类
|
|
45
|
+
|
|
46
|
+
用于管理 JSON 配置文件,支持自动补全、类型安全和实时监听。
|
|
47
|
+
|
|
48
|
+
**构造函数**
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
const config = new Config<ConfigType>(
|
|
52
|
+
'plugin-name:config-name', // 配置名称
|
|
53
|
+
'/path/to/config.json', // 配置文件路径
|
|
54
|
+
defaultConfig, // 默认配置对象
|
|
55
|
+
defineConfig // 配置定义对象
|
|
56
|
+
)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**主要方法**
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// 获取配置值
|
|
63
|
+
const value = config.get<T>('key.path')
|
|
64
|
+
|
|
65
|
+
// 设置配置值
|
|
66
|
+
config.set<T>('key.path', value, save?: boolean)
|
|
67
|
+
|
|
68
|
+
// 获取数组配置(返回 EnhancedArray)
|
|
69
|
+
const arr = config.getArray<T>('key.path')
|
|
70
|
+
|
|
71
|
+
// 监听配置变化(文件修改时触发)
|
|
72
|
+
config.watch((self, oldData, newData) => {
|
|
73
|
+
console.log('配置已更新')
|
|
74
|
+
console.log('旧配置:', oldData)
|
|
75
|
+
console.log('新配置:', newData)
|
|
76
|
+
|
|
77
|
+
// 可以在这里处理配置变化后的逻辑
|
|
78
|
+
if (oldData.enable !== newData.enable) {
|
|
79
|
+
console.log('功能启用状态已改变')
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
// 手动保存配置
|
|
84
|
+
config.save()
|
|
85
|
+
|
|
86
|
+
// 重新加载配置
|
|
87
|
+
config.loadConfig()
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**示例**
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
interface MyConfig {
|
|
94
|
+
enable: boolean
|
|
95
|
+
users: string[]
|
|
96
|
+
settings: {
|
|
97
|
+
timeout: number
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const config = new Config<MyConfig>(
|
|
102
|
+
'my-plugin:config',
|
|
103
|
+
'./config/config.json',
|
|
104
|
+
{
|
|
105
|
+
enable: true,
|
|
106
|
+
users: [],
|
|
107
|
+
settings: { timeout: 5000 }
|
|
108
|
+
},
|
|
109
|
+
{}
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
// 获取配置
|
|
113
|
+
const isEnabled = config.get<boolean>('enable')
|
|
114
|
+
|
|
115
|
+
// 设置配置
|
|
116
|
+
config.set('settings.timeout', 10000, true)
|
|
117
|
+
|
|
118
|
+
// 获取数组
|
|
119
|
+
const users = config.getArray<string>('users')
|
|
120
|
+
users.add('user1', true, true)
|
|
121
|
+
|
|
122
|
+
// 监听配置文件变化
|
|
123
|
+
config.watch((self, oldData, newData) => {
|
|
124
|
+
console.log('配置文件已被外部修改')
|
|
125
|
+
|
|
126
|
+
// 检查特定字段的变化
|
|
127
|
+
if (oldData.enable !== newData.enable) {
|
|
128
|
+
console.log(`功能状态变更: ${oldData.enable} -> ${newData.enable}`)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (oldData.settings.timeout !== newData.settings.timeout) {
|
|
132
|
+
console.log(`超时时间变更: ${oldData.settings.timeout}ms -> ${newData.settings.timeout}ms`)
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
#### EnhancedArray 类
|
|
138
|
+
|
|
139
|
+
扩展的数组类,提供了更多便捷的操作方法。
|
|
140
|
+
|
|
141
|
+
**主要方法**
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
// 检查元素是否存在
|
|
145
|
+
arr.has(element)
|
|
146
|
+
|
|
147
|
+
// 添加单个元素
|
|
148
|
+
arr.add(element, isEqual: boolean, save: boolean)
|
|
149
|
+
|
|
150
|
+
// 批量添加元素
|
|
151
|
+
arr.addSome(elements, isEqual: boolean, save: boolean)
|
|
152
|
+
|
|
153
|
+
// 删除元素
|
|
154
|
+
arr.remove(predicate, save: boolean)
|
|
155
|
+
arr.remove(index, save: boolean, true) // 按索引删除
|
|
156
|
+
|
|
157
|
+
// 批量删除元素
|
|
158
|
+
arr.removeSome(elements, save: boolean)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**示例**
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
const users = config.getArray<string>('users')
|
|
165
|
+
|
|
166
|
+
// 添加元素(去重)
|
|
167
|
+
users.add('user1', true, true)
|
|
168
|
+
|
|
169
|
+
// 批量添加
|
|
170
|
+
users.addSome(['user2', 'user3'], true, true)
|
|
171
|
+
|
|
172
|
+
// 删除元素
|
|
173
|
+
users.remove('user1', true)
|
|
174
|
+
|
|
175
|
+
// 按条件删除
|
|
176
|
+
users.remove(user => user.startsWith('test'), true)
|
|
177
|
+
|
|
178
|
+
// 按索引删除
|
|
179
|
+
users.remove(0, true, true)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
### 数据库模块 (database)
|
|
185
|
+
|
|
186
|
+
数据库模块提供了统一的数据库访问接口,支持 SQLite 等多种数据库。
|
|
187
|
+
|
|
188
|
+
#### 导入方式
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
import { Database } from 'karin-plugin-mys-core/database'
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
#### Database 对象
|
|
195
|
+
|
|
196
|
+
**主要方法**
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
// 获取数据库实例
|
|
200
|
+
const db = Database.get<TableType, DatabaseType>()
|
|
201
|
+
|
|
202
|
+
// 设置默认数据库(不要在你的插件中随意使用)
|
|
203
|
+
Database.default(Dialect.Sqlite)
|
|
204
|
+
|
|
205
|
+
// 添加新的数据库支持
|
|
206
|
+
await Database.Add(DatabaseFn, StaticClass)
|
|
207
|
+
|
|
208
|
+
// 获取数据库列表
|
|
209
|
+
const dbList = Database.details
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**列定义方法**
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
// 1. 普通列 - Column(type, defaultValue, options?)
|
|
216
|
+
Database.Column(
|
|
217
|
+
'STRING', // 数据类型:STRING, INTEGER, BOOLEAN, TEXT 等
|
|
218
|
+
'default', // 默认值
|
|
219
|
+
{ // 可选配置
|
|
220
|
+
allowNull: false, // 是否允许为空
|
|
221
|
+
unique: true // 是否唯一
|
|
222
|
+
}
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
// 2. 主键列 - PkColumn(type, options?)
|
|
226
|
+
Database.PkColumn(
|
|
227
|
+
'STRING', // 数据类型
|
|
228
|
+
{ // 可选配置(已包含 primaryKey: true, allowNull: false)
|
|
229
|
+
autoIncrement: true // 自动递增(仅数字类型)
|
|
230
|
+
}
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
// 3. 数组列 - ArrayColumn(key, transformFn?)
|
|
234
|
+
Database.ArrayColumn(
|
|
235
|
+
'tags', // 列名(必须与 schema 中的 key 一致)
|
|
236
|
+
(data) => data // 可选:数据转换函数
|
|
237
|
+
)
|
|
238
|
+
// 存储格式:逗号分隔的字符串 "tag1,tag2,tag3"
|
|
239
|
+
// 读取返回:DatabaseArray<T> 类型
|
|
240
|
+
|
|
241
|
+
// 4. JSON 列 - JsonColumn(key, defaultValue)
|
|
242
|
+
Database.JsonColumn(
|
|
243
|
+
'metadata', // 列名(必须与 schema 中的 key 一致)
|
|
244
|
+
{} // 默认值(JSON 对象)
|
|
245
|
+
)
|
|
246
|
+
// 存储格式:JSON 字符串
|
|
247
|
+
// 读取返回:自动解析为对象
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**完整列定义示例**
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
import { Database } from 'karin-plugin-mys-core/database'
|
|
254
|
+
|
|
255
|
+
const userSchema = {
|
|
256
|
+
// 主键列:只需指定类型
|
|
257
|
+
userId: Database.PkColumn('STRING'),
|
|
258
|
+
|
|
259
|
+
// 普通列:类型 + 默认值 + 可选配置
|
|
260
|
+
nickname: Database.Column('STRING', 'Guest', { allowNull: false }),
|
|
261
|
+
email: Database.Column('STRING', '', { unique: true }),
|
|
262
|
+
age: Database.Column('INTEGER', 0),
|
|
263
|
+
active: Database.Column('BOOLEAN', true),
|
|
264
|
+
bio: Database.Column('TEXT', ''),
|
|
265
|
+
|
|
266
|
+
// 数组列:列名必须与 key 一致
|
|
267
|
+
tags: Database.ArrayColumn('tags'),
|
|
268
|
+
// 或带转换函数
|
|
269
|
+
roles: Database.ArrayColumn('roles', (data) => {
|
|
270
|
+
return data.filter(role => role !== 'banned')
|
|
271
|
+
}),
|
|
272
|
+
|
|
273
|
+
// JSON 列:列名 + 默认值对象
|
|
274
|
+
profile: Database.JsonColumn('profile', { level: 1, exp: 0 }),
|
|
275
|
+
settings: Database.JsonColumn('settings', { theme: 'light' }),
|
|
276
|
+
metadata: Database.JsonColumn('metadata', {})
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
**数据库类型**
|
|
281
|
+
|
|
282
|
+
本模块支持三种数据库存储类型:
|
|
283
|
+
|
|
284
|
+
| 类型 | 说明 | 存储方式 | 适用场景 |
|
|
285
|
+
|------|------|---------|---------|
|
|
286
|
+
| `DatabaseType.Db` | SQL 数据库 | SQLite/MySQL 等数据库表 | 大量结构化数据、需要复杂查询 |
|
|
287
|
+
| `DatabaseType.File` | 单文件存储 | 每个记录一个 JSON 文件 | 小量数据、独立配置文件 |
|
|
288
|
+
| `DatabaseType.Dir` | 目录存储 | 每个记录一个目录,目录内多个 JSON 文件 | 复杂数据结构、需要分文件存储 |
|
|
289
|
+
|
|
290
|
+
> [!IMPORTANT]
|
|
291
|
+
> **大型数据库说明**:当使用 PostgreSQL、MySQL、MariaDB 等大型数据库时,**三种类型的存储方式都会统一使用 SQL 数据库表**。只有在使用 SQLite 时,才会根据不同的 `DatabaseType` 采用不同的存储策略(文件、目录或数据库)。
|
|
292
|
+
> 不论在使用什么数据库,编写代码时统一只考虑数据库为 SQLite 时使用何DatabaseType类型
|
|
293
|
+
> ```typescript
|
|
294
|
+
> // 使用 PostgreSQL 时(不要在你的插件中随意使用)
|
|
295
|
+
> Database.default(Dialect.PostgreSQL)
|
|
296
|
+
>
|
|
297
|
+
> // 这三种初始化方式最终都会使用 PostgreSQL 数据库表
|
|
298
|
+
> await db.init('./data', 'users', schema, DatabaseType.Db) // ✅ 数据库表
|
|
299
|
+
> await db.init('./data', 'users', schema, DatabaseType.File) // ✅ 数据库表(非文件)
|
|
300
|
+
> await db.init('./data', 'users', schema, DatabaseType.Dir) // ✅ 数据库表(非目录)
|
|
301
|
+
> ```
|
|
302
|
+
|
|
303
|
+
**初始化表**
|
|
304
|
+
|
|
305
|
+
根据不同的数据库类型初始化表:
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
// 1. SQL 数据库模式(推荐用于大量数据)
|
|
309
|
+
const dbInstance = Database.get<UserType, DatabaseType.Db>()
|
|
310
|
+
await dbInstance.init(
|
|
311
|
+
'./data', // 数据目录
|
|
312
|
+
'users', // 表名
|
|
313
|
+
schema, // 表结构
|
|
314
|
+
DatabaseType.Db // 数据库类型:SQL 数据库
|
|
315
|
+
)
|
|
316
|
+
// 存储位置:./data/database/sqlite3.db(表名:users)
|
|
317
|
+
|
|
318
|
+
// 2. 单文件存储模式(适合独立配置)
|
|
319
|
+
const fileInstance = Database.get<ConfigType, DatabaseType.File>()
|
|
320
|
+
await fileInstance.init(
|
|
321
|
+
'./data', // 数据目录
|
|
322
|
+
'configs', // 目录名
|
|
323
|
+
schema, // 数据结构
|
|
324
|
+
DatabaseType.File // 数据库类型:单文件
|
|
325
|
+
)
|
|
326
|
+
// 存储位置:./data/configs/{userId}.json
|
|
327
|
+
|
|
328
|
+
// 3. 目录存储模式(适合复杂数据)
|
|
329
|
+
const dirInstance = Database.get<ComplexType, DatabaseType.Dir>()
|
|
330
|
+
await dirInstance.init(
|
|
331
|
+
'./data', // 数据目录
|
|
332
|
+
'userdata', // 目录名
|
|
333
|
+
schema, // 数据结构
|
|
334
|
+
DatabaseType.Dir // 数据库类型:目录
|
|
335
|
+
)
|
|
336
|
+
// 存储位置:./data/userdata/{userId}/*.json
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
**数据库操作**
|
|
340
|
+
|
|
341
|
+
所有类型的数据库都支持统一的操作接口:
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
// 查找记录(主键)
|
|
345
|
+
const record = await db.findByPk(pk, create?: boolean)
|
|
346
|
+
|
|
347
|
+
// 查找多个记录(批量查询)
|
|
348
|
+
const records = await db.findAllByPks(pks)
|
|
349
|
+
|
|
350
|
+
// 查找所有记录(可排除指定主键)
|
|
351
|
+
const allRecords = await db.findAll(excludePks?: string[])
|
|
352
|
+
|
|
353
|
+
// 保存记录
|
|
354
|
+
await record.save({ key: value })
|
|
355
|
+
|
|
356
|
+
// 删除记录
|
|
357
|
+
await record.destroy()
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
**示例**
|
|
361
|
+
|
|
362
|
+
**示例 1:SQL 数据库模式 - 用户信息表**
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
import { Database, DatabaseType } from 'karin-plugin-mys-core/database'
|
|
366
|
+
|
|
367
|
+
// 定义用户表结构(注意参数顺序)
|
|
368
|
+
const userSchema = {
|
|
369
|
+
userId: Database.PkColumn('STRING'), // 主键
|
|
370
|
+
nickname: Database.Column('STRING', '', { allowNull: false }), // 默认值 '', 不允许为空
|
|
371
|
+
level: Database.Column('INTEGER', 1), // 默认值 1
|
|
372
|
+
coins: Database.Column('INTEGER', 0), // 默认值 0
|
|
373
|
+
vip: Database.Column('BOOLEAN', false), // 默认值 false
|
|
374
|
+
tags: Database.ArrayColumn('tags'), // 数组列
|
|
375
|
+
data: Database.JsonColumn('data', {}) // JSON 列,默认值 {}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// 初始化 SQL 数据库
|
|
379
|
+
const userDB = Database.get<UserType, DatabaseType.Db>()
|
|
380
|
+
await userDB.init('./data', 'users', userSchema, DatabaseType.Db)
|
|
381
|
+
|
|
382
|
+
// 操作数据
|
|
383
|
+
const user = await userDB.findByPk('123456', true) // 不存在则创建
|
|
384
|
+
await user.save({
|
|
385
|
+
level: 10,
|
|
386
|
+
coins: 1000,
|
|
387
|
+
data: { lastLogin: Date.now() }
|
|
388
|
+
})
|
|
389
|
+
|
|
390
|
+
// 批量查询
|
|
391
|
+
const users = await userDB.findAllByPks(['123456', '789012'])
|
|
392
|
+
|
|
393
|
+
// 查询所有用户
|
|
394
|
+
const allUsers = await userDB.findAll()
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
**示例 2:单文件存储 - 配置文件**
|
|
398
|
+
|
|
399
|
+
```typescript
|
|
400
|
+
import { Database, DatabaseType } from 'karin-plugin-mys-core/database'
|
|
401
|
+
|
|
402
|
+
// 定义配置结构(注意参数顺序)
|
|
403
|
+
const configSchema = {
|
|
404
|
+
key: Database.PkColumn('STRING'), // 主键
|
|
405
|
+
value: Database.Column('TEXT', ''), // 默认值 ''
|
|
406
|
+
type: Database.Column('STRING', 'string'), // 默认值 'string'
|
|
407
|
+
updatedAt: Database.Column('INTEGER', 0) // 默认值 0
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// 初始化文件存储
|
|
411
|
+
const configDB = Database.get<ConfigType, DatabaseType.File>()
|
|
412
|
+
await configDB.init('./config', 'settings', configSchema, DatabaseType.File)
|
|
413
|
+
|
|
414
|
+
// 操作配置
|
|
415
|
+
const config = await configDB.findByPk('app_name', true)
|
|
416
|
+
await config.save({
|
|
417
|
+
key: 'app_name',
|
|
418
|
+
value: 'My App',
|
|
419
|
+
type: 'string',
|
|
420
|
+
updatedAt: Date.now()
|
|
421
|
+
})
|
|
422
|
+
// 将保存到:./config/settings/app_name.json
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
**示例 3:目录存储 - 复杂用户数据**
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
import { Database, DatabaseType } from 'karin-plugin-mys-core/database'
|
|
429
|
+
|
|
430
|
+
// 定义复杂数据结构(注意参数顺序)
|
|
431
|
+
const complexSchema = {
|
|
432
|
+
userId: Database.PkColumn('STRING'), // 主键
|
|
433
|
+
profile: Database.JsonColumn('profile', {}), // JSON 列,默认值 {}
|
|
434
|
+
inventory: Database.JsonColumn('inventory', { items: [] }), // JSON 列,默认值 { items: [] }
|
|
435
|
+
achievements: Database.ArrayColumn('achievements'), // 数组列
|
|
436
|
+
settings: Database.JsonColumn('settings', {}) // JSON 列,默认值 {}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// 初始化目录存储
|
|
440
|
+
const complexDB = Database.get<ComplexType, DatabaseType.Dir>()
|
|
441
|
+
await complexDB.init('./data', 'userdata', complexSchema, DatabaseType.Dir)
|
|
442
|
+
|
|
443
|
+
// 操作数据
|
|
444
|
+
const userData = await complexDB.findByPk('123456', true)
|
|
445
|
+
await userData.save({
|
|
446
|
+
userId: '123456',
|
|
447
|
+
profile: { name: '玩家', avatar: 'url' },
|
|
448
|
+
inventory: { items: [], weapons: [] },
|
|
449
|
+
achievements: ['first_login', 'level_10'],
|
|
450
|
+
settings: { theme: 'dark', language: 'zh-cn' }
|
|
451
|
+
})
|
|
452
|
+
// 将保存到:./data/userdata/123456/ 目录下的多个 JSON 文件
|
|
453
|
+
|
|
454
|
+
// 删除数据
|
|
455
|
+
await userData.destroy() // 删除整个目录
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
**数据库方言对比**
|
|
459
|
+
|
|
460
|
+
```typescript
|
|
461
|
+
import { Dialect } from 'karin-plugin-mys-core/database'
|
|
462
|
+
|
|
463
|
+
// SQLite(默认)- 支持三种存储模式
|
|
464
|
+
Database.default(Dialect.Sqlite)
|
|
465
|
+
// ✅ DatabaseType.Db → SQLite 数据库表
|
|
466
|
+
// ✅ DatabaseType.File → JSON 文件存储
|
|
467
|
+
// ✅ DatabaseType.Dir → 目录 + JSON 文件
|
|
468
|
+
|
|
469
|
+
// PostgreSQL/MySQL/MariaDB 等 - 所有数据都使用 Db 模式
|
|
470
|
+
Database.default(Dialect.PostgreSQL) // 或 MySQL, MariaDB
|
|
471
|
+
// ✅ DatabaseType.Db → PostgreSQL 数据库表
|
|
472
|
+
// ✅ DatabaseType.File → PostgreSQL 数据库表
|
|
473
|
+
// ✅ DatabaseType.Dir → PostgreSQL 数据库表
|
|
474
|
+
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
**内置表**
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
import {
|
|
481
|
+
MysUserInfoDB, // 用户信息表
|
|
482
|
+
MysAccountInfoDB, // 账号信息表
|
|
483
|
+
MysDeviceInfoDB // 设备信息表
|
|
484
|
+
} from 'karin-plugin-mys-core/database'
|
|
485
|
+
|
|
486
|
+
// 使用内置表
|
|
487
|
+
const userDB = await MysUserInfoDB()
|
|
488
|
+
const user = await userDB.findByPk(userId, true)
|
|
489
|
+
```
|
|
18
490
|
|
|
19
491
|
---
|
|
20
492
|
|
|
21
|
-
|
|
493
|
+
### 米游社模块 (mys)
|
|
494
|
+
|
|
495
|
+
米游社模块提供了完整的米游社 API 调用和用户管理功能。
|
|
496
|
+
|
|
497
|
+
#### 导入方式
|
|
498
|
+
|
|
499
|
+
```typescript
|
|
500
|
+
import {
|
|
501
|
+
UserInfo, // 用户信息类
|
|
502
|
+
MysGame, // 游戏注册管理
|
|
503
|
+
DefineApi, // API 定义类
|
|
504
|
+
MysApp, // 米游社应用配置
|
|
505
|
+
MysHosts // 米游社主机地址
|
|
506
|
+
} from 'karin-plugin-mys-core/mys'
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
#### UserInfo 类
|
|
510
|
+
|
|
511
|
+
管理用户的米游社账号信息。
|
|
512
|
+
|
|
513
|
+
**创建用户信息**
|
|
514
|
+
|
|
515
|
+
```typescript
|
|
516
|
+
// 创建用户信息实例
|
|
517
|
+
const userInfo = await UserInfo.create(userId, initAll?: boolean)
|
|
518
|
+
|
|
519
|
+
// 刷新 UID
|
|
520
|
+
const result = await UserInfo.refreshUid({
|
|
521
|
+
userId: 'xxx',
|
|
522
|
+
cookie: 'xxx',
|
|
523
|
+
ltuid: 'xxx',
|
|
524
|
+
type: MysAccountType.cn
|
|
525
|
+
}, UidPermission.Allow)
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
**属性和方法**
|
|
529
|
+
|
|
530
|
+
```typescript
|
|
531
|
+
// 获取 ltuid 列表
|
|
532
|
+
const ltuids = userInfo.ltuids
|
|
533
|
+
|
|
534
|
+
// 获取 stuid 列表
|
|
535
|
+
const stuids = userInfo.stuids
|
|
536
|
+
|
|
537
|
+
// 获取账号信息列表
|
|
538
|
+
const accounts = userInfo.LtuidInfoList
|
|
539
|
+
|
|
540
|
+
// 获取特定账号信息
|
|
541
|
+
const account = userInfo.getLtuidInfo(ltuid)
|
|
542
|
+
|
|
543
|
+
// 获取设备信息列表
|
|
544
|
+
const devices = await userInfo.getDeviceInfoList()
|
|
545
|
+
|
|
546
|
+
// 保存用户信息
|
|
547
|
+
await userInfo.saveUserInfo({ key: value })
|
|
548
|
+
|
|
549
|
+
// 保存米游社账号信息
|
|
550
|
+
await userInfo.saveMysAccountInfo(ltuid, { cookie: 'xxx' })
|
|
551
|
+
|
|
552
|
+
// 刷新用户信息
|
|
553
|
+
await userInfo.refresh()
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
#### MysGame 游戏管理
|
|
557
|
+
|
|
558
|
+
注册和管理游戏模块。
|
|
559
|
+
|
|
560
|
+
**注册游戏**
|
|
561
|
+
|
|
562
|
+
使用 `RegisterGameBase` 类注册新游戏:
|
|
563
|
+
|
|
564
|
+
```typescript
|
|
565
|
+
import { MysGame, RegisterGameBase } from 'karin-plugin-mys-core/mys'
|
|
566
|
+
import { GameUserInfo } from './GameUserInfo' // 你的游戏用户信息类
|
|
567
|
+
|
|
568
|
+
// 创建游戏注册对象
|
|
569
|
+
const MyGame = new RegisterGameBase(
|
|
570
|
+
'game_key', // 游戏标识(如:gs, sr, zzz)
|
|
571
|
+
'原神', // 游戏名称
|
|
572
|
+
/^#?(原神|gs)/i, // 指令前缀匹配正则
|
|
573
|
+
GameUserInfo, // 游戏用户信息类
|
|
574
|
+
(info) => { // UID 刷新函数
|
|
575
|
+
return info.map(item => item.game_uid)
|
|
576
|
+
}
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
// 注册到 MysGame
|
|
580
|
+
MysGame.RegisterGame(MyGame)
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
**游戏匹配**
|
|
584
|
+
|
|
585
|
+
```typescript
|
|
586
|
+
// 通过指令前缀匹配游戏
|
|
587
|
+
const game = MysGame.match('#原神角色')
|
|
588
|
+
if (game) {
|
|
589
|
+
console.log('匹配到游戏:', game.name)
|
|
590
|
+
console.log('游戏标识:', game.game)
|
|
591
|
+
console.log('列键名:', game.columnKey) // 'gs-uids'
|
|
592
|
+
}
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
**遍历游戏**
|
|
596
|
+
|
|
597
|
+
```typescript
|
|
598
|
+
// 遍历所有已注册的游戏
|
|
599
|
+
await MysGame.forEachGame(async (game) => {
|
|
600
|
+
console.log(`游戏: ${game.name}`)
|
|
601
|
+
console.log(`标识: ${game.game}`)
|
|
602
|
+
|
|
603
|
+
// 如果需要中断遍历,返回 'break'
|
|
604
|
+
if (someCondition) {
|
|
605
|
+
return 'break'
|
|
606
|
+
}
|
|
607
|
+
})
|
|
608
|
+
|
|
609
|
+
// 获取已注册游戏数量
|
|
610
|
+
console.log(`已注册 ${MysGame.num} 个游戏`)
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
**完整示例**
|
|
614
|
+
|
|
615
|
+
```typescript
|
|
616
|
+
import {
|
|
617
|
+
MysGame,
|
|
618
|
+
RegisterGameBase,
|
|
619
|
+
GameUserInfoBase
|
|
620
|
+
} from 'karin-plugin-mys-core/mys'
|
|
621
|
+
|
|
622
|
+
// 1. 定义游戏用户信息类(继承自 GameUserInfoBase)
|
|
623
|
+
class GenshinUserInfo extends GameUserInfoBase<GenshinUserInfoTableType> {
|
|
624
|
+
static async create(userId: string) {
|
|
625
|
+
// 实现创建逻辑
|
|
626
|
+
const userInfo = new GenshinUserInfo(userId)
|
|
627
|
+
await userInfo.refresh()
|
|
628
|
+
return userInfo
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
async refresh() {
|
|
632
|
+
// 实现刷新逻辑
|
|
633
|
+
return this
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// 2. 创建并注册游戏
|
|
638
|
+
const Genshin = new RegisterGameBase(
|
|
639
|
+
'gs', // 游戏标识
|
|
640
|
+
'原神', // 游戏名称
|
|
641
|
+
/^#?(原神|gs|ys)/i, // 匹配 #原神 #gs #ys
|
|
642
|
+
GenshinUserInfo, // 用户信息类
|
|
643
|
+
(roleList) => { // UID 提取函数
|
|
644
|
+
return roleList
|
|
645
|
+
.filter(role => role.game_biz === 'hk4e_cn')
|
|
646
|
+
.map(role => role.game_uid)
|
|
647
|
+
}
|
|
648
|
+
)
|
|
649
|
+
|
|
650
|
+
MysGame.RegisterGame(Genshin)
|
|
651
|
+
|
|
652
|
+
// 3. 使用游戏
|
|
653
|
+
const game = MysGame.match('#原神角色')
|
|
654
|
+
if (game) {
|
|
655
|
+
// 创建用户信息
|
|
656
|
+
const userInfo = await game.UserInfo.create(userId)
|
|
657
|
+
|
|
658
|
+
// 获取主 UID
|
|
659
|
+
console.log('主 UID:', userInfo.main_uid)
|
|
660
|
+
|
|
661
|
+
// 获取所有绑定的 UID
|
|
662
|
+
console.log('绑定 UID:', userInfo.bind_uids)
|
|
663
|
+
|
|
664
|
+
// 获取 UID 信息
|
|
665
|
+
const uidInfo = userInfo.getUIDInfo(uid)
|
|
666
|
+
}
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
#### DefineApi API 定义
|
|
671
|
+
|
|
672
|
+
定义和调用米游社 API。
|
|
22
673
|
|
|
23
|
-
```
|
|
24
|
-
|
|
674
|
+
```typescript
|
|
675
|
+
// 定义 API
|
|
676
|
+
const myApi = new DefineApi<ResponseType, RequestType>(
|
|
677
|
+
(self, data) => ({
|
|
678
|
+
Method: 'POST',
|
|
679
|
+
Url: new URL('https://api.example.com/endpoint'),
|
|
680
|
+
Body: data,
|
|
681
|
+
HeaderFn: self.DefaultHeaders
|
|
682
|
+
})
|
|
683
|
+
)
|
|
684
|
+
|
|
685
|
+
// 调用 API
|
|
686
|
+
const response = await myApi.request({
|
|
687
|
+
ltuid: 'xxx',
|
|
688
|
+
cookie: 'xxx',
|
|
689
|
+
type: MysAccountType.cn,
|
|
690
|
+
// 其他请求参数...
|
|
691
|
+
})
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
**内置 API**
|
|
695
|
+
|
|
696
|
+
```typescript
|
|
697
|
+
import {
|
|
698
|
+
fetchQRcode, // 获取二维码
|
|
699
|
+
queryQRcode, // 查询二维码状态
|
|
700
|
+
getTokenByGameToken, // 通过游戏 Token 获取 Token
|
|
701
|
+
getCookieTokenBySToken, // 通过 SToken 获取 CookieToken
|
|
702
|
+
getUserGameRolesByCookie // 获取用户游戏角色
|
|
703
|
+
} from 'karin-plugin-mys-core/mys'
|
|
25
704
|
```
|
|
26
705
|
|
|
27
|
-
|
|
28
|
-
|
|
706
|
+
#### 配置常量
|
|
707
|
+
|
|
708
|
+
```typescript
|
|
709
|
+
// 米游社应用配置
|
|
710
|
+
MysApp.version // { cn: '2.70.1', os: '1.5.0' }
|
|
711
|
+
MysApp.appId // 游戏 ID
|
|
712
|
+
MysApp.salt // 签名盐值
|
|
713
|
+
|
|
714
|
+
// 米游社主机地址
|
|
715
|
+
MysHosts.bbs // 社区 API
|
|
716
|
+
MysHosts.web // Web API
|
|
717
|
+
MysHosts.record // 记录 API
|
|
718
|
+
MysHosts.hk4e // 原神 API
|
|
719
|
+
// 更多主机地址...
|
|
720
|
+
```
|
|
29
721
|
|
|
30
722
|
---
|
|
31
723
|
|
|
32
|
-
|
|
724
|
+
### 渲染模块 (render)
|
|
725
|
+
|
|
726
|
+
渲染模块提供了基于 React 的模板渲染功能,可以将 React 组件渲染为图片。
|
|
727
|
+
|
|
728
|
+
#### 导入方式
|
|
729
|
+
|
|
730
|
+
```typescript
|
|
731
|
+
import { ReactRender, React } from 'karin-plugin-mys-core/render'
|
|
732
|
+
// 或者单独导入
|
|
733
|
+
import React from 'karin-plugin-mys-core/render'
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
#### ReactRender 类
|
|
737
|
+
|
|
738
|
+
用于将 React 组件渲染为图片的核心类。
|
|
739
|
+
|
|
740
|
+
**类型定义**
|
|
741
|
+
|
|
742
|
+
```typescript
|
|
743
|
+
interface RenderCfg {
|
|
744
|
+
/** 插件名称 package.json 的 name */
|
|
745
|
+
name: string
|
|
746
|
+
/** 插件版本 package.json 的 version */
|
|
747
|
+
version: string
|
|
748
|
+
/** 根目录绝对路径 */
|
|
749
|
+
pluginDir: string
|
|
750
|
+
/** 插件资源目录 @karinjs/karin-plugin-xxx/resources */
|
|
751
|
+
ResourcesDir: string
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
class ReactRender<P extends Record<string, any>, K extends string>
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
**构造函数**
|
|
758
|
+
|
|
759
|
+
```typescript
|
|
760
|
+
const render = new ReactRender<PluginOptions, TemplateName>(
|
|
761
|
+
{
|
|
762
|
+
name: 'karin-plugin-example',
|
|
763
|
+
version: '1.0.0',
|
|
764
|
+
pluginDir: '/path/to/plugin',
|
|
765
|
+
ResourcesDir: '/path/to/@karinjs/karin-plugin-example/resources'
|
|
766
|
+
},
|
|
767
|
+
{
|
|
768
|
+
// 自定义插件参数(可选)
|
|
769
|
+
customOption: 'value'
|
|
770
|
+
}
|
|
771
|
+
)
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
**主要属性**
|
|
775
|
+
|
|
776
|
+
```typescript
|
|
777
|
+
// 获取插件信息
|
|
778
|
+
render.plugin
|
|
779
|
+
// 返回:
|
|
780
|
+
// {
|
|
781
|
+
// name: string // 插件名称
|
|
782
|
+
// version: string // 插件版本
|
|
783
|
+
// resources: {
|
|
784
|
+
// default: string // 插件内部资源路径
|
|
785
|
+
// download: string // 插件外部资源路径
|
|
786
|
+
// }
|
|
787
|
+
// ...customOptions // 自定义选项
|
|
788
|
+
// }
|
|
789
|
+
|
|
790
|
+
// 获取 Karin 版本信息
|
|
791
|
+
render.karin
|
|
792
|
+
// 返回: { version: string }
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
**template 方法**
|
|
796
|
+
|
|
797
|
+
将 React 组件渲染为图片。
|
|
798
|
+
|
|
799
|
+
```typescript
|
|
800
|
+
async template<C extends React.ComponentType<any>>(
|
|
801
|
+
template: K, // 模板名称
|
|
802
|
+
component: C, // React 组件
|
|
803
|
+
props: React.ComponentProps<C>, // 组件 props
|
|
804
|
+
options?: {
|
|
805
|
+
type?: 'png' | 'jpeg' | 'webp' // 图片格式,默认 'jpeg'
|
|
806
|
+
plugin?: Record<string, any> // 额外插件参数
|
|
807
|
+
render?: { // 渲染选项
|
|
808
|
+
name?: string // 文件名(不含后缀)
|
|
809
|
+
setViewport?: {
|
|
810
|
+
deviceScaleFactor?: number // 设备缩放比例,默认 2
|
|
811
|
+
}
|
|
812
|
+
// 更多 karin.render 选项...
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
): Promise<string | null>
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
**返回值**
|
|
819
|
+
|
|
820
|
+
- 成功:返回 `'base64://...'` 格式的 base64 图片字符串
|
|
821
|
+
- 失败:返回 `null`
|
|
822
|
+
|
|
823
|
+
**渲染流程**
|
|
824
|
+
|
|
825
|
+
1. 将 React 组件渲染为 HTML 字符串
|
|
826
|
+
2. 生成完整的 HTML 文档,自动引入 CSS 文件
|
|
827
|
+
3. 保存 HTML 到临时目录
|
|
828
|
+
4. 使用 Puppeteer 将 HTML 渲染为图片
|
|
829
|
+
5. 返回 base64 格式的图片数据
|
|
830
|
+
|
|
831
|
+
**CSS 文件要求**
|
|
832
|
+
|
|
833
|
+
CSS 文件应放置在 `resources/styles/{插件名}.css` 路径下,会自动被引入到渲染的 HTML 中。
|
|
834
|
+
|
|
835
|
+
**完整示例**
|
|
836
|
+
|
|
837
|
+
```typescript
|
|
838
|
+
import { ReactRender, React } from 'karin-plugin-mys-core/render'
|
|
839
|
+
import path from 'path'
|
|
840
|
+
|
|
841
|
+
// 定义模板名称类型
|
|
842
|
+
type Templates = 'userCard' | 'stats'
|
|
843
|
+
|
|
844
|
+
// 定义自定义插件选项(可选)
|
|
845
|
+
interface PluginOptions {
|
|
846
|
+
theme: string
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
// 创建渲染器实例
|
|
850
|
+
const render = new ReactRender<PluginOptions, Templates>(
|
|
851
|
+
{
|
|
852
|
+
name: 'karin-plugin-example',
|
|
853
|
+
version: '1.0.0',
|
|
854
|
+
pluginDir: path.resolve(__dirname, '..'),
|
|
855
|
+
ResourcesDir: path.resolve(__dirname, '../resources')
|
|
856
|
+
},
|
|
857
|
+
{
|
|
858
|
+
theme: 'light' // 自定义选项
|
|
859
|
+
}
|
|
860
|
+
)
|
|
861
|
+
|
|
862
|
+
// 定义组件的 Props 类型
|
|
863
|
+
interface UserCardProps {
|
|
864
|
+
username: string
|
|
865
|
+
level: number
|
|
866
|
+
avatar: string
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// 创建 React 组件
|
|
870
|
+
const UserCard: React.FC<UserCardProps> = ({ username, level, avatar }) => {
|
|
871
|
+
// 访问插件信息
|
|
872
|
+
const plugin = render.plugin
|
|
873
|
+
|
|
874
|
+
return (
|
|
875
|
+
<div className="user-card">
|
|
876
|
+
<img src={avatar} alt="avatar" />
|
|
877
|
+
<h2>{username}</h2>
|
|
878
|
+
<p>等级: {level}</p>
|
|
879
|
+
<p>主题: {plugin.theme}</p>
|
|
880
|
+
{/* 使用插件资源 */}
|
|
881
|
+
<img src={`${plugin.resources.default}/image/icon.png`} />
|
|
882
|
+
</div>
|
|
883
|
+
)
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// 渲染组件为图片
|
|
887
|
+
async function renderUserCard(userId: string) {
|
|
888
|
+
const image = await render.template(
|
|
889
|
+
'userCard', // 模板名称
|
|
890
|
+
UserCard, // 组件
|
|
891
|
+
{ // Props
|
|
892
|
+
username: '玩家',
|
|
893
|
+
level: 60,
|
|
894
|
+
avatar: 'https://...'
|
|
895
|
+
},
|
|
896
|
+
{ // 选项
|
|
897
|
+
type: 'png', // PNG 格式
|
|
898
|
+
render: {
|
|
899
|
+
name: `user-${userId}`, // 自定义文件名
|
|
900
|
+
setViewport: {
|
|
901
|
+
deviceScaleFactor: 2 // 2倍缩放(高清)
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
)
|
|
906
|
+
|
|
907
|
+
if (image) {
|
|
908
|
+
console.log('渲染成功:', image)
|
|
909
|
+
// 返回 'base64://...'
|
|
910
|
+
return image
|
|
911
|
+
} else {
|
|
912
|
+
console.error('渲染失败')
|
|
913
|
+
return null
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
```
|
|
33
917
|
|
|
34
|
-
|
|
918
|
+
**在消息事件中使用**
|
|
35
919
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
920
|
+
```typescript
|
|
921
|
+
import { plugin } from 'node-karin'
|
|
922
|
+
import { ReactRender, React } from 'karin-plugin-mys-core/render'
|
|
39
923
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
924
|
+
// 创建渲染器
|
|
925
|
+
const render = new ReactRender<{}, 'profile'>(
|
|
926
|
+
{
|
|
927
|
+
name: 'my-plugin',
|
|
928
|
+
version: '1.0.0',
|
|
929
|
+
pluginDir: __dirname,
|
|
930
|
+
ResourcesDir: path.join(__dirname, 'resources')
|
|
931
|
+
}
|
|
932
|
+
)
|
|
43
933
|
|
|
44
|
-
|
|
934
|
+
// 定义组件
|
|
935
|
+
interface ProfileProps {
|
|
936
|
+
nickname: string
|
|
937
|
+
uid: string
|
|
938
|
+
level: number
|
|
939
|
+
}
|
|
45
940
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
941
|
+
const ProfileCard: React.FC<ProfileProps> = ({ nickname, uid, level }) => (
|
|
942
|
+
<div className="profile-card">
|
|
943
|
+
<h1>{nickname}</h1>
|
|
944
|
+
<p>UID: {uid}</p>
|
|
945
|
+
<p>等级: {level}</p>
|
|
946
|
+
</div>
|
|
947
|
+
)
|
|
49
948
|
|
|
50
|
-
|
|
949
|
+
// 在插件中使用
|
|
950
|
+
export const showProfile = plugin({
|
|
951
|
+
name: '查看信息',
|
|
952
|
+
rule: [{ reg: /^#查看信息$/i }]
|
|
953
|
+
}, async (e) => {
|
|
954
|
+
// 渲染图片
|
|
955
|
+
const image = await render.template(
|
|
956
|
+
'profile',
|
|
957
|
+
ProfileCard,
|
|
958
|
+
{
|
|
959
|
+
nickname: e.sender.card || e.sender.nickname,
|
|
960
|
+
uid: e.userId,
|
|
961
|
+
level: 60
|
|
962
|
+
},
|
|
963
|
+
{ type: 'jpeg' }
|
|
964
|
+
)
|
|
965
|
+
|
|
966
|
+
if (image) {
|
|
967
|
+
// 直接发送 base64 图片
|
|
968
|
+
await e.reply(image)
|
|
969
|
+
} else {
|
|
970
|
+
await e.reply('渲染失败')
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
return true
|
|
974
|
+
})
|
|
975
|
+
```
|
|
51
976
|
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
pnpm dev
|
|
55
|
-
```
|
|
56
|
-
- 编写你的插件代码于 `src/` 目录。
|
|
57
|
-
- 编译输出:
|
|
58
|
-
```bash
|
|
59
|
-
pnpm build
|
|
60
|
-
```
|
|
61
|
-
- 调试编译之后的代码:
|
|
62
|
-
```bash
|
|
63
|
-
pnpm app
|
|
64
|
-
```
|
|
65
|
-
- 本地调试建议:
|
|
66
|
-
- 可用 `pnpm link --global` 进行全局软链测试。
|
|
67
|
-
- 或在 karin 根目录用 `pnpm add ../your-plugin-repo -w` 进行本地依赖测试。
|
|
977
|
+
**使用 Tailwind CSS**
|
|
68
978
|
|
|
69
|
-
|
|
979
|
+
插件支持 Tailwind CSS,可以在组件中直接使用 Tailwind 类名:
|
|
70
980
|
|
|
71
|
-
|
|
981
|
+
```typescript
|
|
982
|
+
const Card: React.FC<{ title: string }> = ({ title }) => (
|
|
983
|
+
<div className="bg-white rounded-lg shadow-md p-4">
|
|
984
|
+
<h2 className="text-xl font-bold text-gray-800">{title}</h2>
|
|
985
|
+
<div className="mt-2 space-y-2">
|
|
986
|
+
<p className="text-sm text-gray-600">这是一段文字</p>
|
|
987
|
+
</div>
|
|
988
|
+
</div>
|
|
989
|
+
)
|
|
990
|
+
```
|
|
72
991
|
|
|
73
|
-
|
|
74
|
-
2. 进入 `Access Tokens`,新建 `Classic Token`,类型选 `Automation`。
|
|
75
|
-
3. 复制生成的 Token。
|
|
76
|
-
4. 打开你的 GitHub 仓库 → Settings → Secrets and variables → Actions。
|
|
77
|
-
5. 新建 `NPM_TOKEN`,粘贴 Token。
|
|
78
|
-
6. 允许 GitHub Actions 创建和批准 PR(Settings → Actions)。
|
|
992
|
+
确保已引入 Tailwind:
|
|
79
993
|
|
|
80
|
-
|
|
994
|
+
```css
|
|
995
|
+
@tailwind base;
|
|
996
|
+
@tailwind components;
|
|
997
|
+
@tailwind utilities;
|
|
81
998
|
|
|
82
|
-
|
|
999
|
+
/* 自定义样式 */
|
|
1000
|
+
.custom-class {
|
|
1001
|
+
/* ... */
|
|
1002
|
+
}
|
|
1003
|
+
```
|
|
83
1004
|
|
|
84
|
-
|
|
85
|
-
- 其他如 `author`、`description`、`homepage`、`bugs.url`、`repository` 可在 package.json 中补充完善。
|
|
86
|
-
- **CI 配置无需再手动修改 package-name,已自动同步。**
|
|
1005
|
+
**高级用法:复杂布局**
|
|
87
1006
|
|
|
88
|
-
|
|
1007
|
+
```typescript
|
|
1008
|
+
// 布局组件
|
|
1009
|
+
const DefaultLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => (
|
|
1010
|
+
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 p-4">
|
|
1011
|
+
<div className="max-w-4xl mx-auto">
|
|
1012
|
+
{children}
|
|
1013
|
+
</div>
|
|
1014
|
+
</div>
|
|
1015
|
+
)
|
|
89
1016
|
|
|
90
|
-
|
|
1017
|
+
// 内容组件
|
|
1018
|
+
const ContentCard: React.FC<{ data: any[] }> = ({ data }) => (
|
|
1019
|
+
<DefaultLayout>
|
|
1020
|
+
<div className="bg-white rounded-2xl shadow-xl p-6">
|
|
1021
|
+
<h1 className="text-3xl font-bold mb-4">标题</h1>
|
|
1022
|
+
{data.map((item, idx) => (
|
|
1023
|
+
<div key={idx} className="border-b py-2">
|
|
1024
|
+
{item.name}
|
|
1025
|
+
</div>
|
|
1026
|
+
))}
|
|
1027
|
+
</div>
|
|
1028
|
+
</DefaultLayout>
|
|
1029
|
+
)
|
|
91
1030
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
1031
|
+
// 渲染
|
|
1032
|
+
const image = await render.template('content', ContentCard, {
|
|
1033
|
+
data: [{ name: '项目1' }, { name: '项目2' }]
|
|
1034
|
+
})
|
|
1035
|
+
```
|
|
96
1036
|
|
|
97
|
-
|
|
1037
|
+
**注意事项**
|
|
98
1038
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
1039
|
+
1. **CSS 文件位置**:CSS 文件必须位于 `resources/styles/{插件名}.css`,否则样式无法加载
|
|
1040
|
+
2. **图片格式**:`png` 支持透明背景,`jpeg` 文件更小,`webp` 是现代格式
|
|
1041
|
+
3. **性能优化**:使用 `deviceScaleFactor: 2` 可获得高清图片,但会增加渲染时间
|
|
1042
|
+
4. **临时文件**:HTML 临时文件保存在 `@karinjs/temp/html/{插件名}/{模板名}/` 目录
|
|
1043
|
+
5. **资源路径**:在组件中使用 `render.plugin.resources.default` 访问插件资源
|
|
104
1044
|
|
|
105
1045
|
---
|
|
106
1046
|
|
|
107
|
-
|
|
1047
|
+
### 工具模块 (utils)
|
|
1048
|
+
|
|
1049
|
+
工具模块提供了常用的工具函数和渲染功能。
|
|
1050
|
+
|
|
1051
|
+
#### 导入方式
|
|
108
1052
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
1053
|
+
```typescript
|
|
1054
|
+
import { common } from 'karin-plugin-mys-core/utils'
|
|
1055
|
+
```
|
|
1056
|
+
|
|
1057
|
+
#### common 工具函数
|
|
1058
|
+
|
|
1059
|
+
```typescript
|
|
1060
|
+
// 生成随机字符串
|
|
1061
|
+
const str = common.randomString(10, 'All') // 'Lower' | 'Upper' | 'All'
|
|
1062
|
+
|
|
1063
|
+
// 生成设备 GUID
|
|
1064
|
+
const guid = common.getDeviceGuid()
|
|
1065
|
+
|
|
1066
|
+
// 字符串转对象
|
|
1067
|
+
const obj = common.StrToObj<{ key: string }>('key=value&foo=bar', '&')
|
|
1068
|
+
|
|
1069
|
+
// 对象转字符串
|
|
1070
|
+
const str = common.ObjToStr({ key: 'value', foo: 'bar' }, '&')
|
|
1071
|
+
```
|
|
117
1072
|
|
|
118
1073
|
---
|
|
119
1074
|
|
|
120
1075
|
## 贡献与反馈
|
|
121
1076
|
|
|
122
|
-
- 有任何建议或问题,欢迎在 [Issues](https://github.com/
|
|
1077
|
+
- 有任何建议或问题,欢迎在 [Issues](https://github.com/Karin-Mys-Plugins/karin-plugin-mys-core/issues) 提出。
|
|
123
1078
|
- 也可加入官方交流群交流经验。
|