15 Commits

Author SHA1 Message Date
XuChenXu
dfde832dba 📝 fix readme 2024-11-10 21:11:58 +08:00
enKl03B
987b34e282 🔀 📝 Update README.md (#44) 2024-11-10 21:03:01 +08:00
XuChenXu
49ce2ffb7c 🔖 Version 2.4.5 2024-11-09 11:28:13 +08:00
XuChenXu
fcce94250c 🔀 Merge pull request #43 from ChenXu233/feat#20#39
Feat#20#39
2024-11-09 11:25:20 +08:00
XuChenXu
53e52eabb5 🔀 Merge branch 'master' into feat#20#39 2024-11-09 11:24:16 +08:00
XuChenXu
633ffbde9a 🎨 格式化代码 2024-11-09 11:20:53 +08:00
XuChenXu
d7baa9b04b 🔖 Version 2.4.4 2024-11-08 23:31:25 +08:00
XuChenXu
df2cd495c0 🔀 Merge pull request #41 from ChenXu233:feat#40
 新增是否显示文本排行榜功能
2024-11-08 23:29:34 +08:00
XuChenXu
d2e185c189 新增是否显示文本排行榜功能 2024-11-08 23:28:32 +08:00
XuChenXu
d42140cc95 初步实现:关键词筛选后排行 #20 2024-11-02 14:15:24 +08:00
XuChenXu
a753305282 🔖 version 2.4.3 2024-11-02 00:31:45 +08:00
XuChenXu
ad5f556229 ⬇️ 限制python为3.12以下(3.13依赖不支持) 2024-11-02 00:30:59 +08:00
Chenric
3efaafe81c Merge branch 'master' of https://github.com/ChenXu233/nonebot_plugin_dialectlist 2024-10-01 10:23:36 +08:00
Chenric
e62f2ed488 Merge branch 'master' of https://github.com/ChenXu233/nonebot_plugin_dialectlist 2024-09-30 22:00:44 +08:00
Chenric
e5d107c520 💄 性别显示 2024-09-30 22:00:37 +08:00
9 changed files with 146 additions and 163 deletions

View File

@@ -26,15 +26,16 @@ nonebot-plugin-dialectlist
## 💿 安装
通过`pip``nb`安装
通过`pip``nb`安装
- 通过 pip 安装
```bash
pip install nonebot-plugin-dialectlist
```
- 通过 nb-cli 安装
```bash
nb plugin install nonebot-plugin-dialectlist
```
>**通过 pip **安装
`pip install nonebot-plugin-dialectlist`
>**通过 nb **安装
`nb plugin install nonebot-plugin-dialectlist`
### ✅ 插件依赖于
@@ -96,9 +97,17 @@ __注意__
### 🚀进阶用法
`/{时间类型(今日|年度)?}{B话榜|废话榜} {时间类型?} {ISO8601 格式时间} {群号}`
`/{时间类型(今日|年度)?}{B话榜|废话榜} {时间类型?} {ISO8601 格式时间 ?} {群号} {关键词}`
如:`/B话榜 历史 2024-01-01~2024-01-02 12345678`
如:`/B话榜 历史 2024-01-01~2024-01-02 12345678 女装`
也可以 `/{时间类型(今日|年度)?}{B话榜|废话榜} {时间类型?} {ISO8601 格式时间 ?} -g {群号} -k {关键词}`
以下调用方法均合法:
`/今日B话榜 -g 12345678 -k 女装`
`/昨日B话榜 -k 女装`
`/本周B话榜 -g 12345678`
## 💪 目前支持的平台
@@ -158,14 +167,19 @@ __注意__
- [x] 私聊的查询(超级用户可以任意查询群聊的信息)一半完成
- [ ] 特殊的储存方案优化消息统计
- [x] 特殊的储存方案优化消息统计
- [ ] 查询带某关键词的消息量
- [x] 查询带某关键词的消息量
- [ ] 合并转发
待补充。.....
## 📖版本
## 📖版本日志
<details>
<summary>点我展开</summary>
### V1.0
- 看看群里群友能有多话痨
@@ -199,5 +213,11 @@ __注意__
- 优化代码,添加一些新的可配置项。
### V2.4
- 添加一些新的可配置项。
</details>
### 👾题外话
~~整个项目快被我写成屎山了~~

View File

@@ -31,7 +31,7 @@ from nonebot_plugin_alconna import (
from nonebot_plugin_chatrecorder import get_message_records
from nonebot_plugin_session import Session, SessionIdType, extract_session
from .storage import get_cache,build_cache
from .storage import get_cache, build_cache
from .config import Config, plugin_config
from .usage import __usage__
from .time import (
@@ -74,14 +74,17 @@ def wrapper(slot: Union[int, str], content: Optional[str], context) -> str:
return content
return "" # pragma: no cover
build_cache_cmd = on_command("build_cache", aliases={"重建缓存"}, block=True)
@build_cache_cmd.handle()
async def _build_cache(bot: Bot, event: Event):
await saa.Text("正在重建缓存,请稍等。").send(reply=True)
await build_cache()
await saa.Text("重建缓存完成。").send(reply=True)
rank_cmd = on_alconna(
Alconna(
"B话榜",
@@ -90,6 +93,7 @@ rank_cmd = on_alconna(
str,
],
Option("-g|--group_id", Args["group_id?", str]),
Option("-k|--keyword", Args["keyword?", str]),
behaviors=[SameTime()],
),
aliases={"废话榜"},
@@ -126,6 +130,7 @@ async def _group_message(
type: Optional[str] = None,
time: Optional[str] = None,
group_id: Optional[str] = None,
keyword: Optional[str] = None,
):
t1 = t.time()
state["t1"] = t1
@@ -137,6 +142,8 @@ async def _group_message(
if group_id:
state["group_id"] = group_id
state["keyword"] = keyword
if not type:
await rank_cmd.finish(__plugin_meta__.usage)
@@ -223,7 +230,11 @@ async def handle_rank(
if not id:
await saa.Text("没有指定群哦").finish()
keyword = state["keyword"]
if plugin_config.counting_cache:
if keyword:
await saa.Text("已开启缓存~缓存不支持关键词查询哦").finish()
t1 = t.time()
raw_rank = await get_cache(start, stop, id)
logger.debug(f"获取计数消息花费时间:{t.time() - t1}")
@@ -239,11 +250,13 @@ async def handle_rank(
time_stop=stop,
exclude_id1s=plugin_config.excluded_people,
)
raw_rank = msg_counter(messages)
raw_rank = msg_counter(messages, keyword)
logger.debug(f"获取计数消息花费时间:{t.time() - t1}")
if not raw_rank:
await saa.Text("明明这个时间段都没有人说话怎么会有话痨榜呢?").finish()
await saa.Text(
"没有获取到排行榜数据哦,请确认时间范围和群号是否正确或者关键词是否存在~"
).finish()
rank = got_rank(raw_rank)
ids = await persist_id2user_id([int(i[0]) for i in rank])
@@ -254,30 +267,38 @@ async def handle_rank(
t1 = t.time()
rank2 = await get_user_infos(bot, event, rank)
logger.debug(f"获取用户信息花费时间:{t.time() - t1}")
string: str = ""
for i in rank2:
logger.debug(i.user_name)
for i in range(len(rank2)):
str_example = plugin_config.string_format.format(
index=rank2[i].user_index,
nickname=rank2[i].user_nickname,
chatdatanum=rank2[i].user_bnum,
)
string += str_example
if plugin_config.show_text_rank:
if keyword:
string += f"关于{keyword}的话痨榜结果:\n"
else:
string += "话痨榜:\n"
for i in rank2:
logger.debug(i.user_name)
for i in range(len(rank2)):
str_example = plugin_config.string_format.format(
index=rank2[i].user_index,
nickname=rank2[i].user_nickname,
chatdatanum=rank2[i].user_bnum,
)
string += str_example
msg = saa.Text(string)
t1 = t.time()
if plugin_config.visualization:
t1 = t.time()
image = await get_rank_image(rank2)
msg += saa.Image(image)
logger.debug(f"群聊消息渲染图片花费时间:{t.time() - t1}")
if plugin_config.suffix:
timecost = t.time() - state["t1"]
suffix = saa.Text(plugin_config.string_suffix.format(timecost=timecost))
msg += suffix
logger.debug(f"群聊消息渲染图片花费时间:{t.time() - t1}")
if not msg:
await saa.Text("你把可视化都关了哪来的排行榜?").finish()
await msg.finish(reply=True)

View File

@@ -9,8 +9,10 @@ class ScopedConfig(BaseModel):
suffix: bool = True # 是否显示后缀
excluded_self: bool = True # 是否排除自己
visualization: bool = True # 是否可视化
show_text_rank:bool = True # 是否显示文本排名
counting_cache: bool = False # 计数缓存(能够提高回复速度)
excluded_people: List[str] = [] # 排除的人的QQ号
use_user_info_cache: bool = False # 是否使用用户信息缓存
timezone: Optional[str] = "Asia/Shanghai" # 时区,影响统计时间
string_suffix: str = "统计花费时间:{timecost}" # 消息格式后缀
template_path: str = "./template/rank_template.j2" # 模板路径

View File

@@ -6,7 +6,7 @@ from sqlalchemy import delete, or_, select
from nonebot import get_driver
from nonebot.log import logger
from nonebot.params import Depends
from nonebot.adapters import Event,Bot
from nonebot.adapters import Event, Bot
from nonebot.message import event_postprocessor
from .model import MessageCountCache
@@ -56,7 +56,9 @@ async def build_cache():
where.append(
or_(
MessageCountCache.time
== remove_timezone(msg.time.replace(hour=1, minute=0, second=0, microsecond=0))
== remove_timezone(
msg.time.replace(hour=1, minute=0, second=0, microsecond=0)
)
)
)
statement = select(MessageCountCache).where(*where)
@@ -68,7 +70,9 @@ async def build_cache():
else:
user_cache = MessageCountCache(
session_id=msg.session_persist_id,
time=remove_timezone(msg.time.replace(hour=1, minute=0, second=0, microsecond=0)),
time=remove_timezone(
msg.time.replace(hour=1, minute=0, second=0, microsecond=0)
),
session_bnum=1,
)
db_session.add(user_cache)
@@ -100,7 +104,7 @@ async def _():
@event_postprocessor
async def _(bot: Bot, event: Event,session: Session = Depends(extract_session)):
async def _(bot: Bot, event: Event, session: Session = Depends(extract_session)):
if not plugin_config.counting_cache:
return
if not session.id2:
@@ -112,7 +116,7 @@ async def _(bot: Bot, event: Event,session: Session = Depends(extract_session)):
async with get_session() as db_session:
session_id = await get_session_persist_id(session)
logger.debug("session_id:"+str(session_id))
logger.debug("session_id:" + str(session_id))
where = [or_(MessageCountCache.session_id == session_id)]
where.append(or_(MessageCountCache.time == remove_timezone(now)))
statement = select(MessageCountCache).where(*where)

View File

@@ -2,8 +2,42 @@ from inspect import cleandoc
__usage__ = cleandoc(
"""
/今日B话榜 查看今天群里谁B话最多。
/日B话榜 顾名思义
以此类推,有本周,本月,上周,上月,年度排行榜。
快速调用:
/日B话榜 ————看看今天群友发了多少消息
### 🎨一般用法
-`/B话榜` ————看看有史以来(机器人存在以来)群友们发了多少消息! (好像没写)
-`/今日B话榜` ————看看今天的群友发了多少消息!
-`/昨日B话榜` ————看看昨天的群友发了多少消息!
-`/前日B话榜` ————看看前天的群友发了多少消息!
-`/本周B话榜` ————看看本周的群友发了多少消息!
-`/上周B话榜` ————看看上周的群友发了多少消息!
-`/本月B话榜` ————看看这个月的群友发了多少消息!
-`/年度B话榜` ————看看今年的群友发了多少消息!
-`/历史B话榜` ————看看历史上(机器人存在以来)的群友发了多少消息!
### 🚀进阶用法
`/{时间类型(今日|年度)?}{B话榜|废话榜} {时间类型?} {ISO8601 格式时间 ?} {群号} {关键词}`
如:`/B话榜 历史 2024-01-01~2024-01-02 12345678 女装`
也可以 `/{时间类型(今日|年度)?}{B话榜|废话榜} {时间类型?} {ISO8601 格式时间 ?} -g {群号} -k {关键词}`
以下调用方法均合法:
`/今日B话榜 -g 12345678 -k 女装`
`/昨日B话榜 -k 女装`
`/本周B话榜 -g 12345678`
"""
)

View File

@@ -3,7 +3,7 @@ import httpx
import asyncio
import unicodedata
from typing import Dict, List
from typing import Dict, List, Optional
from sqlalchemy import or_, select
from sqlalchemy.sql import ColumnElement
@@ -77,7 +77,9 @@ async def persist_id2group_id(ids: List[str]) -> List[str]:
return [i.id2 for i in records]
def msg_counter(msg_list: List[MessageRecord]) -> Dict[str, int]:
def msg_counter(
msg_list: List[MessageRecord], keyword: Optional[str]
) -> Dict[str, int]:
"""### 计算每个人的消息量
Args:
@@ -92,6 +94,10 @@ def msg_counter(msg_list: List[MessageRecord]) -> Dict[str, int]:
logger.info("wow , there are {} msgs to count !!!".format(msg_len))
for i in msg_list:
logger.debug(f"processing msg {i.plain_text}")
if keyword:
if keyword not in i.plain_text:
continue
try:
lst[str(i.session_persist_id)] += 1
except KeyError:
@@ -194,6 +200,7 @@ async def _get_user_default_avatar() -> bytes:
).read()
return img
async def _get_user_avatar(user: UserInfo, client: httpx.AsyncClient) -> bytes:
if not user.user_avatar:
return await _get_user_default_avatar()
@@ -208,6 +215,7 @@ async def _get_user_avatar(user: UserInfo, client: httpx.AsyncClient) -> bytes:
await asyncio.sleep(3)
raise NetworkError(f"{url} 下载失败!")
def get_default_user_info() -> UserInfo:
user_info = UserInfo(
user_id="114514",
@@ -215,14 +223,18 @@ def get_default_user_info() -> UserInfo:
)
return user_info
async def get_user_infos(
bot: Bot, event: Event, rank: List, use_cache: bool = True
bot: Bot,
event: Event,
rank: List,
use_cache: bool = plugin_config.use_user_info_cache,
) -> List[UserRankInfo]:
user_ids = [i[0] for i in rank]
pool = [get_user_info(bot, event, id, use_cache) for id in user_ids]
user_infos = await asyncio.gather(*pool)
async with httpx.AsyncClient() as client:
pool = []
for i in user_infos:
@@ -253,11 +265,13 @@ async def get_user_infos(
user_nickname=_get_user_nickname(user_info),
user_avatar_bytes=user_avatars[i],
)
user.user_gender = (
""
if user.user_gender == "male"
else "" if user.user_gender == "female" else ""
)
print(user.user_gender)
if user.user_gender == "male":
user.user_gender = ""
elif user.user_gender == "female":
user.user_gender = ""
else:
user.user_gender = "🤔"
rank2.append(user)
return rank2

4
pdm.lock generated
View File

@@ -5,10 +5,10 @@
groups = ["default", "Test", "dev"]
strategy = ["inherit_metadata"]
lock_version = "4.5.0"
content_hash = "sha256:f31fbbd00506680ed623e0216a48314dcbb142cebec25ebe15f1dc9a50915b25"
content_hash = "sha256:882dfc18d7454ced6bb1f46e04172b14b83f90157475153bf0fff7b13e4d41a2"
[[metadata.targets]]
requires_python = "~=3.9"
requires_python = ">=3.9,<3.13"
[[package]]
name = "aiofiles"

View File

@@ -1,6 +1,6 @@
[project]
name = "nonebot-plugin-dialectlist"
version = "2.4.2"
version = "2.4.5"
description = "看看你群群友有多能说"
authors = [
{name = "Chen_Xu233", email = "woyerpa@outlook.com"},
@@ -18,7 +18,7 @@ dependencies = [
"pillow>=10.4.0",
"nonebot-plugin-uninfo>=0.1.1",
]
requires-python = ">=3.9,<4.0"
requires-python = ">=3.9,<3.13"
readme = "README.md"
license = {text = "MIT"}

View File

@@ -1,112 +0,0 @@
import PIL
import abc
from typing import List, Tuple, Union, Iterable
from PIL import Image, ImageDraw, ImageFont
class BaseElement(abc.ABC):
def __init__(self, box:Union[Iterable[int],Iterable[float]] = (0, 0, 0, 0)):
self.box = [float(i) for i in box]
self.ux = None
self.ly = None
self.dx = None
self.ry = None
self.hight = None
self.width = None
self.image = None
self.position = (self.ux, self.ly)
self.size = (self.width, self.hight)
@abc.abstractmethod
def render(self):
raise NotImplementedError
def get_box(self):
return self.box
def set_box(self, box:Tuple[float, float, float, float]):
self.box = box
def get_size(self):
self.size = (self.width, self.hight)
return self.size
def get_position(self):
self.position = (self.ux, self.ly)
return self.position
class Board(BaseElement):
def __init__(self, width, hight):
self.width = width
self.hight = hight
self.image = Image.new('RGBA', (self.width, self.hight), (255, 255, 255, 0))
self.elements = []
def render(self):
for i in self.elements:
i.ux = self.width * i.box[0]
i.ly = self.hight * i.box[1]
i.dx = self.width * i.box[2]
i.ry = self.hight * i.box[3]
i.hight = i.dx - i.ux
i.width = i.ry - i.ly
i.position = (i.ux, i.ly)
image = i.render()
self.image.paste(image, i.get_position())
def add_element(self, element:BaseElement):
self.elements.append(element)
class Container(BaseElement):
def __init__(self, box:Union[Iterable[int],Iterable[float]] = (0, 0, 0, 0)):
super().__init__(box)
self.elements:List[BaseElement] = []
def render(self,size:Tuple[int, int] = (100, 100)):
if not self.width and self.hight:
self.width = size[0]
self.hight = size[1]
if not self.image:
self.image = Image.new('RGBA', (self.width, self.hight), (255, 255, 255, 0))
for i in self.elements:
image = i.render()
self.image.paste(image, i.get_position())
class Element(BaseElement):
# class BaseContainer(abc.ABC):
# def __init__(self, width:int, height:int):
# self.width = width
# self.height = height
# self.image = Image.new('RGBA', (self.width, self.height), (255, 255, 255, 0))
# @abc.abstractmethod
# def render(self):
# raise NotImplementedError
# def set_image(self,image):
# self.image = image
# class Elements():
# def __init__(self, width:int, height:int):
# self.width = width
# self.height = height
# self.image = Image.new('RGBA', (self.width, self.height), (255, 255, 255, 0))
# def render(self):
# return self.image
# class Container(BaseContainer):
# def __init__(self, width:int, height:int,elements:List[Union['Container',Elements]] = []):
# super().__init__(width, height)
# self.elements = elements
# def render(self):
# for i in self.elements:
# image = i.render()
# self.image.paste(image, i.position)
# return self.image