@sns-parse/core 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/LICENSE +21 -0
- package/README.md +30 -0
- package/lib/extensions.d.ts +57 -0
- package/lib/extensions.js +2 -0
- package/lib/host.d.ts +41 -0
- package/lib/host.js +31 -0
- package/lib/index.d.ts +9 -0
- package/lib/index.js +25 -0
- package/lib/logger.d.ts +30 -0
- package/lib/logger.js +66 -0
- package/lib/platform.d.ts +23 -0
- package/lib/platform.js +2 -0
- package/lib/sender.d.ts +65 -0
- package/lib/sender.js +27 -0
- package/lib/types.d.ts +74 -0
- package/lib/types.js +2 -0
- package/package.json +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 char46
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# @sns-parse/core
|
|
2
|
+
|
|
3
|
+
sns-parse 分层架构的**核心契约包**:所有层共享的类型与抽象,不依赖 Koishi / CLI 任一 SDK。
|
|
4
|
+
|
|
5
|
+
## 内容
|
|
6
|
+
|
|
7
|
+
| 模块 | 说明 |
|
|
8
|
+
|------|------|
|
|
9
|
+
| `types` | `ParsedData`、`LinkMatch`、`CustomPlatformConfig` 等数据模型 |
|
|
10
|
+
| `logger` | 可注入的 `LoggerLike`(默认静默;宿主注入 Koishi Logger / console) |
|
|
11
|
+
| `sender` | 发送层无关 IR `OutboundElement` + `OutboundSender` 契约 |
|
|
12
|
+
| `extensions` | `VideoParserExtensions` 扩展契约(NSFW/合并/翻译/GIF) |
|
|
13
|
+
| `platform` | `PlatformDefinition` 平台定义契约 |
|
|
14
|
+
| `host` | `VideoParserHost` 宿主抽象、`ParserRuntimeLike`、`createHost()` |
|
|
15
|
+
|
|
16
|
+
## 安装
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm i @sns-parse/core
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 用法
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { createHost, el, type OutboundSender, type PlatformDefinition } from '@sns-parse/core'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 许可
|
|
29
|
+
|
|
30
|
+
MIT
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 扩展契约:core 不直接依赖 NSFW/合并/翻译/GIF 实现,而是通过可注入的
|
|
3
|
+
* VideoParserExtensions 调用。宿主可整体替换或按需覆盖单项能力。
|
|
4
|
+
*
|
|
5
|
+
* 默认实现由 extensions 包(@sns-parse/extensions)提供;runtime 会合并默认实现,
|
|
6
|
+
* 因此 core 编排层可直接调用,无需判空。
|
|
7
|
+
*/
|
|
8
|
+
import type { ParserRuntimeLike } from './host';
|
|
9
|
+
export type MediaKind = 'cover' | 'image' | 'avatar' | 'music-cover';
|
|
10
|
+
export interface ImageOutcome {
|
|
11
|
+
/** raw=原图 url;scrambled=混淆 buffer+token;link=仅链接文字;drop=不发送 */
|
|
12
|
+
kind: 'raw' | 'scrambled' | 'link' | 'drop';
|
|
13
|
+
url?: string;
|
|
14
|
+
buffer?: Buffer;
|
|
15
|
+
token?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface VideoOutcome {
|
|
18
|
+
/** raw=照发;card=群内纯文字卡片+token(无封面无视频);link=文字卡片+原链接;drop=仅文字卡片 */
|
|
19
|
+
kind: 'raw' | 'card' | 'link' | 'drop';
|
|
20
|
+
url?: string;
|
|
21
|
+
token?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface TranslateResult {
|
|
24
|
+
text: string;
|
|
25
|
+
provider: string;
|
|
26
|
+
}
|
|
27
|
+
export interface GifOptions {
|
|
28
|
+
maxWidth: number;
|
|
29
|
+
fps: number;
|
|
30
|
+
maxDurationSec: number;
|
|
31
|
+
}
|
|
32
|
+
/** 扩展能力集合(全部可选;runtime 会与默认实现合并) */
|
|
33
|
+
export interface VideoParserExtensions {
|
|
34
|
+
/** 同源切图合并;返回 null 表示不合并 */
|
|
35
|
+
mergeImages?(rt: ParserRuntimeLike, urls: string[]): Promise<{
|
|
36
|
+
buffer: Buffer;
|
|
37
|
+
} | null>;
|
|
38
|
+
/** 单张出站图片处理(NSFW 策略) */
|
|
39
|
+
processImage?(rt: ParserRuntimeLike, platform: string, url: string, kind: MediaKind): Promise<ImageOutcome>;
|
|
40
|
+
/** 出站视频处理(NSFW 策略) */
|
|
41
|
+
processVideo?(rt: ParserRuntimeLike, platform: string, videoUrl: string, coverUrl: string, meta: {
|
|
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
|
+
/** 内容安全能力状态(启动日志) */
|
|
53
|
+
capability?(rt: ParserRuntimeLike): {
|
|
54
|
+
ferret: boolean;
|
|
55
|
+
moderation: string | null;
|
|
56
|
+
};
|
|
57
|
+
}
|
package/lib/host.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 宿主抽象:core 只依赖 VideoParserHost,不依赖 Koishi/CLI 任一 SDK。
|
|
3
|
+
*
|
|
4
|
+
* - logger:分级日志(Koishi 注入 Logger,CLI 注入 console 实现)
|
|
5
|
+
* - baseDir:数据根目录(持久化缓存/Vault 落盘用)
|
|
6
|
+
* - getService:可选服务探测(如 ferret-transform 图片混淆)
|
|
7
|
+
* - sender:发送层实现(见 core/sender.ts)
|
|
8
|
+
* - extensions:扩展能力(见 core/extensions.ts)
|
|
9
|
+
*/
|
|
10
|
+
import { type LoggerLike } from './logger';
|
|
11
|
+
import type { OutboundSender } from './sender';
|
|
12
|
+
import type { VideoParserExtensions } from './extensions';
|
|
13
|
+
export interface VideoParserHost {
|
|
14
|
+
logger: LoggerLike;
|
|
15
|
+
/** 数据根目录(如 koishi ctx.baseDir);缺省时持久化走临时目录 */
|
|
16
|
+
baseDir?: string;
|
|
17
|
+
/** 探测宿主提供的可选服务 */
|
|
18
|
+
getService?<T = any>(name: string): T | undefined;
|
|
19
|
+
/** 发送层实现(缺省时由宿主适配层注入) */
|
|
20
|
+
sender?: OutboundSender;
|
|
21
|
+
/** 扩展实现(部分覆盖;缺省能力由内置实现补齐) */
|
|
22
|
+
extensions?: VideoParserExtensions;
|
|
23
|
+
/** 过渡期:底层上下文(koishi Context 等),core 新代码不应依赖 */
|
|
24
|
+
context?: any;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 运行期最小面(core 契约):上层 runtime 结构上满足即可。
|
|
28
|
+
* 具体实现由各宿主/引擎提供,core 仅以此类型声明扩展方法的入参。
|
|
29
|
+
*/
|
|
30
|
+
export interface ParserRuntimeLike {
|
|
31
|
+
config: any;
|
|
32
|
+
http: any;
|
|
33
|
+
host: VideoParserHost;
|
|
34
|
+
extensions: VideoParserExtensions;
|
|
35
|
+
[key: string]: any;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 从宿主上下文(koishi Context / 任意对象)构建 Host。
|
|
39
|
+
* 已是 Host(含 getService 方法)时原样返回。
|
|
40
|
+
*/
|
|
41
|
+
export declare function createHost(source: any): VideoParserHost;
|
package/lib/host.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createHost = createHost;
|
|
4
|
+
/**
|
|
5
|
+
* 宿主抽象:core 只依赖 VideoParserHost,不依赖 Koishi/CLI 任一 SDK。
|
|
6
|
+
*
|
|
7
|
+
* - logger:分级日志(Koishi 注入 Logger,CLI 注入 console 实现)
|
|
8
|
+
* - baseDir:数据根目录(持久化缓存/Vault 落盘用)
|
|
9
|
+
* - getService:可选服务探测(如 ferret-transform 图片混淆)
|
|
10
|
+
* - sender:发送层实现(见 core/sender.ts)
|
|
11
|
+
* - extensions:扩展能力(见 core/extensions.ts)
|
|
12
|
+
*/
|
|
13
|
+
const logger_1 = require("./logger");
|
|
14
|
+
/**
|
|
15
|
+
* 从宿主上下文(koishi Context / 任意对象)构建 Host。
|
|
16
|
+
* 已是 Host(含 getService 方法)时原样返回。
|
|
17
|
+
*/
|
|
18
|
+
function createHost(source) {
|
|
19
|
+
if (source && typeof source === 'object' && typeof source.getService === 'function') {
|
|
20
|
+
return source;
|
|
21
|
+
}
|
|
22
|
+
const ctx = source && typeof source === 'object' ? source : {};
|
|
23
|
+
return {
|
|
24
|
+
logger: ctx.logger ?? logger_1.silentLogger,
|
|
25
|
+
baseDir: ctx.baseDir,
|
|
26
|
+
getService: (name) => ctx[name],
|
|
27
|
+
sender: ctx.sender,
|
|
28
|
+
extensions: ctx.extensions,
|
|
29
|
+
context: ctx,
|
|
30
|
+
};
|
|
31
|
+
}
|
package/lib/index.d.ts
ADDED
package/lib/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
/**
|
|
18
|
+
* @sns-parse/core 公共出口。
|
|
19
|
+
*/
|
|
20
|
+
__exportStar(require("./types"), exports);
|
|
21
|
+
__exportStar(require("./logger"), exports);
|
|
22
|
+
__exportStar(require("./sender"), exports);
|
|
23
|
+
__exportStar(require("./extensions"), exports);
|
|
24
|
+
__exportStar(require("./platform"), exports);
|
|
25
|
+
__exportStar(require("./host"), exports);
|
package/lib/logger.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 宿主无关的可注入日志层。
|
|
3
|
+
*
|
|
4
|
+
* - core 默认使用静默 logger(测试/嵌入式场景不产生噪音)
|
|
5
|
+
* - Koishi 层启动时 setLogger(ctx.logger)
|
|
6
|
+
* - CLI 层启动时 setLogger(consoleLogger)
|
|
7
|
+
*
|
|
8
|
+
* 日志分级:
|
|
9
|
+
* - info/warn/error 正常输出
|
|
10
|
+
* - debug/verbose 级配合 setVerboseLogging(true) 提升为 info
|
|
11
|
+
*/
|
|
12
|
+
export interface LoggerLike {
|
|
13
|
+
info(...args: any[]): void;
|
|
14
|
+
warn(...args: any[]): void;
|
|
15
|
+
error(...args: any[]): void;
|
|
16
|
+
debug(...args: any[]): void;
|
|
17
|
+
success?(...args: any[]): void;
|
|
18
|
+
}
|
|
19
|
+
/** 静默实现:默认注入,保证无宿主时不产生副作用 */
|
|
20
|
+
export declare const silentLogger: LoggerLike;
|
|
21
|
+
/** 控制台实现:CLI / 独立使用 */
|
|
22
|
+
export declare const consoleLogger: LoggerLike;
|
|
23
|
+
/** 注入宿主日志实现(插件启动时调用一次) */
|
|
24
|
+
export declare function setLogger(l: LoggerLike): void;
|
|
25
|
+
export declare function getLogger(): LoggerLike;
|
|
26
|
+
export declare function setVerboseLogging(v: boolean): void;
|
|
27
|
+
/** 始终代理到当前注入的 logger,支持运行期替换 */
|
|
28
|
+
export declare const logger: LoggerLike;
|
|
29
|
+
/** debug/verbose 级日志:debug 配置开启后提升为 info,否则 debug 级 */
|
|
30
|
+
export declare function debugLog(...args: any[]): void;
|
package/lib/logger.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 宿主无关的可注入日志层。
|
|
4
|
+
*
|
|
5
|
+
* - core 默认使用静默 logger(测试/嵌入式场景不产生噪音)
|
|
6
|
+
* - Koishi 层启动时 setLogger(ctx.logger)
|
|
7
|
+
* - CLI 层启动时 setLogger(consoleLogger)
|
|
8
|
+
*
|
|
9
|
+
* 日志分级:
|
|
10
|
+
* - info/warn/error 正常输出
|
|
11
|
+
* - debug/verbose 级配合 setVerboseLogging(true) 提升为 info
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.logger = exports.consoleLogger = exports.silentLogger = void 0;
|
|
15
|
+
exports.setLogger = setLogger;
|
|
16
|
+
exports.getLogger = getLogger;
|
|
17
|
+
exports.setVerboseLogging = setVerboseLogging;
|
|
18
|
+
exports.debugLog = debugLog;
|
|
19
|
+
/** 静默实现:默认注入,保证无宿主时不产生副作用 */
|
|
20
|
+
exports.silentLogger = {
|
|
21
|
+
info() { },
|
|
22
|
+
warn() { },
|
|
23
|
+
error() { },
|
|
24
|
+
debug() { },
|
|
25
|
+
success() { },
|
|
26
|
+
};
|
|
27
|
+
/** 控制台实现:CLI / 独立使用 */
|
|
28
|
+
exports.consoleLogger = {
|
|
29
|
+
info: (...a) => console.log(...a),
|
|
30
|
+
warn: (...a) => console.warn(...a),
|
|
31
|
+
error: (...a) => console.error(...a),
|
|
32
|
+
debug: (...a) => { if (process.env.DEBUG)
|
|
33
|
+
console.debug(...a); },
|
|
34
|
+
};
|
|
35
|
+
let current = exports.silentLogger;
|
|
36
|
+
let verbose = false;
|
|
37
|
+
/** 注入宿主日志实现(插件启动时调用一次) */
|
|
38
|
+
function setLogger(l) { current = l ?? exports.silentLogger; }
|
|
39
|
+
function getLogger() { return current; }
|
|
40
|
+
function setVerboseLogging(v) { verbose = v; }
|
|
41
|
+
/** 始终代理到当前注入的 logger,支持运行期替换 */
|
|
42
|
+
exports.logger = {
|
|
43
|
+
info: (...a) => current.info(...a),
|
|
44
|
+
warn: (...a) => current.warn(...a),
|
|
45
|
+
error: (...a) => current.error(...a),
|
|
46
|
+
debug: (...a) => current.debug(...a),
|
|
47
|
+
success: (...a) => (current.success ?? current.info)(...a),
|
|
48
|
+
};
|
|
49
|
+
function fmt(args) {
|
|
50
|
+
return args.map(a => {
|
|
51
|
+
try {
|
|
52
|
+
return typeof a === 'object' ? JSON.stringify(a) : String(a);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return '[unserializable]';
|
|
56
|
+
}
|
|
57
|
+
}).join(' ');
|
|
58
|
+
}
|
|
59
|
+
/** debug/verbose 级日志:debug 配置开启后提升为 info,否则 debug 级 */
|
|
60
|
+
function debugLog(...args) {
|
|
61
|
+
const line = fmt(args);
|
|
62
|
+
if (verbose)
|
|
63
|
+
exports.logger.info(line);
|
|
64
|
+
else
|
|
65
|
+
exports.logger.debug(line);
|
|
66
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 平台定义契约:每个平台的自描述单元,是「每平台一个包」的基础。
|
|
3
|
+
*
|
|
4
|
+
* 一个 PlatformDefinition 应自包含该平台的全部静态信息(链接规则、专属 API、
|
|
5
|
+
* 提示等);动态解析逻辑(normalize/parseNative)在后续阶段以可选钩子加入。
|
|
6
|
+
*/
|
|
7
|
+
export interface PlatformDefinition {
|
|
8
|
+
/** 平台类型标识(与 config.platformEnabled 的键一致) */
|
|
9
|
+
type: string;
|
|
10
|
+
/** 链接识别规则(全局标志由聚合层统一处理) */
|
|
11
|
+
rules: RegExp[];
|
|
12
|
+
/** 平台专属 API(双网关;缺省走主 API) */
|
|
13
|
+
dedicated?: {
|
|
14
|
+
/** 旧网关 api.bugpk.com(无需 Key) */
|
|
15
|
+
legacy?: string;
|
|
16
|
+
/** 新网关 api-new.ifphp.com(需 Key) */
|
|
17
|
+
next?: string;
|
|
18
|
+
};
|
|
19
|
+
/** 是否默认优先专属 API(可被 config.platformDedicatedFirst 覆盖) */
|
|
20
|
+
dedicatedFirst?: boolean;
|
|
21
|
+
/** 解析失败时的平台特定引导提示 */
|
|
22
|
+
hints?: string[];
|
|
23
|
+
}
|
package/lib/platform.js
ADDED
package/lib/sender.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 发送层契约(core 与宿主解耦的关键接缝)。
|
|
3
|
+
*
|
|
4
|
+
* core 只构建与发送层无关的「出站元素」(OutboundElement),由宿主提供的
|
|
5
|
+
* OutboundSender 翻译为具体实现:
|
|
6
|
+
* - Koishi 层:翻译为 koishi `h` 元素并调用 `session.send`
|
|
7
|
+
* - CLI 层:翻译为纯文本 / 下载动作
|
|
8
|
+
*
|
|
9
|
+
* 这样 core 的 flush/compose/forward 不依赖任何宿主 SDK。
|
|
10
|
+
*/
|
|
11
|
+
/** 出站元素(发送层无关 IR) */
|
|
12
|
+
export type OutboundElement = {
|
|
13
|
+
type: 'text';
|
|
14
|
+
text: string;
|
|
15
|
+
} | {
|
|
16
|
+
type: 'image';
|
|
17
|
+
url?: string;
|
|
18
|
+
buffer?: Buffer;
|
|
19
|
+
mime?: string;
|
|
20
|
+
} | {
|
|
21
|
+
type: 'video';
|
|
22
|
+
url: string;
|
|
23
|
+
} | {
|
|
24
|
+
type: 'audio';
|
|
25
|
+
url: string;
|
|
26
|
+
} | {
|
|
27
|
+
type: 'quote';
|
|
28
|
+
id: string;
|
|
29
|
+
};
|
|
30
|
+
/** 合并转发的一个气泡 */
|
|
31
|
+
export interface ForwardBubble {
|
|
32
|
+
content: OutboundElement[];
|
|
33
|
+
/** 气泡作者 id(缺省由发送层用 session.selfId) */
|
|
34
|
+
id?: string;
|
|
35
|
+
/** 气泡作者昵称 */
|
|
36
|
+
name?: string;
|
|
37
|
+
}
|
|
38
|
+
/** 会话最小面(Koishi session / CLI 上下文均可满足) */
|
|
39
|
+
export interface SessionLike {
|
|
40
|
+
platform?: string;
|
|
41
|
+
selfId?: string;
|
|
42
|
+
userId?: string;
|
|
43
|
+
channelId?: string;
|
|
44
|
+
guildId?: string;
|
|
45
|
+
messageId?: string;
|
|
46
|
+
send?(content: any): Promise<any>;
|
|
47
|
+
[key: string]: any;
|
|
48
|
+
}
|
|
49
|
+
export interface OutboundSender {
|
|
50
|
+
/** 发送一组出站元素;retries 覆盖默认重试次数 */
|
|
51
|
+
send(session: SessionLike, elements: OutboundElement[], retries?: number): Promise<any>;
|
|
52
|
+
/** 发送合并转发(每个 ForwardBubble 一个气泡) */
|
|
53
|
+
sendForward(session: SessionLike, bubbles: ForwardBubble[], retries?: number): Promise<any>;
|
|
54
|
+
}
|
|
55
|
+
/** 出站元素构造器(避免各模块散落字面量) */
|
|
56
|
+
export declare const el: {
|
|
57
|
+
text: (text: string) => OutboundElement;
|
|
58
|
+
image: (url: string) => OutboundElement;
|
|
59
|
+
imageBuffer: (buffer: Buffer, mime: string) => OutboundElement;
|
|
60
|
+
video: (url: string) => OutboundElement;
|
|
61
|
+
audio: (url: string) => OutboundElement;
|
|
62
|
+
quote: (id: string) => OutboundElement;
|
|
63
|
+
};
|
|
64
|
+
/** 视频元素发送失败时的文本降级 */
|
|
65
|
+
export declare function videoFallback(element: OutboundElement): OutboundElement;
|
package/lib/sender.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 发送层契约(core 与宿主解耦的关键接缝)。
|
|
4
|
+
*
|
|
5
|
+
* core 只构建与发送层无关的「出站元素」(OutboundElement),由宿主提供的
|
|
6
|
+
* OutboundSender 翻译为具体实现:
|
|
7
|
+
* - Koishi 层:翻译为 koishi `h` 元素并调用 `session.send`
|
|
8
|
+
* - CLI 层:翻译为纯文本 / 下载动作
|
|
9
|
+
*
|
|
10
|
+
* 这样 core 的 flush/compose/forward 不依赖任何宿主 SDK。
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.el = void 0;
|
|
14
|
+
exports.videoFallback = videoFallback;
|
|
15
|
+
/** 出站元素构造器(避免各模块散落字面量) */
|
|
16
|
+
exports.el = {
|
|
17
|
+
text: (text) => ({ type: 'text', text }),
|
|
18
|
+
image: (url) => ({ type: 'image', url }),
|
|
19
|
+
imageBuffer: (buffer, mime) => ({ type: 'image', buffer, mime }),
|
|
20
|
+
video: (url) => ({ type: 'video', url }),
|
|
21
|
+
audio: (url) => ({ type: 'audio', url }),
|
|
22
|
+
quote: (id) => ({ type: 'quote', id }),
|
|
23
|
+
};
|
|
24
|
+
/** 视频元素发送失败时的文本降级 */
|
|
25
|
+
function videoFallback(element) {
|
|
26
|
+
return element.type === 'video' ? exports.el.text(`视频链接:${element.url}`) : element;
|
|
27
|
+
}
|
package/lib/types.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export interface VideoQuality {
|
|
2
|
+
quality: string;
|
|
3
|
+
url: string;
|
|
4
|
+
bit_rate?: number;
|
|
5
|
+
}
|
|
6
|
+
export interface ParsedData {
|
|
7
|
+
type: string;
|
|
8
|
+
title: string;
|
|
9
|
+
desc: string;
|
|
10
|
+
author: string;
|
|
11
|
+
uid: string;
|
|
12
|
+
avatar: string;
|
|
13
|
+
cover: string;
|
|
14
|
+
video: string;
|
|
15
|
+
videos: VideoQuality[];
|
|
16
|
+
images: string[];
|
|
17
|
+
live_photo: Array<{
|
|
18
|
+
image: string;
|
|
19
|
+
video: string;
|
|
20
|
+
}>;
|
|
21
|
+
music: {
|
|
22
|
+
title?: string;
|
|
23
|
+
author?: string;
|
|
24
|
+
cover?: string;
|
|
25
|
+
url?: string;
|
|
26
|
+
};
|
|
27
|
+
like: number;
|
|
28
|
+
comment: number;
|
|
29
|
+
collect: number;
|
|
30
|
+
share: number;
|
|
31
|
+
play: number;
|
|
32
|
+
duration: number;
|
|
33
|
+
publishTime: number;
|
|
34
|
+
author_followers: number;
|
|
35
|
+
author_signature: string;
|
|
36
|
+
admire: number;
|
|
37
|
+
/** 推文动图(animated_gif):True 时可按配置转 GIF 发送 */
|
|
38
|
+
isGif?: boolean;
|
|
39
|
+
/** 多视频推文的其余视频(第一个在 video 字段;每项带自身 isGif/时长/封面) */
|
|
40
|
+
extraVideos?: {
|
|
41
|
+
url: string;
|
|
42
|
+
isGif?: boolean;
|
|
43
|
+
duration?: number;
|
|
44
|
+
cover?: string;
|
|
45
|
+
}[];
|
|
46
|
+
/** 推文语种(BCP-47 短码,如 fr/zh/und),供翻译跳过判定 */
|
|
47
|
+
lang?: string;
|
|
48
|
+
/** 外语推文译文(tweetTranslate 开启时填充,经 ${翻译} 展示) */
|
|
49
|
+
translation?: string;
|
|
50
|
+
/** 译文提供方(Grok / Google / MyMemory),随译文一起展示 */
|
|
51
|
+
translationProvider?: string;
|
|
52
|
+
}
|
|
53
|
+
export interface LinkMatch {
|
|
54
|
+
type: string;
|
|
55
|
+
url: string;
|
|
56
|
+
id: string;
|
|
57
|
+
}
|
|
58
|
+
export interface ApiItem {
|
|
59
|
+
url: string;
|
|
60
|
+
label: string;
|
|
61
|
+
apiKey?: string;
|
|
62
|
+
authHeaderType?: string;
|
|
63
|
+
customHeaderName?: string;
|
|
64
|
+
fieldMapping?: Record<string, string>;
|
|
65
|
+
}
|
|
66
|
+
export interface CustomPlatformConfig {
|
|
67
|
+
name: string;
|
|
68
|
+
apiUrl: string;
|
|
69
|
+
apiKey: string;
|
|
70
|
+
authHeaderType: string;
|
|
71
|
+
customHeaderName: string;
|
|
72
|
+
fieldMapping?: Record<string, string>;
|
|
73
|
+
proxy?: any;
|
|
74
|
+
}
|
package/lib/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sns-parse/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "sns-parse 核心契约与宿主抽象:类型、可注入日志、Host、发送层契约、平台定义",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"types": "lib/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"lib"
|
|
9
|
+
],
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"clean": "rimraf lib",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"sns-parse",
|
|
18
|
+
"video",
|
|
19
|
+
"parser",
|
|
20
|
+
"core",
|
|
21
|
+
"koishi"
|
|
22
|
+
],
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^20.11.0",
|
|
25
|
+
"rimraf": "^5.0.5",
|
|
26
|
+
"typescript": "^5.3.3"
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public",
|
|
30
|
+
"registry": "https://registry.npmjs.org"
|
|
31
|
+
},
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/sns-parse/core.git"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=16.0.0"
|
|
38
|
+
}
|
|
39
|
+
}
|