koishi-plugin-noah 1.0.0
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/lib/config.d.ts +2 -0
- package/lib/core/command.d.ts +4 -0
- package/lib/core/commands/bind.d.ts +3 -0
- package/lib/core/commands/card.d.ts +3 -0
- package/lib/core/commands/help.d.ts +3 -0
- package/lib/core/commands/server.d.ts +3 -0
- package/lib/core/config.d.ts +2 -0
- package/lib/core/database.d.ts +49 -0
- package/lib/core/index.d.ts +6 -0
- package/lib/core/services/card-service.d.ts +46 -0
- package/lib/core/services/drawer-service.d.ts +0 -0
- package/lib/core/services/server-service.d.ts +61 -0
- package/lib/core/types/index.d.ts +16 -0
- package/lib/core/utils/card.d.ts +24 -0
- package/lib/core/utils/role.d.ts +6 -0
- package/lib/drawer/BaseDrawer.d.ts +19 -0
- package/lib/drawer/Core/index.d.ts +22 -0
- package/lib/drawer/DrawerFactory.d.ts +8 -0
- package/lib/drawer/IIDX/index.d.ts +29 -0
- package/lib/drawer/SDVX/index.d.ts +35 -0
- package/lib/drawer/index.d.ts +13 -0
- package/lib/games/iidx/services/drawer-service.d.ts +0 -0
- package/lib/games/sdvx/command.d.ts +4 -0
- package/lib/games/sdvx/commands/vf.d.ts +3 -0
- package/lib/games/sdvx/config.d.ts +2 -0
- package/lib/games/sdvx/database.d.ts +3 -0
- package/lib/games/sdvx/event.d.ts +3 -0
- package/lib/games/sdvx/index.d.ts +6 -0
- package/lib/games/sdvx/services/drawer-service.d.ts +0 -0
- package/lib/games/sdvx/services/music-service.d.ts +10 -0
- package/lib/games/sdvx/services/score-service.d.ts +18 -0
- package/lib/games/sdvx/types/index.d.ts +82 -0
- package/lib/games/sdvx/utils/vf.d.ts +19 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.js +2389 -0
- package/lib/servers/Asphyxia/index.d.ts +11 -0
- package/lib/servers/Asphyxia/services/iidx-service.d.ts +9 -0
- package/lib/servers/Asphyxia/services/sdvx-service.d.ts +12 -0
- package/lib/servers/Mao/index.d.ts +11 -0
- package/lib/servers/Mao/services/sdvx-service.d.ts +19 -0
- package/lib/servers/ServerFactory.d.ts +5 -0
- package/lib/servers/config.d.ts +0 -0
- package/lib/servers/index.d.ts +13 -0
- package/lib/servers/utils/difficulty.d.ts +14 -0
- package/lib/servers/utils/grade.d.ts +3 -0
- package/lib/servers/utils/xml.d.ts +14 -0
- package/lib/types/config.d.ts +13 -0
- package/lib/types/index.d.ts +66 -0
- package/package.json +47 -0
- package/readme.md +5 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,2389 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name11 in all)
|
|
10
|
+
__defProp(target, name11, { get: all[name11], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
Config: () => Config,
|
|
34
|
+
apply: () => apply10,
|
|
35
|
+
inject: () => inject3,
|
|
36
|
+
name: () => name10
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(src_exports);
|
|
39
|
+
|
|
40
|
+
// src/core/index.ts
|
|
41
|
+
var core_exports = {};
|
|
42
|
+
__export(core_exports, {
|
|
43
|
+
apply: () => apply3,
|
|
44
|
+
inject: () => inject,
|
|
45
|
+
logger: () => logger,
|
|
46
|
+
name: () => name3
|
|
47
|
+
});
|
|
48
|
+
var import_koishi4 = require("koishi");
|
|
49
|
+
|
|
50
|
+
// src/core/locales/en-US.yml
|
|
51
|
+
var en_US_default = { _config: { $desc: "Core Module Settings", adminUsers: "**Plugin Admin** User ID (use inspect command to get)" }, commands: { timeout: "Noah didn't wait for your reply, please try again!", noah: { help: { description: "Show Noah help information", messages: { content: "<p>Help document:</p>\n<a>https://docs.logthm.com/noah</a>" } } }, bind: { description: "Bind card", messages: { prompt: "<p>Please enter your card number:</p>", "invalid-code": "<p>The card number is incorrect, if you don't remember it, go to the arcade to check it~</p>", duplicate: "<p>This card has already been bound (︶^︶)</p>", name: "<p>Received! Please give this card a name, no spaces allowed:</p>", invalid_name: "<p>No spaces allowed! Please try again o(一︿一+)o</p>", success: "<p>Bound successfully, your card information is as follows:</p>\n<p>[{cardName}]</p>\n<p>{cardCode}</p>" } }, "add-server": { description: "Add server" }, card: { description: "Manage cards", messages: { "invalid-code": "<p>The card number is incorrect, if you don't remember it, go to the arcade to check it~</p>", "invalid-select": "<p>No such option!</p>", "menu-select": "<p>[Card Management]</p>\n{card_list}\n<p>0. Add new card</p>\n<p>Please enter the serial number:</p>", menu: "<p>[{name}]</p>\n<p>1. Set as default card</p>\n<p>2. View or modify card number</p>\n<p>3. Delete the card</p>\n<p>4. Rename the card</p>\n<p>5. Bind the card to a specified server</p>\n<p>0. Return to card selection</p>\n<p>Please enter the serial number:</p>", "menu-has-bound-server": "<p>[{name}]</p>\n<p>Bound server: {defaultServerName}</p>\n<p>1. Set as default card</p>\n<p>2. View or modify card number</p>\n<p>3. Delete the card</p>\n<p>4. Rename the card</p>\n<p>5. Bind the card to a specified server</p>\n<p>0. Return to card selection</p>\n<p>Please enter the serial number:</p>", "menu-1-success": "<p>Set successfully!</p>", "menu-1-error-duplicate": "<p>The selected card is the same as the old default card!</p>", "menu-2-prompt": "<p>Card number: {code}</p>\n<p>Please enter the new card number:</p>\n<p>Enter 0 to return</p>", "menu-2-success": "<p>Modified successfully!</p>", "menu-2-error-invalid-code": "<p>The card number is incorrect, if you don't remember it, go to the arcade to check it~</p>", "menu-3-success": "<p>Deleted successfully!</p>", "menu-4-prompt": "<p>Enter a new name, no spaces allowed:</p>", "menu-4-error-invalid-name": "<p>No spaces allowed! Please try again o(一︿一+)o</p>", "menu-4-success": "<p>Modified successfully!</p>", "menu-5": "{server_list}\n<p>Please enter the serial number:</p>", "menu-5-success": "<p>Set successfully!</p>", "menu-5-error-duplicate": "<p>The selected server is the same as the old default server!</p>" } }, server: { description: "Manage servers", messages: { "invalid-select": "<p>No such option!</p>", "no-auth": "<p>Only group admins can use this feature~</p>", "admin-menu-select": "<p>[Group server management]</p>\n{server_list}\n<p>0. Add new server</p>\n<p>Please enter the serial number:</p>", "menu-select": "<p>[Server management]</p>\n{server_list}\n<p>0. Add new server</p>\n<p>Please enter the serial number:</p>", menu: "<p>[{name}]</p>\n<p>Type: {type}</p>\n<p>1. Set as default server</p>\n<p>2. View or modify url</p>\n<p>3. Delete the server</p>\n<p>4. Rename the server</p>\n<p>0. Return to server selection</p>\n<p>Please enter the serial number:</p>", "menu-1-success": "<p>Set successfully!</p>", "menu-1-error-duplicate": "<p>The selected server is the same as the old default server!</p>", "menu-2-prompt": "<p>The server's url: {baseUrl}</p>\n<p>Please enter the new server url:</p>\n<p>Enter 0 to return</p>", "menu-2-success": "<p>Modified successfully!</p>", "menu-3-success": "<p>Deleted successfully!</p>", "menu-4-prompt": "<p>Enter a new name, no spaces allowed:</p>", "menu-4-error-invalid-name": "<p>No spaces allowed! Please try again o(一︿一+)o</p>", "menu-4-success": "<p>Modified successfully!</p>", "add-type": "<p>Please select the server type:</p>\n{server_type_list}", "add-url": "<p>Please enter the server url:</p>", "add-name": "<p>Received! Please give the server a name, no spaces allowed:</p>", "add-invalid_name": "<p>No spaces allowed! Please try again o(一︿一+)o</p>", "add-success": "<p>Added successfully, the server information is as follows:</p>\n<p>[{serverName}]</p>\n<p>Type: {serverType}</p>" } } } };
|
|
52
|
+
|
|
53
|
+
// src/core/locales/zh-CN.yml
|
|
54
|
+
var zh_CN_default = { _config: { $desc: "Core 模块设置", adminUsers: "**插件管理员** 的用户id (使用 inspect 指令获取)" }, commands: { timeout: "Noah 没等到你的回复,请重试!", noah: { help: { description: "显示 Noah 帮助信息", messages: { content: "<p>帮助文档:</p>\n<a>https://docs.logthm.com/noah</a>" } } }, bind: { description: "绑定卡片", messages: { prompt: "<p>请输入你的卡号:</p>", "invalid-code": "<p>卡号不对哟,不记得的话去机台刷一下吧~</p>", duplicate: "<p>这张卡已经被绑定啦 (︶^︶)</p>", name: "<p>收到!为这张卡取一个名字吧,不要带空格哦:</p>", invalid_name: "<p>名字不要带空格!重来重来 o(一︿一+)o</p>", success: "<p>绑好啦,你的卡片信息如下:</p>\n<p>[{cardName}]</p>\n<p>{cardCode}</p>" } }, "add-server": { description: "添加服务器" }, card: { description: "管理卡片", messages: { "invalid-code": "<p>卡号不对哟,不记得的话去机台刷一下吧~</p>", "invalid-select": "<p>没有该选项!</p>", "menu-select": "<p>[卡片管理]</p>\n{card_list}\n<p>0. 添加新卡片</p>\n<p>请输入序号:</p>", menu: "<p>[{name}]</p>\n<p>1. 设为默认卡片</p>\n<p>2. 查看或修改卡号</p>\n<p>3. 删除该卡</p>\n<p>4. 重命名该卡片</p>\n<p>5. 将卡片与指定服务器绑定</p>\n<p>0. 返回卡片选择</p>\n<p>请输入序号:</p>", "menu-has-bound-server": "<p>[{name}]</p>\n<p>已绑定服务器:{defaultServerName}</p>\n<p>1. 设为默认卡片</p>\n<p>2. 查看或修改卡号</p>\n<p>3. 删除该卡</p>\n<p>4. 重命名该卡片</p>\n<p>5. 将卡片与指定服务器绑定</p>\n<p>0. 返回卡片选择</p>\n<p>请输入序号:</p>", "menu-1-success": "<p>设置成功!</p>", "menu-1-error-duplicate": "<p>选择的卡片与旧的默认卡片相同!</p>", "menu-2-prompt": "<p>卡号:{code}</p>\n<p>请输入新的卡号:</p>\n<p>输入 0 返回</p>", "menu-2-success": "<p>修改成功!</p>", "menu-2-error-invalid-code": "<p>卡号不对哟,不记得的话去机台刷一下吧~</p>", "menu-3-success": "<p>已删除!</p>", "menu-4-prompt": "<p>输入一个新名字,不要带空格哦:</p>", "menu-4-error-invalid-name": "<p>名字不要带空格!重来重来 o(一︿一+)o</p>", "menu-4-success": "<p>修改成功!</p>", "menu-5": "{server_list}\n<p>请输入序号:</p>", "menu-5-success": "<p>设置成功!</p>", "menu-5-error-duplicate": "<p>选择的服务器与旧的默认服务器相同!</p>" } }, server: { description: "管理服务器", messages: { "invalid-select": "<p>没有该选项!</p>", "no-auth": "<p>只有群管理员能使用本功能哦~</p>", "admin-menu-select": "<p>[群聊服务器管理]</p>\n{server_list}\n<p>0. 添加新服务器</p>\n<p>请输入序号:</p>", "menu-select": "<p>[服务器管理]</p>\n{server_list}\n<p>0. 添加新服务器</p>\n<p>请输入序号:</p>", menu: "<p>[{name}]</p>\n<p>类型:{type}</p>\n<p>1. 设为默认服务器</p>\n<p>2. 查看或修改 url</p>\n<p>3. 删除该服务器</p>\n<p>4. 重命名该服务器</p>\n<p>0. 返回服务器选择</p>\n<p>请输入序号:</p>", "menu-1-success": "<p>设置成功!</p>", "menu-1-error-duplicate": "<p>选择的服务器与旧的默认服务器相同!</p>", "menu-2-prompt": "<p>该服务器的 url:{baseUrl}</p>\n<p>请输入新的服务器 url:</p>\n<p>输入 0 返回</p>", "menu-2-success": "<p>修改成功!</p>", "menu-3-success": "<p>已删除!</p>", "menu-4-prompt": "<p>输入一个新名字,不要带空格哦:</p>", "menu-4-error-invalid-name": "<p>名字不要带空格!重来重来 o(一︿一+)o</p>", "menu-4-success": "<p>修改成功!</p>", "add-type": "<p>请选择服务器的类型:</p>\n{server_type_list}", "add-url": "<p>请输入服务器的 url:</p>", "add-name": "<p>收到!为服务器取一个名字吧,不要带空格哦:</p>", "add-invalid_name": "<p>名字不要带空格!重来重来 o(一︿一+)o</p>", "add-success": "<p>添加成功啦,服务器信息如下:</p>\n<p>[{serverName}]</p>\n<p>类型:{serverType}</p>" } } } };
|
|
55
|
+
|
|
56
|
+
// src/core/database.ts
|
|
57
|
+
var database_exports = {};
|
|
58
|
+
__export(database_exports, {
|
|
59
|
+
apply: () => apply,
|
|
60
|
+
name: () => name
|
|
61
|
+
});
|
|
62
|
+
var name = "database";
|
|
63
|
+
function apply(ctx) {
|
|
64
|
+
ctx.model.extend("user", {
|
|
65
|
+
defaultCardId: "unsigned",
|
|
66
|
+
defaultServerId: "unsigned"
|
|
67
|
+
});
|
|
68
|
+
ctx.model.extend("channel", {
|
|
69
|
+
defaultServerId: "unsigned"
|
|
70
|
+
});
|
|
71
|
+
ctx.model.extend("card", {
|
|
72
|
+
id: "unsigned",
|
|
73
|
+
code: "string",
|
|
74
|
+
name: "string",
|
|
75
|
+
defaultServerId: "unsigned"
|
|
76
|
+
}, {
|
|
77
|
+
autoInc: true
|
|
78
|
+
});
|
|
79
|
+
ctx.model.extend("server", {
|
|
80
|
+
id: "unsigned",
|
|
81
|
+
type: "string",
|
|
82
|
+
name: "string",
|
|
83
|
+
baseUrl: "string",
|
|
84
|
+
pcbid: "string",
|
|
85
|
+
supportedGames: "list"
|
|
86
|
+
}, {
|
|
87
|
+
autoInc: true
|
|
88
|
+
});
|
|
89
|
+
ctx.model.extend("user_card", {
|
|
90
|
+
id: "unsigned",
|
|
91
|
+
uid: "unsigned",
|
|
92
|
+
cid: "unsigned"
|
|
93
|
+
}, {
|
|
94
|
+
autoInc: true
|
|
95
|
+
});
|
|
96
|
+
ctx.model.extend("user_server", {
|
|
97
|
+
id: "unsigned",
|
|
98
|
+
uid: "unsigned",
|
|
99
|
+
sid: "unsigned"
|
|
100
|
+
}, {
|
|
101
|
+
autoInc: true
|
|
102
|
+
});
|
|
103
|
+
ctx.model.extend("channel_server", {
|
|
104
|
+
id: "unsigned",
|
|
105
|
+
cid: "string",
|
|
106
|
+
sid: "unsigned"
|
|
107
|
+
}, {
|
|
108
|
+
autoInc: true
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
__name(apply, "apply");
|
|
112
|
+
|
|
113
|
+
// src/core/command.ts
|
|
114
|
+
var command_exports = {};
|
|
115
|
+
__export(command_exports, {
|
|
116
|
+
apply: () => apply2,
|
|
117
|
+
name: () => name2
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// src/core/commands/help.ts
|
|
121
|
+
function help(ctx, config) {
|
|
122
|
+
ctx.command("noah.help").alias("帮助").action(({ session }) => {
|
|
123
|
+
return session.text(".content");
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
__name(help, "help");
|
|
127
|
+
|
|
128
|
+
// src/core/utils/card.ts
|
|
129
|
+
var crypto = __toESM(require("crypto"));
|
|
130
|
+
var KEY_STRING = "?I'llB2c.YouXXXeMeHaYpy!";
|
|
131
|
+
var KEY_BUFFER = Buffer.from(KEY_STRING, "ascii");
|
|
132
|
+
var DES3_KEY = Buffer.from(KEY_BUFFER.map((b) => b * 2 & 255));
|
|
133
|
+
var IV = Buffer.alloc(8, 0);
|
|
134
|
+
var ALPHABET = "0123456789ABCDEFGHJKLMNPRSTUWXYZ";
|
|
135
|
+
function encDes(data) {
|
|
136
|
+
if (data.length !== 8) {
|
|
137
|
+
throw new Error("encDes: data must be 8 bytes");
|
|
138
|
+
}
|
|
139
|
+
const cipher = crypto.createCipheriv("des-ede3-cbc", DES3_KEY, IV);
|
|
140
|
+
cipher.setAutoPadding(false);
|
|
141
|
+
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
|
|
142
|
+
return encrypted;
|
|
143
|
+
}
|
|
144
|
+
__name(encDes, "encDes");
|
|
145
|
+
function decDes(data) {
|
|
146
|
+
if (data.length !== 8) {
|
|
147
|
+
throw new Error("decDes: data must be 8 bytes");
|
|
148
|
+
}
|
|
149
|
+
const decipher = crypto.createDecipheriv("des-ede3-cbc", DES3_KEY, IV);
|
|
150
|
+
decipher.setAutoPadding(false);
|
|
151
|
+
const decrypted = Buffer.concat([decipher.update(data), decipher.final()]);
|
|
152
|
+
return decrypted;
|
|
153
|
+
}
|
|
154
|
+
__name(decDes, "decDes");
|
|
155
|
+
function pack5(data) {
|
|
156
|
+
let bits = data.map((n) => n.toString(2).padStart(5, "0")).join("");
|
|
157
|
+
const padLength = (8 - bits.length % 8) % 8;
|
|
158
|
+
bits += "0".repeat(padLength);
|
|
159
|
+
const bytes = [];
|
|
160
|
+
for (let i = 0; i < bits.length; i += 8) {
|
|
161
|
+
const byteStr = bits.slice(i, i + 8);
|
|
162
|
+
bytes.push(parseInt(byteStr, 2));
|
|
163
|
+
}
|
|
164
|
+
return Buffer.from(bytes);
|
|
165
|
+
}
|
|
166
|
+
__name(pack5, "pack5");
|
|
167
|
+
function unpack5(data) {
|
|
168
|
+
let bits = Array.from(data).map((b) => b.toString(2).padStart(8, "0")).join("");
|
|
169
|
+
const padLength = (5 - bits.length % 5) % 5;
|
|
170
|
+
bits += "0".repeat(padLength);
|
|
171
|
+
const numbers = [];
|
|
172
|
+
for (let i = 0; i < bits.length; i += 5) {
|
|
173
|
+
const chunk = bits.slice(i, i + 5);
|
|
174
|
+
numbers.push(parseInt(chunk, 2));
|
|
175
|
+
}
|
|
176
|
+
return numbers;
|
|
177
|
+
}
|
|
178
|
+
__name(unpack5, "unpack5");
|
|
179
|
+
function checksum(data) {
|
|
180
|
+
if (data.length < 15) {
|
|
181
|
+
throw new Error("checksum: data must have at least 15 elements");
|
|
182
|
+
}
|
|
183
|
+
let chk = 0;
|
|
184
|
+
for (let i = 0; i < 15; i++) {
|
|
185
|
+
chk += data[i] * (i % 3 + 1);
|
|
186
|
+
}
|
|
187
|
+
while (chk > 31) {
|
|
188
|
+
chk = (chk >> 5) + (chk & 31);
|
|
189
|
+
}
|
|
190
|
+
return chk;
|
|
191
|
+
}
|
|
192
|
+
__name(checksum, "checksum");
|
|
193
|
+
function toKonamiId(uid) {
|
|
194
|
+
const upperUid = uid.toUpperCase();
|
|
195
|
+
let cardType;
|
|
196
|
+
if (upperUid.startsWith("E004")) {
|
|
197
|
+
cardType = 1;
|
|
198
|
+
} else if (upperUid.startsWith("0")) {
|
|
199
|
+
cardType = 2;
|
|
200
|
+
} else {
|
|
201
|
+
throw new Error("Invalid UID prefix");
|
|
202
|
+
}
|
|
203
|
+
if (uid.length !== 16) {
|
|
204
|
+
throw new Error("UID must be 16 hex digits");
|
|
205
|
+
}
|
|
206
|
+
const kid = Buffer.from(uid, "hex");
|
|
207
|
+
if (kid.length !== 8) {
|
|
208
|
+
throw new Error("Parsed UID must be 8 bytes");
|
|
209
|
+
}
|
|
210
|
+
const reversedKid = Buffer.from(kid).reverse();
|
|
211
|
+
const encrypted = encDes(reversedKid);
|
|
212
|
+
const unpacked = unpack5(encrypted);
|
|
213
|
+
const out = unpacked.slice(0, 13).concat([0, 0, 0]);
|
|
214
|
+
out[0] ^= cardType;
|
|
215
|
+
out[13] = 1;
|
|
216
|
+
for (let i = 1; i < 14; i++) {
|
|
217
|
+
out[i] ^= out[i - 1];
|
|
218
|
+
}
|
|
219
|
+
out[14] = cardType;
|
|
220
|
+
out[15] = checksum(out);
|
|
221
|
+
let konamiId = "";
|
|
222
|
+
for (const n of out) {
|
|
223
|
+
if (n < 0 || n >= ALPHABET.length) {
|
|
224
|
+
throw new Error(`Invalid index ${n} when mapping to alphabet`);
|
|
225
|
+
}
|
|
226
|
+
konamiId += ALPHABET[n];
|
|
227
|
+
}
|
|
228
|
+
return konamiId;
|
|
229
|
+
}
|
|
230
|
+
__name(toKonamiId, "toKonamiId");
|
|
231
|
+
function toUid(konamiId) {
|
|
232
|
+
if (konamiId.length !== 16) {
|
|
233
|
+
throw new Error("KONAMI ID must be 16 characters");
|
|
234
|
+
}
|
|
235
|
+
const cardTypeChar = konamiId[14];
|
|
236
|
+
let cardType;
|
|
237
|
+
if (cardTypeChar === "1") {
|
|
238
|
+
cardType = 1;
|
|
239
|
+
} else if (cardTypeChar === "2") {
|
|
240
|
+
cardType = 2;
|
|
241
|
+
} else {
|
|
242
|
+
throw new Error("Invalid KONAMI ID card type");
|
|
243
|
+
}
|
|
244
|
+
const card2 = [];
|
|
245
|
+
for (const ch of konamiId) {
|
|
246
|
+
const index = ALPHABET.indexOf(ch);
|
|
247
|
+
if (index === -1) {
|
|
248
|
+
throw new Error(`Invalid character in KONAMI ID: ${ch}`);
|
|
249
|
+
}
|
|
250
|
+
card2.push(index);
|
|
251
|
+
}
|
|
252
|
+
if ((card2[11] & 1) !== (card2[12] & 1)) {
|
|
253
|
+
throw new Error("Parity check failed (positions 11 and 12)");
|
|
254
|
+
}
|
|
255
|
+
if (card2[13] !== (card2[12] ^ 1)) {
|
|
256
|
+
throw new Error("Encoding check failed (position 13)");
|
|
257
|
+
}
|
|
258
|
+
if (card2[15] !== checksum(card2)) {
|
|
259
|
+
throw new Error("Checksum failed");
|
|
260
|
+
}
|
|
261
|
+
for (let i = 13; i >= 1; i--) {
|
|
262
|
+
card2[i] ^= card2[i - 1];
|
|
263
|
+
}
|
|
264
|
+
card2[0] ^= cardType;
|
|
265
|
+
const packed = pack5(card2.slice(0, 13));
|
|
266
|
+
const dataForDec = packed.slice(0, 8);
|
|
267
|
+
const decrypted = decDes(dataForDec);
|
|
268
|
+
const uidBuffer = Buffer.from(decrypted).reverse();
|
|
269
|
+
const uidHex = uidBuffer.toString("hex").toUpperCase();
|
|
270
|
+
if (cardType === 1) {
|
|
271
|
+
if (!uidHex.startsWith("E004")) {
|
|
272
|
+
throw new Error("Invalid card type: expected magnetic stripe (E004 prefix)");
|
|
273
|
+
}
|
|
274
|
+
} else if (cardType === 2) {
|
|
275
|
+
if (!uidHex.startsWith("0")) {
|
|
276
|
+
throw new Error("Invalid card type: expected FeliCa (leading 0)");
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return uidHex;
|
|
280
|
+
}
|
|
281
|
+
__name(toUid, "toUid");
|
|
282
|
+
function classifyCardId(input) {
|
|
283
|
+
if (input.length !== 16) {
|
|
284
|
+
return "invalid";
|
|
285
|
+
}
|
|
286
|
+
const uidRegex = /^[0-9A-Fa-f]{16}$/;
|
|
287
|
+
if (uidRegex.test(input)) {
|
|
288
|
+
try {
|
|
289
|
+
toKonamiId(input);
|
|
290
|
+
} catch (e) {
|
|
291
|
+
return "invalid";
|
|
292
|
+
}
|
|
293
|
+
return "uid";
|
|
294
|
+
}
|
|
295
|
+
const ALPHABET2 = "0123456789ABCDEFGHJKLMNPRSTUWXYZ";
|
|
296
|
+
const isKonami = input.split("").every((ch) => ALPHABET2.includes(ch));
|
|
297
|
+
if (isKonami) {
|
|
298
|
+
try {
|
|
299
|
+
toUid(input);
|
|
300
|
+
} catch (e) {
|
|
301
|
+
return "invalid";
|
|
302
|
+
}
|
|
303
|
+
return "konamiid";
|
|
304
|
+
}
|
|
305
|
+
return "invalid";
|
|
306
|
+
}
|
|
307
|
+
__name(classifyCardId, "classifyCardId");
|
|
308
|
+
function processInputCardCode(input) {
|
|
309
|
+
return input.toUpperCase().replace(/[IOQV]/gi, (match) => {
|
|
310
|
+
switch (match.toUpperCase()) {
|
|
311
|
+
case "I":
|
|
312
|
+
return "1";
|
|
313
|
+
case "O":
|
|
314
|
+
return "0";
|
|
315
|
+
case "Q":
|
|
316
|
+
return "0";
|
|
317
|
+
case "V":
|
|
318
|
+
return "U";
|
|
319
|
+
default:
|
|
320
|
+
return match;
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
__name(processInputCardCode, "processInputCardCode");
|
|
325
|
+
|
|
326
|
+
// src/core/services/card-service.ts
|
|
327
|
+
var CardService = class {
|
|
328
|
+
constructor(ctx) {
|
|
329
|
+
this.ctx = ctx;
|
|
330
|
+
}
|
|
331
|
+
static {
|
|
332
|
+
__name(this, "CardService");
|
|
333
|
+
}
|
|
334
|
+
tableName = "card";
|
|
335
|
+
/**
|
|
336
|
+
* 获取所有卡片列表
|
|
337
|
+
*/
|
|
338
|
+
async getAllCards() {
|
|
339
|
+
const cards = await this.ctx.database.get(this.tableName, {});
|
|
340
|
+
return cards.map((card2) => ({
|
|
341
|
+
...card2,
|
|
342
|
+
defaultServerId: card2.defaultServerId ?? 0
|
|
343
|
+
}));
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* 根据 user id 获取卡片列表
|
|
347
|
+
* @param uid 用户的 user id
|
|
348
|
+
* @returns 该用户绑定的卡片的完整信息列表
|
|
349
|
+
*/
|
|
350
|
+
async getCardsByUid(uid) {
|
|
351
|
+
const rows = await this.ctx.database.get("user_card", { uid }, ["cid"]);
|
|
352
|
+
const cards = await this.ctx.database.get(this.tableName, rows.map((obj) => obj.cid));
|
|
353
|
+
return cards.map((card2) => ({
|
|
354
|
+
...card2,
|
|
355
|
+
defaultServerId: card2.defaultServerId ?? 0
|
|
356
|
+
}));
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* 根据 id 获取卡片信息
|
|
360
|
+
*/
|
|
361
|
+
async getCardById(id) {
|
|
362
|
+
const rows = await this.ctx.database.get(this.tableName, id);
|
|
363
|
+
if (!rows[0])
|
|
364
|
+
return null;
|
|
365
|
+
return {
|
|
366
|
+
...rows[0],
|
|
367
|
+
defaultServerId: rows[0].defaultServerId ?? 0
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* 根据 code 获取卡片信息
|
|
372
|
+
*/
|
|
373
|
+
async getCardByCode(code) {
|
|
374
|
+
const rows = await this.ctx.database.get(this.tableName, { code });
|
|
375
|
+
if (!rows[0])
|
|
376
|
+
return null;
|
|
377
|
+
return {
|
|
378
|
+
...rows[0],
|
|
379
|
+
defaultServerId: rows[0].defaultServerId ?? 0
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* 创建一张新的卡
|
|
384
|
+
* @param uid 用户的 uid
|
|
385
|
+
* @param code 卡片的卡号
|
|
386
|
+
* @param name 卡片的名称
|
|
387
|
+
* @param defaultServerId 默认服务器 ID(若没有默认服务器则为 0)
|
|
388
|
+
* @returns 创建成功后包含自动填充 id 等字段的完整对象
|
|
389
|
+
*/
|
|
390
|
+
async createCard(uid, code, name11, defaultServerId = 0) {
|
|
391
|
+
const data = {
|
|
392
|
+
code,
|
|
393
|
+
name: name11,
|
|
394
|
+
defaultServerId
|
|
395
|
+
};
|
|
396
|
+
const res = await this.ctx.database.create(this.tableName, data);
|
|
397
|
+
await this.ctx.database.create("user_card", { uid, cid: res.id });
|
|
398
|
+
return {
|
|
399
|
+
...res,
|
|
400
|
+
defaultServerId: res.defaultServerId ?? 0
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* 更新卡片的信息
|
|
405
|
+
* - 可更新 code、name、defaultServerId
|
|
406
|
+
* @param id 卡片的 id
|
|
407
|
+
* @param partial 更新字段
|
|
408
|
+
*/
|
|
409
|
+
async updateCard(id, partial) {
|
|
410
|
+
if (partial.defaultServerId === void 0) {
|
|
411
|
+
partial.defaultServerId = 0;
|
|
412
|
+
}
|
|
413
|
+
await this.ctx.database.set(this.tableName, id, partial);
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* 删除卡片
|
|
417
|
+
* @param id 卡片 id
|
|
418
|
+
*/
|
|
419
|
+
async removeCard(id) {
|
|
420
|
+
await this.ctx.database.remove(this.tableName, id);
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
// src/core/commands/bind.ts
|
|
425
|
+
function bind(ctx, config) {
|
|
426
|
+
ctx.command("bind [cardCode]").alias("绑卡").userFields(["defaultCardId", "id"]).action(async ({ session }, cardCode) => {
|
|
427
|
+
const cardService = new CardService(ctx);
|
|
428
|
+
if (!cardCode) {
|
|
429
|
+
await session.send(session.text(".prompt"));
|
|
430
|
+
cardCode = await session.prompt();
|
|
431
|
+
if (!cardCode)
|
|
432
|
+
return session.text("commands.timeout");
|
|
433
|
+
}
|
|
434
|
+
cardCode = processInputCardCode(cardCode);
|
|
435
|
+
switch (classifyCardId(cardCode)) {
|
|
436
|
+
case "invalid":
|
|
437
|
+
return session.text(".invalid-code");
|
|
438
|
+
case "konamiid":
|
|
439
|
+
cardCode = toUid(cardCode);
|
|
440
|
+
}
|
|
441
|
+
if (await cardService.getCardByCode(cardCode) != null)
|
|
442
|
+
return session.text(".duplicate");
|
|
443
|
+
await session.send(session.text(".name"));
|
|
444
|
+
const cardName = await session.prompt();
|
|
445
|
+
if (!cardName)
|
|
446
|
+
return session.text("commands.timeout");
|
|
447
|
+
if (cardName.includes(" "))
|
|
448
|
+
return session.text(".invalid-name");
|
|
449
|
+
const res = await cardService.createCard(session.user.id, cardCode, cardName);
|
|
450
|
+
if (session.user.defaultCardId === 0)
|
|
451
|
+
session.user.defaultCardId = res.id;
|
|
452
|
+
return session.text(".success", { cardName, cardCode });
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
__name(bind, "bind");
|
|
456
|
+
|
|
457
|
+
// src/core/commands/card.ts
|
|
458
|
+
var import_koishi = require("koishi");
|
|
459
|
+
|
|
460
|
+
// src/core/services/server-service.ts
|
|
461
|
+
var ServerService = class {
|
|
462
|
+
constructor(ctx) {
|
|
463
|
+
this.ctx = ctx;
|
|
464
|
+
}
|
|
465
|
+
static {
|
|
466
|
+
__name(this, "ServerService");
|
|
467
|
+
}
|
|
468
|
+
tableName = "server";
|
|
469
|
+
/**
|
|
470
|
+
* 获取所有服务器列表
|
|
471
|
+
*/
|
|
472
|
+
async getAllServers() {
|
|
473
|
+
return await this.ctx.database.get(this.tableName, {});
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* 根据 user id 获取服务器列表
|
|
477
|
+
* @param uid 用户的 user id
|
|
478
|
+
* @returns 该用户绑定的服务器的完整信息列表
|
|
479
|
+
*/
|
|
480
|
+
async getServersByUid(uid) {
|
|
481
|
+
const rows = await this.ctx.database.get("user_server", { uid }, ["sid"]);
|
|
482
|
+
return await this.ctx.database.get("server", rows.map((obj) => obj.sid));
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* 根据 channel id 获取服务器列表
|
|
486
|
+
* @param cid 频道的 channel id
|
|
487
|
+
* @returns 该频道绑定的服务器的完整信息列表
|
|
488
|
+
*/
|
|
489
|
+
async getServersByCid(cid) {
|
|
490
|
+
const rows = await this.ctx.database.get("channel_server", { cid }, ["sid"]);
|
|
491
|
+
return await this.ctx.database.get("server", rows.map((obj) => obj.sid));
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* 根据 id 获取服务器信息
|
|
495
|
+
*/
|
|
496
|
+
async getServerById(id) {
|
|
497
|
+
const rows = await this.ctx.database.get(this.tableName, id);
|
|
498
|
+
return rows[0] ?? null;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* 根据名称查找服务器
|
|
502
|
+
*/
|
|
503
|
+
async getServerByName(name11) {
|
|
504
|
+
const rows = await this.ctx.database.get(this.tableName, { name: name11 });
|
|
505
|
+
return rows[0] ?? null;
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* 根据类型筛选服务器
|
|
509
|
+
*/
|
|
510
|
+
async getServersByType(type) {
|
|
511
|
+
const servers = await this.ctx.database.get(this.tableName, { type });
|
|
512
|
+
return servers;
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* 创建一个新的服务器记录并关联到用户
|
|
516
|
+
* @param serverData 要插入的数据 (不含 id)
|
|
517
|
+
* @param uid 用户的 ID
|
|
518
|
+
* @returns 插入后的完整数据
|
|
519
|
+
*/
|
|
520
|
+
async createServerForUser(serverData, uid) {
|
|
521
|
+
const res = await this.ctx.database.create(this.tableName, serverData);
|
|
522
|
+
await this.ctx.database.create("user_server", { uid, sid: res.id });
|
|
523
|
+
return res;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* 创建一个新的服务器记录并关联到频道
|
|
527
|
+
* @param serverData 要插入的数据 (不含 id)
|
|
528
|
+
* @param cid 频道的 ID
|
|
529
|
+
* @returns 插入后的完整数据
|
|
530
|
+
*/
|
|
531
|
+
async createServerForChannel(serverData, cid) {
|
|
532
|
+
const res = await this.ctx.database.create(this.tableName, serverData);
|
|
533
|
+
await this.ctx.database.create("channel_server", { cid, sid: res.id });
|
|
534
|
+
return res;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* 更新服务器记录
|
|
538
|
+
* @param id 服务器 id
|
|
539
|
+
* @param partial 要更新的字段
|
|
540
|
+
*/
|
|
541
|
+
async updateServer(id, partial) {
|
|
542
|
+
await this.ctx.database.set(this.tableName, id, partial);
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* 删除服务器记录
|
|
546
|
+
* @param id 服务器 id
|
|
547
|
+
*/
|
|
548
|
+
async removeServer(id) {
|
|
549
|
+
await this.ctx.database.remove(this.tableName, id);
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
// src/core/commands/card.ts
|
|
554
|
+
function card(ctx, config) {
|
|
555
|
+
ctx.command("card").alias("卡片管理").userFields(["defaultCardId", "defaultServerId", "id"]).channelFields(["defaultServerId", "id"]).action(async ({ session }) => {
|
|
556
|
+
const cardService = new CardService(ctx);
|
|
557
|
+
const serverService = new ServerService(ctx);
|
|
558
|
+
return await showCardSelectMenu(
|
|
559
|
+
ctx,
|
|
560
|
+
session,
|
|
561
|
+
cardService,
|
|
562
|
+
serverService,
|
|
563
|
+
session.user.id,
|
|
564
|
+
session.user.defaultCardId,
|
|
565
|
+
session.user.defaultServerId,
|
|
566
|
+
session.channel.id,
|
|
567
|
+
session.channel.defaultServerId
|
|
568
|
+
);
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
__name(card, "card");
|
|
572
|
+
async function showCardSelectMenu(ctx, session, cardService, serverService, uid, userDefaultCardId, userDefaultServerId, cid, channelDefaultServerId) {
|
|
573
|
+
const res = await cardService.getCardsByUid(uid);
|
|
574
|
+
let cardListMsg = "";
|
|
575
|
+
for (let i = 0; i < res.length; i++) {
|
|
576
|
+
if (res[i].id === userDefaultCardId) {
|
|
577
|
+
cardListMsg += `<p>${i + 1}. ${res[i].name} < 默认卡片</p>`;
|
|
578
|
+
} else {
|
|
579
|
+
cardListMsg += `<p>${i + 1}. ${res[i].name}</p>`;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
const msg = import_koishi.h.unescape(session.text(".menu-select", { card_list: cardListMsg })).replace("< 默认卡片", "< 默认卡片");
|
|
583
|
+
await session.send(msg);
|
|
584
|
+
const select = await session.prompt();
|
|
585
|
+
if (!select)
|
|
586
|
+
return session.text("commands.timeout");
|
|
587
|
+
const selectNum = parseInt(select, 10);
|
|
588
|
+
if (isNaN(selectNum) || selectNum < 0 || selectNum > res.length) {
|
|
589
|
+
return session.text(".invalid-select");
|
|
590
|
+
}
|
|
591
|
+
if (selectNum === 0) {
|
|
592
|
+
return session.execute("bind");
|
|
593
|
+
} else {
|
|
594
|
+
return showCardMenu(ctx, session, res[selectNum - 1], cardService, serverService, uid, userDefaultCardId, userDefaultServerId, cid, channelDefaultServerId);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
__name(showCardSelectMenu, "showCardSelectMenu");
|
|
598
|
+
async function showCardMenu(ctx, session, card2, cardService, serverService, uid, userDefaultCardId, userDefaultServerId, cid, channelDefaultServerId) {
|
|
599
|
+
if (card2.defaultServerId != 0) {
|
|
600
|
+
const server2 = await serverService.getServerById(card2.defaultServerId);
|
|
601
|
+
await session.send(session.text(".menu-has-bound-server", { name: card2.name, defaultServerName: server2.name }));
|
|
602
|
+
} else {
|
|
603
|
+
await session.send(session.text(".menu", card2));
|
|
604
|
+
}
|
|
605
|
+
const select = await session.prompt();
|
|
606
|
+
if (!select)
|
|
607
|
+
return session.text("commands.timeout");
|
|
608
|
+
const selectNum = parseInt(select, 10);
|
|
609
|
+
if (isNaN(selectNum) || selectNum < 0 || selectNum > 5) {
|
|
610
|
+
return await session.text(".invalid-select");
|
|
611
|
+
}
|
|
612
|
+
if (selectNum === 0) {
|
|
613
|
+
return await showCardSelectMenu(ctx, session, cardService, serverService, uid, userDefaultCardId, userDefaultServerId, cid, channelDefaultServerId);
|
|
614
|
+
}
|
|
615
|
+
if (selectNum === 1) {
|
|
616
|
+
if (userDefaultCardId === card2.id)
|
|
617
|
+
return session.text(".menu-1-error-duplicate");
|
|
618
|
+
userDefaultCardId = card2.id;
|
|
619
|
+
return session.text(".menu-1-success");
|
|
620
|
+
}
|
|
621
|
+
if (selectNum === 2) {
|
|
622
|
+
await session.send(session.text(".menu-2-prompt", card2));
|
|
623
|
+
let cardCode = await session.prompt();
|
|
624
|
+
if (!cardCode)
|
|
625
|
+
return session.text("commands.timeout");
|
|
626
|
+
if (cardCode === "0")
|
|
627
|
+
return showCardMenu(ctx, session, card2, cardService, serverService, uid, userDefaultCardId, userDefaultServerId, cid, channelDefaultServerId);
|
|
628
|
+
cardCode = processInputCardCode(cardCode);
|
|
629
|
+
switch (classifyCardId(cardCode)) {
|
|
630
|
+
case "invalid":
|
|
631
|
+
return session.text(".menu-2-error-invalid-code");
|
|
632
|
+
case "konamiid":
|
|
633
|
+
cardCode = toUid(cardCode);
|
|
634
|
+
}
|
|
635
|
+
if (await cardService.getCardByCode(cardCode) != null)
|
|
636
|
+
return session.text(".duplicate");
|
|
637
|
+
await cardService.updateCard(card2.id, { code: cardCode });
|
|
638
|
+
return session.text(".menu-2-success");
|
|
639
|
+
}
|
|
640
|
+
if (selectNum === 3) {
|
|
641
|
+
await cardService.removeCard(card2.id);
|
|
642
|
+
if (userDefaultCardId === card2.id) {
|
|
643
|
+
const res = await cardService.getCardsByUid(uid);
|
|
644
|
+
if (res.length === 0)
|
|
645
|
+
userDefaultCardId = 0;
|
|
646
|
+
else
|
|
647
|
+
userDefaultCardId = res[0].id;
|
|
648
|
+
}
|
|
649
|
+
return session.text(".menu-3-success");
|
|
650
|
+
}
|
|
651
|
+
if (selectNum === 4) {
|
|
652
|
+
await session.send(session.text(".menu-4-prompt"));
|
|
653
|
+
const cardName = await session.prompt();
|
|
654
|
+
if (!cardName)
|
|
655
|
+
return session.text("commands.timeout");
|
|
656
|
+
if (cardName.includes(" "))
|
|
657
|
+
return session.text(".menu-4-error-invalid-name");
|
|
658
|
+
await cardService.updateCard(card2.id, { name: cardName });
|
|
659
|
+
return session.text(".menu-4-success");
|
|
660
|
+
}
|
|
661
|
+
if (selectNum === 5) {
|
|
662
|
+
const userRes = await serverService.getServersByUid(uid);
|
|
663
|
+
const channelRes = await serverService.getServersByCid(cid);
|
|
664
|
+
const res = channelRes.concat(userRes);
|
|
665
|
+
let serverListMsg = "";
|
|
666
|
+
for (let i = 0; i < res.length; i++) {
|
|
667
|
+
if (res[i].id === userDefaultServerId) {
|
|
668
|
+
serverListMsg += `<p>${i + 1}. ${res[i].name} < 你的默认服务器</p>`;
|
|
669
|
+
} else if (res[i].id === channelDefaultServerId) {
|
|
670
|
+
serverListMsg += `<p>${i + 1}. ${res[i].name} < 群组默认服务器</p>`;
|
|
671
|
+
} else {
|
|
672
|
+
serverListMsg += `<p>${i + 1}. ${res[i].name}</p>`;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
const msg = import_koishi.h.unescape(session.text(".menu-5", { server_list: serverListMsg })).replace("< 你的默认服务器", "< 你的默认服务器").replace("< 群组默认服务器", "< 群组默认服务器");
|
|
676
|
+
await session.send(msg);
|
|
677
|
+
const select2 = await session.prompt();
|
|
678
|
+
if (!select2)
|
|
679
|
+
return session.text("commands.timeout");
|
|
680
|
+
const selectNum2 = parseInt(select2, 10);
|
|
681
|
+
if (isNaN(selectNum2) || selectNum2 < 1 || selectNum2 > res.length) {
|
|
682
|
+
return session.text(".invalid-select");
|
|
683
|
+
}
|
|
684
|
+
if (card2.defaultServerId === res[selectNum2 - 1].id)
|
|
685
|
+
return session.text(".menu-5-error-duplicate");
|
|
686
|
+
await cardService.updateCard(card2.id, { defaultServerId: res[selectNum2 - 1].id });
|
|
687
|
+
return session.text(".menu-5-success");
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
__name(showCardMenu, "showCardMenu");
|
|
691
|
+
|
|
692
|
+
// src/core/commands/server.ts
|
|
693
|
+
var import_koishi3 = require("koishi");
|
|
694
|
+
|
|
695
|
+
// src/core/config.ts
|
|
696
|
+
var import_koishi2 = require("koishi");
|
|
697
|
+
var coreConfig = import_koishi2.Schema.object({
|
|
698
|
+
adminUsers: import_koishi2.Schema.union([
|
|
699
|
+
import_koishi2.Schema.array(String),
|
|
700
|
+
import_koishi2.Schema.transform(String, (adminUsers) => [adminUsers])
|
|
701
|
+
])
|
|
702
|
+
}).i18n({
|
|
703
|
+
"en-US": en_US_default._config,
|
|
704
|
+
"zh-CN": zh_CN_default._config
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
// src/core/utils/role.ts
|
|
708
|
+
function hasPermission(...perms) {
|
|
709
|
+
return perms.some((perm) => perm === true);
|
|
710
|
+
}
|
|
711
|
+
__name(hasPermission, "hasPermission");
|
|
712
|
+
function isPluginAdmin(session, config) {
|
|
713
|
+
console.log(config.adminUsers, session.userId);
|
|
714
|
+
return config.adminUsers?.includes(session.userId) ?? false;
|
|
715
|
+
}
|
|
716
|
+
__name(isPluginAdmin, "isPluginAdmin");
|
|
717
|
+
function isGuildAdmin(session) {
|
|
718
|
+
const platform = session.event.platform;
|
|
719
|
+
let guildMember;
|
|
720
|
+
if (platform === "onebot") {
|
|
721
|
+
guildMember = session.event.member.roles[0];
|
|
722
|
+
return guildMember === "owner" || guildMember === "admin" || guildMember === "SUBCHANNEL_ADMIN" || guildMember === "OWNER" || guildMember === "ADMIN";
|
|
723
|
+
} else if (platform === "qq") {
|
|
724
|
+
return true;
|
|
725
|
+
} else {
|
|
726
|
+
return true;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
__name(isGuildAdmin, "isGuildAdmin");
|
|
730
|
+
|
|
731
|
+
// src/types/index.ts
|
|
732
|
+
var serverValues = ["asphyxia", "mao"];
|
|
733
|
+
|
|
734
|
+
// src/core/commands/server.ts
|
|
735
|
+
function server(ctx, config) {
|
|
736
|
+
ctx.command("server").alias("服务器管理").option("channel", "-c").userFields(["defaultServerId", "id"]).channelFields(["defaultServerId", "id"]).action(async ({ session, options }) => {
|
|
737
|
+
const serverService = new ServerService(ctx);
|
|
738
|
+
if (options.channel) {
|
|
739
|
+
if (!hasPermission(isGuildAdmin(session), isPluginAdmin(session, config)))
|
|
740
|
+
return session.text(".no-auth");
|
|
741
|
+
return await showChannelServerSelectMenu(
|
|
742
|
+
ctx,
|
|
743
|
+
session,
|
|
744
|
+
serverService,
|
|
745
|
+
session.user.id,
|
|
746
|
+
session.user.defaultServerId,
|
|
747
|
+
session.channel.id,
|
|
748
|
+
session.channel.defaultServerId
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
return await showUserServerSelectMenu(
|
|
752
|
+
ctx,
|
|
753
|
+
session,
|
|
754
|
+
serverService,
|
|
755
|
+
session.user.id,
|
|
756
|
+
session.user.defaultServerId,
|
|
757
|
+
session.channel.id,
|
|
758
|
+
session.channel.defaultServerId
|
|
759
|
+
);
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
__name(server, "server");
|
|
763
|
+
async function showChannelServerSelectMenu(ctx, session, serverService, uid, userDefaultServerId, cid, channelDefaultServerId) {
|
|
764
|
+
const res = await serverService.getServersByCid(cid);
|
|
765
|
+
let serverListMsg = "";
|
|
766
|
+
for (let i = 0; i < res.length; i++) {
|
|
767
|
+
if (res[i].id === channelDefaultServerId) {
|
|
768
|
+
serverListMsg += `<p>${i + 1}. ${res[i].name} < 群聊默认服务器</p>`;
|
|
769
|
+
} else {
|
|
770
|
+
serverListMsg += `<p>${i + 1}. ${res[i].name}</p>`;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
const msg = import_koishi3.h.unescape(session.text(".menu-select", { server_list: serverListMsg })).replace("< 群聊默认服务器", "< 群聊默认服务器");
|
|
774
|
+
await session.send(msg);
|
|
775
|
+
const select = await session.prompt();
|
|
776
|
+
if (!select)
|
|
777
|
+
return session.text("commands.timeout");
|
|
778
|
+
const selectNum = parseInt(select, 10);
|
|
779
|
+
if (isNaN(selectNum) || selectNum < 0 || selectNum > res.length) {
|
|
780
|
+
return session.text(".invalid-select");
|
|
781
|
+
}
|
|
782
|
+
if (selectNum === 0) {
|
|
783
|
+
return addServer(ctx, session, serverService, "channel", uid, userDefaultServerId, cid, channelDefaultServerId);
|
|
784
|
+
} else {
|
|
785
|
+
return showServerMenu(ctx, session, serverService, res[selectNum - 1], "channel", uid, userDefaultServerId, cid, channelDefaultServerId);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
__name(showChannelServerSelectMenu, "showChannelServerSelectMenu");
|
|
789
|
+
async function showUserServerSelectMenu(ctx, session, serverService, uid, userDefaultServerId, cid, channelDefaultServerId) {
|
|
790
|
+
const res = await serverService.getServersByUid(uid);
|
|
791
|
+
let serverListMsg = "";
|
|
792
|
+
for (let i = 0; i < res.length; i++) {
|
|
793
|
+
if (res[i].id === userDefaultServerId) {
|
|
794
|
+
serverListMsg += `<p>${i + 1}. ${res[i].name} < 默认服务器</p>`;
|
|
795
|
+
} else {
|
|
796
|
+
serverListMsg += `<p>${i + 1}. ${res[i].name}</p>`;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
const msg = import_koishi3.h.unescape(session.text(".menu-select", { server_list: serverListMsg })).replace("< 默认服务器", "< 默认服务器");
|
|
800
|
+
await session.send(msg);
|
|
801
|
+
const select = await session.prompt();
|
|
802
|
+
if (!select)
|
|
803
|
+
return session.text("commands.timeout");
|
|
804
|
+
const selectNum = parseInt(select, 10);
|
|
805
|
+
if (isNaN(selectNum) || selectNum < 0 || selectNum > res.length) {
|
|
806
|
+
return session.text(".invalid-select");
|
|
807
|
+
}
|
|
808
|
+
if (selectNum === 0) {
|
|
809
|
+
return addServer(ctx, session, serverService, "user", uid, userDefaultServerId, cid, channelDefaultServerId);
|
|
810
|
+
} else {
|
|
811
|
+
return showServerMenu(ctx, session, serverService, res[selectNum - 1], "user", uid, userDefaultServerId, cid, channelDefaultServerId);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
__name(showUserServerSelectMenu, "showUserServerSelectMenu");
|
|
815
|
+
async function showServerMenu(ctx, session, serverService, server2, from, uid, userDefaultServerId, cid, channelDefaultServerId) {
|
|
816
|
+
await session.send(session.text(".menu", server2));
|
|
817
|
+
const select = await session.prompt();
|
|
818
|
+
if (!select)
|
|
819
|
+
return session.text("commands.timeout");
|
|
820
|
+
const selectNum = parseInt(select, 10);
|
|
821
|
+
if (isNaN(selectNum) || selectNum < 0 || selectNum > 4) {
|
|
822
|
+
return await session.text(".invalid-select");
|
|
823
|
+
}
|
|
824
|
+
if (selectNum === 0) {
|
|
825
|
+
if (from === "user")
|
|
826
|
+
return await showUserServerSelectMenu(ctx, session, serverService, uid, userDefaultServerId, cid, channelDefaultServerId);
|
|
827
|
+
else
|
|
828
|
+
return await showChannelServerSelectMenu(ctx, session, serverService, uid, userDefaultServerId, cid, channelDefaultServerId);
|
|
829
|
+
}
|
|
830
|
+
if (selectNum === 1) {
|
|
831
|
+
if (from === "user") {
|
|
832
|
+
if (userDefaultServerId === server2.id)
|
|
833
|
+
return session.text(".menu-1-error-duplicate");
|
|
834
|
+
userDefaultServerId = server2.id;
|
|
835
|
+
} else {
|
|
836
|
+
if (channelDefaultServerId === server2.id)
|
|
837
|
+
return session.text(".menu-1-error-duplicate");
|
|
838
|
+
channelDefaultServerId = server2.id;
|
|
839
|
+
}
|
|
840
|
+
return session.text(".menu-1-success");
|
|
841
|
+
}
|
|
842
|
+
if (selectNum === 2) {
|
|
843
|
+
await session.send(session.text(".menu-2-prompt", server2));
|
|
844
|
+
let url = await session.prompt();
|
|
845
|
+
if (!url)
|
|
846
|
+
return session.text("commands.timeout");
|
|
847
|
+
if (url === "0")
|
|
848
|
+
return showServerMenu(ctx, session, serverService, server2, from, uid, userDefaultServerId, cid, channelDefaultServerId);
|
|
849
|
+
await serverService.updateServer(server2.id, { baseUrl: url });
|
|
850
|
+
return session.text(".menu-2-success");
|
|
851
|
+
}
|
|
852
|
+
if (selectNum === 3) {
|
|
853
|
+
await serverService.removeServer(server2.id);
|
|
854
|
+
if (from === "user") {
|
|
855
|
+
if (userDefaultServerId === server2.id) {
|
|
856
|
+
const res = await serverService.getServersByUid(uid);
|
|
857
|
+
if (res.length === 0)
|
|
858
|
+
userDefaultServerId = 0;
|
|
859
|
+
else
|
|
860
|
+
userDefaultServerId = res[0].id;
|
|
861
|
+
}
|
|
862
|
+
} else {
|
|
863
|
+
if (channelDefaultServerId === server2.id) {
|
|
864
|
+
const res = await serverService.getServersByCid(cid);
|
|
865
|
+
if (res.length === 0)
|
|
866
|
+
channelDefaultServerId = 0;
|
|
867
|
+
else
|
|
868
|
+
channelDefaultServerId = res[0].id;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
return session.text(".menu-3-success");
|
|
872
|
+
}
|
|
873
|
+
if (selectNum === 4) {
|
|
874
|
+
await session.send(session.text(".menu-4-prompt"));
|
|
875
|
+
const serverName = await session.prompt();
|
|
876
|
+
if (!serverName)
|
|
877
|
+
return session.text("commands.timeout");
|
|
878
|
+
if (serverName.includes(" "))
|
|
879
|
+
return session.text(".menu-4-error-invalid-name");
|
|
880
|
+
await serverService.updateServer(server2.id, { name: serverName });
|
|
881
|
+
return session.text(".menu-4-success");
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
__name(showServerMenu, "showServerMenu");
|
|
885
|
+
async function addServer(ctx, session, serverService, from, uid, userDefaultServerId, cid, channelDefaultServerId) {
|
|
886
|
+
let serverTypeListMsg = "";
|
|
887
|
+
for (let i = 0; i < serverValues.length; i++) {
|
|
888
|
+
serverTypeListMsg += `<p>${i + 1}. ${serverValues[i]}</p>`;
|
|
889
|
+
}
|
|
890
|
+
const msg = import_koishi3.h.unescape(session.text(".add-type", { server_type_list: serverTypeListMsg }));
|
|
891
|
+
await session.send(msg);
|
|
892
|
+
const select = await session.prompt();
|
|
893
|
+
if (!select)
|
|
894
|
+
return session.text("commands.timeout");
|
|
895
|
+
const selectNum = parseInt(select, 10);
|
|
896
|
+
if (isNaN(selectNum) || selectNum < 1 || selectNum > serverValues.length) {
|
|
897
|
+
return await session.text(".invalid-select");
|
|
898
|
+
}
|
|
899
|
+
const serverType = serverValues[selectNum - 1];
|
|
900
|
+
await session.send(session.text(".add-url"));
|
|
901
|
+
const url = await session.prompt();
|
|
902
|
+
if (!url)
|
|
903
|
+
return session.text("commands.timeout");
|
|
904
|
+
await session.send(session.text(".add-name"));
|
|
905
|
+
const serverName = await session.prompt();
|
|
906
|
+
if (!serverName)
|
|
907
|
+
return session.text("commands.timeout");
|
|
908
|
+
if (serverName.includes(" "))
|
|
909
|
+
return session.text(".add-invalid-name");
|
|
910
|
+
const res = from === "user" ? await serverService.createServerForUser({
|
|
911
|
+
type: serverType,
|
|
912
|
+
name: serverName,
|
|
913
|
+
baseUrl: url,
|
|
914
|
+
supportedGames: []
|
|
915
|
+
}, uid) : await serverService.createServerForChannel({
|
|
916
|
+
type: serverType,
|
|
917
|
+
name: serverName,
|
|
918
|
+
baseUrl: url,
|
|
919
|
+
supportedGames: []
|
|
920
|
+
}, cid);
|
|
921
|
+
if (from === "user") {
|
|
922
|
+
if (userDefaultServerId === 0) {
|
|
923
|
+
userDefaultServerId = res.id;
|
|
924
|
+
}
|
|
925
|
+
} else {
|
|
926
|
+
if (channelDefaultServerId === 0) {
|
|
927
|
+
channelDefaultServerId = res.id;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
return session.text(".add-success", { serverName, serverType });
|
|
931
|
+
}
|
|
932
|
+
__name(addServer, "addServer");
|
|
933
|
+
|
|
934
|
+
// src/core/command.ts
|
|
935
|
+
var name2 = "command";
|
|
936
|
+
function apply2(ctx, config) {
|
|
937
|
+
help(ctx, config);
|
|
938
|
+
bind(ctx, config);
|
|
939
|
+
card(ctx, config);
|
|
940
|
+
server(ctx, config);
|
|
941
|
+
}
|
|
942
|
+
__name(apply2, "apply");
|
|
943
|
+
|
|
944
|
+
// src/core/index.ts
|
|
945
|
+
var name3 = "Noah-Core";
|
|
946
|
+
var inject = ["database"];
|
|
947
|
+
var logger = new import_koishi4.Logger("Noah-Core");
|
|
948
|
+
function apply3(ctx, config) {
|
|
949
|
+
[["en-US", en_US_default], ["zh-CN", zh_CN_default]].forEach(([lang, file]) => ctx.i18n.define(lang, file));
|
|
950
|
+
ctx.plugin(database_exports, config.core);
|
|
951
|
+
ctx.plugin(command_exports, config.core);
|
|
952
|
+
}
|
|
953
|
+
__name(apply3, "apply");
|
|
954
|
+
|
|
955
|
+
// src/games/sdvx/index.ts
|
|
956
|
+
var sdvx_exports = {};
|
|
957
|
+
__export(sdvx_exports, {
|
|
958
|
+
apply: () => apply9,
|
|
959
|
+
inject: () => inject2,
|
|
960
|
+
logger: () => logger2,
|
|
961
|
+
name: () => name9
|
|
962
|
+
});
|
|
963
|
+
var import_koishi9 = require("koishi");
|
|
964
|
+
|
|
965
|
+
// src/games/sdvx/locales/en-US.yml
|
|
966
|
+
var en_US_default2 = { _config: { $desc: "SDVX Module Settings", default_model: "<p>Default model value (e.g. `2024110700`)</p>" }, commands: { vf: { description: "Show Noah help information", messages: { "card-not-found": "<p>You haven't bound a card yet, go bind a card first~</p>", "server-not-found": "<p>No available servers, add one yourself~</p>", "no-scores-found": "<p>No scores found, please check your data.</p>", error: "<p>An error occurred:</p>\n<p>{message}</p>", drawing: "<p>Noah is drawing, please wait patiently~</p>" } } } };
|
|
967
|
+
|
|
968
|
+
// src/games/sdvx/locales/zh-CN.yml
|
|
969
|
+
var zh_CN_default2 = { _config: { $desc: "SDVX 模块设置", default_model: "<p>默认的 model 值(如 `2024110700`)</p>" }, commands: { vf: { description: "显示 Noah 帮助信息", messages: { "card-not-found": "<p>你还没绑卡,去绑个卡再来吧~</p>", "server-not-found": "<p>没有可用的服务器哦,自己添加一个吧~</p>", "no-scores-found": "<p>没有找到你的分数数据哦~</p>", error: "<p>发生了错误:</p>\n<p>{message}</p>", drawing: "<p>Noah 正在画啦,请耐心等待~</p>" } } } };
|
|
970
|
+
|
|
971
|
+
// src/games/sdvx/database.ts
|
|
972
|
+
var database_exports2 = {};
|
|
973
|
+
__export(database_exports2, {
|
|
974
|
+
apply: () => apply4,
|
|
975
|
+
name: () => name4
|
|
976
|
+
});
|
|
977
|
+
var name4 = "database";
|
|
978
|
+
function apply4(ctx) {
|
|
979
|
+
}
|
|
980
|
+
__name(apply4, "apply");
|
|
981
|
+
|
|
982
|
+
// src/games/sdvx/command.ts
|
|
983
|
+
var command_exports2 = {};
|
|
984
|
+
__export(command_exports2, {
|
|
985
|
+
apply: () => apply7,
|
|
986
|
+
name: () => name7
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
// src/games/sdvx/commands/vf.ts
|
|
990
|
+
var import_koishi8 = require("koishi");
|
|
991
|
+
|
|
992
|
+
// src/servers/index.ts
|
|
993
|
+
var servers_exports = {};
|
|
994
|
+
__export(servers_exports, {
|
|
995
|
+
ServerManager: () => ServerManager,
|
|
996
|
+
apply: () => apply5,
|
|
997
|
+
name: () => name5
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
// src/servers/Asphyxia/index.ts
|
|
1001
|
+
var import_koishi5 = require("koishi");
|
|
1002
|
+
|
|
1003
|
+
// src/servers/utils/xml.ts
|
|
1004
|
+
var xml2js = __toESM(require("xml2js"));
|
|
1005
|
+
var xmlToJson = /* @__PURE__ */ __name((xml) => {
|
|
1006
|
+
return new Promise((resolve2, reject) => {
|
|
1007
|
+
xml2js.parseString(xml, (err, result) => {
|
|
1008
|
+
if (err) {
|
|
1009
|
+
reject(new Error("Error parsing XML: " + err));
|
|
1010
|
+
} else {
|
|
1011
|
+
resolve2(result);
|
|
1012
|
+
}
|
|
1013
|
+
});
|
|
1014
|
+
});
|
|
1015
|
+
}, "xmlToJson");
|
|
1016
|
+
|
|
1017
|
+
// src/servers/Asphyxia/services/sdvx-service.ts
|
|
1018
|
+
var SDVXService = class _SDVXService {
|
|
1019
|
+
static {
|
|
1020
|
+
__name(this, "SDVXService");
|
|
1021
|
+
}
|
|
1022
|
+
static instance;
|
|
1023
|
+
logger;
|
|
1024
|
+
constructor(logger3) {
|
|
1025
|
+
this.logger = logger3;
|
|
1026
|
+
}
|
|
1027
|
+
static getInstance(logger3) {
|
|
1028
|
+
if (!_SDVXService.instance) {
|
|
1029
|
+
_SDVXService.instance = new _SDVXService(logger3);
|
|
1030
|
+
}
|
|
1031
|
+
return _SDVXService.instance;
|
|
1032
|
+
}
|
|
1033
|
+
async fetchRefid(ctx, url, model, card2) {
|
|
1034
|
+
const requestUrl = `?model=KFC:J:G:A:${model}&f=message.get`;
|
|
1035
|
+
const xmlRequestBody = `
|
|
1036
|
+
<call model="KFC:J:G:A:${model}" srcid="00010203040506070809">
|
|
1037
|
+
<cardmng cardid="${card2}" cardtype="2" method="inquire" update="0" />
|
|
1038
|
+
</call>
|
|
1039
|
+
`;
|
|
1040
|
+
const response = await ctx.http.post(requestUrl, xmlRequestBody, { baseURL: url });
|
|
1041
|
+
const decodedResponse = new TextDecoder("utf-8").decode(response);
|
|
1042
|
+
const jsonResponse = await xmlToJson(decodedResponse);
|
|
1043
|
+
return jsonResponse.response.cardmng[0].$.refid;
|
|
1044
|
+
}
|
|
1045
|
+
async fetchProfile(ctx, url, model, card2) {
|
|
1046
|
+
const refid = await this.fetchRefid(ctx, url, model, card2);
|
|
1047
|
+
const requestUrl = `?model=KFC:J:G:A:${model}&f=message.get`;
|
|
1048
|
+
const xmlRequestBody = `
|
|
1049
|
+
<call model="KFC:J:G:A:${model}" srcid="00010203040506070809">
|
|
1050
|
+
<game method="sv6_load" ver="0">
|
|
1051
|
+
<dataid __type="str">${refid}</dataid>
|
|
1052
|
+
<cardid __type="str">${card2}</cardid>
|
|
1053
|
+
<refid __type="str">${refid}</refid>
|
|
1054
|
+
<locid __type="str">ea</locid>
|
|
1055
|
+
</game>
|
|
1056
|
+
</call>
|
|
1057
|
+
`;
|
|
1058
|
+
const response = await ctx.http.post(requestUrl, xmlRequestBody, { baseURL: url });
|
|
1059
|
+
const decodedResponse = new TextDecoder("utf-8").decode(response);
|
|
1060
|
+
const jsonResponse = await xmlToJson(decodedResponse);
|
|
1061
|
+
return jsonResponse.response;
|
|
1062
|
+
}
|
|
1063
|
+
async fetchScore(ctx, url, model, card2) {
|
|
1064
|
+
const refid = await this.fetchRefid(ctx, url, model, card2);
|
|
1065
|
+
const requestUrl = `?model=KFC:J:G:A:${model}&f=message.get`;
|
|
1066
|
+
const xmlRequestBody = `
|
|
1067
|
+
<call model="KFC:J:G:A:${model}" srcid="00010203040506070809">
|
|
1068
|
+
<game method="sv6_load_m" ver="0">
|
|
1069
|
+
<dataid __type="str">${refid}</dataid>
|
|
1070
|
+
<cardid __type="str">${card2}</cardid>
|
|
1071
|
+
<refid __type="str">${refid}</refid>
|
|
1072
|
+
<locid __type="str">ea</locid>
|
|
1073
|
+
</game>
|
|
1074
|
+
</call>
|
|
1075
|
+
`;
|
|
1076
|
+
const response = await ctx.http.post(requestUrl, xmlRequestBody, { baseURL: url });
|
|
1077
|
+
const decodedResponse = new TextDecoder("utf-8").decode(response);
|
|
1078
|
+
const jsonResponse = await xmlToJson(decodedResponse);
|
|
1079
|
+
const rawScoreData = jsonResponse.response.game[0].music[0].info;
|
|
1080
|
+
return rawScoreData.map((item) => {
|
|
1081
|
+
const params = item.param[0]._.split(" ");
|
|
1082
|
+
return {
|
|
1083
|
+
music: {
|
|
1084
|
+
music_id: params[0],
|
|
1085
|
+
// music_id
|
|
1086
|
+
music_type: params[1],
|
|
1087
|
+
// music_type
|
|
1088
|
+
score: parseInt(params[2]),
|
|
1089
|
+
// score
|
|
1090
|
+
exscore: parseInt(params[3]),
|
|
1091
|
+
// exscore
|
|
1092
|
+
clear_type: params[4],
|
|
1093
|
+
// clear_type
|
|
1094
|
+
score_grade: params[5],
|
|
1095
|
+
// score_grade
|
|
1096
|
+
btn_rate: params[8],
|
|
1097
|
+
// btn_rate
|
|
1098
|
+
long_rate: params[9],
|
|
1099
|
+
// long_rate
|
|
1100
|
+
vol_rate: params[10]
|
|
1101
|
+
// vol_rate
|
|
1102
|
+
}
|
|
1103
|
+
};
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
};
|
|
1107
|
+
|
|
1108
|
+
// src/servers/Asphyxia/services/iidx-service.ts
|
|
1109
|
+
var IIDXService = class _IIDXService {
|
|
1110
|
+
static {
|
|
1111
|
+
__name(this, "IIDXService");
|
|
1112
|
+
}
|
|
1113
|
+
static instance;
|
|
1114
|
+
logger;
|
|
1115
|
+
constructor(logger3) {
|
|
1116
|
+
this.logger = logger3;
|
|
1117
|
+
}
|
|
1118
|
+
static getInstance(logger3) {
|
|
1119
|
+
if (!_IIDXService.instance) {
|
|
1120
|
+
_IIDXService.instance = new _IIDXService(logger3);
|
|
1121
|
+
}
|
|
1122
|
+
return _IIDXService.instance;
|
|
1123
|
+
}
|
|
1124
|
+
async fetchData(url) {
|
|
1125
|
+
}
|
|
1126
|
+
};
|
|
1127
|
+
|
|
1128
|
+
// src/servers/Asphyxia/index.ts
|
|
1129
|
+
var Asphyxia = class {
|
|
1130
|
+
static {
|
|
1131
|
+
__name(this, "Asphyxia");
|
|
1132
|
+
}
|
|
1133
|
+
name = "asphyxia";
|
|
1134
|
+
supportedGames = ["sdvx", "iidx"];
|
|
1135
|
+
gameServices = {};
|
|
1136
|
+
logger = new import_koishi5.Logger("Noah-Asphyxia");
|
|
1137
|
+
constructor() {
|
|
1138
|
+
this.gameServices["sdvx"] = SDVXService.getInstance(this.logger);
|
|
1139
|
+
this.gameServices["iidx"] = IIDXService.getInstance(this.logger);
|
|
1140
|
+
}
|
|
1141
|
+
};
|
|
1142
|
+
|
|
1143
|
+
// src/servers/Mao/index.ts
|
|
1144
|
+
var import_koishi6 = require("koishi");
|
|
1145
|
+
|
|
1146
|
+
// src/servers/utils/grade.ts
|
|
1147
|
+
function getSDVXGrade(score) {
|
|
1148
|
+
if (score > 99e5)
|
|
1149
|
+
return "S";
|
|
1150
|
+
if (score >= 98e5)
|
|
1151
|
+
return "AAA+";
|
|
1152
|
+
if (score >= 97e5)
|
|
1153
|
+
return "AAA";
|
|
1154
|
+
if (score >= 95e5)
|
|
1155
|
+
return "AA+";
|
|
1156
|
+
if (score >= 93e5)
|
|
1157
|
+
return "AA";
|
|
1158
|
+
if (score >= 9e6)
|
|
1159
|
+
return "A+";
|
|
1160
|
+
if (score >= 87e5)
|
|
1161
|
+
return "A";
|
|
1162
|
+
if (score >= 75e5)
|
|
1163
|
+
return "B";
|
|
1164
|
+
if (score >= 65e5)
|
|
1165
|
+
return "C";
|
|
1166
|
+
return "D";
|
|
1167
|
+
}
|
|
1168
|
+
__name(getSDVXGrade, "getSDVXGrade");
|
|
1169
|
+
function getSDVXClearType(clearType) {
|
|
1170
|
+
return clearType === 5 ? "PUC" : clearType === 4 ? "UC" : clearType === 3 ? "HC" : clearType === 2 ? "NC" : clearType === 1 ? "PLAYED" : "NO PLAY";
|
|
1171
|
+
}
|
|
1172
|
+
__name(getSDVXClearType, "getSDVXClearType");
|
|
1173
|
+
|
|
1174
|
+
// src/servers/utils/difficulty.ts
|
|
1175
|
+
function getDiffName(diffStr, infVer) {
|
|
1176
|
+
switch (diffStr) {
|
|
1177
|
+
case "novice":
|
|
1178
|
+
return "NOV";
|
|
1179
|
+
case "advanced":
|
|
1180
|
+
return "ADV";
|
|
1181
|
+
case "exhaust":
|
|
1182
|
+
return "EXH";
|
|
1183
|
+
case "infinite": {
|
|
1184
|
+
const infVerStr = String(infVer);
|
|
1185
|
+
switch (infVerStr) {
|
|
1186
|
+
case "2":
|
|
1187
|
+
return "INF";
|
|
1188
|
+
case "3":
|
|
1189
|
+
return "GRV";
|
|
1190
|
+
case "4":
|
|
1191
|
+
return "HVN";
|
|
1192
|
+
case "5":
|
|
1193
|
+
return "VVD";
|
|
1194
|
+
case "6":
|
|
1195
|
+
return "XCD";
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
case "maximum":
|
|
1199
|
+
return "MXM";
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
__name(getDiffName, "getDiffName");
|
|
1203
|
+
function getDiffFullName(diffStr, infVer) {
|
|
1204
|
+
switch (diffStr) {
|
|
1205
|
+
case "novice":
|
|
1206
|
+
return "NOVICE";
|
|
1207
|
+
case "advanced":
|
|
1208
|
+
return "ADVANCED";
|
|
1209
|
+
case "exhaust":
|
|
1210
|
+
return "EXHAUST";
|
|
1211
|
+
case "infinite": {
|
|
1212
|
+
const infVerStr = String(infVer);
|
|
1213
|
+
switch (infVerStr) {
|
|
1214
|
+
case "2":
|
|
1215
|
+
return "INFINITE";
|
|
1216
|
+
case "3":
|
|
1217
|
+
return "GRAVITY";
|
|
1218
|
+
case "4":
|
|
1219
|
+
return "HEAVENLY";
|
|
1220
|
+
case "5":
|
|
1221
|
+
return "VIVID";
|
|
1222
|
+
case "6":
|
|
1223
|
+
return "EXCEED";
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
case "maximum":
|
|
1227
|
+
return "MAXIMUM";
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
__name(getDiffFullName, "getDiffFullName");
|
|
1231
|
+
|
|
1232
|
+
// src/games/sdvx/utils/vf.ts
|
|
1233
|
+
var GRADE_FACTORS = {
|
|
1234
|
+
"S": 1.05,
|
|
1235
|
+
"AAA+": 1.02,
|
|
1236
|
+
"AAA": 1,
|
|
1237
|
+
"AA+": 0.97,
|
|
1238
|
+
"AA": 0.94,
|
|
1239
|
+
"A+": 0.91,
|
|
1240
|
+
"A": 0.88,
|
|
1241
|
+
"B": 0.85,
|
|
1242
|
+
"C": 0.82,
|
|
1243
|
+
"D": 0.8
|
|
1244
|
+
};
|
|
1245
|
+
var CLEAR_FACTORS = {
|
|
1246
|
+
"S-PUC": 1.1,
|
|
1247
|
+
"PUC": 1.1,
|
|
1248
|
+
"UC": 1.05,
|
|
1249
|
+
"HC": 1.02,
|
|
1250
|
+
"NC": 1,
|
|
1251
|
+
"PLAYED": 0.5,
|
|
1252
|
+
"NO PLAY": 0.5
|
|
1253
|
+
};
|
|
1254
|
+
function calculateVolforce(raw_score) {
|
|
1255
|
+
const score = raw_score.music.score;
|
|
1256
|
+
const level = raw_score.music.music_diff;
|
|
1257
|
+
const grade = raw_score.music.score_grade;
|
|
1258
|
+
const clearType = raw_score.music.clear_type;
|
|
1259
|
+
const gradeFactor = GRADE_FACTORS[grade];
|
|
1260
|
+
const clearFactor = CLEAR_FACTORS[clearType];
|
|
1261
|
+
const volforce = level * score * gradeFactor * clearFactor * 2e-6;
|
|
1262
|
+
return Math.round(volforce) / 20;
|
|
1263
|
+
}
|
|
1264
|
+
__name(calculateVolforce, "calculateVolforce");
|
|
1265
|
+
|
|
1266
|
+
// src/games/sdvx/services/music-service.ts
|
|
1267
|
+
var MusicService = class _MusicService {
|
|
1268
|
+
static {
|
|
1269
|
+
__name(this, "MusicService");
|
|
1270
|
+
}
|
|
1271
|
+
static instance;
|
|
1272
|
+
sdvx_data_url;
|
|
1273
|
+
constructor(config) {
|
|
1274
|
+
this.sdvx_data_url = config.sdvx_data_url;
|
|
1275
|
+
}
|
|
1276
|
+
static getInstance(config) {
|
|
1277
|
+
if (!_MusicService.instance) {
|
|
1278
|
+
_MusicService.instance = new _MusicService(config);
|
|
1279
|
+
}
|
|
1280
|
+
return _MusicService.instance;
|
|
1281
|
+
}
|
|
1282
|
+
async getMusic(ctx, musicIds) {
|
|
1283
|
+
try {
|
|
1284
|
+
const response = await ctx.http.post(`/music`, {
|
|
1285
|
+
ids: musicIds
|
|
1286
|
+
}, {
|
|
1287
|
+
baseURL: this.sdvx_data_url
|
|
1288
|
+
});
|
|
1289
|
+
const musicInfo = await response;
|
|
1290
|
+
return musicInfo;
|
|
1291
|
+
} catch (error) {
|
|
1292
|
+
throw error;
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
};
|
|
1296
|
+
|
|
1297
|
+
// src/servers/Mao/services/sdvx-service.ts
|
|
1298
|
+
var SDVXService2 = class _SDVXService {
|
|
1299
|
+
static {
|
|
1300
|
+
__name(this, "SDVXService");
|
|
1301
|
+
}
|
|
1302
|
+
static instance;
|
|
1303
|
+
logger;
|
|
1304
|
+
constructor(logger3) {
|
|
1305
|
+
this.logger = logger3;
|
|
1306
|
+
}
|
|
1307
|
+
static getInstance(logger3) {
|
|
1308
|
+
if (!_SDVXService.instance) {
|
|
1309
|
+
_SDVXService.instance = new _SDVXService(logger3);
|
|
1310
|
+
}
|
|
1311
|
+
return _SDVXService.instance;
|
|
1312
|
+
}
|
|
1313
|
+
/**
|
|
1314
|
+
* Get the difficulty string based on the difficulty type number
|
|
1315
|
+
* @param diffType Difficulty type (0-4)
|
|
1316
|
+
* @returns The difficulty string (novice, advanced, exhaust, infinite, maximum)
|
|
1317
|
+
*/
|
|
1318
|
+
getDifficultyString(diffType) {
|
|
1319
|
+
const diffStrMap = {
|
|
1320
|
+
0: "novice",
|
|
1321
|
+
1: "advanced",
|
|
1322
|
+
2: "exhaust",
|
|
1323
|
+
3: "infinite",
|
|
1324
|
+
4: "maximum"
|
|
1325
|
+
};
|
|
1326
|
+
return diffStrMap[diffType] || "novice";
|
|
1327
|
+
}
|
|
1328
|
+
async getAllScore(ctx, url, cardId, config) {
|
|
1329
|
+
try {
|
|
1330
|
+
const data = await ctx.http.get(`/sdvx/scores?card=${cardId}`, { baseURL: url, responseType: "json" });
|
|
1331
|
+
const filteredData = data.filter((score) => score.music_id !== 244 && score.music_id !== 1759 && score.music_id !== 1761);
|
|
1332
|
+
const musicIds = filteredData.map((score) => score.music_id);
|
|
1333
|
+
const diffTypes = filteredData.map((score) => parseInt(score.music_diff));
|
|
1334
|
+
const musicService = MusicService.getInstance(config);
|
|
1335
|
+
const musicData = await musicService.getMusic(ctx, musicIds);
|
|
1336
|
+
const musicDataMap = new Map(musicData.map((music) => [music.id, music]));
|
|
1337
|
+
const scores = filteredData.map((score, index) => {
|
|
1338
|
+
const musicId = score.music_id;
|
|
1339
|
+
const music = musicDataMap.get(musicId);
|
|
1340
|
+
const musicDiffType = parseInt(score.music_diff);
|
|
1341
|
+
const diffStr = this.getDifficultyString(musicDiffType);
|
|
1342
|
+
let difficultyData = null;
|
|
1343
|
+
if (music && music.difficulty && music.difficulty.length > 0) {
|
|
1344
|
+
difficultyData = music.difficulty.find(
|
|
1345
|
+
(diff) => diff.difstr.toLowerCase() === diffStr
|
|
1346
|
+
) || null;
|
|
1347
|
+
}
|
|
1348
|
+
const infVer = music ? music.inf_ver : 2;
|
|
1349
|
+
const scoreObj = {
|
|
1350
|
+
music: {
|
|
1351
|
+
music_id: musicId,
|
|
1352
|
+
music_diff: difficultyData ? difficultyData.difnum : 0,
|
|
1353
|
+
music_diff_name: getDiffName(diffStr, infVer),
|
|
1354
|
+
music_diff_full_name: getDiffFullName(diffStr, infVer),
|
|
1355
|
+
score: score.score,
|
|
1356
|
+
exscore: score.exscore,
|
|
1357
|
+
clear_type: getSDVXClearType(score.clear_type),
|
|
1358
|
+
score_grade: getSDVXGrade(score.score),
|
|
1359
|
+
btn_rate: score.btn_rate.toString(),
|
|
1360
|
+
long_rate: score.long_rate.toString(),
|
|
1361
|
+
vol_rate: score.vol_rate.toString()
|
|
1362
|
+
},
|
|
1363
|
+
extra: {
|
|
1364
|
+
play_count: score.play_count,
|
|
1365
|
+
update_at: score.update_at,
|
|
1366
|
+
volforce: 0
|
|
1367
|
+
},
|
|
1368
|
+
music_data: music || null,
|
|
1369
|
+
difficulty_data: difficultyData || null
|
|
1370
|
+
};
|
|
1371
|
+
scoreObj.extra.volforce = calculateVolforce(scoreObj);
|
|
1372
|
+
return scoreObj;
|
|
1373
|
+
});
|
|
1374
|
+
return scores;
|
|
1375
|
+
} catch (error) {
|
|
1376
|
+
throw error;
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
async getScore(ctx, url, cardId, musicId, config) {
|
|
1380
|
+
try {
|
|
1381
|
+
const response = await ctx.http.get(`/sdvx/find?card=${cardId}&mid=${musicId}`, { baseURL: url, responseType: "text" });
|
|
1382
|
+
const text = await response;
|
|
1383
|
+
if (text === "没有找到相关记录") {
|
|
1384
|
+
const musicService2 = MusicService.getInstance(config);
|
|
1385
|
+
const musicData2 = await musicService2.getMusic(ctx, [musicId]);
|
|
1386
|
+
const music2 = musicData2.length > 0 ? musicData2[0] : null;
|
|
1387
|
+
const diffStr2 = "novice";
|
|
1388
|
+
const infVer2 = music2 ? music2.inf_ver : 2;
|
|
1389
|
+
let difficultyData2 = null;
|
|
1390
|
+
if (music2 && music2.difficulty && music2.difficulty.length > 0) {
|
|
1391
|
+
difficultyData2 = music2.difficulty.find(
|
|
1392
|
+
(diff) => diff.difstr.toLowerCase() === diffStr2
|
|
1393
|
+
) || music2.difficulty[0];
|
|
1394
|
+
}
|
|
1395
|
+
return {
|
|
1396
|
+
music: {
|
|
1397
|
+
music_id: musicId,
|
|
1398
|
+
music_diff: difficultyData2 ? difficultyData2.difnum : 0,
|
|
1399
|
+
music_diff_name: getDiffName(diffStr2, infVer2),
|
|
1400
|
+
music_diff_full_name: getDiffFullName(diffStr2, infVer2),
|
|
1401
|
+
score: 0,
|
|
1402
|
+
exscore: 0,
|
|
1403
|
+
clear_type: getSDVXClearType(0),
|
|
1404
|
+
// NO PLAY
|
|
1405
|
+
score_grade: getSDVXGrade(0),
|
|
1406
|
+
btn_rate: "0",
|
|
1407
|
+
long_rate: "0",
|
|
1408
|
+
vol_rate: "0"
|
|
1409
|
+
},
|
|
1410
|
+
extra: {
|
|
1411
|
+
play_count: 0,
|
|
1412
|
+
update_at: "0",
|
|
1413
|
+
volforce: 0
|
|
1414
|
+
},
|
|
1415
|
+
music_data: music2,
|
|
1416
|
+
difficulty_data: difficultyData2
|
|
1417
|
+
};
|
|
1418
|
+
}
|
|
1419
|
+
const matches = text.match(/MusicType: (\d+), Score: (\d+), Exscore: (\d+), ClearType: (\d+), PlayCount: (\d+)/);
|
|
1420
|
+
if (!matches) {
|
|
1421
|
+
throw new Error(`Invalid score format received: ${text}`);
|
|
1422
|
+
}
|
|
1423
|
+
const [, musicType, score, exscore, clearType, playCount] = matches;
|
|
1424
|
+
const musicTypeNum = parseInt(musicType);
|
|
1425
|
+
const diffStr = this.getDifficultyString(musicTypeNum);
|
|
1426
|
+
const musicService = MusicService.getInstance(config);
|
|
1427
|
+
const musicData = await musicService.getMusic(ctx, [musicId]);
|
|
1428
|
+
const music = musicData.length > 0 ? musicData[0] : null;
|
|
1429
|
+
let difficultyData = null;
|
|
1430
|
+
if (music && music.difficulty && music.difficulty.length > 0) {
|
|
1431
|
+
difficultyData = music.difficulty.find(
|
|
1432
|
+
(diff) => diff.difstr.toLowerCase() === diffStr
|
|
1433
|
+
) || null;
|
|
1434
|
+
}
|
|
1435
|
+
const infVer = music ? music.inf_ver : 2;
|
|
1436
|
+
const scoreObj = {
|
|
1437
|
+
music: {
|
|
1438
|
+
music_id: musicId,
|
|
1439
|
+
music_diff: difficultyData ? difficultyData.difnum : 0,
|
|
1440
|
+
music_diff_name: getDiffName(diffStr, infVer),
|
|
1441
|
+
music_diff_full_name: getDiffFullName(diffStr, infVer),
|
|
1442
|
+
score: parseInt(score),
|
|
1443
|
+
exscore: parseInt(exscore),
|
|
1444
|
+
clear_type: getSDVXClearType(parseInt(clearType)),
|
|
1445
|
+
score_grade: getSDVXGrade(parseInt(score)),
|
|
1446
|
+
btn_rate: "0",
|
|
1447
|
+
// Not provided in response
|
|
1448
|
+
long_rate: "0",
|
|
1449
|
+
// Not provided in response
|
|
1450
|
+
vol_rate: "0"
|
|
1451
|
+
// Not provided in response
|
|
1452
|
+
},
|
|
1453
|
+
extra: {
|
|
1454
|
+
play_count: parseInt(playCount),
|
|
1455
|
+
update_at: "0",
|
|
1456
|
+
volforce: 0
|
|
1457
|
+
},
|
|
1458
|
+
music_data: music,
|
|
1459
|
+
difficulty_data: difficultyData
|
|
1460
|
+
};
|
|
1461
|
+
scoreObj.extra.volforce = calculateVolforce(scoreObj);
|
|
1462
|
+
return scoreObj;
|
|
1463
|
+
} catch (error) {
|
|
1464
|
+
throw error;
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
async getRecentScores(ctx, url, cardId, count = 1, config) {
|
|
1468
|
+
try {
|
|
1469
|
+
const text = await ctx.http.get(`/sdvx/recent?card=${cardId}&count=${count}`, { baseURL: url, responseType: "text" });
|
|
1470
|
+
const scores = [];
|
|
1471
|
+
const musicIds = [];
|
|
1472
|
+
const tempScores = [];
|
|
1473
|
+
const lines = text.split("\n");
|
|
1474
|
+
for (const line of lines) {
|
|
1475
|
+
if (line.startsWith("mid:")) {
|
|
1476
|
+
const match = line.match(/mid:(\d+)\s+mtype:(\d+)\s+ctype:(\d+)\s+score:(\d+)\s+exscore(\d+)\s+time:(.+)/);
|
|
1477
|
+
if (match) {
|
|
1478
|
+
const [, musicId, musicType, clearType, score, exscore, time] = match;
|
|
1479
|
+
const parsedMusicId = parseInt(musicId);
|
|
1480
|
+
tempScores.push({
|
|
1481
|
+
musicId: parsedMusicId,
|
|
1482
|
+
musicType: parseInt(musicType),
|
|
1483
|
+
clearType: parseInt(clearType),
|
|
1484
|
+
score: parseInt(score),
|
|
1485
|
+
exscore: parseInt(exscore),
|
|
1486
|
+
time
|
|
1487
|
+
});
|
|
1488
|
+
musicIds.push(parsedMusicId);
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
if (tempScores.length === 0) {
|
|
1493
|
+
return [];
|
|
1494
|
+
}
|
|
1495
|
+
const musicService = MusicService.getInstance(config);
|
|
1496
|
+
const musicDataList = await musicService.getMusic(ctx, musicIds);
|
|
1497
|
+
const musicDataMap = new Map(musicDataList.map((music) => [music.id, music]));
|
|
1498
|
+
tempScores.forEach((tempScore) => {
|
|
1499
|
+
const scoreNum = tempScore.score;
|
|
1500
|
+
const musicId = tempScore.musicId;
|
|
1501
|
+
const musicTypeNum = tempScore.musicType;
|
|
1502
|
+
const music = musicDataMap.get(musicId);
|
|
1503
|
+
const diffStr = this.getDifficultyString(musicTypeNum);
|
|
1504
|
+
let difficultyData = null;
|
|
1505
|
+
if (music && music.difficulty && music.difficulty.length > 0) {
|
|
1506
|
+
difficultyData = music.difficulty.find(
|
|
1507
|
+
(diff) => diff.difstr.toLowerCase() === diffStr
|
|
1508
|
+
) || null;
|
|
1509
|
+
}
|
|
1510
|
+
const infVer = music ? music.inf_ver : 2;
|
|
1511
|
+
const scoreObj = {
|
|
1512
|
+
music: {
|
|
1513
|
+
music_id: musicId,
|
|
1514
|
+
music_diff: difficultyData ? difficultyData.difnum : 0,
|
|
1515
|
+
music_diff_name: getDiffName(diffStr, infVer),
|
|
1516
|
+
music_diff_full_name: getDiffFullName(diffStr, infVer),
|
|
1517
|
+
score: scoreNum,
|
|
1518
|
+
exscore: tempScore.exscore,
|
|
1519
|
+
clear_type: getSDVXClearType(tempScore.clearType),
|
|
1520
|
+
score_grade: getSDVXGrade(scoreNum),
|
|
1521
|
+
btn_rate: "0",
|
|
1522
|
+
// Not provided in API response
|
|
1523
|
+
long_rate: "0",
|
|
1524
|
+
// Not provided in API response
|
|
1525
|
+
vol_rate: "0"
|
|
1526
|
+
// Not provided in API response
|
|
1527
|
+
},
|
|
1528
|
+
extra: {
|
|
1529
|
+
play_count: 0,
|
|
1530
|
+
update_at: tempScore.time,
|
|
1531
|
+
volforce: 0
|
|
1532
|
+
},
|
|
1533
|
+
music_data: music || null,
|
|
1534
|
+
difficulty_data: difficultyData || null
|
|
1535
|
+
};
|
|
1536
|
+
scoreObj.extra.volforce = calculateVolforce(scoreObj);
|
|
1537
|
+
scores.push(scoreObj);
|
|
1538
|
+
});
|
|
1539
|
+
return scores;
|
|
1540
|
+
} catch (error) {
|
|
1541
|
+
throw error;
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
};
|
|
1545
|
+
|
|
1546
|
+
// src/servers/Mao/index.ts
|
|
1547
|
+
var Mao = class {
|
|
1548
|
+
static {
|
|
1549
|
+
__name(this, "Mao");
|
|
1550
|
+
}
|
|
1551
|
+
name = "mao";
|
|
1552
|
+
supportedGames = ["sdvx"];
|
|
1553
|
+
gameServices = {};
|
|
1554
|
+
logger = new import_koishi6.Logger("Noah-Mao");
|
|
1555
|
+
constructor() {
|
|
1556
|
+
this.gameServices["sdvx"] = SDVXService2.getInstance(this.logger);
|
|
1557
|
+
}
|
|
1558
|
+
};
|
|
1559
|
+
|
|
1560
|
+
// src/servers/ServerFactory.ts
|
|
1561
|
+
var ServerFactory = class {
|
|
1562
|
+
static {
|
|
1563
|
+
__name(this, "ServerFactory");
|
|
1564
|
+
}
|
|
1565
|
+
serverInstances = /* @__PURE__ */ new Map();
|
|
1566
|
+
getServer(serverType) {
|
|
1567
|
+
if (!this.serverInstances.has(serverType)) {
|
|
1568
|
+
let serverInstance;
|
|
1569
|
+
switch (serverType) {
|
|
1570
|
+
case "asphyxia":
|
|
1571
|
+
serverInstance = new Asphyxia();
|
|
1572
|
+
break;
|
|
1573
|
+
case "mao":
|
|
1574
|
+
serverInstance = new Mao();
|
|
1575
|
+
break;
|
|
1576
|
+
default:
|
|
1577
|
+
throw new Error(`Unsupported server type: ${serverType}`);
|
|
1578
|
+
}
|
|
1579
|
+
this.serverInstances.set(serverType, serverInstance);
|
|
1580
|
+
}
|
|
1581
|
+
return this.serverInstances.get(serverType);
|
|
1582
|
+
}
|
|
1583
|
+
};
|
|
1584
|
+
|
|
1585
|
+
// src/servers/index.ts
|
|
1586
|
+
var name5 = "Noah-Server";
|
|
1587
|
+
var ServerManager = class _ServerManager {
|
|
1588
|
+
static {
|
|
1589
|
+
__name(this, "ServerManager");
|
|
1590
|
+
}
|
|
1591
|
+
static instance;
|
|
1592
|
+
factory;
|
|
1593
|
+
constructor() {
|
|
1594
|
+
this.factory = new ServerFactory();
|
|
1595
|
+
}
|
|
1596
|
+
static getInstance() {
|
|
1597
|
+
if (!_ServerManager.instance) {
|
|
1598
|
+
_ServerManager.instance = new _ServerManager();
|
|
1599
|
+
}
|
|
1600
|
+
return _ServerManager.instance;
|
|
1601
|
+
}
|
|
1602
|
+
getServer(serverType) {
|
|
1603
|
+
return this.factory.getServer(serverType);
|
|
1604
|
+
}
|
|
1605
|
+
getGameService(serverType, gameType) {
|
|
1606
|
+
const server2 = this.factory.getServer(serverType);
|
|
1607
|
+
return server2.gameServices[gameType];
|
|
1608
|
+
}
|
|
1609
|
+
};
|
|
1610
|
+
function apply5(ctx, config) {
|
|
1611
|
+
}
|
|
1612
|
+
__name(apply5, "apply");
|
|
1613
|
+
|
|
1614
|
+
// src/games/sdvx/services/score-service.ts
|
|
1615
|
+
var ScoreService = class _ScoreService {
|
|
1616
|
+
static {
|
|
1617
|
+
__name(this, "ScoreService");
|
|
1618
|
+
}
|
|
1619
|
+
static instance;
|
|
1620
|
+
constructor() {
|
|
1621
|
+
}
|
|
1622
|
+
static getInstance() {
|
|
1623
|
+
if (!_ScoreService.instance) {
|
|
1624
|
+
_ScoreService.instance = new _ScoreService();
|
|
1625
|
+
}
|
|
1626
|
+
return _ScoreService.instance;
|
|
1627
|
+
}
|
|
1628
|
+
/**
|
|
1629
|
+
* Sort scores by volforce in descending order
|
|
1630
|
+
* @param scores Array of SDVX scores to sort
|
|
1631
|
+
* @returns New array of scores sorted by volforce (highest to lowest)
|
|
1632
|
+
*/
|
|
1633
|
+
sortByVolforce(scores) {
|
|
1634
|
+
return [...scores].sort((a, b) => b.extra.volforce - a.extra.volforce);
|
|
1635
|
+
}
|
|
1636
|
+
/**
|
|
1637
|
+
* Get the top 50 scores by volforce, including ties
|
|
1638
|
+
* @param scores Array of SDVX scores to process
|
|
1639
|
+
* @returns Array of scores with highest volforce values (may exceed 50 if there are ties)
|
|
1640
|
+
*/
|
|
1641
|
+
getBest50(scores) {
|
|
1642
|
+
const sortedScores = this.sortByVolforce(scores);
|
|
1643
|
+
if (sortedScores.length <= 50) {
|
|
1644
|
+
return sortedScores;
|
|
1645
|
+
}
|
|
1646
|
+
const fiftiethVF = sortedScores[49].extra.volforce;
|
|
1647
|
+
return sortedScores.filter((score) => score.extra.volforce >= fiftiethVF);
|
|
1648
|
+
}
|
|
1649
|
+
};
|
|
1650
|
+
|
|
1651
|
+
// src/drawer/index.ts
|
|
1652
|
+
var drawer_exports = {};
|
|
1653
|
+
__export(drawer_exports, {
|
|
1654
|
+
DrawerManager: () => DrawerManager,
|
|
1655
|
+
apply: () => apply6,
|
|
1656
|
+
name: () => name6
|
|
1657
|
+
});
|
|
1658
|
+
var import_koishi7 = require("koishi");
|
|
1659
|
+
|
|
1660
|
+
// src/drawer/BaseDrawer.ts
|
|
1661
|
+
var import_sharp = __toESM(require("sharp"));
|
|
1662
|
+
var BaseDrawer = class {
|
|
1663
|
+
constructor(ctx) {
|
|
1664
|
+
this.ctx = ctx;
|
|
1665
|
+
}
|
|
1666
|
+
static {
|
|
1667
|
+
__name(this, "BaseDrawer");
|
|
1668
|
+
}
|
|
1669
|
+
/**
|
|
1670
|
+
* Compress an image buffer
|
|
1671
|
+
* @param canvas The canvas object to compress
|
|
1672
|
+
* @param compression Compression options
|
|
1673
|
+
* @returns Promise resolving to a compressed buffer
|
|
1674
|
+
*/
|
|
1675
|
+
async compressImage(canvas, compression) {
|
|
1676
|
+
if (!compression) {
|
|
1677
|
+
compression = { lossless: false };
|
|
1678
|
+
}
|
|
1679
|
+
try {
|
|
1680
|
+
if (compression.lossless) {
|
|
1681
|
+
const pngBuffer = await canvas.toBuffer("png");
|
|
1682
|
+
return (0, import_sharp.default)(pngBuffer).png({
|
|
1683
|
+
compressionLevel: 9,
|
|
1684
|
+
// Maximum compression
|
|
1685
|
+
adaptiveFiltering: true,
|
|
1686
|
+
progressive: true
|
|
1687
|
+
}).toBuffer();
|
|
1688
|
+
} else {
|
|
1689
|
+
const quality = compression?.quality || 0.9;
|
|
1690
|
+
return canvas.toBuffer("jpeg", { quality });
|
|
1691
|
+
}
|
|
1692
|
+
} catch (error) {
|
|
1693
|
+
this.ctx.logger("BaseDrawer").error(`Error compressing image: ${error.message}`);
|
|
1694
|
+
return canvas.toBuffer("png");
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
};
|
|
1698
|
+
|
|
1699
|
+
// src/drawer/Core/index.ts
|
|
1700
|
+
var CoreDrawer = class extends BaseDrawer {
|
|
1701
|
+
constructor(ctx) {
|
|
1702
|
+
super(ctx);
|
|
1703
|
+
this.ctx = ctx;
|
|
1704
|
+
}
|
|
1705
|
+
static {
|
|
1706
|
+
__name(this, "CoreDrawer");
|
|
1707
|
+
}
|
|
1708
|
+
/**
|
|
1709
|
+
* Generate a generic image for core functionality
|
|
1710
|
+
* @param options Configuration options for image generation
|
|
1711
|
+
* @param compression Compression options
|
|
1712
|
+
* @returns Promise resolving to a Buffer containing the image data
|
|
1713
|
+
*/
|
|
1714
|
+
async generateImage(options, compression) {
|
|
1715
|
+
const { Canvas, loadImage } = this.ctx.skia;
|
|
1716
|
+
const width = options?.width || 800;
|
|
1717
|
+
const height = options?.height || 600;
|
|
1718
|
+
const text = options?.text || "Noah Core";
|
|
1719
|
+
const backgroundColor = options?.backgroundColor || "#2b2b2b";
|
|
1720
|
+
const textColor = options?.textColor || "#ffffff";
|
|
1721
|
+
const canvas = new Canvas(width, height);
|
|
1722
|
+
const ctx = canvas.getContext("2d");
|
|
1723
|
+
ctx.fillStyle = backgroundColor;
|
|
1724
|
+
ctx.fillRect(0, 0, width, height);
|
|
1725
|
+
ctx.fillStyle = textColor;
|
|
1726
|
+
ctx.font = "bold 48px Arial";
|
|
1727
|
+
ctx.textAlign = "center";
|
|
1728
|
+
ctx.textBaseline = "middle";
|
|
1729
|
+
ctx.fillText(text, width / 2, height / 2);
|
|
1730
|
+
return this.compressImage(canvas, compression);
|
|
1731
|
+
}
|
|
1732
|
+
};
|
|
1733
|
+
|
|
1734
|
+
// src/drawer/SDVX/index.ts
|
|
1735
|
+
var import_path = require("path");
|
|
1736
|
+
var fs = __toESM(require("fs"));
|
|
1737
|
+
var SDVXDrawer = class extends BaseDrawer {
|
|
1738
|
+
constructor(ctx) {
|
|
1739
|
+
super(ctx);
|
|
1740
|
+
this.ctx = ctx;
|
|
1741
|
+
}
|
|
1742
|
+
static {
|
|
1743
|
+
__name(this, "SDVXDrawer");
|
|
1744
|
+
}
|
|
1745
|
+
/**
|
|
1746
|
+
* Generate a Volforce image for SDVX scores
|
|
1747
|
+
* @param options Configuration options for image generation
|
|
1748
|
+
* @param compression Compression options
|
|
1749
|
+
* @returns Promise resolving to a Buffer containing the image data
|
|
1750
|
+
*/
|
|
1751
|
+
async generateVFImage(options, compression) {
|
|
1752
|
+
const { Canvas, loadImage, FontLibrary } = this.ctx.skia;
|
|
1753
|
+
if (!compression) {
|
|
1754
|
+
compression = { lossless: true };
|
|
1755
|
+
}
|
|
1756
|
+
try {
|
|
1757
|
+
const notoSansPath = (0, import_path.resolve)(__dirname, "../../assets/fonts/NotoSans-VariableFont_wdth,wght.ttf");
|
|
1758
|
+
const slantPath = (0, import_path.resolve)(__dirname, "../../assets/fonts/Slant.ttf");
|
|
1759
|
+
const fredokaPath = (0, import_path.resolve)(__dirname, "../../assets/fonts/FredokaOne.ttf");
|
|
1760
|
+
const notoSansJPPath = (0, import_path.resolve)(__dirname, "../../assets/fonts/NotoSansJP-VariableFont_wght.ttf");
|
|
1761
|
+
if (fs.existsSync(notoSansPath)) {
|
|
1762
|
+
FontLibrary.use("Noto Sans", notoSansPath);
|
|
1763
|
+
this.ctx.logger("SDVX-Drawer").debug("Loaded Noto Sans font successfully");
|
|
1764
|
+
} else {
|
|
1765
|
+
this.ctx.logger("SDVX-Drawer").warn(`Noto Sans font file not found at: ${notoSansPath}`);
|
|
1766
|
+
}
|
|
1767
|
+
if (fs.existsSync(slantPath)) {
|
|
1768
|
+
FontLibrary.use("Slant", slantPath);
|
|
1769
|
+
this.ctx.logger("SDVX-Drawer").debug("Loaded Slant font successfully");
|
|
1770
|
+
} else {
|
|
1771
|
+
this.ctx.logger("SDVX-Drawer").warn(`Slant font file not found at: ${slantPath}`);
|
|
1772
|
+
}
|
|
1773
|
+
if (fs.existsSync(fredokaPath)) {
|
|
1774
|
+
FontLibrary.use("Fredoka One", fredokaPath);
|
|
1775
|
+
this.ctx.logger("SDVX-Drawer").debug("Loaded Fredoka One font successfully");
|
|
1776
|
+
} else {
|
|
1777
|
+
this.ctx.logger("SDVX-Drawer").warn(`Fredoka One font file not found at: ${fredokaPath}`);
|
|
1778
|
+
}
|
|
1779
|
+
if (fs.existsSync(notoSansJPPath)) {
|
|
1780
|
+
FontLibrary.use("Noto Sans JP", notoSansJPPath);
|
|
1781
|
+
this.ctx.logger("SDVX-Drawer").debug("Loaded Noto Sans JP font successfully");
|
|
1782
|
+
} else {
|
|
1783
|
+
this.ctx.logger("SDVX-Drawer").warn(`Noto Sans JP font file not found at: ${notoSansJPPath}`);
|
|
1784
|
+
}
|
|
1785
|
+
} catch (error) {
|
|
1786
|
+
this.ctx.logger("SDVX-Drawer").warn(`Error loading fonts: ${error.message}`);
|
|
1787
|
+
}
|
|
1788
|
+
const bgImage = await loadImage((0, import_path.resolve)(__dirname, "../../assets/sdvx/vf/main_bg.png"));
|
|
1789
|
+
const canvas = new Canvas(bgImage.width, bgImage.height);
|
|
1790
|
+
const ctx = canvas.getContext("2d");
|
|
1791
|
+
ctx.imageSmoothingEnabled = true;
|
|
1792
|
+
ctx.imageSmoothingQuality = "high";
|
|
1793
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1794
|
+
ctx.drawImage(bgImage, 0, 0);
|
|
1795
|
+
const averageVF = this.calculateAverageVF(options.scores);
|
|
1796
|
+
ctx.fillStyle = "#FFFFFF";
|
|
1797
|
+
ctx.textAlign = "left";
|
|
1798
|
+
ctx.textBaseline = "top";
|
|
1799
|
+
ctx.font = '300 128px "Noto Sans"';
|
|
1800
|
+
ctx.fillText(options.playerName, 1451, 187);
|
|
1801
|
+
ctx.fillText(averageVF.toFixed(3), 3849, 187);
|
|
1802
|
+
const cardBgImage = await loadImage((0, import_path.resolve)(__dirname, "../../assets/sdvx/vf/card_bg.png"));
|
|
1803
|
+
const sortedScores = [...options.scores].sort((a, b) => b.extra.volforce - a.extra.volforce).slice(0, 50);
|
|
1804
|
+
await this.drawScoreCards(ctx, sortedScores, cardBgImage, loadImage, options.config);
|
|
1805
|
+
return this.compressImage(canvas, compression);
|
|
1806
|
+
}
|
|
1807
|
+
/**
|
|
1808
|
+
* Draw score cards on the canvas
|
|
1809
|
+
*/
|
|
1810
|
+
async drawScoreCards(ctx, scores, cardBgImage, loadImage, config) {
|
|
1811
|
+
if (scores.length === 0)
|
|
1812
|
+
return;
|
|
1813
|
+
const startX = 144;
|
|
1814
|
+
const startY = 692;
|
|
1815
|
+
const cardsPerRow = 5;
|
|
1816
|
+
const cardHorizontalSpacing = 48;
|
|
1817
|
+
const cardVerticalSpacing = 100;
|
|
1818
|
+
const cardWidth = cardBgImage.width;
|
|
1819
|
+
const cardHeight = cardBgImage.height;
|
|
1820
|
+
const effectiveCardWidth = cardWidth + cardHorizontalSpacing;
|
|
1821
|
+
const gradeImages = {};
|
|
1822
|
+
const vfBadges = {};
|
|
1823
|
+
const circleImages = {};
|
|
1824
|
+
const jacketImages = {};
|
|
1825
|
+
try {
|
|
1826
|
+
const grades = ["D", "C", "B", "A", "A+", "AA", "AA+", "AAA", "AAA+", "S"];
|
|
1827
|
+
for (const grade of grades) {
|
|
1828
|
+
try {
|
|
1829
|
+
const gradePath = (0, import_path.resolve)(__dirname, `../../assets/sdvx/vf/grade/Type=${grade}.png`);
|
|
1830
|
+
gradeImages[grade] = await loadImage(gradePath);
|
|
1831
|
+
} catch (error) {
|
|
1832
|
+
this.ctx.logger("SDVX-Drawer").warn(`Failed to load grade image for ${grade}: ${error.message}`);
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
for (let i = 1; i <= 10; i++) {
|
|
1836
|
+
try {
|
|
1837
|
+
const circlePath = (0, import_path.resolve)(__dirname, `../../assets/sdvx/vf/circle/Class=${i}.png`);
|
|
1838
|
+
circleImages[i] = await loadImage(circlePath);
|
|
1839
|
+
} catch (error) {
|
|
1840
|
+
this.ctx.logger("SDVX-Drawer").warn(`Failed to load circle image for class ${i}: ${error.message}`);
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
for (let i = 1; i <= 10; i++) {
|
|
1844
|
+
try {
|
|
1845
|
+
const badgePath = (0, import_path.resolve)(__dirname, `../../assets/sdvx/vf/badge/Class=${i}.png`);
|
|
1846
|
+
vfBadges[i] = await loadImage(badgePath);
|
|
1847
|
+
} catch (error) {
|
|
1848
|
+
this.ctx.logger("SDVX-Drawer").warn(`Failed to load badge image for class ${i}: ${error.message}`);
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
for (const score of scores) {
|
|
1852
|
+
try {
|
|
1853
|
+
if (score.difficulty_data?.cover_url) {
|
|
1854
|
+
const coverUrl = `${config.sdvx_data_url}${score.difficulty_data.cover_url}`.replace(".webp", ".png");
|
|
1855
|
+
if (!jacketImages[coverUrl]) {
|
|
1856
|
+
jacketImages[coverUrl] = await loadImage(coverUrl);
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
} catch (error) {
|
|
1860
|
+
this.ctx.logger("SDVX-Drawer").warn(`Failed to load jacket image: ${error.message}`);
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
} catch (error) {
|
|
1864
|
+
this.ctx.logger("SDVX-Drawer").warn(`Failed to load images: ${error.message}`);
|
|
1865
|
+
}
|
|
1866
|
+
for (let i = 0; i < scores.length; i++) {
|
|
1867
|
+
const score = scores[i];
|
|
1868
|
+
const row = Math.floor(i / cardsPerRow);
|
|
1869
|
+
const col = i % cardsPerRow;
|
|
1870
|
+
const x = startX + col * effectiveCardWidth;
|
|
1871
|
+
const y = startY + row * (cardHeight + cardVerticalSpacing);
|
|
1872
|
+
const vf2 = score.extra.volforce;
|
|
1873
|
+
let vfClass = 1;
|
|
1874
|
+
if (vf2 >= 20)
|
|
1875
|
+
vfClass = 10;
|
|
1876
|
+
else if (vf2 >= 19)
|
|
1877
|
+
vfClass = 9;
|
|
1878
|
+
else if (vf2 >= 18)
|
|
1879
|
+
vfClass = 8;
|
|
1880
|
+
else if (vf2 >= 17)
|
|
1881
|
+
vfClass = 7;
|
|
1882
|
+
else if (vf2 >= 16)
|
|
1883
|
+
vfClass = 6;
|
|
1884
|
+
else if (vf2 >= 15)
|
|
1885
|
+
vfClass = 5;
|
|
1886
|
+
else if (vf2 >= 14)
|
|
1887
|
+
vfClass = 4;
|
|
1888
|
+
else if (vf2 >= 12)
|
|
1889
|
+
vfClass = 3;
|
|
1890
|
+
else if (vf2 >= 10)
|
|
1891
|
+
vfClass = 2;
|
|
1892
|
+
else
|
|
1893
|
+
vfClass = 1;
|
|
1894
|
+
if (circleImages[vfClass]) {
|
|
1895
|
+
ctx.drawImage(circleImages[vfClass], x + 628, y - 42);
|
|
1896
|
+
}
|
|
1897
|
+
ctx.drawImage(cardBgImage, x, y);
|
|
1898
|
+
try {
|
|
1899
|
+
const diffName = score.music.music_diff_name || this.getDifficultyAbbreviation(score.music.music_diff);
|
|
1900
|
+
const diffLevel = score.music.music_diff.toString();
|
|
1901
|
+
const diffColors = {
|
|
1902
|
+
"NOV": "#914FC3",
|
|
1903
|
+
// Novice - Purple
|
|
1904
|
+
"ADV": "#BAB616",
|
|
1905
|
+
// Advanced - Yellow
|
|
1906
|
+
"EXH": "#BC3535",
|
|
1907
|
+
// Exhaust - Red
|
|
1908
|
+
"INF": "#CE3378",
|
|
1909
|
+
// Infinite - Pink
|
|
1910
|
+
"MXM": "#7C7C7C",
|
|
1911
|
+
// Maximum - Gray
|
|
1912
|
+
"GRV": "#BE5906",
|
|
1913
|
+
// Gravity - Orange
|
|
1914
|
+
"HVN": "#04A2D1",
|
|
1915
|
+
// Heaven - Light Blue
|
|
1916
|
+
"VVD": "#CF55B0",
|
|
1917
|
+
// Vivid - Magenta
|
|
1918
|
+
"XCD": "#4265B1"
|
|
1919
|
+
// Exceed - Blue
|
|
1920
|
+
};
|
|
1921
|
+
const bgColor = diffColors[diffName] || "#7C7C7C";
|
|
1922
|
+
ctx.font = '24px "Fredoka One"';
|
|
1923
|
+
const diffNameWidth = ctx.measureText(diffName).width;
|
|
1924
|
+
const diffLevelWidth = ctx.measureText(diffLevel).width;
|
|
1925
|
+
const paddingHorizontal = 16;
|
|
1926
|
+
const paddingVertical = 4;
|
|
1927
|
+
const gap = 8;
|
|
1928
|
+
const totalWidth = diffNameWidth + diffLevelWidth + gap + paddingHorizontal * 2;
|
|
1929
|
+
const height = 24 * 1.21 + paddingVertical * 2;
|
|
1930
|
+
ctx.fillStyle = bgColor;
|
|
1931
|
+
this.roundRect(ctx, x + 410 - totalWidth / 2, y + 158 - height / 2, totalWidth, height, 100);
|
|
1932
|
+
ctx.fill();
|
|
1933
|
+
ctx.fillStyle = "#FFFFFF";
|
|
1934
|
+
ctx.textAlign = "center";
|
|
1935
|
+
ctx.textBaseline = "middle";
|
|
1936
|
+
ctx.fillText(diffName, x + 410 - gap / 2 - diffLevelWidth / 2, y + 158);
|
|
1937
|
+
ctx.fillText(diffLevel, x + 410 + gap / 2 + diffNameWidth / 2, y + 158);
|
|
1938
|
+
const clearType = score.music.clear_type || "PLAYED";
|
|
1939
|
+
const clearColors = {
|
|
1940
|
+
"PUC": "#F2C027",
|
|
1941
|
+
// Perfect Ultimate Chain - Gold
|
|
1942
|
+
"S-PUC": "#F2E127",
|
|
1943
|
+
// S-Perfect Ultimate Chain - Light Gold/Yellow
|
|
1944
|
+
"UC": "#C539AB",
|
|
1945
|
+
// Ultimate Chain - Magenta
|
|
1946
|
+
"HC": "#C5393B",
|
|
1947
|
+
// Hard Clear - Red
|
|
1948
|
+
"NC": "#39C539",
|
|
1949
|
+
// Normal Clear - Green
|
|
1950
|
+
"PLAYED": "#AF970A",
|
|
1951
|
+
// Played - Dark Yellow
|
|
1952
|
+
"NO PLAY": "#8F8E8B"
|
|
1953
|
+
// No Play - Gray
|
|
1954
|
+
};
|
|
1955
|
+
const clearDisplayText = {
|
|
1956
|
+
"PUC": "PUC",
|
|
1957
|
+
"S-PUC": "S-PUC",
|
|
1958
|
+
"UC": "UC",
|
|
1959
|
+
"HC": "COMP",
|
|
1960
|
+
"NC": "COMP",
|
|
1961
|
+
"PLAYED": "PLAYED",
|
|
1962
|
+
"NO PLAY": "NO PLAY"
|
|
1963
|
+
};
|
|
1964
|
+
const clearDisplayName = clearDisplayText[clearType] || clearType;
|
|
1965
|
+
const clearBgColor = clearColors[clearType] || "#8F8E8B";
|
|
1966
|
+
ctx.font = '24px "Fredoka One"';
|
|
1967
|
+
const clearTextWidth = ctx.measureText(clearDisplayName).width;
|
|
1968
|
+
const clearTotalWidth = clearTextWidth + paddingHorizontal * 2;
|
|
1969
|
+
const clearX = x + 410 + totalWidth / 2 + 22;
|
|
1970
|
+
ctx.fillStyle = clearBgColor;
|
|
1971
|
+
this.roundRect(ctx, clearX, y + 158 - height / 2, clearTotalWidth, height, 100);
|
|
1972
|
+
ctx.fill();
|
|
1973
|
+
ctx.fillStyle = "#FFFFFF";
|
|
1974
|
+
ctx.textAlign = "center";
|
|
1975
|
+
ctx.textBaseline = "middle";
|
|
1976
|
+
ctx.fillText(clearDisplayName, clearX + clearTotalWidth / 2, y + 158);
|
|
1977
|
+
ctx.textAlign = "left";
|
|
1978
|
+
ctx.textBaseline = "alphabetic";
|
|
1979
|
+
const scoreText = score.music.score.toString();
|
|
1980
|
+
const firstThreeDigits = scoreText.slice(0, -4);
|
|
1981
|
+
const lastDigit = scoreText.slice(-4);
|
|
1982
|
+
const scoreGradient = ctx.createLinearGradient(x + 525, y + 215, x + 600, y + 380);
|
|
1983
|
+
scoreGradient.addColorStop(0, "#878787");
|
|
1984
|
+
scoreGradient.addColorStop(0.4, "#878787");
|
|
1985
|
+
scoreGradient.addColorStop(0.41, "#5C5C5C");
|
|
1986
|
+
scoreGradient.addColorStop(1, "#5C5C5C");
|
|
1987
|
+
ctx.fillStyle = scoreGradient;
|
|
1988
|
+
ctx.font = '64px "Fredoka One"';
|
|
1989
|
+
const firstThreeWidth = ctx.measureText(firstThreeDigits).width;
|
|
1990
|
+
ctx.fillText(firstThreeDigits, x + 525, y + 278);
|
|
1991
|
+
ctx.font = '36px "Fredoka One"';
|
|
1992
|
+
ctx.fillText(lastDigit, x + 525 + firstThreeWidth, y + 278);
|
|
1993
|
+
ctx.textAlign = "left";
|
|
1994
|
+
ctx.textBaseline = "top";
|
|
1995
|
+
ctx.font = '64px "Fredoka One"';
|
|
1996
|
+
const vfText = vf2.toFixed(2);
|
|
1997
|
+
const vfColorMap = {
|
|
1998
|
+
1: { from: "#CC7631", to: "#854F24" },
|
|
1999
|
+
// Class 1
|
|
2000
|
+
2: { from: "#5087ED", to: "#1354CD" },
|
|
2001
|
+
// Class 2
|
|
2002
|
+
3: { from: "#F2971A", to: "#CA7B0E" },
|
|
2003
|
+
// Class 3
|
|
2004
|
+
4: { from: "#1BC8CE", to: "#14A0A5" },
|
|
2005
|
+
// Class 4
|
|
2006
|
+
5: { from: "#D6373D", to: "#981E22" },
|
|
2007
|
+
// Class 5
|
|
2008
|
+
6: { from: "#E586A1", to: "#D94A72" },
|
|
2009
|
+
// Class 6
|
|
2010
|
+
7: { from: "#ABBAC8", to: "#738DA5" },
|
|
2011
|
+
// Class 7
|
|
2012
|
+
8: { from: "#EEBB40", to: "#D9A015" },
|
|
2013
|
+
// Class 8
|
|
2014
|
+
9: { from: "#D56763", to: "#BC3531" },
|
|
2015
|
+
// Class 9
|
|
2016
|
+
10: { from: "#9531D9", to: "#63278B" }
|
|
2017
|
+
// Class 10
|
|
2018
|
+
};
|
|
2019
|
+
const vfColors = vfColorMap[vfClass] || vfColorMap[1];
|
|
2020
|
+
const vfGradient = ctx.createLinearGradient(x + 565, y + 296, x + 675, y + 450);
|
|
2021
|
+
vfGradient.addColorStop(0, vfColors.from);
|
|
2022
|
+
vfGradient.addColorStop(0.4, vfColors.from);
|
|
2023
|
+
vfGradient.addColorStop(0.41, vfColors.to);
|
|
2024
|
+
vfGradient.addColorStop(1, vfColors.to);
|
|
2025
|
+
ctx.fillStyle = vfGradient;
|
|
2026
|
+
ctx.fillText(vfText, x + 525, y + 323);
|
|
2027
|
+
ctx.fillStyle = "#FFFFFF";
|
|
2028
|
+
ctx.font = '32px "Fredoka One"';
|
|
2029
|
+
ctx.fillStyle = "#FFFFFF";
|
|
2030
|
+
const indexText = `#${String(i + 1).padStart(2, "0")}`;
|
|
2031
|
+
ctx.fillText(indexText, x + 744, y + 349);
|
|
2032
|
+
ctx.textAlign = "left";
|
|
2033
|
+
ctx.font = '400 24px "Noto Sans JP"';
|
|
2034
|
+
ctx.fillStyle = "#FFFFFF";
|
|
2035
|
+
const musicId = score.music.music_id;
|
|
2036
|
+
let mainTitle = `Music ID ${musicId}`;
|
|
2037
|
+
let subTitle = ``;
|
|
2038
|
+
if (score.music_data) {
|
|
2039
|
+
if (score.music_data.main_title_name) {
|
|
2040
|
+
mainTitle = score.music_data.main_title_name;
|
|
2041
|
+
} else if (score.music_data.title_name) {
|
|
2042
|
+
mainTitle = score.music_data.title_name;
|
|
2043
|
+
}
|
|
2044
|
+
if (score.music_data.sub_title_name) {
|
|
2045
|
+
subTitle = score.music_data.sub_title_name;
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
const wrappedText = this.wrapText(ctx, mainTitle, 413);
|
|
2049
|
+
wrappedText.forEach((line, index) => {
|
|
2050
|
+
ctx.fillText(line, x + 388, y + 90 + index * 30);
|
|
2051
|
+
});
|
|
2052
|
+
ctx.fillStyle = "rgba(255, 255, 255, 0.7)";
|
|
2053
|
+
const wrappedSubTitle = this.wrapText(ctx, subTitle, 413);
|
|
2054
|
+
const startY2 = y + 388 + wrappedText.length * 30;
|
|
2055
|
+
wrappedSubTitle.forEach((line, index) => {
|
|
2056
|
+
ctx.fillText(line, x + 388, startY2 + index * 28);
|
|
2057
|
+
});
|
|
2058
|
+
ctx.fillStyle = "#FFFFFF";
|
|
2059
|
+
const grade = score.music.score_grade;
|
|
2060
|
+
if (gradeImages[grade]) {
|
|
2061
|
+
ctx.drawImage(gradeImages[grade], x + 352, y + 230);
|
|
2062
|
+
}
|
|
2063
|
+
if (vfBadges[vfClass]) {
|
|
2064
|
+
ctx.drawImage(vfBadges[vfClass], x + 396, y + 340);
|
|
2065
|
+
}
|
|
2066
|
+
if (score.difficulty_data?.cover_url) {
|
|
2067
|
+
const coverUrl = `${config.sdvx_data_url}${score.difficulty_data.cover_url}`.replace(".webp", ".png");
|
|
2068
|
+
const jacketImage = jacketImages[coverUrl];
|
|
2069
|
+
if (jacketImage) {
|
|
2070
|
+
ctx.drawImage(jacketImage, x + 24, y + 89, 300, 300);
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
} catch (error) {
|
|
2074
|
+
this.ctx.logger("SDVX-Drawer").warn(`Error drawing card ${i}: ${error.message}`);
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
/**
|
|
2079
|
+
* Get abbreviation for a difficulty level
|
|
2080
|
+
*/
|
|
2081
|
+
getDifficultyAbbreviation(diff) {
|
|
2082
|
+
const diffMap = {
|
|
2083
|
+
0: "NOV",
|
|
2084
|
+
// Novice
|
|
2085
|
+
1: "ADV",
|
|
2086
|
+
// Advanced
|
|
2087
|
+
2: "EXH",
|
|
2088
|
+
// Exhaust
|
|
2089
|
+
3: "INF",
|
|
2090
|
+
// Infinite
|
|
2091
|
+
4: "MXM"
|
|
2092
|
+
// Maximum
|
|
2093
|
+
};
|
|
2094
|
+
return diffMap[diff] || "UNK";
|
|
2095
|
+
}
|
|
2096
|
+
/**
|
|
2097
|
+
* Calculate the average volforce from best 50 scores
|
|
2098
|
+
*/
|
|
2099
|
+
calculateAverageVF(scores) {
|
|
2100
|
+
if (scores.length === 0)
|
|
2101
|
+
return 0;
|
|
2102
|
+
const sortedScores = [...scores].sort(
|
|
2103
|
+
(a, b) => b.extra.volforce - a.extra.volforce
|
|
2104
|
+
).slice(0, 50);
|
|
2105
|
+
const total = sortedScores.reduce((sum, score) => sum + score.extra.volforce, 0);
|
|
2106
|
+
return total / sortedScores.length;
|
|
2107
|
+
}
|
|
2108
|
+
wrapText(ctx, text, maxWidth) {
|
|
2109
|
+
const words = text.split(" ");
|
|
2110
|
+
const lines = [];
|
|
2111
|
+
let currentLine = "";
|
|
2112
|
+
for (const word of words) {
|
|
2113
|
+
const testLine = currentLine + word + " ";
|
|
2114
|
+
const testWidth = ctx.measureText(testLine).width;
|
|
2115
|
+
if (testWidth > maxWidth) {
|
|
2116
|
+
lines.push(currentLine.trim());
|
|
2117
|
+
currentLine = word + " ";
|
|
2118
|
+
} else {
|
|
2119
|
+
currentLine = testLine;
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
if (currentLine.trim()) {
|
|
2123
|
+
lines.push(currentLine.trim());
|
|
2124
|
+
}
|
|
2125
|
+
return lines;
|
|
2126
|
+
}
|
|
2127
|
+
roundRect(ctx, x, y, width, height, radius) {
|
|
2128
|
+
if (width < 2 * radius)
|
|
2129
|
+
radius = width / 2;
|
|
2130
|
+
if (height < 2 * radius)
|
|
2131
|
+
radius = height / 2;
|
|
2132
|
+
ctx.beginPath();
|
|
2133
|
+
ctx.moveTo(x + radius, y);
|
|
2134
|
+
ctx.arcTo(x + width, y, x + width, y + height, radius);
|
|
2135
|
+
ctx.arcTo(x + width, y + height, x, y + height, radius);
|
|
2136
|
+
ctx.arcTo(x, y + height, x, y, radius);
|
|
2137
|
+
ctx.arcTo(x, y, x + width, y, radius);
|
|
2138
|
+
ctx.closePath();
|
|
2139
|
+
}
|
|
2140
|
+
};
|
|
2141
|
+
|
|
2142
|
+
// src/drawer/IIDX/index.ts
|
|
2143
|
+
var IIDXDrawer = class extends BaseDrawer {
|
|
2144
|
+
constructor(ctx) {
|
|
2145
|
+
super(ctx);
|
|
2146
|
+
this.ctx = ctx;
|
|
2147
|
+
}
|
|
2148
|
+
static {
|
|
2149
|
+
__name(this, "IIDXDrawer");
|
|
2150
|
+
}
|
|
2151
|
+
/**
|
|
2152
|
+
* Generate an image for IIDX data
|
|
2153
|
+
* @param options Configuration options for image generation
|
|
2154
|
+
* @param compression Compression options
|
|
2155
|
+
* @returns Promise resolving to a Buffer containing the image data
|
|
2156
|
+
*/
|
|
2157
|
+
async generateImage(options, compression) {
|
|
2158
|
+
const { Canvas, loadImage } = this.ctx.skia;
|
|
2159
|
+
const width = options?.width || 1e3;
|
|
2160
|
+
const height = options?.height || 600;
|
|
2161
|
+
const playerName = options?.playerName || "IIDX Player";
|
|
2162
|
+
const backgroundColor = options?.backgroundColor || "#000000";
|
|
2163
|
+
const textColor = options?.textColor || "#FFFFFF";
|
|
2164
|
+
const dj_level = options?.dj_level || "UNKNOWN";
|
|
2165
|
+
const sp_dan = options?.sp_dan || "UNKNOWN";
|
|
2166
|
+
const dp_dan = options?.dp_dan || "UNKNOWN";
|
|
2167
|
+
const canvas = new Canvas(width, height);
|
|
2168
|
+
const ctx = canvas.getContext("2d");
|
|
2169
|
+
ctx.fillStyle = backgroundColor;
|
|
2170
|
+
ctx.fillRect(0, 0, width, height);
|
|
2171
|
+
ctx.fillStyle = "#FFCC00";
|
|
2172
|
+
ctx.fillRect(0, 0, width, 100);
|
|
2173
|
+
ctx.fillStyle = "#000000";
|
|
2174
|
+
ctx.font = "bold 32px Arial";
|
|
2175
|
+
ctx.textAlign = "center";
|
|
2176
|
+
ctx.fillText(`BEATMANIA IIDX`, width / 2, 60);
|
|
2177
|
+
ctx.fillStyle = textColor;
|
|
2178
|
+
ctx.font = "bold 24px Arial";
|
|
2179
|
+
ctx.textAlign = "center";
|
|
2180
|
+
ctx.fillText(`${playerName}`, width / 2, 150);
|
|
2181
|
+
ctx.font = "20px Arial";
|
|
2182
|
+
ctx.fillText(`DJ LEVEL: ${dj_level}`, width / 2, 200);
|
|
2183
|
+
this.drawRankingInfo(ctx, width / 2, 250, sp_dan, dp_dan, textColor);
|
|
2184
|
+
return this.compressImage(canvas, compression);
|
|
2185
|
+
}
|
|
2186
|
+
/**
|
|
2187
|
+
* Draw ranking information
|
|
2188
|
+
*/
|
|
2189
|
+
drawRankingInfo(ctx, centerX, y, sp_dan, dp_dan, textColor) {
|
|
2190
|
+
ctx.fillStyle = textColor;
|
|
2191
|
+
ctx.font = "18px Arial";
|
|
2192
|
+
ctx.textAlign = "right";
|
|
2193
|
+
ctx.fillText("SP DAN:", centerX - 10, y);
|
|
2194
|
+
ctx.textAlign = "left";
|
|
2195
|
+
ctx.fillText(sp_dan, centerX + 10, y);
|
|
2196
|
+
ctx.textAlign = "right";
|
|
2197
|
+
ctx.fillText("DP DAN:", centerX - 10, y + 40);
|
|
2198
|
+
ctx.textAlign = "left";
|
|
2199
|
+
ctx.fillText(dp_dan, centerX + 10, y + 40);
|
|
2200
|
+
}
|
|
2201
|
+
};
|
|
2202
|
+
|
|
2203
|
+
// src/drawer/DrawerFactory.ts
|
|
2204
|
+
var DrawerFactory = class {
|
|
2205
|
+
constructor(ctx) {
|
|
2206
|
+
this.ctx = ctx;
|
|
2207
|
+
}
|
|
2208
|
+
static {
|
|
2209
|
+
__name(this, "DrawerFactory");
|
|
2210
|
+
}
|
|
2211
|
+
drawerInstances = /* @__PURE__ */ new Map();
|
|
2212
|
+
getDrawer(drawerType) {
|
|
2213
|
+
if (!this.drawerInstances.has(drawerType)) {
|
|
2214
|
+
let drawerInstance;
|
|
2215
|
+
switch (drawerType) {
|
|
2216
|
+
case "sdvx":
|
|
2217
|
+
drawerInstance = new SDVXDrawer(this.ctx);
|
|
2218
|
+
break;
|
|
2219
|
+
case "iidx":
|
|
2220
|
+
drawerInstance = new IIDXDrawer(this.ctx);
|
|
2221
|
+
break;
|
|
2222
|
+
case "core":
|
|
2223
|
+
drawerInstance = new CoreDrawer(this.ctx);
|
|
2224
|
+
break;
|
|
2225
|
+
default:
|
|
2226
|
+
throw new Error(`Unsupported drawer type: ${drawerType}`);
|
|
2227
|
+
}
|
|
2228
|
+
this.drawerInstances.set(drawerType, drawerInstance);
|
|
2229
|
+
}
|
|
2230
|
+
return this.drawerInstances.get(drawerType);
|
|
2231
|
+
}
|
|
2232
|
+
};
|
|
2233
|
+
|
|
2234
|
+
// src/drawer/index.ts
|
|
2235
|
+
var name6 = "Noah-Drawer";
|
|
2236
|
+
var DrawerManager = class _DrawerManager {
|
|
2237
|
+
static {
|
|
2238
|
+
__name(this, "DrawerManager");
|
|
2239
|
+
}
|
|
2240
|
+
static instance;
|
|
2241
|
+
factory;
|
|
2242
|
+
logger;
|
|
2243
|
+
constructor(ctx) {
|
|
2244
|
+
this.factory = new DrawerFactory(ctx);
|
|
2245
|
+
this.logger = new import_koishi7.Logger("Noah-Drawer");
|
|
2246
|
+
}
|
|
2247
|
+
static getInstance(ctx) {
|
|
2248
|
+
if (!_DrawerManager.instance) {
|
|
2249
|
+
_DrawerManager.instance = new _DrawerManager(ctx);
|
|
2250
|
+
}
|
|
2251
|
+
return _DrawerManager.instance;
|
|
2252
|
+
}
|
|
2253
|
+
getDrawer(gameType) {
|
|
2254
|
+
this.logger.info(`Getting drawer for ${gameType}`);
|
|
2255
|
+
return this.factory.getDrawer(gameType);
|
|
2256
|
+
}
|
|
2257
|
+
};
|
|
2258
|
+
function apply6(ctx, config) {
|
|
2259
|
+
ctx.logger("Noah-Drawer").info("Initializing drawer manager");
|
|
2260
|
+
const drawerManager = DrawerManager.getInstance(ctx);
|
|
2261
|
+
}
|
|
2262
|
+
__name(apply6, "apply");
|
|
2263
|
+
|
|
2264
|
+
// src/games/sdvx/commands/vf.ts
|
|
2265
|
+
function vf(ctx, config) {
|
|
2266
|
+
ctx.command("vf [cardCode]").userFields(["defaultCardId", "defaultServerId", "id"]).channelFields(["defaultServerId", "id"]).option("lossless", "-l Use lossless compression for webp").action(async ({ session, options }, cardCode) => {
|
|
2267
|
+
const model = config.default_model;
|
|
2268
|
+
const cardService = new CardService(ctx);
|
|
2269
|
+
const serverService = new ServerService(ctx);
|
|
2270
|
+
const userCards = await cardService.getCardsByUid(session.user.id);
|
|
2271
|
+
if (userCards.length === 0)
|
|
2272
|
+
return session.text(".card-not-found");
|
|
2273
|
+
const userRes = await serverService.getServersByUid(session.user.id);
|
|
2274
|
+
const channelRes = await serverService.getServersByCid(session.channel.id);
|
|
2275
|
+
const serverRes = channelRes.concat(userRes);
|
|
2276
|
+
if (serverRes.length === 0)
|
|
2277
|
+
return session.text(".server-not-found");
|
|
2278
|
+
if (!cardCode) {
|
|
2279
|
+
const cardRes = await cardService.getCardById(session.user.defaultCardId);
|
|
2280
|
+
cardCode = cardRes.code;
|
|
2281
|
+
}
|
|
2282
|
+
const card2 = await cardService.getCardByCode(cardCode);
|
|
2283
|
+
let server2;
|
|
2284
|
+
if (card2.defaultServerId != 0)
|
|
2285
|
+
server2 = await serverService.getServerById(card2.defaultServerId);
|
|
2286
|
+
else if (session.channel.defaultServerId != 0)
|
|
2287
|
+
server2 = await serverService.getServerById(session.channel.defaultServerId);
|
|
2288
|
+
else if (session.user.defaultServerId != 0)
|
|
2289
|
+
server2 = await serverService.getServerById(session.user.defaultServerId);
|
|
2290
|
+
const serverManager = ServerManager.getInstance();
|
|
2291
|
+
const sdvxService = serverManager.getGameService(server2.type, "sdvx");
|
|
2292
|
+
const scoreService = ScoreService.getInstance();
|
|
2293
|
+
try {
|
|
2294
|
+
const scoreList = scoreService.getBest50(await sdvxService.getAllScore(ctx, server2.baseUrl, card2.code, config));
|
|
2295
|
+
if (scoreList.length === 0) {
|
|
2296
|
+
return session.text(".no-scores-found");
|
|
2297
|
+
}
|
|
2298
|
+
session.send(session.text(".drawing"));
|
|
2299
|
+
const drawerManager = DrawerManager.getInstance(ctx);
|
|
2300
|
+
const sdvxDrawer = drawerManager.getDrawer("sdvx");
|
|
2301
|
+
const imageBuffer = await sdvxDrawer.generateVFImage({
|
|
2302
|
+
scores: scoreList,
|
|
2303
|
+
playerName: card2.name || "Unknown Player",
|
|
2304
|
+
config
|
|
2305
|
+
}, {
|
|
2306
|
+
lossless: options.lossless || false
|
|
2307
|
+
});
|
|
2308
|
+
if (options.lossless) {
|
|
2309
|
+
return import_koishi8.h.file(imageBuffer, "image/png");
|
|
2310
|
+
}
|
|
2311
|
+
return import_koishi8.h.image(imageBuffer, "image/jpg");
|
|
2312
|
+
} catch (error) {
|
|
2313
|
+
ctx.logger("SDVX-VF").warn(error);
|
|
2314
|
+
return session.text(".error", { message: error.message });
|
|
2315
|
+
}
|
|
2316
|
+
});
|
|
2317
|
+
}
|
|
2318
|
+
__name(vf, "vf");
|
|
2319
|
+
|
|
2320
|
+
// src/games/sdvx/command.ts
|
|
2321
|
+
var name7 = "command";
|
|
2322
|
+
function apply7(ctx, config) {
|
|
2323
|
+
vf(ctx, config);
|
|
2324
|
+
}
|
|
2325
|
+
__name(apply7, "apply");
|
|
2326
|
+
|
|
2327
|
+
// src/games/sdvx/event.ts
|
|
2328
|
+
var event_exports = {};
|
|
2329
|
+
__export(event_exports, {
|
|
2330
|
+
apply: () => apply8,
|
|
2331
|
+
name: () => name8
|
|
2332
|
+
});
|
|
2333
|
+
var name8 = "event";
|
|
2334
|
+
function apply8(ctx) {
|
|
2335
|
+
}
|
|
2336
|
+
__name(apply8, "apply");
|
|
2337
|
+
|
|
2338
|
+
// src/games/sdvx/index.ts
|
|
2339
|
+
var name9 = "Noah-SDVX";
|
|
2340
|
+
var inject2 = ["database"];
|
|
2341
|
+
var logger2 = new import_koishi9.Logger("Noah-SDVX");
|
|
2342
|
+
async function apply9(ctx, config) {
|
|
2343
|
+
[["en-US", en_US_default2], ["zh-CN", zh_CN_default2]].forEach(([lang, file]) => ctx.i18n.define(lang, file));
|
|
2344
|
+
ctx.plugin(database_exports2, config.sdvx);
|
|
2345
|
+
ctx.plugin(command_exports2, config.sdvx);
|
|
2346
|
+
ctx.plugin(event_exports, config.sdvx);
|
|
2347
|
+
const serverManager = ServerManager.getInstance();
|
|
2348
|
+
const sdvxService = serverManager.getGameService("mao", "sdvx");
|
|
2349
|
+
const musicService = MusicService.getInstance(config.sdvx);
|
|
2350
|
+
const music = await musicService.getMusic(ctx, [1438, 1259]);
|
|
2351
|
+
}
|
|
2352
|
+
__name(apply9, "apply");
|
|
2353
|
+
|
|
2354
|
+
// src/config.ts
|
|
2355
|
+
var import_koishi11 = require("koishi");
|
|
2356
|
+
|
|
2357
|
+
// src/games/sdvx/config.ts
|
|
2358
|
+
var import_koishi10 = require("koishi");
|
|
2359
|
+
var sdvxConfig = import_koishi10.Schema.object({
|
|
2360
|
+
default_model: import_koishi10.Schema.string().default("2024110700"),
|
|
2361
|
+
sdvx_data_url: import_koishi10.Schema.string().required()
|
|
2362
|
+
}).i18n({
|
|
2363
|
+
"en-US": en_US_default2._config,
|
|
2364
|
+
"zh-CN": zh_CN_default2._config
|
|
2365
|
+
});
|
|
2366
|
+
|
|
2367
|
+
// src/config.ts
|
|
2368
|
+
var Config = import_koishi11.Schema.object({
|
|
2369
|
+
core: coreConfig,
|
|
2370
|
+
sdvx: sdvxConfig
|
|
2371
|
+
});
|
|
2372
|
+
|
|
2373
|
+
// src/index.ts
|
|
2374
|
+
var name10 = "noah";
|
|
2375
|
+
var inject3 = ["database", "skia"];
|
|
2376
|
+
function apply10(ctx, config) {
|
|
2377
|
+
ctx.plugin(core_exports, config);
|
|
2378
|
+
ctx.plugin(sdvx_exports, config);
|
|
2379
|
+
ctx.plugin(servers_exports, config);
|
|
2380
|
+
ctx.plugin(drawer_exports, config);
|
|
2381
|
+
}
|
|
2382
|
+
__name(apply10, "apply");
|
|
2383
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
2384
|
+
0 && (module.exports = {
|
|
2385
|
+
Config,
|
|
2386
|
+
apply,
|
|
2387
|
+
inject,
|
|
2388
|
+
name
|
|
2389
|
+
});
|