// 主库 import { segment } from "oicq"; import fetch from "node-fetch"; // 爬虫库 import puppeteer from "../../../lib/puppeteer/puppeteer.js"; import _ from "lodash"; // http库 import axios from "axios"; import fs from "node:fs"; // 常量 import { CAT_LIMIT } from "../utils/constant.js"; // 书库 import { getZHelper, getYiBook, getBookDetail } from "../utils/books.js"; export class query extends plugin { constructor() { super({ name: "R插件查询类", dsc: "R插件查询相关指令", event: "message.group", priority: 500, rule: [ { reg: "^#医药查询(.*)$", fnc: "doctor", }, { reg: "^#(cat)$", fnc: "cat", }, { reg: "^#推荐软件$", fnc: "softwareRecommended", }, { reg: "^#买家秀$", fnc: "buyerShow", }, { reg: "^#(累了)$", fnc: "cospro", }, { reg: "^#青年大学习$", fnc: "youthLearning", }, { reg: "^#(搜书)(.*)$$", fnc: "searchBook", }, { reg: "^#(bookid)(.*)$$", fnc: "searchBookById", }, { reg: "^#epic", fnc: "epicGame", } ], }); } async doctor(e) { let keyword = e.msg.replace("#医药查询", "").trim(); const url = `https://api2.dayi.org.cn/api/search2?keyword=${keyword}&pageNo=1&pageSize=10`; let res = await fetch(url) .then(resp => resp.json()) .then(resp => resp.list); let msg = []; for (const element of res) { const title = this.removeTag(element.title); const template = ` ${title}\n 标签:${element.secondTitle}\n 介绍:${element.introduction} `; // 如果完全匹配,直接响应页面 if (title === keyword) { const browser = await puppeteer.browserInit(); const page = await browser.newPage(); await page.goto(`https://www.dayi.org.cn/drug/${element.id}`); let buff = await page.screenshot({ fullPage: true, type: "jpeg", omitBackground: false, quality: 90, }); browser.close(); await e.reply(segment.image(buff)); } msg.push({ message: { type: "text", text: `${template}` }, nickname: Bot.nickname, user_id: Bot.user_id, }); } /** 最后回复消息 */ return !!this.reply(await Bot.makeForwardMsg(msg)); } async cat(e) { let images = []; let reqRes = [ ...(await fetch(`https://shibe.online/api/cats?count=${CAT_LIMIT}`).then(data => data.json(), )), ...(await fetch(`https://api.thecatapi.com/v1/images/search?limit=${CAT_LIMIT}`) .then(data => data.json()) .then(json => json.map(item => item.url))), ]; e.reply("涩图也不看了,就看猫是吧"); reqRes.forEach(item => { images.push({ message: segment.image(item), nickname: this.e.sender.card || this.e.user_id, user_id: this.e.user_id, }); }); return !!(await this.reply(await Bot.makeForwardMsg(images))); } async softwareRecommended(e) { // 接口 const pcUrl = "https://www.ghxi.com/ghapi?type=query&n=pc"; const andUrl = "https://www.ghxi.com/ghapi?type=query&n=and"; // 一起请求 const res = [ await fetch(pcUrl) .then(resp => resp.json()) .catch(err => logger.error(err)), await fetch(andUrl) .then(resp => resp.json()) .catch(err => logger.error(err)), ]; // 时间复杂度(n^2) 待优化 const msg = res.map(async recommend => { return recommend.data.list.map(element => { const template = `推荐软件:${element.title}\n地址:${element.url}\n`; return { message: { type: "text", text: template }, nickname: e.sender.card || e.user_id, user_id: e.user_id, }; }); }); await Promise.all(msg).then(res => { res.forEach(async item => { e.reply(await Bot.makeForwardMsg(item)); }); }); return true; } async buyerShow(e) { // http://3650000.xyz/api/?type=img // https://api.vvhan.com/api/tao // https://api.uomg.com/api/rand.img3?format=json // const randomIndex = Math.floor(Math.random() * urls.length); // const randomElement = urls.splice(randomIndex, 1)[0]; const p1 = new Promise((resolve, reject) => { fetch("https://api.vvhan.com/api/tao") .then(resp => { return resolve(resp.url); }) .catch(err => reject(err)); }); const p2 = new Promise((resolve, reject) => { fetch("https://api.uomg.com/api/rand.img3?format=json") .then(resp => resp.json()) .then(resp => { return resolve(resp.imgurl); }) .catch(err => reject(err)); }); Promise.all([p1, p2]).then(res => { res.forEach(item => { e.reply(segment.image(item)); }); }); return true; } // 青年大学习 async youthLearning(e) { await axios .get( "https://qczj.h5yunban.com/qczj-youth-learning/cgi-bin/common-api/course/current", { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36 Edg/95.0.1020.53", }, }, ) .then(resp => { // console.log(resp.data); return resp.data.result.uri.replace("index.html", "m.html"); }) .then(async uri => { axios .get(uri, { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36 Edg/95.0.1020.53", }, }) .then(resp => { const content = resp.data; const resList = content.match(/
<\/div>/g); const valueList = resList.map(item => { return item.match(/data-a="(\d+)"/)[1]; }); let result = []; // 转换ABCD const digitToLetter = { 0: "A", 1: "B", 2: "C", 3: "D", }; for (let i = 0; i < valueList.length; i += 4) { const group = valueList.slice(i, i + 4); if (group.length < 4) { continue; } const letters = group .map((d, indx) => { if (d === "1") { return digitToLetter[indx]; } }) .join(""); result.push(letters); } // 封装答案 let ans = ""; for (let i = 0; i < result.length; i++) { ans += `${i + 1}. ${result[i]}\n`; } e.reply(ans); const imgMatch = uri.match(/[^\/]+/g); const imgId = imgMatch[imgMatch.length - 2]; axios .get(`https://h5.cyol.com/special/daxuexi/${imgId}/images/end.jpg`, { 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", }) .then(resp => { const filePath = "./youthLearning.png"; const writer = fs.createWriteStream(filePath); resp.data.pipe(writer); return new Promise((resolve, reject) => { writer.on("finish", () => { writer.close(() => { resolve(filePath); }); }); writer.on("error", err => { fs.unlink(filePath, () => { reject(err); }); }); }); }) .then(filePath => { e.reply(segment.image(fs.readFileSync(filePath))); fs.unlinkSync(filePath, err => { if (err) throw err; console.error("删除青年大学习文件失败"); }); }); }); }); return true; } async cospro(e) { let req = [ ...(await fetch("https://imgapi.cn/cos2.php?return=jsonpro") .then(resp => resp.json()) .then(json => json.imgurls)), ...(await fetch("https://imgapi.cn/cos.php?return=jsonpro") .then(resp => resp.json()) .then(json => json.imgurls)), ]; e.reply("哪天克火掉一定是在这个群里面..."); let images = []; req.forEach(item => { images.push({ message: segment.image(encodeURI(item)), nickname: this.e.sender.card || this.e.user_id, user_id: this.e.user_id, }); }); return !!(await this.reply(await Bot.makeForwardMsg(images))); } // 搜书 async searchBook(e) { let keyword = e.msg.replace(/#|搜书/g, "").trim(); const thisBookMethod = this; // 主要数据来源 await Promise.all([getZHelper(e, keyword), getYiBook(e, keyword)]).then(async allRes => { const [zHelper, yiBook] = allRes; if (!_.isUndefined(yiBook) && yiBook.length > 0) { await e.reply(await Bot.makeForwardMsg(yiBook)); } if (!_.isUndefined(zHelper) && zHelper.length > 0) { await e.reply(await Bot.makeForwardMsg(zHelper)); await e.reply( "请选择一个你想要的ID、来源,例如:\n" + "11918807 superlib\n" + "只回复11918807 默认zlibrary\n" + "书源若不对应则回复无效链接,数字字母之间空格", ); thisBookMethod.setContext("searchBookContext"); } }); return true; } // 通过id搜书 async searchBookById(e) { let keyword = e.msg.replace(/#bookid/, "").trim(); let id, source; if (keyword.includes(" ")) { [id, source] = keyword.split(" "); } else { id = /\d+/.exec(keyword)[0]; source = ""; } const res = await getBookDetail(e, id, source); await this.reply(await Bot.makeForwardMsg(res)); } /** * @link searchBook 的上下文 * @returns {Promise} */ async searchBookContext() { // 当前消息 const curMsg = this.e; // 上一个消息 // const preMsg = this.getContext(); if (!curMsg.msg) { this.e.reply("请回复id和来源!"); return; } // 获取id和来源 let id, source; if (curMsg.msg.includes(" ")) { [id, source] = curMsg.msg.split(" "); } else { id = /\d+/.exec(curMsg.msg)[0]; source = ""; } const res = await getBookDetail(curMsg, id, source); await this.reply(await Bot.makeForwardMsg(res)); this.finish("searchBookContext"); } // epic游戏查询:逻辑来自于https://github.com/monsterxcn/nonebot_plugin_epicfree/blob/main/nonebot_plugin_epicfree/data_source.py async epicGame(e) { const API = `https://store-site-backend-static-ipv4.ak.epicgames.com/freeGamesPromotions?locale=zh-CN&country=CN&allowCountries=CN`; const res = await fetch(API, { headers: { Referer: "https://www.epicgames.com/store/zh-CN/", "Content-Type": "application/json; charset=utf-8", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36", }, timeout: 10000, }).then((resp) => resp.json()); const games = res.data.Catalog.searchStore.elements; // console.log(games); e.reply( `获取到${games.length}个游戏,${games.map((item) => item.title).join(", ")}` ); for (const game of games) { const gameName = game?.title || "未知"; if (game?.promotions === undefined) { continue; } const game_promotions = game.promotions?.promotionalOffers; const upcoming_promotions = game.promotions?.upcomingPromotionalOffers; const originalPrice = game.price.totalPrice.fmtPrice.originalPrice; const discount_price = game.price.totalPrice.fmtPrice.discountPrice; if (!game_promotions && upcoming_promotions) { logger.mark(`跳过即将推出免费游玩的游戏:${gameName}(${discount_price})`); continue; } else if (game.price.totalPrice.fmtPrice.discountPrice != "0") { logger.mark(`跳过促销但不免费的游戏:${gameName}(${discount_price})`); continue; } // 图片处理 let availableImgUrl = []; const available = [ "Thumbnail", "VaultOpened", "DieselStoreFrontWide", "OfferImageWide", ]; for (const image of game.keyImages) { if ((image.url != null) && available.includes(image.type)) { availableImgUrl.push(image.url); } } // 处理游戏发行信息 let gameDev = game.seller.name; let gamePub = game.seller.name; for (const pair of game.customAttributes) { if (pair.key === "developerName") { gameDev = pair.value; } else if (pair.key === "publisherName") { gamePub = pair.value; } } const dev_com = gameDev != gamePub ? `${gameDev} 开发、` : ""; const companies = gamePub != "Epic Dev Test Account" ? `由 ${dev_com}${gamePub} 发行,` : ""; //处理游戏限免结束时间 const dateRfc3339 = game_promotions?.[0].promotionalOffers[0].endDate; let endDate = "不明确"; if (dateRfc3339 !== undefined) { const date = new Date(dateRfc3339); const options = { timeZone: "Asia/Shanghai", month: "long", day: "2-digit", hour: "2-digit", minute: "2-digit", }; endDate = date .toLocaleString("zh-CN", options) .replace(/(\d{1,2})\s/, "$1日 "); } const resMsg = `${gameName} (${originalPrice})\n\n${game.description}\n\n游戏${companies}\n${game?.url}\n将在 ${endDate} 结束免费游玩,戳上方链接领取吧~`; e.reply([resMsg, ...availableImgUrl.map(url => segment.image(url))]) } } // 删除标签 removeTag(title) { const titleRex = /<[^>]+>/g; return title.replace(titleRex, ""); } }