@sns-parse/core 0.5.0-alpha.1 → 0.6.0-alpha.2
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/lib/engine/compose.d.ts +57 -0
- package/lib/engine/compose.js +184 -0
- package/lib/engine/config.js +9 -0
- package/lib/engine/fetcher.js +2 -1
- package/lib/engine/flush.d.ts +25 -0
- package/lib/engine/flush.js +163 -0
- package/lib/engine/forward.d.ts +15 -0
- package/lib/engine/forward.js +61 -0
- package/lib/engine/generic.d.ts +31 -0
- package/lib/engine/generic.js +13 -0
- package/lib/engine/runtime.d.ts +19 -16
- package/lib/engine/runtime.js +17 -8
- package/lib/extensions.d.ts +25 -27
- package/lib/extensions.js +21 -0
- package/lib/host.d.ts +3 -3
- package/lib/index.d.ts +6 -0
- package/lib/index.js +6 -0
- package/lib/registry.d.ts +23 -15
- package/lib/registry.js +56 -32
- package/lib/workflow/default.d.ts +3 -0
- package/lib/workflow/default.js +74 -0
- package/lib/workflow/hooks.d.ts +130 -0
- package/lib/workflow/hooks.js +58 -0
- package/package.json +1 -1
package/lib/engine/runtime.js
CHANGED
|
@@ -5,13 +5,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.createRuntime = createRuntime;
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* 运行时装配(本文件在 core 内,平台无关)。
|
|
9
9
|
*
|
|
10
|
-
* 平台定义列表(defs
|
|
11
|
-
* - koishi
|
|
12
|
-
* - CLI
|
|
13
|
-
*
|
|
14
|
-
* core
|
|
10
|
+
* 平台定义列表(defs)由宿主按已加载平台插件声明注入:
|
|
11
|
+
* - koishi 兼容层:collectPlatformDefinitions(anchor)(聚合包 + 粒度包并集)
|
|
12
|
+
* - CLI:collectPlatformDefinitions()
|
|
13
|
+
* 工作流阶段基线在 createDefaultPipeline(),扩展经 WorkflowExtension.setup(hooks) 注入/替换。
|
|
14
|
+
* core 不依赖任何扩展包、不含扩展实现。
|
|
15
15
|
*/
|
|
16
16
|
const axios_1 = __importDefault(require("axios"));
|
|
17
17
|
const https_proxy_agent_1 = require("https-proxy-agent");
|
|
@@ -20,10 +20,11 @@ const cache_1 = require("../utils/cache");
|
|
|
20
20
|
const field_mapping_1 = require("../utils/field-mapping");
|
|
21
21
|
const platform_config_1 = require("./platform-config");
|
|
22
22
|
const gateway_1 = require("./gateway");
|
|
23
|
+
const default_1 = require("../workflow/default");
|
|
23
24
|
function createRuntime(source, config, opts = {}) {
|
|
24
25
|
const host = (0, host_1.createHost)(source);
|
|
25
26
|
const ctx = host.context;
|
|
26
|
-
const extensions =
|
|
27
|
+
const extensions = [...(opts.extensions || []), ...(host.extensions || [])];
|
|
27
28
|
const dedupCache = new cache_1.SimpleLRUCache(1000, config.deduplicationInterval * 1000);
|
|
28
29
|
const cacheTTL = (config.cacheTTL || 600) * 1000;
|
|
29
30
|
const urlCacheLocal = new cache_1.SimpleLRUCache(500, cacheTTL);
|
|
@@ -61,7 +62,12 @@ function createRuntime(source, config, opts = {}) {
|
|
|
61
62
|
axiosConfig.proxy = false;
|
|
62
63
|
}
|
|
63
64
|
const http = axios_1.default.create(axiosConfig);
|
|
64
|
-
|
|
65
|
+
const pipeline = (0, default_1.createDefaultPipeline)(null);
|
|
66
|
+
for (const ext of extensions) {
|
|
67
|
+
if (ext && typeof ext.setup === 'function')
|
|
68
|
+
ext.setup(pipeline);
|
|
69
|
+
}
|
|
70
|
+
const runtime = {
|
|
65
71
|
host,
|
|
66
72
|
ctx,
|
|
67
73
|
config,
|
|
@@ -76,5 +82,8 @@ function createRuntime(source, config, opts = {}) {
|
|
|
76
82
|
defs,
|
|
77
83
|
dedicatedApis,
|
|
78
84
|
extensions,
|
|
85
|
+
pipeline,
|
|
79
86
|
};
|
|
87
|
+
pipeline.rt = runtime;
|
|
88
|
+
return runtime;
|
|
80
89
|
}
|
package/lib/extensions.d.ts
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 扩展契约:core
|
|
3
|
-
* VideoParserExtensions 调用。宿主可整体替换或按需覆盖单项能力。
|
|
2
|
+
* 扩展契约:core 完整工作流 + 基线行为;扩展经阶段钩子注入/修改。
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* core 不内建任何扩展实现(NSFW/合并/翻译/GIF),也不依赖扩展包;
|
|
5
|
+
* 基线行为保证无扩展时全链路可跑(媒体直通、不合并逐张发、GIF 退化发视频、不翻译)。
|
|
6
|
+
* 扩展导出 WorkflowExtension(含 setup(hooks)),由宿主收集后注入管道。
|
|
7
7
|
*/
|
|
8
|
+
import type { ConfigContribution } from './config';
|
|
8
9
|
import type { ParserRuntimeLike } from './host';
|
|
10
|
+
import type { WorkflowHooks } from './workflow/hooks';
|
|
9
11
|
export type MediaKind = 'cover' | 'image' | 'avatar' | 'music-cover';
|
|
10
12
|
export interface ImageOutcome {
|
|
11
|
-
/** raw=原图 url
|
|
13
|
+
/** raw=原图 url 或 buffer 直发;scrambled=混淆 buffer+token;link=只发链接文本;drop=丢弃 */
|
|
12
14
|
kind: 'raw' | 'scrambled' | 'link' | 'drop';
|
|
13
15
|
url?: string;
|
|
14
16
|
buffer?: Buffer;
|
|
15
17
|
token?: string;
|
|
18
|
+
/** buffer 直发时的 mime(缺省 image/jpeg) */
|
|
19
|
+
mime?: string;
|
|
16
20
|
}
|
|
17
21
|
export interface VideoOutcome {
|
|
18
|
-
/** raw
|
|
22
|
+
/** raw=直发视频;card=群内容易吞 → 暂存+token,无法直发视频;link=只发卡片+原链接;drop=只发卡片 */
|
|
19
23
|
kind: 'raw' | 'card' | 'link' | 'drop';
|
|
20
24
|
url?: string;
|
|
21
25
|
token?: string;
|
|
@@ -29,29 +33,23 @@ export interface GifOptions {
|
|
|
29
33
|
fps: number;
|
|
30
34
|
maxDurationSec: number;
|
|
31
35
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
title?: string;
|
|
43
|
-
author?: string;
|
|
44
|
-
requesterId: string;
|
|
45
|
-
}): Promise<VideoOutcome>;
|
|
46
|
-
/** 合并图审核;返回 null 表示回退逐张处理 */
|
|
47
|
-
processMergedImage?(rt: ParserRuntimeLike, platform: string, buffer: Buffer, refUrl: string): Promise<ImageOutcome | null>;
|
|
48
|
-
/** 视频转 GIF;失败返回 null 回退原视频 */
|
|
49
|
-
mp4ToGif?(rt: ParserRuntimeLike, url: string, durationSec: number, opts: GifOptions): Promise<Buffer | null>;
|
|
50
|
-
/** 文本翻译;失败返回 null */
|
|
51
|
-
translate?(rt: ParserRuntimeLike, text: string, target: string, sourceLang?: string): Promise<TranslateResult | null>;
|
|
52
|
-
/** 内容安全能力状态(启动日志) */
|
|
36
|
+
export interface VideoMeta {
|
|
37
|
+
title?: string;
|
|
38
|
+
author?: string;
|
|
39
|
+
requesterId: string;
|
|
40
|
+
}
|
|
41
|
+
/** 单个扩展片段:能力注册(setup 钩子注入)+ 可选配置贡献/能力位 */
|
|
42
|
+
export interface WorkflowExtension {
|
|
43
|
+
name: string;
|
|
44
|
+
setup(hooks: WorkflowHooks): void;
|
|
45
|
+
configContribution?: ConfigContribution;
|
|
53
46
|
capability?(rt: ParserRuntimeLike): {
|
|
54
47
|
ferret: boolean;
|
|
55
48
|
moderation: string | null;
|
|
56
49
|
};
|
|
57
50
|
}
|
|
51
|
+
/** 汇总各扩展能力位(日志用) */
|
|
52
|
+
export declare function collectCapabilities(extensions: WorkflowExtension[] | undefined, rt: ParserRuntimeLike): {
|
|
53
|
+
ferret: boolean;
|
|
54
|
+
moderation: string | null;
|
|
55
|
+
};
|
package/lib/extensions.js
CHANGED
|
@@ -1,2 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.collectCapabilities = collectCapabilities;
|
|
4
|
+
/** 汇总各扩展能力位(日志用) */
|
|
5
|
+
function collectCapabilities(extensions, rt) {
|
|
6
|
+
let ferret = false;
|
|
7
|
+
let moderation = null;
|
|
8
|
+
for (const e of extensions || []) {
|
|
9
|
+
if (typeof e.capability !== 'function')
|
|
10
|
+
continue;
|
|
11
|
+
try {
|
|
12
|
+
const c = e.capability(rt);
|
|
13
|
+
if (c?.ferret)
|
|
14
|
+
ferret = true;
|
|
15
|
+
if (c?.moderation)
|
|
16
|
+
moderation = c.moderation;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// 能力位探测失败不影响主链路
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return { ferret, moderation };
|
|
23
|
+
}
|
package/lib/host.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { type LoggerLike } from './logger';
|
|
11
11
|
import type { OutboundSender } from './sender';
|
|
12
|
-
import type {
|
|
12
|
+
import type { WorkflowExtension } from './extensions';
|
|
13
13
|
export interface VideoParserHost {
|
|
14
14
|
logger: LoggerLike;
|
|
15
15
|
/** 数据根目录(如 koishi ctx.baseDir);缺省时持久化走临时目录 */
|
|
@@ -19,7 +19,7 @@ export interface VideoParserHost {
|
|
|
19
19
|
/** 发送层实现(缺省时由宿主适配层注入) */
|
|
20
20
|
sender?: OutboundSender;
|
|
21
21
|
/** 扩展实现(部分覆盖;缺省能力由内置实现补齐) */
|
|
22
|
-
extensions?:
|
|
22
|
+
extensions?: WorkflowExtension[];
|
|
23
23
|
/** 过渡期:底层上下文(koishi Context 等),core 新代码不应依赖 */
|
|
24
24
|
context?: any;
|
|
25
25
|
}
|
|
@@ -31,7 +31,7 @@ export interface ParserRuntimeLike {
|
|
|
31
31
|
config: any;
|
|
32
32
|
http: any;
|
|
33
33
|
host: VideoParserHost;
|
|
34
|
-
extensions:
|
|
34
|
+
extensions: WorkflowExtension[];
|
|
35
35
|
[key: string]: any;
|
|
36
36
|
}
|
|
37
37
|
/**
|
package/lib/index.d.ts
CHANGED
|
@@ -21,7 +21,13 @@ export * from './engine/config';
|
|
|
21
21
|
export * from './engine/platform-config';
|
|
22
22
|
export * from './engine/translate';
|
|
23
23
|
export * from './engine/parser';
|
|
24
|
+
export * from './engine/generic';
|
|
24
25
|
export * from './engine/fetcher';
|
|
26
|
+
export * from './engine/compose';
|
|
27
|
+
export * from './engine/forward';
|
|
28
|
+
export * from './engine/flush';
|
|
25
29
|
export * from './engine/runtime';
|
|
26
30
|
export * from './engine/config-io';
|
|
31
|
+
export * from './workflow/hooks';
|
|
32
|
+
export * from './workflow/default';
|
|
27
33
|
export * from './registry';
|
package/lib/index.js
CHANGED
|
@@ -37,7 +37,13 @@ __exportStar(require("./engine/config"), exports);
|
|
|
37
37
|
__exportStar(require("./engine/platform-config"), exports);
|
|
38
38
|
__exportStar(require("./engine/translate"), exports);
|
|
39
39
|
__exportStar(require("./engine/parser"), exports);
|
|
40
|
+
__exportStar(require("./engine/generic"), exports);
|
|
40
41
|
__exportStar(require("./engine/fetcher"), exports);
|
|
42
|
+
__exportStar(require("./engine/compose"), exports);
|
|
43
|
+
__exportStar(require("./engine/forward"), exports);
|
|
44
|
+
__exportStar(require("./engine/flush"), exports);
|
|
41
45
|
__exportStar(require("./engine/runtime"), exports);
|
|
42
46
|
__exportStar(require("./engine/config-io"), exports);
|
|
47
|
+
__exportStar(require("./workflow/hooks"), exports);
|
|
48
|
+
__exportStar(require("./workflow/default"), exports);
|
|
43
49
|
__exportStar(require("./registry"), exports);
|
package/lib/registry.d.ts
CHANGED
|
@@ -1,20 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 已安装碎片包(平台 @sns-parse/platform-* / 扩展 @sns-parse/ext-*)的动态发现。
|
|
3
|
+
*
|
|
4
|
+
* - 平台:先聚合包(@sns-parse/platforms 的 definitions[]),再逐个候选粒度包,
|
|
5
|
+
* 按 type 去重合并(粒度包覆盖聚合条目);宿主可传自己的 require 锚点
|
|
6
|
+
* (koishi/CLI 包路径),pnpm strict 下也能发现宿主直依赖。
|
|
7
|
+
* - 扩展:loadWorkflowExtensions() 返回 WorkflowExtension[](含 setup 钩子),
|
|
8
|
+
* 未安装的自动缺席(能力/配置贡献同时缺席,不报错)。
|
|
9
|
+
*/
|
|
10
|
+
import { createRequire } from 'node:module';
|
|
1
11
|
import type { PlatformDefinition } from './platform';
|
|
2
12
|
import type { ConfigContribution } from './config';
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
13
|
+
import type { WorkflowExtension } from './extensions';
|
|
14
|
+
type NodeRequire = ReturnType<typeof createRequire>;
|
|
15
|
+
/** 收集已加载平台的定义(聚合包 + 粒度包并集;粒度包覆盖聚合条目) */
|
|
16
|
+
export declare function collectPlatformDefinitions(anchor?: NodeRequire): PlatformDefinition[];
|
|
17
|
+
/** 已加载平台的链接规则(按平台声明) */
|
|
18
|
+
export declare function collectPlatformLinkRules(anchor?: NodeRequire): {
|
|
8
19
|
pattern: RegExp;
|
|
9
20
|
type: string;
|
|
10
21
|
}[];
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
export
|
|
18
|
-
export declare function loadExtensionContributions(): ConfigContribution[];
|
|
19
|
-
/** 合并所有已安装扩展的能力片段 */
|
|
20
|
-
export declare function loadExtensionImplementations(): Partial<VideoParserExtensions>;
|
|
22
|
+
/** 探测并实例化全部已安装扩展(WorkflowExtension[];含 setup 钩子) */
|
|
23
|
+
export declare function loadWorkflowExtensions(anchor?: NodeRequire): WorkflowExtension[];
|
|
24
|
+
/** 已安装扩展的配置贡献(settings 动态渲染用) */
|
|
25
|
+
export declare function loadExtensionContributions(anchor?: NodeRequire): ConfigContribution[];
|
|
26
|
+
/** 兼容旧名(已废弃:请用 loadWorkflowExtensions) */
|
|
27
|
+
export declare function loadExtensionImplementations(anchor?: NodeRequire): WorkflowExtension[];
|
|
28
|
+
export {};
|
package/lib/registry.js
CHANGED
|
@@ -2,76 +2,100 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.collectPlatformDefinitions = collectPlatformDefinitions;
|
|
4
4
|
exports.collectPlatformLinkRules = collectPlatformLinkRules;
|
|
5
|
-
exports.
|
|
5
|
+
exports.loadWorkflowExtensions = loadWorkflowExtensions;
|
|
6
6
|
exports.loadExtensionContributions = loadExtensionContributions;
|
|
7
7
|
exports.loadExtensionImplementations = loadExtensionImplementations;
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* 已安装碎片包(平台 @sns-parse/platform-* / 扩展 @sns-parse/ext-*)的动态发现。
|
|
10
10
|
*
|
|
11
|
-
* -
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* - 平台:先聚合包(@sns-parse/platforms 的 definitions[]),再逐个候选粒度包,
|
|
12
|
+
* 按 type 去重合并(粒度包覆盖聚合条目);宿主可传自己的 require 锚点
|
|
13
|
+
* (koishi/CLI 包路径),pnpm strict 下也能发现宿主直依赖。
|
|
14
|
+
* - 扩展:loadWorkflowExtensions() 返回 WorkflowExtension[](含 setup 钩子),
|
|
15
|
+
* 未安装的自动缺席(能力/配置贡献同时缺席,不报错)。
|
|
14
16
|
*/
|
|
15
17
|
const node_module_1 = require("node:module");
|
|
16
|
-
const
|
|
18
|
+
const fallbackRequire = (0, node_module_1.createRequire)(typeof __filename !== 'undefined' ? __filename : process.cwd() + '/');
|
|
19
|
+
function req(anchor) {
|
|
20
|
+
return anchor || fallbackRequire;
|
|
21
|
+
}
|
|
17
22
|
const PLATFORM_TYPES = [
|
|
18
23
|
'bilibili', 'douyin', 'kuaishou', 'xiaohongshu', 'weibo', 'xigua', 'youtube',
|
|
19
24
|
'tiktok', 'acfun', 'zhihu', 'weishi', 'huya', 'haokan', 'meipai', 'twitter',
|
|
20
25
|
'instagram', 'doubao', 'doubao_image', 'jimeng', 'oasis', 'wechat_channel',
|
|
21
26
|
'lishi', 'quanmin', 'pipigx', 'pipixia', 'zuiyou', 'toutiao',
|
|
22
27
|
];
|
|
23
|
-
/**
|
|
24
|
-
function collectPlatformDefinitions() {
|
|
25
|
-
const
|
|
28
|
+
/** 收集已加载平台的定义(聚合包 + 粒度包并集;粒度包覆盖聚合条目) */
|
|
29
|
+
function collectPlatformDefinitions(anchor) {
|
|
30
|
+
const nodeRequire = req(anchor);
|
|
31
|
+
const byType = new Map();
|
|
32
|
+
// 1) 聚合包(若安装):一次拿全声明
|
|
33
|
+
try {
|
|
34
|
+
const agg = nodeRequire('@sns-parse/platforms');
|
|
35
|
+
const defs = agg?.definitions || agg?.default?.definitions || [];
|
|
36
|
+
for (const d of defs) {
|
|
37
|
+
if (d && d.type && !byType.has(d.type))
|
|
38
|
+
byType.set(d.type, d);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// 未装聚合包 → 只看粒度包
|
|
43
|
+
}
|
|
44
|
+
// 2) 粒度包(可覆盖聚合条目)
|
|
26
45
|
for (const type of PLATFORM_TYPES) {
|
|
27
46
|
try {
|
|
28
47
|
const mod = nodeRequire(`@sns-parse/platform-${type}`);
|
|
29
48
|
const def = mod.default || mod[type];
|
|
30
49
|
if (def && def.type)
|
|
31
|
-
|
|
50
|
+
byType.set(def.type, def);
|
|
32
51
|
}
|
|
33
52
|
catch {
|
|
34
|
-
//
|
|
53
|
+
// 未装该平台 → 缺席
|
|
35
54
|
}
|
|
36
55
|
}
|
|
37
|
-
return
|
|
56
|
+
return [...byType.values()];
|
|
38
57
|
}
|
|
39
|
-
/**
|
|
40
|
-
function collectPlatformLinkRules() {
|
|
41
|
-
return collectPlatformDefinitions().flatMap(d => d.rules.map(pattern => ({ pattern, type: d.type })));
|
|
58
|
+
/** 已加载平台的链接规则(按平台声明) */
|
|
59
|
+
function collectPlatformLinkRules(anchor) {
|
|
60
|
+
return collectPlatformDefinitions(anchor).flatMap(d => d.rules.map(pattern => ({ pattern, type: d.type })));
|
|
42
61
|
}
|
|
43
62
|
const EXTENSION_SPECS = [
|
|
44
|
-
{ name: 'ext-nsfw',
|
|
63
|
+
{ name: 'ext-nsfw', extensionKey: 'nsfwExtension', contributionKey: 'nsfwConfigContribution' },
|
|
45
64
|
{ name: 'ext-merge', extensionKey: 'mergeExtension' },
|
|
46
65
|
{ name: 'ext-translate', extensionKey: 'translateExtension' },
|
|
47
66
|
{ name: 'ext-gif', extensionKey: 'gifExtension' },
|
|
48
67
|
];
|
|
49
|
-
/**
|
|
50
|
-
function
|
|
68
|
+
/** 探测并实例化全部已安装扩展(WorkflowExtension[];含 setup 钩子) */
|
|
69
|
+
function loadWorkflowExtensions(anchor) {
|
|
70
|
+
const nodeRequire = req(anchor);
|
|
51
71
|
const out = [];
|
|
52
72
|
for (const spec of EXTENSION_SPECS) {
|
|
53
73
|
try {
|
|
54
74
|
const mod = nodeRequire(`@sns-parse/${spec.name}`);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
75
|
+
const factory = mod?.[spec.extensionKey];
|
|
76
|
+
if (typeof factory !== 'function')
|
|
77
|
+
continue;
|
|
78
|
+
const ext = factory();
|
|
79
|
+
if (!ext || typeof ext.setup !== 'function')
|
|
80
|
+
continue;
|
|
81
|
+
if (!ext.configContribution && spec.contributionKey && mod[spec.contributionKey]) {
|
|
82
|
+
ext.configContribution = mod[spec.contributionKey];
|
|
83
|
+
}
|
|
84
|
+
out.push(ext);
|
|
62
85
|
}
|
|
63
86
|
catch {
|
|
64
|
-
//
|
|
87
|
+
// 未安装的扩展 → 缺席(能力与配置贡献同时缺席)
|
|
65
88
|
}
|
|
66
89
|
}
|
|
67
90
|
return out;
|
|
68
91
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
92
|
+
/** 已安装扩展的配置贡献(settings 动态渲染用) */
|
|
93
|
+
function loadExtensionContributions(anchor) {
|
|
94
|
+
return loadWorkflowExtensions(anchor)
|
|
95
|
+
.map(e => e.configContribution)
|
|
72
96
|
.filter((c) => !!c);
|
|
73
97
|
}
|
|
74
|
-
/**
|
|
75
|
-
function loadExtensionImplementations() {
|
|
76
|
-
return
|
|
98
|
+
/** 兼容旧名(已废弃:请用 loadWorkflowExtensions) */
|
|
99
|
+
function loadExtensionImplementations(anchor) {
|
|
100
|
+
return loadWorkflowExtensions(anchor);
|
|
77
101
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.createDefaultPipeline = createDefaultPipeline;
|
|
37
|
+
const hooks_1 = require("./hooks");
|
|
38
|
+
const fetcher = __importStar(require("../engine/fetcher"));
|
|
39
|
+
const compose = __importStar(require("../engine/compose"));
|
|
40
|
+
const forward = __importStar(require("../engine/forward"));
|
|
41
|
+
async function sendDispatch(rt, input) {
|
|
42
|
+
const { items, session, sender, quoteId } = input;
|
|
43
|
+
const config = rt.config || {};
|
|
44
|
+
const forwardAllowed = config.enableForward && (session.platform === 'onebot' || session.platform === 'satori');
|
|
45
|
+
if (forwardAllowed) {
|
|
46
|
+
await forward.sendForward(rt, sender, session, items);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (config.sendStrategy === 'split') {
|
|
50
|
+
for (const item of items)
|
|
51
|
+
await compose.sendSplit(rt, sender, session, item, { quoteId });
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
for (const item of items) {
|
|
55
|
+
await compose.sendSingle(rt, sender, session, item, { quoteId });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** 构建带全部基线实现的管道;随后调用各扩展 setup(hooks) 注入/覆盖 */
|
|
59
|
+
function createDefaultPipeline(rt) {
|
|
60
|
+
const p = new hooks_1.Pipeline(rt);
|
|
61
|
+
p.replace('parse', (input, r) => fetcher.fetchApi(r, input.url, input.type, input.fieldMapping, input.platformConf));
|
|
62
|
+
// 通用链接解析(预留):基线=未实现(null)——非平台链接不进入解析流程;
|
|
63
|
+
// 后续由宿主/扩展 replace 注入(OG/HTML/LLM,契约见 engine/generic.ts)
|
|
64
|
+
p.replace('parse.generic', async () => null);
|
|
65
|
+
p.replace('translate', async () => null);
|
|
66
|
+
p.replace('media.image', async (input) => ({ kind: 'raw', url: input.url }));
|
|
67
|
+
p.replace('media.video', async (input) => ({ kind: 'raw', url: input.videoUrl }));
|
|
68
|
+
p.replace('media.merged', async (input) => ({ kind: 'raw', buffer: input.buffer, url: input.refUrl }));
|
|
69
|
+
p.replace('merge', async () => null);
|
|
70
|
+
p.replace('transcode', async () => null);
|
|
71
|
+
p.replace('compose', (input, r) => compose.buildUnits(r, input.item));
|
|
72
|
+
p.replace('send', (input, r) => sendDispatch(r, input));
|
|
73
|
+
return p;
|
|
74
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 工作流阶段钩子管道(core 内建完整工作流,扩展经钩子注入/修改)。
|
|
3
|
+
*
|
|
4
|
+
* 每阶段结构:{ before[], impl, after[] }
|
|
5
|
+
* - before:前置,可改输入(返回新输入;返回 undefined 表示不变)
|
|
6
|
+
* - after:后置,可改输出(返回新输出;返回 undefined 表示不变)
|
|
7
|
+
* - replace:整体替换该阶段实现(后注册者胜)
|
|
8
|
+
*
|
|
9
|
+
* 基线实现见 workflow/default.ts(无扩展时全链路可跑:
|
|
10
|
+
* 不合并逐张发 / GIF 退化发视频 / 不翻译 / 媒体直通)。
|
|
11
|
+
*/
|
|
12
|
+
import type { ParsedData } from '../types';
|
|
13
|
+
import type { ImageOutcome, VideoOutcome, TranslateResult, GifOptions, MediaKind, VideoMeta } from '../extensions';
|
|
14
|
+
import type { ProcessedItem, MessageUnit } from '../engine/compose';
|
|
15
|
+
import type { OutboundSender, SessionLike } from '../sender';
|
|
16
|
+
import type { GenericLinkInput, GenericLinkResult } from '../engine/generic';
|
|
17
|
+
export type StageName = 'parse' | 'parse.generic' | 'translate' | 'media.image' | 'media.video' | 'media.merged' | 'merge' | 'transcode' | 'compose' | 'send';
|
|
18
|
+
export interface ParseInput {
|
|
19
|
+
url: string;
|
|
20
|
+
type: string;
|
|
21
|
+
fieldMapping?: Record<string, string>;
|
|
22
|
+
platformConf?: any;
|
|
23
|
+
}
|
|
24
|
+
export interface TranslateInput {
|
|
25
|
+
text: string;
|
|
26
|
+
target: string;
|
|
27
|
+
sourceLang?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface MediaImageInput {
|
|
30
|
+
platform: string;
|
|
31
|
+
url: string;
|
|
32
|
+
kind: MediaKind;
|
|
33
|
+
}
|
|
34
|
+
export interface MediaVideoInput {
|
|
35
|
+
platform: string;
|
|
36
|
+
videoUrl: string;
|
|
37
|
+
coverUrl: string;
|
|
38
|
+
meta: VideoMeta;
|
|
39
|
+
}
|
|
40
|
+
export interface MediaMergedInput {
|
|
41
|
+
platform: string;
|
|
42
|
+
buffer: Buffer;
|
|
43
|
+
refUrl: string;
|
|
44
|
+
}
|
|
45
|
+
export interface MergeInput {
|
|
46
|
+
urls: string[];
|
|
47
|
+
}
|
|
48
|
+
export type MergeResult = {
|
|
49
|
+
buffer: Buffer;
|
|
50
|
+
layout?: any;
|
|
51
|
+
} | null;
|
|
52
|
+
export interface TranscodeInput {
|
|
53
|
+
url: string;
|
|
54
|
+
durationSec: number;
|
|
55
|
+
opts: GifOptions;
|
|
56
|
+
}
|
|
57
|
+
export interface ComposeInput {
|
|
58
|
+
item: ProcessedItem;
|
|
59
|
+
}
|
|
60
|
+
export interface SendInput {
|
|
61
|
+
items: ProcessedItem[];
|
|
62
|
+
session: SessionLike;
|
|
63
|
+
sender: OutboundSender;
|
|
64
|
+
quoteId?: string;
|
|
65
|
+
}
|
|
66
|
+
export interface StageIO {
|
|
67
|
+
parse: {
|
|
68
|
+
input: ParseInput;
|
|
69
|
+
output: ParsedData;
|
|
70
|
+
};
|
|
71
|
+
'parse.generic': {
|
|
72
|
+
input: GenericLinkInput;
|
|
73
|
+
output: GenericLinkResult | null;
|
|
74
|
+
};
|
|
75
|
+
translate: {
|
|
76
|
+
input: TranslateInput;
|
|
77
|
+
output: TranslateResult | null;
|
|
78
|
+
};
|
|
79
|
+
'media.image': {
|
|
80
|
+
input: MediaImageInput;
|
|
81
|
+
output: ImageOutcome;
|
|
82
|
+
};
|
|
83
|
+
'media.video': {
|
|
84
|
+
input: MediaVideoInput;
|
|
85
|
+
output: VideoOutcome;
|
|
86
|
+
};
|
|
87
|
+
'media.merged': {
|
|
88
|
+
input: MediaMergedInput;
|
|
89
|
+
output: ImageOutcome | null;
|
|
90
|
+
};
|
|
91
|
+
merge: {
|
|
92
|
+
input: MergeInput;
|
|
93
|
+
output: MergeResult;
|
|
94
|
+
};
|
|
95
|
+
transcode: {
|
|
96
|
+
input: TranscodeInput;
|
|
97
|
+
output: Buffer | null;
|
|
98
|
+
};
|
|
99
|
+
compose: {
|
|
100
|
+
input: ComposeInput;
|
|
101
|
+
output: MessageUnit[];
|
|
102
|
+
};
|
|
103
|
+
send: {
|
|
104
|
+
input: SendInput;
|
|
105
|
+
output: void;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
export type StageImpl<S extends StageName = StageName> = (input: StageIO[S]['input'], rt: any) => StageIO[S]['output'] | Promise<StageIO[S]['output']>;
|
|
109
|
+
export type BeforeHandler<S extends StageName = StageName> = (input: StageIO[S]['input'], rt: any) => StageIO[S]['input'] | void | Promise<StageIO[S]['input'] | void>;
|
|
110
|
+
export type AfterHandler<S extends StageName = StageName> = (output: StageIO[S]['output'], input: StageIO[S]['input'], rt: any) => StageIO[S]['output'] | void | Promise<StageIO[S]['output'] | void>;
|
|
111
|
+
/** 扩展注入面:before/after 可插入,replace 可整体替换(后注册者胜) */
|
|
112
|
+
export interface WorkflowHooks {
|
|
113
|
+
before<S extends StageName>(stage: S, h: BeforeHandler<S>): void;
|
|
114
|
+
after<S extends StageName>(stage: S, h: AfterHandler<S>): void;
|
|
115
|
+
replace<S extends StageName>(stage: S, h: StageImpl<S>): void;
|
|
116
|
+
}
|
|
117
|
+
export declare class Pipeline implements WorkflowHooks {
|
|
118
|
+
rt: any;
|
|
119
|
+
private impls;
|
|
120
|
+
private befores;
|
|
121
|
+
private afters;
|
|
122
|
+
constructor(rt: any);
|
|
123
|
+
before<S extends StageName>(stage: S, h: BeforeHandler<S>): void;
|
|
124
|
+
after<S extends StageName>(stage: S, h: AfterHandler<S>): void;
|
|
125
|
+
replace<S extends StageName>(stage: S, h: StageImpl<S>): void;
|
|
126
|
+
run<S extends StageName>(stage: S, input: StageIO[S]['input']): Promise<StageIO[S]['output']>;
|
|
127
|
+
}
|
|
128
|
+
/** 取(或懒挂)runtime 的管道;裸 rt 对象也能工作(自动挂默认管道)。 */
|
|
129
|
+
export declare function ensurePipeline(rt: any): Pipeline;
|
|
130
|
+
export declare function runStage<S extends StageName>(rt: any, stage: S, input: StageIO[S]['input']): Promise<StageIO[S]['output']>;
|