@zhin.js/adapter-juejin 0.0.0-stage → 0.1.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/README.md +145 -2
- package/adapters/juejin/index.js +19 -0
- package/adapters/juejin/index.ts +22 -0
- package/commands/juejin-draft/drop/[id]/index.js +13 -0
- package/commands/juejin-draft/drop/[id]/index.ts +13 -0
- package/commands/juejin-draft/edit/[id]/index.js +16 -0
- package/commands/juejin-draft/edit/[id]/index.ts +15 -0
- package/commands/juejin-draft/list/index.js +12 -0
- package/commands/juejin-draft/list/index.ts +12 -0
- package/commands/juejin-draft/send/[id]/index.js +13 -0
- package/commands/juejin-draft/send/[id]/index.ts +13 -0
- package/commands/juejin-draft/show/[id]/index.js +13 -0
- package/commands/juejin-draft/show/[id]/index.ts +13 -0
- package/commands/juejin-login/status/index.js +12 -0
- package/commands/juejin-login/status/index.ts +12 -0
- package/lib/adapter.d.ts +22 -0
- package/lib/adapter.js +43 -0
- package/lib/client.d.ts +83 -0
- package/lib/client.js +192 -0
- package/lib/console.d.ts +16 -0
- package/lib/console.js +172 -0
- package/lib/drafts.d.ts +50 -0
- package/lib/drafts.js +137 -0
- package/lib/endpoint-directory.d.ts +16 -0
- package/lib/endpoint-directory.js +63 -0
- package/lib/endpoint.d.ts +86 -0
- package/lib/endpoint.js +440 -0
- package/lib/index.d.ts +11 -0
- package/lib/index.js +11 -0
- package/lib/juejin-runtime-state.d.ts +4 -0
- package/lib/juejin-runtime-state.js +3 -0
- package/lib/login-text.d.ts +5 -0
- package/lib/login-text.js +25 -0
- package/lib/protocol.d.ts +192 -0
- package/lib/protocol.js +214 -0
- package/lib/review.d.ts +29 -0
- package/lib/review.js +156 -0
- package/lib/self-profile.d.ts +9 -0
- package/lib/self-profile.js +14 -0
- package/lib/storage.d.ts +11 -0
- package/lib/storage.js +54 -0
- package/package.json +113 -3
- package/pages/index/CommentReview.js +140 -0
- package/pages/index/CommentReview.tsx +264 -0
- package/pages/index/index.js +18 -0
- package/pages/index/index.tsx +18 -0
- package/pages/index/messages.js +57 -0
- package/pages/index/messages.ts +59 -0
- package/pages/index/rpc.js +31 -0
- package/pages/index/rpc.ts +72 -0
- package/plugin.js +33 -0
- package/schema.json +485 -0
- package/src/adapter.ts +45 -0
- package/src/client.ts +247 -0
- package/src/console.ts +161 -0
- package/src/drafts.ts +180 -0
- package/src/endpoint-directory.ts +64 -0
- package/src/endpoint.ts +504 -0
- package/src/index.ts +50 -0
- package/src/juejin-runtime-state.ts +11 -0
- package/src/login-text.ts +28 -0
- package/src/protocol.ts +377 -0
- package/src/review.ts +149 -0
- package/src/self-profile.ts +20 -0
- package/src/storage.ts +52 -0
package/schema.json
ADDED
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"additionalProperties": false,
|
|
5
|
+
"properties": {
|
|
6
|
+
"apiBaseUrl": {
|
|
7
|
+
"type": "string",
|
|
8
|
+
"default": "https://api.juejin.cn"
|
|
9
|
+
},
|
|
10
|
+
"pollIntervalSec": {
|
|
11
|
+
"type": "number",
|
|
12
|
+
"minimum": 60,
|
|
13
|
+
"default": 60,
|
|
14
|
+
"description": "轮询间隔(秒),最小 60,实际会加 0–20% 随机抖动",
|
|
15
|
+
"x-i18n": {
|
|
16
|
+
"en": {
|
|
17
|
+
"description": "Polling interval in seconds, at least 60; adds 0–20% random jitter."
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"sendMode": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"enum": [
|
|
24
|
+
"review",
|
|
25
|
+
"off"
|
|
26
|
+
],
|
|
27
|
+
"default": "review",
|
|
28
|
+
"description": "出站模式:review=AI 回复只进草稿队列,master 在 Console 或用 juejin-draft send <id> 审批后才发出(默认);off=禁止出站。不提供自动直发",
|
|
29
|
+
"x-i18n": {
|
|
30
|
+
"en": {
|
|
31
|
+
"description": "Outbound mode: review (default) queues AI replies as drafts; a master approves each send on the Console page or with juejin-draft send. off disables outbound messages. Automatic direct replies are not supported."
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"minSendIntervalSec": {
|
|
36
|
+
"type": "number",
|
|
37
|
+
"minimum": 10,
|
|
38
|
+
"default": 30,
|
|
39
|
+
"description": "审批后两次真实发送的最小间隔(秒),最小 10;不足时拒绝发送,不排队",
|
|
40
|
+
"x-i18n": {
|
|
41
|
+
"en": {
|
|
42
|
+
"description": "Minimum interval between approved sends in seconds (at least 10). Sending too soon is rejected, not queued."
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"dataDir": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"default": "data/juejin",
|
|
49
|
+
"description": "本地数据目录:待审草稿存在 <dataDir>/<endpoint id>/drafts.json(权限 600,相对进程工作目录),重启不丢。不要放进仓库",
|
|
50
|
+
"x-i18n": {
|
|
51
|
+
"en": {
|
|
52
|
+
"description": "Local data directory. Pending drafts are stored in <dataDir>/<endpoint id>/drafts.json (mode 600, relative to the working directory) and survive restarts. Keep it out of the repository."
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"notify": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"additionalProperties": false,
|
|
59
|
+
"description": "有新草稿或登录失效时,主动发消息提醒到这个聊天会话(通常是你和机器人的私聊)。不填则只在 Console「掘金评论审批」页和日志里看到",
|
|
60
|
+
"x-i18n": {
|
|
61
|
+
"en": {
|
|
62
|
+
"description": "Chat conversation to notify when a new draft arrives or the login expires (usually your private chat with the bot). Without it, drafts only appear on the Console Juejin comment review page and in logs."
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"properties": {
|
|
66
|
+
"adapter": {
|
|
67
|
+
"type": "string",
|
|
68
|
+
"description": "适配器名,如 icqq、telegram",
|
|
69
|
+
"x-i18n": {
|
|
70
|
+
"en": {
|
|
71
|
+
"description": "Adapter name, such as icqq or telegram."
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"endpoint": {
|
|
76
|
+
"type": "string",
|
|
77
|
+
"description": "该适配器的 endpoint id",
|
|
78
|
+
"x-i18n": {
|
|
79
|
+
"en": {
|
|
80
|
+
"description": "Endpoint id of that adapter."
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"kind": {
|
|
85
|
+
"type": "string",
|
|
86
|
+
"enum": [
|
|
87
|
+
"private",
|
|
88
|
+
"group",
|
|
89
|
+
"channel"
|
|
90
|
+
],
|
|
91
|
+
"default": "private",
|
|
92
|
+
"description": "会话类型",
|
|
93
|
+
"x-i18n": {
|
|
94
|
+
"en": {
|
|
95
|
+
"description": "Conversation type."
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"id": {
|
|
100
|
+
"type": [
|
|
101
|
+
"string",
|
|
102
|
+
"number"
|
|
103
|
+
],
|
|
104
|
+
"description": "会话 id:私聊填你的账号,群聊填群号",
|
|
105
|
+
"x-i18n": {
|
|
106
|
+
"en": {
|
|
107
|
+
"description": "Conversation id: your account for a private chat, or the group id."
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
"required": [
|
|
113
|
+
"adapter",
|
|
114
|
+
"endpoint",
|
|
115
|
+
"id"
|
|
116
|
+
]
|
|
117
|
+
},
|
|
118
|
+
"articles": {
|
|
119
|
+
"type": "array",
|
|
120
|
+
"items": {
|
|
121
|
+
"type": [
|
|
122
|
+
"string"
|
|
123
|
+
],
|
|
124
|
+
"pattern": "^(?:[1-9][0-9]{0,19}|https?://(?:www\\.)?juejin\\.cn/post/[1-9][0-9]{0,19}(?:[/?#].*)?)$"
|
|
125
|
+
},
|
|
126
|
+
"description": "白名单文章:文章 id(请写成带引号的字符串)或 https://juejin.cn/post/<id> 链接。只轮询这些文章的评论,不做全站搜索;留空 = 不轮询",
|
|
127
|
+
"x-i18n": {
|
|
128
|
+
"en": {
|
|
129
|
+
"description": "Allowed articles: article IDs (quoted strings) or https://juejin.cn/post/<id> links. Only these articles are polled; there is no site-wide search. Empty means nothing is polled."
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
"master": {
|
|
134
|
+
"type": [
|
|
135
|
+
"string",
|
|
136
|
+
"number"
|
|
137
|
+
],
|
|
138
|
+
"description": "框架 master(掘金 uid)。endpoints[i].master 可逐项覆盖",
|
|
139
|
+
"x-i18n": {
|
|
140
|
+
"en": {
|
|
141
|
+
"description": "Framework masters (Juejin uid). endpoints[i].master can override this value."
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
"trusted": {
|
|
146
|
+
"type": "array",
|
|
147
|
+
"items": {
|
|
148
|
+
"type": [
|
|
149
|
+
"string",
|
|
150
|
+
"number"
|
|
151
|
+
],
|
|
152
|
+
"description": "信任的掘金用户 ID",
|
|
153
|
+
"x-i18n": {
|
|
154
|
+
"en": {
|
|
155
|
+
"description": "Trusted Juejin user ID"
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
"description": "框架 trusted 用户列表(掘金 uid)",
|
|
160
|
+
"x-i18n": {
|
|
161
|
+
"en": {
|
|
162
|
+
"description": "Framework trusted users (Juejin uid)."
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"commandPrefix": {
|
|
167
|
+
"type": "string",
|
|
168
|
+
"default": "",
|
|
169
|
+
"description": "命令前缀(默认 '' 无前缀,任意文本按命令匹配;如 '/' 要求 / 开头)。endpoints[i] 可逐项覆盖",
|
|
170
|
+
"x-i18n": {
|
|
171
|
+
"en": {
|
|
172
|
+
"description": "Command prefix. The default empty string allows unprefixed commands; '/' requires a leading slash. Each endpoints[i] can override it."
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
"endpoints": {
|
|
177
|
+
"type": "array",
|
|
178
|
+
"minItems": 1,
|
|
179
|
+
"description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(id 必填,其余覆盖顶层)",
|
|
180
|
+
"items": {
|
|
181
|
+
"type": "object",
|
|
182
|
+
"additionalProperties": false,
|
|
183
|
+
"properties": {
|
|
184
|
+
"id": {
|
|
185
|
+
"type": "string",
|
|
186
|
+
"description": "Endpoint 名称(本地标识,不是掘金账号)",
|
|
187
|
+
"x-i18n": {
|
|
188
|
+
"en": {
|
|
189
|
+
"description": "Local endpoint name, not a Juejin account ID."
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
"cookie": {
|
|
194
|
+
"type": "string",
|
|
195
|
+
"description": "掘金网页登录态 Cookie 整串(至少含 sessionid;以你的账号身份操作;请用 ${JUEJIN_COOKIE} 注入,切勿写入仓库)",
|
|
196
|
+
"x-i18n": {
|
|
197
|
+
"en": {
|
|
198
|
+
"description": "Full Juejin web login Cookie header (must include sessionid), acting as your account. Reference ${JUEJIN_COOKIE}; never commit it to the repository."
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
"articles": {
|
|
203
|
+
"type": "array",
|
|
204
|
+
"items": {
|
|
205
|
+
"type": [
|
|
206
|
+
"string"
|
|
207
|
+
],
|
|
208
|
+
"pattern": "^(?:[1-9][0-9]{0,19}|https?://(?:www\\.)?juejin\\.cn/post/[1-9][0-9]{0,19}(?:[/?#].*)?)$"
|
|
209
|
+
},
|
|
210
|
+
"description": "白名单文章:文章 id(请写成带引号的字符串)或 https://juejin.cn/post/<id> 链接。只轮询这些文章的评论,不做全站搜索;留空 = 不轮询",
|
|
211
|
+
"x-i18n": {
|
|
212
|
+
"en": {
|
|
213
|
+
"description": "Allowed articles: article IDs (quoted strings) or https://juejin.cn/post/<id> links. Only these articles are polled; there is no site-wide search. Empty means nothing is polled."
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
"pollIntervalSec": {
|
|
218
|
+
"type": "number",
|
|
219
|
+
"minimum": 60,
|
|
220
|
+
"default": 60,
|
|
221
|
+
"description": "轮询间隔(秒),最小 60,实际会加 0–20% 随机抖动",
|
|
222
|
+
"x-i18n": {
|
|
223
|
+
"en": {
|
|
224
|
+
"description": "Polling interval in seconds, at least 60; adds 0–20% random jitter."
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
"sendMode": {
|
|
229
|
+
"type": "string",
|
|
230
|
+
"enum": [
|
|
231
|
+
"review",
|
|
232
|
+
"off"
|
|
233
|
+
],
|
|
234
|
+
"default": "review",
|
|
235
|
+
"description": "出站模式:review=AI 回复只进草稿队列,master 在 Console 或用 juejin-draft send <id> 审批后才发出(默认);off=禁止出站。不提供自动直发",
|
|
236
|
+
"x-i18n": {
|
|
237
|
+
"en": {
|
|
238
|
+
"description": "Outbound mode: review (default) queues AI replies as drafts; a master approves each send on the Console page or with juejin-draft send. off disables outbound messages. Automatic direct replies are not supported."
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
"minSendIntervalSec": {
|
|
243
|
+
"type": "number",
|
|
244
|
+
"minimum": 10,
|
|
245
|
+
"default": 30,
|
|
246
|
+
"description": "审批后两次真实发送的最小间隔(秒),最小 10;不足时拒绝发送,不排队",
|
|
247
|
+
"x-i18n": {
|
|
248
|
+
"en": {
|
|
249
|
+
"description": "Minimum interval between approved sends in seconds (at least 10). Sending too soon is rejected, not queued."
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
"dataDir": {
|
|
254
|
+
"type": "string",
|
|
255
|
+
"default": "data/juejin",
|
|
256
|
+
"description": "本地数据目录:待审草稿存在 <dataDir>/<endpoint id>/drafts.json(权限 600,相对进程工作目录),重启不丢。不要放进仓库",
|
|
257
|
+
"x-i18n": {
|
|
258
|
+
"en": {
|
|
259
|
+
"description": "Local data directory. Pending drafts are stored in <dataDir>/<endpoint id>/drafts.json (mode 600, relative to the working directory) and survive restarts. Keep it out of the repository."
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
"notify": {
|
|
264
|
+
"type": "object",
|
|
265
|
+
"additionalProperties": false,
|
|
266
|
+
"description": "有新草稿或登录失效时,主动发消息提醒到这个聊天会话(通常是你和机器人的私聊)。不填则只在 Console「掘金评论审批」页和日志里看到",
|
|
267
|
+
"x-i18n": {
|
|
268
|
+
"en": {
|
|
269
|
+
"description": "Chat conversation to notify when a new draft arrives or the login expires (usually your private chat with the bot). Without it, drafts only appear on the Console Juejin comment review page and in logs."
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
"properties": {
|
|
273
|
+
"adapter": {
|
|
274
|
+
"type": "string",
|
|
275
|
+
"description": "适配器名,如 icqq、telegram",
|
|
276
|
+
"x-i18n": {
|
|
277
|
+
"en": {
|
|
278
|
+
"description": "Adapter name, such as icqq or telegram."
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
"endpoint": {
|
|
283
|
+
"type": "string",
|
|
284
|
+
"description": "该适配器的 endpoint id",
|
|
285
|
+
"x-i18n": {
|
|
286
|
+
"en": {
|
|
287
|
+
"description": "Endpoint id of that adapter."
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
"kind": {
|
|
292
|
+
"type": "string",
|
|
293
|
+
"enum": [
|
|
294
|
+
"private",
|
|
295
|
+
"group",
|
|
296
|
+
"channel"
|
|
297
|
+
],
|
|
298
|
+
"default": "private",
|
|
299
|
+
"description": "会话类型",
|
|
300
|
+
"x-i18n": {
|
|
301
|
+
"en": {
|
|
302
|
+
"description": "Conversation type."
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
"id": {
|
|
307
|
+
"type": [
|
|
308
|
+
"string",
|
|
309
|
+
"number"
|
|
310
|
+
],
|
|
311
|
+
"description": "会话 id:私聊填你的账号,群聊填群号",
|
|
312
|
+
"x-i18n": {
|
|
313
|
+
"en": {
|
|
314
|
+
"description": "Conversation id: your account for a private chat, or the group id."
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
"required": [
|
|
320
|
+
"adapter",
|
|
321
|
+
"endpoint",
|
|
322
|
+
"id"
|
|
323
|
+
]
|
|
324
|
+
},
|
|
325
|
+
"apiBaseUrl": {
|
|
326
|
+
"type": "string",
|
|
327
|
+
"default": "https://api.juejin.cn"
|
|
328
|
+
},
|
|
329
|
+
"master": {
|
|
330
|
+
"type": [
|
|
331
|
+
"string",
|
|
332
|
+
"number"
|
|
333
|
+
],
|
|
334
|
+
"description": "本 endpoint 的框架 master(掘金 uid)",
|
|
335
|
+
"x-i18n": {
|
|
336
|
+
"en": {
|
|
337
|
+
"description": "Framework masters for this endpoint (Juejin uid)."
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
"trusted": {
|
|
342
|
+
"type": "array",
|
|
343
|
+
"items": {
|
|
344
|
+
"type": [
|
|
345
|
+
"string",
|
|
346
|
+
"number"
|
|
347
|
+
],
|
|
348
|
+
"description": "信任的掘金用户 ID",
|
|
349
|
+
"x-i18n": {
|
|
350
|
+
"en": {
|
|
351
|
+
"description": "Trusted Juejin user ID"
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
"description": "本 endpoint 的 trusted 列表",
|
|
356
|
+
"x-i18n": {
|
|
357
|
+
"en": {
|
|
358
|
+
"description": "Trusted users for this endpoint."
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
"commandPrefix": {
|
|
363
|
+
"type": "string"
|
|
364
|
+
},
|
|
365
|
+
"nickname": {
|
|
366
|
+
"type": [
|
|
367
|
+
"string",
|
|
368
|
+
"null"
|
|
369
|
+
],
|
|
370
|
+
"description": "账号昵称兜底",
|
|
371
|
+
"x-i18n": {
|
|
372
|
+
"en": {
|
|
373
|
+
"description": "Fallback account nickname"
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
"profile": {
|
|
378
|
+
"$ref": "#/$defs/selfProfileFallback",
|
|
379
|
+
"description": "账号资料兜底",
|
|
380
|
+
"x-i18n": {
|
|
381
|
+
"en": {
|
|
382
|
+
"description": "Fallback account profile"
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
},
|
|
387
|
+
"required": [
|
|
388
|
+
"id"
|
|
389
|
+
]
|
|
390
|
+
},
|
|
391
|
+
"x-i18n": {
|
|
392
|
+
"en": {
|
|
393
|
+
"description": "Multiple accounts: mount multiple endpoints in one plugin instance. Each entry uses the top-level fields; id is required and other fields override defaults."
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
"nickname": {
|
|
398
|
+
"type": [
|
|
399
|
+
"string",
|
|
400
|
+
"null"
|
|
401
|
+
],
|
|
402
|
+
"description": "账号昵称兜底",
|
|
403
|
+
"x-i18n": {
|
|
404
|
+
"en": {
|
|
405
|
+
"description": "Fallback account nickname"
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
"profile": {
|
|
410
|
+
"$ref": "#/$defs/selfProfileFallback",
|
|
411
|
+
"description": "账号资料兜底",
|
|
412
|
+
"x-i18n": {
|
|
413
|
+
"en": {
|
|
414
|
+
"description": "Fallback account profile"
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
},
|
|
419
|
+
"required": [
|
|
420
|
+
"endpoints"
|
|
421
|
+
],
|
|
422
|
+
"$defs": {
|
|
423
|
+
"selfProfileFallback": {
|
|
424
|
+
"type": "object",
|
|
425
|
+
"additionalProperties": false,
|
|
426
|
+
"properties": {
|
|
427
|
+
"gender": {
|
|
428
|
+
"type": [
|
|
429
|
+
"string",
|
|
430
|
+
"null"
|
|
431
|
+
],
|
|
432
|
+
"enum": [
|
|
433
|
+
"male",
|
|
434
|
+
"female",
|
|
435
|
+
"other",
|
|
436
|
+
"unknown",
|
|
437
|
+
null
|
|
438
|
+
]
|
|
439
|
+
},
|
|
440
|
+
"age": {
|
|
441
|
+
"type": [
|
|
442
|
+
"integer",
|
|
443
|
+
"null"
|
|
444
|
+
],
|
|
445
|
+
"minimum": 0
|
|
446
|
+
},
|
|
447
|
+
"birthday": {
|
|
448
|
+
"type": [
|
|
449
|
+
"string",
|
|
450
|
+
"null"
|
|
451
|
+
],
|
|
452
|
+
"pattern": "^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])$"
|
|
453
|
+
},
|
|
454
|
+
"signature": {
|
|
455
|
+
"type": [
|
|
456
|
+
"string",
|
|
457
|
+
"null"
|
|
458
|
+
]
|
|
459
|
+
},
|
|
460
|
+
"avatar": {
|
|
461
|
+
"type": [
|
|
462
|
+
"string",
|
|
463
|
+
"null"
|
|
464
|
+
],
|
|
465
|
+
"pattern": "^(https?://[^\\s/?#]+(?:[/?#].*)?|)$"
|
|
466
|
+
},
|
|
467
|
+
"interests": {
|
|
468
|
+
"type": [
|
|
469
|
+
"array",
|
|
470
|
+
"null"
|
|
471
|
+
],
|
|
472
|
+
"items": {
|
|
473
|
+
"type": "string"
|
|
474
|
+
}
|
|
475
|
+
},
|
|
476
|
+
"bio": {
|
|
477
|
+
"type": [
|
|
478
|
+
"string",
|
|
479
|
+
"null"
|
|
480
|
+
]
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
package/src/adapter.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Adapter, type AdapterContext, type EndpointCreationContext } from 'zhin.js/adapter';
|
|
2
|
+
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
3
|
+
import { JuejinClient, type JuejinClientEventMap, type JuejinFetch } from './client.js';
|
|
4
|
+
import { JuejinEndpoint } from './endpoint.js';
|
|
5
|
+
import { resolveJuejinConfig, type JuejinEndpointConfig } from './protocol.js';
|
|
6
|
+
import { juejinNotifierToken } from './juejin-runtime-state.js';
|
|
7
|
+
import { formatNotice } from './review.js';
|
|
8
|
+
|
|
9
|
+
export class JuejinAdapter extends Adapter<JuejinEndpoint, JuejinEndpointConfig, JuejinClientEventMap> {
|
|
10
|
+
readonly #context: AdapterContext<JuejinEndpointConfig>;
|
|
11
|
+
constructor(context: AdapterContext<JuejinEndpointConfig>) { super(); this.#context = context; }
|
|
12
|
+
override get configurationIdentityFields(): readonly string[] { return ['id']; }
|
|
13
|
+
override get configurationIdentityProbeFields(): readonly string[] { return ['cookie', 'apiBaseUrl']; }
|
|
14
|
+
override get configurationUpdateFields(): readonly string[] {
|
|
15
|
+
return ['cookie', 'articles', 'pollIntervalSec', 'sendMode', 'minSendIntervalSec', 'notify', 'apiBaseUrl'];
|
|
16
|
+
}
|
|
17
|
+
protected override validateConfiguration(config: JuejinEndpointConfig): void { resolveJuejinConfig(config); }
|
|
18
|
+
protected override prepareConfiguration(config: JuejinEndpointConfig, identity: Readonly<{ instanceId: string }>): JuejinEndpointConfig {
|
|
19
|
+
return { ...config, stateDirectoryId: identity.instanceId };
|
|
20
|
+
}
|
|
21
|
+
protected override async identifyConfiguration(input: JuejinEndpointConfig, signal: AbortSignal): Promise<string | undefined> {
|
|
22
|
+
const config = resolveJuejinConfig(input);
|
|
23
|
+
if (!config.cookie) return undefined;
|
|
24
|
+
const self = await new JuejinClient(config, globalThis.fetch as unknown as JuejinFetch, signal).getSelf();
|
|
25
|
+
signal.throwIfAborted();
|
|
26
|
+
return self ? `juejin:${self.userId}` : undefined;
|
|
27
|
+
}
|
|
28
|
+
protected createEndpoint(input: JuejinEndpointConfig, execution: EndpointCreationContext): JuejinEndpoint {
|
|
29
|
+
const config = resolveJuejinConfig(input);
|
|
30
|
+
const notifier = this.#context.use(juejinNotifierToken);
|
|
31
|
+
const logger = getAdapterLogger('juejin', config.id);
|
|
32
|
+
return new JuejinEndpoint({ id: execution.canonicalId, config, signal: execution.signal,
|
|
33
|
+
stateDir: config.dataDir,
|
|
34
|
+
notify: async notice => {
|
|
35
|
+
if (!config.notify) return;
|
|
36
|
+
if (!await notifier(config.notify, formatNotice(notice, { formatCommand: path => path }))) {
|
|
37
|
+
logger.info(formatCompact({ op: 'juejin_notify_skipped', kind: notice.kind, hint: '没有可用的出站通道' }));
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
declare module '@zhin.js/feature-kit' {
|
|
44
|
+
interface AdapterRegistry { readonly juejin: JuejinAdapter; }
|
|
45
|
+
}
|