mirror of
https://github.com/ChenXu233/nonebot_plugin_dialectlist.git
synced 2024-11-27 17:45:07 +08:00
commit
d8ac05d5ae
26
README.md
26
README.md
@ -25,6 +25,10 @@ nonebot-plugin-dialectlist
|
||||
### V1.2
|
||||
|
||||
- 排行榜可视化
|
||||
|
||||
### V1.3
|
||||
|
||||
- 添加了一些可配置项
|
||||
|
||||
### ⚠ 适配nonebot2-2.0.0b5+
|
||||
|
||||
@ -54,10 +58,14 @@ nonebot-plugin-dialectlist
|
||||
|
||||
在环境配置中,可以添加以下配置项
|
||||
```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_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['饼图','圆环图','柱状图'] = '圆环图' #可视化方案
|
||||
dialectlist_font:str = 'SimHei'#字体格式
|
||||
dialectlist_excluded_people:List[str] = []#排除的人的QQ号(或频道号?(未经测试))
|
||||
dialectlist_excluded_self:bool = True #是否排除机器人自己QQ
|
||||
```
|
||||
💭也可以不进行配置,这将会使插件按照默认配置运行
|
||||
|
||||
@ -78,8 +86,6 @@ dialectlist_visualization:bool = True #是否开启可视化
|
||||
|
||||
-`/历史群话痨排行榜` ————看看历史上(机器人存在以来)的群友发了多少消息!
|
||||
|
||||
**注意:频道内不管任何一个都是查看有史以来的消息(储存时间的不好写(以后会写的!))**
|
||||
|
||||
## 另外
|
||||
|
||||
### 感谢
|
||||
@ -92,14 +98,10 @@ dialectlist_visualization:bool = True #是否开启可视化
|
||||
## TODO
|
||||
|
||||
1. 私聊的查询 ~~(让我先咕一会)~~
|
||||
|
||||
2. 优化代码结构(?)(看起来非常乱了)
|
||||
|
||||
## 已知BUG
|
||||
|
||||
### 频道中同名的人会出现计数重复
|
||||
- 这是因为!!因为chatrecorder其实不支持频道消息,所有我写了一个保存在json中的版本,然后频道api怎么样都通过不了user_id获取nickname,于是我就放弃了(悲)
|
||||
- *今后会试试~~写一个~~适配频道的chatrecorder*(((((
|
||||
|
||||
1. 字体可能要自己配置(bug?)
|
||||
|
||||
#
|
||||
**学业问题可能只有周末才能看iusse和更新插件**
|
||||
|
@ -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,26 +165,41 @@ 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):
|
||||
|
||||
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 = await get_message_records(
|
||||
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"))
|
||||
)
|
||||
msg = await msg_counter(gid=event.group_id, bot=bot, msg=msg,got_num=plugin_config.dialectlist_get_num)
|
||||
|
||||
elif isinstance(event, GuildMessageEvent):
|
||||
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=plugin_config.dialectlist_excluded_people,
|
||||
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) #让图片先发出来
|
||||
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))
|
||||
|
@ -1,21 +1,19 @@
|
||||
from typing import Optional
|
||||
from typing import Optional, Literal, List
|
||||
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['饼图','圆环图','柱状图'] = '圆环图' #可视化方案
|
||||
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)
|
||||
plugin_config = Config(**global_config.dict())
|
@ -1,4 +1,3 @@
|
||||
import time
|
||||
import pygal
|
||||
import unicodedata
|
||||
|
||||
@ -7,8 +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
|
||||
@ -16,10 +13,11 @@ 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
|
||||
style=Style(font_family=plugin_config.dialectlist_font)
|
||||
|
||||
|
||||
def remove_control_characters(string:str) -> str:
|
||||
"""将字符串中的控制符去除
|
||||
@ -32,6 +30,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,86 +95,97 @@ 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
|
||||
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)
|
||||
logger.info('spent {} seconds to count from {} msg'.format(time.time()-st,msg_len))
|
||||
|
||||
return Message(out)+process_msg
|
@ -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)
|
Loading…
Reference in New Issue
Block a user