koishi-plugin-bilibili-notify 0.0.1

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.
@@ -0,0 +1,29 @@
1
+ import { Context, Service } from "koishi";
2
+ import { CookieJar } from 'tough-cookie';
3
+ declare module 'koishi' {
4
+ interface Context {
5
+ biliAPI: BiliAPI;
6
+ }
7
+ }
8
+ declare class BiliAPI extends Service {
9
+ static inject: string[];
10
+ jar: CookieJar;
11
+ client: any;
12
+ loginData: any;
13
+ constructor(ctx: Context);
14
+ protected start(): void | Promise<void>;
15
+ getTimeNow(): Promise<any>;
16
+ getUserSpaceDynamic(mid: string): Promise<any>;
17
+ getCookieInfo(refreshToken: string): Promise<any>;
18
+ getUserInfo(mid: string): Promise<any>;
19
+ getMyselfInfo(): Promise<any>;
20
+ getLoginQRCode(): Promise<any>;
21
+ getLoginStatus(qrcodeKey: string): Promise<any>;
22
+ getLiveRoomInfo(roomId: string): Promise<any>;
23
+ getMasterInfo(mid: string): Promise<any>;
24
+ createNewClient(): void;
25
+ getCookies(): Promise<string>;
26
+ loadCookiesFromDatabase(): Promise<void>;
27
+ checkIfTokenNeedRefresh(refreshToken: string, csrf: string): Promise<void>;
28
+ }
29
+ export default BiliAPI;
package/lib/biliAPI.js ADDED
@@ -0,0 +1,196 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const koishi_1 = require("koishi");
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const tough_cookie_1 = require("tough-cookie");
9
+ const axios_cookiejar_support_1 = require("axios-cookiejar-support");
10
+ const jsdom_1 = require("jsdom");
11
+ const GET_DYNAMIC_LIST = 'https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all';
12
+ const GET_USER_SPACE_DYNAMIC_LIST = 'https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space';
13
+ const GET_COOKIES_INFO = 'https://passport.bilibili.com/x/passport-login/web/cookie/info';
14
+ const GET_USER_INFO = 'https://api.bilibili.com/x/space/wbi/acc/info';
15
+ const GET_MYSELF_INFO = 'https://api.bilibili.com/x/member/web/account';
16
+ const GET_LOGIN_QRCODE = 'https://passport.bilibili.com/x/passport-login/web/qrcode/generate';
17
+ const GET_LOGIN_STATUS = 'https://passport.bilibili.com/x/passport-login/web/qrcode/poll';
18
+ const GET_LIVE_ROOM_INFO = 'https://api.live.bilibili.com/room/v1/Room/get_info';
19
+ const GET_MASTER_INFO = 'https://api.live.bilibili.com/live_user/v1/Master/info';
20
+ const GET_TIME_NOW = 'https://api.bilibili.com/x/report/click/now';
21
+ class BiliAPI extends koishi_1.Service {
22
+ static inject = ['database', 'wbi'];
23
+ jar;
24
+ client;
25
+ loginData;
26
+ constructor(ctx) {
27
+ super(ctx, 'biliAPI');
28
+ }
29
+ start() {
30
+ /* this.client = this.ctx.http.extend({
31
+ endpoint: 'https://api.live.bilibili.com',
32
+ }) */
33
+ this.createNewClient();
34
+ this.loadCookiesFromDatabase();
35
+ this.logger.info('BiliAPI已被注册到Context中');
36
+ }
37
+ async getTimeNow() {
38
+ const { data } = await this.client.get(GET_TIME_NOW);
39
+ return data;
40
+ }
41
+ async getUserSpaceDynamic(mid) {
42
+ const { data } = await this.client.get(`${GET_USER_SPACE_DYNAMIC_LIST}?host_mid=${mid}`);
43
+ return data;
44
+ }
45
+ // Check if Token need refresh
46
+ async getCookieInfo(refreshToken) {
47
+ const { data } = await this.client.get(`${GET_COOKIES_INFO}?csrf=${refreshToken}`);
48
+ return data;
49
+ }
50
+ async getUserInfo(mid) {
51
+ const wbi = await this.ctx.wbi.getWbi({ mid });
52
+ const { data } = await this.client.get(`${GET_USER_INFO}?${wbi}`);
53
+ return data;
54
+ }
55
+ async getMyselfInfo() {
56
+ const { data } = await this.client.get(GET_MYSELF_INFO);
57
+ return data;
58
+ }
59
+ async getLoginQRCode() {
60
+ const { data } = await this.client.get(GET_LOGIN_QRCODE);
61
+ return data;
62
+ }
63
+ async getLoginStatus(qrcodeKey) {
64
+ const { data } = await this.client.get(`${GET_LOGIN_STATUS}?qrcode_key=${qrcodeKey}`);
65
+ return data;
66
+ }
67
+ async getLiveRoomInfo(roomId) {
68
+ const { data } = await this.client.get(`${GET_LIVE_ROOM_INFO}?room_id=${roomId}`);
69
+ return data;
70
+ }
71
+ async getMasterInfo(mid) {
72
+ const { data } = await this.client.get(`${GET_MASTER_INFO}?uid=${mid}`);
73
+ return data;
74
+ }
75
+ createNewClient() {
76
+ this.jar = new tough_cookie_1.CookieJar();
77
+ this.client = (0, axios_cookiejar_support_1.wrapper)(axios_1.default.create({ jar: this.jar, headers: { 'Content-Type': 'application/json' } }));
78
+ }
79
+ async getCookies() {
80
+ let cookies;
81
+ this.jar.store.getAllCookies((err, c) => {
82
+ if (err)
83
+ throw err;
84
+ cookies = JSON.stringify(c, null, 2);
85
+ });
86
+ return cookies;
87
+ }
88
+ async loadCookiesFromDatabase() {
89
+ // 读取数据库获取cookies
90
+ const data = (await this.ctx.database.get('loginBili', 1))[0];
91
+ // 没有数据则直接返回
92
+ if (data === undefined)
93
+ return;
94
+ // 解密数据
95
+ const decryptedCookies = this.ctx.wbi.decrypt(data.bili_cookies);
96
+ // 解析从数据库读到的cookies
97
+ const cookies = JSON.parse(decryptedCookies);
98
+ // 定义CSRF Token
99
+ let csrf;
100
+ cookies.forEach(cookieData => {
101
+ // console.log(cookieData);
102
+ // 获取key为bili_jct的值
103
+ if (cookieData.key === 'bili_jct')
104
+ csrf = cookieData.value;
105
+ // 创建一个完整的 Cookie 实例
106
+ const cookie = new tough_cookie_1.Cookie({
107
+ key: cookieData.key,
108
+ value: cookieData.value,
109
+ expires: new Date(cookieData.expires),
110
+ domain: cookieData.domain,
111
+ path: cookieData.path,
112
+ secure: cookieData.secure,
113
+ httpOnly: cookieData.httpOnly,
114
+ sameSite: cookieData.sameSite
115
+ });
116
+ this.jar.setCookieSync(cookie, `http${cookie.secure ? 's' : ''}://${cookie.domain}${cookie.path}`, {});
117
+ });
118
+ // 解密refresh_token
119
+ const decryptedRefreshToken = this.ctx.wbi.decrypt(data.bili_refresh_token);
120
+ // Check if token need refresh
121
+ this.checkIfTokenNeedRefresh(decryptedRefreshToken, csrf);
122
+ }
123
+ async checkIfTokenNeedRefresh(refreshToken, csrf) {
124
+ const { data } = await this.getCookieInfo(refreshToken);
125
+ // 不需要刷新,直接返回
126
+ if (!data.refresh)
127
+ return;
128
+ const publicKey = await crypto.subtle.importKey("jwk", {
129
+ kty: "RSA",
130
+ n: "y4HdjgJHBlbaBN04VERG4qNBIFHP6a3GozCl75AihQloSWCXC5HDNgyinEnhaQ_4-gaMud_GF50elYXLlCToR9se9Z8z433U3KjM-3Yx7ptKkmQNAMggQwAVKgq3zYAoidNEWuxpkY_mAitTSRLnsJW-NCTa0bqBFF6Wm1MxgfE",
131
+ e: "AQAB",
132
+ }, { name: "RSA-OAEP", hash: "SHA-256" }, true, ["encrypt"]);
133
+ async function getCorrespondPath(timestamp) {
134
+ const data = new TextEncoder().encode(`refresh_${timestamp}`);
135
+ const encrypted = new Uint8Array(await crypto.subtle.encrypt({ name: "RSA-OAEP" }, publicKey, data));
136
+ return encrypted.reduce((str, c) => str + c.toString(16).padStart(2, "0"), "");
137
+ }
138
+ const correspondPath = await getCorrespondPath(Date.now());
139
+ const { data: refreshCsrfHtml } = await this.client.get(`https://www.bilibili.com/correspond/1/${correspondPath}`);
140
+ // 创建一个虚拟的DOM元素
141
+ const { document } = new jsdom_1.JSDOM(refreshCsrfHtml).window;
142
+ // 提取标签name为1-name的内容
143
+ const targetElement = document.getElementById('1-name');
144
+ const refresh_csrf = targetElement ? targetElement.textContent : null;
145
+ // 发送刷新请求
146
+ const { data: refreshData } = await this.client.post('https://passport.bilibili.com/x/passport-login/web/cookie/refresh', {
147
+ csrf,
148
+ refresh_csrf,
149
+ source: 'main_web',
150
+ refresh_token: refreshToken
151
+ });
152
+ // 检查是否有其他问题
153
+ switch (refreshData.code) {
154
+ // 账号未登录
155
+ case -101: return this.createNewClient();
156
+ case -111: throw new Error('csrf 校验失败');
157
+ case 86095: throw new Error('refresh_csrf 错误或 refresh_token 与 cookie 不匹配');
158
+ }
159
+ // 更新refresh_token
160
+ await this.ctx.database.upsert('loginBili', [{
161
+ id: 1,
162
+ bili_refresh_token: refreshData.data.refresh_token
163
+ }]);
164
+ // Get new csrf from cookies
165
+ let newCsrf;
166
+ this.jar.store.getAllCookies((err, c) => {
167
+ if (err)
168
+ throw err;
169
+ c.forEach(cookie => {
170
+ if (cookie.key === 'bili_jct')
171
+ newCsrf = cookie.value;
172
+ });
173
+ });
174
+ // Accept update
175
+ const { data: aceeptData } = await this.client.post('https://passport.bilibili.com/x/passport-login/web/confirm/refresh', {
176
+ csrf: newCsrf,
177
+ refresh_token: refreshToken
178
+ });
179
+ // 检查是否有其他问题
180
+ switch (aceeptData.code) {
181
+ case -111: throw new Error('csrf 校验失败');
182
+ case -400: throw new Error('请求错误');
183
+ }
184
+ // 没有问题,cookies已更新完成
185
+ }
186
+ }
187
+ /* namespace LiveAPI {
188
+ export interface Config {
189
+ roomId: string
190
+ }
191
+
192
+ export const Config: Schema<Config> = Schema.object({
193
+ roomId: Schema.string().required()
194
+ })
195
+ } */
196
+ exports.default = BiliAPI;
@@ -0,0 +1,17 @@
1
+ import { Context, Logger, Schema, Session } from "koishi";
2
+ declare class ComRegister {
3
+ static inject: string[];
4
+ logger: Logger;
5
+ config: ComRegister.Config;
6
+ constructor(ctx: Context, config: ComRegister.Config);
7
+ dynamicDetect(ctx: Context, session: Session, uid: string, dispose: Function): () => Promise<"账号未登录" | "未知错误">;
8
+ liveDetect(ctx: Context, session: Session, roomId: string, dispose: Function): () => Promise<void>;
9
+ checkIfNeedSub(comNeed: boolean, subType: string, session: Session, data?: any): Promise<boolean>;
10
+ }
11
+ declare namespace ComRegister {
12
+ interface Config {
13
+ pushTime: number;
14
+ }
15
+ const Config: Schema<Config>;
16
+ }
17
+ export default ComRegister;
@@ -0,0 +1,402 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const koishi_1 = require("koishi");
7
+ // 导入qrcode
8
+ const qrcode_1 = __importDefault(require("qrcode"));
9
+ var LiveType;
10
+ (function (LiveType) {
11
+ LiveType[LiveType["NotLiveBroadcast"] = 0] = "NotLiveBroadcast";
12
+ LiveType[LiveType["StartBroadcasting"] = 1] = "StartBroadcasting";
13
+ LiveType[LiveType["LiveBroadcast"] = 2] = "LiveBroadcast";
14
+ })(LiveType || (LiveType = {}));
15
+ class ComRegister {
16
+ static inject = ['biliAPI', 'gimg', 'wbi'];
17
+ logger;
18
+ config;
19
+ constructor(ctx, config) {
20
+ this.logger = ctx.logger('commandRegister');
21
+ this.config = config;
22
+ ctx.command('test')
23
+ .subcommand('.cookies')
24
+ .usage('测试指令,用于测试从数据库读取cookies')
25
+ .action(async () => {
26
+ await ctx.biliAPI.loadCookiesFromDatabase();
27
+ });
28
+ ctx.command('test')
29
+ .subcommand('.my')
30
+ .usage('测试指令,用于测试获取自己信息')
31
+ .example('test.my')
32
+ .action(async () => {
33
+ const content = await ctx.biliAPI.getMyselfInfo();
34
+ console.log(content);
35
+ });
36
+ ctx.command('test')
37
+ .subcommand('.user <mid:string>')
38
+ .usage('测试指令,用于测试获取用户信息')
39
+ .example('test.user 用户UID')
40
+ .action(async (_, mid) => {
41
+ const content = await ctx.biliAPI.getUserInfo(mid);
42
+ console.log(content);
43
+ });
44
+ ctx.command('test')
45
+ .subcommand('.time')
46
+ .usage('测试时间接口')
47
+ .example('test.time')
48
+ .action(async () => {
49
+ const content = await ctx.biliAPI.getTimeNow();
50
+ console.log(content);
51
+ });
52
+ ctx.command('test')
53
+ .subcommand('.exist')
54
+ .usage('测试写法')
55
+ .example('test.exist')
56
+ .action(async () => {
57
+ let num = 1;
58
+ console.log(num && `Hello World`);
59
+ });
60
+ ctx.command('test')
61
+ .subcommand('.gimg <uid:string> <index:number>')
62
+ .usage('测试图片生成')
63
+ .example('test.gimg')
64
+ .action(async (_, uid, index) => {
65
+ // 获取用户空间动态数据
66
+ const { data } = await ctx.biliAPI.getUserSpaceDynamic(uid);
67
+ const [pic] = await ctx.gimg.generateDynamicImg(data.items[index]);
68
+ return pic;
69
+ });
70
+ ctx.command('bili', 'bili-notify插件相关指令')
71
+ .subcommand('.login', '登录B站之后才可以进行之后的操作')
72
+ .usage('使用二维码登录,登录B站之后才可以进行之后的操作')
73
+ .example('bili login')
74
+ .action(async ({ session }) => {
75
+ this.logger.info('调用bili login指令');
76
+ // 获取二维码
77
+ const content = await ctx.biliAPI.getLoginQRCode();
78
+ // 判断是否出问题
79
+ if (content.code !== 0)
80
+ return await session.send('出问题咯,请联系管理员解决!');
81
+ // 设置二维码参数
82
+ let options = {
83
+ errorCorrectionLevel: 'H', // 错误更正水平
84
+ type: 'image/png', // 输出类型
85
+ quality: 0.92, // 图像质量(仅适用于'image/jpeg')
86
+ margin: 1, // 边距大小
87
+ color: {
88
+ dark: '#000000', // 二维码颜色
89
+ light: '#FFFFFF' // 背景颜色
90
+ }
91
+ };
92
+ // 生成二维码
93
+ qrcode_1.default.toBuffer(content.data.url, options, async (err, buffer) => {
94
+ if (err)
95
+ return await session.send('二维码生成出错,请联系管理员解决!');
96
+ await session.send(koishi_1.h.image(buffer, 'image/png'));
97
+ });
98
+ // 定义定时器
99
+ let dispose;
100
+ // 发起登录请求检查登录状态
101
+ dispose = ctx.setInterval(async () => {
102
+ const loginContent = await ctx.biliAPI.getLoginStatus(content.data.qrcode_key);
103
+ if (loginContent.code !== 0) {
104
+ dispose();
105
+ return await session.send('登录失败!请联系管理员解决!');
106
+ }
107
+ if (loginContent.data.code === 86038) {
108
+ dispose();
109
+ return await session.send('二维码已失效,请重新登录!');
110
+ }
111
+ if (loginContent.data.code === 0) { // 登录成功
112
+ const encryptedCookies = ctx.wbi.encrypt(await ctx.biliAPI.getCookies());
113
+ const encryptedRefreshToken = ctx.wbi.encrypt(loginContent.data.refresh_token);
114
+ await ctx.database.upsert('loginBili', [{
115
+ id: 1,
116
+ bili_cookies: encryptedCookies,
117
+ bili_refresh_token: encryptedRefreshToken
118
+ }]);
119
+ dispose();
120
+ return await session.send('登录成功!');
121
+ }
122
+ }, 1000);
123
+ });
124
+ ctx.command('bili')
125
+ .subcommand('.sub <mid:string> [groupid:string]')
126
+ .option('live', '-l')
127
+ .option('dynamic', '-d')
128
+ .usage('订阅用户动态和直播通知,若需要订阅直播请加上-l,需要订阅动态则加上-d。若没有加任何参数,之后会向你单独询问,<>中为必选参数,[]中为可选参数,目标群号若不填,则默认为当前群聊')
129
+ .example('bili sub 用户uid 目标QQ群号(可选) -l -d')
130
+ .action(async ({ session, options }, mid, groupid) => {
131
+ this.logger.info('调用bili.sub指令');
132
+ // 检查必选参数是否有值
133
+ if (mid === undefined) {
134
+ return '请输入用户uid';
135
+ }
136
+ // 定义是否需要直播通知,动态订阅,视频推送
137
+ let liveMsg, dynamicMsg;
138
+ // 获取用户信息
139
+ const content = await ctx.biliAPI.getUserInfo(mid);
140
+ // 判断是否有其他问题
141
+ if (content.code !== 0) {
142
+ let msg;
143
+ switch (content.code) {
144
+ case -400:
145
+ msg = '请求错误';
146
+ break;
147
+ case -403:
148
+ msg = '访问权限不足,尝试重新登录,如果不行请联系管理员';
149
+ break;
150
+ case -404:
151
+ msg = '用户不存在';
152
+ break;
153
+ case -352:
154
+ msg = '请登录后再尝试订阅';
155
+ break;
156
+ default: msg = '未知错误,请联系管理员';
157
+ }
158
+ return msg;
159
+ }
160
+ // 获取data
161
+ const { data } = content;
162
+ // 判断是否需要订阅直播
163
+ liveMsg = await this.checkIfNeedSub(options.live, '直播', session, data);
164
+ // 判断是否需要订阅动态
165
+ dynamicMsg = await this.checkIfNeedSub(options.dynamic, '动态', session);
166
+ // console.log(liveMsg, dynamicMsg, videoMsg);
167
+ });
168
+ ctx.command('bili')
169
+ .subcommand('.dynamic <uid:string>')
170
+ .usage('订阅用户动态推送')
171
+ .example('bili dynamic 1')
172
+ .action(async ({ session }, uid) => {
173
+ this.logger.info('调用bili.dynamic指令');
174
+ let dispose;
175
+ // 开始循环检测
176
+ dispose = ctx.setInterval(this.dynamicDetect(ctx, session, uid, dispose), 60000);
177
+ });
178
+ ctx.command('bili')
179
+ .subcommand('.live <roomId:string>')
180
+ .usage('订阅主播开播通知')
181
+ .example('bili live 732')
182
+ .action(async ({ session }, roomId) => {
183
+ if (!roomId) {
184
+ await session.send('请输入需要订阅主播的房间号');
185
+ return;
186
+ }
187
+ this.logger.info('调用bili.live指令');
188
+ let dispose;
189
+ // 开始循环检测
190
+ dispose = ctx.setInterval(this.liveDetect(ctx, session, roomId, dispose), 5000);
191
+ });
192
+ ctx.command('bili')
193
+ .subcommand('.status <roomId:string>')
194
+ .usage('查询主播当前直播状态')
195
+ .example('bili status 732')
196
+ .action(async ({ session }, roomId) => {
197
+ this.logger.info('调用bili.status指令');
198
+ if (!roomId)
199
+ return session.send('请输入房间号!');
200
+ const content = await ctx.biliAPI.getLiveRoomInfo(roomId);
201
+ const { data } = content;
202
+ const { data: userData } = await ctx.biliAPI.getMasterInfo(data.uid);
203
+ // B站出问题了
204
+ if (content.code !== 0) {
205
+ if (content.msg === '未找到该房间') {
206
+ session.send('未找到该房间!');
207
+ }
208
+ else {
209
+ console.log(content);
210
+ session.send('未知错误,请呼叫管理员检查问题!');
211
+ }
212
+ return;
213
+ }
214
+ const string = await ctx.gimg.generateLiveImg(data, userData, data.live_status === 1 ?
215
+ LiveType.StartBroadcasting :
216
+ LiveType.NotLiveBroadcast);
217
+ session.send(string);
218
+ });
219
+ }
220
+ dynamicDetect(ctx, session, uid, dispose) {
221
+ let firstSubscription = true;
222
+ let timePoint;
223
+ return async () => {
224
+ // 第一次的判断
225
+ if (firstSubscription) {
226
+ // 获取用户信息
227
+ const { data: userData } = await ctx.biliAPI.getMasterInfo(uid);
228
+ // 发送订阅消息通知
229
+ session.send(`订阅${userData.info.uname}动态通知!`);
230
+ // 设置第一次的时间点
231
+ timePoint = (await ctx.biliAPI.getTimeNow()).data.now;
232
+ console.log(timePoint);
233
+ // 设置第一次为false
234
+ firstSubscription = false;
235
+ return;
236
+ }
237
+ // 获取用户空间动态数据
238
+ const content = await ctx.biliAPI.getUserSpaceDynamic(uid);
239
+ // 判断是否出现其他问题
240
+ if (content.code !== 0) {
241
+ switch (content.code) {
242
+ case -101: { // 账号未登录
243
+ return '账号未登录';
244
+ }
245
+ default: { // 未知错误
246
+ return '未知错误';
247
+ }
248
+ }
249
+ }
250
+ // 获取数据内容
251
+ const items = content.data.items;
252
+ // 发送请求 只查看前五条数据
253
+ for (let num = 4; num >= 0; num--) {
254
+ // 没有动态内容则直接跳过
255
+ if (!items[num])
256
+ continue;
257
+ // 寻找发布时间比时间点时间更晚的动态
258
+ if (items[num].modules.module_author.pub_ts > timePoint) {
259
+ // 将时间点设置为这条动态的发布时间
260
+ timePoint = items[num].modules.module_author.pub_ts;
261
+ // 推送该条动态
262
+ const [pic, _] = await ctx.gimg.generateDynamicImg(items[num]);
263
+ session.send(pic);
264
+ }
265
+ }
266
+ };
267
+ }
268
+ liveDetect(ctx, session, roomId, dispose) {
269
+ let firstSubscription = true;
270
+ let timer = 0;
271
+ let open = false;
272
+ let liveTime;
273
+ let uData;
274
+ return async () => {
275
+ // Test code
276
+ /* console.log('===========================>');
277
+ console.log(timer);
278
+ console.log(open);
279
+ console.log('===========================>'); */
280
+ // 发送请求检测直播状态
281
+ const content = await ctx.biliAPI.getLiveRoomInfo(roomId);
282
+ const { data } = content;
283
+ // B站出问题了
284
+ if (content.code !== 0) {
285
+ if (content.msg === '未找到该房间') {
286
+ session.send('未找到该房间!');
287
+ }
288
+ else {
289
+ console.log(content);
290
+ session.send('未知错误,请呼叫管理员检查问题!');
291
+ }
292
+ dispose();
293
+ return;
294
+ }
295
+ if (firstSubscription) {
296
+ firstSubscription = false;
297
+ // 获取主播信息
298
+ const { data: userData } = await ctx.biliAPI.getMasterInfo(data.uid);
299
+ // 主播信息不会变,请求一次即可
300
+ uData = userData;
301
+ // 发送订阅消息通知
302
+ session.send(`订阅${userData.info.uname}直播通知`);
303
+ if (data.live_status === 1) { // 当前正在直播
304
+ // 改变开播状态
305
+ open = true;
306
+ // 推送直播信息
307
+ await session.send(await ctx
308
+ .gimg
309
+ .generateLiveImg(data, uData, LiveType.LiveBroadcast));
310
+ } // 未开播,直接返回
311
+ return;
312
+ }
313
+ // 检查直播状态
314
+ switch (data.live_status) {
315
+ case 0:
316
+ case 2: { // 状态 0 和 2 说明未开播
317
+ if (open) { // 之前开播,现在下播了
318
+ // 更改直播状态
319
+ open = false;
320
+ // 下播了将定时器清零
321
+ timer = 0;
322
+ // 发送下播通知
323
+ session.send(`${uData.info.uname}下播啦,本次直播了${ctx.gimg.getTimeDifference(liveTime)}`);
324
+ }
325
+ // 未进循环,还未开播,继续循环
326
+ break;
327
+ }
328
+ case 1: {
329
+ if (!open) { // 之前未开播,现在开播了
330
+ // 更改直播状态
331
+ open = true;
332
+ // 设置开播时间
333
+ liveTime = data.live_time;
334
+ // 获取主播信息
335
+ const { data: userData } = await ctx.biliAPI.getMasterInfo(data.uid);
336
+ // 主播信息不会变,开播时刷新一次即可
337
+ uData = userData;
338
+ // 发送直播通知
339
+ await session.send(await ctx.gimg.generateLiveImg(data, uData, LiveType.StartBroadcasting));
340
+ await session.send(`https://live.bilibili.com/${roomId}`);
341
+ }
342
+ else { // 还在直播
343
+ if (this.config.pushTime > 0) {
344
+ timer++;
345
+ // 开始记录时间
346
+ if (timer >= (12 * 30 * this.config.pushTime)) { // 到时间推送直播消息
347
+ // 到时间重新计时
348
+ timer = 0;
349
+ // 发送状态信息
350
+ session.send(await ctx
351
+ .gimg
352
+ .generateLiveImg(data, uData, LiveType.LiveBroadcast));
353
+ await session.send(`https://live.bilibili.com/${roomId}`);
354
+ }
355
+ }
356
+ // 否则继续循环
357
+ }
358
+ }
359
+ }
360
+ };
361
+ }
362
+ async checkIfNeedSub(comNeed, subType, session, data) {
363
+ if (comNeed) {
364
+ if (subType === '直播' && data.live_room.roomStatus === 0) {
365
+ return false;
366
+ }
367
+ return true;
368
+ }
369
+ let input; // 用户输入
370
+ // 询问用户是否需要订阅直播
371
+ while (1) {
372
+ session.send(`是否需要订阅${subType}?需要输入 y 不需要输入 n `);
373
+ input = await session.prompt();
374
+ if (!input) {
375
+ await session.send('输入超时!请重新订阅');
376
+ continue;
377
+ }
378
+ switch (input) {
379
+ case 'y': { // 需要订阅直播
380
+ // 如果用户没有开通直播间则无法订阅
381
+ if (subType === '直播' && data.live_room.roomStatus === 0) {
382
+ await session.send('该用户没有开通直播间,无法订阅直播');
383
+ return false;
384
+ }
385
+ // 开启直播订阅
386
+ return true;
387
+ }
388
+ // 不需要
389
+ case 'n': return false;
390
+ default: { // 输入了其他的内容
391
+ session.send('输入有误,请输入 y 或 n');
392
+ }
393
+ }
394
+ }
395
+ }
396
+ }
397
+ (function (ComRegister) {
398
+ ComRegister.Config = koishi_1.Schema.object({
399
+ pushTime: koishi_1.Schema.number().required()
400
+ });
401
+ })(ComRegister || (ComRegister = {}));
402
+ exports.default = ComRegister;
@@ -0,0 +1,23 @@
1
+ import { Context } from "koishi";
2
+ declare module 'koishi' {
3
+ interface Tables {
4
+ bilibili: Bilibili;
5
+ loginBili: LoginBili;
6
+ }
7
+ }
8
+ export interface Bilibili {
9
+ id: number;
10
+ uid: string;
11
+ room_id: string;
12
+ dynamic: number;
13
+ video: number;
14
+ live: number;
15
+ time: Date;
16
+ }
17
+ export interface LoginBili {
18
+ id: number;
19
+ bili_cookies: string;
20
+ bili_refresh_token: string;
21
+ }
22
+ export declare const name = "Database";
23
+ export declare function apply(ctx: Context): void;