✨ 新增 stat rank 功能
This commit is contained in:
@ -1,12 +1,16 @@
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
from collections import Counter
|
||||
|
||||
from nonebot import Bot
|
||||
|
||||
from liteyuki.utils.message.html_tool import template2image
|
||||
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
|
||||
from liteyuki.utils.message.string_tool import convert_seconds_to_time
|
||||
from ...utils.external.logo import get_group_icon, get_user_icon
|
||||
|
||||
|
||||
async def count_msg_by_bot_id(bot_id: str) -> int:
|
||||
@ -22,12 +26,18 @@ async def count_msg_by_bot_id(bot_id: str) -> int:
|
||||
return len(msg_rows)
|
||||
|
||||
|
||||
async def get_stat_msg_image(duration: int, period: int, group_id: str = None, bot_id: str = None, user_id: str = None,
|
||||
ulang: Language = Language()) -> bytes:
|
||||
async def get_stat_msg_image(
|
||||
duration: int,
|
||||
period: int,
|
||||
group_id: str = None,
|
||||
bot_id: str = None,
|
||||
user_id: str = None,
|
||||
ulang: Language = Language()
|
||||
) -> bytes:
|
||||
"""
|
||||
获取统计消息
|
||||
Args:
|
||||
ctx:
|
||||
user_id:
|
||||
ulang:
|
||||
bot_id:
|
||||
group_id:
|
||||
@ -76,22 +86,87 @@ async def get_stat_msg_image(duration: int, period: int, group_id: str = None, b
|
||||
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 "") + (f" User {user_id}" if user_id else ""),
|
||||
"times": timestamps,
|
||||
"counts": msg_count
|
||||
}
|
||||
]
|
||||
"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 "") + (
|
||||
f" User {user_id}" if user_id else ""),
|
||||
"times" : timestamps,
|
||||
"counts": msg_count
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
return await template2image(get_path("templates/stat_msg.html"), templates)
|
||||
|
||||
# if not timestamps or period_start_time != timestamps[-1]:
|
||||
# timestamps.append(period_start_time)
|
||||
# msg_count.append(1)
|
||||
# else:
|
||||
# msg_count[-1] += 1
|
||||
#
|
||||
|
||||
async def get_stat_rank_image(
|
||||
rank_type: str,
|
||||
limit: dict[str, Any],
|
||||
ulang: Language = Language(),
|
||||
bot: Bot = None,
|
||||
) -> bytes:
|
||||
if rank_type == "user":
|
||||
condition = "user_id != ''"
|
||||
condition_args = []
|
||||
else:
|
||||
condition = "group_id != ''"
|
||||
condition_args = []
|
||||
|
||||
for k, v in limit.items():
|
||||
match k:
|
||||
case "user_id":
|
||||
condition += " AND user_id = ?"
|
||||
condition_args.append(v)
|
||||
case "group_id":
|
||||
condition += " AND group_id = ?"
|
||||
condition_args.append(v)
|
||||
case "bot_id":
|
||||
condition += " AND bot_id = ?"
|
||||
condition_args.append(v)
|
||||
case "duration":
|
||||
condition += " AND time > ?"
|
||||
condition_args.append(v)
|
||||
|
||||
msg_rows = msg_db.where_all(
|
||||
MessageEventModel(),
|
||||
condition,
|
||||
*condition_args
|
||||
)
|
||||
|
||||
"""
|
||||
{
|
||||
name: string, # user name or group name
|
||||
count: int, # message count
|
||||
icon: string # icon url
|
||||
}
|
||||
"""
|
||||
|
||||
if rank_type == "user":
|
||||
ranking_counter = Counter([msg.user_id for msg in msg_rows])
|
||||
else:
|
||||
ranking_counter = Counter([msg.group_id for msg in msg_rows])
|
||||
sorted_data = sorted(ranking_counter.items(), key=lambda x: x[1], reverse=True)
|
||||
|
||||
ranking: list[dict[str, Any]] = [
|
||||
{
|
||||
"name" : _[0],
|
||||
"count": _[1],
|
||||
"icon" : await (get_group_icon(platform="qq", group_id=_[0]) if rank_type == "group" else get_user_icon(
|
||||
platform="qq", user_id=_[0]
|
||||
))
|
||||
}
|
||||
for _ in sorted_data[0:min(len(sorted_data), limit["rank"])]
|
||||
]
|
||||
|
||||
templates = {
|
||||
"data":
|
||||
{
|
||||
"name" : ulang.get("stat.rank") + f" Type {rank_type}" + f" Limit {limit}",
|
||||
"ranking": ranking
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return await template2image(get_path("templates/stat_rank.html"), templates, debug=True)
|
||||
|
@ -1,5 +1,5 @@
|
||||
from nonebot import Bot, require
|
||||
from liteyuki.utils.message.npl import convert_duration, convert_time_to_seconds
|
||||
from liteyuki.utils.message.string_tool import convert_duration, convert_time_to_seconds
|
||||
from .stat_api import *
|
||||
from liteyuki.utils import event as event_utils
|
||||
from liteyuki.utils.base.language import Language
|
||||
@ -7,7 +7,16 @@ from liteyuki.utils.base.ly_typing import T_MessageEvent
|
||||
|
||||
require("nonebot_plugin_alconna")
|
||||
|
||||
from nonebot_plugin_alconna import UniMessage, on_alconna, Alconna, Args, Subcommand, Arparma, Option
|
||||
from nonebot_plugin_alconna import (
|
||||
UniMessage,
|
||||
on_alconna,
|
||||
Alconna,
|
||||
Args,
|
||||
Subcommand,
|
||||
Arparma,
|
||||
Option,
|
||||
MultiVar
|
||||
)
|
||||
|
||||
stat_msg = on_alconna(
|
||||
Alconna(
|
||||
@ -42,6 +51,33 @@ stat_msg = on_alconna(
|
||||
),
|
||||
alias={"msg", "m"},
|
||||
help_text="查看统计次数内的消息"
|
||||
),
|
||||
Subcommand(
|
||||
"rank",
|
||||
Option(
|
||||
"-u|--user",
|
||||
help_text="以用户为指标",
|
||||
),
|
||||
Option(
|
||||
"-g|--group",
|
||||
help_text="以群组为指标",
|
||||
),
|
||||
Option(
|
||||
"-l|--limit",
|
||||
Args["limit", MultiVar(str)],
|
||||
help_text="限制参数,使用key=val格式",
|
||||
),
|
||||
Option(
|
||||
"-d|--duration",
|
||||
Args["duration", str, "1d"],
|
||||
help_text="统计时间",
|
||||
),
|
||||
Option(
|
||||
"-r|--rank",
|
||||
Args["rank", int, 20],
|
||||
help_text="指定排名",
|
||||
),
|
||||
alias={"r"},
|
||||
)
|
||||
),
|
||||
aliases={"stat"}
|
||||
@ -51,7 +87,6 @@ stat_msg = on_alconna(
|
||||
@stat_msg.assign("message")
|
||||
async def _(result: Arparma, event: T_MessageEvent, bot: Bot):
|
||||
ulang = Language(event_utils.get_user_id(event))
|
||||
|
||||
try:
|
||||
duration = convert_time_to_seconds(result.other_args.get("duration", "2d")) # 秒数
|
||||
period = convert_time_to_seconds(result.other_args.get("period", "1m"))
|
||||
@ -77,3 +112,24 @@ async def _(result: Arparma, event: T_MessageEvent, bot: Bot):
|
||||
|
||||
img = await get_stat_msg_image(duration=duration, period=period, group_id=group_id, bot_id=bot_id, user_id=user_id, ulang=ulang)
|
||||
await stat_msg.send(UniMessage.image(raw=img))
|
||||
|
||||
|
||||
@stat_msg.assign("rank")
|
||||
async def _(result: Arparma, event: T_MessageEvent, bot: Bot):
|
||||
ulang = Language(event_utils.get_user_id(event))
|
||||
rank_type = "user"
|
||||
duration = convert_time_to_seconds(result.other_args.get("duration", "1d"))
|
||||
print(result)
|
||||
if result.subcommands.get("rank").options.get("user"):
|
||||
rank_type = "user"
|
||||
elif result.subcommands.get("rank").options.get("group"):
|
||||
rank_type = "group"
|
||||
|
||||
limit = result.other_args.get("limit", {})
|
||||
if limit:
|
||||
limit = dict([i.split("=") for i in limit])
|
||||
limit["duration"] = time.time() - duration # 起始时间戳
|
||||
limit["rank"] = result.other_args.get("rank", 20)
|
||||
|
||||
img = await get_stat_rank_image(rank_type=rank_type, limit=limit, ulang=ulang)
|
||||
await stat_msg.send(UniMessage.image(raw=img))
|
||||
|
Reference in New Issue
Block a user