koishi-plugin-nitter 0.0.5 → 0.0.7
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/index.d.ts +3 -1
- package/lib/index.js +51 -33
- package/package.json +1 -2
package/lib/index.d.ts
CHANGED
|
@@ -14,8 +14,10 @@ export interface Config {
|
|
|
14
14
|
temperature: number;
|
|
15
15
|
timeout?: number;
|
|
16
16
|
app: string;
|
|
17
|
-
sendPic: boolean;
|
|
18
17
|
enableReTweet: boolean;
|
|
18
|
+
sendPic: boolean;
|
|
19
|
+
maxSize: number;
|
|
19
20
|
}
|
|
20
21
|
export declare const Config: Schema<Config, Dict>;
|
|
21
22
|
export declare function apply(ctx: Context, config: Config): void;
|
|
23
|
+
export declare function downloadVideosToBase64(videoUrls: any, maxSize?: number): Promise<any[]>;
|
package/lib/index.js
CHANGED
|
@@ -32,6 +32,7 @@ var src_exports = {};
|
|
|
32
32
|
__export(src_exports, {
|
|
33
33
|
Config: () => Config,
|
|
34
34
|
apply: () => apply,
|
|
35
|
+
downloadVideosToBase64: () => downloadVideosToBase64,
|
|
35
36
|
inject: () => inject,
|
|
36
37
|
name: () => name
|
|
37
38
|
});
|
|
@@ -154,13 +155,11 @@ __name(addTranslate, "addTranslate");
|
|
|
154
155
|
var import_rettiwt_api = require("rettiwt-api");
|
|
155
156
|
var import_node_cron = require("node-cron");
|
|
156
157
|
var import_fluent_ffmpeg = __toESM(require("fluent-ffmpeg"));
|
|
157
|
-
var import_ffmpeg_static = __toESM(require("ffmpeg-static"));
|
|
158
158
|
var import_path = __toESM(require("path"));
|
|
159
159
|
var import_promises = __toESM(require("fs/promises"));
|
|
160
|
-
var import_url = require("url");
|
|
161
160
|
var import_jsx_runtime = require("@satorijs/element/jsx-runtime");
|
|
162
|
-
import_fluent_ffmpeg.default.setFfmpegPath(import_ffmpeg_static.default);
|
|
163
161
|
var name = "nitter";
|
|
162
|
+
var logger = new import_koishi.Logger(name);
|
|
164
163
|
var inject = ["puppeteer", "subscription"];
|
|
165
164
|
var Config = import_koishi.Schema.intersect([
|
|
166
165
|
import_koishi.Schema.object({
|
|
@@ -195,9 +194,15 @@ var Config = import_koishi.Schema.intersect([
|
|
|
195
194
|
app: import_koishi.Schema.string().description("subscription配置中应用名")
|
|
196
195
|
}).description("订阅配置"),
|
|
197
196
|
import_koishi.Schema.object({
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}).description("推送设置")
|
|
197
|
+
enableReTweet: import_koishi.Schema.boolean().default(false).description("是否发送转推"),
|
|
198
|
+
sendPic: import_koishi.Schema.boolean().default(false).description("是否单独发送推文中的图片")
|
|
199
|
+
}).description("推送设置"),
|
|
200
|
+
import_koishi.Schema.union([
|
|
201
|
+
import_koishi.Schema.object({
|
|
202
|
+
sendPic: import_koishi.Schema.const(true).required(),
|
|
203
|
+
maxSize: import_koishi.Schema.number().default(10).description("发送视频的最大大小,单位为mb")
|
|
204
|
+
})
|
|
205
|
+
])
|
|
201
206
|
]);
|
|
202
207
|
function apply(ctx, config) {
|
|
203
208
|
config.nitterUrl = config.nitterUrl.replace(/\/+$/, "");
|
|
@@ -248,15 +253,14 @@ function apply(ctx, config) {
|
|
|
248
253
|
const [screenshot, imageUrls, hlsUrls] = await renderTweetScreenshot(ctx, tweetId, config);
|
|
249
254
|
let msg = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: "data:image/png;base64," + screenshot.toString("base64") });
|
|
250
255
|
let forwardMsg = [];
|
|
251
|
-
const videoUrls = await
|
|
256
|
+
const videoUrls = await downloadVideosToBase64(hlsUrls, config.maxSize);
|
|
252
257
|
if (imageUrls.length > 0) {
|
|
253
258
|
forwardMsg.push(...imageUrls.map((url) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("message", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: url }) })));
|
|
254
259
|
}
|
|
255
260
|
if (videoUrls.length > 0) {
|
|
256
|
-
forwardMsg.push(...videoUrls.map((url) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("message", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("video", { src:
|
|
261
|
+
forwardMsg.push(...videoUrls.map((url) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("message", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("video", { src: url }) })));
|
|
257
262
|
}
|
|
258
263
|
await session.send(msg + /* @__PURE__ */ (0, import_jsx_runtime.jsx)("message", { forward: true, children: forwardMsg }));
|
|
259
|
-
await deleteFiles(videoUrls);
|
|
260
264
|
} catch (error) {
|
|
261
265
|
ctx.logger("nitter").error("获取推文失败:", error);
|
|
262
266
|
return "获取推文失败";
|
|
@@ -273,18 +277,17 @@ function apply(ctx, config) {
|
|
|
273
277
|
const [screenshot, imageUrls, hlsUrls] = await renderTweetScreenshot(ctx, tweetId, config);
|
|
274
278
|
const screenshotMsg = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: "data:image/png;base64," + screenshot.toString("base64") });
|
|
275
279
|
ctx.subscription.broadcast(config.app, account, screenshotMsg);
|
|
276
|
-
let forwardMsg =
|
|
277
|
-
const videoUrls = await
|
|
280
|
+
let forwardMsg = [];
|
|
281
|
+
const videoUrls = await downloadVideosToBase64(hlsUrls, config.maxSize);
|
|
278
282
|
if (imageUrls.length > 0) {
|
|
279
|
-
forwardMsg
|
|
283
|
+
forwardMsg.push(...imageUrls.map((url) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("message", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: url }) })));
|
|
280
284
|
}
|
|
281
285
|
if (videoUrls.length > 0) {
|
|
282
|
-
forwardMsg
|
|
286
|
+
forwardMsg.push(...videoUrls.map((url) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("message", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("video", { src: url }) })));
|
|
283
287
|
}
|
|
284
288
|
if (forwardMsg.length > 0) {
|
|
285
289
|
await ctx.subscription.broadcastForward(config.app, account, forwardMsg);
|
|
286
290
|
}
|
|
287
|
-
await deleteFiles(videoUrls);
|
|
288
291
|
} catch (error) {
|
|
289
292
|
ctx.logger("nitter").error("获取推文失败:", error);
|
|
290
293
|
}
|
|
@@ -316,33 +319,47 @@ function apply(ctx, config) {
|
|
|
316
319
|
}
|
|
317
320
|
__name(apply, "apply");
|
|
318
321
|
var tempDir = import_path.default.join(process.cwd(), "tmp");
|
|
319
|
-
async function
|
|
322
|
+
async function downloadVideosToBase64(videoUrls, maxSize = 20) {
|
|
320
323
|
await import_promises.default.mkdir(tempDir, { recursive: true });
|
|
321
|
-
const
|
|
324
|
+
const results = [];
|
|
325
|
+
for (const url of videoUrls) {
|
|
326
|
+
let outputPath = null;
|
|
322
327
|
try {
|
|
323
|
-
|
|
328
|
+
outputPath = import_path.default.join(tempDir, `video_${Date.now()}_${Math.random().toString(36).substr(2, 9)}.mp4`);
|
|
324
329
|
await new Promise((resolve, reject) => {
|
|
325
|
-
(0, import_fluent_ffmpeg.default)(url).
|
|
326
|
-
|
|
327
|
-
|
|
330
|
+
(0, import_fluent_ffmpeg.default)(url).inputOptions([
|
|
331
|
+
"-protocol_whitelist",
|
|
332
|
+
"file,http,https,tcp,tls,crypto"
|
|
333
|
+
]).outputOptions([
|
|
334
|
+
"-c",
|
|
335
|
+
"copy"
|
|
336
|
+
]).output(outputPath).on("end", () => resolve(outputPath)).on("error", reject).run();
|
|
328
337
|
});
|
|
329
|
-
|
|
338
|
+
const stats = await import_promises.default.stat(outputPath);
|
|
339
|
+
const fileSizeInMB = stats.size / (1024 * 1024);
|
|
340
|
+
if (fileSizeInMB > maxSize) {
|
|
341
|
+
logger.info(`视频大小 ${fileSizeInMB.toFixed(2)}MB 超过${maxSize}MB限制,跳过转换`);
|
|
342
|
+
} else {
|
|
343
|
+
const fileBuffer = await import_promises.default.readFile(outputPath);
|
|
344
|
+
const base64String = fileBuffer.toString("base64");
|
|
345
|
+
const dataUrl = `data:video/mp4;base64,${base64String}`;
|
|
346
|
+
results.push(dataUrl);
|
|
347
|
+
}
|
|
330
348
|
} catch (error) {
|
|
331
349
|
console.error(`处理失败 (${url}):`, error.message);
|
|
332
|
-
|
|
350
|
+
results.push(null);
|
|
351
|
+
} finally {
|
|
352
|
+
if (outputPath) {
|
|
353
|
+
try {
|
|
354
|
+
await import_promises.default.unlink(outputPath);
|
|
355
|
+
} catch (deleteError) {
|
|
356
|
+
}
|
|
357
|
+
}
|
|
333
358
|
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
return results.filter((path2) => path2 !== null);
|
|
337
|
-
}
|
|
338
|
-
__name(downloadAndConvertVideos, "downloadAndConvertVideos");
|
|
339
|
-
async function deleteFiles(filePaths) {
|
|
340
|
-
await Promise.allSettled(
|
|
341
|
-
filePaths.map((filePath) => import_promises.default.unlink(filePath).catch(() => {
|
|
342
|
-
}))
|
|
343
|
-
);
|
|
359
|
+
}
|
|
360
|
+
return results;
|
|
344
361
|
}
|
|
345
|
-
__name(
|
|
362
|
+
__name(downloadVideosToBase64, "downloadVideosToBase64");
|
|
346
363
|
async function renderTweetScreenshot(ctx, tweetId, config) {
|
|
347
364
|
const puppeteer = ctx.puppeteer;
|
|
348
365
|
if (!puppeteer) {
|
|
@@ -399,6 +416,7 @@ __name(renderTweetScreenshot, "renderTweetScreenshot");
|
|
|
399
416
|
0 && (module.exports = {
|
|
400
417
|
Config,
|
|
401
418
|
apply,
|
|
419
|
+
downloadVideosToBase64,
|
|
402
420
|
inject,
|
|
403
421
|
name
|
|
404
422
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-nitter",
|
|
3
3
|
"description": "使用Rettiwt-API订阅推文,并使用nitter渲染",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.7",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -24,7 +24,6 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"node-cron": "^4.2.1",
|
|
26
26
|
"rettiwt-api": "^6.0.6",
|
|
27
|
-
"ffmpeg-static": "^5.2.0",
|
|
28
27
|
"fluent-ffmpeg": "^2.1.3"
|
|
29
28
|
}
|
|
30
29
|
}
|