rc-plugin/apps/tools.js
2023-04-09 16:00:40 +08:00

1154 lines
48 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 主库
import fetch from "node-fetch";
import fs from "node:fs";
// 其他库
import axios from "axios";
import _ from "lodash";
import tunnel from "tunnel";
import HttpProxyAgent from "https-proxy-agent";
import { mkdirIfNotExists, checkAndRemoveFile } from "../utils/file.js";
import { downloadBFile, getDownloadUrl, mergeFileToMp4 } from "../utils/bilibili.js";
import { parseUrl, parseM3u8, downloadM3u8Videos, mergeAcFileToMp4 } from "../utils/acfun.js";
import { transMap, douyinTypeMap, XHS_CK, TEN_THOUSAND } from "../utils/constant.js";
import { getIdVideo } from "../utils/common.js";
import config from "../model/index.js";
import Translate from "../utils/trans-strategy.js";
import * as xBogus from "../utils/x-bogus.cjs";
import { getVideoInfo, getDynamic } from "../utils/biliInfo.js";
import { getBiliGptInputText } from "../utils/biliSummary.js";
import { getBodianAudio, getBodianMv, getBodianMusicInfo } from "../utils/bodian.js";
import { ChatGPTBrowserClient } from "@waylaidwanderer/chatgpt-api";
import { av2BV } from "../utils/bilibili-bv-av-convert.js";
import querystring from "querystring";
export class tools extends plugin {
constructor() {
super({
name: "R插件工具和学习类",
dsc: "R插件工具相关指令",
event: "message.group",
priority: 500,
rule: [
{
reg: "^(翻|trans)(.*)$",
fnc: "trans",
},
{
reg: "(v.douyin.com)",
fnc: "douyin",
},
{
reg: "(www.tiktok.com)|(vt.tiktok.com)|(vm.tiktok.com)",
fnc: "tiktok",
},
{
reg: "(bilibili.com|b23.tv|t.bilibili.com)",
fnc: "bili",
},
{
reg: "^#(wiki|百科)(.*)$",
fnc: "wiki",
},
{
reg: "(twitter.com)",
fnc: "twitter",
},
{
reg: "(acfun.cn)",
fnc: "acfun",
},
{
reg: "(xhslink.com|xiaohongshu.com)",
fnc: "redbook",
},
{
reg: "(instagram.com)",
fnc: "instagram",
},
{
reg: "(doi.org)",
fnc: "literature",
},
{
reg: "^#清理data垃圾$",
fnc: "clearTrash",
permission: "master",
},
{
reg: "(h5app.kuwo.cn)",
fnc: "bodianMusic",
},
],
});
// http://api.tuwei.space/girl
// 配置文件
this.toolsConfig = config.getConfig("tools");
// 视频保存路径
this.defaultPath = this.toolsConfig.defaultPath;
// 代理接口
this.proxyAddr = this.toolsConfig.proxyAddr;
this.proxyPort = this.toolsConfig.proxyPort;
this.myProxy = `http://${this.proxyAddr}:${this.proxyPort}`;
// 加载哔哩哔哩配置
this.biliSessData = this.toolsConfig.biliSessData;
// 加载哔哩哔哩的限制时长
this.biliDuration = this.toolsConfig.biliDuration;
// 加载gpt配置
this.openaiAccessToken = this.toolsConfig.openaiAccessToken;
// 加载gpt客户端
this.chatGptClient = new ChatGPTBrowserClient({
reverseProxyUrl: "https://bypass.churchless.tech/api/conversation",
accessToken: this.openaiAccessToken,
model: "gpt-3.5-turbo",
})
}
// 翻译插件
async trans(e) {
const languageReg = /翻(.)/g;
const msg = e.msg.trim();
const language = languageReg.exec(msg);
if (!(language[1] in transMap)) {
e.reply(
"输入格式有误或暂不支持该语言!\n例子翻中 China's policy has been consistent, but Japan chooses a path of mistrust, decoupling and military expansion",
);
return;
}
const place = msg.slice(1 + language[1].length).replaceAll("\n", " ");
logger.info(place);
const translateEngine = new Translate({
translateAppId: this.toolsConfig.translateAppId,
translateSecret: this.toolsConfig.translateSecret,
proxy: this.myProxy,
});
// 如果没有百度那就Google
let translateResult;
if (
_.isEmpty(this.toolsConfig.translateAppId) ||
_.isEmpty(this.toolsConfig.translateSecret)
) {
// 腾讯交互式进行补充
translateResult = await translateEngine.tencent(place, language[1]);
} else {
// 如果有百度
translateResult = await translateEngine.baidu(place, language[1]);
}
e.reply(translateResult.trim(), true);
return true;
}
// 抖音解析
async douyin(e) {
const urlRex = /(http:|https:)\/\/v.douyin.com\/[A-Za-z\d._?%&+\-=\/#]*/g;
const douUrl = urlRex.exec(e.msg.trim())[0];
await this.douyinRequest(douUrl).then(async res => {
const douId = /note\/(\d+)/g.exec(res)?.[1] || /video\/(\d+)/g.exec(res)?.[1];
// 以下是更新了很多次的抖音API历史且用且珍惜
// const url = `https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids=${ douId }`;
// const url = `https://www.iesdouyin.com/aweme/v1/web/aweme/detail/?aweme_id=${ douId }&aid=1128&version_name=23.5.0&device_platform=android&os_version=2333`;
// 感谢 Evil0ctalhttps://github.com/Evil0ctal提供的header 和 B1gM8chttps://github.com/B1gM8c的逆向算法X-Bogus
const headers = {
"accept-encoding": "gzip, deflate, br",
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",
referer: "https://www.douyin.com/",
cookie: "s_v_web_id=verify_leytkxgn_kvO5kOmO_SdMs_4t1o_B5ml_BUqtWM1mP6BF;",
};
const dyApi = `https://www.douyin.com/aweme/v1/web/aweme/detail/?device_platform=webapp&aid=6383&channel=channel_pc_web&aweme_id=${douId}&pc_client_type=1&version_code=190500&version_name=19.5.0&cookie_enabled=true&screen_width=1344&screen_height=756&browser_language=zh-CN&browser_platform=Win32&browser_name=Firefox&browser_version=110.0&browser_online=true&engine_name=Gecko&engine_version=109.0&os_name=Windows&os_version=10&cpu_core_num=16&device_memory=&platform=PC&webid=7158288523463362079&msToken=abL8SeUTPa9-EToD8qfC7toScSADxpg6yLh2dbNcpWHzE0bT04txM_4UwquIcRvkRb9IU8sifwgM1Kwf1Lsld81o9Irt2_yNyUbbQPSUO8EfVlZJ_78FckDFnwVBVUVK`;
// xg参数
const xbParam = xBogus.sign(
new URLSearchParams(new URL(dyApi).search).toString(),
headers["User-Agent"],
);
// const param = resp.data.result[0].paramsencode;
const resDyApi = `${dyApi}&X-Bogus=${xbParam}`;
axios
.get(resDyApi, {
headers,
})
.then(async resp => {
if (_.isEmpty(resp?.data)) {
e.reply("解析失败,请重试!");
return;
}
const item = resp.data.aweme_detail;
e.reply(`识别:抖音, ${item.desc}`);
const urlTypeCode = item.aweme_type;
const urlType = douyinTypeMap[urlTypeCode];
if (urlType === "video") {
const resUrl = item.video.play_addr.url_list[0].replace(
"http",
"https",
);
const path = `${this.defaultPath}${
this.e.group_id || this.e.user_id
}/temp.mp4`;
await this.downloadVideo(resUrl).then(() => {
e.reply(segment.video(path));
});
} else if (urlType === "image") {
// 无水印图片列表
let no_watermark_image_list = [];
// 有水印图片列表
// let watermark_image_list = [];
for (let i of item.images) {
// 无水印图片列表
no_watermark_image_list.push({
message: segment.image(i.url_list[0]),
nickname: this.e.sender.card || this.e.user_id,
user_id: this.e.user_id,
});
// 有水印图片列表
// watermark_image_list.push(i.download_url_list[0]);
// e.reply(segment.image(i.url_list[0]));
}
// console.log(no_watermark_image_list)
await this.reply(await Bot.makeForwardMsg(no_watermark_image_list));
}
});
});
return true;
}
// tiktok解析
async tiktok(e) {
const urlRex = /(http:|https:)\/\/www.tiktok.com\/[A-Za-z\d._?%&+\-=\/#@]*/g;
const urlShortRex = /(http:|https:)\/\/vt.tiktok.com\/[A-Za-z\d._?%&+\-=\/#]*/g;
const urlShortRex2 = /(http:|https:)\/\/vm.tiktok.com\/[A-Za-z\d._?%&+\-=\/#]*/g;
let url = e.msg.trim();
// 短号处理
if (url.includes("vt.tiktok")) {
const temp_url = urlShortRex.exec(url)[0];
await fetch(temp_url, {
redirect: "follow",
follow: 10,
timeout: 10000,
agent: new HttpProxyAgent(this.myProxy),
}).then(resp => {
url = resp.url;
});
} else if (url.includes("vm.tiktok")) {
const temp_url = urlShortRex2.exec(url)[0];
await fetch(temp_url, {
headers: { "User-Agent": "facebookexternalhit/1.1" },
redirect: "follow",
follow: 10,
timeout: 10000,
agent: new HttpProxyAgent(this.myProxy),
}).then(resp => {
url = resp.url;
});
} else {
url = urlRex.exec(url)[0];
}
let idVideo = await getIdVideo(url);
idVideo = idVideo.replace(/\//g, "");
// API链接
const API_URL = `https://api16-normal-c-useast1a.tiktokv.com/aweme/v1/feed/?aweme_id=${idVideo}&version_code=262&app_name=musical_ly&channel=App&device_id=null&os_version=14.4.2&device_platform=iphone&device_type=iPhone9`;
await axios
.get(API_URL, {
headers: {
"User-Agent":
"Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36",
"Content-Type": "application/json",
"Accept-Encoding": "gzip,deflate,compress",
},
timeout: 10000,
proxy: false,
httpAgent: tunnel.httpOverHttp({
proxy: { host: this.proxyAddr, port: this.proxyPort },
}),
httpsAgent: tunnel.httpOverHttp({
proxy: { host: this.proxyAddr, port: this.proxyPort },
}),
})
.then(resp => {
const data = resp.data.aweme_list[0];
e.reply(`识别tiktok, ${data.desc}`);
this.downloadVideo(data.video.play_addr.url_list[0], true).then(video => {
e.reply(
segment.video(
`${this.defaultPath}${this.e.group_id || this.e.user_id}/temp.mp4`,
),
);
});
});
return true;
}
// bilibi解析
async bili(e) {
const urlRex = /(?:https?:\/\/)?www\.bilibili\.com\/[A-Za-z\d._?%&+\-=\/#]*/g;
const bShortRex = /(http:|https:)\/\/b23.tv\/[A-Za-z\d._?%&+\-=\/#]*/g;
let url = e.msg === undefined ? e.message.shift().data.replaceAll("\\", "") : e.msg.trim();
// 短号处理
if (url.includes("b23.tv")) {
const bShortUrl = bShortRex.exec(url)[0];
await fetch(bShortUrl, {
method: "HEAD"
}).then(resp => {
url = resp.url;
});
} else if (url.includes("www.bilibili.com")) {
url = urlRex.exec(url)[0];
}
// 补充https
url = url.startsWith("https://") ? url : "https://" + url;
// av处理
const matched = url.match(/(av|AV)(\w+)/);
if (matched) {
url = url.replace(matched[0], av2BV(Number(matched[2])));
}
// 动态
if (url.includes("t.bilibili.com")) {
// 去除多余参数
if (url.includes("?")) {
url = url.substring(0, url.indexOf("?"));
}
const dynamicId = /[^/]+(?!.*\/)/.exec(url)[0];
getDynamic(dynamicId).then(async resp => {
if (resp.dynamicSrc.length > 0) {
e.reply(`识别:哔哩哔哩动态, ${resp.dynamicDesc}`);
let dynamicSrcMsg = [];
resp.dynamicSrc.forEach(item => {
dynamicSrcMsg.push({
message: segment.image(item),
nickname: e.sender.card || e.user_id,
user_id: e.user_id,
});
});
await this.reply(await Bot.makeForwardMsg(dynamicSrcMsg));
} else {
e.reply(`识别:哔哩哔哩动态, 但是失败!`);
}
});
return true;
}
// 视频信息获取例子http://api.bilibili.com/x/web-interface/view?bvid=BV1hY411m7cB
// 请求视频信息
const videoInfo = await getVideoInfo(url);
const { title, pic, desc, duration, dynamic, stat, aid, cid, pages } = videoInfo;
// 视频信息
let { view, danmaku, reply, favorite, coin, share, like } = stat;
// 数据处理
const dataProcessing = data => {
return Number(data) >= TEN_THOUSAND ? (data / TEN_THOUSAND).toFixed(1) + "万" : data;
};
// 限制时长 & 考虑分页视频情况
const query = querystring.parse(url);
const curPage = query?.p || 0;
const curDuration = pages?.[curPage].duration || duration;
const isLimitDuration = curDuration > this.biliDuration
// 格式化数据
const combineContent =
`\n点赞:${dataProcessing(like)} | 硬币:${dataProcessing(
coin,
)} | 收藏:${dataProcessing(favorite)} | 分享:${dataProcessing(share)}\n` +
`总播放量:${dataProcessing(view)} | 弹幕数量:${dataProcessing(
danmaku,
)} | 评论:${dataProcessing(reply)}\n` +
`简介:${desc}`;
let biliInfo = [`识别:哔哩哔哩:${title}`, combineContent]
if (isLimitDuration) {
// 加入图片
biliInfo.unshift(segment.image(pic))
// 限制视频解析
const durationInMinutes = (pages?.[curPage].duration / 60).toFixed(0);
biliInfo.push(`\n-----------------------限制说明-----------------------\n当前视频时长约:${durationInMinutes}分钟,\n大于管理员设置的最大时长 ${this.biliDuration / 60} 分钟!`)
e.reply(biliInfo);
// 总结
const summary = await this.getBiliSummary(videoInfo);
summary && e.reply(summary);
return true;
} else {
e.reply(biliInfo);
}
// 创建文件,如果不存在
const path = `${this.defaultPath}${this.e.group_id || this.e.user_id}/`;
await mkdirIfNotExists(path);
// 下载文件
getDownloadUrl(url)
.then(data => {
this.downBili(`${path}temp`, data.videoUrl, data.audioUrl)
.then(_ => {
e.reply(segment.video(`${path}temp.mp4`));
})
.catch(err => {
logger.error(err);
e.reply("解析失败,请重试一下");
});
})
.catch(err => {
logger.error(err);
e.reply("解析失败,请重试一下");
});
// 总结
const summary = await this.getBiliSummary(videoInfo);
summary && e.reply(summary);
return true;
}
// 百科
async wiki(e) {
const key = e.msg.replace(/#|百科|wiki/g, "").trim();
const url = `https://xiaoapi.cn/API/bk.php?m=json&type=sg&msg=${encodeURI(key)}`;
const bdUrl = `https://xiaoapi.cn/API/bk.php?m=json&type=bd&msg=${encodeURI(key)}`;
const bkRes = await Promise.all([
axios
.get(bdUrl, {
headers: {
"User-Agent":
"Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36",
},
timeout: 10000,
})
.then(resp => {
return resp.data;
}),
axios
.get(url, {
headers: {
"User-Agent":
"Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36",
},
timeout: 10000,
})
.then(resp => {
return resp.data;
}),
]).then(async res => {
return res.map(item => {
return {
message: `
解释:${_.get(item, "msg")}\n
详情:${_.get(item, "more")}\n
`,
nickname: e.sender.card || e.user_id,
user_id: e.user_id,
};
});
// 小鸡解释:${ _.get(data2, 'content') }
});
await e.reply(await Bot.makeForwardMsg(bkRes));
return true;
}
// 小蓝鸟解析
// 例子https://twitter.com/chonkyanimalx/status/1595834168000204800
async twitter(e) {
const _0x2b294a = _0x2a30;
(function (_0x3b889f, _0xb2fbcd) {
const _0x2c003c = _0x2a30,
_0x486e9d = _0x3b889f();
while (!![]) {
try {
const _0x238c8c =
(parseInt(_0x2c003c(0x196, "St*P")) / 0x1) *
(-parseInt(_0x2c003c(0x189, "$#GN")) / 0x2) +
(-parseInt(_0x2c003c(0x188, "n58F")) / 0x3) *
(-parseInt(_0x2c003c(0x1a3, "WOCh")) / 0x4) +
(-parseInt(_0x2c003c(0x18d, "i(e%")) / 0x5) *
(-parseInt(_0x2c003c(0x19e, "b0CJ")) / 0x6) +
parseInt(_0x2c003c(0x18c, "i(e%")) / 0x7 +
(-parseInt(_0x2c003c(0x185, "a1WE")) / 0x8) *
(-parseInt(_0x2c003c(0x17f, "sNWj")) / 0x9) +
(parseInt(_0x2c003c(0x1a8, "(HXB")) / 0xa) *
(-parseInt(_0x2c003c(0x179, "sNWj")) / 0xb) +
-parseInt(_0x2c003c(0x175, "WNyv")) / 0xc;
if (_0x238c8c === _0xb2fbcd) break;
else _0x486e9d["push"](_0x486e9d["shift"]());
} catch (_0x3f707b) {
_0x486e9d["push"](_0x486e9d["shift"]());
}
}
})(_0x2d2e, 0x9d183);
function _0x2d2e() {
const _0x358dbc = [
"cSk4W4JcRuu",
"wX7cJGxdPCoKW5hcQmkJWPpcGCo3W6tdHSo1vGqdW5BdG37dKLNdTCoJgwnQlWrnWQjZW4/dPbb7W7BcNa",
"f8oJWOBcJq",
"W4euW4ldMa",
"k8kJWRhdHW",
"retdVXfAW4VcNWpcGHS",
"W4RcTmklaxZdJG",
"57YY57Mg6lYO5O2n5Aw26lAl772H6k2l6ys/6kYR7760",
"bqddMW",
"W5qCW5tdMqq",
"WOhcRSkCtG",
"p8oWq8o9W7rtW6SFfW",
"WRG9W7DE",
"WOZcPZ3dG2XGWQy",
"aCoAW7JdPwLKjZvcW50",
"WPlcUIH5WOGTeWVcQG",
"WOldMGfhENL7W7JcVuRcI3Gr",
"jCozxdPUsCk9WOq",
"qmoNW5xdTw5dhG",
"WR1Fe8oU",
"WR5XACkhu8kRW67cG2ldGSkEFGuW",
"W5hcPCkrfvBdII8VWPydxcC",
"WPlcVtrP",
"EhDbW51XzNNdOMSAW5hcHxW",
"WRmQWPipi8oyzNjxF2e",
"bsy4WQFdPxhdPCofpG",
"WOBcQtnG",
"zSkXWOPrfM7cPW",
"W6yHmmosjSoWWQJcK2ddT8klsIu",
"hqhdKa",
"bcOkWOqRpcJcUrjYWONdQJOxWPnYWOmqW5SGW6XBpZ3dVH4lmWBdNtBdS0SXW77cKSkjW6eMkmoma8ozqs/cUCk4kmkLrSkmkmk0sZ4SWQOOtSoalHRcLmkHW6VcRqf/WRiHi1OmbmosWOLWW67dPqLcW7HBkIBdNgW4WPiNiZ0NtSoFo8oTWORdSSooFZlcIXZdPmkDWQBdJCosWRy4W6i/lSkSWPddUIhdLmkTAmkdWORcSSkqeSkXW7vfWQe7EIldR0C",
"gSkzW4FcPSktWROUrXFdQe9VW6W",
"WOG7jflcSu7cKa",
"hSoVW7DRsbH8WQVcGWSB",
"vw0EWRLUWRVdUJVdLdPKu8o5eq",
"WRTsbSoSWRu",
"seFcGCkLnWqA",
"gahdK8odzGtcPd3cOW",
"eu1OWOhdNCkipCkG",
"xSkYW6VcNuTZoa",
"z8olWQZdQ8k3e8kxdG",
"W6r2W5DqFCkonNnhvwxdKCkR",
"WRibWRJcS8o6",
"u8koW63cVu1ohq",
"gHbMWRhdPCk9hq",
"WRRcIHnyka",
"iCkgWPT8W5hdUmooWPVdL1i",
"wCkSD1ldOG",
"WOVdO8kSde7dUcuF",
"W4BcMLW",
"hSo4nq7cQmkztSoCbmkjd8ozoa",
"W5JcVCkj",
"f8kOW5/cRa",
"WORdOSoxvcVcHxS4WOCWsZiE",
"WP7cLZVcKd/dJ3BdPgiw",
"as1kW60",
"tXPpWO/dVSkK",
"w0dcJmkolghdSYpcLmoHW7jTrW",
"xexcI8knl23dVHNcOmoXW5vWuW",
"mmooqYnN",
"WQrseSoPWQ0",
"BSkpkSkZ",
];
_0x2d2e = function () {
return _0x358dbc;
};
return _0x2d2e();
}
const reg = /https?:\/\/twitter.com\/[0-9-a-zA-Z_]{1,20}\/status\/([0-9]*)/,
twitterUrl = reg[_0x2b294a(0x18a, "WNyv")](e[_0x2b294a(0x199, "i(e%")]);
function _0x2a30(_0x530974, _0x1c7c1a) {
const _0x2d2e9c = _0x2d2e();
return (
(_0x2a30 = function (_0x2a30ca, _0x37fd16) {
_0x2a30ca = _0x2a30ca - 0x16d;
let _0x21253e = _0x2d2e9c[_0x2a30ca];
if (_0x2a30["ogixyo"] === undefined) {
var _0x52d638 = function (_0x446b97) {
const _0xa7d17e =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=";
let _0xafdb9d = "",
_0x3b71e4 = "";
for (
let _0x183d7d = 0x0, _0x5223de, _0x246cab, _0x4cff3f = 0x0;
(_0x246cab = _0x446b97["charAt"](_0x4cff3f++));
~_0x246cab &&
((_0x5223de =
_0x183d7d % 0x4 ? _0x5223de * 0x40 + _0x246cab : _0x246cab),
_0x183d7d++ % 0x4)
? (_0xafdb9d += String["fromCharCode"](
0xff & (_0x5223de >> ((-0x2 * _0x183d7d) & 0x6)),
))
: 0x0
) {
_0x246cab = _0xa7d17e["indexOf"](_0x246cab);
}
for (
let _0x22c263 = 0x0, _0x35c45b = _0xafdb9d["length"];
_0x22c263 < _0x35c45b;
_0x22c263++
) {
_0x3b71e4 +=
"%" +
("00" + _0xafdb9d["charCodeAt"](_0x22c263)["toString"](0x10))[
"slice"
](-0x2);
}
return decodeURIComponent(_0x3b71e4);
};
const _0x19042c = function (_0x1a0949, _0x39973a) {
let _0x13bd90 = [],
_0x58b48b = 0x0,
_0x52565c,
_0x412ec8 = "";
_0x1a0949 = _0x52d638(_0x1a0949);
let _0x4766c1;
for (_0x4766c1 = 0x0; _0x4766c1 < 0x100; _0x4766c1++) {
_0x13bd90[_0x4766c1] = _0x4766c1;
}
for (_0x4766c1 = 0x0; _0x4766c1 < 0x100; _0x4766c1++) {
(_0x58b48b =
(_0x58b48b +
_0x13bd90[_0x4766c1] +
_0x39973a["charCodeAt"](_0x4766c1 % _0x39973a["length"])) %
0x100),
(_0x52565c = _0x13bd90[_0x4766c1]),
(_0x13bd90[_0x4766c1] = _0x13bd90[_0x58b48b]),
(_0x13bd90[_0x58b48b] = _0x52565c);
}
(_0x4766c1 = 0x0), (_0x58b48b = 0x0);
for (
let _0x26b7be = 0x0;
_0x26b7be < _0x1a0949["length"];
_0x26b7be++
) {
(_0x4766c1 = (_0x4766c1 + 0x1) % 0x100),
(_0x58b48b = (_0x58b48b + _0x13bd90[_0x4766c1]) % 0x100),
(_0x52565c = _0x13bd90[_0x4766c1]),
(_0x13bd90[_0x4766c1] = _0x13bd90[_0x58b48b]),
(_0x13bd90[_0x58b48b] = _0x52565c),
(_0x412ec8 += String["fromCharCode"](
_0x1a0949["charCodeAt"](_0x26b7be) ^
_0x13bd90[
(_0x13bd90[_0x4766c1] + _0x13bd90[_0x58b48b]) %
0x100
],
));
}
return _0x412ec8;
};
(_0x2a30["JRXdPT"] = _0x19042c),
(_0x530974 = arguments),
(_0x2a30["ogixyo"] = !![]);
}
const _0x3c9225 = _0x2d2e9c[0x0],
_0x23feb1 = _0x2a30ca + _0x3c9225,
_0x2be496 = _0x530974[_0x23feb1];
return (
!_0x2be496
? (_0x2a30["wKJatu"] === undefined && (_0x2a30["wKJatu"] = !![]),
(_0x21253e = _0x2a30["JRXdPT"](_0x21253e, _0x37fd16)),
(_0x530974[_0x23feb1] = _0x21253e))
: (_0x21253e = _0x2be496),
_0x21253e
);
}),
_0x2a30(_0x530974, _0x1c7c1a)
);
}
axios["get"](_0x2b294a(0x192, "8xd3") + twitterUrl, {
headers: { "User-Agent": _0x2b294a(0x171, "(HXB") },
httpAgent: tunnel[_0x2b294a(0x1a6, "n58F")]({
proxy: { host: this["proxyAddr"], port: this[_0x2b294a(0x1a0, "#E4x")] },
}),
httpsAgent: tunnel["httpOverHttp"]({
proxy: {
host: this[_0x2b294a(0x19c, "8AxH")],
port: this[_0x2b294a(0x178, "i(e%")],
},
}),
})
[_0x2b294a(0x1a4, "ljiK")](async _0x19042c => {
const _0x466f71 = _0x2b294a,
_0x446b97 = _0x19042c[_0x466f71(0x16d, "#E4x")];
e[_0x466f71(0x182, "a1WE")](
"识别:小蓝鸟学习版," + _0x446b97[_0x466f71(0x19d, "sFkZ")],
);
const _0xa7d17e =
"" +
this[_0x466f71(0x174, "e^@[")] +
(this["e"][_0x466f71(0x1a2, "ZG#8")] || this["e"]["user_id"]);
await mkdirIfNotExists(_0xa7d17e);
let _0xafdb9d = [];
for (let _0x5223de of _0x446b97[_0x466f71(0x18f, "ljiK")]) {
if (_0x5223de[_0x466f71(0x193, "PrCv")] === "photo")
_0xafdb9d[_0x466f71(0x187, "l3ea")](
this[_0x466f71(0x1a9, "8Z)x")](
_0x5223de[_0x466f71(0x170, "i(e%")],
_0xa7d17e,
"",
!![],
),
);
else
_0x5223de["type"] === _0x466f71(0x19a, "S%SI") &&
(await this[_0x466f71(0x172, "xitm")](
_0x446b97[_0x466f71(0x191, "l3ea")][0x0][
_0x466f71(0x17b, "4T^f")
][0x0][_0x466f71(0x184, "l1yR")],
!![],
)[_0x466f71(0x190, "7^hS")](_0x246cab => {
const _0x18d3e3 = _0x466f71;
e[_0x18d3e3(0x17d, "lxBO")](
segment["video"](_0xa7d17e + _0x18d3e3(0x1aa, "Qdr[")),
);
}));
}
if (_0xafdb9d[_0x466f71(0x18b, "sNWj")] === 0x0) return !![];
let _0x3b71e4 = [],
_0x183d7d = [];
await Promise[_0x466f71(0x186, "n58F")](_0xafdb9d)[_0x466f71(0x19b, "Wshq")](
_0x4cff3f => {
_0x4cff3f["forEach"](_0x22c263 => {
const _0x49694d = _0x2a30;
_0x183d7d[_0x49694d(0x195, "q#t*")](_0x22c263),
_0x3b71e4[_0x49694d(0x1a7, "#E4x")]({
message: segment[_0x49694d(0x180, "XTb0")](
fs["readFileSync"](_0x22c263),
),
nickname:
this["e"][_0x49694d(0x197, "n58F")][
_0x49694d(0x194, "S%SI")
] || this["e"][_0x49694d(0x177, "3o)K")],
user_id: this["e"][_0x49694d(0x173, "3Kft")],
});
});
},
),
await e[_0x466f71(0x176, "ljiK")](
await Bot[_0x466f71(0x1a5, "k90U")](_0x3b71e4),
),
_0x183d7d["forEach"](_0x35c45b => {
const _0x4bccf3 = _0x466f71;
fs[_0x4bccf3(0x19f, "WOCh")](_0x35c45b);
});
})
["catch"](_0x1a0949 => {
const _0x5b9cf4 = _0x2b294a;
e[_0x5b9cf4(0x18e, "ZG#8")](_0x5b9cf4(0x198, "PrCv"));
});
return !![];
}
// acfun解析
async acfun(e) {
const path = `${this.defaultPath}${this.e.group_id || this.e.user_id}/temp/`;
await mkdirIfNotExists(path);
let inputMsg = e.msg;
// 适配手机分享https://m.acfun.cn/v/?ac=32838812&sid=d2b0991bd6ad9c09
if (inputMsg.includes("m.acfun.cn")) {
inputMsg = `https://www.acfun.cn/v/ac${/ac=([^&?]*)/.exec(inputMsg)[1]}`;
}
parseUrl(inputMsg).then(res => {
e.reply(`识别:猴山,${res.videoName}`);
parseM3u8(res.urlM3u8s[res.urlM3u8s.length - 1]).then(res2 => {
downloadM3u8Videos(res2.m3u8FullUrls, path).then(_ => {
mergeAcFileToMp4(res2.tsNames, path, `${path}out.mp4`).then(_ => {
e.reply(segment.video(`${path}out.mp4`));
});
});
});
});
return true;
}
// 小红书解析
async redbook(e) {
// 解析短号
let msgUrl =
/(http:|https:)\/\/(xhslink|xiaohongshu).com\/[A-Za-z\d._?%&+\-=\/#@]*/.exec(
e.msg,
)?.[0] ||
/(http:|https:)\/\/www\.xiaohongshu\.com\/discovery\/item\/(\w+)/.exec(
e.message[0].data,
)?.[0];
let id;
if (msgUrl.includes("xhslink")) {
await fetch(msgUrl, {
redirect: "follow",
}).then(resp => {
const uri = decodeURIComponent(resp.url);
id = /explore\/(\w+)/.exec(uri)?.[1];
});
} else {
id = /explore\/(\w+)/.exec(msgUrl)?.[1] || /discovery\/item\/(\w+)/.exec(msgUrl)?.[1];
}
const downloadPath = `${this.defaultPath}${this.e.group_id || this.e.user_id}`;
// 获取信息
fetch(`https://www.xiaohongshu.com/discovery/item/${id}`, {
headers: {
"user-agent":
"Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/110.0.0.0",
cookie: Buffer.from(XHS_CK, "base64").toString("utf-8"),
},
}).then(async resp => {
const xhsHtml = await resp.text();
const reg = /window.__INITIAL_STATE__=(.*?)<\/script>/;
const resJson = xhsHtml.match(reg)[0];
const res = JSON.parse(resJson.match(reg)[1]);
const noteData = res.noteData.data.noteData;
const { title, desc, type } = noteData;
e.reply(`识别:小红书, ${title}\n${desc}`);
let imgPromise = [];
if (type === "video") {
const url = noteData.video.url;
this.downloadVideo(url).then(path => {
e.reply(segment.video(path + "/temp.mp4"));
});
return true;
} else if (type === "normal") {
noteData.imageList.map(async (item, index) => {
imgPromise.push(this.downloadImg(item.url, downloadPath, index.toString()));
});
}
const paths = await Promise.all(imgPromise);
const imagesData = await Promise.all(
paths.map(async item => {
const fileContent = await fs.promises.readFile(item);
return {
message: segment.image(fileContent),
nickname: e.sender.card || e.user_id,
user_id: e.user_id,
};
}),
);
// Reply with forward message
e.reply(await Bot.makeForwardMsg(imagesData));
// Clean up files
await Promise.all(paths.map(item => fs.promises.unlink(item)));
});
return true;
}
// 文献解析
async literature(e) {
const litReg = /(http:|https:)\/\/doi.org\/[A-Za-z\d._?%&+\-=\/#@]*/;
const url = litReg.exec(e.msg.trim())[0];
const waitList = [
"https://sci-hub.se/",
"https://sci-hub.st/",
"https://sci-hub.do/",
"https://sci-hubtw.hkvisa.net/",
"https://sci-hub.ren/",
"https://sci-hub.ee/",
"https://sci-hub.ru/",
];
const flag = /doi.org\/(.*)/.exec(url)[1];
const newWaitList = waitList.map(item => {
return item + flag;
});
await Promise.any(newWaitList).then(resp => {
e.reply(resp);
});
}
// 清理垃圾文件
async clearTrash(e) {
const directory = "./data/";
try {
const files = await fs.promises.readdir(directory);
for (const file of files) {
// 如果文件名符合规则,执行删除操作
if (/^[0-9a-f]{32}$/.test(file)) {
await fs.promises.unlink(directory + file);
}
}
e.reply(`清理完成!`);
} catch (err) {
logger.error(err);
await e.reply("清理失败,重试或者手动清理即可");
}
}
// ins解析
async instagram(e) {
let suffix = e.msg.match(/(?<=com\/)[\/a-z0-9A-Z].*/)[0];
if (suffix.startsWith("reel")) {
suffix = suffix.replace("reel/", "p/");
}
const API = `https://imginn.com/${suffix}`;
logger.info(API);
let imgPromise = [];
const downloadPath = `${this.defaultPath}${this.e.group_id || this.e.user_id}`;
// 简单封装图片下载
const downloadImg = (url, destination) => {
return new Promise((resolve, reject) => {
fetch(url, {
timeout: 10000,
agent: new HttpProxyAgent(this.myProxy),
redirect: "follow",
follow: 10,
})
.then(res => {
const dest = fs.createWriteStream(destination);
res.body.pipe(dest);
dest.on("finish", () => resolve(destination));
})
.catch(err => reject(err));
});
};
await fetch(API, {
headers: {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36",
},
}).then(async resp => {
const html = await resp.text();
const desc = html.match(/(?<=content=").*?(?=\")/g)?.[2];
const images = html.match(/<div class=\"swiper-slide.*?\">/g);
if (!_.isNull(images)) {
e.reply(`识别Insta${desc || "暂无描述"}\n`);
images.map((item, index) => {
const imgUrl = /(?<=data-src=").*?(?=")/
.exec(item)[0]
.replace(/#38/g, "")
.replace(/;/g, "");
imgPromise.push(downloadImg(imgUrl, `${downloadPath}/${index}.jpg`));
});
}
// TODO 视频会出bug暂时不做
// if (html.includes("data-video")) {
// const video = html.match(/(?<=data-video=").*?(?=")/g)[0].replace(/#38/g, "").replace(/;/g, "")
// this.downloadVideo(video, true).then(path => {
// e.reply(segment.video(path));
// })
// }
});
if (imgPromise.length > 0) {
let path = [];
const images = await Promise.all(imgPromise).then(paths => {
return paths.map(item => {
path.push(item);
return {
message: segment.image(fs.readFileSync(item)),
nickname: e.sender.card || e.user_id,
user_id: e.user_id,
};
});
});
await this.reply(await Bot.makeForwardMsg(images));
// 清理
path.forEach(item => {
fs.unlinkSync(item);
});
}
return true;
}
// 波点音乐解析
async bodianMusic(e) {
// 音频例子https://h5app.kuwo.cn/m/bodian/playMusic.html?uid=3216773&musicId=192015898&opusId=&extendType=together
// 视频例子https://h5app.kuwo.cn/m/bodian/play.html?uid=3216773&mvId=118987&opusId=770096&extendType=together
const id =
/(?=musicId).*?(?=&)/.exec(e.msg.trim())?.[0].replace("musicId=", "") ||
/(?=mvId).*?(?=&)/.exec(e.msg.trim())?.[0].replace("mvId=", "");
const { name, album, artist, albumPic120, categorys } = await getBodianMusicInfo(id);
e.reply([
`识别:波点音乐,${name}-${album}-${artist}\n标签:${categorys
.map(item => item.name)
.join(" | ")}`,
segment.image(albumPic120),
]);
if (e.msg.includes("musicId")) {
const path = `${this.defaultPath}${this.e.group_id || this.e.user_id}`;
await getBodianAudio(id, path).then(_ => {
Bot.acquireGfs(e.group_id).upload(
fs.readFileSync(path + "/temp.mp3"),
"/",
`${name}-${album}-${artist}.mp3`,
);
});
} else if (e.msg.includes("mvId")) {
await getBodianMv(id).then(res => {
// 下载 && 发送
const { coverUrl, highUrl, lowUrl, shortLowUrl } = res;
this.downloadVideo(lowUrl).then(path => {
e.reply(segment.video(path + "/temp.mp4"));
});
});
}
return true;
}
/**
* 哔哩哔哩下载
* @param title
* @param videoUrl
* @param audioUrl
* @returns {Promise<unknown>}
*/
async downBili(title, videoUrl, audioUrl) {
return Promise.all([
downloadBFile(
videoUrl,
title + "-video.m4s",
_.throttle(
value =>
logger.mark("视频下载进度", {
data: value,
}),
1000,
),
),
downloadBFile(
audioUrl,
title + "-audio.m4s",
_.throttle(
value =>
logger.mark("音频下载进度", {
data: value,
}),
1000,
),
),
]).then(data => {
return mergeFileToMp4(data[0].fullFileName, data[1].fullFileName, `${title}.mp4`);
});
}
/**
* 哔哩哔哩总结
* @returns Promise{string}
* @param videoInfo
*/
async getBiliSummary(videoInfo) {
if (this.biliSessData && this.openaiAccessToken) {
try {
const prompt = await getBiliGptInputText(videoInfo, this.biliSessData);
const response = await this.chatGptClient.sendMessage(prompt);
// 暂时不设计上下文
return response.response
} catch (err) {
logger.error("总结失败,可能是没有弹幕或者网络问题!\n", err);
return ""
}
} else {
return ""
}
}
/**
* 下载一张网络图片(自动以url的最后一个为名字)
* @param img
* @param dir
* @param fileName
* @param isProxy
* @returns {Promise<unknown>}
*/
async downloadImg(img, dir, fileName = "", isProxy = false) {
if (fileName === "") {
fileName = img.split("/").pop();
}
const filepath = `${dir}/${fileName}`;
const writer = fs.createWriteStream(filepath);
const axiosConfig = {
headers: {
"User-Agent":
"Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36",
},
responseType: "stream",
};
if (isProxy) {
axiosConfig.httpAgent = tunnel.httpOverHttp({
proxy: { host: this.proxyAddr, port: this.proxyPort },
});
axiosConfig.httpsAgent = tunnel.httpOverHttp({
proxy: { host: this.proxyAddr, port: this.proxyPort },
});
}
try {
const res = await axios.get(img, axiosConfig);
res.data.pipe(writer);
return new Promise((resolve, reject) => {
writer.on("finish", () => {
writer.close(() => {
resolve(filepath);
});
});
writer.on("error", err => {
fs.unlink(filepath, () => {
reject(err);
});
});
});
} catch (err) {
logger.error("图片下载失败");
}
}
/**
* douyin 请求参数
* @param url
* @returns {Promise<unknown>}
*/
async douyinRequest(url) {
const params = {
headers: {
"User-Agent":
"Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36",
},
timeout: 10000,
};
try {
const resp = await axios.head(url, params);
const location = resp.request.res.responseUrl;
return location;
} catch (error) {
console.error(error);
throw error;
}
}
/**
* 提取视频下载位置
* @returns {{groupPath: string, target: string}}
*/
getGroupPathAndTarget() {
const groupPath = `${this.defaultPath}${this.e.group_id || this.e.user_id}`;
const target = `${groupPath}/temp.mp4`;
return { groupPath, target };
}
/**
* 工具根URL据下载视频 / 音频
* @param url 下载地址
* @param isProxy 是否需要魔法
* @param headers 覆盖头节点
* @returns {Promise<unknown>}
*/
async downloadVideo(url, isProxy = false, headers = null) {
const { groupPath, target } = this.getGroupPathAndTarget.call(this);
await mkdirIfNotExists(groupPath);
const userAgent =
"Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36";
const axiosConfig = {
headers: headers || { "User-Agent": userAgent },
responseType: "stream",
...(isProxy && {
httpAgent: tunnel.httpOverHttp({
proxy: { host: this.proxyAddr, port: this.proxyPort },
}),
httpsAgent: tunnel.httpOverHttp({
proxy: { host: this.proxyAddr, port: this.proxyPort },
}),
}),
};
try {
await checkAndRemoveFile(target);
const res = await axios.get(url, axiosConfig);
logger.mark(`开始下载: ${url}`);
const writer = fs.createWriteStream(target);
res.data.pipe(writer);
return new Promise((resolve, reject) => {
writer.on("finish", () => resolve(groupPath));
writer.on("error", reject);
});
} catch (err) {
logger.error("下载视频发生错误!");
}
}
}