mirror of
https://github.com/TriM-Organization/LiteyukiBot-TriM.git
synced 2024-11-28 16:24:51 +08:00
✨ message 统计
This commit is contained in:
parent
c6f2a29320
commit
041ceb81d8
@ -1,41 +0,0 @@
|
|||||||
def convert_duration(text: str, default) -> float:
|
|
||||||
"""
|
|
||||||
转换自然语言时间为秒数
|
|
||||||
Args:
|
|
||||||
text: 1d2h3m
|
|
||||||
default: 出错时返回
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
float: 总秒数
|
|
||||||
"""
|
|
||||||
units = {
|
|
||||||
"d" : 86400,
|
|
||||||
"h" : 3600,
|
|
||||||
"m" : 60,
|
|
||||||
"s" : 1,
|
|
||||||
"ms": 0.001
|
|
||||||
}
|
|
||||||
|
|
||||||
duration = 0
|
|
||||||
current_number = ''
|
|
||||||
current_unit = ''
|
|
||||||
try:
|
|
||||||
for char in text:
|
|
||||||
if char.isdigit():
|
|
||||||
current_number += char
|
|
||||||
else:
|
|
||||||
if current_number:
|
|
||||||
duration += int(current_number) * units[current_unit]
|
|
||||||
current_number = ''
|
|
||||||
if char in units:
|
|
||||||
current_unit = char
|
|
||||||
else:
|
|
||||||
current_unit = ''
|
|
||||||
|
|
||||||
if current_number:
|
|
||||||
duration += int(current_number) * units[current_unit]
|
|
||||||
|
|
||||||
return duration
|
|
||||||
|
|
||||||
except:
|
|
||||||
return default
|
|
@ -16,7 +16,7 @@ __plugin_meta__ = PluginMetadata(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
from ..internal.base.language import Language, get_default_lang_code
|
from ..utils.base.language import Language, get_default_lang_code
|
||||||
|
|
||||||
print("\033[34m" + r"""
|
print("\033[34m" + r"""
|
||||||
__ ______ ________ ________ __ __ __ __ __ __ ______
|
__ ______ ________ ________ __ __ __ __ __ __ ______
|
||||||
|
@ -10,12 +10,12 @@ from nonebot.exception import MockApiException
|
|||||||
from nonebot.internal.matcher import Matcher
|
from nonebot.internal.matcher import Matcher
|
||||||
from nonebot.permission import SUPERUSER
|
from nonebot.permission import SUPERUSER
|
||||||
|
|
||||||
from liteyuki.internal.base.config import get_config, load_from_yaml
|
from liteyuki.utils.base.config import get_config, load_from_yaml
|
||||||
from liteyuki.internal.base.data_manager import StoredConfig, TempConfig, common_db
|
from liteyuki.utils.base.data_manager import StoredConfig, TempConfig, common_db
|
||||||
from liteyuki.internal.base.language import get_user_lang
|
from liteyuki.utils.base.language import get_user_lang
|
||||||
from liteyuki.internal.base.ly_typing import T_Bot, T_MessageEvent
|
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent
|
||||||
from liteyuki.internal.message.message import MarkdownMessage as md, broadcast_to_superusers
|
from liteyuki.utils.message.message import MarkdownMessage as md, broadcast_to_superusers
|
||||||
from liteyuki.internal.base.reloader import Reloader
|
from liteyuki.utils.base.reloader import Reloader
|
||||||
from .api import update_liteyuki
|
from .api import update_liteyuki
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
|
@ -4,9 +4,9 @@ import nonebot
|
|||||||
from watchdog.observers import Observer
|
from watchdog.observers import Observer
|
||||||
from watchdog.events import FileSystemEventHandler
|
from watchdog.events import FileSystemEventHandler
|
||||||
|
|
||||||
from liteyuki.internal.base.config import get_config
|
from liteyuki.utils.base.config import get_config
|
||||||
from liteyuki.internal.base.reloader import Reloader
|
from liteyuki.utils.base.reloader import Reloader
|
||||||
from liteyuki.internal.base.resource import load_resources
|
from liteyuki.utils.base.resource import load_resources
|
||||||
|
|
||||||
if get_config("debug", False):
|
if get_config("debug", False):
|
||||||
nonebot.logger.info("Liteyuki Reload is enable, watching for file changes...")
|
nonebot.logger.info("Liteyuki Reload is enable, watching for file changes...")
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import nonebot.plugin
|
import nonebot.plugin
|
||||||
|
|
||||||
from liteyuki.internal import init_log
|
from liteyuki.utils import init_log
|
||||||
from liteyuki.internal.base.config import get_config
|
from liteyuki.utils.base.config import get_config
|
||||||
from liteyuki.internal.base.data_manager import InstalledPlugin, plugin_db
|
from liteyuki.utils.base.data_manager import InstalledPlugin, plugin_db
|
||||||
from liteyuki.internal.base.resource import load_resources
|
from liteyuki.utils.base.resource import load_resources
|
||||||
from liteyuki.internal.message.tools import check_for_package
|
from liteyuki.utils.message.tools import check_for_package
|
||||||
|
|
||||||
load_resources()
|
load_resources()
|
||||||
init_log()
|
init_log()
|
||||||
|
@ -9,7 +9,7 @@ from nonebot_plugin_alconna import on_alconna, Alconna, Subcommand, Args, MultiV
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from .canvas import *
|
from .canvas import *
|
||||||
from ...internal.base.resource import get_path
|
from ...utils.base.resource import get_path
|
||||||
|
|
||||||
resolution = 256
|
resolution = 256
|
||||||
|
|
||||||
|
@ -2,9 +2,9 @@ import nonebot
|
|||||||
from nonebot import on_message, require
|
from nonebot import on_message, require
|
||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
|
|
||||||
from liteyuki.internal.base.data import Database, LiteModel
|
from liteyuki.utils.base.data import Database, LiteModel
|
||||||
from liteyuki.internal.base.ly_typing import T_Bot, T_MessageEvent
|
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent
|
||||||
from liteyuki.internal.message.message import MarkdownMessage as md
|
from liteyuki.utils.message.message import MarkdownMessage as md
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
from nonebot_plugin_alconna import on_alconna
|
from nonebot_plugin_alconna import on_alconna
|
||||||
|
@ -4,9 +4,9 @@ from nonebot.params import CommandArg
|
|||||||
from nonebot.permission import SUPERUSER
|
from nonebot.permission import SUPERUSER
|
||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
|
|
||||||
from liteyuki.internal.base.ly_typing import T_Bot, T_MessageEvent, v11
|
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent, v11
|
||||||
from liteyuki.internal.message.message import MarkdownMessage as md, broadcast_to_superusers
|
from liteyuki.utils.message.message import MarkdownMessage as md, broadcast_to_superusers
|
||||||
from liteyuki.internal.message.html_tool import *
|
from liteyuki.utils.message.html_tool import *
|
||||||
|
|
||||||
md_test = on_command("mdts", permission=SUPERUSER)
|
md_test = on_command("mdts", permission=SUPERUSER)
|
||||||
btn_test = on_command("btnts", permission=SUPERUSER)
|
btn_test = on_command("btnts", permission=SUPERUSER)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import random
|
import random
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from liteyuki.internal.message.message import MarkdownMessage as md
|
from liteyuki.utils.message.message import MarkdownMessage as md
|
||||||
|
|
||||||
class Dot(BaseModel):
|
class Dot(BaseModel):
|
||||||
row: int
|
row: int
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from nonebot import require
|
from nonebot import require
|
||||||
|
|
||||||
from liteyuki.internal.base.ly_typing import T_Bot, T_MessageEvent
|
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent
|
||||||
from liteyuki.internal.message.message import MarkdownMessage as md
|
from liteyuki.utils.message.message import MarkdownMessage as md
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
from .game import Minesweeper
|
from .game import Minesweeper
|
||||||
|
@ -4,9 +4,9 @@ from typing import Optional
|
|||||||
import aiofiles
|
import aiofiles
|
||||||
import nonebot.plugin
|
import nonebot.plugin
|
||||||
|
|
||||||
from liteyuki.internal.base.data import LiteModel
|
from liteyuki.utils.base.data import LiteModel
|
||||||
from liteyuki.internal.base.data_manager import GlobalPlugin, Group, User, group_db, plugin_db, user_db
|
from liteyuki.utils.base.data_manager import GlobalPlugin, Group, User, group_db, plugin_db, user_db
|
||||||
from liteyuki.internal.base.ly_typing import T_MessageEvent
|
from liteyuki.utils.base.ly_typing import T_MessageEvent
|
||||||
|
|
||||||
__group_data = {} # 群数据缓存, {group_id: Group}
|
__group_data = {} # 群数据缓存, {group_id: Group}
|
||||||
__user_data = {} # 用户数据缓存, {user_id: User}
|
__user_data = {} # 用户数据缓存, {user_id: User}
|
||||||
|
@ -14,13 +14,13 @@ from nonebot.permission import SUPERUSER
|
|||||||
from nonebot.plugin import Plugin, PluginMetadata
|
from nonebot.plugin import Plugin, PluginMetadata
|
||||||
from nonebot.utils import run_sync
|
from nonebot.utils import run_sync
|
||||||
|
|
||||||
from liteyuki.internal.base.data_manager import InstalledPlugin
|
from liteyuki.utils.base.data_manager import InstalledPlugin
|
||||||
from liteyuki.internal.base.language import get_user_lang
|
from liteyuki.utils.base.language import get_user_lang
|
||||||
from liteyuki.internal.base.ly_typing import T_Bot
|
from liteyuki.utils.base.ly_typing import T_Bot
|
||||||
from liteyuki.internal.message.message import MarkdownMessage as md
|
from liteyuki.utils.message.message import MarkdownMessage as md
|
||||||
from liteyuki.internal.message.markdown import MarkdownComponent as mdc, compile_md, escape_md
|
from liteyuki.utils.message.markdown import MarkdownComponent as mdc, compile_md, escape_md
|
||||||
from liteyuki.internal.base.permission import GROUP_ADMIN, GROUP_OWNER
|
from liteyuki.utils.base.permission import GROUP_ADMIN, GROUP_OWNER
|
||||||
from liteyuki.internal.message.tools import clamp
|
from liteyuki.utils.message.tools import clamp
|
||||||
from .common import *
|
from .common import *
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
|
@ -5,10 +5,10 @@ import yaml
|
|||||||
from nonebot import require
|
from nonebot import require
|
||||||
from nonebot.permission import SUPERUSER
|
from nonebot.permission import SUPERUSER
|
||||||
|
|
||||||
from liteyuki.internal.base.language import get_user_lang
|
from liteyuki.utils.base.language import get_user_lang
|
||||||
from liteyuki.internal.base.ly_typing import T_Bot, T_MessageEvent
|
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent
|
||||||
from liteyuki.internal.message.message import MarkdownMessage as md
|
from liteyuki.utils.message.message import MarkdownMessage as md
|
||||||
from liteyuki.internal.base.resource import (ResourceMetadata, add_resource_pack, change_priority, check_exist, check_status, get_loaded_resource_packs, get_resource_metadata, load_resources, remove_resource_pack)
|
from liteyuki.utils.base.resource import (ResourceMetadata, add_resource_pack, change_priority, check_exist, check_status, get_loaded_resource_packs, get_resource_metadata, load_resources, remove_resource_pack)
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
from nonebot_plugin_alconna import Alconna, Args, on_alconna, Arparma, Subcommand
|
from nonebot_plugin_alconna import Alconna, Args, on_alconna, Arparma, Subcommand
|
||||||
|
@ -5,10 +5,10 @@ import aiohttp
|
|||||||
from nonebot import require
|
from nonebot import require
|
||||||
from nonebot.plugin import PluginMetadata
|
from nonebot.plugin import PluginMetadata
|
||||||
|
|
||||||
from liteyuki.internal.base.config import get_config
|
from liteyuki.utils.base.config import get_config
|
||||||
from liteyuki.internal.base.data import Database, LiteModel
|
from liteyuki.utils.base.data import Database, LiteModel
|
||||||
from liteyuki.internal.base.resource import get_path
|
from liteyuki.utils.base.resource import get_path
|
||||||
from liteyuki.internal.message.html_tool import template2image
|
from liteyuki.utils.message.html_tool import template2image
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
require("nonebot_plugin_apscheduler")
|
require("nonebot_plugin_apscheduler")
|
||||||
@ -76,7 +76,7 @@ async def _():
|
|||||||
async def _():
|
async def _():
|
||||||
query_stamp = [1, 5, 10, 15]
|
query_stamp = [1, 5, 10, 15]
|
||||||
|
|
||||||
reply = "Count from last " + ", ".join([str(i) for i in query_stamp]) + "mins"
|
reply = "QPS from last " + ", ".join([str(i) for i in query_stamp]) + "mins"
|
||||||
for name, url in SIGN_COUNT_URLS.items():
|
for name, url in SIGN_COUNT_URLS.items():
|
||||||
count_data = []
|
count_data = []
|
||||||
for stamp in query_stamp:
|
for stamp in query_stamp:
|
||||||
@ -84,8 +84,8 @@ async def _():
|
|||||||
if len(count_rows) < 2:
|
if len(count_rows) < 2:
|
||||||
count_data.append(-1)
|
count_data.append(-1)
|
||||||
else:
|
else:
|
||||||
count_data.append(count_rows[-1].count - count_rows[0].count)
|
count_data.append((count_rows[-1].count - count_rows[0].count)/(stamp*60))
|
||||||
reply += f"\n{name}: " + ", ".join([str(i) for i in count_data])
|
reply += f"\n{name}: " + ", ".join([f"{i:.1f}" for i in count_data])
|
||||||
await sign_status.send(reply)
|
await sign_status.send(reply)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from liteyuki.internal.base.data import Database, LiteModel
|
from liteyuki.utils.base.data import Database, LiteModel
|
||||||
|
|
||||||
|
|
||||||
class MessageEventModel(LiteModel):
|
class MessageEventModel(LiteModel):
|
||||||
|
@ -1,12 +1,22 @@
|
|||||||
import time
|
import time
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from liteyuki.utils.message.html_tool import template2image
|
||||||
from .common import MessageEventModel, msg_db
|
from .common import MessageEventModel, msg_db
|
||||||
|
from liteyuki.utils.base.language import Language
|
||||||
|
from liteyuki.utils.base.resource import get_path
|
||||||
|
from liteyuki.utils.message.npl import convert_seconds_to_time
|
||||||
|
from contextvars import ContextVar
|
||||||
|
|
||||||
|
|
||||||
def get_stat_msg_data(duration, period) -> tuple[list[int,], list[int,]]:
|
async def get_stat_msg_image(duration: int, period: int, group_id: str = None, bot_id: str = None, ulang: Language = Language()) -> bytes:
|
||||||
"""
|
"""
|
||||||
获取统计消息
|
获取统计消息
|
||||||
Args:
|
Args:
|
||||||
|
ctx:
|
||||||
|
ulang:
|
||||||
|
bot_id:
|
||||||
|
group_id:
|
||||||
duration: 统计时间,单位秒
|
duration: 统计时间,单位秒
|
||||||
period: 统计周期,单位秒
|
period: 统计周期,单位秒
|
||||||
|
|
||||||
@ -14,20 +24,56 @@ def get_stat_msg_data(duration, period) -> tuple[list[int,], list[int,]]:
|
|||||||
tuple: [int,], [int,] 两个列表,分别为周期中心时间戳和消息数量
|
tuple: [int,], [int,] 两个列表,分别为周期中心时间戳和消息数量
|
||||||
"""
|
"""
|
||||||
now = int(time.time())
|
now = int(time.time())
|
||||||
|
start_time = (now - duration)
|
||||||
|
|
||||||
|
condition = "time > ?"
|
||||||
|
condition_args = [start_time]
|
||||||
|
|
||||||
|
if group_id:
|
||||||
|
condition += " AND group_id = ?"
|
||||||
|
condition_args.append(group_id)
|
||||||
|
if bot_id:
|
||||||
|
condition += " AND bot_id = ?"
|
||||||
|
condition_args.append(bot_id)
|
||||||
|
|
||||||
msg_rows = msg_db.where_all(
|
msg_rows = msg_db.where_all(
|
||||||
MessageEventModel(),
|
MessageEventModel(),
|
||||||
"time > ?",
|
condition,
|
||||||
now - duration
|
*condition_args
|
||||||
)
|
)
|
||||||
timestamps = []
|
timestamps = []
|
||||||
msg_count = []
|
msg_count = []
|
||||||
msg_rows.sort(key=lambda x: x.time)
|
msg_rows.sort(key=lambda x: x.time)
|
||||||
for msg_row in msg_rows:
|
|
||||||
period_center_time = msg_row.time - msg_row.time % period + period // 2
|
|
||||||
|
|
||||||
# if not timestamps or period_start_time != timestamps[-1]:
|
start_time = max(msg_rows[0].time, start_time)
|
||||||
# timestamps.append(period_start_time)
|
|
||||||
# msg_count.append(1)
|
for i in range(start_time, now, period):
|
||||||
# else:
|
timestamps.append(i + period // 2)
|
||||||
# msg_count[-1] += 1
|
msg_count.append(0)
|
||||||
#
|
|
||||||
|
for msg in msg_rows:
|
||||||
|
period_start_time = start_time + (msg.time - start_time) // period * period
|
||||||
|
period_center_time = period_start_time + period // 2
|
||||||
|
index = timestamps.index(period_center_time)
|
||||||
|
msg_count[index] += 1
|
||||||
|
|
||||||
|
templates = {
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"name" : ulang.get("stat.message")
|
||||||
|
+ f" Period {convert_seconds_to_time(period)}" + f" Duration {convert_seconds_to_time(duration)}"
|
||||||
|
+ (f" Group {group_id}" if group_id else "") + (f" Bot {bot_id}" if bot_id else ""),
|
||||||
|
"times" : timestamps,
|
||||||
|
"counts": msg_count
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return await template2image(get_path("templates/stat_msg.html"), templates, debug=True)
|
||||||
|
|
||||||
|
# if not timestamps or period_start_time != timestamps[-1]:
|
||||||
|
# timestamps.append(period_start_time)
|
||||||
|
# msg_count.append(1)
|
||||||
|
# else:
|
||||||
|
# msg_count[-1] += 1
|
||||||
|
#
|
||||||
|
@ -1,31 +1,39 @@
|
|||||||
from nonebot import require
|
from nonebot import Bot, require
|
||||||
from liteyuki.internal.message.npl import convert_duration
|
from liteyuki.utils.message.npl import convert_duration, convert_time_to_seconds
|
||||||
from .stat_api import *
|
from .stat_api import *
|
||||||
|
from ...utils.base.language import Language
|
||||||
|
from ...utils.base.ly_typing import T_MessageEvent
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
|
|
||||||
from nonebot_plugin_alconna import on_alconna, Alconna, Args, Subcommand, Arparma, Option
|
from nonebot_plugin_alconna import UniMessage, on_alconna, Alconna, Args, Subcommand, Arparma, Option
|
||||||
|
|
||||||
stat_msg = on_alconna(
|
stat_msg = on_alconna(
|
||||||
Alconna(
|
Alconna(
|
||||||
"stat",
|
"stat",
|
||||||
Subcommand(
|
Subcommand(
|
||||||
"message",
|
"message",
|
||||||
Args["duration", str, "1d"], # 默认为1天
|
# Args["duration", str, "2d"]["period", str, "60s"], # 默认为1天
|
||||||
|
Option(
|
||||||
|
"-d|--duration",
|
||||||
|
Args["duration", str, "2d"],
|
||||||
|
help_text="统计时间",
|
||||||
|
),
|
||||||
|
Option(
|
||||||
|
"-p|--period",
|
||||||
|
Args["period", str, "60s"],
|
||||||
|
help_text="统计周期",
|
||||||
|
),
|
||||||
Option(
|
Option(
|
||||||
"-b|--bot", # 生成图表
|
"-b|--bot", # 生成图表
|
||||||
Args["bot_id", str, ""],
|
Args["bot_id", str, "current"],
|
||||||
help_text="是否指定机器人",
|
help_text="是否指定机器人",
|
||||||
),
|
),
|
||||||
Option(
|
Option(
|
||||||
"-g|--group",
|
"-g|--group",
|
||||||
Args["group_id", str, ""],
|
Args["group_id", str, "current"],
|
||||||
help_text="指定群组"
|
help_text="指定群组"
|
||||||
),
|
),
|
||||||
Option(
|
|
||||||
"-c|--chart", # 生成图表
|
|
||||||
help_text="是否生成图表",
|
|
||||||
),
|
|
||||||
alias={"msg", "m"},
|
alias={"msg", "m"},
|
||||||
help_text="查看统计次数内的消息"
|
help_text="查看统计次数内的消息"
|
||||||
)
|
)
|
||||||
@ -34,13 +42,27 @@ stat_msg = on_alconna(
|
|||||||
|
|
||||||
|
|
||||||
@stat_msg.assign("message")
|
@stat_msg.assign("message")
|
||||||
async def _(result: Arparma):
|
async def _(result: Arparma, event: T_MessageEvent, bot: Bot):
|
||||||
args = result.subcommands.get("message").args
|
ulang = Language(event.user_id)
|
||||||
options = result.subcommands.get("message").options
|
|
||||||
duration = convert_duration(args.get("duration"), 86400) # 秒数
|
|
||||||
enable_chart = options.get("chart")
|
|
||||||
|
|
||||||
if options.get("group"):
|
try:
|
||||||
group_id = options["group"].args.get("group_id")
|
duration = convert_time_to_seconds(result.other_args.get("duration", "2d")) # 秒数
|
||||||
else:
|
period = convert_time_to_seconds(result.other_args.get("period", "1m"))
|
||||||
msg_rows = get_stat_msg_data(duration)
|
except BaseException as e:
|
||||||
|
await stat_msg.send(ulang.get("liteyuki.invalid_command", TEXT=str(e.__str__())))
|
||||||
|
return
|
||||||
|
|
||||||
|
group_id = result.other_args.get("group_id")
|
||||||
|
bot_id = result.other_args.get("bot_id")
|
||||||
|
|
||||||
|
if group_id in ["current", "c"]:
|
||||||
|
group_id = str(event.group_id)
|
||||||
|
|
||||||
|
if group_id in ["all", "a"]:
|
||||||
|
group_id = "all"
|
||||||
|
|
||||||
|
if bot_id == ["current", "c"]:
|
||||||
|
bot_id = str(bot.self_id)
|
||||||
|
|
||||||
|
img = await get_stat_msg_image(duration, period, group_id, bot_id)
|
||||||
|
await stat_msg.send(UniMessage.image(raw=img))
|
||||||
|
@ -3,8 +3,8 @@ import time
|
|||||||
from nonebot import require
|
from nonebot import require
|
||||||
from nonebot.message import event_postprocessor
|
from nonebot.message import event_postprocessor
|
||||||
|
|
||||||
from liteyuki.internal.base.data import Database, LiteModel
|
from liteyuki.utils.base.data import Database, LiteModel
|
||||||
from liteyuki.internal.base.ly_typing import v11
|
from liteyuki.utils.base.ly_typing import v11
|
||||||
|
|
||||||
from .common import MessageEventModel, msg_db
|
from .common import MessageEventModel, msg_db
|
||||||
|
|
||||||
|
@ -5,12 +5,12 @@ import nonebot
|
|||||||
import psutil
|
import psutil
|
||||||
from cpuinfo import cpuinfo
|
from cpuinfo import cpuinfo
|
||||||
from nonebot import require
|
from nonebot import require
|
||||||
from liteyuki.internal import __NAME__, __VERSION__
|
from liteyuki.utils import __NAME__, __VERSION__
|
||||||
from liteyuki.internal.base.config import get_config
|
from liteyuki.utils.base.config import get_config
|
||||||
from liteyuki.internal.base.data_manager import TempConfig, common_db
|
from liteyuki.utils.base.data_manager import TempConfig, common_db
|
||||||
from liteyuki.internal.base.language import Language
|
from liteyuki.utils.base.language import Language
|
||||||
from liteyuki.internal.base.resource import get_loaded_resource_packs, get_path
|
from liteyuki.utils.base.resource import get_loaded_resource_packs, get_path
|
||||||
from liteyuki.internal.message.html_tool import template2image
|
from liteyuki.utils.message.html_tool import template2image
|
||||||
|
|
||||||
require("nonebot_plugin_apscheduler")
|
require("nonebot_plugin_apscheduler")
|
||||||
from nonebot_plugin_apscheduler import scheduler
|
from nonebot_plugin_apscheduler import scheduler
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
from nonebot import require
|
from nonebot import require
|
||||||
|
|
||||||
from liteyuki.internal.base.resource import get_path
|
from liteyuki.utils.base.resource import get_path
|
||||||
from liteyuki.internal.message.html_tool import template2image
|
from liteyuki.utils.message.html_tool import template2image
|
||||||
from liteyuki.internal.base.language import get_user_lang
|
from liteyuki.utils.base.language import get_user_lang
|
||||||
from .api import *
|
from .api import *
|
||||||
from ...internal.base.ly_typing import T_Bot, T_MessageEvent
|
from ...utils.base.ly_typing import T_Bot, T_MessageEvent
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
from nonebot_plugin_alconna import on_alconna, Alconna, Args, Subcommand, Arparma, UniMessage
|
from nonebot_plugin_alconna import on_alconna, Alconna, Args, Subcommand, Arparma, UniMessage
|
||||||
|
@ -3,11 +3,11 @@ from typing import Optional
|
|||||||
import pytz
|
import pytz
|
||||||
from nonebot import require
|
from nonebot import require
|
||||||
|
|
||||||
from liteyuki.internal.base.data import LiteModel, Database
|
from liteyuki.utils.base.data import LiteModel, Database
|
||||||
from liteyuki.internal.base.data_manager import User, user_db, group_db
|
from liteyuki.utils.base.data_manager import User, user_db, group_db
|
||||||
from liteyuki.internal.base.language import Language, change_user_lang, get_all_lang, get_user_lang
|
from liteyuki.utils.base.language import Language, change_user_lang, get_all_lang, get_user_lang
|
||||||
from liteyuki.internal.base.ly_typing import T_Bot, T_MessageEvent
|
from liteyuki.utils.base.ly_typing import T_Bot, T_MessageEvent
|
||||||
from liteyuki.internal.message.message import MarkdownMessage as md
|
from liteyuki.utils.message.message import MarkdownMessage as md
|
||||||
from .const import representative_timezones_list
|
from .const import representative_timezones_list
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
|
@ -15,7 +15,7 @@ __plugin_meta__ = PluginMetadata(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
from ...internal.base.data_manager import set_memory_data
|
from ...utils.base.data_manager import set_memory_data
|
||||||
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
|
|
||||||
|
@ -3,8 +3,8 @@ import aiohttp
|
|||||||
from .qw_models import *
|
from .qw_models import *
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
from ...internal.base.data_manager import get_memory_data
|
from ...utils.base.data_manager import get_memory_data
|
||||||
from ...internal.base.language import Language
|
from ...utils.base.language import Language
|
||||||
|
|
||||||
dev_url = "https://devapi.qweather.com/" # 开发HBa
|
dev_url = "https://devapi.qweather.com/" # 开发HBa
|
||||||
com_url = "https://api.qweather.com/" # 正式环境
|
com_url = "https://api.qweather.com/" # 正式环境
|
||||||
@ -27,9 +27,9 @@ async def check_key_dev(key: str) -> bool:
|
|||||||
"location": "101010100",
|
"location": "101010100",
|
||||||
"key" : key,
|
"key" : key,
|
||||||
}
|
}
|
||||||
async with httpx.AsyncClient() as client:
|
async with aiohttp.ClientSession() as client:
|
||||||
resp = await client.get(url, params=params)
|
resp = await client.get(url, params=params)
|
||||||
return (resp.json()).get("code") != "200" # 查询不到付费数据为开发版
|
return (await resp.json()).get("code") != "200" # 查询不到付费数据为开发版
|
||||||
|
|
||||||
|
|
||||||
def get_local_data(ulang_code: str) -> dict:
|
def get_local_data(ulang_code: str) -> dict:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from liteyuki.internal.base.data import LiteModel
|
from liteyuki.utils.base.data import LiteModel
|
||||||
|
|
||||||
|
|
||||||
class Location(LiteModel):
|
class Location(LiteModel):
|
||||||
|
@ -2,14 +2,14 @@ from nonebot import require, on_endswith
|
|||||||
from nonebot.adapters.onebot.v11 import MessageSegment
|
from nonebot.adapters.onebot.v11 import MessageSegment
|
||||||
from nonebot.internal.matcher import Matcher
|
from nonebot.internal.matcher import Matcher
|
||||||
|
|
||||||
from liteyuki.internal.base.config import get_config
|
from liteyuki.utils.base.config import get_config
|
||||||
from liteyuki.internal.base.ly_typing import T_MessageEvent
|
from liteyuki.utils.base.ly_typing import T_MessageEvent
|
||||||
|
|
||||||
from .qw_api import *
|
from .qw_api import *
|
||||||
from liteyuki.internal.base.data_manager import User, user_db
|
from liteyuki.utils.base.data_manager import User, user_db
|
||||||
from liteyuki.internal.base.language import Language, get_user_lang
|
from liteyuki.utils.base.language import Language, get_user_lang
|
||||||
from liteyuki.internal.base.resource import get_path
|
from liteyuki.utils.base.resource import get_path
|
||||||
from liteyuki.internal.message.html_tool import template2image
|
from liteyuki.utils.message.html_tool import template2image
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
from nonebot_plugin_alconna import on_alconna, Alconna, Args, MultiVar, Arparma
|
from nonebot_plugin_alconna import on_alconna, Alconna, Args, MultiVar, Arparma
|
||||||
|
@ -17,7 +17,7 @@ data.forEach((item) => {
|
|||||||
item["counts"].forEach((count, index) => {
|
item["counts"].forEach((count, index) => {
|
||||||
// 计算平均值,index - 1的count + index的count + index + 1的count /3
|
// 计算平均值,index - 1的count + index的count + index + 1的count /3
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
timeCount.push((item["counts"][index] - item["counts"][index - 1]) / (60 * (item["times"][index] - item["times"][index - 1])))
|
timeCount.push((item["counts"][index] - item["counts"][index - 1]) / (60*(item["times"][index] - item["times"][index - 1])))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
1
liteyuki/resources/liteyuki_statistics/lang/zh-CN.lang
Normal file
1
liteyuki/resources/liteyuki_statistics/lang/zh-CN.lang
Normal file
@ -0,0 +1 @@
|
|||||||
|
stat.message=统计消息
|
3
liteyuki/resources/liteyuki_statistics/metadata.yml
Normal file
3
liteyuki/resources/liteyuki_statistics/metadata.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
name: 轻雪统计信息附件
|
||||||
|
description: For Liteyuki statistic
|
||||||
|
version: 2024.4.26
|
@ -0,0 +1,4 @@
|
|||||||
|
.sign-chart {
|
||||||
|
height: 400px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.7);
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
// 数据类型声明
|
||||||
|
// import * as echarts from 'echarts';
|
||||||
|
|
||||||
|
let data = JSON.parse(document.getElementById("data").innerText) // object
|
||||||
|
const signChartDivTemplate = document.importNode(document.getElementById("sign-chart-template").content, true)
|
||||||
|
data.forEach((item) => {
|
||||||
|
let signChartDiv = signChartDivTemplate.cloneNode(true)
|
||||||
|
let chartID = item["name"]
|
||||||
|
// 初始化ECharts实例
|
||||||
|
// 设置id
|
||||||
|
signChartDiv.querySelector(".sign-chart").id = chartID
|
||||||
|
document.body.appendChild(signChartDiv)
|
||||||
|
|
||||||
|
let signChart = echarts.init(document.getElementById(chartID))
|
||||||
|
|
||||||
|
signChart.setOption(
|
||||||
|
{
|
||||||
|
animation: false,
|
||||||
|
title: {
|
||||||
|
text: item["name"],
|
||||||
|
textStyle: {
|
||||||
|
color: '#000000' // 设置标题文本颜色为红色
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: item["times"].map(timestampToTime),
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
min: Math.min(...item["counts"]),
|
||||||
|
},
|
||||||
|
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: item["counts"],
|
||||||
|
type: 'line',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
function timestampToTime(timestamp) {
|
||||||
|
let date = new Date(timestamp * 1000)
|
||||||
|
let Y = date.getFullYear() + '-'
|
||||||
|
let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
|
||||||
|
let D = date.getDate() + ' '
|
||||||
|
let h = date.getHours() + ':'
|
||||||
|
let m = date.getMinutes() + ':'
|
||||||
|
let s = date.getSeconds()
|
||||||
|
return M + D + h + m + s
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh" xmlns="http://www.w3.org/1999/html">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Liteyuki Stats Message</title>
|
||||||
|
<link rel="stylesheet" href="./css/card.css">
|
||||||
|
<link rel="stylesheet" href="./css/fonts.css">
|
||||||
|
<link rel="stylesheet" href="./css/stat_msg.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<template id="sign-chart-template">
|
||||||
|
<div class="info-box sign-chart">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="data-storage" id="data">{{ data | tojson }}</div>
|
||||||
|
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.5.0/echarts.min.js"></script>
|
||||||
|
<script src="./js/stat_msg.js"></script>
|
||||||
|
<script src="./js/card.js"></script>
|
||||||
|
</body>
|
@ -11,9 +11,9 @@ __VERSION__ = "6.3.2" # 60201
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from liteyuki.internal.base.config import load_from_yaml, config
|
from liteyuki.utils.base.config import load_from_yaml, config
|
||||||
from liteyuki.internal.base.log import init_log
|
from liteyuki.utils.base.log import init_log
|
||||||
from liteyuki.internal.base.data_manager import TempConfig, auto_migrate, common_db
|
from liteyuki.utils.base.data_manager import TempConfig, auto_migrate, common_db
|
||||||
|
|
||||||
major, minor, patch = map(int, __VERSION__.split("."))
|
major, minor, patch = map(int, __VERSION__.split("."))
|
||||||
__VERSION_I__ = major * 10000 + minor * 100 + patch
|
__VERSION_I__ = major * 10000 + minor * 100 + patch
|
@ -42,7 +42,7 @@ def load_resource_from_dir(path: str):
|
|||||||
metadata["path"] = path
|
metadata["path"] = path
|
||||||
metadata["folder"] = os.path.basename(path)
|
metadata["folder"] = os.path.basename(path)
|
||||||
if os.path.exists(os.path.join(path, "lang")):
|
if os.path.exists(os.path.join(path, "lang")):
|
||||||
from liteyuki.internal.base.language import load_from_dir
|
from liteyuki.utils.base.language import load_from_dir
|
||||||
load_from_dir(os.path.join(path, "lang"))
|
load_from_dir(os.path.join(path, "lang"))
|
||||||
_loaded_resource_packs.insert(0, ResourceMetadata(**metadata))
|
_loaded_resource_packs.insert(0, ResourceMetadata(**metadata))
|
||||||
|
|
@ -6,11 +6,10 @@ import aiofiles
|
|||||||
import nonebot
|
import nonebot
|
||||||
from nonebot import require
|
from nonebot import require
|
||||||
|
|
||||||
from liteyuki.internal.base.resource import load_resources
|
|
||||||
|
|
||||||
require("nonebot_plugin_htmlrender")
|
require("nonebot_plugin_htmlrender")
|
||||||
|
|
||||||
from nonebot_plugin_htmlrender import *
|
from nonebot_plugin_htmlrender import *
|
||||||
|
from .tools import random_hex_string
|
||||||
|
|
||||||
|
|
||||||
async def html2image(
|
async def html2image(
|
||||||
@ -76,7 +75,7 @@ async def template2image(
|
|||||||
)
|
)
|
||||||
async with aiofiles.open(os.path.join(template_path, "latest-debug.html"), "w", encoding="utf-8") as f:
|
async with aiofiles.open(os.path.join(template_path, "latest-debug.html"), "w", encoding="utf-8") as f:
|
||||||
await f.write(raw_html)
|
await f.write(raw_html)
|
||||||
nonebot.logger.info("Debug HTML: %s" % "latest-debug.html")
|
nonebot.logger.info("Debug HTML: %s" % f"debug-{random_hex_string(6)}.html")
|
||||||
|
|
||||||
return await template_to_pic(
|
return await template_to_pic(
|
||||||
template_name=template_name,
|
template_name=template_name,
|
101
liteyuki/utils/message/npl.py
Normal file
101
liteyuki/utils/message/npl.py
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import nonebot
|
||||||
|
|
||||||
|
|
||||||
|
def convert_duration(text: str, default) -> float:
|
||||||
|
"""
|
||||||
|
转换自然语言时间为秒数
|
||||||
|
Args:
|
||||||
|
text: 1d2h3m
|
||||||
|
default: 出错时返回
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: 总秒数
|
||||||
|
"""
|
||||||
|
units = {
|
||||||
|
"d" : 86400,
|
||||||
|
"h" : 3600,
|
||||||
|
"m" : 60,
|
||||||
|
"s" : 1,
|
||||||
|
"ms": 0.001
|
||||||
|
}
|
||||||
|
|
||||||
|
duration = 0
|
||||||
|
current_number = ''
|
||||||
|
current_unit = ''
|
||||||
|
try:
|
||||||
|
for char in text:
|
||||||
|
if char.isdigit():
|
||||||
|
current_number += char
|
||||||
|
else:
|
||||||
|
if current_number:
|
||||||
|
duration += int(current_number) * units[current_unit]
|
||||||
|
current_number = ''
|
||||||
|
if char in units:
|
||||||
|
current_unit = char
|
||||||
|
else:
|
||||||
|
current_unit = ''
|
||||||
|
|
||||||
|
if current_number:
|
||||||
|
duration += int(current_number) * units[current_unit]
|
||||||
|
|
||||||
|
return duration
|
||||||
|
|
||||||
|
except BaseException as e:
|
||||||
|
nonebot.logger.info(f"convert_duration error: {e}")
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
def convert_time_to_seconds(time_str):
|
||||||
|
"""转换自然语言时长为秒数
|
||||||
|
Args:
|
||||||
|
time_str: 1d2m3s
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
seconds = 0
|
||||||
|
current_number = ''
|
||||||
|
|
||||||
|
for char in time_str:
|
||||||
|
if char.isdigit() or char == '.':
|
||||||
|
current_number += char
|
||||||
|
elif char == 'd':
|
||||||
|
seconds += float(current_number) * 24 * 60 * 60
|
||||||
|
current_number = ''
|
||||||
|
elif char == 'h':
|
||||||
|
seconds += float(current_number) * 60 * 60
|
||||||
|
current_number = ''
|
||||||
|
elif char == 'm':
|
||||||
|
seconds += float(current_number) * 60
|
||||||
|
current_number = ''
|
||||||
|
elif char == 's':
|
||||||
|
seconds += float(current_number)
|
||||||
|
current_number = ''
|
||||||
|
|
||||||
|
return int(seconds)
|
||||||
|
|
||||||
|
|
||||||
|
def convert_seconds_to_time(seconds):
|
||||||
|
"""转换秒数为自然语言时长
|
||||||
|
Args:
|
||||||
|
seconds: 10000
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
d = seconds // (24 * 60 * 60)
|
||||||
|
h = (seconds % (24 * 60 * 60)) // (60 * 60)
|
||||||
|
m = (seconds % (60 * 60)) // 60
|
||||||
|
s = seconds % 60
|
||||||
|
|
||||||
|
# 若值为0则不显示
|
||||||
|
time_str = ''
|
||||||
|
if d:
|
||||||
|
time_str += f"{d}d"
|
||||||
|
if h:
|
||||||
|
time_str += f"{h}h"
|
||||||
|
if m:
|
||||||
|
time_str += f"{m}m"
|
||||||
|
if not time_str:
|
||||||
|
time_str = f"{s}s"
|
||||||
|
return time_str
|
10
main.py
10
main.py
@ -1,9 +1,9 @@
|
|||||||
import nonebot
|
import nonebot
|
||||||
from nonebot.adapters.onebot import v11, v12
|
from nonebot.adapters.onebot import v11, v12
|
||||||
from liteyuki.internal import init
|
from liteyuki.utils import init
|
||||||
from liteyuki.internal.base.config import load_from_yaml
|
from liteyuki.utils.base.config import load_from_yaml
|
||||||
from liteyuki.internal.base.data_manager import StoredConfig, common_db
|
from liteyuki.utils.base.data_manager import StoredConfig, common_db
|
||||||
from liteyuki.internal.base.ly_api import liteyuki_api
|
from liteyuki.utils.base.ly_api import liteyuki_api
|
||||||
|
|
||||||
if __name__ == "__mp_main__":
|
if __name__ == "__mp_main__":
|
||||||
init()
|
init()
|
||||||
@ -25,5 +25,5 @@ if __name__ == "__mp_main__":
|
|||||||
liteyuki_api.bug_report(str(e.__repr__()))
|
liteyuki_api.bug_report(str(e.__repr__()))
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
from liteyuki.internal.base.reloader import Reloader
|
from liteyuki.utils.base.reloader import Reloader
|
||||||
nonebot.run()
|
nonebot.run()
|
||||||
|
Loading…
Reference in New Issue
Block a user