koishi-plugin-kkk 3.4.0 → 3.4.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
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# 更新日志
|
|
2
2
|
|
|
3
|
+
## 3.4.1
|
|
4
|
+
|
|
5
|
+
### 抖音解析失败时的提示看得懂了
|
|
6
|
+
|
|
7
|
+
- 抖音接口没返回数据时(常见原因:Cookie 过期、被风控,或者链接 / 作品已失效),以前会直接抛一个英文错误
|
|
8
|
+
TypeError: Cannot read properties of undefined (reading 'aweme_detail'),你收到的是一张满屏英文的错误卡片。
|
|
9
|
+
现在会明说是哪一类问题:抖音没有返回这条作品的数据(错误码 8:风控校验失败),常见原因:Cookie 失效、被风控,或者这条链接已经失效 / 作品已删除。
|
|
10
|
+
- 日志里会留下接口的原始错误码和返回内容,排查时一眼就能看出是风控还是 Cookie 过期。
|
|
11
|
+
- 顺带:抖音的冒烟测试补了「接口被风控」这个场景,以后不会再退回那种英文报错。
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
3
15
|
## 3.4.0
|
|
4
16
|
|
|
5
17
|
> 这一版修的都是「用起来别扭」的地方:图片不再重复发、面板不再和卡片挤在一起、
|
|
@@ -19,6 +31,7 @@
|
|
|
19
31
|
### 出错的时候:上报照旧,发什么你说了算
|
|
20
32
|
|
|
21
33
|
- 新增开关「**出错只发文字(不渲染错误卡片)**」(面板 → 通用 → 错误上报,默认关闭)。
|
|
34
|
+
想法来自 @Jingmozhiyu 的 PR #1
|
|
22
35
|
打开后出错只在群里发一行:错误信息已上传,ID:xxx —— 上传的东西一点不少,只是省掉十几秒渲染和一张几 MB 的大图。
|
|
23
36
|
- 以前错误提示里那个加群链接是拿群号拼的,点开是 404;现在换成真正的加群链接,能直接点进去。
|
|
24
37
|
- 以前遇到「一行日志里塞了几 MB 的图片数据」这种报错,错误卡片要渲染十几分钟、最后还发不出去;现在这种超长内容会自动折起来,几秒钟出图。
|
|
@@ -91,8 +91,24 @@ class DouYin extends utils_1.Base {
|
|
|
91
91
|
const VideoData = await this.amagi.douyin.fetcher.parseWork({
|
|
92
92
|
aweme_id: data.aweme_id
|
|
93
93
|
});
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
/**
|
|
95
|
+
* **接口没给出作品详情时的兜底**(线上真实报错):
|
|
96
|
+
*
|
|
97
|
+
* TypeError: Cannot read properties of undefined (reading 'aweme_detail')
|
|
98
|
+
* at DouYin.DouyinHandler (douyin.js:94)
|
|
99
|
+
*
|
|
100
|
+
* 以前只挡了「aweme_detail === null」这一种 —— 而接口被风控 / Cookie 失效 / 链接失效时,
|
|
101
|
+
* 返回体里**连 data 都没有**,于是直接 TypeError,用户拿到的是一张满屏英文的错误卡片。
|
|
102
|
+
* 现在把整段摊开检查,并给一句人话 + 把原始响应写进日志(排查时才看得到错误码)。
|
|
103
|
+
*/
|
|
104
|
+
if (!VideoData || !VideoData.data || VideoData.data.aweme_detail === null || VideoData.data.aweme_detail === undefined) {
|
|
105
|
+
const raw = VideoData ?? {};
|
|
106
|
+
const code = raw.code ?? raw.data?.code;
|
|
107
|
+
const message = raw.message ?? raw.msg ?? raw.data?.message ?? raw.data?.msg;
|
|
108
|
+
node_karin_2.logger.error('[抖音] 接口没有返回作品详情: ' + JSON.stringify({ code, message, 顶层键: Object.keys(raw) }).slice(0, 300));
|
|
109
|
+
throw new Error('抖音没有返回这条作品的数据'
|
|
110
|
+
+ (code !== undefined ? '(错误码 ' + String(code) + (message ? ':' + String(message) : '') + ')' : '')
|
|
111
|
+
+ ',常见原因:Cookie 失效、被风控,或者这条链接已经失效 / 作品已删除。');
|
|
96
112
|
}
|
|
97
113
|
// 根据 API 返回的数据判断作品类型,而不是依赖 URL
|
|
98
114
|
// aweme_type: 0=视频, 68=图集, 163=文章
|
package/package.json
CHANGED
package/scripts/smoke-douyin.cjs
CHANGED
|
@@ -40,6 +40,9 @@ const LONG_URL = 'https://www.douyin.com/video/7123456789012345678'
|
|
|
40
40
|
axios.get = async () => ({ request: { res: { responseUrl: LONG_URL } }, data: '' })
|
|
41
41
|
|
|
42
42
|
// 2) 替换 amagi 的客户端工厂(每个 Base 实例都会新建 client,必须包住工厂)
|
|
43
|
+
/** 由末尾的第二个场景打开:让 parseWork 返回「风控」响应 */
|
|
44
|
+
let windy = false
|
|
45
|
+
|
|
43
46
|
const amagi = require('@ikenxuan/amagi')
|
|
44
47
|
const realFactory = amagi.default
|
|
45
48
|
|
|
@@ -94,7 +97,10 @@ function makeDetail () {
|
|
|
94
97
|
|
|
95
98
|
amagi.default = function (options) {
|
|
96
99
|
const client = realFactory(options)
|
|
97
|
-
|
|
100
|
+
// windy = true 时模拟「接口被风控」:返回体里连 data 都没有(见文件末尾的第二个场景)
|
|
101
|
+
client.douyin.fetcher.parseWork = async () => (windy
|
|
102
|
+
? { success: false, code: 8, message: '风控校验失败' }
|
|
103
|
+
: { success: true, code: 200, message: 'OK', data: { aweme_detail: makeDetail() } })
|
|
98
104
|
client.douyin.fetcher.fetchWorkComments = async () => ({ success: true, code: 200, message: 'OK', data: { comments: [], cursor: 0, has_more: 0, total: 0 } })
|
|
99
105
|
client.douyin.fetcher.fetchEmojiList = async () => ({ success: true, code: 200, message: 'OK', data: { emoji_list: [] } })
|
|
100
106
|
client.douyin.fetcher.fetchUserProfile = async () => ({
|
|
@@ -134,7 +140,8 @@ const middlewares = []
|
|
|
134
140
|
const originalMiddleware = ctx.middleware.bind(ctx)
|
|
135
141
|
ctx.middleware = (fn, ...rest) => { middlewares.push(fn); return originalMiddleware(fn, ...rest) }
|
|
136
142
|
|
|
137
|
-
|
|
143
|
+
// parseDedupe: false —— 下面有两个场景会解析同一条链接,去重会把第二个场景挡掉
|
|
144
|
+
ctx.plugin(plugin, { dataPath: dataRoot, debug: true, masters: ['12345'], webui: false, parseDedupe: false })
|
|
138
145
|
|
|
139
146
|
const makeSession = (content) => ({
|
|
140
147
|
content, selfId: '10000', userId: '12345', guildId: '456', channelId: '456',
|
|
@@ -170,6 +177,27 @@ setTimeout(async () => {
|
|
|
170
177
|
return '[' + type + '] ' + attrs
|
|
171
178
|
}).join(' | ').slice(0, 400))
|
|
172
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* 第二个场景:**接口被风控**(返回体里连 data 都没有)。
|
|
182
|
+
*
|
|
183
|
+
* 线上真实报错:以前只挡了 aweme_detail === null,风控时直接
|
|
184
|
+
* TypeError: Cannot read properties of undefined (reading 'aweme_detail') —— 用户拿到一张满屏英文的错误卡片。
|
|
185
|
+
* 现在应当给一句人话(并且日志里留下错误码)。
|
|
186
|
+
*/
|
|
187
|
+
console.log('\n=== 抖音接口被风控时的提示 ===')
|
|
188
|
+
windy = true
|
|
189
|
+
const before = sent.length
|
|
190
|
+
try {
|
|
191
|
+
await dispatch('https://www.douyin.com/video/7123456789012345678')
|
|
192
|
+
console.log(' ❌ 期望它抛错(这样才会走错误提示),但它正常跑完了')
|
|
193
|
+
process.exitCode = 1
|
|
194
|
+
} catch (error) {
|
|
195
|
+
const message = String(error?.message ?? error)
|
|
196
|
+
const friendly = /抖音没有返回这条作品的数据/.test(message) && /风控校验失败/.test(message)
|
|
197
|
+
console.log((friendly ? ' ✅' : ' ❌') + ' 提示是人话: ' + message.slice(0, 140))
|
|
198
|
+
if (!friendly) process.exitCode = 1
|
|
199
|
+
if (sent.length !== before) console.log(' (顺带:还多发了 ' + (sent.length - before) + ' 条消息)')
|
|
200
|
+
}
|
|
173
201
|
} catch (error) {
|
|
174
202
|
console.error('抖音冒烟测试失败:', error && error.stack ? error.stack : error)
|
|
175
203
|
process.exitCode = 1
|
|
@@ -115,8 +115,24 @@ export class DouYin extends Base {
|
|
|
115
115
|
aweme_id: data.aweme_id
|
|
116
116
|
})
|
|
117
117
|
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
/**
|
|
119
|
+
* **接口没给出作品详情时的兜底**(线上真实报错):
|
|
120
|
+
*
|
|
121
|
+
* TypeError: Cannot read properties of undefined (reading 'aweme_detail')
|
|
122
|
+
* at DouYin.DouyinHandler (douyin.js:94)
|
|
123
|
+
*
|
|
124
|
+
* 以前只挡了「aweme_detail === null」这一种 —— 而接口被风控 / Cookie 失效 / 链接失效时,
|
|
125
|
+
* 返回体里**连 data 都没有**,于是直接 TypeError,用户拿到的是一张满屏英文的错误卡片。
|
|
126
|
+
* 现在把整段摊开检查,并给一句人话 + 把原始响应写进日志(排查时才看得到错误码)。
|
|
127
|
+
*/
|
|
128
|
+
if (!VideoData || !(VideoData as any).data || (VideoData as any).data.aweme_detail === null || (VideoData as any).data.aweme_detail === undefined) {
|
|
129
|
+
const raw: any = VideoData ?? {}
|
|
130
|
+
const code = raw.code ?? raw.data?.code
|
|
131
|
+
const message = raw.message ?? raw.msg ?? raw.data?.message ?? raw.data?.msg
|
|
132
|
+
logger.error('[抖音] 接口没有返回作品详情: ' + JSON.stringify({ code, message, 顶层键: Object.keys(raw) }).slice(0, 300))
|
|
133
|
+
throw new Error('抖音没有返回这条作品的数据'
|
|
134
|
+
+ (code !== undefined ? '(错误码 ' + String(code) + (message ? ':' + String(message) : '') + ')' : '')
|
|
135
|
+
+ ',常见原因:Cookie 失效、被风控,或者这条链接已经失效 / 作品已删除。')
|
|
120
136
|
}
|
|
121
137
|
// 根据 API 返回的数据判断作品类型,而不是依赖 URL
|
|
122
138
|
// aweme_type: 0=视频, 68=图集, 163=文章
|