@oomol-lab/open-connector 1.6.3 → 1.6.5

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.
Files changed (108) hide show
  1. package/assets/open-connector/catalog/apps/aisa.json +568 -0
  2. package/assets/open-connector/catalog/apps/bilibili.json +2586 -0
  3. package/assets/open-connector/catalog/apps/bitwarden.json +1423 -0
  4. package/assets/open-connector/catalog/apps/cronfree.json +303 -0
  5. package/assets/open-connector/catalog/apps/cufinder.json +368 -0
  6. package/assets/open-connector/catalog/apps/feishu_app_bot.json +2 -2
  7. package/assets/open-connector/catalog/apps/gmail.json +73 -0
  8. package/assets/open-connector/catalog/apps/investoday_mcp.json +22228 -197
  9. package/assets/open-connector/catalog/apps/ixspy.json +2274 -0
  10. package/assets/open-connector/catalog/apps/jin10.json +1 -1
  11. package/assets/open-connector/catalog/apps/osv.json +663 -0
  12. package/assets/open-connector/catalog/apps/pdf_vector.json +329 -0
  13. package/assets/open-connector/catalog/apps/pulsetic.json +406 -0
  14. package/assets/open-connector/catalog/apps/shopee.json +1785 -0
  15. package/assets/open-connector/catalog/apps/slack.json +81 -0
  16. package/assets/open-connector/catalog/apps/slackbot.json +81 -0
  17. package/assets/open-connector/catalog/apps/speak_ai.json +360 -0
  18. package/assets/open-connector/catalog/apps/tongdaxin.json +11 -440
  19. package/assets/open-connector/catalog/apps/zyhub.json +1585 -0
  20. package/assets/open-connector/catalog/apps-index.json +1 -1
  21. package/package.json +1 -1
  22. package/src/core/json-schema.d.ts +5 -1
  23. package/src/core/json-schema.js +14 -2
  24. package/src/core/types.d.ts +9 -0
  25. package/src/oauth/oauth-credential-refresh-service.js +3 -1
  26. package/src/oauth/oauth-flow-service.js +17 -2
  27. package/src/oauth/oauth-token.d.ts +13 -0
  28. package/src/providers/action-contracts.generated.d.ts +16 -4
  29. package/src/providers/aisa/executors.d.ts +4 -0
  30. package/src/providers/aisa/executors.js +176 -0
  31. package/src/providers/bilibili/articles.d.ts +3 -0
  32. package/src/providers/bilibili/articles.js +369 -0
  33. package/src/providers/bilibili/executors.d.ts +8 -0
  34. package/src/providers/bilibili/executors.js +262 -0
  35. package/src/providers/bilibili/runtime.d.ts +95 -0
  36. package/src/providers/bilibili/runtime.js +238 -0
  37. package/src/providers/bilibili/upload.d.ts +14 -0
  38. package/src/providers/bilibili/upload.js +105 -0
  39. package/src/providers/bitwarden/executors.d.ts +3 -0
  40. package/src/providers/bitwarden/executors.js +146 -0
  41. package/src/providers/cronfree/executors.d.ts +4 -0
  42. package/src/providers/cronfree/executors.js +89 -0
  43. package/src/providers/cufinder/executors.d.ts +4 -0
  44. package/src/providers/cufinder/executors.js +105 -0
  45. package/src/providers/dropbox/executors.js +21 -3
  46. package/src/providers/elevenreader/runtime.js +6 -5
  47. package/src/providers/feishu_app_bot/scopes.js +1 -1
  48. package/src/providers/gmail/attachment-stream.d.ts +2 -0
  49. package/src/providers/gmail/attachment-stream.js +162 -0
  50. package/src/providers/gmail/executors.d.ts +3 -1
  51. package/src/providers/gmail/executors.js +53 -25
  52. package/src/providers/investoday_mcp/official-actions.js +19 -372
  53. package/src/providers/investoday_mcp/official-tool-types.d.ts +10 -0
  54. package/src/providers/investoday_mcp/official-tool-types.js +4 -0
  55. package/src/providers/investoday_mcp/official-tools/charts.d.ts +2 -0
  56. package/src/providers/investoday_mcp/official-tools/charts.js +32 -0
  57. package/src/providers/investoday_mcp/official-tools/fixed-income.d.ts +2 -0
  58. package/src/providers/investoday_mcp/official-tools/fixed-income.js +326 -0
  59. package/src/providers/investoday_mcp/official-tools/funds.d.ts +2 -0
  60. package/src/providers/investoday_mcp/official-tools/funds.js +1085 -0
  61. package/src/providers/investoday_mcp/official-tools/hong-kong.d.ts +2 -0
  62. package/src/providers/investoday_mcp/official-tools/hong-kong.js +275 -0
  63. package/src/providers/investoday_mcp/official-tools/indices-industries.d.ts +2 -0
  64. package/src/providers/investoday_mcp/official-tools/indices-industries.js +642 -0
  65. package/src/providers/investoday_mcp/official-tools/macro-calendar.d.ts +2 -0
  66. package/src/providers/investoday_mcp/official-tools/macro-calendar.js +398 -0
  67. package/src/providers/investoday_mcp/official-tools/other.d.ts +2 -0
  68. package/src/providers/investoday_mcp/official-tools/other.js +131 -0
  69. package/src/providers/investoday_mcp/official-tools/research-news.d.ts +2 -0
  70. package/src/providers/investoday_mcp/official-tools/research-news.js +302 -0
  71. package/src/providers/investoday_mcp/official-tools/stocks.d.ts +2 -0
  72. package/src/providers/investoday_mcp/official-tools/stocks.js +2829 -0
  73. package/src/providers/ixspy/executors.d.ts +3 -0
  74. package/src/providers/ixspy/executors.js +220 -0
  75. package/src/providers/mcp-client.d.ts +1 -0
  76. package/src/providers/mcp-client.js +41 -27
  77. package/src/providers/osv/executors.d.ts +2 -0
  78. package/src/providers/osv/executors.js +11 -0
  79. package/src/providers/osv/runtime.d.ts +9 -0
  80. package/src/providers/osv/runtime.js +66 -0
  81. package/src/providers/pdf_vector/executors.d.ts +4 -0
  82. package/src/providers/pdf_vector/executors.js +108 -0
  83. package/src/providers/provider-runtime.d.ts +3 -0
  84. package/src/providers/provider-runtime.js +4 -1
  85. package/src/providers/pulsetic/executors.d.ts +4 -0
  86. package/src/providers/pulsetic/executors.js +126 -0
  87. package/src/providers/registry.generated.js +12 -0
  88. package/src/providers/sellersprite_mcp/executors.js +2 -1
  89. package/src/providers/shopee/actions.d.ts +2 -0
  90. package/src/providers/shopee/actions.js +474 -0
  91. package/src/providers/shopee/executors.d.ts +3 -0
  92. package/src/providers/shopee/executors.js +85 -0
  93. package/src/providers/shopee/runtime.d.ts +79 -0
  94. package/src/providers/shopee/runtime.js +693 -0
  95. package/src/providers/slack/actions.js +17 -0
  96. package/src/providers/slack/runtime.js +62 -4
  97. package/src/providers/speak_ai/executors.d.ts +3 -0
  98. package/src/providers/speak_ai/executors.js +187 -0
  99. package/src/providers/tongdaxin/executors.js +5 -4
  100. package/src/providers/tongdaxin/named-action-output.js +3 -9
  101. package/src/providers/tongdaxin/named-action-routing.js +1 -1
  102. package/src/providers/tongdaxin/named-actions.js +1 -6
  103. package/src/providers/tushare/runtime.js +22 -9
  104. package/src/providers/twenty_crm/runtime.js +4 -2
  105. package/src/providers/zyhub/executors.d.ts +3 -0
  106. package/src/providers/zyhub/executors.js +240 -0
  107. package/src/server/files/transit-files.d.ts +3 -2
  108. package/src/server/files/transit-files.js +31 -35
@@ -0,0 +1,369 @@
1
+ import { compactObject, looseArray, optionalBoolean, optionalNumber, optionalRecord, optionalString, optionalStringArray, positiveInteger, } from "../../core/cast.js";
2
+ import { providerInputError, providerResponseError, readTransitFileInput, requiredInputNumber, requiredInputString, requiredResponseRecord, } from "../provider-runtime.js";
3
+ import { assertAnyInputField, bilibiliApiRequest, bilibiliFormRequest, readBilibiliReplyIncrement } from "./runtime.js";
4
+ import { uploadBilibiliArticleImage } from "./upload.js";
5
+ export const bilibiliArticleActionHandlers = {
6
+ async list_article_categories(_input, context) {
7
+ const data = await bilibiliApiRequest(context, {
8
+ path: "/article/categories",
9
+ label: "Bilibili article category list",
10
+ });
11
+ return { categories: looseArray(data) };
12
+ },
13
+ async upload_article_image(input, context) {
14
+ const image = await readTransitFileInput(input.file, context);
15
+ return uploadBilibiliArticleImage(context, image.file, optionalBoolean(input.watermark));
16
+ },
17
+ async submit_article(input, context) {
18
+ const data = requiredResponseRecord(await bilibiliFormRequest(context, {
19
+ path: "/article/add",
20
+ fields: readArticleFormFields(input),
21
+ label: "Bilibili article submit",
22
+ }), "Bilibili article submit");
23
+ return { articleId: positiveInteger(data.id, "id", providerResponseError) };
24
+ },
25
+ async edit_article(input, context) {
26
+ const articleId = requiredInputNumber(input.articleId, "articleId");
27
+ assertAnyInputField(input, [
28
+ "title",
29
+ "categoryId",
30
+ "templateId",
31
+ "summary",
32
+ "content",
33
+ "bannerUrl",
34
+ "original",
35
+ "imageUrls",
36
+ "tags",
37
+ "anthologyId",
38
+ "upClosedReply",
39
+ "topVideoBvid",
40
+ ]);
41
+ // Fail fast on an explicit mutually exclusive pair before reading anything.
42
+ assertArticleCoverChoice(input);
43
+ // Bilibili replaces every field on edit, so unchanged fields are refilled
44
+ // from the current article before submitting.
45
+ const current = requiredResponseRecord(await bilibiliApiRequest(context, {
46
+ path: "/article/detail",
47
+ query: { id: String(articleId) },
48
+ label: "Bilibili article detail",
49
+ }), "Bilibili article detail");
50
+ const currentCategory = optionalRecord(current.category);
51
+ const currentAnthology = optionalRecord(current.list);
52
+ await bilibiliFormRequest(context, {
53
+ path: "/article/edit",
54
+ fields: readArticleFormFields(input, {
55
+ id: articleId,
56
+ title: optionalString(current.title),
57
+ category: optionalNumber(currentCategory?.id),
58
+ template_id: optionalNumber(current.template_id),
59
+ summary: optionalString(current.summary),
60
+ content: optionalString(current.content),
61
+ banner_url: optionalString(current.banner_url),
62
+ original: optionalNumber(current.original),
63
+ image_urls: optionalStringArray(current.image_urls)?.join(","),
64
+ tags: readArticleTagNames(current.tags),
65
+ list_id: optionalNumber(currentAnthology?.id),
66
+ top_video_bvid: optionalString(current.top_video_bvid),
67
+ }),
68
+ label: "Bilibili article edit",
69
+ });
70
+ return { articleId };
71
+ },
72
+ async delete_articles(input, context) {
73
+ const ids = readArticleIdList(input.articleIds);
74
+ await bilibiliFormRequest(context, {
75
+ path: "/article/delete",
76
+ fields: { ids: ids.join(",") },
77
+ label: "Bilibili article delete",
78
+ });
79
+ return { articleIds: ids, deleted: true };
80
+ },
81
+ async get_article(input, context) {
82
+ const articleId = requiredInputNumber(input.articleId, "articleId");
83
+ const data = await bilibiliApiRequest(context, {
84
+ path: "/article/detail",
85
+ query: { id: String(articleId) },
86
+ label: "Bilibili article detail",
87
+ });
88
+ return normalizeBilibiliArticle(data);
89
+ },
90
+ async list_articles(input, context) {
91
+ const sort = optionalNumber(input.sort);
92
+ const group = optionalNumber(input.group);
93
+ const category = optionalNumber(input.categoryId);
94
+ const data = requiredResponseRecord(await bilibiliApiRequest(context, {
95
+ path: "/article/list",
96
+ query: {
97
+ pn: String(optionalNumber(input.pn) ?? 1),
98
+ ps: String(optionalNumber(input.ps) ?? 10),
99
+ sort: sort === undefined ? undefined : String(sort),
100
+ group: group === undefined ? undefined : String(group),
101
+ category: category === undefined ? undefined : String(category),
102
+ },
103
+ label: "Bilibili article list",
104
+ }), "Bilibili article list");
105
+ const page = optionalRecord(data.artPage);
106
+ const counts = optionalRecord(data.creationArtsType);
107
+ return {
108
+ articles: looseArray(data.articles).map(normalizeBilibiliArticle),
109
+ page: compactObject({
110
+ pn: optionalNumber(page?.pn),
111
+ ps: optionalNumber(page?.ps),
112
+ total: optionalNumber(page?.total),
113
+ }),
114
+ counts: compactObject({
115
+ all: optionalNumber(counts?.all),
116
+ audit: optionalNumber(counts?.audit),
117
+ passed: optionalNumber(counts?.passed),
118
+ notPassed: optionalNumber(counts?.notPassed),
119
+ }),
120
+ };
121
+ },
122
+ async get_article_card_snippet(input, context) {
123
+ const resourceId = requiredInputString(input.resourceId, "resourceId");
124
+ // The document's formal URL field says /article/card while its own curl
125
+ // sample calls /article/cards; this follows the documented field.
126
+ const data = requiredResponseRecord(await bilibiliApiRequest(context, {
127
+ path: "/article/card",
128
+ query: { resource_id: resourceId },
129
+ label: "Bilibili article card",
130
+ }), "Bilibili article card");
131
+ return compactObject({ snippet: optionalString(data.snippet) });
132
+ },
133
+ async create_anthology(input, context) {
134
+ const data = requiredResponseRecord(await bilibiliFormRequest(context, {
135
+ path: "/article/anthology/add",
136
+ fields: {
137
+ name: requiredInputString(input.name, "name"),
138
+ summary: optionalString(input.summary),
139
+ image_url: optionalString(input.imageUrl),
140
+ },
141
+ label: "Bilibili anthology create",
142
+ }), "Bilibili anthology create");
143
+ return compactObject({
144
+ anthologyId: optionalNumber(data.id),
145
+ name: optionalString(data.name),
146
+ });
147
+ },
148
+ async edit_anthology(input, context) {
149
+ const anthologyId = requiredInputNumber(input.anthologyId, "anthologyId");
150
+ assertAnyInputField(input, ["name", "summary", "imageUrl"]);
151
+ // Same full-field replacement semantics as article editing: unchanged
152
+ // fields are refilled from the current anthology.
153
+ const current = requiredResponseRecord(await bilibiliApiRequest(context, {
154
+ path: "/article/anthology/detail",
155
+ query: { id: String(anthologyId) },
156
+ label: "Bilibili anthology detail",
157
+ }), "Bilibili anthology detail");
158
+ const anthology = optionalRecord(current.list) ?? current;
159
+ const name = optionalString(input.name) ?? optionalString(anthology.name);
160
+ if (!name) {
161
+ throw providerInputError("name is required when the current anthology does not provide it.");
162
+ }
163
+ await bilibiliFormRequest(context, {
164
+ path: "/article/anthology/edit",
165
+ fields: {
166
+ list_id: String(anthologyId),
167
+ name,
168
+ summary: optionalString(input.summary) ?? optionalString(anthology.summary),
169
+ image_url: optionalString(input.imageUrl) ?? optionalString(anthology.image_url),
170
+ },
171
+ label: "Bilibili anthology edit",
172
+ });
173
+ return { anthologyId };
174
+ },
175
+ async delete_anthology(input, context) {
176
+ const anthologyId = requiredInputNumber(input.anthologyId, "anthologyId");
177
+ await bilibiliFormRequest(context, {
178
+ path: "/article/anthology/delete",
179
+ fields: { id: String(anthologyId) },
180
+ label: "Bilibili anthology delete",
181
+ });
182
+ return { anthologyId, deleted: true };
183
+ },
184
+ async list_anthologies(_input, context) {
185
+ const data = requiredResponseRecord(await bilibiliApiRequest(context, { path: "/article/anthology/list", label: "Bilibili anthology list" }), "Bilibili anthology list");
186
+ return {
187
+ anthologies: looseArray(data.lists).map(normalizeBilibiliAnthology),
188
+ total: optionalNumber(data.total),
189
+ };
190
+ },
191
+ async get_anthology(input, context) {
192
+ const anthologyId = requiredInputNumber(input.anthologyId, "anthologyId");
193
+ const data = requiredResponseRecord(await bilibiliApiRequest(context, {
194
+ path: "/article/anthology/detail",
195
+ query: { id: String(anthologyId) },
196
+ label: "Bilibili anthology detail",
197
+ }), "Bilibili anthology detail");
198
+ return {
199
+ anthology: normalizeBilibiliAnthology(optionalRecord(data.list) ?? {}),
200
+ articles: looseArray(data.articles).map((article) => {
201
+ const record = optionalRecord(article) ?? {};
202
+ return compactObject({
203
+ articleId: optionalNumber(record.id),
204
+ title: optionalString(record.title),
205
+ state: optionalNumber(record.state),
206
+ publishTime: optionalNumber(record.publish_time),
207
+ });
208
+ }),
209
+ total: optionalNumber(data.total),
210
+ };
211
+ },
212
+ async set_anthology_articles(input, context) {
213
+ const anthologyId = requiredInputNumber(input.anthologyId, "anthologyId");
214
+ // An empty list clears the anthology; otherwise the given list replaces the current one.
215
+ const articleIds = readArticleIdList(input.articleIds, { allowEmpty: true });
216
+ await bilibiliFormRequest(context, {
217
+ path: "/article/belong",
218
+ fields: { list_id: String(anthologyId), article_ids: articleIds.join(",") },
219
+ label: "Bilibili anthology article update",
220
+ });
221
+ return { anthologyId, articleIds };
222
+ },
223
+ async get_article_stats(input, context) {
224
+ const ids = readArticleIdList(input.articleIds);
225
+ const data = requiredResponseRecord(await bilibiliApiRequest(context, {
226
+ path: "/data/art/stat",
227
+ query: { ids: ids.join(",") },
228
+ label: "Bilibili article stats",
229
+ }), "Bilibili article stats");
230
+ return {
231
+ articles: Object.entries(data).map(([id, article]) => ({
232
+ articleId: Number(id),
233
+ ...normalizeBilibiliArticle(article),
234
+ })),
235
+ };
236
+ },
237
+ async get_article_inc_stats(_input, context) {
238
+ const data = requiredResponseRecord(await bilibiliApiRequest(context, { path: "/data/art/inc-stats", label: "Bilibili article incremental stats" }), "Bilibili article incremental stats");
239
+ return compactObject({
240
+ incRead: optionalNumber(data.inc_read),
241
+ incReply: readBilibiliReplyIncrement(data),
242
+ incFavorite: optionalNumber(data.inc_fav),
243
+ incLikes: optionalNumber(data.inc_likes),
244
+ incShare: optionalNumber(data.inc_share),
245
+ incCoin: optionalNumber(data.inc_coin),
246
+ });
247
+ },
248
+ };
249
+ /** Reject an explicit bannerUrl+topVideoBvid pair; the document makes them mutually exclusive. */
250
+ function assertArticleCoverChoice(input) {
251
+ if (optionalString(input.bannerUrl) && optionalString(input.topVideoBvid)) {
252
+ throw providerInputError("bannerUrl and topVideoBvid are mutually exclusive.");
253
+ }
254
+ }
255
+ /** Build the multipart form fields of an article add/edit request, merging over `current` when editing. */
256
+ function readArticleFormFields(input, current = {}) {
257
+ const original = optionalBoolean(input.original);
258
+ const upClosedReply = optionalBoolean(input.upClosedReply);
259
+ const anthologyId = optionalNumber(input.anthologyId);
260
+ const imageUrls = input.imageUrls === undefined ? undefined : readArticleImageUrls(input.imageUrls);
261
+ assertArticleCoverChoice(input);
262
+ // An explicit input replaces the inherited counterpart so editing can switch.
263
+ const inputBannerUrl = optionalString(input.bannerUrl);
264
+ const inputTopVideoBvid = optionalString(input.topVideoBvid);
265
+ const bannerUrl = inputBannerUrl ?? (inputTopVideoBvid ? undefined : current.banner_url?.toString());
266
+ const topVideoBvid = inputTopVideoBvid ?? (inputBannerUrl ? undefined : current.top_video_bvid?.toString());
267
+ return {
268
+ id: current.id === undefined ? undefined : String(current.id),
269
+ title: optionalString(input.title) ?? requiredInputString(current.title, "title"),
270
+ category: String(optionalNumber(input.categoryId) ?? requiredInputNumber(current.category, "category")),
271
+ template_id: String(optionalNumber(input.templateId) ?? requiredInputNumber(current.template_id, "templateId")),
272
+ summary: optionalString(input.summary) ?? requiredInputString(current.summary, "summary"),
273
+ content: optionalString(input.content) ?? requiredInputString(current.content, "content"),
274
+ banner_url: bannerUrl,
275
+ original: original === undefined ? current.original?.toString() : original ? "1" : "0",
276
+ image_urls: imageUrls ?? current.image_urls?.toString(),
277
+ tags: optionalString(input.tags) ?? current.tags?.toString(),
278
+ list_id: anthologyId === undefined ? current.list_id?.toString() : String(anthologyId),
279
+ up_closed_reply: upClosedReply === undefined ? undefined : upClosedReply ? "1" : "0",
280
+ top_video_bvid: topVideoBvid,
281
+ };
282
+ }
283
+ function readArticleImageUrls(value) {
284
+ const urls = optionalStringArray(value);
285
+ if (!urls || urls.length === 0) {
286
+ throw providerInputError("imageUrls must be a non-empty array of Bilibili-hosted image URLs.");
287
+ }
288
+ return urls.join(",");
289
+ }
290
+ function readArticleIdList(value, options = {}) {
291
+ const ids = Array.isArray(value) ? value.map((id) => positiveInteger(id, "articleIds", providerInputError)) : [];
292
+ if (ids.length === 0 && !options.allowEmpty) {
293
+ throw providerInputError("articleIds must be a non-empty array of article ids.");
294
+ }
295
+ return ids;
296
+ }
297
+ function readArticleTagNames(value) {
298
+ const tags = looseArray(value)
299
+ .map((tag) => optionalString(optionalRecord(tag)?.name))
300
+ .filter((name) => name !== undefined);
301
+ return tags.length > 0 ? tags.join(",") : undefined;
302
+ }
303
+ function normalizeBilibiliArticle(value) {
304
+ const record = optionalRecord(value) ?? {};
305
+ const category = optionalRecord(record.category);
306
+ const stats = optionalRecord(record.stats);
307
+ const anthology = optionalRecord(record.list);
308
+ return compactObject({
309
+ articleId: optionalNumber(record.id),
310
+ title: optionalString(record.title),
311
+ summary: optionalString(record.summary),
312
+ content: optionalString(record.content),
313
+ bannerUrl: optionalString(record.banner_url),
314
+ templateId: optionalNumber(record.template_id),
315
+ state: optionalNumber(record.state),
316
+ reason: optionalString(record.reason),
317
+ imageUrls: optionalStringArray(record.image_urls),
318
+ publishTime: optionalNumber(record.publish_time),
319
+ ctime: optionalNumber(record.ctime),
320
+ words: optionalNumber(record.words),
321
+ original: optionalNumber(record.original),
322
+ topVideoBvid: optionalString(record.top_video_bvid),
323
+ type: optionalNumber(record.type),
324
+ category: category === undefined
325
+ ? undefined
326
+ : compactObject({
327
+ id: optionalNumber(category.id),
328
+ parentId: optionalNumber(category.parent_id),
329
+ name: optionalString(category.name),
330
+ }),
331
+ stats: stats === undefined
332
+ ? undefined
333
+ : compactObject({
334
+ view: optionalNumber(stats.view),
335
+ favorite: optionalNumber(stats.favorite),
336
+ like: optionalNumber(stats.like),
337
+ dislike: optionalNumber(stats.dislike),
338
+ reply: optionalNumber(stats.reply),
339
+ share: optionalNumber(stats.share),
340
+ coin: optionalNumber(stats.coin),
341
+ }),
342
+ tags: looseArray(record.tags).map((tag) => {
343
+ const item = optionalRecord(tag) ?? {};
344
+ return compactObject({ tid: optionalNumber(item.tid), name: optionalString(item.name) });
345
+ }),
346
+ anthology: anthology === undefined
347
+ ? undefined
348
+ : compactObject({ anthologyId: optionalNumber(anthology.id), name: optionalString(anthology.name) }),
349
+ });
350
+ }
351
+ function normalizeBilibiliAnthology(value) {
352
+ const record = optionalRecord(value) ?? {};
353
+ return compactObject({
354
+ anthologyId: optionalNumber(record.id),
355
+ name: optionalString(record.name),
356
+ imageUrl: optionalString(record.image_url),
357
+ summary: optionalString(record.summary),
358
+ words: optionalNumber(record.words),
359
+ read: optionalNumber(record.read),
360
+ state: optionalNumber(record.state),
361
+ reason: optionalString(record.reason),
362
+ total: optionalNumber(record.total),
363
+ ctime: optionalNumber(record.ctime),
364
+ publishTime: optionalNumber(record.publish_time),
365
+ updateTime: optionalNumber(record.update_time),
366
+ applyTime: optionalString(record.apply_time),
367
+ checkTime: optionalString(record.check_time),
368
+ });
369
+ }
@@ -0,0 +1,8 @@
1
+ import type { CredentialValidators, ProviderExecutors } from "../../core/types.ts";
2
+ import type { ProviderOAuthRuntime } from "../../oauth/oauth-token.ts";
3
+ import type { ProviderActionHandlers } from "../provider-runtime.ts";
4
+ import type { BilibiliActionHandler } from "./runtime.ts";
5
+ export declare const bilibiliActionHandlers: ProviderActionHandlers<"bilibili", BilibiliActionHandler>;
6
+ export declare const executors: ProviderExecutors;
7
+ export declare const oauth: ProviderOAuthRuntime;
8
+ export declare const credentialValidators: CredentialValidators;
@@ -0,0 +1,262 @@
1
+ import { compactObject, looseArray, optionalBoolean, optionalNumber, optionalRecord, optionalString, optionalStringArray, requiredString, } from "../../core/cast.js";
2
+ import { combineProviderActionHandlers, defineProviderExecutors, providerInputError, providerResponseError, readTransitFileInput, requiredInputNumber, requiredInputString, requiredResponseRecord, } from "../provider-runtime.js";
3
+ import { bilibiliArticleActionHandlers } from "./articles.js";
4
+ import { assertAnyInputField, bilibiliApiRequest, bilibiliOAuthRefreshTokenUrl, createBilibiliContext, readBilibiliReplyIncrement, readBilibiliSigningMaterial, requestBilibiliOAuthToken, } from "./runtime.js";
5
+ import { uploadBilibiliCover, uploadBilibiliVideo } from "./upload.js";
6
+ const service = "bilibili";
7
+ const bilibiliAccountActionHandlers = {
8
+ async get_user_info(_input, context) {
9
+ const data = requiredResponseRecord(await bilibiliApiRequest(context, { path: "/user/account/info", label: "Bilibili user info" }), "Bilibili user info");
10
+ return compactObject({
11
+ name: optionalString(data.name),
12
+ face: optionalString(data.face),
13
+ openid: optionalString(data.openid),
14
+ });
15
+ },
16
+ async get_user_union_id(_input, context) {
17
+ const data = requiredResponseRecord(await bilibiliApiRequest(context, {
18
+ method: "POST",
19
+ path: "/user/account/union_id",
20
+ label: "Bilibili user union id",
21
+ }), "Bilibili user union id");
22
+ return compactObject({ unionId: optionalString(data.union_id) });
23
+ },
24
+ async get_user_scopes(_input, context) {
25
+ const data = requiredResponseRecord(await bilibiliApiRequest(context, { path: "/user/account/scopes", label: "Bilibili user scopes" }), "Bilibili user scopes");
26
+ return compactObject({
27
+ openid: optionalString(data.openid),
28
+ scopes: optionalStringArray(data.scopes) ?? [],
29
+ });
30
+ },
31
+ async get_user_stat(_input, context) {
32
+ const data = requiredResponseRecord(await bilibiliApiRequest(context, { path: "/data/user/stat", label: "Bilibili user stat" }), "Bilibili user stat");
33
+ return compactObject({
34
+ following: optionalNumber(data.following),
35
+ follower: optionalNumber(data.follower),
36
+ arcPassedTotal: optionalNumber(data.arc_passed_total),
37
+ });
38
+ },
39
+ async list_archive_types(_input, context) {
40
+ const data = await bilibiliApiRequest(context, { path: "/archive/type/list", label: "Bilibili archive type list" });
41
+ return { types: looseArray(data) };
42
+ },
43
+ async upload_video(input, context) {
44
+ const title = requiredInputString(input.title, "title");
45
+ const tid = requiredInputNumber(input.tid, "tid");
46
+ const tag = requiredInputString(input.tag, "tag");
47
+ const copyright = requiredInputNumber(input.copyright, "copyright");
48
+ const source = optionalString(input.source);
49
+ if (copyright === 2 && !source) {
50
+ throw providerInputError("source is required when copyright is 2 (reposted content).");
51
+ }
52
+ const video = await readTransitFileInput(input.file, context);
53
+ const coverInput = input.cover === undefined ? undefined : await readTransitFileInput(input.cover, context);
54
+ const coverUrl = coverInput ? await uploadBilibiliCover(context, coverInput.file) : undefined;
55
+ const uploadToken = await uploadBilibiliVideo(context, video.file);
56
+ const data = requiredResponseRecord(await bilibiliApiRequest(context, {
57
+ method: "POST",
58
+ path: "/archive/add-by-utoken",
59
+ query: { upload_token: uploadToken },
60
+ body: compactObject({
61
+ title,
62
+ tid,
63
+ tag,
64
+ copyright,
65
+ cover: coverUrl,
66
+ desc: optionalString(input.desc),
67
+ no_reprint: optionalBoolean(input.noReprint) === true ? 1 : 0,
68
+ source,
69
+ }),
70
+ label: "Bilibili archive submit",
71
+ }), "Bilibili archive submit");
72
+ return compactObject({
73
+ resourceId: requiredString(data.resource_id, "resource_id", providerResponseError),
74
+ coverUrl,
75
+ });
76
+ },
77
+ async list_archives(input, context) {
78
+ const data = requiredResponseRecord(await bilibiliApiRequest(context, {
79
+ path: "/archive/viewlist",
80
+ query: {
81
+ pn: String(optionalNumber(input.pn) ?? 1),
82
+ ps: String(optionalNumber(input.ps) ?? 20),
83
+ status: optionalString(input.status),
84
+ },
85
+ label: "Bilibili archive list",
86
+ }), "Bilibili archive list");
87
+ const page = optionalRecord(data.page);
88
+ return {
89
+ archives: looseArray(data.list).map(normalizeBilibiliArchive),
90
+ page: compactObject({
91
+ pn: optionalNumber(page?.pn),
92
+ ps: optionalNumber(page?.ps),
93
+ total: optionalNumber(page?.total),
94
+ }),
95
+ };
96
+ },
97
+ async get_archive(input, context) {
98
+ const resourceId = requiredInputString(input.resourceId, "resourceId");
99
+ const data = await bilibiliApiRequest(context, {
100
+ path: "/archive/view",
101
+ query: { resource_id: resourceId },
102
+ label: "Bilibili archive view",
103
+ });
104
+ return normalizeBilibiliArchive(data);
105
+ },
106
+ async edit_archive(input, context) {
107
+ const resourceId = requiredInputString(input.resourceId, "resourceId");
108
+ assertAnyInputField(input, ["title", "tid", "coverUrl", "desc", "noReprint"]);
109
+ // Bilibili replaces every descriptive field on edit, so unchanged fields
110
+ // are refilled from the current archive before submitting.
111
+ const current = requiredResponseRecord(await bilibiliApiRequest(context, {
112
+ path: "/archive/view",
113
+ query: { resource_id: resourceId },
114
+ label: "Bilibili archive view",
115
+ }), "Bilibili archive view");
116
+ const title = optionalString(input.title) ?? optionalString(current.title);
117
+ const tid = optionalNumber(input.tid) ?? optionalNumber(current.tid);
118
+ if (!title || tid === undefined) {
119
+ throw providerInputError("title and tid are required when the current archive does not provide them.");
120
+ }
121
+ const noReprint = optionalBoolean(input.noReprint);
122
+ await bilibiliApiRequest(context, {
123
+ method: "POST",
124
+ path: "/archive/edit",
125
+ body: compactObject({
126
+ resource_id: resourceId,
127
+ title,
128
+ tid,
129
+ cover: optionalString(input.coverUrl) ?? optionalString(current.cover),
130
+ desc: optionalString(input.desc) ?? optionalString(current.desc),
131
+ no_reprint: noReprint === undefined ? optionalNumber(current.no_reprint) : noReprint ? 1 : 0,
132
+ }),
133
+ label: "Bilibili archive edit",
134
+ });
135
+ return { resourceId };
136
+ },
137
+ async delete_archive(input, context) {
138
+ const resourceId = requiredInputString(input.resourceId, "resourceId");
139
+ await bilibiliApiRequest(context, {
140
+ method: "POST",
141
+ path: "/archive/delete",
142
+ body: { resource_id: resourceId },
143
+ label: "Bilibili archive delete",
144
+ });
145
+ return { resourceId, deleted: true };
146
+ },
147
+ async get_archive_stat(input, context) {
148
+ const resourceId = requiredInputString(input.resourceId, "resourceId");
149
+ const data = requiredResponseRecord(await bilibiliApiRequest(context, {
150
+ path: "/data/arc/stat",
151
+ query: { resource_id: resourceId },
152
+ label: "Bilibili archive stat",
153
+ }), "Bilibili archive stat");
154
+ return compactObject({
155
+ title: optionalString(data.title),
156
+ ptime: optionalNumber(data.ptime),
157
+ view: optionalNumber(data.view),
158
+ danmaku: optionalNumber(data.danmaku),
159
+ reply: optionalNumber(data.reply),
160
+ favorite: optionalNumber(data.favorite),
161
+ coin: optionalNumber(data.coin),
162
+ share: optionalNumber(data.share),
163
+ like: optionalNumber(data.like),
164
+ });
165
+ },
166
+ async get_archive_inc_stats(_input, context) {
167
+ const data = requiredResponseRecord(await bilibiliApiRequest(context, { path: "/data/arc/inc-stats", label: "Bilibili archive incremental stats" }), "Bilibili archive incremental stats");
168
+ return compactObject({
169
+ incClick: optionalNumber(data.inc_click),
170
+ incDanmaku: optionalNumber(data.inc_dm),
171
+ incReply: readBilibiliReplyIncrement(data),
172
+ incFavorite: optionalNumber(data.inc_fav),
173
+ incCoin: optionalNumber(data.inc_coin),
174
+ incShare: optionalNumber(data.inc_share),
175
+ incLike: optionalNumber(data.inc_like),
176
+ incElec: optionalNumber(data.inc_elec),
177
+ });
178
+ },
179
+ };
180
+ function normalizeBilibiliArchive(value) {
181
+ const record = optionalRecord(value) ?? {};
182
+ const review = optionalRecord(record.addit_info);
183
+ const video = optionalRecord(record.video_info);
184
+ return compactObject({
185
+ resourceId: optionalString(record.resource_id),
186
+ title: optionalString(record.title),
187
+ cover: optionalString(record.cover),
188
+ tid: optionalNumber(record.tid),
189
+ tag: optionalString(record.tag),
190
+ desc: optionalString(record.desc),
191
+ copyright: optionalNumber(record.copyright),
192
+ noReprint: optionalNumber(record.no_reprint),
193
+ state: optionalNumber(review?.state),
194
+ stateDesc: optionalString(review?.state_desc),
195
+ rejectReason: optionalString(review?.reject_reason),
196
+ ctime: optionalNumber(record.ctime),
197
+ ptime: optionalNumber(record.ptime),
198
+ video: video === undefined
199
+ ? undefined
200
+ : compactObject({
201
+ cid: optionalNumber(video.cid),
202
+ filename: optionalString(video.filename),
203
+ duration: optionalNumber(video.duration),
204
+ shareUrl: optionalString(video.share_url),
205
+ iframeUrl: optionalString(video.iframe_url),
206
+ }),
207
+ });
208
+ }
209
+ export const bilibiliActionHandlers = combineProviderActionHandlers(service, bilibiliAccountActionHandlers, bilibiliArticleActionHandlers);
210
+ export const executors = defineProviderExecutors({
211
+ service,
212
+ handlers: bilibiliActionHandlers,
213
+ // Both API hosts are hardcoded literals: member.bilibili.com and openupos.bilivideo.com.
214
+ skipDnsValidation: true,
215
+ createContext: createBilibiliContext,
216
+ });
217
+ export const oauth = {
218
+ async exchangeCode(input) {
219
+ return requestBilibiliOAuthToken({
220
+ url: input.tokenUrl,
221
+ fields: {
222
+ client_id: input.clientConfig.clientId,
223
+ client_secret: input.clientConfig.clientSecret,
224
+ grant_type: "authorization_code",
225
+ code: input.code,
226
+ },
227
+ fetcher: input.fetcher,
228
+ signal: input.signal,
229
+ createError: input.createError,
230
+ });
231
+ },
232
+ async refreshAccessToken(input) {
233
+ return requestBilibiliOAuthToken({
234
+ url: bilibiliOAuthRefreshTokenUrl,
235
+ fields: {
236
+ client_id: input.clientConfig.clientId,
237
+ client_secret: input.clientConfig.clientSecret,
238
+ grant_type: "refresh_token",
239
+ refresh_token: input.refreshToken,
240
+ },
241
+ fetcher: input.fetcher,
242
+ createError: input.createError,
243
+ });
244
+ },
245
+ };
246
+ export const credentialValidators = {
247
+ async oauth2(input, { fetcher, signal }) {
248
+ const signing = readBilibiliSigningMaterial(input.metadata);
249
+ if (!signing) {
250
+ return;
251
+ }
252
+ const data = requiredResponseRecord(await bilibiliApiRequest({ accessToken: input.accessToken, clientId: signing.clientId, appSecret: signing.appSecret, fetcher, signal }, { path: "/user/account/info", label: "Bilibili credential validation" }), "Bilibili credential validation");
253
+ const openid = optionalString(data.openid);
254
+ return {
255
+ profile: {
256
+ accountId: openid ?? "bilibili:oauth2",
257
+ displayName: optionalString(data.name) ?? openid ?? "Bilibili OAuth Credential",
258
+ grantedScopes: optionalStringArray(input.metadata.scopes) ?? [],
259
+ },
260
+ };
261
+ },
262
+ };