yz-yuki-plugin 2.0.8-9 → 2.0.9-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.
- package/CHANGELOG.md +3 -0
- package/README.md +0 -3
- package/defaultConfig/bilibili/config.yaml +1 -0
- package/defaultConfig/help/help.yaml +0 -9
- package/defaultConfig/weibo/config.yaml +1 -0
- package/lib/apps/bilibili.js +36 -50
- package/lib/apps/weibo.js +19 -105
- package/lib/components/dynamic/Account.js +4 -4
- package/lib/components/dynamic/Content.js +4 -4
- package/lib/components/dynamic/Footer.js +4 -4
- package/lib/components/dynamic/ForwardContent.js +2 -2
- package/lib/components/dynamic/LogoText.js +2 -2
- package/lib/components/dynamic/MainPage.js +2 -2
- package/lib/components/help/Help.js +3 -3
- package/lib/components/loginQrcode/Page.js +2 -2
- package/lib/components/version/Version.js +2 -2
- package/lib/models/bilibili/bilibili.main.get.web.data.js +130 -59
- package/lib/models/bilibili/bilibili.main.query.js +16 -8
- package/lib/models/bilibili/bilibili.main.task.js +24 -7
- package/lib/models/bilibili/bilibili.risk.cookie.js +799 -0
- package/lib/models/weibo/weibo.main.api.js +7 -10
- package/lib/models/weibo/weibo.main.get.web.data.js +127 -69
- package/lib/models/weibo/weibo.risk.cookie.js +940 -0
- package/package.json +5 -2
- package/resources/css/dynamic/MainPage.css +1 -1
- package/lib/models/bilibili/bilibili.main.models.js +0 -493
- package/lib/models/weibo/weibo.main.models.js +0 -347
|
@@ -1,347 +0,0 @@
|
|
|
1
|
-
import WeiboApi from './weibo.main.api.js';
|
|
2
|
-
import { getRidFromBd } from './weibo.risk.bd.rid.js';
|
|
3
|
-
import { parse, serialize } from 'cookie';
|
|
4
|
-
import lodash from 'lodash';
|
|
5
|
-
import moment from 'moment';
|
|
6
|
-
import fetch from 'node-fetch';
|
|
7
|
-
import { Segment, Redis } from 'yunzaijs';
|
|
8
|
-
|
|
9
|
-
class WeiboMainModels {
|
|
10
|
-
/**
|
|
11
|
-
* *******************************************************************
|
|
12
|
-
* Login 相关
|
|
13
|
-
* *******************************************************************
|
|
14
|
-
*/
|
|
15
|
-
/**申请登陆二维码(web端) */
|
|
16
|
-
static async applyLoginQRCode(e) {
|
|
17
|
-
const response = await fetch('https://passport.weibo.com/sso/signin?entry=wapsso&source=wapssowb&url=https://m.weibo.cn/', {
|
|
18
|
-
method: 'GET',
|
|
19
|
-
headers: lodash.merge(WeiboApi.WEIBO_GET_X_CSRF_TOKEN_HEADERS, { Host: 'passport.weibo.com' }),
|
|
20
|
-
redirect: 'follow'
|
|
21
|
-
});
|
|
22
|
-
const setCookie = response.headers.get('set-cookie');
|
|
23
|
-
const tokenMatch = setCookie?.match(/X-CSRF-TOKEN=([^;]+)/);
|
|
24
|
-
const X_CSRF_TOKEN = tokenMatch ? tokenMatch[1].replace(/X-CSRF-TOKEN=/g, '') : null;
|
|
25
|
-
if (X_CSRF_TOKEN) {
|
|
26
|
-
const resData = (await fetch('https://passport.weibo.com/sso/v2/qrcode/image?entry=wapsso&size=180', {
|
|
27
|
-
method: 'GET',
|
|
28
|
-
headers: lodash.merge(WeiboApi.WEIBO_LOGIN_QR_CODE_HEADERS, {
|
|
29
|
-
'Host': 'passport.weibo.com',
|
|
30
|
-
'X-CSRF-TOKEN': X_CSRF_TOKEN,
|
|
31
|
-
'Cookie': `X-CSRF-TOKEN=${X_CSRF_TOKEN}`
|
|
32
|
-
}),
|
|
33
|
-
redirect: 'follow'
|
|
34
|
-
}).then(res => res.json()));
|
|
35
|
-
if (resData?.retcode === 20000000) {
|
|
36
|
-
const qrid = resData?.data?.qrid;
|
|
37
|
-
const qrcodeUrl = resData?.data?.image;
|
|
38
|
-
let msg = [];
|
|
39
|
-
if (qrid && qrcodeUrl) {
|
|
40
|
-
const imgResponse = await fetch(qrcodeUrl, {
|
|
41
|
-
method: 'GET',
|
|
42
|
-
headers: WeiboApi.WEIBO_LOGIN_QR_CODE_IMAGE_HEADERS,
|
|
43
|
-
redirect: 'follow'
|
|
44
|
-
});
|
|
45
|
-
if (!imgResponse.ok) {
|
|
46
|
-
logger.error(`获取微博登录二维码失败: ${imgResponse.status}`);
|
|
47
|
-
throw new Error(`获取微博登录图片失败,状态码: ${imgResponse.status}`);
|
|
48
|
-
}
|
|
49
|
-
// 等待3秒再获取rid
|
|
50
|
-
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
51
|
-
const ridData = await getRidFromBd(X_CSRF_TOKEN);
|
|
52
|
-
if (ridData?.retcode === 20000000) {
|
|
53
|
-
const rid = ridData?.data?.rid;
|
|
54
|
-
const arrayBuffer = await imgResponse.arrayBuffer();
|
|
55
|
-
msg.push(Segment.image(Buffer.from(arrayBuffer)));
|
|
56
|
-
e.reply('请在3分钟内扫码以完成微博登陆绑定');
|
|
57
|
-
e.reply(msg);
|
|
58
|
-
logger.info(`优纪插件: 如果发送二维码图片消息失败可复制如下URL, 浏览器访问此URL查看二维码并扫码`);
|
|
59
|
-
logger.info(`优纪插件: 微博登陆二维码URL: ${qrcodeUrl}`);
|
|
60
|
-
return { qrid, rid, X_CSRF_TOKEN };
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
logger.error('微博登录:获取rid失败');
|
|
64
|
-
e.reply(`获取微博登录rid密钥失败: ${JSON.stringify(ridData)},\n接口逆向进度受阻,未完成,无法登录。\n无妨,已切换启用临时ck`);
|
|
65
|
-
throw new Error(`获取微博登录rid密钥失败: ${JSON.stringify(ridData)},\n接口逆向进度受阻,未完成,无法登录。\n无妨,已切换启用临时ck`);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
e.reply(`获取微博登录二维码失败: ${JSON.stringify(resData)}`);
|
|
71
|
-
throw new Error(`获取微博登录二维码失败: ${JSON.stringify(resData)}`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
logger.error('微博登录:获取X_CSRF_TOKEN失败');
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
/**处理扫码结果 */
|
|
80
|
-
static async pollLoginQRCode(e, qrid, rid, X_CSRF_TOKEN) {
|
|
81
|
-
const url = `https://passport.weibo.com/sso/v2/qrcode/check?entry=wapsso&source=wapssowb&url=https://m.weibo.cn/&qrid=${qrid}&rid=${rid}&ver=20250520`;
|
|
82
|
-
const response = await fetch(url, {
|
|
83
|
-
method: 'GET',
|
|
84
|
-
headers: lodash.merge(WeiboApi.WEIBO_POLL_LOGIN_STATUS_HEADERS, {
|
|
85
|
-
'X-CSRF-TOKEN': X_CSRF_TOKEN,
|
|
86
|
-
'Cookie': `X-CSRF-TOKEN=${X_CSRF_TOKEN}`
|
|
87
|
-
}),
|
|
88
|
-
redirect: 'follow'
|
|
89
|
-
});
|
|
90
|
-
if (!response.ok) {
|
|
91
|
-
throw new Error(`处理B站登录token网络请求失败,状态码: ${response.status}`);
|
|
92
|
-
}
|
|
93
|
-
const data = (await response.json());
|
|
94
|
-
if (data?.retcode === 20000000) {
|
|
95
|
-
// 获取 cookie url
|
|
96
|
-
const getCookieUrl = data?.data?.url;
|
|
97
|
-
if (getCookieUrl) {
|
|
98
|
-
const CookieResp = await fetch(getCookieUrl, {
|
|
99
|
-
method: 'GET',
|
|
100
|
-
headers: lodash.merge(WeiboApi.WEIBO_COOKIE_HEADERS, { Cookie: `X-CSRF-TOKEN=${X_CSRF_TOKEN}` }),
|
|
101
|
-
redirect: 'follow'
|
|
102
|
-
});
|
|
103
|
-
const setCookie = CookieResp.headers.get('set-cookie');
|
|
104
|
-
if (setCookie) {
|
|
105
|
-
await this.saveLoginCookie(e, setCookie);
|
|
106
|
-
e.reply(`~微博登陆成功~`);
|
|
107
|
-
return setCookie;
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
e.reply(`获取微博登录Cookie失败: ${JSON.stringify(CookieResp.headers)}`);
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
else if (data?.retcode === 50114001) {
|
|
116
|
-
// 未扫码
|
|
117
|
-
// 继续轮询
|
|
118
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
119
|
-
global?.logger?.mark(`优纪插件:扫码微博登录:未扫码,轮询中...`);
|
|
120
|
-
return this.pollLoginQRCode(e, qrid, rid, X_CSRF_TOKEN);
|
|
121
|
-
}
|
|
122
|
-
else if (data?.retcode === 50114002) {
|
|
123
|
-
// 已扫码未确认
|
|
124
|
-
// 继续轮询
|
|
125
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
126
|
-
return this.pollLoginQRCode(e, qrid, rid, X_CSRF_TOKEN);
|
|
127
|
-
}
|
|
128
|
-
else if (data?.retcode === 50114003) {
|
|
129
|
-
// 二维码已失效
|
|
130
|
-
e.reply('微博登陆二维码已失效');
|
|
131
|
-
return null;
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
e.reply('处理微博登录扫码结果出错');
|
|
135
|
-
throw new Error(`处理微博登录扫码结果出错: ${JSON.stringify(data)}`);
|
|
136
|
-
}
|
|
137
|
-
return null;
|
|
138
|
-
}
|
|
139
|
-
/**查看app扫码登陆获取的ck的有效状态*/
|
|
140
|
-
static async checkWeiboLogin(e) {
|
|
141
|
-
const LoginCookie = await this.readLoginCookie();
|
|
142
|
-
if (String(LoginCookie).trim().length < 10) {
|
|
143
|
-
e.reply('啊咧?微博登录CK呢?哦,没 #扫码微博登录# 或缓存失效了啊,那没事了。');
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
const { cookie, mark } = await WeiboMainModels.readSyncCookie();
|
|
148
|
-
let COOKIE = '', XSRF_TOKEN = '';
|
|
149
|
-
if (String(cookie).includes('XSRF-TOKEN')) {
|
|
150
|
-
COOKIE = cookie;
|
|
151
|
-
XSRF_TOKEN = await WeiboMainModels.readSavedCookieItems(cookie, ['XSRF-TOKEN'], false);
|
|
152
|
-
}
|
|
153
|
-
const resData = (await fetch('https://m.weibo.cn/api/config', {
|
|
154
|
-
method: 'GET',
|
|
155
|
-
headers: lodash.merge(WeiboApi.WEIBO_HEADERS, {
|
|
156
|
-
'X-XSRF-TOKEN': `${XSRF_TOKEN}`,
|
|
157
|
-
'Cookie': `${COOKIE}`,
|
|
158
|
-
'Host': 'm.weibo.cn',
|
|
159
|
-
'Referer': 'https://m.weibo.cn'
|
|
160
|
-
}),
|
|
161
|
-
redirect: 'follow'
|
|
162
|
-
}).then(res => res.json()));
|
|
163
|
-
global?.logger?.debug(`微博验证登录状态:${JSON.stringify(resData)}`);
|
|
164
|
-
if (resData.data?.login === true) {
|
|
165
|
-
const uid = Number(resData.data.uid);
|
|
166
|
-
const user_token = resData.data.user_token;
|
|
167
|
-
const infoRes = (await fetch(`https://m.weibo.cn/profile/info?uid=${uid}`, {
|
|
168
|
-
method: 'GET',
|
|
169
|
-
headers: lodash.merge(WeiboApi.WEIBO_HEADERS, {
|
|
170
|
-
'X-XSRF-TOKEN': `${XSRF_TOKEN}`,
|
|
171
|
-
'Cookie': `${COOKIE}`,
|
|
172
|
-
'Host': 'm.weibo.cn',
|
|
173
|
-
'x-h5-user-token': `${user_token}`,
|
|
174
|
-
'Referer': `https://m.weibo.cn/profile/${uid}?user_token=${user_token}`
|
|
175
|
-
}),
|
|
176
|
-
redirect: 'follow'
|
|
177
|
-
}).then(res => res.json()));
|
|
178
|
-
let uname = infoRes.data?.user?.screen_name;
|
|
179
|
-
let mid = infoRes.data?.user?.id;
|
|
180
|
-
let follow_count = infoRes.data?.user?.follow_count;
|
|
181
|
-
let svip = infoRes.data?.user?.svip;
|
|
182
|
-
const cookie = await this.readLoginCookie();
|
|
183
|
-
const ALF = this.extractALFValue(cookie);
|
|
184
|
-
let TLL = 0;
|
|
185
|
-
if (ALF) {
|
|
186
|
-
TLL = Number(ALF) * 1000;
|
|
187
|
-
}
|
|
188
|
-
const EXP_TIME = moment(TLL * 1000).format('YYYY年MM月DD日 HH:mm:ss');
|
|
189
|
-
e.reply(`~微博账号已登陆~\n昵称:${uname}\nuid:${mid}\nsvip等级:${svip}\n关注:${follow_count}\n登录失效时间:${EXP_TIME}`);
|
|
190
|
-
}
|
|
191
|
-
else {
|
|
192
|
-
// 处理其他情况
|
|
193
|
-
e.reply('意外情况,未能获取微博登录ck的有效状态');
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
/** 获取ALF值*/
|
|
199
|
-
static extractALFValue(cookieString) {
|
|
200
|
-
const match = cookieString.match(/ALF=(\d+)/);
|
|
201
|
-
if (match && match[1]) {
|
|
202
|
-
return parseInt(match[1], 10);
|
|
203
|
-
}
|
|
204
|
-
return null;
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* *******************************************************************
|
|
208
|
-
* cookie相关
|
|
209
|
-
* *******************************************************************
|
|
210
|
-
*/
|
|
211
|
-
/**保存扫码登录的weiboLoginCK*/
|
|
212
|
-
static async saveLoginCookie(e, weiboLoginCk) {
|
|
213
|
-
if (weiboLoginCk && weiboLoginCk.length > 0) {
|
|
214
|
-
const weiboLoginCkKey = 'Yz:yuki:weibo:loginCookie';
|
|
215
|
-
Redis.set(weiboLoginCkKey, weiboLoginCk, { EX: 3600 * 24 * 300 });
|
|
216
|
-
}
|
|
217
|
-
else {
|
|
218
|
-
e.reply('扫码超时');
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
/** 读取扫码登陆后缓存的weiboCookie */
|
|
222
|
-
static async readLoginCookie() {
|
|
223
|
-
const CK_KEY = 'Yz:yuki:weibo:loginCookie';
|
|
224
|
-
const tempCk = await Redis.get(CK_KEY);
|
|
225
|
-
return tempCk ? tempCk : '';
|
|
226
|
-
}
|
|
227
|
-
/** 读取手动绑定的weibo CK */
|
|
228
|
-
static async readLocalBiliCk() {
|
|
229
|
-
const CK_KEY = 'Yz:yuki:weibo:localCookie';
|
|
230
|
-
const tempCk = await Redis.get(CK_KEY);
|
|
231
|
-
return tempCk ? tempCk : '';
|
|
232
|
-
}
|
|
233
|
-
/** 覆盖保存手动获取绑定的weibo ck */
|
|
234
|
-
static async saveLocalBiliCk(data) {
|
|
235
|
-
const weiboLoginCkKey = 'Yz:yuki:weibo:localCookie';
|
|
236
|
-
Redis.set(weiboLoginCkKey, data, { EX: 3600 * 24 * 300 });
|
|
237
|
-
}
|
|
238
|
-
/** 读取扫码登陆后缓存的weibo cookie的有效时间 */
|
|
239
|
-
static async readLoginCookieTTL() {
|
|
240
|
-
const CK_KEY = 'Yz:yuki:weibo:loginCookie';
|
|
241
|
-
const tempCk = await Redis.get(CK_KEY);
|
|
242
|
-
if (tempCk) {
|
|
243
|
-
const LoginCookieTTL = await Redis.ttl(CK_KEY);
|
|
244
|
-
return LoginCookieTTL;
|
|
245
|
-
}
|
|
246
|
-
else {
|
|
247
|
-
return -2;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
/** 综合获取ck,返回优先级:localCK > loginCK */
|
|
251
|
-
static async readSyncCookie() {
|
|
252
|
-
const localCk = await this.readLocalBiliCk();
|
|
253
|
-
const loginCk = await this.readLoginCookie();
|
|
254
|
-
const validCk = (ck) => ck?.includes('SUB=') && ck?.includes('SUBP=');
|
|
255
|
-
if (validCk(localCk)) {
|
|
256
|
-
return { cookie: localCk, mark: 'localCk' };
|
|
257
|
-
}
|
|
258
|
-
else if (validCk(loginCk)) {
|
|
259
|
-
return { cookie: loginCk + ';', mark: 'loginCk' };
|
|
260
|
-
}
|
|
261
|
-
else {
|
|
262
|
-
return { cookie: '', mark: 'ckIsEmpty' };
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* 综合读取、筛选 传入的或本地或redis存储的cookie的item
|
|
267
|
-
* @param {string} mark 读取存储的CK类型,'localCK' 'loginCK' 或传入值 'xxx'并进行筛选
|
|
268
|
-
* @param {Array} items 选取获取CK的项 选全部值:items[0] = 'all' ,或选取其中的值 ['XSRF-TOKEN', 'SUB', 'SUBP', 'SRF', 'SCF', 'SRT', ' _T_WM', 'M_WEIBOCN_PARAMS', 'SSOLoginState','ALF']
|
|
269
|
-
* @param {boolean} isInverted 控制正取和反取,true为反取,默认为false正取
|
|
270
|
-
* @returns {string}
|
|
271
|
-
**/
|
|
272
|
-
static async readSavedCookieItems(mark, items, isInverted = false) {
|
|
273
|
-
let ckString;
|
|
274
|
-
switch (mark) {
|
|
275
|
-
case 'localCK':
|
|
276
|
-
ckString = await this.readLocalBiliCk();
|
|
277
|
-
break;
|
|
278
|
-
case 'loginCK':
|
|
279
|
-
ckString = await this.readLoginCookie();
|
|
280
|
-
break;
|
|
281
|
-
default:
|
|
282
|
-
ckString = mark;
|
|
283
|
-
}
|
|
284
|
-
const Wck = lodash.trim(ckString);
|
|
285
|
-
if (!Wck) {
|
|
286
|
-
return '';
|
|
287
|
-
}
|
|
288
|
-
if (items[0] === 'all') {
|
|
289
|
-
return Wck;
|
|
290
|
-
}
|
|
291
|
-
const cookiePairs = String(Wck)
|
|
292
|
-
.trim()
|
|
293
|
-
.match(/([a-zA-Z0-9_-]+)=([^;|,]+)/g) //正则 /(\w+)=([^;]+);/g 匹配 a=b 的内容,并分组为 [^;|,]+ 来匹配值,其中 [^;|,] 表示除了分号和,以外的任意字符
|
|
294
|
-
?.map(match => match.split('='))
|
|
295
|
-
.filter(([key, value]) => (isInverted ? !items.includes(key) : items.includes(key)) && value !== '')
|
|
296
|
-
.map(([key, value]) => `${key}=${value}`)
|
|
297
|
-
.join(';') || '';
|
|
298
|
-
return cookiePairs;
|
|
299
|
-
}
|
|
300
|
-
// 取反读取ck、筛选 传入的或本地或redis存储的cookie的item
|
|
301
|
-
static async readSavedCookieOtherItems(mark, items) {
|
|
302
|
-
return await this.readSavedCookieItems(mark, items, true);
|
|
303
|
-
}
|
|
304
|
-
/**更新cookie */
|
|
305
|
-
static async updateCookieWithSetCookie(setCookieHeaders, mark) {
|
|
306
|
-
// 1. 读取当前保存的 Cookie
|
|
307
|
-
const currentCookie = await this.readLoginCookie();
|
|
308
|
-
// 使用 Record<string, string | undefined> 类型接收解析结果
|
|
309
|
-
const cookieObj = currentCookie ? parse(currentCookie) : {};
|
|
310
|
-
// 2. 解析 Set-Cookie 响应头并更新 Cookie 对象
|
|
311
|
-
setCookieHeaders.forEach(header => {
|
|
312
|
-
const parsedCookie = parse(header.split(';')[0]);
|
|
313
|
-
Object.entries(parsedCookie).forEach(([key, value]) => {
|
|
314
|
-
if (value === 'deleted') {
|
|
315
|
-
delete cookieObj[key];
|
|
316
|
-
}
|
|
317
|
-
else if (value !== undefined) {
|
|
318
|
-
// 添加类型守卫
|
|
319
|
-
cookieObj[key] = value;
|
|
320
|
-
}
|
|
321
|
-
});
|
|
322
|
-
});
|
|
323
|
-
// 3. 过滤掉 undefined 值并重新组合为字符串
|
|
324
|
-
const validCookies = {};
|
|
325
|
-
Object.entries(cookieObj).forEach(([key, value]) => {
|
|
326
|
-
if (value !== undefined) {
|
|
327
|
-
validCookies[key] = value;
|
|
328
|
-
}
|
|
329
|
-
});
|
|
330
|
-
const updatedCookie = Object.entries(validCookies)
|
|
331
|
-
.map(([key, value]) => serialize(key, value))
|
|
332
|
-
.join('; ');
|
|
333
|
-
// 4. 保存更新后的 Cookie
|
|
334
|
-
if (updatedCookie) {
|
|
335
|
-
let key = '';
|
|
336
|
-
if (mark === 'loginCK') {
|
|
337
|
-
key = 'Yz:yuki:weibo:loginCookie';
|
|
338
|
-
}
|
|
339
|
-
else if (mark === 'localCK') {
|
|
340
|
-
key = 'Yz:yuki:weibo:localCookie';
|
|
341
|
-
}
|
|
342
|
-
Redis.set(key, updatedCookie, { EX: 3600 * 24 * 300 });
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
export { WeiboMainModels };
|