Skip to content

Commit

Permalink
fix: 新增#铃状态
Browse files Browse the repository at this point in the history
  • Loading branch information
yusheng929 committed Aug 28, 2024
1 parent 194efd1 commit 7a91f39
Show file tree
Hide file tree
Showing 44 changed files with 3,077 additions and 0 deletions.
38 changes: 38 additions & 0 deletions apps/State.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Config, Render } from '#components'
import { logger, karin } from 'node-karin'
import { si } from '../models/State/utils.js'
import { getData, getMonitorData } from '../models/State/index.js'

let interval = false

export const State = karin.command(/^#()?(|status)(pro)?(debug)?$/i, async (e) => {
if (!//.test(e.msg) && !Config.state.default) return false

if (!si) return e.reply('没有检测到systeminformation依赖,请运行:"pnpm add systeminformation -w"进行安装')

// 防止多次触发
if (interval) { return false } else interval = true
try {
// 获取数据
const data = await getData(e)
// 渲染图片
const img = await Render.render('state/index', {
...data,
scale: 1.4
})
await e.reply(img)
} catch (error) {
logger.error(error)
interval = false
}
interval = false
}, { name: '铃状态', priority: '-1' })

export const Monitor = karin.command(/^#?(|monitor)$/i, async (e) => {
const data = await getMonitorData()
const img = await Render.render('state/monitor', data, {
e,
scale: 1.4
})
return e.reply(img)
}, { name: '监控', priority: '-1' })
4 changes: 4 additions & 0 deletions components/Config.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ class Config {
get Other () {
return this.getDefOrConfig('other')
}

get state () {
return this.getDefOrConfig('state')
}

/** 默认配置和用户配置 */
getDefOrConfig (name) {
Expand Down
67 changes: 67 additions & 0 deletions config/default_config/state.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# 是否设置为默认状态
default: false

# 测试访问的网址列表(可空)设置为true或default将使用默认配置
# show: 是否显示 true - 显示 - false - 不显示 pro - 只有pro显示
# name: 显示名称
# url: 要访问的网址
# timeout: 测试超时时间
# useProxy: 是否使用插件配置中的代理访问
psTestSites:
show: pro
list:
- name: Baidu
url: https://baidu.com
- name: Google
url: https://google.com
- name: Google(proxy)
url: https://google.com
useProxy: true
timeout: 5000

# 监控任务
statusTask: true

# 如果出现内存异常的情况可将此配置项开启,如果打开后报错请将监控任务关闭
statusPowerShellStart: false

# 背景图片api,设置为false则一直使用默认背景
# CF跳板(会损失一点访问时间,但后期API地址可以自动更新)
# http://dir.dengfenglai.icu
# 默认地址(直连,但是不确定是否会GG)
# https://t.mwm.moe/mp/
backdrop: "http://dir.dengfenglai.icu"

# 当api请求失败时使用的默认背景图,请放置在resources/state/img/bg目录下
# random - 随机选择state/img/bg目录里的一张图片
# default_bg.jpg 可直接指定bg目录里一张图片,注意请带上后缀名
backdropDefault: "random"

# 关闭node的那个圈圈
closedNodeInfo: true

# 关闭图表
closedChart: false

# 是否显示FastFetch 参数:true, false, pro ,default
# true - 开启
# false - 关闭
# pro - 只有状态pro显示
# default - win默认只有pro显示,liunx等其他正常也会显示
showFastFetch: default

# Bot昵称的颜色
BotNameColor: "#000"
# Bot昵称渐变色 设置了该值BotNameColor值将无效 如要使用BotNameColor请将该值设为none或false
# 该值请参考https://developer.mozilla.org/zh-CN/docs/Web/CSS/gradient/linear-gradient
# xxdeg 为渐变角度 后为渐变颜色
# 原来花里胡哨的渐变271.14deg,#001bff 0.98%,#00f0ff 25.79%,#fce84a 47.33%,#f34628 65.77%,#b275ff 91.4%
BotNameColorGradient: "none"

#主硬件进度条和磁盘进度条的颜色
#严重
highColor: "#d73403"
#警告
mediumColor: "#ffa500"
#正常
lowColor: "#21E488"
121 changes: 121 additions & 0 deletions models/State/BotState.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import moment from 'moment'
import { Version } from '#components'
import { Bot, redis } from 'node-karin'
import { getImgPalette } from './utils.js'

export default async function getBotState (e) {
const botList = _getBotList(e)
const dataPromises = botList.map(async (i) => {
const bot = Bot.getBot(i)
const uin = bot?.account?.uin || bot?.uin
if (!uin) return false

const { status = 11, version } = bot

const nickname = bot.nickname || bot.account.name || '未知'

// 头像
const avatarUrl = bot.avatar ?? (Number(uin) ? `https://q1.qlogo.cn/g?b=qq&s=0&nk=${uin}` : 'default')
const avatar = await getAvatarColor(avatarUrl)

const platform = version?.version ?? '未知'

const messageCount = await getMessageCount(bot)

const countContacts = await getCountContacts(bot)

let startTime = bot?.stat?.start_time || bot.adapter?.start_time || Math.floor(Date.now() / 1000)
if (startTime > 999999999) {
startTime = Math.floor(startTime / 1000)
}
const botRunTime = formatDuration(Math.floor(Date.now() / 1000) - startTime)
const botVersion = `${version.name} v${version?.version || ''}`

return {
avatar,
nickname,
botRunTime,
status,
platform,
botVersion,
messageCount,
countContacts
}
})

return Promise.all(dataPromises).then(r => r.filter(Boolean))
}

async function getAvatarColor (url) {
const defaultAvatar = `${Version.pluginPath}/resources/state/img/default_avatar.jpg`
try {
if (url == 'default') {
url = defaultAvatar
}
const avatar = await getImgPalette(url)
return avatar
} catch {
return {
similarColor1: '#fff1eb',
similarColor2: '#ace0f9',
path: url
}
}
}
async function getMessageCount (bot) {
const today = moment().format('YYYY-MM-DD')
const keys = (() => {
return [
`karin:count:send:${today}`,
`karin:count:recv:${today}`
]
})()

// const values = await redis.mGet(keys) || []
const values = []
for (const i of keys) {
values.push(await redis.get(i))
}

const sent = values[0] || bot.stat?.sent_msg_cnt || 0
const recv = values[1] || bot.stat?.recv_msg_cnt || 0
const screenshot = values[2] || values[3] || 0

return {
sent: sent > 0 ? '发送: ' + sent : 0,
recv: recv > 0 ? '接收: ' + recv : 0,
screenshot
}
}

async function getCountContacts (bot) {
const friend = (await bot.GetFriendList?.())?.length || bot.fl?.size || 0
const group = (await bot.GetGroupList?.())?.length || bot.gl?.size || 0
const groupMember = Array.from(bot?.gml?.values() || []).reduce((acc, curr) => acc + curr.size, 0)
return {
friend: friend > 0 ? '好友: ' + friend : 0,
group: group > 0 ? '群: ' + group : 0,
groupMember
}
}

function _getBotList (e) {
/** bot列表 */
let BotList = [e.self_id]

// TODO: 多账号支持
if (e.isPro) {
BotList = Bot.getBotAll().map(i => i.account.uin)
}
return BotList
}

function formatDuration (seconds) {
const duration = moment.duration(seconds, 'seconds')
const days = Math.floor(duration.asDays())
const hours = duration.hours()
const minutes = duration.minutes()
const secs = duration.seconds()

return `${days}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`
}
24 changes: 24 additions & 0 deletions models/State/CPU.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { si, Circle } from './utils.js'

/** 获取CPU占用 */
export default async function getCpuInfo () {
let { currentLoad: { currentLoad }, cpu, fullLoad } = await si.get({
currentLoad: 'currentLoad',
cpu: 'manufacturer,speed,cores',
fullLoad: '*'
})
let { manufacturer, speed, cores } = cpu
if (currentLoad == null || currentLoad == undefined) return false
fullLoad = Math.round(fullLoad)
manufacturer = manufacturer?.split(' ')?.[0] ?? 'unknown'
return {
...Circle(currentLoad / 100),
inner: Math.round(currentLoad) + '%',
title: 'CPU',
info: [
`${manufacturer} ${cores}${speed}GHz`,
`CPU满载率 ${fullLoad}%`
]

}
}
35 changes: 35 additions & 0 deletions models/State/FastFetch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Config, Version } from '#components'
import child_process from 'child_process'
import { logger } from 'node-karin'
/**
* 获取FastFetch
* @param e
*/
export default async function getFastFetch (e) {
if (!isFeatureVisible(e.isPro)) return ''
const ret = await execSync(`bash plugins/${Version.pluginName}/resources/state/state.sh`)
if (ret.error) {
logger.error(`[${Version.pluginName}][状态]Error FastFetch 请检查是否使用git bash启动${Version.BotName},错误信息:${ret.stderr || ret.stdout}`)
return ''
}
return ret.stdout.trim()
}
function isFeatureVisible (isPro) {
const { showFastFetch } = Config.state
if (showFastFetch === true) return true
if (showFastFetch === 'pro' && isPro) return true
if (showFastFetch === 'default') {
if (!isPlatformWin() || isPro) return true
}
return false
}
function isPlatformWin () {
return process.platform === 'win32'
}
async function execSync (cmd) {
return new Promise((resolve, reject) => {
child_process.exec(cmd, (error, stdout, stderr) => {
resolve({ error, stdout, stderr })
})
})
}
44 changes: 44 additions & 0 deletions models/State/FsSize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import _ from 'lodash'
import { getFileSize, si } from './utils.js'
import Monitor from './Monitor.js'

/**
* 获取硬盘
*/
export async function getFsSize () {
// 去重
const HardDisk = _.uniqWith(await si.fsSize(),
(a, b) =>
a.used === b.used && a.size === b.size && a.use === b.use && a.available === b.available
)
.filter(item => item.size && item.used && item.available && item.use)
// 为空返回false
if (_.isEmpty(HardDisk)) return false
// 数值转换
return HardDisk.map(item => {
item.used = getFileSize(item.used)
item.size = getFileSize(item.size)
item.use = Math.round(item.use)
item.color = 'var(--low-color)'
if (item.use >= 90) {
item.color = 'var(--high-color)'
} else if (item.use >= 70) {
item.color = 'var(--medium-color)'
}
return item
})
}
/**
* 获取磁盘读写速度
* @returns {object | boolean} 返回一个对象,包含读速度(rx_sec)和写速度(wx_sec),如果无法获取则返回false。
*/
export function getDiskSpeed () {
const fsStats = Monitor.fsStats
if (!fsStats || fsStats.rx_sec == null || fsStats.wx_sec == null) {
return false
}
return {
rx_sec: getFileSize(fsStats.rx_sec, { showByte: false, showSuffix: false }),
wx_sec: getFileSize(fsStats.wx_sec, { showByte: false, showSuffix: false })
}
}
49 changes: 49 additions & 0 deletions models/State/GPU.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Circle, si, initDependence } from './utils.js'
import { logger } from 'node-karin'
import { Version } from '#components'

let isGPU = false;

(async function initGetIsGPU () {
if (!await initDependence()) return
const { controllers } = await si.graphics()
// 初始化GPU获取
if (controllers?.find(item =>
item.memoryUsed && item.memoryFree && item.utilizationGpu)
) {
isGPU = true
}
})()

/** 获取GPU占用 */
export default async function getGPU () {
if (!isGPU) return false
try {
const { controllers } = await si.graphics()
const graphics = controllers?.find(item =>
item.memoryUsed && item.memoryFree && item.utilizationGpu
)
if (!graphics) {
logger.warn(`[${Version.pluginName}][state]状态GPU数据异常:\n`, controllers)
return false
}
let {
vendor, temperatureGpu, utilizationGpu,
memoryTotal, memoryUsed /* powerDraw */
} = graphics
temperatureGpu && (temperatureGpu = temperatureGpu + '℃')
// powerDraw && (powerDraw = powerDraw + "W")
return {
...Circle(utilizationGpu / 100),
inner: Math.round(utilizationGpu) + '%',
title: 'GPU',
info: [
`${(memoryUsed / 1024).toFixed(2)} GB / ${(memoryTotal / 1024).toFixed(2)} GB`,
`${vendor} ${temperatureGpu}`
]
}
} catch (e) {
logger.warn(`[${Version.pluginName}][State] 获取GPU失败`)
return false
}
}
Loading

0 comments on commit 7a91f39

Please sign in to comment.