From ffcd7992dda078567b035c24e0753c355d1e4cdc Mon Sep 17 00:00:00 2001 From: ShiXui Date: Sun, 18 Sep 2022 09:15:07 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nonebot_plugin_dialectlist/__init__.py | 34 ++++--- nonebot_plugin_dialectlist/config.py | 17 ++-- .../{qqDBRecorder.py => function.py} | 95 +++++++++++-------- .../qqGuildJsonRecorder.py | 65 ------------- 4 files changed, 83 insertions(+), 128 deletions(-) rename nonebot_plugin_dialectlist/{qqDBRecorder.py => function.py} (73%) delete mode 100644 nonebot_plugin_dialectlist/qqGuildJsonRecorder.py diff --git a/nonebot_plugin_dialectlist/__init__.py b/nonebot_plugin_dialectlist/__init__.py index eda6212..e2d282c 100644 --- a/nonebot_plugin_dialectlist/__init__.py +++ b/nonebot_plugin_dialectlist/__init__.py @@ -17,15 +17,14 @@ from nonebot.matcher import Matcher from nonebot.adapters import Bot from nonebot.adapters.onebot.v11 import GroupMessageEvent, PrivateMessageEvent, Message -require("nonebot_plugin_datastore") +require("nonebot_plugin_chatrecorder_guild_patch") +from nonebot_plugin_chatrecorder_guild_patch import get_guild_all_channel require("nonebot_plugin_chatrecorder") require("nonebot_plugin_guild_patch") from nonebot_plugin_guild_patch import GuildMessageEvent -from .qqDBRecorder import get_message_records,msg_counter +from .function import get_message_records,msg_counter, msg_list2msg from .config import plugin_config -from .qqGuildJsonRecorder import get_guild_message_records - def parse_datetime(key: str): """解析数字,并将结果存入 state 中""" @@ -166,24 +165,37 @@ async def handle_message( ): st = time.time() - bot_id = await bot.call_api('get_login_info') - bot_id = [str(bot_id['user_id'])] + if isinstance(event,GroupMessageEvent): - + bot_id = await bot.call_api('get_login_info') + bot_id = [str(bot_id['user_id'])] gids:List[str] = [str(event.group_id)] - msg = await get_message_records( + msg_list = await get_message_records( group_ids=gids, exclude_user_ids=bot_id, message_type='group', time_start=start.astimezone(ZoneInfo("UTC")), time_stop=stop.astimezone(ZoneInfo("UTC")) ) - msg = await msg_counter(gid=event.group_id, bot=bot, msg=msg,got_num=plugin_config.dialectlist_get_num) elif isinstance(event, GuildMessageEvent): + bot_id = await bot.call_api('get_guild_service_profile') + bot_id = [str(bot_id['tiny_id'])] + guild_ids:List[str] = await get_guild_all_channel(event.guild_id,bot=bot) + msg_list = await get_message_records( + group_ids=guild_ids, + exclude_user_ids=bot_id, + message_type='group', + time_start=start.astimezone(ZoneInfo("UTC")), + time_stop=stop.astimezone(ZoneInfo("UTC")) + ) + - guild_id = event.guild_id - msg = await get_guild_message_records(guild_id=str(guild_id),bot=bot) + msg_dict = await msg_counter(msg_list=msg_list) + if isinstance(event,GroupMessageEvent): + msg = await msg_list2msg(msg_list=msg_dict,gid=event.group_id,platform='qq',bot=bot,got_num=plugin_config.dialectlist_get_num) + elif isinstance(event, GuildMessageEvent): + msg = await msg_list2msg(msg_list=msg_dict,gid=event.guild_id,platform='guild',bot=bot,got_num=plugin_config.dialectlist_get_num) await rankings.send(msg) await asyncio.sleep(1) #让图片先发出来 diff --git a/nonebot_plugin_dialectlist/config.py b/nonebot_plugin_dialectlist/config.py index 284b380..87c1438 100644 --- a/nonebot_plugin_dialectlist/config.py +++ b/nonebot_plugin_dialectlist/config.py @@ -1,21 +1,16 @@ -from typing import Optional +from typing import Optional, Union, Literal from nonebot import get_driver from pydantic import BaseModel, Extra -from pathlib import Path -import os class Config(BaseModel, extra=Extra.ignore): timezone: Optional[str] - dialectlist_string_format: str = '第{index}名:\n{nickname},{chatdatanum}条消息\n' - dialectlist_string_suffix_format: str = '你们的职业是水群吗?————MYX\n计算花费时间:{timecost}秒' - dialectlist_path:str = os.path.dirname(__file__) - dialectlist_image_path: Path = Path(dialectlist_path)/'image.png' - dialectlist_imageSvg_path: Path = Path(dialectlist_path)/'image.svg' - dialectlist_json_path:Path = Path(dialectlist_path)/'qqguild.json' - dialectlist_get_num:int = 10 - dialectlist_visualization:bool = True + dialectlist_string_format: str = '第{index}名:\n{nickname},{chatdatanum}条消息\n' #消息格式 + dialectlist_string_suffix_format: str = '你们的职业是水群吗?————MYX\n计算花费时间:{timecost}秒' #消息后缀格式 + dialectlist_get_num:int = 10 #获取人数数量 + dialectlist_visualization:bool = True #是否可视化 + dialectlist_visualization_type:Literal['饼图','圆环图','柱状图'] = '圆环图' #可视化方案 global_config = get_driver().config plugin_config = Config.parse_obj(global_config) \ No newline at end of file diff --git a/nonebot_plugin_dialectlist/qqDBRecorder.py b/nonebot_plugin_dialectlist/function.py similarity index 73% rename from nonebot_plugin_dialectlist/qqDBRecorder.py rename to nonebot_plugin_dialectlist/function.py index ed3abab..90c4e55 100644 --- a/nonebot_plugin_dialectlist/qqDBRecorder.py +++ b/nonebot_plugin_dialectlist/function.py @@ -9,18 +9,18 @@ from typing import Iterable, List, Optional, Dict from pygal.style import Style style=Style(font_family="SimHei",) - from nonebot.log import logger from nonebot.adapters import Bot from nonebot.adapters.onebot.v11 import Message,MessageSegment from nonebot.adapters.onebot.v11.exception import ActionFailed from nonebot_plugin_datastore import create_session - from nonebot_plugin_chatrecorder.model import MessageRecord from .config import plugin_config + + def remove_control_characters(string:str) -> str: """将字符串中的控制符去除 @@ -32,6 +32,8 @@ def remove_control_characters(string:str) -> str: """ return "".join(ch for ch in string if unicodedata.category(ch)[0]!="C") + + async def get_message_records( user_ids: Optional[Iterable[str]] = None, group_ids: Optional[Iterable[str]] = None, @@ -95,78 +97,90 @@ async def get_message_records( -async def msg_counter( - gid:int, - bot:Bot, - msg:List[MessageRecord], - got_num:int=10, -)->Message: +async def msg_counter(msg_list:List[MessageRecord])->Dict[str,int]: ''' - 计算出结果并返回可以直接发送的字符串和图片 + 计算出话最多的几个人的id并返回 ''' - st = time.time() - - logger.debug('loading msg from group {}'.format(gid)) - gnl = await bot.call_api('get_group_member_list',group_id=int(gid)) - logger.debug('group {} have number {}'.format(gid,len(gnl))) lst:Dict[str,int] = {} - msg_len = len(msg) - for i in msg: + msg_len = len(msg_list) + logger.info('wow , there are {} msgs to count !!!'.format(msg_len)) + + for i in msg_list: try: lst[i.user_id] +=1 except KeyError: lst[i.user_id] =1 logger.debug(lst) - logger.debug('group number num is '+str(len(lst))) + + return lst +async def msg_list2msg( + msg_list:Dict[str,int], + gid:int, + got_num:int, + platform:Optional[Literal['guild', 'qq']], + bot:Bot +)->Message: + ranking = [] while len(ranking) < got_num: try: - maxkey = max(lst, key=lst.get) # type: ignore + maxkey = max(msg_list, key=msg_list.get) # type: ignore except ValueError: ranking.append(["null",0]) continue - logger.debug('searching number {} from group {}'.format(str(maxkey),str(gid))) - + logger.debug('searching member {} from group {}'.format(str(maxkey),str(gid))) + try: - - member_info = await bot.call_api( - "get_group_member_info", - group_id=int(gid), - user_id=int(maxkey), - no_cache=True - ) - nickname:str = member_info['nickname']if not member_info['card'] else member_info['card'] - ranking.append([remove_control_characters(nickname).strip(),lst.pop(maxkey)]) - + if platform == 'qq': + member_info = await bot.call_api( + "get_group_member_info", + group_id=int(gid), + user_id=int(maxkey), + no_cache=True + ) + nickname:str = member_info['nickname']if not member_info['card'] else member_info['card'] + else: + member_info = await bot.call_api( + "get_guild_member_profile", + guild_id=str(gid), + user_id=str(maxkey) + ) + nickname:str = member_info['nickname'] + ranking.append([remove_control_characters(nickname).strip(),msg_list.pop(maxkey)]) except ActionFailed as e: - logger.warning(e) - logger.warning('number {} is not exit in group {}'.format(str(maxkey),str(gid))) - lst.pop(maxkey) + logger.warning('member {} is not exit in group {}'.format(str(maxkey),str(gid))) + msg_list.pop(maxkey) + logger.debug('loaded list:\n{}'.format(ranking)) - if plugin_config.dialectlist_visualization: - - view = pygal.Pie(inner_radius=0.6,style=style) - view.title = '消息圆环图' + if plugin_config.dialectlist_visualization_type == '圆环图': + view = pygal.Pie(inner_radius=0.6,style=style) + elif plugin_config.dialectlist_visualization_type == '饼图': + view = pygal.Pie(style=style) + else: + view = pygal.Bar(style=style) + + view.title = '消息可视化' for i in ranking: view.add(str(i[0]),int(i[1])) try: - png: bytes = view.render_to_png()# type: ignore - process_msg = Message(MessageSegment.image(png)) + png: bytes = view.render_to_png() # type: ignore + process_msg = Message(MessageSegment.image(png)) except OSError: logger.error('GTK+(GIMP Toolkit) is not installed, the svg can not be transformed to png') plugin_config.dialectlist_visualization = False - process_msg = Message('无法发送可视化图片,请检查是否安装GTK+,详细安装教程可见github\nhttps://github.com/tschoonj/GTK-for-Windows-Runtime-Environment-Installer \n若不想安装这个软件,再次使用指令会转换为发送字符串而不是发送图片') + process_msg = Message('无法发送可视化图片,请检查是否安装GTK+,详细安装教程可见github\nhttps://github.com/tschoonj/GTK-for-Windows-Runtime-Environment-Installer \n若不想安装这个软件,再次使用这个指令不会显示这个提示') else: process_msg = '' + out:str = '' for i in range(got_num): index = i+1 @@ -175,6 +189,5 @@ async def msg_counter( out = out + str_example logger.debug(out) - logger.info('spent {} seconds to count from {} msg'.format(time.time()-st,msg_len)) return Message(out)+process_msg diff --git a/nonebot_plugin_dialectlist/qqGuildJsonRecorder.py b/nonebot_plugin_dialectlist/qqGuildJsonRecorder.py deleted file mode 100644 index 084cfb6..0000000 --- a/nonebot_plugin_dialectlist/qqGuildJsonRecorder.py +++ /dev/null @@ -1,65 +0,0 @@ -import json -from typing import Dict - -from nonebot.log import logger -from nonebot.message import event_postprocessor -from nonebot.adapters import Bot -from nonebot.adapters.onebot.v11 import Message -from nonebot.adapters.onebot.v11.exception import ActionFailed - -from nonebot_plugin_guild_patch import GuildMessageEvent - -from .config import plugin_config - - -def update_json(updatedata:Dict): - - with open(plugin_config.dialectlist_json_path, 'w', encoding='utf-8') as f: - json.dump(updatedata, f, ensure_ascii=False, indent=4) - -def get_json()-> Dict[str,Dict]: - - if not plugin_config.dialectlist_json_path.exists(): - return {} - - with open(plugin_config.dialectlist_json_path, 'r', encoding='utf-8') as f: - data:Dict = json.load(f) - return data - - -@event_postprocessor -async def _pocesser(event:GuildMessageEvent): - - data = get_json() - try: - data[str(event.guild_id)][str(event.sender.nickname)] += 1 - except KeyError: - data[str(event.guild_id)] = {str(event.sender.nickname):1} - update_json(data) - - -async def get_guild_message_records( - guild_id:str, - bot:Bot, - got_num:int=10, -)->Message: - data = get_json() - ranking = [] - while len(ranking) < got_num: - - try: - maxkey = max(data[guild_id], key=data[guild_id].get) # type: ignore - except ValueError: - ranking.append(("null",0)) - continue - ranking.append((maxkey,data[guild_id].pop(maxkey))) - - logger.debug('loaded list:\n{}'.format(ranking)) - out:str = '' - for i in range(got_num): - index = i+1 - nickname,chatdatanum = ranking[i] - str_example = plugin_config.dialectlist_string_format.format(index=index,nickname=nickname,chatdatanum=chatdatanum) - out = out + str_example - - return Message(out) From 95c937203d2ef68bf01f33b16353e59a3919198b Mon Sep 17 00:00:00 2001 From: ShiXui Date: Mon, 3 Oct 2022 20:21:24 +0800 Subject: [PATCH 2/3] #feature --- nonebot_plugin_dialectlist/__init__.py | 16 +++++++++------- nonebot_plugin_dialectlist/config.py | 11 +++++++---- nonebot_plugin_dialectlist/function.py | 6 ++---- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/nonebot_plugin_dialectlist/__init__.py b/nonebot_plugin_dialectlist/__init__.py index e2d282c..4a937e1 100644 --- a/nonebot_plugin_dialectlist/__init__.py +++ b/nonebot_plugin_dialectlist/__init__.py @@ -167,24 +167,26 @@ async def handle_message( st = time.time() if isinstance(event,GroupMessageEvent): - bot_id = await bot.call_api('get_login_info') - bot_id = [str(bot_id['user_id'])] + if plugin_config.dialectlist_excluded_self: + bot_id:str = await bot.call_api('get_login_info') + plugin_config.dialectlist_excluded_people.append(bot_id) gids:List[str] = [str(event.group_id)] msg_list = await get_message_records( group_ids=gids, - exclude_user_ids=bot_id, + exclude_user_ids=plugin_config.dialectlist_excluded_people, message_type='group', time_start=start.astimezone(ZoneInfo("UTC")), time_stop=stop.astimezone(ZoneInfo("UTC")) ) elif isinstance(event, GuildMessageEvent): - bot_id = await bot.call_api('get_guild_service_profile') - bot_id = [str(bot_id['tiny_id'])] + if plugin_config.dialectlist_excluded_self: + bot_id = await bot.call_api('get_guild_service_profile') + plugin_config.dialectlist_excluded_people.append(bot_id) guild_ids:List[str] = await get_guild_all_channel(event.guild_id,bot=bot) msg_list = await get_message_records( group_ids=guild_ids, - exclude_user_ids=bot_id, + exclude_user_ids=plugin_config.dialectlist_excluded_people, message_type='group', time_start=start.astimezone(ZoneInfo("UTC")), time_stop=stop.astimezone(ZoneInfo("UTC")) @@ -199,5 +201,5 @@ async def handle_message( await rankings.send(msg) await asyncio.sleep(1) #让图片先发出来 - await rankings.finish(plugin_config.dialectlist_string_suffix_format.format(timecost=time.time()-st-1)) + await rankings.finish(plugin_config.dialectlist_string_suffix_format.format(timeCost=time.time()-st-1)) \ No newline at end of file diff --git a/nonebot_plugin_dialectlist/config.py b/nonebot_plugin_dialectlist/config.py index 87c1438..8e44f81 100644 --- a/nonebot_plugin_dialectlist/config.py +++ b/nonebot_plugin_dialectlist/config.py @@ -1,4 +1,4 @@ -from typing import Optional, Union, Literal +from typing import Optional, Literal, List from nonebot import get_driver from pydantic import BaseModel, Extra @@ -6,11 +6,14 @@ from pydantic import BaseModel, Extra class Config(BaseModel, extra=Extra.ignore): timezone: Optional[str] - dialectlist_string_format: str = '第{index}名:\n{nickname},{chatdatanum}条消息\n' #消息格式 - dialectlist_string_suffix_format: str = '你们的职业是水群吗?————MYX\n计算花费时间:{timecost}秒' #消息后缀格式 + dialectlist_string_format: str = '第{index}名:\n{nickname},{chatDataNum}条消息\n' #消息格式 + dialectlist_string_suffix_format: str = '你们的职业是水群吗?————MYX\n计算花费时间:{timeCost}秒' #消息后缀格式 dialectlist_get_num:int = 10 #获取人数数量 dialectlist_visualization:bool = True #是否可视化 dialectlist_visualization_type:Literal['饼图','圆环图','柱状图'] = '圆环图' #可视化方案 + dialectlist_font:str = 'SimHei'#字体格式 + dialectlist_excluded_people:List[str] = []#排除的人的QQ号(或频道号?(未经测试)) + dialectlist_excluded_self:bool = True global_config = get_driver().config -plugin_config = Config.parse_obj(global_config) \ No newline at end of file +plugin_config = Config(**global_config.dict()) \ No newline at end of file diff --git a/nonebot_plugin_dialectlist/function.py b/nonebot_plugin_dialectlist/function.py index 90c4e55..227dc7b 100644 --- a/nonebot_plugin_dialectlist/function.py +++ b/nonebot_plugin_dialectlist/function.py @@ -1,4 +1,3 @@ -import time import pygal import unicodedata @@ -7,7 +6,6 @@ from sqlmodel import select, or_ from typing_extensions import Literal from typing import Iterable, List, Optional, Dict from pygal.style import Style -style=Style(font_family="SimHei",) from nonebot.log import logger from nonebot.adapters import Bot @@ -18,7 +16,7 @@ from nonebot_plugin_datastore import create_session from nonebot_plugin_chatrecorder.model import MessageRecord from .config import plugin_config - +style=Style(font_family=plugin_config.dialectlist_font) def remove_control_characters(string:str) -> str: @@ -185,7 +183,7 @@ async def msg_list2msg( for i in range(got_num): index = i+1 nickname,chatdatanum = ranking[i] - str_example = plugin_config.dialectlist_string_format.format(index=index,nickname=nickname,chatdatanum=chatdatanum) + str_example = plugin_config.dialectlist_string_format.format(index=index,nickname=nickname,chatDataNum=chatdatanum) out = out + str_example logger.debug(out) From 4aa2ebce29905142e277e5a68a0a488274491f25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Skirt=E2=9C=BF?= <91937041+X-Skirt-X@users.noreply.github.com> Date: Mon, 3 Oct 2022 20:32:39 +0800 Subject: [PATCH 3/3] Update README.md --- README.md | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 079e4f0..100f897 100644 --- a/README.md +++ b/README.md @@ -9,14 +9,27 @@ # 话痨排行榜 nonebot-plugin-dialectlist -\>📄看看群友们这些天在群里水了多少话📄< +\>💬**看看群友们这些天在群里水了多少话**💬< ## 版本 + +### V1.0 + + - 看看群里群友能有多话痨 + +### V1.1 + + - 支持频道咯!(*^_^*) + +### V1.2 + + - 排行榜可视化 + +### V1.3 + - 添加了一些可配置项 -v1.0 支持频道咯!(*^_^*) - -⚠ 适配nonebot2-2.0.0b5+ +### ⚠ 适配nonebot2-2.0.0b5+ ## 安装 @@ -44,11 +57,16 @@ v1.0 支持频道咯!(*^_^*) 在环境配置中,可以添加以下配置项 ```python -dialectlist_string_format: str = '第{index}名:\n{nickname},{chatdatanum}条消息\n' #格式 -dialectlist_string_suffix_format: str = '\n你们的职业是水群吗?————MYX\n计算花费时间:{timecost}秒' #后缀字符 -dialectlist_get_num:int = 10 #排行榜长度 + dialectlist_string_format: str = '第{index}名:\n{nickname},{chatDataNum}条消息\n' #消息格式 + dialectlist_string_suffix_format: str = '你们的职业是水群吗?————MYX\n计算花费时间:{timeCost}秒' #消息后缀格式 + dialectlist_get_num:int = 10 #获取人数数量 + dialectlist_visualization:bool = True #是否可视化 + dialectlist_visualization_type:Literal['饼图','圆环图','柱状图'] = '圆环图' #可视化方案 + dialectlist_font:str = 'SimHei'#字体格式 + dialectlist_excluded_people:List[str] = []#排除的人的QQ号(或频道号?(未经测试)) + dialectlist_excluded_self:bool = True #是否排除机器人自己QQ ``` -也可以完全不配置,将会按照默认配置走 +💭也可以不进行配置,这将会使插件按照默认配置运行 ## 命令 @@ -67,8 +85,6 @@ dialectlist_get_num:int = 10 #排行榜长度 -`/历史群话痨排行榜` ————看看历史上(机器人存在以来)的群友发了多少消息! -**注意:频道内不管任何一个都是查看有史以来的消息(储存时间的不好写(以后会写的!))** - ## 另外 ### 感谢 @@ -81,16 +97,10 @@ dialectlist_get_num:int = 10 #排行榜长度 ## TODO 1. 私聊的查询 ~~(让我先咕一会)~~ - -2. 可视化排行榜 - -3. 优化代码结构(?)(看起来非常乱了) ## 已知BUG - -### 频道中同名的人会出现计数重复 - - 这是因为!!因为chatrecorder其实不支持频道消息,所有我写了一个保存在json中的版本,然后频道api怎么样都通过不了user_id获取nickname,于是我就放弃了(悲) - - *今后会试试~~写一个~~适配频道的chatrecorder*((((( + +1. 字体可能要自己配置(bug?) # **学业问题可能只有周末才能看iusse和更新插件**