♻️ common.js

This commit is contained in:
yeyang
2024-03-24 23:01:58 +08:00
committed by 🌌
parent 6735bfe959
commit a0518cf95a
13 changed files with 355 additions and 26 deletions

View File

@@ -24,6 +24,7 @@ module.exports = {
'quote-props': ['error', 'consistent'],
'no-eval': ['error', { allowIndirect: true }],
'jsdoc/require-returns': 0,
'jsdoc/require-jsdoc': 0,
'jsdoc/require-param-description': 0,
'jsdoc/require-returns-description': 0,
'jsdoc/require-param-type': 0

View File

@@ -1,6 +1,7 @@
import { Bika, common, Pixiv } from '../model/index.js'
import { Config } from '../components/index.js'
import { Admin } from './admin.js'
import translateChinaNum from '../tools/translateChinaNum.js'
// 汉字数字匹配正则
const numReg = '[零一壹二两三四五六七八九十百千万亿\\d]+'
@@ -64,7 +65,7 @@ export class NewBika extends plugin {
if (!await this._Authentication(e)) return
e.reply(Pixiv.startMsg)
let regRet = e.msg.match(searchReg)
let page = common.translateChinaNum(regRet[5])
let page = translateChinaNum(regRet[5])
await Bika.search(regRet[3], page, regRet[2])
.then(res => common.recallSendForwardMsg(e, res))
.catch(err => common.handleException(e, err))
@@ -78,8 +79,8 @@ export class NewBika extends plugin {
if (!await this._Authentication(e)) return
e.reply(Pixiv.startMsg)
let regRet = e.msg.match(comicPageReg)
let page = common.translateChinaNum(regRet[4])
let order = common.translateChinaNum(regRet[6])
let page = translateChinaNum(regRet[4])
let order = translateChinaNum(regRet[6])
await Bika.comicPage(regRet[2], page, order)
.then(res => common.recallSendForwardMsg(e, res))
.catch(err => common.handleException(e, err))

View File

@@ -1,6 +1,7 @@
import { common } from '../../model/index.js'
import { Config } from '../../components/index.js'
import moment from 'moment'
import formatDuration from '../../tools/formatDuration.js'
Bot.on?.('notice.group', async (e) => {
let msg
@@ -142,7 +143,7 @@ Bot.on?.('notice.group', async (e) => {
}
// 禁言 (这里仅处理机器人被禁言)
case 'ban': {
const forbiddenTime = common.formatTime(e.duration, 'default')
const forbiddenTime = formatDuration(e.duration, 'default')
if (!Config.getGroup(e.group_id).botBeenBanned) return false

View File

@@ -1,9 +1,11 @@
import _ from 'lodash'
import moment from 'moment'
import { Config } from '../../components/index.js'
import { common, GroupAdmin as Ga, puppeteer, QQApi, GroupBannedWords } from '../../model/index.js'
import cronValidate from '../../tools/cronValidate.js'
import { Time_unit } from '../../constants/other.js'
import { GroupAdmin as Ga, GroupBannedWords, QQApi, common, puppeteer } from '../../model/index.js'
import cronValidate from '../../tools/cronValidate.js'
import translateChinaNum from '../../tools/translateChinaNum.js'
// API请求错误文案
const API_ERROR = '❎ 出错辣,请稍后重试'
// 正则
@@ -169,7 +171,7 @@ export class GroupAdmin extends plugin {
let qq = e.message.find(item => item.type == 'at')?.qq
let reg = `#禁言\\s?((\\d+)\\s)?(${Numreg})?(${TimeUnitReg})?`
let regRet = e.msg.match(new RegExp(reg))
const time = common.translateChinaNum(regRet[3])
const time = translateChinaNum(regRet[3])
new Ga(e).muteMember(
e.group_id, qq ?? regRet[2], e.user_id, time, regRet[4]
).then(res => e.reply(res))
@@ -221,7 +223,7 @@ export class GroupAdmin extends plugin {
// 解析正则
let regRet = Autisticreg.exec(e.msg)
// 获取数字
let TabooTime = common.translateChinaNum(regRet[2] || 5)
let TabooTime = translateChinaNum(regRet[2] || 5)
let Company = Time_unit[_.toUpper(regRet[3]) || '分']
@@ -407,7 +409,7 @@ export class GroupAdmin extends plugin {
if (!common.checkPermission(e, 'admin', 'admin')) { return true }
let regRet = noactivereg.exec(e.msg)
regRet[2] = common.translateChinaNum(regRet[2] || 1)
regRet[2] = translateChinaNum(regRet[2] || 1)
// 确认清理直接执行
if (regRet[1] == '确认清理') {
try {
@@ -423,7 +425,7 @@ export class GroupAdmin extends plugin {
}
}
// 查看和清理都会发送列表
let page = common.translateChinaNum(regRet[5] || 1)
let page = translateChinaNum(regRet[5] || 1)
let msg = null
try {
msg = await new Ga(e).getNoactiveInfo(
@@ -472,7 +474,7 @@ export class GroupAdmin extends plugin {
}
// 发送列表
let page = e.msg.match(new RegExp(Numreg))
page = page ? common.translateChinaNum(page[0]) : 1
page = page ? translateChinaNum(page[0]) : 1
new Ga(e).getNeverSpeakInfo(e.group_id, page)
.then(res => common.getforwardMsg(e, res, {
isxml: true,
@@ -484,7 +486,7 @@ export class GroupAdmin extends plugin {
// 查看不活跃排行榜和入群记录
async RankingList (e) {
let num = e.msg.match(new RegExp(Numreg))
num = num ? common.translateChinaNum(num[0]) : 10
num = num ? translateChinaNum(num[0]) : 10
let msg = ''
if (/(不活跃|潜水)/.test(e.msg)) {
msg = await new Ga(e).InactiveRanking(e.group_id, num)

View File

@@ -1,5 +1,6 @@
import { GroupAdmin as Ga, common } from '../../model/index.js'
import { Time_unit } from '../../constants/other.js'
import translateChinaNum from '../../tools/translateChinaNum.js'
// 正则
const Numreg = '[一壹二两三四五六七八九十百千万亿\\d]+'
const TimeUnitReg = Object.keys(Time_unit).join('|')
@@ -34,7 +35,7 @@ export class PrivateGroupAdmin extends plugin {
async muteMember (e) {
if (!common.checkPermission(e, 'master')) return
let regRet = e.msg.match(muteMemberReg)
const time = common.translateChinaNum(regRet[3])
const time = translateChinaNum(regRet[3])
let res = await new Ga(e).muteMember(regRet[1], regRet[2], e.user_id, time, regRet[4])
e.reply(res)
}

View File

@@ -3,6 +3,7 @@ import { Config } from '../components/index.js'
import { Pixiv, common, setu } from '../model/index.js'
import { Admin } from './admin.js'
import { ImageRPSS } from '../constants/pixiv.js'
import translateChinaNum from '../tools/translateChinaNum.js'
// 文案
const SWITCH_ERROR = '主人没有开放这个功能哦(*/ω\*)'
// 汉字数字匹配正则
@@ -106,7 +107,7 @@ export class NewPixiv extends plugin {
e.reply(Pixiv.startMsg)
let page = common.translateChinaNum(regRet[6])
let page = translateChinaNum(regRet[6])
await Pixiv.Rank(page, regRet[2], regRet[3], regRet[4])
.then(res => common.recallSendForwardMsg(e, res))
.catch(err => common.handleException(e, err))
@@ -123,7 +124,7 @@ export class NewPixiv extends plugin {
e.reply(Pixiv.startMsg)
let page = common.translateChinaNum(regRet[4])
let page = translateChinaNum(regRet[4])
await Pixiv[`${regRet[1] ? 's' : 'vilipixS'}earchTags`](regRet[2], page, !setu.getR18(e.group_id))
.then(res => common.recallSendForwardMsg(e, res))
.catch(err => common.handleException(e, err))
@@ -151,7 +152,7 @@ export class NewPixiv extends plugin {
e.reply(Pixiv.startMsg)
let regRet = uidReg.exec(e.msg)
let page = common.translateChinaNum(regRet[3])
let page = translateChinaNum(regRet[3])
await Pixiv.userIllust(regRet[1], page, !setu.getR18(e.group_id))
.then(res => common.recallSendForwardMsg(e, res))
@@ -169,7 +170,7 @@ export class NewPixiv extends plugin {
e.reply('你要的太多辣,奴家只给你一张辣(•́へ•́ ╬)')
num = 1
}
num = common.translateChinaNum(num)
num = translateChinaNum(num)
await Pixiv.vilipixRandomImg(num)
.then(res => common.recallSendForwardMsg(e, res))
.catch(err => common.handleException(e, err))
@@ -207,7 +208,7 @@ export class NewPixiv extends plugin {
e.reply(Pixiv.startMsg)
let regRet = e.msg.match(searchUser)
let page = common.translateChinaNum(regRet[3])
let page = translateChinaNum(regRet[3])
await Pixiv.searchUser(regRet[1], page, !setu.getR18(e.group_id))
.then(res => common.recallSendForwardMsg(e, res))
.catch(err => common.handleException(e, err))

View File

@@ -2,6 +2,7 @@ import plugin from '../../../lib/plugins/plugin.js'
import { Config } from '../components/index.js'
import { setu, common } from '../model/index.js'
import { Admin } from './admin.js'
import translateChinaNum from '../tools/translateChinaNum.js'
const NumReg = '[零一壹二两三四五六七八九十百千万亿\\d]+'
@@ -48,7 +49,7 @@ export class SeSe extends plugin {
if (cdTime) return e.reply(` ${setu.CDMsg}你的CD还有${cdTime}`, false, { at: true })
let num = e.msg.match(new RegExp(NumReg))
num = num ? common.translateChinaNum(num[0]) : 1
num = num ? translateChinaNum(num[0]) : 1
if (num > 20) {
return e.reply('❎ 最大张数不能大于20张')
} else if (num > 6) {
@@ -76,7 +77,7 @@ export class SeSe extends plugin {
num = 1
} else {
tag = tag.replace(num[0], '').trim()
num = common.translateChinaNum(num[1])
num = translateChinaNum(num[1])
}
if (num > 20) {
@@ -114,7 +115,7 @@ export class SeSe extends plugin {
// 设置群撤回间隔和cd
async setGroupRecallAndCD (e) {
let num = e.msg.match(new RegExp(NumReg))
num = common.translateChinaNum(num[0])
num = translateChinaNum(num[0])
let type = /撤回间隔/.test(e.msg)
setu.setGroupRecallTimeAndCd(e.group_id, num, type)
new Admin().SeSe_Settings(e)
@@ -132,7 +133,7 @@ export class SeSe extends plugin {
let reg = `^#?设置cd\\s?((\\d+)\\s)?(${NumReg})(s|秒)?$`
let regRet = e.msg.match(new RegExp(reg))
let qq = e.message.find(item => item.type == 'at')?.qq ?? regRet[2]
let cd = common.translateChinaNum(regRet[3])
let cd = translateChinaNum(regRet[3])
if (!qq) return e.reply('❎ 请输入要设置QQ', true)
if (!cd) return e.reply('❎ CD为空请检查', true)
setu.setUserCd(e, qq ?? regRet[2], cd)

View File

@@ -6,6 +6,7 @@ import plugin from '../../../lib/plugins/plugin.js'
import { Config, Version, Plugin_Name } from '../components/index.js'
import { status } from '../constants/other.js'
import { State, common, puppeteer } from '../model/index.js'
import formatDuration from '../tools/formatDuration.js'
const require = createRequire(import.meta.url)
let interval = false
@@ -122,7 +123,7 @@ export class NewState extends plugin {
const getBotState = async (botList) => {
const defaultAvatar = `../../../../../plugins/${Plugin_Name}/resources/state/img/default_avatar.jpg`
const BotName = Version.name
const systime = common.formatTime(os.uptime(), 'dd天hh小时mm分', false)
const systime = formatDuration(os.uptime(), 'dd天hh小时mm分', false)
const calendar = moment().format('YYYY-MM-DD HH:mm:ss')
const dataPromises = botList.map(async (i) => {
@@ -141,7 +142,7 @@ const getBotState = async (botList) => {
const friendQuantity = bot.fl?.size || 0
const groupQuantity = bot.gl?.size || 0
const groupMemberQuantity = Array.from(bot.gml?.values() || []).reduce((acc, curr) => acc + curr.size, 0)
const runTime = common.formatTime(Date.now() / 1000 - bot.stat?.start_time, 'dd天hh小时mm分', false)
const runTime = formatDuration(Date.now() / 1000 - bot.stat?.start_time, 'dd天hh小时mm分', false)
const botVersion = bot.version ? `${bot.version.name}(${bot.version.id})${bot.apk ? ` ${bot.version.version}` : ''}` : `ICQQ(QQ) v${require('icqq/package.json').version}`
return `<div class="box">

183
lib/common/sendMsgModule.js Normal file
View File

@@ -0,0 +1,183 @@
import common from '../../../../lib/common/common.js'
import Config from '../../components/Config.js'
import _ from 'lodash'
import setu from '../../model/setu.js'
export default class {
/**
* 给主人发消息
* @param msg
*/
async sendMasterMsg (msg) {
if (Config.whole.notificationsAll) {
// 发送全部管理
for (let index of Config.masterQQ) {
await common.relpyPrivate(index, msg)
await this.sleep(5000)
}
} else {
// 发给第一个管理
await common.relpyPrivate(Config.masterQQ[0], msg)
}
}
/**
* 发送转发消息
* @async
* @param {object} e - 发送消息的目标对象
* @param {Array<any[]>} message - 发送的消息数组,数组每一项为转发消息的一条消息
* @param {object} [options] - 发送消息的配置项
* @param {number} [options.recallMsg] - 撤回时间单位秒默认为0表示不撤回
* @param {{nickname: string, user_id: number}} [options.info] - 转发发送人信息 nickname-转发人昵称 user_id-转发人QQ
* @param {string | Array} [options.fkmsg] - 风控消息,不传则默认消息
* @param {boolean} [options.isxml] - 处理卡片
* @param {boolean} [options.xmlTitle] - XML 标题
* @param {boolean} [options.oneMsg] - 用于只有一条消息,不用再转成二维数组
* @param {boolean | import("icqq").Anonymous} [options.anony] - 匿名消息若为true则发送匿名消息
* @param {boolean} [options.shouldSendMsg] - 是否直接发送消息true为直接发送否则返回需要发送的消息
* @returns {Promise<import("icqq").MessageRet|import("icqq").XmlElem|import("icqq").JsonElem>} 消息发送结果的Promise对象
*/
async getforwardMsg (e, message, {
recallMsg = 0,
info,
fkmsg,
isxml,
xmlTitle,
oneMsg,
anony,
shouldSendMsg = true
} = {}) {
let forwardMsg = []
if (_.isEmpty(message)) throw Error('[Yenai-Plugin][sendforwardMsg][Error]发送的转发消息不能为空')
let add = (msg) => forwardMsg.push(
{
message: msg,
nickname: info?.nickname ?? (e.bot ?? Bot).nickname,
user_id: info?.user_id ?? (e.bot ?? Bot).uin
}
)
oneMsg ? add(message) : message.forEach(item => add(item))
// 发送
if (e.isGroup) {
forwardMsg = await e.group.makeForwardMsg(forwardMsg)
} else {
forwardMsg = await e.friend.makeForwardMsg(forwardMsg)
}
if (isxml && typeof (forwardMsg.data) !== 'object') {
// 处理转发卡片
forwardMsg.data = forwardMsg.data.replace('<?xml version="1.0" encoding="utf-8"?>", "<?xml version="1.0" encoding="utf-8" ?>')
}
if (xmlTitle) {
if (typeof (forwardMsg.data) === 'object') {
let detail = forwardMsg.data?.meta?.detail
if (detail) {
detail.news = [{ text: xmlTitle }]
}
} else {
forwardMsg.data = forwardMsg.data
.replace(/\n/g, '')
.replace(/<title color="#777777" size="26">(.+?)<\/title>/g, '___')
.replace(/___+/, `<title color="#777777" size="26">${xmlTitle}</title>`)
}
}
if (shouldSendMsg) {
let msgRes = await this.reply(e, forwardMsg, false, {
anony,
fkmsg,
recallMsg
})
return msgRes
} else {
return forwardMsg
}
}
/**
* 发送消息
* @async
* @param {*} e oicq 事件对象
* @param {Array | string} msg 消息内容
* @param {boolean} quote 是否引用回复
* @param {object} data 其他参数
* @param {number} data.recallMsg 撤回时间
* @param {boolean} data.fkmsg 风控消息
* @param {boolean | import("icqq").Anonymous} data.anony 匿名消息
* @param {boolean | number} data.at 是否艾特该成员
* @returns {Promise<import("icqq").MessageRet>} 返回发送消息后的结果对象
*/
async reply (e, msg, quote, {
recallMsg = 0,
fkmsg = '',
at = false,
anony
} = {}) {
if (at && e.isGroup) {
let text = ''
if (e?.sender?.card) {
text = _.truncate(e.sender.card, { length: 10 })
}
if (at === true) {
at = Number(e.user_id)
} else if (!isNaN(at)) {
let info = e.group.pickMember(at).info
text = info?.card ?? info?.nickname
text = _.truncate(text, { length: 10 })
}
if (Array.isArray(msg)) {
msg = [segment.at(at, text), ...msg]
} else {
msg = [segment.at(at, text), msg]
}
}
let msgRes = null
// 发送消息
if (e.isGroup) {
// 判断是否开启匿名
if (anony) {
let getAnonyInfo = await e.group.getAnonyInfo()
if (!getAnonyInfo.enable) {
e.reply('[警告]该群未开启匿名,请启用匿名再使用匿名功能')
anony = false
}
}
msgRes = await e.group.sendMsg(msg, quote ? e : undefined, anony)
} else {
msgRes = await e.reply(msg, quote)
if (!msgRes) await e.reply(fkmsg || '消息发送失败,可能被风控')
}
if (recallMsg > 0 && msgRes?.message_id) {
if (e.isGroup) {
setTimeout(() => e.group.recallMsg(msgRes.message_id), recallMsg * 1000)
} else if (e.friend) {
setTimeout(() => e.friend.recallMsg(msgRes.message_id), recallMsg * 1000)
}
}
return msgRes
}
/**
* 获取配置的撤回事件和匿名发送普通消息
* @param {*} e oicq
* @param {Array | string} msg 消息
* @param {boolean} quote 是否引用回复
* @param {object} data 其他参数
* @param {number} data.recallMsg 撤回时间
* @param {boolean} data.fkmsg 风控消息
* @param {boolean | import("icqq").Anonymous} data.anony 匿名消息
* @returns {Promise<import("icqq").MessageRet>}
*/
async recallsendMsg (e, msg, quote, data = {}) {
let recallMsg = setu.getRecallTime(e.group_id)
let anony = Config.getGroup(e.group_id).anonymous
let msgRes = this.reply(e, msg, quote, {
recallMsg,
anony,
...data
})
return msgRes
}
}

View File

@@ -33,7 +33,7 @@ export default new class {
/**
* 发送HTTP GET请求并返回响应
* @async
* @function
* @name get
* @param {string} url - 请求的URL
* @param {object} [options] - 请求的配置项
* @param {object} [options.params] - 请求的参数

View File

@@ -3,6 +3,7 @@ import { Data, Plugin_Path, Config } from '../components/index.js'
import _ from 'lodash'
import { setuMsg } from '../constants/msg.js'
import request from '../lib/request/request.js'
import formatDuration from '../tools/formatDuration.js'
export default new class setu {
constructor () {
this.root = `${Plugin_Path}/config/setu`
@@ -130,7 +131,7 @@ export default new class setu {
over = (this.temp[userId] - present)
}
if (over <= 0) return false
return common.formatTime(over, 'default', false)
return formatDuration(over, 'default', false)
}
/**

71
tools/formatDuration.js Normal file
View File

@@ -0,0 +1,71 @@
/**
* 格式化持续时间。
* @param {number} time - 要格式化的时间(以秒为单位)。
* @param {'default' | string | Function} format - 指定的格式,可以是字符串或函数。
* - 如果为字符串,可以包含占位符(例如"dd"表示天数,"hh"表示小时,"mm"表示分钟,"ss"表示秒),这些占位符将被实际的时间值替换。
* - 如果为函数,可以自定义处理时间对象并返回格式化结果。
* @param {boolean} [repair] - 修复小时、分钟和秒的显示格式的可选参数。如果设置为true并且小时、分钟或秒小于10则在值前面添加零。
* @returns {string | object} - 格式化后的持续时间。
* - 如果format为"default",返回一个字符串,表示格式化后的持续时间。字符串包含天数、小时、分钟和秒的信息,根据时间值的大小,只包含大于零的部分。
* - 如果format为字符串根据指定的格式进行替换后返回格式化后的字符串。
* - 如果format为函数将时间对象作为参数传递给该函数并返回函数处理后的结果。
* - 如果format既不是"default"、字符串,也不是函数,则返回包含天、小时、分钟和秒的时间对象。
* @example
* // 使用默认格式
* const result = formatDuration(3665, 'default');
* // 输出: "1小时1分5秒"
* @example
* // 使用自定义格式
* const result = formatDuration(3665, 'hh:mm:ss');
* // 输出: "01:01:05"
* @example
* // 使用自定义处理函数
* const customFormat = (time) => `${time.hour}h ${time.minute}m ${time.second}s`;
* const result = formatDuration(3665, customFormat);
* // 输出: "1h 1m 5s"
*/
export default function formatDuration (time, format, repair = true) {
const second = parseInt(time % 60)
const minute = parseInt((time / 60) % 60)
const hour = parseInt((time / (60 * 60)) % 24)
const day = parseInt(time / (24 * 60 * 60))
const timeObj = {
day,
hour: repair && hour < 10 ? `0${hour}` : hour,
minute: repair && minute < 10 ? `0${minute}` : minute,
second: repair && second < 10 ? `0${second}` : second
}
if (typeof format === 'function') {
return format(timeObj)
}
if (typeof format === 'string') {
format = format
.replace(/dd/g, day)
.replace(/hh/g, timeObj.hour)
.replace(/mm/g, timeObj.minute)
.replace(/ss/g, timeObj.second)
return format
}
if (format == 'default') {
let result = ''
if (day > 0) {
result += `${day}`
}
if (hour > 0) {
result += `${timeObj.hour}小时`
}
if (minute > 0) {
result += `${timeObj.minute}`
}
if (second > 0) {
result += `${timeObj.second}`
}
return result
}
return timeObj
}

View File

@@ -0,0 +1,65 @@
/**
* 使用JS将数字从汉字形式转化为阿拉伯形式
* @param {string} s_123
* @returns {number}
*/
export default function translateChinaNum (s_123) {
if (!s_123 && s_123 != 0) return s_123
// 如果是纯数字直接返回
if (/^\d+$/.test(s_123)) return Number(s_123)
// 字典
let map = new Map()
map.set('一', 1)
map.set('壹', 1) // 特殊
map.set('二', 2)
map.set('两', 2) // 特殊
map.set('三', 3)
map.set('四', 4)
map.set('五', 5)
map.set('六', 6)
map.set('七', 7)
map.set('八', 8)
map.set('九', 9)
// 按照亿、万为分割将字符串划分为三部分
let split = ''
split = s_123.split('亿')
let s_1_23 = split.length > 1 ? split : ['', s_123]
let s_23 = s_1_23[1]
let s_1 = s_1_23[0]
split = s_23.split('万')
let s_2_3 = split.length > 1 ? split : ['', s_23]
let s_2 = s_2_3[0]
let s_3 = s_2_3[1]
let arr = [s_1, s_2, s_3]
// ------------- 对各个部分处理 -------------
arr = arr.map(item => {
let result = ''
result = item.replace('零', '')
// [ "一百三十二", "四千五百", "三千二百一十三" ] ==>
let reg = new RegExp(`[${Array.from(map.keys()).join('')}]`, 'g')
result = result.replace(reg, substring => {
return map.get(substring)
})
// [ "1百3十2", "4千5百", "3千2百1十3" ] ==> ["0132", "4500", "3213"]
let temp
temp = /\d(?=千)/.exec(result)
let num1 = temp ? temp[0] : '0'
temp = /\d(?=百)/.exec(result)
let num2 = temp ? temp[0] : '0'
temp = /\d?(?=十)/.exec(result)
let num3
if (temp === null) { // 说明没十:一百零二
num3 = '0'
} else if (temp[0] === '') { // 说明十被简写了:十一
num3 = '1'
} else { // 正常情况:一百一十一
num3 = temp[0]
}
temp = /\d$/.exec(result)
let num4 = temp ? temp[0] : '0'
return num1 + num2 + num3 + num4
})
// 借助parseInt自动去零
return parseInt(arr.join(''))
}