diff --git a/apps/arknights.js b/apps/arknights.js deleted file mode 100644 index f91cd18..0000000 --- a/apps/arknights.js +++ /dev/null @@ -1,32 +0,0 @@ - -import { segment } from "oicq"; -import puppeteer from "../../../lib/puppeteer/puppeteer.js"; - -export class arknights extends plugin { - constructor (e) { - super({ - name: '明日方舟', - dsc: '明日方舟信息查询', - event: 'message', - priority: 500, - rule: [ - { - reg: '^#(明日方舟wiki|arkwiki)(.*)$', - fnc: 'arkWiki' - } - ] - }) - } - - async arkWiki (e) { - const key = e.msg.replace(/#|明日方舟wiki|arkwiki/g, "").trim(); - const browser = await puppeteer.browserInit() - const page = await browser.newPage() - await page.goto(`https://wiki.biligame.com/arknights/${ key }`) - const wikiImg = await page.screenshot({ - fullPage: true, type: 'jpeg', omitBackground: false, quality: 90 - }) - browser.close() - await e.reply(segment.image(wikiImg)) - } -} \ No newline at end of file diff --git a/apps/tools.js b/apps/tools.js index 9869ae1..cb16fca 100644 --- a/apps/tools.js +++ b/apps/tools.js @@ -55,6 +55,10 @@ export class tools extends plugin { { reg: "(.*)(acfun.cn)", fnc: "acfun" + }, + { + reg: "(.*)(xhslink.com|xiaohongshu.com)", + fnc: "redbook" } ], }); @@ -64,6 +68,7 @@ export class tools extends plugin { // redis的key this.redisKey = `Yz:tools:cache:${ this.group_id }`; // 代理接口 + // TODO 填写服务器的内网ID和clash的端口 this.proxyAddr = '10.0.8.10'; this.proxyPort = '7890' this.myProxy = `http://${this.proxyAddr}:${this.proxyPort}`; @@ -81,7 +86,8 @@ export class tools extends plugin { } const place = msg.replace(language[0], "").trim(); // let url = /[\u4E00-\u9FFF]+/g.test(place) - let url = `http://api.fanyi.baidu.com/api/trans/vip/translate?from=auto&to=${ transMap[language[1]] }&appid=20210422000794040&salt=542716863&sign=${ md5("20210422000794040" + place + "542716863" + "HooD_ndgwcGH6SAnxGrM") }&q=${ place }`; + // TODO 查阅百度文档填写 + let url = `http://api.fanyi.baidu.com/api/trans/vip/translate?from=auto&to=${ transMap[language[1]] }&appid=&salt=&sign=${ md5("" + place + "" + "") }&q=${ place }`; await fetch(url) .then((resp) => resp.json()) .then((text) => text.trans_result) @@ -164,9 +170,9 @@ export class tools extends plugin { // 短号处理 if (url.includes('b23.tv')) { const bShortUrl = bShortRex.exec(url)[0] - await this.douyinRequest(bShortUrl).then((res) => { - url = res.replace("m", "www") - }); + await fetch(bShortUrl).then(resp => { + url = resp.url; + }) } else { url = urlRex.exec(url)[0]; } @@ -180,7 +186,7 @@ export class tools extends plugin { // 视频信息获取例子:http://api.bilibili.com/x/web-interface/view?bvid=BV1hY411m7cB const baseVideoInfo = "http://api.bilibili.com/x/web-interface/view"; console.log(url); - const videoId = /video\/(.*)(\/|\?)/g.exec(url)[1]; + const videoId = /video\/(.*?)(\/|\?)/g.exec(url)[1]; // 获取视频信息,然后发送 fetch(videoId.startsWith("BV") ? `${baseVideoInfo}?bvid=${videoId}` : `${baseVideoInfo}?aid=${videoId}`) .then(resp => resp.json()) @@ -207,7 +213,7 @@ export class tools extends plugin { // 百科 async wiki (e) { const key = e.msg.replace(/#|百科|wiki/g, "").trim(); - const url = `https://xiaoapi.cn/API/bk.php?m=json&type=bd&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, { @@ -258,7 +264,7 @@ export class tools extends plugin { const twitterUrl = reg.exec(e.msg); const id = twitterUrl[1]; const httpAgent = new HttpProxyAgent(this.myProxy) - const twitterClient = new TwitterApi('AAAAAAAAAAAAAAAAAAAAAArXkwEAAAAAhSrZLK61mRibO0BKwRXgVvEnIzU%3DRUtuE2PL9EGsi1fjHPDsM7SLhmR1UWuCJMt4PB8FFdm94uQ5qL', {httpAgent}); + const twitterClient = new TwitterApi('', {httpAgent}); // Tell typescript it's a readonly app const readOnlyClient = twitterClient.readOnly; @@ -417,6 +423,558 @@ export class tools extends plugin { return true; } + // 小红书解析 + 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}` + + await 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", + "Content-Type": "application/json", + "Accept-Encoding": "gzip,deflate,compress" + }, + timeout: 10000, + proxy: false, + }).then((resp) => { + const reg = / resp.json()) + .then((text) => text.trans_result) + .then((res) => this.reply(`${ res[0].dst }`, true)) + .catch((err) => logger.error(err)); + 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 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 resp = await fetch(url); + const json = await resp.json(); + const item = json.aweme_detail; + e.reply(`识别:抖音, ${item.desc}`); + const url_2 = item.video.play_addr.url_list[0]; + this.downloadVideo(url_2).then(video => { + e.reply(segment.video(`${this.defaultPath}${this.e.group_id || this.e.user_id}/temp.mp4`)); + }); + }); + 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; + 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 { + url = urlRex.exec(url)[0] + } + const idVideo = await this.getIdVideo(url) + // API链接 + const API_URL = `https://api19-core-useast5.us.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 = /(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() + // 短号处理 + if (url.includes('b23.tv')) { + const bShortUrl = bShortRex.exec(url)[0] + await fetch(bShortUrl).then(resp => { + url = resp.url; + }) + } else { + url = urlRex.exec(url)[0]; + } + + const path = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }/temp` + // 待优化 + if (fs.existsSync(`${ path }.mp4`)) { + console.log("视频已存在"); + fs.unlinkSync(`${ path }.mp4`); + } + // 视频信息获取例子:http://api.bilibili.com/x/web-interface/view?bvid=BV1hY411m7cB + const baseVideoInfo = "http://api.bilibili.com/x/web-interface/view"; + console.log(url); + const videoId = /video\/(.*?)(\/|\?)/g.exec(url)[1]; + // 获取视频信息,然后发送 + fetch(videoId.startsWith("BV") ? `${baseVideoInfo}?bvid=${videoId}` : `${baseVideoInfo}?aid=${videoId}`) + .then(resp => resp.json()) + .then(resp => { + e.reply(`识别:哔哩哔哩, ${resp.data.title}`) + }) + + await getDownloadUrl(url) + .then(data => { + this.downBili(path, data.videoUrl, data.audioUrl) + .then(data => { + e.reply(segment.video(`${ path }.mp4`)) + }) + .catch(data => { + e.reply('解析失败,请重试一下') + }); + }) + .catch(err => { + e.reply('解析失败,请重试一下') + }); + 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 url2 = 'https://api.jikipedia.com/go/auto_complete' + Promise.all([ + // axios.post(url2, { + // 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", + // }, + // timeout: 10000, + // "phrase": key, + // }) + // .then(resp => { + // const data = resp.data.data + // if (_.isEmpty(data)) { + // return data; + // } + // return data[0].entities[0]; + // }), + 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(res => { + const data = res[0] + // const data2 = res[0] + const template = ` + 解释:${ _.get(data, 'msg') }\n + 详情:${ _.get(data, 'more') }\n + `; + // 小鸡解释:${ _.get(data2, 'content') } + e.reply(template) + }) + return true + } + + // twitter解析 + // 例子:https://twitter.com/chonkyanimalx/status/1595834168000204800 + async twitter (e) { + // 配置参数及解析 + const reg = /https?:\/\/twitter.com\/[0-9-a-zA-Z_]{1,20}\/status\/([0-9]*)/ + const twitterUrl = reg.exec(e.msg); + const id = twitterUrl[1]; + const httpAgent = new HttpProxyAgent(this.myProxy) + // TODO 需要填写Twitter id + const twitterClient = new TwitterApi('', {httpAgent}); + + // Tell typescript it's a readonly app + const readOnlyClient = twitterClient.readOnly; + + readOnlyClient.v2.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', + ], + }).then(resp => { + e.reply(`识别:腿忒学习版,${resp.data.text}`) + const downloadPath = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }`; + // 创建文件夹(如果没有过这个群) + if (!fs.existsSync(downloadPath)) { + mkdirsSync(downloadPath); + } + // 开始读取数据 + if (resp.includes.media[0].type === 'photo') { + // 图片 + resp.includes.media.map(item => { + const filePath = `${downloadPath}/${item.url.split('/').pop()}` + this.downloadImgs(item.url, downloadPath).then(tmp => { + e.reply(segment.image(fs.readFileSync(filePath))) + }) + }) + } else { + // 视频 + this.downloadVideo(resp.includes.media[0].variants[0].url, true).then(video => { + e.reply(segment.video(`${downloadPath}/temp.mp4`)); + }); + } + }); + return true; + } + + // 视频解析 + async tx( e ) { + const url = e.msg + 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 ) { + e.reply( name + '\n' + k ) + let forward = await this.makeForwardMsg( url ) + e.reply( forward ) + return true + } else { + e.reply( '解析腾讯视频失败~\n去浏览器使用拼接接口吧...' ) + let forward = await this.makeForwardMsg( url ) + e.reply( forward ) + return true + } + } + + + // 请求参数 + 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, + }; + return new Promise((resolve, reject) => { + axios + .head(url, params) + .then((resp) => { + const location = resp.request.res.responseUrl + resolve(location); + }) + .catch((err) => { + reject(err); + }); + }); + } + + // 工具:根URL据下载视频 / 音频 + 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`; + // 待优化 + if (fs.existsSync(target)) { + console.log(`视频已存在`); + fs.unlinkSync(target); + } + let res; + if (!isProxy) { + res = await 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", + }, + responseType: "stream", + }); + } else { + res = await 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", + }, + responseType: "stream", + httpAgent: tunnel.httpOverHttp({ proxy: { host: this.proxyAddr, port: this.proxyPort } }), + httpsAgent: tunnel.httpOverHttp({ proxy: { host: this.proxyAddr, port: this.proxyPort } }), + }); + } + console.log(`开始下载: ${ url }`); + const writer = fs.createWriteStream(target); + res.data.pipe(writer); + + return new Promise((resolve, reject) => { + writer.on("finish", resolve); + writer.on("error", reject); + }); + } + + // 工具:找到tiktok的视频id + async getIdVideo (url) { + const matching = url.includes("/video/") + if (!matching) { + this.e.reply("没找到,正在获取随机视频!") + return null + } + const idVideo = url.substring(url.indexOf("/video/") + 7, url.length); + return (idVideo.length > 19) ? idVideo.substring(0, idVideo.indexOf("?")) : idVideo; + } + + // acfun解析 + async acfun(e) { + const path = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }/temp/` + if (!fs.existsSync(path)) { + mkdirsSync(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) { + 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}` + + await 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", + "Content-Type": "application/json", + "Accept-Encoding": "gzip,deflate,compress" + }, + timeout: 10000, + proxy: false, + }).then((resp) => { + const reg = / 0) { + e.reply(Bot.makeForwardMsg(images)) + } else { + e.reply("解析失败,重新解析下"); + } + }) + + return true; + } + + // 工具:下载哔哩哔哩 + async downBili (title, videoUrl, audioUrl) { + return Promise.all([ + downloadBFile( + videoUrl, + title + '-video.m4s', + _.throttle( + value => + console.log('download-progress', { + type: 'video', + data: value, + }), + 1000, + ), + ), + downloadBFile( + audioUrl, + title + '-audio.m4s', + _.throttle( + value => + console.log('download-progress', { + type: 'audio', + data: value, + }), + 1000, + ), + ), + ]) + .then(data => { + return mergeFileToMp4(data[0].fullFileName, data[1].fullFileName, title + '.mp4'); + }) + } + + // 工具:下载一张网络图片 + async downloadImgs(img, dir) { + + const filename = img.split('/').pop(); + const filepath = `${dir}/${filename}`; + const writer = fs.createWriteStream(filepath); + return axios.get(img, { + 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", + httpAgent: tunnel.httpOverHttp({ proxy: { host: this.proxyAddr, port: this.proxyPort } }), + httpsAgent: tunnel.httpOverHttp({ proxy: { host: this.proxyAddr, port: this.proxyPort } }), + }).then(res => { + res.data.pipe(writer); + return new Promise((resolve, reject) => { + writer.on('finish', () => resolve(filepath)); + writer.on('error', reject); + }); + }); + } + } + + let images = []; + while (res = reg.exec(resp.data)) { + console.log(`https://ci.xiaohongshu.com${res[2]}`) + images.push({ + message: segment.image(`https://ci.xiaohongshu.com${res[2]}`), + nickname: e.sender.card || e.user_id, + user_id: e.user_id + }) + } + if (images.length > 0) { + e.reply(Bot.makeForwardMsg(images)) + } else { + e.reply("解析失败,重新解析下"); + } + }) + + return true; + } + // 工具:下载哔哩哔哩 async downBili (title, videoUrl, audioUrl) { return Promise.all([