koishi-plugin-nitter 0.0.6 → 0.0.8
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/download.d.ts +2 -0
- package/lib/fixedSet.d.ts +7 -0
- package/lib/index.d.ts +10 -2
- package/lib/index.js +190 -112
- package/lib/taskqueue.d.ts +8 -0
- package/lib/translate.d.ts +0 -1
- package/package.json +3 -3
package/lib/index.d.ts
CHANGED
|
@@ -14,9 +14,17 @@ 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;
|
|
20
|
+
}
|
|
21
|
+
declare module 'koishi' {
|
|
22
|
+
interface Tables {
|
|
23
|
+
nitter_records: {
|
|
24
|
+
id: string;
|
|
25
|
+
createdAt: Date;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
19
28
|
}
|
|
20
29
|
export declare const Config: Schema<Config, Dict>;
|
|
21
30
|
export declare function apply(ctx: Context, config: Config): void;
|
|
22
|
-
export declare function downloadVideosToBase64(videoUrls: any): Promise<any[]>;
|
package/lib/index.js
CHANGED
|
@@ -32,7 +32,6 @@ var src_exports = {};
|
|
|
32
32
|
__export(src_exports, {
|
|
33
33
|
Config: () => Config,
|
|
34
34
|
apply: () => apply,
|
|
35
|
-
downloadVideosToBase64: () => downloadVideosToBase64,
|
|
36
35
|
inject: () => inject,
|
|
37
36
|
name: () => name
|
|
38
37
|
});
|
|
@@ -109,6 +108,7 @@ json输出格式:["翻译结果1", "翻译结果2", ...]`
|
|
|
109
108
|
});
|
|
110
109
|
if (response.data && response.data.choices && response.data.choices[0]) {
|
|
111
110
|
const content = response.data.choices[0].message.content;
|
|
111
|
+
console.log(content);
|
|
112
112
|
data = JSON.parse(content);
|
|
113
113
|
if (!Array.isArray(data)) {
|
|
114
114
|
if (typeof data === "object" && Object.keys(data).length === 1) {
|
|
@@ -122,7 +122,7 @@ json输出格式:["翻译结果1", "翻译结果2", ...]`
|
|
|
122
122
|
throw new Error("API返回数据格式异常");
|
|
123
123
|
}
|
|
124
124
|
});
|
|
125
|
-
return data;
|
|
125
|
+
return data.map((content) => content.replace(/\\n/g, "\n"));
|
|
126
126
|
}, "translate");
|
|
127
127
|
}
|
|
128
128
|
__name(setOpenAiTranslate, "setOpenAiTranslate");
|
|
@@ -154,13 +154,87 @@ __name(addTranslate, "addTranslate");
|
|
|
154
154
|
// src/index.tsx
|
|
155
155
|
var import_rettiwt_api = require("rettiwt-api");
|
|
156
156
|
var import_node_cron = require("node-cron");
|
|
157
|
+
|
|
158
|
+
// src/download.ts
|
|
157
159
|
var import_fluent_ffmpeg = __toESM(require("fluent-ffmpeg"));
|
|
158
160
|
var import_path = __toESM(require("path"));
|
|
159
161
|
var import_promises = __toESM(require("fs/promises"));
|
|
162
|
+
var import_axios2 = __toESM(require("axios"));
|
|
163
|
+
var tempDir = import_path.default.join(process.cwd(), "tmp");
|
|
164
|
+
async function downloadVideosToBase64(videoUrls, maxSize = 20) {
|
|
165
|
+
await import_promises.default.mkdir(tempDir, { recursive: true });
|
|
166
|
+
const results = [];
|
|
167
|
+
for (const url of videoUrls) {
|
|
168
|
+
let outputPath = null;
|
|
169
|
+
try {
|
|
170
|
+
outputPath = import_path.default.join(tempDir, `video_${Date.now()}_${Math.random().toString(36).substr(2, 9)}.mp4`);
|
|
171
|
+
await new Promise((resolve, reject) => {
|
|
172
|
+
(0, import_fluent_ffmpeg.default)(url).inputOptions([
|
|
173
|
+
"-protocol_whitelist",
|
|
174
|
+
"file,http,https,tcp,tls,crypto"
|
|
175
|
+
]).outputOptions([
|
|
176
|
+
"-c",
|
|
177
|
+
"copy"
|
|
178
|
+
]).output(outputPath).on("end", () => resolve(outputPath)).on("error", reject).run();
|
|
179
|
+
});
|
|
180
|
+
const stats = await import_promises.default.stat(outputPath);
|
|
181
|
+
const fileSizeInMB = stats.size / (1024 * 1024);
|
|
182
|
+
if (fileSizeInMB > maxSize) {
|
|
183
|
+
console.log(`视频大小 ${fileSizeInMB.toFixed(2)}MB 超过${maxSize}MB限制,跳过转换`);
|
|
184
|
+
} else {
|
|
185
|
+
const fileBuffer = await import_promises.default.readFile(outputPath);
|
|
186
|
+
const base64String = fileBuffer.toString("base64");
|
|
187
|
+
const dataUrl = `data:video/mp4;base64,${base64String}`;
|
|
188
|
+
results.push(dataUrl);
|
|
189
|
+
}
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error(`处理失败 (${url}):`, error.message);
|
|
192
|
+
results.push(null);
|
|
193
|
+
} finally {
|
|
194
|
+
if (outputPath) {
|
|
195
|
+
try {
|
|
196
|
+
await import_promises.default.unlink(outputPath);
|
|
197
|
+
} catch (deleteError) {
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return results;
|
|
203
|
+
}
|
|
204
|
+
__name(downloadVideosToBase64, "downloadVideosToBase64");
|
|
205
|
+
|
|
206
|
+
// src/taskqueue.ts
|
|
207
|
+
var taskQueue = class {
|
|
208
|
+
static {
|
|
209
|
+
__name(this, "taskQueue");
|
|
210
|
+
}
|
|
211
|
+
queue = [];
|
|
212
|
+
consumer;
|
|
213
|
+
processing = false;
|
|
214
|
+
push(task) {
|
|
215
|
+
this.queue.push(task);
|
|
216
|
+
if (!this.processing) {
|
|
217
|
+
this.process();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
onProcess(handler) {
|
|
221
|
+
this.consumer = handler;
|
|
222
|
+
}
|
|
223
|
+
async process() {
|
|
224
|
+
if (this.processing || !this.consumer) return;
|
|
225
|
+
this.processing = true;
|
|
226
|
+
while (this.queue.length > 0) {
|
|
227
|
+
const task = this.queue.shift();
|
|
228
|
+
await this.consumer(task);
|
|
229
|
+
}
|
|
230
|
+
this.processing = false;
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
// src/index.tsx
|
|
160
235
|
var import_jsx_runtime = require("@satorijs/element/jsx-runtime");
|
|
161
236
|
var name = "nitter";
|
|
162
|
-
var
|
|
163
|
-
var inject = ["puppeteer", "subscription"];
|
|
237
|
+
var inject = ["puppeteer", "subscription", "database"];
|
|
164
238
|
var Config = import_koishi.Schema.intersect([
|
|
165
239
|
import_koishi.Schema.object({
|
|
166
240
|
apiKey: import_koishi.Schema.string().required().description("Twitter API Key"),
|
|
@@ -194,18 +268,31 @@ var Config = import_koishi.Schema.intersect([
|
|
|
194
268
|
app: import_koishi.Schema.string().description("subscription配置中应用名")
|
|
195
269
|
}).description("订阅配置"),
|
|
196
270
|
import_koishi.Schema.object({
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}).description("推送设置")
|
|
271
|
+
enableReTweet: import_koishi.Schema.boolean().default(false).description("是否发送转推"),
|
|
272
|
+
sendPic: import_koishi.Schema.boolean().default(false).description("是否单独发送推文中的图片")
|
|
273
|
+
}).description("推送设置"),
|
|
274
|
+
import_koishi.Schema.union([
|
|
275
|
+
import_koishi.Schema.object({
|
|
276
|
+
sendPic: import_koishi.Schema.const(true).required(),
|
|
277
|
+
maxSize: import_koishi.Schema.number().default(20).description("发送视频的最大大小,单位为mb")
|
|
278
|
+
})
|
|
279
|
+
])
|
|
200
280
|
]);
|
|
201
281
|
function apply(ctx, config) {
|
|
282
|
+
ctx.model.extend("nitter_records", {
|
|
283
|
+
id: "string",
|
|
284
|
+
createdAt: "timestamp"
|
|
285
|
+
}, {
|
|
286
|
+
primary: "id",
|
|
287
|
+
autoInc: false
|
|
288
|
+
});
|
|
202
289
|
config.nitterUrl = config.nitterUrl.replace(/\/+$/, "");
|
|
203
290
|
const twitterClient = new import_rettiwt_api.Rettiwt({
|
|
204
291
|
apiKey: config.apiKey,
|
|
205
292
|
proxyUrl: config.proxy ? new URL(config.proxy) : void 0
|
|
206
293
|
});
|
|
207
|
-
let latestTweetId;
|
|
208
294
|
let cronJob;
|
|
295
|
+
const queue = new taskQueue();
|
|
209
296
|
(async () => {
|
|
210
297
|
if (config.enableTranslate == "google") {
|
|
211
298
|
setGoogleTranslate(config.googleApiKey, config.proxy);
|
|
@@ -213,12 +300,14 @@ function apply(ctx, config) {
|
|
|
213
300
|
setOpenAiTranslate(config.baseurl, config.openaiApiKey, config.model, config.prompt, config.temperature, config.timeout);
|
|
214
301
|
}
|
|
215
302
|
const tweetList = await getFollowedFeed();
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
303
|
+
await ctx.database.upsert("nitter_records", tweetList.map((data) => {
|
|
304
|
+
return {
|
|
305
|
+
id: data.id,
|
|
306
|
+
createdAt: new Date(data.createdAt)
|
|
307
|
+
};
|
|
308
|
+
}));
|
|
309
|
+
queue.onProcess(broadcast);
|
|
310
|
+
cronJob = (0, import_node_cron.schedule)("15 */5 * * * *", checkForUpdates);
|
|
222
311
|
ctx.on("dispose", () => {
|
|
223
312
|
cronJob.stop();
|
|
224
313
|
});
|
|
@@ -244,10 +333,10 @@ function apply(ctx, config) {
|
|
|
244
333
|
return "请输入推文ID";
|
|
245
334
|
}
|
|
246
335
|
try {
|
|
247
|
-
const [screenshot, imageUrls, hlsUrls] = await renderTweetScreenshot(
|
|
336
|
+
const [screenshot, imageUrls, hlsUrls] = await renderTweetScreenshot(tweetId);
|
|
248
337
|
let msg = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: "data:image/png;base64," + screenshot.toString("base64") });
|
|
249
338
|
let forwardMsg = [];
|
|
250
|
-
const videoUrls = await downloadVideosToBase64(hlsUrls);
|
|
339
|
+
const videoUrls = await downloadVideosToBase64(hlsUrls, config.maxSize);
|
|
251
340
|
if (imageUrls.length > 0) {
|
|
252
341
|
forwardMsg.push(...imageUrls.map((url) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("message", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: url }) })));
|
|
253
342
|
}
|
|
@@ -264,15 +353,16 @@ function apply(ctx, config) {
|
|
|
264
353
|
if (!tweetId) {
|
|
265
354
|
return "请输入推文ID";
|
|
266
355
|
}
|
|
267
|
-
|
|
356
|
+
queue.push({ account, tweetId });
|
|
268
357
|
});
|
|
269
|
-
async function broadcast(
|
|
358
|
+
async function broadcast(task) {
|
|
359
|
+
const { account, tweetId } = task;
|
|
270
360
|
try {
|
|
271
|
-
const [screenshot, imageUrls, hlsUrls] = await renderTweetScreenshot(
|
|
361
|
+
const [screenshot, imageUrls, hlsUrls] = await renderTweetScreenshot(tweetId);
|
|
272
362
|
const screenshotMsg = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: "data:image/png;base64," + screenshot.toString("base64") });
|
|
273
363
|
ctx.subscription.broadcast(config.app, account, screenshotMsg);
|
|
274
364
|
let forwardMsg = [];
|
|
275
|
-
const videoUrls = await downloadVideosToBase64(hlsUrls);
|
|
365
|
+
const videoUrls = await downloadVideosToBase64(hlsUrls, config.maxSize);
|
|
276
366
|
if (imageUrls.length > 0) {
|
|
277
367
|
forwardMsg.push(...imageUrls.map((url) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("message", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: url }) })));
|
|
278
368
|
}
|
|
@@ -300,117 +390,105 @@ function apply(ctx, config) {
|
|
|
300
390
|
for (const data of tweetList) {
|
|
301
391
|
if (!config.enableReTweet && data.retweetedTweet)
|
|
302
392
|
continue;
|
|
303
|
-
if (data.
|
|
393
|
+
if ((/* @__PURE__ */ new Date()).getTime() - new Date(data.createdAt).getTime() > 60 * 60 * 1e3)
|
|
394
|
+
continue;
|
|
395
|
+
if ((await ctx.database.get("nitter_records", { id: data.id })).length > 0)
|
|
304
396
|
continue;
|
|
397
|
+
await ctx.database.upsert("nitter_records", [{
|
|
398
|
+
id: data.id,
|
|
399
|
+
createdAt: new Date(data.createdAt)
|
|
400
|
+
}]);
|
|
305
401
|
if (!ctx.subscription.getAvailableAccounts(config.app).includes(data.tweetBy.userName))
|
|
306
402
|
continue;
|
|
307
|
-
latestTweetId = data.id;
|
|
308
403
|
ctx.logger("nitter").info(`检测到推文id:${data.id},开始推送`);
|
|
309
|
-
|
|
404
|
+
queue.push({ account: data.tweetBy.userName, tweetId: data.id });
|
|
310
405
|
}
|
|
311
406
|
}
|
|
312
407
|
__name(checkForUpdates, "checkForUpdates");
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
for (const url of videoUrls) {
|
|
320
|
-
let outputPath = null;
|
|
408
|
+
async function renderTweetScreenshot(tweetId) {
|
|
409
|
+
const puppeteer = ctx.puppeteer;
|
|
410
|
+
if (!puppeteer) {
|
|
411
|
+
throw new Error("Puppeteer 服务未找到,请安装 koishi-plugin-puppeteer");
|
|
412
|
+
}
|
|
413
|
+
const page = await puppeteer.page();
|
|
321
414
|
try {
|
|
322
|
-
|
|
323
|
-
await
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
"copy"
|
|
330
|
-
]).output(outputPath).on("end", () => resolve(outputPath)).on("error", reject).run();
|
|
331
|
-
});
|
|
332
|
-
const stats = await import_promises.default.stat(outputPath);
|
|
333
|
-
const fileSizeInMB = stats.size / (1024 * 1024);
|
|
334
|
-
if (fileSizeInMB > 10) {
|
|
335
|
-
logger.info(`视频大小 ${fileSizeInMB.toFixed(2)}MB 超过10MB限制,跳过转换`);
|
|
336
|
-
} else {
|
|
337
|
-
const fileBuffer = await import_promises.default.readFile(outputPath);
|
|
338
|
-
const base64String = fileBuffer.toString("base64");
|
|
339
|
-
const dataUrl = `data:video/mp4;base64,${base64String}`;
|
|
340
|
-
results.push(dataUrl);
|
|
341
|
-
}
|
|
342
|
-
} catch (error) {
|
|
343
|
-
console.error(`处理失败 (${url}):`, error.message);
|
|
344
|
-
results.push(null);
|
|
345
|
-
} finally {
|
|
346
|
-
if (outputPath) {
|
|
415
|
+
const tweetUrl = `${config.nitterUrl}/i/status/${tweetId}`;
|
|
416
|
+
await retry(3, async () => {
|
|
417
|
+
await page.goto(tweetUrl);
|
|
418
|
+
const element2 = await page.$(".main-thread");
|
|
419
|
+
if (!element2) throw new Error("Rate Limited");
|
|
420
|
+
}, 2e3);
|
|
421
|
+
if (config.enableTranslate) {
|
|
347
422
|
try {
|
|
348
|
-
await
|
|
349
|
-
} catch (
|
|
423
|
+
await addTranslate(page, ".main-thread .tweet-content, .main-thread .quote-text");
|
|
424
|
+
} catch (e) {
|
|
425
|
+
ctx.logger("nitter").info("翻译失败", e);
|
|
350
426
|
}
|
|
351
427
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
428
|
+
await page.evaluate(() => {
|
|
429
|
+
const nav = document.querySelector("nav");
|
|
430
|
+
if (nav) {
|
|
431
|
+
nav.style.visibility = "hidden";
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
435
|
+
await page.evaluate(() => {
|
|
436
|
+
const element2 = document.querySelector(".main-thread");
|
|
437
|
+
if (!element2) return;
|
|
438
|
+
Object.assign(element2.style, {
|
|
439
|
+
border: "1px solid #1DA1F2",
|
|
440
|
+
borderRadius: "8px",
|
|
441
|
+
boxShadow: "0px 1px 9px 12px rgba(29, 161, 242, 0.2)",
|
|
442
|
+
margin: "20px",
|
|
443
|
+
boxSizing: "border-box",
|
|
444
|
+
overflow: "hidden",
|
|
445
|
+
width: "100%",
|
|
446
|
+
padding: "10px 10px 0 10px",
|
|
447
|
+
backgroundColor: "#fff"
|
|
448
|
+
});
|
|
449
|
+
});
|
|
450
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
451
|
+
const element = await page.$(".main-thread");
|
|
452
|
+
const boundingBox = await page.evaluate((el) => {
|
|
453
|
+
const rect = el.getBoundingClientRect();
|
|
454
|
+
const margin = 20;
|
|
455
|
+
return {
|
|
456
|
+
x: rect.left - margin,
|
|
457
|
+
y: rect.top - margin,
|
|
458
|
+
width: rect.width + margin * 2,
|
|
459
|
+
height: rect.height + margin * 2
|
|
460
|
+
};
|
|
461
|
+
}, element);
|
|
462
|
+
const buffer = await page.screenshot({
|
|
463
|
+
type: "png",
|
|
464
|
+
omitBackground: false,
|
|
465
|
+
clip: boundingBox
|
|
466
|
+
});
|
|
467
|
+
if (config.sendPic) {
|
|
468
|
+
const originalImages = await page.$$eval(
|
|
469
|
+
".main-tweet a.still-image",
|
|
470
|
+
(links, baseUrl) => links.map((link) => link.getAttribute("href")).filter((href) => href).map((href) => `${baseUrl}${href}`),
|
|
471
|
+
config.nitterUrl
|
|
472
|
+
);
|
|
473
|
+
const hlsUrls = await page.$$eval(".main-tweet video", (videos, baseUrl) => {
|
|
474
|
+
return videos.map((video) => video.getAttribute("data-url")).filter((dataUrl) => dataUrl).map((dataUrl) => `${baseUrl}${dataUrl}`);
|
|
475
|
+
}, config.nitterUrl);
|
|
476
|
+
return [buffer, originalImages, hlsUrls];
|
|
382
477
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
omitBackground: false
|
|
389
|
-
});
|
|
390
|
-
if (config.sendPic) {
|
|
391
|
-
const originalImages = await page.$$eval(
|
|
392
|
-
".main-tweet a.still-image",
|
|
393
|
-
(links, baseUrl) => links.map((link) => link.getAttribute("href")).filter((href) => href).map((href) => `${baseUrl}${href}`),
|
|
394
|
-
config.nitterUrl
|
|
395
|
-
);
|
|
396
|
-
const hlsUrls = await page.$$eval(".main-tweet video", (videos, baseUrl) => {
|
|
397
|
-
return videos.map((video) => video.getAttribute("data-url")).filter((dataUrl) => dataUrl).map((dataUrl) => `${baseUrl}${dataUrl}`);
|
|
398
|
-
}, config.nitterUrl);
|
|
399
|
-
return [buffer, originalImages, hlsUrls];
|
|
478
|
+
return [buffer, [], []];
|
|
479
|
+
} catch (e) {
|
|
480
|
+
throw e;
|
|
481
|
+
} finally {
|
|
482
|
+
await page.close();
|
|
400
483
|
}
|
|
401
|
-
return [buffer, [], []];
|
|
402
|
-
} catch (e) {
|
|
403
|
-
throw e;
|
|
404
|
-
} finally {
|
|
405
|
-
await page.close();
|
|
406
484
|
}
|
|
485
|
+
__name(renderTweetScreenshot, "renderTweetScreenshot");
|
|
407
486
|
}
|
|
408
|
-
__name(
|
|
487
|
+
__name(apply, "apply");
|
|
409
488
|
// Annotate the CommonJS export names for ESM import in node:
|
|
410
489
|
0 && (module.exports = {
|
|
411
490
|
Config,
|
|
412
491
|
apply,
|
|
413
|
-
downloadVideosToBase64,
|
|
414
492
|
inject,
|
|
415
493
|
name
|
|
416
494
|
});
|
package/lib/translate.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Page } from 'puppeteer-core';
|
|
2
2
|
export declare function retry(retries: number, fn: () => any, delay?: number): any;
|
|
3
3
|
export declare function setGoogleTranslate(key: string, proxy: string): void;
|
|
4
|
-
export declare function setSiliconTranslate(key: string, model: string, prompt: string, timeout?: number): void;
|
|
5
4
|
export declare function setOpenAiTranslate(url: string, key: string, model: string, prompt: string, temperature: number, timeout?: number): void;
|
|
6
5
|
export declare function addTranslate(page: Page, className: string): Promise<void>;
|
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.8",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"koishi-plugin-subscription": "^0.0.5"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
+
"fluent-ffmpeg": "^2.1.3",
|
|
25
26
|
"node-cron": "^4.2.1",
|
|
26
|
-
"rettiwt-api": "^6.
|
|
27
|
-
"fluent-ffmpeg": "^2.1.3"
|
|
27
|
+
"rettiwt-api": "^6.1.3"
|
|
28
28
|
}
|
|
29
29
|
}
|