🎉初始化仓库

This commit is contained in:
zhiyu1998 2022-11-20 16:07:23 +08:00
parent ea89429a1e
commit a81e55047f
69 changed files with 25327 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.idea

38
README.md Normal file
View File

@ -0,0 +1,38 @@
# R-plugin
个人团队用的Yunzai-bot插件插件的各种业务来源于周围人
<img src="https://cdn.jsdelivr.net/gh/xianxincoder/xianxincoder/assets/github-contribution-grid-snake.svg">
## 🗃️文件架构
apps -- 业务核心
config -- 配置文件
model -- 核心文件[建议不动]
resource -- 资源文件
test -- 爬虫文件[python]
index -- 主入口
## 📔使用说明
1. `test -- main.py`爬取链接
> python3 main.py
2. 下载mongodb
> linux系统下自己装一个mongodb上一个密码(不上有风险)
3. 在`Yunzai-Bot`安装mongodb依赖
> pnpm i mongodb
4. 下载插件
> 注可以不用mongodb这些操作只是用不了一些命令而已
## 📦业务
![help](./img/help.jpg)
## 🤳版本
![help](./img/version.jpg)
## 🚀后记
* 文件借鉴了很多插件,精简个人认为可以精简的内容。
* 素材来源于网络,仅供交流学习使用
* 严禁用于任何商业用途和非法行为
* 如果对你有帮助辛苦给个star这是对我最大的鼓励

35
apps/cat.js Normal file
View File

@ -0,0 +1,35 @@
// 主库
import { segment } from 'oicq'
import fetch from 'node-fetch'
// 配置文件库
import config from '../model/index.js'
export class cat extends plugin {
constructor () {
super({
name: '猫',
dsc: '猫相关指令',
event: 'message.group',
priority: 500,
rule: [
{
reg: '^#(cat)$', fnc: 'cat'
}
]
})
this.catConfig = config.getConfig('cat')
}
async cat (e) {
const numb = this.catConfig.count
let images = []
let reqRes = [...await fetch(`https://shibe.online/api/cats?count=${numb}`).then(data => data.json()), ...await fetch(`https://api.thecatapi.com/v1/images/search?limit=${numb}`).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)))
}
}

61
apps/doctor.js Normal file
View File

@ -0,0 +1,61 @@
// 主库
import { segment } from 'oicq'
import fetch from 'node-fetch'
// 爬虫库
import puppeteer from '../../../lib/puppeteer/puppeteer.js'
export class doctor extends plugin {
constructor () {
super({
name: '医药查询',
dsc: '医药相关指令',
event: 'message.group',
priority: 500,
rule: [
{
reg: '^#*医药查询 (.*)$',
fnc: 'doctor'
}
]
})
}
async doctor (e) {
let keyword = e.msg.split(' ')[1]
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.uin
})
}
/** 最后回复消息 */
return !!this.reply(await Bot.makeForwardMsg(msg))
}
// 删除标签
removeTag (title) {
const titleRex = /<[^>]+>/g
return title.replace(titleRex, '')
}
}

46
apps/help.js Normal file
View File

@ -0,0 +1,46 @@
import Help from '../model/help.js'
import puppeteer from '../../../lib/puppeteer/puppeteer.js'
import md5 from 'md5'
let helpData = {
md5: '',
img: ''
}
export class help extends plugin {
constructor (e) {
super({
name: 'RConsole插件帮助',
dsc: 'RConsole插件帮助插件帮助',
event: 'message',
priority: 500,
rule: [
{
reg: '^#*R(插件)?(命令|帮助|菜单|help|说明|功能|指令|使用说明)$',
fnc: 'help'
}
]
})
}
/**
* rule - 闲心插件帮助
* @returns
*/
async help () {
let data = await Help.get(this.e)
if (!data) return
let img = await this.cache(data)
await this.reply(img)
}
async cache (data) {
let tmp = md5(JSON.stringify(data))
if (helpData.md5 == tmp) return helpData.img
helpData.img = await puppeteer.screenshot('help', data)
helpData.md5 = tmp
return helpData.img
}
}

82
apps/hotSearch.js Normal file
View File

@ -0,0 +1,82 @@
import _ from 'lodash'
import fetch from 'node-fetch'
export class hotSearch extends plugin {
constructor () {
super({
name: '热搜查询',
dsc: '热搜相关指令',
event: 'message.group',
priority: 500,
rule: [
{
reg: '^#(热搜)(.*)$', fnc: 'hotSearch'
}
]
})
}
async hotSearch (e) {
let keyword = e.msg.replace(/#|热搜/g, '').trim()
// 虎扑/知乎/36氪/百度/哔哩哔哩/贴吧/微博/抖音/豆瓣/少数派/IT资讯/微信
let url = 'https://api.vvhan.com/api/hotlist?type='
switch (keyword) {
case '虎扑':
url += 'hupu'
break
case '知乎':
url += 'zhihuHot'
break
case '36氪':
url += '36Ke'
break
case '百度':
url += 'baiduRD'
break
case '哔哩哔哩':
url += 'bili'
break
case '贴吧':
url += 'baiduRY'
break
case '微博':
url += 'wbHot'
break
case '抖音':
url += 'douyinHot'
break
case '豆瓣':
url += 'douban'
break
case '少数派':
url += 'ssPai'
break
case 'IT资讯':
url += 'itInfo'
break
case '微信':
url += 'wxHot'
break
default:
url += 'history'
break
}
let msg = []
let res = await fetch(url)
.then((resp) => resp.json())
.then((resp) => resp.data)
.catch((err) => logger.error(err))
res.forEach((element) => {
const template = `
标题${element.title}\n
简介${_.isNull(element.desc) ? '' : element.desc}\n
热度${element.hot}\n
访问详情${element.url}\n
`
msg.push({
message: { type: 'text', text: `${template}` }, nickname: Bot.nickname, user_id: Bot.uin
})
})
return !!this.reply(await Bot.makeForwardMsg(msg))
}
}

275
apps/mystery.js Normal file
View File

@ -0,0 +1,275 @@
// 主库
import { segment } from 'oicq'
import common from '../../../lib/common/common.js'
import fetch from 'node-fetch'
// 配置文件
import config from '../model/index.js'
// 其他库
import _ from 'lodash'
import mongodb from 'mongodb'
// Mongodb初始化
function initMongo () {
const MongoClient = mongodb.MongoClient
const url = 'mongodb://localhost:27017/'
return new Promise((resolve, reject) => {
MongoClient.connect(url, (err, db) => {
const dbo = db.db('test')
if (err) {
throw err // 和调用 reject(err) 效果类似
}
let collection = dbo.collection('temp')
resolve(collection)
})
})
}
const mongo = initMongo()
export class mystery extends plugin {
constructor () {
super({
name: '神秘区域',
dsc: '神秘指令',
event: 'message.group',
priority: 500,
rule: [
{
reg: '^#(雀食|确实)$', fnc: 'mystery'
},
{
reg: '^#来份涩图$', fnc: 'setu'
},
{
reg: '^#(累了)$', fnc: 'cospro'
},
{
reg: '^#(啊?|啊?)$', fnc: 'aaa'
},
{
reg: '^#沃日吗$', fnc: 'tuiimg'
}
]
})
this.mysteryConfig = config.getConfig('mystery')
}
// 接受到消息都会先执行一次
async accept () {
let oldReply = this.e.reply
this.e.reply = async function (msgs, quote, data) {
if (!msgs) return false
if (!Array.isArray(msgs)) msgs = [msgs]
let result = await oldReply(msgs, quote, data)
if (!result || !result.message_id) {
let isxml = false
for (let msg of msgs) {
if (msg && msg?.type == 'xml' && msg?.data) {
msg.data = msg.data.replace(/^<\?xml.*update=.*?>/g, '<?xml update="1.0" encoding="utf-8" ?>')
isxml = true
}
}
if (isxml) {
result = await oldReply(msgs, quote, data)
} else {
let MsgList = [{
message: msgs, nickname: Bot.nickname, user_id: Bot.uin
}]
let forwardMsg = await Bot.makeForwardMsg(MsgList)
forwardMsg.data = forwardMsg.data
.replace('<?xml update="1.0" encoding="utf-8"?>', '<?xml update="1.0" encoding="utf-8" ?>')
.replace(/\n/g, '')
.replace(/<title color="#777777" size="26">(.+?)<\/title>/g, '___')
.replace(/___+/, '<title color="#777777" size="26">请点击查看内容</title>')
msgs = forwardMsg
result = await oldReply(msgs, quote, data)
}
if (!result || !result.message_id) {
logger.error('风控消息处理失败请登录手机QQ查看是否可手动解除风控')
}
}
return result
}
}
async mystery (e) {
// 最大页数
const maxPage = this.mysteryConfig.mystery.maxPage
const maxPigObj = this.mysteryConfig.mystery.maxPigObj
// 限制最大图片数量
const imageCountLimit = this.mysteryConfig.mystery.imageCountLimit
// 随机算法
const page = _.random(1, maxPage)
const randomIndex = _.random(0, maxPigObj - 1)
// 回复
this.reply('确实是吧, 正在探索...')
// 请求
let images = []
let imgData = []
let url = `https://www.cos6.net/wp-json/wp/v2/posts?page=${page}`
await fetch(url)
.then((resp) => {
return resp.json()
})
.then((json) => {
if (!json.length) {
this.e.reply('探索失败,你再我去一次吧')
return false
}
const content = json[randomIndex].content
images = this.getImages(content.rendered)
// 如果图片为空直接返回
if (images.length === 0) {
this.e.reply('探索失败,你再我去一次吧')
return false
}
// 洗牌
images = _.shuffle(images)
// 限制长度
if (images.length > imageCountLimit) {
images = images.slice(1, imageCountLimit + 1)
}
// 循环队列
images.forEach((item) => {
imgData.push({
message: segment.image(item),
nickname: this.e.sender.card || this.e.user_id,
user_id: this.e.user_id
})
})
})
.catch((err) => logger.error(err))
return !!(await this.reply(await Bot.makeForwardMsg(imgData)))
}
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 aaa (e) {
// https://yingtall.com/wp-json/wp/v2/posts?page=64
// 最大页数
const maxPage = this.mysteryConfig.aaa.maxPage
const maxPigObj = this.mysteryConfig.aaa.maxPigObj
// 限制最大图片数量
const imageCountLimit = this.mysteryConfig.aaa.imageCountLimit
// 随机算法
const page = _.random(1, maxPage)
const randomIndex = _.random(0, maxPigObj - 1)
// 回复
this.reply('真变态啊...')
// 请求
let images = []
let imgData = []
let url = `https://yingtall.com/wp-json/wp/v2/posts?page=${page}`
await fetch(url)
.then((resp) => {
return resp.json()
})
.then((json) => {
if (!json.length) {
this.e.reply('探索失败,你再我去一次吧')
return false
}
const content = json[randomIndex].content
images = this.getImages2(content.rendered)
console.log(images)
// 如果图片为空直接返回
if (images.length === 0) {
this.e.reply('探索失败,你再我去一次吧')
return false
}
// 洗牌
images = _.shuffle(images)
// 限制长度
if (images.length > imageCountLimit) {
images = images.slice(1, imageCountLimit + 1)
}
// 循环队列
images.forEach((item) => {
imgData.push({
message: segment.image(item),
nickname: this.e.sender.card || this.e.user_id,
user_id: this.e.user_id
})
})
})
.catch((err) => logger.error(err))
return !!(await this.reply(await Bot.makeForwardMsg(imgData)))
}
async setu (e) {
const numb = this.mysteryConfig.setu.count
// 图源
const urlList = ['https://iw233.cn/api.php?sort=random', 'https://iw233.cn/API/Random.php']
e.reply('探索中...')
let images = []
for (let i = numb; i > 0; i--) {
urlList.forEach(url => {
images.push({
message: segment.image(url), nickname: this.e.sender.card || this.e.user_id, user_id: this.e.user_id
})
})
await common.sleep(200)
}
return !!(await this.reply(await Bot.makeForwardMsg(images)))
}
async tuiimg (e) {
const MAX_SIZE = this.mysteryConfig.tuiimg.count
this.reply('这群早晚被你整没了...')
let images = []
const template = {
nickname: this.e.sender.card || this.e.user_id, user_id: this.e.user_id
}
await mongo.then(conn => {
return conn.aggregate([{ $sample: { size: MAX_SIZE } }]).toArray()
}).then((result) => {
console.log(result)
result.forEach((item) => {
images.push({
message: segment.image(item.url), ...template
})
})
})
return !!(await this.reply(await Bot.makeForwardMsg(images), false, 60))
}
// 正则:获取图片
getImages (string) {
const imgRex = /(http|https):\/\/([\w.]+\/?)\S*.(jpg|JPG|png|PNG|gif|GIF|jpeg|JPEG)/g
const images = []
let img
while ((img = imgRex.exec(string))) {
images.push(encodeURI(img[0]))
}
return images
}
// 正则:获取图片
getImages2 (string) {
const imgRex = /<img.*?src="(.*?)"[^>]+>/g
const images = []
let img
while ((img = imgRex.exec(string))) {
images.push(encodeURI(img[1]))
}
return images
}
}

55
apps/recommend.js Normal file
View File

@ -0,0 +1,55 @@
import fetch from 'node-fetch'
export class recommend extends plugin {
constructor () {
super({
name: '推荐软件',
dsc: '推荐相关指令',
event: 'message.group',
priority: 500,
rule: [
{
/** 命令正则匹配 */
reg: '^#电脑软件推荐$', /** 执行方法 */
fnc: 'computerRecommended'
}, {
/** 命令正则匹配 */
reg: '^#安卓软件推荐$', /** 执行方法 */
fnc: 'androidRecommended'
}
]
})
}
async computerRecommended (e) {
let url = 'https://www.ghxi.com/ghapi?type=query&n=pc'
/** 调用接口获取数据 */
let res = await fetch(url).catch((err) => logger.error(err))
/** 接口结果json字符串转对象 */
res = await res.json()
let msg = []
res.data.list.forEach((element) => {
const template = `推荐软件:${element.title}\n地址:${element.url}\n`
msg.push({
message: { type: 'text', text: `${template}` }, nickname: Bot.nickname, user_id: Bot.uin
})
})
/** 最后回复消息 */
return !!this.reply(await Bot.makeForwardMsg(msg))
}
async androidRecommended (e) {
let url = 'https://www.ghxi.com/ghapi?type=query&n=and'
let res = await fetch(url).catch((err) => logger.error(err))
res = await res.json()
let msg = []
res.data.list.forEach((element) => {
const template = `推荐软件:${element.title}\n地址:${element.url}\n`
msg.push({
message: { type: 'text', text: `${template}` }, nickname: Bot.nickname, user_id: Bot.uin
})
})
return !!this.reply(await Bot.makeForwardMsg(msg))
}
}

30
apps/tools.js Normal file
View File

@ -0,0 +1,30 @@
import fetch from 'node-fetch'
import md5 from 'md5'
export class tools extends plugin {
constructor () {
super({
name: '工具和学习类',
dsc: '工具相关指令',
event: 'message.group',
priority: 500,
rule: [
{
reg: '^#(翻译)(.*)$', fnc: 'trans'
}
]
})
}
// 翻译插件
async trans (e) {
let place = e.msg.replace(/#|翻译/g, '').trim()
let url = /[\u4E00-\u9FFF]+/g.test(place) ? `http://api.fanyi.baidu.com/api/trans/vip/translate?from=zh&to=en&appid=20210422000794040&salt=542716863&sign=${md5('20210422000794040' + place + '542716863' + 'HooD_ndgwcGH6SAnxGrM')}&q=${place}` : `http://api.fanyi.baidu.com/api/trans/vip/translate?from=en&to=zh&appid=20210422000794040&salt=542716863&sign=${md5('20210422000794040' + place + '542716863' + 'HooD_ndgwcGH6SAnxGrM')}&q=${place}`
await fetch(url)
.then(resp => resp.json())
.then(text => text.trans_result)
.then(res => this.reply(`${res[0].dst}`, true))
.catch((err) => logger.error(err))
return true
}
}

37
apps/update.js Normal file
View File

@ -0,0 +1,37 @@
// 主库
import Version from '../model/version.js'
import config from '../model/index.js'
import puppeteer from '../../../lib/puppeteer/puppeteer.js'
/**
* 处理插件更新
*/
export class update extends plugin {
constructor () {
super({
name: '更新插件',
dsc: '更新插件代码',
event: 'message',
priority: 4000,
rule: [
{
reg: '^#*R(插件)?版本$',
fnc: 'version'
}
]
})
this.versionData = config.getConfig('version')
}
/**
* rule - 插件版本信息
*/
async version () {
const data = await new Version(this.e).getData(
this.versionData.slice(0, 3)
)
let img = await puppeteer.screenshot('version', data)
this.e.reply(img)
}
}

2
config/cat.yaml Normal file
View File

@ -0,0 +1,2 @@
# 发送张数
count: 10

54
config/help.yaml Normal file
View File

@ -0,0 +1,54 @@
- group: 医学功能
list:
- icon: 记录
title: "#医药查询 疾病/症状/医院/医生/药品"
desc: 便利的医药查询功能
- group: 猫猫图
list:
- icon: 树脂
title: "#猫猫|cat 开始吸猫"
desc: 猫咪图捕捉
- group: 神秘功能合集
list:
- icon: 养成计算
title: "#雀食/#确实"
desc: 原汤化原食
- icon: 史莱姆
title: "#来份涩图"
desc: 涩涩的
- icon: weapon
title: "#累了"
desc: 疲惫往往是过度劳累
- icon: 攻略
title: "#啊?"
desc: 啊?
- icon: sign
title: "#沃日吗"
desc: 什么鬼?
- group: 热搜功能合集
list:
- icon: 打卡
title: "#热搜 虎扑/知乎/36氪/百度/哔哩哔哩/贴吧/微博/抖音/豆瓣/少数派/IT资讯/微信"
desc: 开局五子棋游戏
- group: 工具类合集
list:
- icon: 绑定账号
title: "#翻译 xxx"
desc: 百度翻译
- icon: 纠缠之缘
title: "#电脑软件推荐"
desc: 推荐PC软件
- icon: ledger
title: "#安卓软件推荐"
desc: 推荐安卓软件
- group: 其他指令[实验]
list:
- icon: 问号
title: "#RConsole插件版本"
desc: "查看,最近维护的版本信息"
- icon: 史莱姆
title: "#RConsole插件更新"
desc: "进行更新RConsole插件"
- icon: 原石
title: "#RConsole插件强制更新"
desc: "进行强制更新RConsole插件"

16
config/mystery.yaml Normal file
View File

@ -0,0 +1,16 @@
mystery:
# 最大页数
maxPage: 15
maxPigObj: 10
# 限制最大图片数量
imageCountLimit: 20
aaa:
# 最大页数
maxPage: 15
maxPigObj: 10
# 限制最大图片数量
imageCountLimit: 20
setu:
count: 10
tuiimg:
count: 10

10
config/version.yaml Normal file
View File

@ -0,0 +1,10 @@
- {
version: 1.0,
data:
[
更改单个组件<span class="cmd">#任助理</span>架构为插件架构,
添加<span class="cmd">#R帮助</span>获取插件帮助,
添加<span class="cmd">#R版本</span>获取插件版本,
更新风控缓解、重构系统文件
],
}

BIN
img/help.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

BIN
img/version.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

27
model/base.js Normal file
View File

@ -0,0 +1,27 @@
export default class base {
constructor (e = {}) {
this.e = e
this.userId = e?.user_id
this.model = 'RConsole-plugin'
this._path = process.cwd().replace(/\\/g, '/')
}
get prefix () {
return `Yz:RConsole-plugin:${this.model}:`
}
/**
* 截图默认数据
* @param saveId html保存id
* @param tplFile 模板html路径
* @param pluResPath 插件资源路径
*/
get screenData () {
return {
saveId: this.userId,
tplFile: `./plugins/RConsole-plugin/resources/html/${this.model}/${this.model}.html`,
/** 绝对路径 */
pluResPath: `${this._path}/plugins/RConsole-plugin/resources/`
}
}
}

42
model/help.js Normal file
View File

@ -0,0 +1,42 @@
import base from './base.js'
import config from './index.js'
import cfg from '../../../lib/config/config.js'
export default class Help extends base {
constructor (e) {
super(e)
this.model = 'help'
}
static async get (e) {
let html = new Help(e)
return await html.getData()
}
async getData () {
let helpData = config.getConfig('help')
let groupCfg = cfg.getGroup(this.group_id)
if (groupCfg.disable && groupCfg.disable.length) {
helpData.map((item) => {
if (groupCfg.disable.includes(item.group)) {
item.disable = true
}
return item
})
}
let versionData = config.getConfig('version')
const version =
(versionData && versionData.length && versionData[0].version) || '1.0.0'
return {
...this.screenData,
saveId: 'help',
version,
helpData
}
}
}

87
model/index.js Normal file
View File

@ -0,0 +1,87 @@
import fs from 'node:fs'
import _ from 'lodash'
import YAML from 'yaml'
import chokidar from 'chokidar'
class RConfig {
constructor () {
// 配置文件
this.configPath = './plugins/RConsole-plugin/config/'
this.config = {}
// 监听文件
this.watcher = { config: {} }
}
// 获取配置文件
getConfig (name) {
let ignore = []
if (ignore.includes(`${name}`)) {
return this.getYaml(name)
}
return this.getYaml(name)
}
/**
* 获取配置yaml
* @param app 功能
* @param name 名称
* @param type 默认跑配置-defSet用户配置-config
*/
getYaml (name) {
// 获取文件路径
let file = this.getFilePath(name)
// 解析xml
const yaml = YAML.parse(fs.readFileSync(file, 'utf8'))
// 监听文件
this.watch(file, name)
return yaml
}
/**
* 获取文件路径
* @param app
* @param name
* @param type
* @returns {string}
*/
getFilePath (name) {
return `${this.configPath}${name}.yaml`
}
/**
* 听配置文件
* @param file
* @param app
* @param name
* @param type
*/
watch (file, name) {
const watcher = chokidar.watch(file)
watcher.on('change', (path) => {
logger.mark(`[修改配置文件][${name}]`)
})
}
/**
* 保存配置
* @param app
* @param name
* @param type
* @param data
*/
saveSet (name, data) {
let file = this.getFilePath(name)
if (_.isEmpty(data)) {
fs.existsSync(file) && fs.unlinkSync(file)
} else {
let yaml = YAML.stringify(data)
fs.writeFileSync(file, yaml, 'utf8')
}
}
}
export default new RConfig()

22
model/version.js Normal file
View File

@ -0,0 +1,22 @@
import base from './base.js'
export default class Version extends base {
constructor (e) {
super(e)
this.model = 'version'
}
/** 生成版本信息图片 */
async getData (versionData) {
const version =
(versionData && versionData.length && versionData[0].version) || '1.0.0'
let data = {
...this.screenData,
userId: version,
quality: 100,
saveId: version,
versionData
}
return data
}
}

View File

@ -0,0 +1,140 @@
@font-face {
font-family: "tttgbnumber";
src: url("../../../../../resources/font/tttgbnumber.ttf");
font-weight: normal;
font-style: normal;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
}
body {
font-family: sans-serif;
font-size: 16px;
width: 788px;
color: #1e1f20;
transform: scale(1.5);
transform-origin: 0 0;
}
.container {
width: 788px;
padding: 20px 15px 10px 15px;
background-image: url(../../img/bg.jpeg);
background-size: 100%;
}
.head_box {
border-radius: 15px;
font-family: 'tttgbnumber';
padding: 10px 20px;
position: relative;
color: white;
box-shadow: 0 5px 10px 0 rgb(0 0 0 / 15%);
}
.head_box .id_text {
font-size: 24px;
}
.head_box .day_text {
font-size: 20px;
}
.head_box .genshin_logo {
position: absolute;
top: -10px;
right: 15px;
width: 97px;
}
.base_info {
position: relative;
padding-left: 10px;
}
.uid {
font-family: tttgbnumber;
}
.data_box {
border-radius: 15px;
margin-top: 20px;
margin-bottom: 15px;
padding: 20px 0px 5px 0px;
background: transparent;
box-shadow: 0 0 1px 0 #ccc, 2px 2px 4px 0 rgba(50, 50, 50, 0.8);
position: relative;
}
.tab_lable {
position: absolute;
top: -10px;
left: -8px;
background-color: rgb(76, 76, 76);
box-shadow: 0 0 1px 0 #ccc, 2px 2px 4px 0 rgba(50, 50, 50, 0.8);
color: #fff;
font-size: 14px;
padding: 3px 10px;
border-radius: 15px 0px 15px 15px;
z-index: 20;
}
.data_line {
display: flex;
justify-content: space-around;
margin-bottom: 14px;
}
.data_line_item {
width: 100px;
text-align: center;
/*margin: 0 20px;*/
}
.num {
font-family: tttgbnumber;
font-size: 24px;
}
.data_box .lable {
font-size: 14px;
color: #7f858a;
line-height: 1;
margin-top: 3px;
}
.list{
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
}
.list .item {
width: 235px;
display: flex;
align-items: center;
color: white;
background: transparent;
box-shadow: 0 0 1px 0 #ccc, 2px 2px 4px 0 rgba(50, 50, 50, 0.8);
padding: 8px 6px 8px 6px;
border-radius: 8px;
margin: 0 0px 10px 10px;
}
.list .item .icon{
width: 24px;
height: 24px;
background-repeat: no-repeat;
background-size: 100% 100%;
position: relative;
flex-shrink: 0;
}
.list .item .title{
font-size: 16px;
margin-left: 6px;
line-height: 20px;
}
/* .list .item .title .text{
white-space: nowrap;
} */
.list .item .title .dec{
font-size: 12px;
color: #999;
margin-top: 2px;
}
.logo{
text-align: center;
font-size: 14px;
color: white;
font-family: 'tttgbnumber';
}

View File

@ -0,0 +1,45 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<link
rel="stylesheet"
type="text/css"
href="{{pluResPath}}html/help/help.css"
/>
<link rel="preload" href="{{resPath}}font/tttgbnumber.ttf" as="font" />
<link rel="preload" href="{{pluResPath}}img/bg.jpeg" as="image" />
<link rel="preload" href="{{pluResPath}}img/rank/top.png" as="image" />
<link rel="shortcut icon" href="#" />
</head>
{{@headStyle}}
<body>
<div class="container" id="container">
<div class="head_box">
<div class="id_text">R-Plugin</div>
<h2 class="day_text">使用说明-v{{update}}</h2>
<img class="genshin_logo" src="{{pluResPath}}img/rank/top.png" />
</div>
{{each helpData val}}
<div class="data_box">
<div class="tab_lable">
{{val.group}}{{val.disable ? ' - 已禁用' : ''}}
</div>
<div class="list">
{{each val.list item}}
<div class="item">
<img class="icon" src="{{pluResPath}}img/icon/{{item.icon}}.png" />
<div class="title">
<div class="text">{{@item.title}}</div>
<div class="dec">{{item.desc}}</div>
</div>
</div>
{{/each}}
</div>
</div>
{{/each}}
<div class="logo">Created By Yunzai-Bot & R-Plugin</div>
</div>
</body>
</html>

View File

@ -0,0 +1,82 @@
@font-face {
font-family: "tttgbnumber";
src: url("../../../../../resources/font/tttgbnumber.ttf");
font-weight: normal;
font-style: normal;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
}
body {
font-size: 16px;
font-family: "tttgbnumber";
transform: scale(1.5);
transform-origin: 0 0;
color: white;
}
.container {
width: 536px;
background-image: url(../../img/bg.jpeg);
background-size: 100%;
padding: 10px 0 10px 0;
}
.version-card {
background: transparent;
margin: 5px 10px 8px 10px;
position: relative;
box-shadow: 0 0 1px 0 #ccc, 2px 2px 4px 0 rgba(50, 50, 50, 0.8);
overflow: hidden;
color: #fff;
font-size: 16px;
border-radius: 4px;
}
.version-card .title {
background: rgba(0, 0, 0, 0.4);
box-shadow: 0 0 1px 0 #fff;
color: white;
font-family: Number, YS;
padding: 10px 20px;
text-align: left;
font-size: 16px;
padding: 8px 20px 8px;
font-weight: bold;
}
.version-card .content {
padding: 10px 15px;
font-size: 12px;
background: rgba(0, 0, 0, 0.5);
box-shadow: 0 0 1px 0 #fff;
font-family: "HYWenHei-55W";
font-weight: normal;
}
.version-card ul {
font-size: 14px;
padding-left: 20px;
}
.version-card ul li {
margin: 3px 0;
}
.version-card .cmd {
color: #d3bc8e;
display: inline-block;
border-radius: 3px;
padding: 0 3px;
margin: 1px 2px;
}
.version-card .strong {
color: #67a9e4;
display: inline-block;
border-radius: 3px;
padding: 0 3px;
margin: 1px 2px;
}
.logo{
text-align: center;
font-size: 14px;
color: white;
font-family: 'tttgbnumber';
}

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<link rel="shortcut icon" href="#" />
<link
rel="stylesheet"
type="text/css"
href="{{pluResPath}}html/version/version.css"
/>
<link rel="preload" href="{{resPath}}font/tttgbnumber.ttf" as="font" />
<link rel="preload" href="{{pluResPath}}img/bg.jpeg" as="image" />
</head>
<body>
<div class="container" id="container">
{{each versionData item idx}}
<div class="version-card">
<div class="title">{{item.update}}{{idx ? '': ' - 当前版本'}}</div>
<div class="content">
<ul>
{{each item.data sub}}
<li>{{@sub}}</li>
{{/each}}
</ul>
</div>
</div>
{{/each}}
<div class="logo">Created By Yunzai-Bot & R-Plugin</div>
</div>
</body>
<script type="text/javascript"></script>
</html>

BIN
resources/img/bg.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 KiB

View File

BIN
resources/img/gobang/bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

0
resources/img/icon/.keep Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
resources/img/icon/role.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
resources/img/icon/sign.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
resources/img/icon/team.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

BIN
resources/img/icon/wiki.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

0
resources/img/mypk/.keep Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 546 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

0
resources/img/rank/.keep Normal file
View File

BIN
resources/img/rank/top.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

BIN
resources/img/rank/top0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
resources/img/rank/top1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
resources/img/rank/top2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

23974
test/get-pip.py Normal file

File diff suppressed because it is too large Load Diff

130
test/main.py Normal file
View File

@ -0,0 +1,130 @@
import asyncio
import re
import time
import aiohttp
import motor
from bs4 import BeautifulSoup
url = 'https://www.tuiimg.com/meinv/'
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.35",
'Pragma': 'no-cache',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
"Content-Type": "text/html;charset=utf-8"}
# 请求分类页面的并发限制(不要一次请求太多分类)
sem_page = asyncio.Semaphore(5)
async def fetch_content(url):
'''
根据url获取内容
:param url: 网页的url
:return:
'''
async with sem_page:
print(f'开始解析链接:{url}')
max_retries = 3
attempt = 0
while True:
try:
async with aiohttp.ClientSession(
headers=headers, connector=aiohttp.TCPConnector(ssl=False)
) as session:
async with session.get(url, timeout=10) as resp:
return await resp.text()
break
except (
asyncio.TimeoutError
):
if attempt < max_retries:
print("解析链接异常,稍后自动重试:{} times:{}".format(url, attempt))
attempt += 1
else:
raise
async def page_pic(connect, pic_page):
'''
处理一套图片分文件夹存放
:param connect: Mongodb连接
:param pic_page: 一套图片的url
:return:
'''
bs = BeautifulSoup(pic_page, 'lxml')
div = bs.find('div', {'class': "content"})
img_temp_link = div.find('img')['src']
img_base_link = img_temp_link[0:-5]
all_text = bs.find('i', id='allbtn').get_text()
pattern = re.compile("\((.*?)\)")
total = pattern.search(all_text).group(1).split("/")[1]
img_urls = []
for i in range(1, int(total) + 1):
img_url = img_base_link + str(i) + '.jpg'
img_urls.append(img_url)
task = [insert_url(connect, url_temp) for url_temp in img_urls]
await asyncio.gather(*task)
async def page_main(url):
'''
单页下载
:param url: 单页的url
:return: None
'''
connect = build_connect()
main_page_text = await fetch_content(url)
bs = BeautifulSoup(main_page_text, 'lxml')
a_all = bs.find_all('a', {'class': 'pic'})
page_urls = []
for a in a_all:
page_urls.append(a['href'])
tasks = [fetch_content(pic_url) for pic_url in page_urls]
pic_pages = await asyncio.gather(*tasks)
pics = [page_pic(connect, pic_page) for pic_page in pic_pages]
await asyncio.gather(*pics)
async def main():
'''
遍历所有页面的url进行下载
:return:
'''
# await target_folder()
start = time.time()
mainPageText = await fetch_content(url)
bs = BeautifulSoup(mainPageText, 'lxml')
page_count = bs.find('div', {'class', 'page'}).find('a', {'class', "end"}).get_text()
page_urls = []
for i in range(1, int(page_count) + 1):
page_url = f'{url}list_{i}.html'
page_urls.append(page_url)
tasks = [page_main(page_url) for page_url in page_urls]
await asyncio.gather(*tasks)
end = time.time()
print(f"耗时:{end - start:.2f}")
# 单例建立MongoDB连接
def build_connect():
client = motor.motor_tornado.MotorClient('localhost', 27017)
db = client.test
return db.temp
def insert_url(connect, url_temp):
query = {'url': url_temp}
return connect.update_one(query, {"$set": query}, True)
if __name__ == '__main__':
# linux下用此方法
# asyncio.run(main())
# windows下上面的方法会报错会在运行完成后报错不影响下载可以换成用下面这两行
loop = asyncio.get_event_loop()
loop.run_until_complete(main())

4
test/requirements.txt Normal file
View File

@ -0,0 +1,4 @@
aiohttp~=3.8.3
motor~=3.1.1
beautifulsoup4~=4.11.1
pymongo~=4.3.2