koishi-plugin-checkgal 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/api.d.ts +10 -0
- package/lib/api.js +62 -0
- package/lib/cache.d.ts +9 -0
- package/lib/cache.js +30 -0
- package/lib/commands.d.ts +11 -0
- package/lib/commands.js +75 -0
- package/lib/config.d.ts +6 -0
- package/lib/config.js +8 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.js +24 -0
- package/lib/types.d.ts +27 -0
- package/lib/types.js +2 -0
- package/package.json +28 -0
package/lib/api.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Context } from 'koishi';
|
|
2
|
+
import { GameInfo, DownloadResource } from './types';
|
|
3
|
+
import { Config } from './config';
|
|
4
|
+
export declare class TouchGalAPI {
|
|
5
|
+
private http;
|
|
6
|
+
private logger;
|
|
7
|
+
constructor(ctx: Context);
|
|
8
|
+
searchGame(keyword: string, config: Config): Promise<GameInfo[]>;
|
|
9
|
+
getDownloads(patchId: number): Promise<DownloadResource[]>;
|
|
10
|
+
}
|
package/lib/api.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TouchGalAPI = void 0;
|
|
4
|
+
class TouchGalAPI {
|
|
5
|
+
constructor(ctx) {
|
|
6
|
+
this.http = ctx.http;
|
|
7
|
+
this.logger = ctx.logger('checkgal-api');
|
|
8
|
+
}
|
|
9
|
+
async searchGame(keyword, config) {
|
|
10
|
+
const url = 'https://www.touchgal.us/api/search';
|
|
11
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
12
|
+
const queryString = JSON.stringify([{ type: 'keyword', name: keyword }]);
|
|
13
|
+
const payload = {
|
|
14
|
+
queryString: queryString,
|
|
15
|
+
limit: config.searchLimit,
|
|
16
|
+
searchOption: {
|
|
17
|
+
searchInIntroduction: true,
|
|
18
|
+
searchInAlias: true,
|
|
19
|
+
searchInTag: true,
|
|
20
|
+
},
|
|
21
|
+
page: 1,
|
|
22
|
+
selectedType: 'all',
|
|
23
|
+
selectedLanguage: 'all',
|
|
24
|
+
selectedPlatform: 'all',
|
|
25
|
+
sortField: 'resource_update_time',
|
|
26
|
+
sortOrder: 'desc',
|
|
27
|
+
selectedYears: ['all'],
|
|
28
|
+
selectedMonths: ['all'],
|
|
29
|
+
};
|
|
30
|
+
const cookieString = `kun-patch-setting-store|state|data|kunNsfwEnable=${config.enableNsfw ? 'all' : 'sfw'}`;
|
|
31
|
+
try {
|
|
32
|
+
const responseData = await this.http.post(url, payload, {
|
|
33
|
+
headers: {
|
|
34
|
+
...headers,
|
|
35
|
+
'Cookie': cookieString,
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
if (!responseData || !responseData.galgames) {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
return responseData.galgames;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
this.logger.error('Failed to search game:', error);
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async getDownloads(patchId) {
|
|
49
|
+
const url = 'https://www.touchgal.us/api/patch/resource';
|
|
50
|
+
try {
|
|
51
|
+
const responseData = await this.http.get(url, {
|
|
52
|
+
params: { patchId },
|
|
53
|
+
});
|
|
54
|
+
return responseData || [];
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
this.logger.error(`Failed to get downloads for patchId ${patchId}:`, error);
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.TouchGalAPI = TouchGalAPI;
|
package/lib/cache.d.ts
ADDED
package/lib/cache.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GameCache = void 0;
|
|
4
|
+
class GameCache {
|
|
5
|
+
constructor(ttlSeconds = 86400) {
|
|
6
|
+
this.cache = new Map();
|
|
7
|
+
this.ttl = ttlSeconds * 1000;
|
|
8
|
+
}
|
|
9
|
+
get(key) {
|
|
10
|
+
return this.cache.get(key)?.data;
|
|
11
|
+
}
|
|
12
|
+
set(key, value) {
|
|
13
|
+
// 如果已存在,清除旧的定时器
|
|
14
|
+
const existingEntry = this.cache.get(key);
|
|
15
|
+
if (existingEntry) {
|
|
16
|
+
clearTimeout(existingEntry.timer);
|
|
17
|
+
}
|
|
18
|
+
const timer = setTimeout(() => {
|
|
19
|
+
this.cache.delete(key);
|
|
20
|
+
}, this.ttl);
|
|
21
|
+
this.cache.set(key, { data: value, timer });
|
|
22
|
+
}
|
|
23
|
+
clear() {
|
|
24
|
+
for (const entry of this.cache.values()) {
|
|
25
|
+
clearTimeout(entry.timer);
|
|
26
|
+
}
|
|
27
|
+
this.cache.clear();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.GameCache = GameCache;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Context } from 'koishi';
|
|
2
|
+
import { Config } from './config';
|
|
3
|
+
import { TouchGalAPI } from './api';
|
|
4
|
+
import { GameCache } from './cache';
|
|
5
|
+
declare module 'koishi' {
|
|
6
|
+
interface Context {
|
|
7
|
+
touchgal: TouchGalAPI;
|
|
8
|
+
gameCache: GameCache;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export declare function apply(ctx: Context, config: Config): void;
|
package/lib/commands.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.apply = apply;
|
|
4
|
+
const koishi_1 = require("koishi");
|
|
5
|
+
function apply(ctx, config) {
|
|
6
|
+
ctx.command('查询gal <keyword:text>', '查询Galgame信息')
|
|
7
|
+
.action(async ({ session }, keyword) => {
|
|
8
|
+
if (!session)
|
|
9
|
+
return '该指令只能在聊天环境中使用。';
|
|
10
|
+
if (!keyword)
|
|
11
|
+
return '请输入要查询的游戏名。';
|
|
12
|
+
await session.send('正在查询,请稍候...');
|
|
13
|
+
const results = await ctx.touchgal.searchGame(keyword, config);
|
|
14
|
+
if (!results.length) {
|
|
15
|
+
return `未找到关于“${keyword}”的任何游戏。`;
|
|
16
|
+
}
|
|
17
|
+
// 缓存结果
|
|
18
|
+
results.forEach(game => ctx.gameCache.set(game.id, game));
|
|
19
|
+
const forwardMessages = results.map(game => {
|
|
20
|
+
const content = [
|
|
21
|
+
(0, koishi_1.h)('image', { url: game.banner }),
|
|
22
|
+
`ID: ${game.id}`,
|
|
23
|
+
`名称: ${game.name}`,
|
|
24
|
+
`平台: ${game.platform.join(', ')}`,
|
|
25
|
+
`语言: ${game.language.join(', ')}`,
|
|
26
|
+
].join('\n');
|
|
27
|
+
// 使用固定的机器人名称和 ID,避免 bot.username 可能不存在的问题
|
|
28
|
+
return (0, koishi_1.h)('message', { userId: session.bot.selfId, nickname: 'CheckGal Bot' }, content);
|
|
29
|
+
});
|
|
30
|
+
await session.send((0, koishi_1.h)('message', { forward: true }, forwardMessages));
|
|
31
|
+
});
|
|
32
|
+
ctx.command('下载gal <id:number>', '获取Galgame下载地址')
|
|
33
|
+
.action(async ({ session }, id) => {
|
|
34
|
+
if (!session)
|
|
35
|
+
return '该指令只能在聊天环境中使用。';
|
|
36
|
+
if (!id)
|
|
37
|
+
return '请输入游戏ID。';
|
|
38
|
+
let gameInfo = ctx.gameCache.get(id);
|
|
39
|
+
// 如果缓存中没有,尝试重新获取(这可能发生在机器人重启后)
|
|
40
|
+
if (!gameInfo) {
|
|
41
|
+
await session.send('缓存中未找到该游戏信息,正在尝试重新搜索...');
|
|
42
|
+
const results = await ctx.touchgal.searchGame(String(id), config);
|
|
43
|
+
const foundGame = results.find(g => g.id === id);
|
|
44
|
+
if (foundGame) {
|
|
45
|
+
gameInfo = foundGame;
|
|
46
|
+
ctx.gameCache.set(id, gameInfo);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// 如果通过ID也搜不到,就直接获取下载链接
|
|
50
|
+
await session.send(`无法获取游戏“${id}”的详细信息,但仍会尝试获取下载链接...`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const downloads = await ctx.touchgal.getDownloads(id);
|
|
54
|
+
if (!downloads.length) {
|
|
55
|
+
return `未找到ID为 ${id} 的下载资源。`;
|
|
56
|
+
}
|
|
57
|
+
const gameTitle = gameInfo ? `游戏: ${gameInfo.name} (ID: ${id})` : `游戏ID: ${id}`;
|
|
58
|
+
const header = [
|
|
59
|
+
gameInfo ? (0, koishi_1.h)('image', { url: gameInfo.banner }) : '',
|
|
60
|
+
gameTitle,
|
|
61
|
+
`共找到 ${downloads.length} 个下载资源:`,
|
|
62
|
+
].filter(Boolean).join('\n');
|
|
63
|
+
const downloadDetails = downloads.map(res => {
|
|
64
|
+
return [
|
|
65
|
+
`› 名称: ${res.name}`,
|
|
66
|
+
` 平台: ${res.platform.join(', ')} | 大小: ${res.size}`,
|
|
67
|
+
` 下载地址: ${res.content}`,
|
|
68
|
+
` 提取码: ${res.code || '无'}`,
|
|
69
|
+
` 解压码: ${res.password || '无'}`,
|
|
70
|
+
` 备注: ${res.note || '无'}`,
|
|
71
|
+
].join('\n');
|
|
72
|
+
}).join('\n\n');
|
|
73
|
+
return `${header}\n\n${downloadDetails}`;
|
|
74
|
+
});
|
|
75
|
+
}
|
package/lib/config.d.ts
ADDED
package/lib/config.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Config = void 0;
|
|
4
|
+
const koishi_1 = require("koishi");
|
|
5
|
+
exports.Config = koishi_1.Schema.object({
|
|
6
|
+
searchLimit: koishi_1.Schema.number().default(15).min(1).max(50).description('单次搜索返回的最大结果数量。'),
|
|
7
|
+
enableNsfw: koishi_1.Schema.boolean().default(false).description('是否允许搜索 NSFW 内容。'),
|
|
8
|
+
});
|
package/lib/index.d.ts
ADDED
package/lib/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Config = exports.name = void 0;
|
|
4
|
+
exports.apply = apply;
|
|
5
|
+
const config_1 = require("./config");
|
|
6
|
+
Object.defineProperty(exports, "Config", { enumerable: true, get: function () { return config_1.Config; } });
|
|
7
|
+
const api_1 = require("./api");
|
|
8
|
+
const cache_1 = require("./cache");
|
|
9
|
+
const commands_1 = require("./commands");
|
|
10
|
+
exports.name = 'checkgal';
|
|
11
|
+
function apply(ctx, config) {
|
|
12
|
+
// 初始化服务
|
|
13
|
+
const api = new api_1.TouchGalAPI(ctx);
|
|
14
|
+
const cache = new cache_1.GameCache();
|
|
15
|
+
// 将服务挂载到上下文中
|
|
16
|
+
ctx.provide('touchgal', api);
|
|
17
|
+
ctx.provide('gameCache', cache);
|
|
18
|
+
// 注册指令
|
|
19
|
+
ctx.plugin(commands_1.apply, config);
|
|
20
|
+
// 在插件停用时清理缓存
|
|
21
|
+
ctx.on('dispose', () => {
|
|
22
|
+
cache.clear();
|
|
23
|
+
});
|
|
24
|
+
}
|
package/lib/types.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface GameInfo {
|
|
2
|
+
id: number;
|
|
3
|
+
name: string;
|
|
4
|
+
alias: string[];
|
|
5
|
+
tags: string[];
|
|
6
|
+
platform: string[];
|
|
7
|
+
language: string[];
|
|
8
|
+
introduction: string;
|
|
9
|
+
banner: string;
|
|
10
|
+
cover: string;
|
|
11
|
+
download: number;
|
|
12
|
+
created: string;
|
|
13
|
+
resource_update_time: string;
|
|
14
|
+
}
|
|
15
|
+
export interface DownloadResource {
|
|
16
|
+
id: number;
|
|
17
|
+
patchId: number;
|
|
18
|
+
name: string;
|
|
19
|
+
platform: string[];
|
|
20
|
+
language: string[];
|
|
21
|
+
size: string;
|
|
22
|
+
content: string;
|
|
23
|
+
code: string;
|
|
24
|
+
password: any;
|
|
25
|
+
note: string;
|
|
26
|
+
created: string;
|
|
27
|
+
}
|
package/lib/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "koishi-plugin-checkgal",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "个人测试请勿使用",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"typings": "lib/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"lib"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"chatbot",
|
|
15
|
+
"koishi",
|
|
16
|
+
"plugin",
|
|
17
|
+
"galgame"
|
|
18
|
+
],
|
|
19
|
+
"author": "Roo",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"koishi": {
|
|
22
|
+
"name": "checkgal"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"koishi": "^4.18.9",
|
|
26
|
+
"typescript": "^5.9.3"
|
|
27
|
+
}
|
|
28
|
+
}
|