diff --git a/liteyuki/liteyuki_main/core.py b/liteyuki/liteyuki_main/core.py index 2c6b4e55..0e41f7ab 100644 --- a/liteyuki/liteyuki_main/core.py +++ b/liteyuki/liteyuki_main/core.py @@ -108,7 +108,7 @@ async def _(bot: T_Bot, event: T_MessageEvent): nonebot.logger.error(f"Pull from {origin} failed: {e}") reply = "Liteyuki updated!\n" reply += f"```\n{logs}\n```\n" - btn_restart = md.button(ulang.get("liteyuki.restart_now"), "reload-liteyuki") + btn_restart = md.cmd(ulang.get("liteyuki.restart_now"), "reload-liteyuki") pip.main(["install", "-r", "requirements.txt"]) reply += f"{ulang.get('liteyuki.update_restart', RESTART=btn_restart)}" await md.send_md(reply, bot, event=event, at_sender=False) diff --git a/liteyuki/liteyuki_main/runtime.py b/liteyuki/liteyuki_main/runtime.py index fdbcef3f..5b036186 100644 --- a/liteyuki/liteyuki_main/runtime.py +++ b/liteyuki/liteyuki_main/runtime.py @@ -7,7 +7,7 @@ from cpuinfo import get_cpu_info from nonebot import on_command from nonebot.adapters.onebot.v11 import MessageSegment from nonebot.permission import SUPERUSER - +from playwright.async_api import async_playwright from liteyuki.utils import __NAME__, __VERSION__, load_from_yaml from liteyuki.utils.htmlrender import template2image from liteyuki.utils.language import Language, get_default_lang, get_user_lang @@ -29,6 +29,8 @@ protocol_names = { } + + @stats.handle() async def _(bot: T_Bot, event: T_MessageEvent): ulang = get_user_lang(str(event.user_id)) @@ -36,7 +38,7 @@ async def _(bot: T_Bot, event: T_MessageEvent): get_path("templates/stats.html", abs_path=True), { "data": await get_stats_data(bot.self_id, ulang.lang_code) - }, + }, debug=True ) await stats.finish(MessageSegment.image(image)) diff --git a/liteyuki/plugins/liteyuki_minigame/game.py b/liteyuki/plugins/liteyuki_minigame/game.py index d193b081..c53c412c 100644 --- a/liteyuki/plugins/liteyuki_minigame/game.py +++ b/liteyuki/plugins/liteyuki_minigame/game.py @@ -157,14 +157,14 @@ class Minesweeper: print([d.value for d in row]) for dot in row: if dot.mask and not dot.flagged: - text += md.button(self.MASK, f"minesweeper reveal {dot.row} {dot.col}") + text += md.cmd(self.MASK, f"minesweeper reveal {dot.row} {dot.col}") elif dot.flagged: - text += md.button(self.FLAG, f"minesweeper mark {dot.row} {dot.col}") + text += md.cmd(self.FLAG, f"minesweeper mark {dot.row} {dot.col}") else: text += self.NUMS[dot.value] text += dis text += "\n" - btn_mark = md.button("标记", f"minesweeper mark ", enter=False) - btn_end = md.button("结束", "minesweeper end", enter=True) + btn_mark = md.cmd("标记", f"minesweeper mark ", enter=False) + btn_end = md.cmd("结束", "minesweeper end", enter=True) text += f" {btn_mark} {btn_end}" return text diff --git a/liteyuki/plugins/liteyuki_npm/installer.py b/liteyuki/plugins/liteyuki_npm/installer.py index 2740c6b5..a0e65556 100644 --- a/liteyuki/plugins/liteyuki_npm/installer.py +++ b/liteyuki/plugins/liteyuki_npm/installer.py @@ -71,7 +71,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot): if len(rs): reply = f"{ulang.get('npm.search_result')} | {ulang.get('npm.total', TOTAL=len(rs))}\n***" for plugin in rs[:min(max_show, len(rs))]: - btn_install = md.button(ulang.get("npm.install"), "npm install %s" % plugin.module_name) + btn_install = md.cmd(ulang.get("npm.install"), "npm install %s" % plugin.module_name) link_page = md.link(ulang.get("npm.homepage"), plugin.homepage) link_pypi = md.link(ulang.get("npm.pypi"), plugin.homepage) @@ -96,7 +96,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot): if not store_plugin: await npm_alc.finish(ulang.get("npm.plugin_not_found", NAME=plugin_module_name)) - homepage_btn = md.button(ulang.get("npm.homepage"), store_plugin.homepage) + homepage_btn = md.cmd(ulang.get("npm.homepage"), store_plugin.homepage) if r: r_load = nonebot.load_plugin(plugin_module_name) # 加载插件 diff --git a/liteyuki/plugins/liteyuki_npm/manager.py b/liteyuki/plugins/liteyuki_npm/manager.py index 7e2bf576..c3a998ba 100644 --- a/liteyuki/plugins/liteyuki_npm/manager.py +++ b/liteyuki/plugins/liteyuki_npm/manager.py @@ -67,7 +67,7 @@ async def _(event: T_MessageEvent, bot: T_Bot, result: Arparma): for plugin in loaded_plugin_list[(page - 1) * num_per_page: min(page * num_per_page, len(loaded_plugin_list))]: # 检查是否有 metadata 属性 # 添加帮助按钮 - btn_usage = md.button(lang.get("npm.usage"), f"help {plugin.module_name}", False) + btn_usage = md.cmd(lang.get("npm.usage"), f"help {plugin.module_name}", False) store_plugin = await get_store_plugin(plugin.module_name) session_enable = get_plugin_session_enable(event, plugin.module_name) @@ -98,7 +98,7 @@ async def _(event: T_MessageEvent, bot: T_Bot, result: Arparma): cmd_toggle = f"{'disable' if session_enable else 'enable'} {plugin.module_name}" text_toggle = lang.get("npm.disable" if session_enable else "npm.enable") can_be_toggle = get_plugin_can_be_toggle(plugin.module_name) - btn_toggle = text_toggle if not can_be_toggle else md.button(text_toggle, cmd_toggle) + btn_toggle = text_toggle if not can_be_toggle else md.cmd(text_toggle, cmd_toggle) reply += f" {btn_toggle}" @@ -107,12 +107,12 @@ async def _(event: T_MessageEvent, bot: T_Bot, result: Arparma): # 添加移除插件和全局切换按钮 global_enable = get_plugin_global_enable(plugin.module_name) btn_uninstall = ( - md.button(lang.get("npm.uninstall"), f'npm uninstall {plugin.module_name}')) if plugin_in_database else lang.get( + md.cmd(lang.get("npm.uninstall"), f'npm uninstall {plugin.module_name}')) if plugin_in_database else lang.get( 'npm.uninstall') btn_toggle_global_text = lang.get("npm.disable_global" if global_enable else "npm.enable_global") cmd_toggle_global = f"{'disable-global' if global_enable else 'enable-global'} {plugin.module_name}" - btn_toggle_global = btn_toggle_global_text if not can_be_toggle else md.button(btn_toggle_global_text, cmd_toggle_global) + btn_toggle_global = btn_toggle_global_text if not can_be_toggle else md.cmd(btn_toggle_global_text, cmd_toggle_global) reply += f" {btn_uninstall} {btn_toggle_global}" diff --git a/liteyuki/plugins/liteyuki_user/profile_manager.py b/liteyuki/plugins/liteyuki_user/profile_manager.py index ecc40456..2b6795aa 100644 --- a/liteyuki/plugins/liteyuki_user/profile_manager.py +++ b/liteyuki/plugins/liteyuki_user/profile_manager.py @@ -90,8 +90,8 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot): continue val = profile.dict()[key] key_text = ulang.get(f"user.profile.{key}") - btn_set = md.button(ulang.get("user.profile.edit"), f"profile set {key}", - enter=True if key in enter_attr else False) + btn_set = md.cmd(ulang.get("user.profile.edit"), f"profile set {key}", + enter=True if key in enter_attr else False) reply += (f"\n**{key_text}** **{val}**\n" f"\n> {ulang.get(f'user.profile.{key}.desc')}" f"\n> {btn_set} \n\n***\n") @@ -117,11 +117,11 @@ def get_profile_menu(key: str, ulang: Language) -> Optional[str]: reply = f"**{setting_name} {ulang.get('user.profile.settings')}**\n***\n" if key == "lang": for lang_code, lang_name in get_all_lang().items(): - btn_set = md.button(ulang.get("user.profile.set"), f"profile set {key} {lang_code}") + btn_set = md.cmd(ulang.get("user.profile.set"), f"profile set {key} {lang_code}") reply += f"\n{btn_set} | **{lang_name}** - {lang_code}\n***\n" elif key == "timezone": for tz in representative_timezones_list: - btn_set_tz = md.button(tz, f"profile set {key} {tz}") + btn_set_tz = md.cmd(tz, f"profile set {key} {tz}") reply += f"{btn_set_tz}\n" return reply diff --git a/liteyuki/resources/templates/fonts/MiSans/MiSans-Medium.woff2 b/liteyuki/resources/templates/fonts/MiSans/MiSans-Medium.woff2 deleted file mode 100644 index 993d1f6f..00000000 Binary files a/liteyuki/resources/templates/fonts/MiSans/MiSans-Medium.woff2 and /dev/null differ diff --git a/liteyuki/resources/templates/fonts/MiSans/MiSans-Regular.woff2 b/liteyuki/resources/templates/fonts/MiSans/MiSans-Regular.woff2 deleted file mode 100644 index 7544be21..00000000 Binary files a/liteyuki/resources/templates/fonts/MiSans/MiSans-Regular.woff2 and /dev/null differ diff --git a/liteyuki/resources/templates/fonts/MiSans/MiSans-Thin.woff2 b/liteyuki/resources/templates/fonts/MiSans/MiSans-Thin.woff2 deleted file mode 100644 index 0ef3e88e..00000000 Binary files a/liteyuki/resources/templates/fonts/MiSans/MiSans-Thin.woff2 and /dev/null differ diff --git a/liteyuki/resources/templates/js/motto.js b/liteyuki/resources/templates/js/motto.js new file mode 100644 index 00000000..f349a9c1 --- /dev/null +++ b/liteyuki/resources/templates/js/motto.js @@ -0,0 +1,63 @@ +// 存放格言 +const mottos = [ + { + "text": "人生自古谁无死,留取丹心照汗青。", + "author": "文天祥", + "source": "《正气歌》" + }, + { + "text": "同是天涯沦落人,相逢何必曾相识。", + "author": "白居易", + "source": "《琵琶行》" + }, + { + "text": "海Memory知己,天涯若比邻。", + "author": "王勃", + "source": "《送杜少府之任蜀州》" + }, + { + "text": "人生到处知何似,应似飞鸿踏雪泥。", + "author": "苏轼", + "source": "《水调歌头》" + }, + { + "text": "大鹏一日同风起,扶摇直上九万里。", + "author": "李白", + "source": "《将进酒》" + }, + { + "text": "银烛秋光冷画屏,轻罗小扇扑流萤。", + "author": "陆游", + "source": "《秋夕》" + }, + { + "text": "明月几时有,把酒问青天。", + "author": "辛弃疾", + "source": "《水龙吟》" + }, + { + "text": "逸一时,误一世,逸久逸久罢已龄", + "author": "田所浩二", + "source": "《仲夏夜之淫梦》" + }, + { + "text": "你知道吗,轻雪只能在Python3.10以上的版本中使用。", + "author": "SnowyKami", + "source": "轻雪文档" + }, + { + "text": "你知道吗,轻雪可以通过自定义资源包来扩展主题和语言", + "author": "SnowyKami", + "source": "轻雪文档" + }, + { + "text": "你知道吗,轻雪交流群的群号是 775840726", + "author": "SnowyKami", + "source": "轻雪文档" + }, + { + "text": "你知道吗,轻雪运行过程中会启动一个node.js运行环境", + "author": "SnowyKami", + "source": "轻雪文档" + }, +] \ No newline at end of file diff --git a/liteyuki/resources/templates/js/stats.js b/liteyuki/resources/templates/js/stats.js new file mode 100644 index 00000000..d8e9f0ef --- /dev/null +++ b/liteyuki/resources/templates/js/stats.js @@ -0,0 +1,244 @@ +{ + // 环形图 + + let bgs = ["bg1.jpg", "bg2.jpg", "bg3.jpg", "bg4.jpg"] + // 随机选择背景图片 + document.body.style.backgroundImage = `url(./img/${bgs[Math.floor(Math.random() * bgs.length)]})`; + + + let cpuInfo = echarts.init(document.getElementById('cpu-chart')); + let memInfo = echarts.init(document.getElementById('mem-chart')); + let swapInfo = echarts.init(document.getElementById('swap-chart')); + + let data = JSON.parse(document.getElementById('data').innerText); + + let cpuData = data.cpu; + let memData = data.mem; + let swapData = data.swap; + let diskData = data.disk; + let sub_tag_data = { + cpu: data.cpuTags, + mem: data.memTags, + swap: data.swapTags + } + for (let key in sub_tag_data) { + let infoDiv = document.getElementById(key + '-info'); + sub_tag_data[key].forEach(tag => { + let tagSpan = document.createElement('div'); + tagSpan.innerText = tag; + tagSpan.className = 'chart-label'; + infoDiv.appendChild(tagSpan); + }); + } + cpuInfo.setOption(getPieOption(data.cpu_trans, cpuData)); + memInfo.setOption(getPieOption(data.mem_trans, memData)); + swapInfo.setOption(getPieOption(data.swap_trans, swapData)); + + + // 在disks-info中插入每个disk的div,用横向柱状图表示用量,每一行div显示一个disk,不加info-box + diskData.forEach(disk => { + let diskDiv = document.createElement('div'); + document.getElementById('disks-info').appendChild(diskDiv); + let diskChart = document.createElement('div'); + diskChart.style.width = '100%'; + diskChart.style.height = '100px'; + diskDiv.appendChild(diskChart); + let diskInfo = echarts.init(diskChart); + // let diskTitle = disk.name + ' {{ FREE }} ' + disk.free + ' {{ TOTAL }} ' + disk.total; + let diskTitle = `${disk.name} ${data.free_trans} ${disk.free} ${data.total_trans} ${disk.total}`; + diskInfo.setOption(getBarOption(diskTitle, disk.percent)); + }); + + let botData = data.bot; + // 清空bot-info + let botInfos = document.getElementsByClassName('bot-info'); + while (botInfos.length > 0) { + botInfos[0].remove(); + } + botData.forEach(bot => { + // 在hardware-info前面插入一个div + let botDiv = document.createElement('div'); + botDiv.className = 'info-box bot-info'; + // 在body内的hardware-info前面插入botDiv + document.body.insertBefore(botDiv, document.getElementById('hardware-info')); + + let botIconBlock = document.createElement('div'); + let botIcon = document.createElement('img'); + botIcon.src = bot.icon; + botIcon.className = 'bot-icon'; + botIconBlock.appendChild(botIcon); + botDiv.appendChild(botIconBlock); + + let botDetail = document.createElement('div'); + let botName = document.createElement('div'); + botName.className = 'bot-name'; + botName.innerText = bot.name; + if (bot.self) { + // 添加颜色 + botName.style.color = '#d0e9ff'; + } + botDetail.appendChild(botName); + + let botTags = document.createElement('div'); + botTags.className = 'bot-tag'; + botDetail.appendChild(botTags) + + bot.tags.forEach((tag, index) => { + if (!tag) { + return; + } + let tagSpan = document.createElement('span'); + + tagSpan.innerText = tag; + tagSpan.className = 'tag'; + if (bot.self) { + // 添加颜色 + tagSpan.style.color = '#a2d8f4'; + } + botTags.appendChild(tagSpan); + if (index === bot.tags.length - 1) { + tagSpan.setAttribute("suffix", "0") + } else { + tagSpan.setAttribute("suffix", "1") + } + }); + + botDiv.appendChild(botDetail); + } + ) + + // 从/js/motto.js中读取mottos{},随机选择一句 + let motto = mottos[Math.floor(Math.random() * mottos.length)]; + // 正文在中间,作者和来源格式为--作者 来源,在右下方 + let mottoDiv = document.getElementById('motto-info'); + let mottoText = document.createElement('div'); + mottoText.className = 'motto-text'; + mottoText.innerText = motto.text; + mottoDiv.appendChild(mottoText); + let mottoAuthor = document.createElement('div'); + mottoAuthor.className = 'motto-author'; + // motto.author和motto.source可能不存在为空,所以要判断 + if (!motto.author) { + motto.author = ''; + } + if (!motto.source) { + motto.source = ''; + } + mottoAuthor.innerText = `\n--${motto.author} ${motto.source}`; + mottoAuthor.style.textAlign = 'right'; + mottoDiv.appendChild(mottoAuthor); + + + function getPieOption(title, data) { + return { + animation: false, + title: { + text: title, + left: 'center', + top: 'center', + textStyle: { + //文字颜色 + color: '#fff', + fontSize: 30 + } + }, + tooltip: { + show: true, + trigger: "item", + backgroundColor: "#ffffff00", + // {a}(系列名称),{b}(数据项名称),{c}(数值), {d}(百分比) + }, + color: ['#a2d8f4', "#ffffff44", '#00a6ff'], + series: [ + { + name: 'info', + type: 'pie', + radius: ['80%', '100%'], + center: ['50%', '50%'], + itemStyle: { + normal: { + label: { + show: false + }, + labelLine: { + show: false + } + }, + emphasis: { + label: { + show: true, + textStyle: { + fontSize: '50', + fontWeight: 'bold' + } + } + } + }, + data: data + } + ] + }; + } + + function getBarOption(title, percent) { + // data为百分比,最大值为100 + return { + background: '#d0e9ff', + title: { + text: title, + left: '5%', + top: 'center', + textStyle: { + color: '#fff', + fontSize: 30 + } + }, + tooltip: { + show: true, + trigger: "item", + backgroundColor: "#ffffff", + }, + grid: { + left: '0', + right: '0', + top: '10%', + bottom: '10%' + }, + xAxis: { + type: 'value', + show: false + }, + yAxis: { + type: 'category', + data: [''], + show: false + }, + series: [ + { + name: 'Used', + type: 'bar', + stack: 'total', + data: [percent], + itemStyle: { + normal: { + color: '#a2d8f4', + barBorderRadius: [50, 0, 0, 50] + } + }, + }, + { + name: 'Free', + type: 'bar', + stack: 'total', + data: [100 - percent], + itemStyle: { + normal: { + color: '#d0e9ff', + barBorderRadius: [0, 50, 50, 0] + } + }, + } + ] + }; + } +} \ No newline at end of file diff --git a/liteyuki/resources/templates/static/motto.txt b/liteyuki/resources/templates/static/motto.txt new file mode 100644 index 00000000..e69de29b diff --git a/liteyuki/resources/templates/stats.html b/liteyuki/resources/templates/stats.html index a6ee00f8..d0aa8e5b 100644 --- a/liteyuki/resources/templates/stats.html +++ b/liteyuki/resources/templates/stats.html @@ -6,6 +6,7 @@