From 97684297d701fb617c7a73e4754bf8eee417c353 Mon Sep 17 00:00:00 2001 From: zhiyu1998 Date: Sat, 25 Feb 2023 17:35:11 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20=E6=B7=BB=E5=8A=A0tiktok?= =?UTF-8?q?=E6=96=B0=E7=9F=AD=E5=8F=B7=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/tools.js | 128 +++++++++++++++++++++++++++----------------------- 1 file changed, 70 insertions(+), 58 deletions(-) diff --git a/apps/tools.js b/apps/tools.js index 2919325..6fa2965 100644 --- a/apps/tools.js +++ b/apps/tools.js @@ -17,7 +17,7 @@ import { retry } from "../utils/common.js"; import config from "../model/index.js"; export class tools extends plugin { - constructor() { + constructor () { super({ name: "工具和学习类", dsc: "工具相关指令", @@ -33,7 +33,7 @@ export class tools extends plugin { fnc: "douyin", }, { - reg: "(.*)(www.tiktok.com)|(vt.tiktok.com)", + reg: "(.*)(www.tiktok.com)|(vt.tiktok.com)|(vm.tiktok.com)", fnc: "tiktok", }, { @@ -72,12 +72,12 @@ export class tools extends plugin { // 视频保存路径 this.defaultPath = this.toolsConfig.defaultPath; // redis的key - this.redisKey = `Yz:tools:cache:${this.group_id}`; + this.redisKey = `Yz:tools:cache:${ this.group_id }`; // 代理接口 // TODO 填写服务器的内网ID和clash的端口 this.proxyAddr = this.toolsConfig.proxyAddr; this.proxyPort = this.toolsConfig.proxyPort; - this.myProxy = `http://${this.proxyAddr}:${this.proxyPort}`; + this.myProxy = `http://${ this.proxyAddr }:${ this.proxyPort }`; // console.log(this.myProxy) // 加载百度翻译配置 this.translateAppId = this.toolsConfig.translateAppId; @@ -87,7 +87,7 @@ export class tools extends plugin { } // 翻译插件 - async trans(e) { + async trans (e) { const languageReg = /翻(.)/g; const msg = e.msg.trim(); const language = languageReg.exec(msg); @@ -102,20 +102,20 @@ export class tools extends plugin { // let url = `http://api.fanyi.baidu.com/api/trans/vip/translate?from=auto&to=${ transMap[language[1]] }&appid=APP ID&salt=自定义&sign=${ md5("APP ID" + place + "自定义" + "密钥") }&q=${ place }`; let url = `http://api.fanyi.baidu.com/api/trans/vip/translate?from=auto&to=${ transMap[language[1]] - }&appid=${this.translateAppId}&salt=rconsole&sign=${md5( + }&appid=${ this.translateAppId }&salt=rconsole&sign=${ md5( this.translateAppId + place + "rconsole" + this.translateSecret - )}&q=${place}`; + ) }&q=${ place }`; // console.log(url) await fetch(url) .then(resp => resp.json()) .then(text => text.trans_result) - .then(res => this.reply(`${res[0].dst}`, true)) + .then(res => this.reply(`${ res[0].dst }`, true)) .catch(err => logger.error(err)); return true; } // 抖音解析 - async douyin(e) { + async douyin (e) { const urlRex = /(http:|https:)\/\/v.douyin.com\/[A-Za-z\d._?%&+\-=\/#]*/g; const douUrl = urlRex.exec(e.msg.trim())[0]; @@ -123,7 +123,7 @@ export class tools extends plugin { const douRex = /.*video\/(\d+)\/(.*?)/g; const douId = douRex.exec(res)[1]; // 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`; + 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`; // 默认重试3次,每次间隔1s (防止SyntaxError: Unexpected token b in JSON at position 0) retry( await function () { @@ -131,7 +131,7 @@ export class tools extends plugin { } ).then(async resp_json => { const item = resp_json.aweme_detail; - e.reply(`识别:抖音, ${item.desc}`); + e.reply(`识别:抖音, ${ item.desc }`); const url_type_code = item.aweme_type; const url_type = douyinTypeMap[url_type_code]; if (url_type === "video") { @@ -139,7 +139,7 @@ export class tools extends plugin { this.downloadVideo(url_2).then(video => { e.reply( segment.video( - `${this.defaultPath}${this.e.group_id || this.e.user_id}/temp.mp4` + `${ this.defaultPath }${ this.e.group_id || this.e.user_id }/temp.mp4` ) ); }); @@ -168,9 +168,10 @@ export class tools extends plugin { } // tiktok解析 - async tiktok(e) { + 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")) { @@ -183,12 +184,23 @@ export class tools extends plugin { }).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]; } const idVideo = await this.getIdVideo(url); // 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`; + 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, { @@ -209,11 +221,11 @@ export class tools extends plugin { }) .then(resp => { const data = resp.data.aweme_list[0]; - e.reply(`识别:tiktok, ${data.desc}`); + 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` + `${ this.defaultPath }${ this.e.group_id || this.e.user_id }/temp.mp4` ) ); }); @@ -222,7 +234,7 @@ export class tools extends plugin { } // bilibi解析 - async bili(e) { + async bili (e) { const urlRex = /(http:|https:)\/\/www.bilibili.com\/[A-Za-z\d._?%&+\-=\/#]*/g; const bShortRex = /(http:|https:)\/\/b23.tv\/[A-Za-z\d._?%&+\-=\/#]*/g; let url = e.msg.trim(); @@ -246,7 +258,7 @@ export class tools extends plugin { // console.log(dynamicId) getDynamic(dynamicId).then(resp => { if (resp.dynamicSrc.length > 0) { - e.reply(`识别:哔哩哔哩动态, ${resp.dynamicDesc}`); + e.reply(`识别:哔哩哔哩动态, ${ resp.dynamicDesc }`); // let dynamicSrcMsg = [] // resp.dynamicSrc.forEach(item => { // dynamicSrcMsg.push({ @@ -265,7 +277,7 @@ export class tools extends plugin { return true; } - const path = `${this.defaultPath}${this.e.group_id || this.e.user_id}/`; + const path = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }/`; if (!fs.existsSync(path)) { mkdirsSync(path); } @@ -277,12 +289,12 @@ export class tools extends plugin { // 获取视频信息,然后发送 fetch( videoId.startsWith("BV") - ? `${baseVideoInfo}?bvid=${videoId}` - : `${baseVideoInfo}?aid=${videoId}` + ? `${ baseVideoInfo }?bvid=${ videoId }` + : `${ baseVideoInfo }?aid=${ videoId }` ) .then(resp => resp.json()) .then(resp => { - e.reply(`识别:哔哩哔哩, ${resp.data.title}`).catch(err => { + e.reply(`识别:哔哩哔哩, ${ resp.data.title }`).catch(err => { e.reply("解析失败,重试一下"); console.log(err); }); @@ -291,9 +303,9 @@ export class tools extends plugin { await getDownloadUrl(url) .then(data => { - this.downBili(`${path}temp`, data.videoUrl, data.audioUrl) + this.downBili(`${ path }temp`, data.videoUrl, data.audioUrl) .then(data => { - e.reply(segment.video(`${path}temp.mp4`)); + e.reply(segment.video(`${ path }temp.mp4`)); }) .catch(err => { console.log(err); @@ -308,9 +320,9 @@ export class tools extends plugin { } // 百科 - async wiki(e) { + 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 url = `https://xiaoapi.cn/API/bk.php?m=json&type=sg&msg=${ encodeURI(key) }`; // const url2 = 'https://api.jikipedia.com/go/auto_complete' Promise.all([ // axios.post(url2, { @@ -344,8 +356,8 @@ export class tools extends plugin { const data = res[0]; // const data2 = res[0] const template = ` - 解释:${_.get(data, "msg")}\n - 详情:${_.get(data, "more")}\n + 解释:${ _.get(data, "msg") }\n + 详情:${ _.get(data, "more") }\n `; // 小鸡解释:${ _.get(data2, 'content') } e.reply(template); @@ -355,7 +367,7 @@ export class tools extends plugin { // 小蓝鸟解析 // 例子:https://twitter.com/chonkyanimalx/status/1595834168000204800 - async twitter(e) { + async twitter (e) { // 配置参数及解析 const reg = /https?:\/\/twitter.com\/[0-9-a-zA-Z_]{1,20}\/status\/([0-9]*)/; const twitterUrl = reg.exec(e.msg); @@ -370,11 +382,11 @@ export class tools extends plugin { .singleTweet(id, { "media.fields": "duration_ms,height,media_key,preview_image_url,public_metrics,type,url,width,alt_text,variants", - expansions: ["entities.mentions.username", "attachments.media_keys"], + expansions: [ "entities.mentions.username", "attachments.media_keys" ], }) .then(resp => { - e.reply(`识别:小蓝鸟学习版,${resp.data.text}`); - const downloadPath = `${this.defaultPath}${this.e.group_id || this.e.user_id}`; + e.reply(`识别:小蓝鸟学习版,${ resp.data.text }`); + const downloadPath = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }`; // 创建文件夹(如果没有过这个群) if (!fs.existsSync(downloadPath)) { mkdirsSync(downloadPath); @@ -383,7 +395,7 @@ export class tools extends plugin { for (let item of resp.includes.media) { if (item.type === "photo") { // 图片 - const filePath = `${downloadPath}/${item.url.split("/").pop()}`; + const filePath = `${ downloadPath }/${ item.url.split("/").pop() }`; this.downloadImg(item.url, downloadPath).then(_ => { e.reply(segment.image(fs.readFileSync(filePath))); fs.unlinkSync(filePath); @@ -391,7 +403,7 @@ export class tools extends plugin { } else if (item.type === "video") { // 视频 this.downloadVideo(resp.includes.media[0].variants[0].url, true).then(_ => { - e.reply(segment.video(`${downloadPath}/temp.mp4`)); + e.reply(segment.video(`${ downloadPath }/temp.mp4`)); }); } } @@ -400,9 +412,9 @@ export class tools extends plugin { } // 视频解析 - async tx(e) { + async tx (e) { const url = e.msg; - const data = await (await fetch(`https://xian.txma.cn/API/jx_txjx.php?url=${url}`)).json(); + const data = await (await fetch(`https://xian.txma.cn/API/jx_txjx.php?url=${ url }`)).json(); const k = data.url; const name = data.title; if (k && name) { @@ -419,7 +431,7 @@ export class tools extends plugin { } // 请求参数 - async douyinRequest(url) { + async douyinRequest (url) { const params = { headers: { "User-Agent": @@ -441,12 +453,12 @@ export class tools extends plugin { } // 工具:根URL据下载视频 / 音频 - async downloadVideo(url, isProxy = false) { - const groupPath = `${this.defaultPath}${this.e.group_id || this.e.user_id}`; + async downloadVideo (url, isProxy = false) { + const groupPath = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }`; if (!fs.existsSync(groupPath)) { mkdirsSync(groupPath); } - const target = `${groupPath}/temp.mp4`; + const target = `${ groupPath }/temp.mp4`; // 待优化 if (fs.existsSync(target)) { console.log(`视频已存在`); @@ -476,7 +488,7 @@ export class tools extends plugin { }), }); } - console.log(`开始下载: ${url}`); + console.log(`开始下载: ${ url }`); const writer = fs.createWriteStream(target); res.data.pipe(writer); @@ -487,7 +499,7 @@ export class tools extends plugin { } // 工具:找到tiktok的视频id - async getIdVideo(url) { + async getIdVideo (url) { const matching = url.includes("/video/"); if (!matching) { this.e.reply("没找到,正在获取随机视频!"); @@ -498,8 +510,8 @@ export class tools extends plugin { } // acfun解析 - async acfun(e) { - const path = `${this.defaultPath}${this.e.group_id || this.e.user_id}/temp/`; + async acfun (e) { + const path = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }/temp/`; if (!fs.existsSync(path)) { mkdirsSync(path); } @@ -507,15 +519,15 @@ export class tools extends plugin { 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]}`; + inputMsg = `https://www.acfun.cn/v/ac${ /ac=([^&?]*)/.exec(inputMsg)[1] }`; } parseUrl(inputMsg).then(res => { - e.reply(`识别:猴山,${res.videoName}`); + 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`)); + mergeAcFileToMp4(res2.tsNames, path, `${ path }out.mp4`).then(_ => { + e.reply(segment.video(`${ path }out.mp4`)); }); }); }); @@ -524,11 +536,11 @@ export class tools extends plugin { } // 小红书解析 - async redbook(e) { + async redbook (e) { const msgUrl = /(http:|https:)\/\/(xhslink|xiaohongshu).com\/[A-Za-z\d._?%&+\-=\/#@]*/.exec( e.msg )[0]; - const url = `https://dlpanda.com/zh-CN/xhs?url=${msgUrl}`; + const url = `https://dlpanda.com/zh-CN/xhs?url=${ msgUrl }`; await axios .get(url, { @@ -545,13 +557,13 @@ export class tools extends plugin { const reg = / { - const filepath = `${downloadPath}/${/com\/(.*)\?/.exec(addr)[1]}.jpg`; + const filepath = `${ downloadPath }/${ /com\/(.*)\?/.exec(addr)[1] }.jpg`; const writer = fs.createWriteStream(filepath); resp.data.pipe(writer); return new Promise((resolve, reject) => { @@ -580,7 +592,7 @@ export class tools extends plugin { } // 文献解析 - async literature(e) { + async literature (e) { const litReg = /(http:|https:)\/\/doi.org\/[A-Za-z\d._?%&+\-=\/#@]*/; const url = litReg.exec(e.msg.trim())[0]; const waitList = [ @@ -601,7 +613,7 @@ export class tools extends plugin { }); } - async downBili(title, videoUrl, audioUrl) { + async downBili (title, videoUrl, audioUrl) { return Promise.all([ downloadBFile( videoUrl, @@ -633,9 +645,9 @@ export class tools extends plugin { } // 工具:下载一张网络图片 - async downloadImg(img, dir) { + async downloadImg (img, dir) { const filename = img.split("/").pop(); - const filepath = `${dir}/${filename}`; + const filepath = `${ dir }/${ filename }`; const writer = fs.createWriteStream(filepath); return axios .get(img, { @@ -669,7 +681,7 @@ export class tools extends plugin { } // 工具:下载pdf文件 - async downloadPDF(url, filename) { + async downloadPDF (url, filename) { return axios({ url: url, responseType: "stream",