mirror of
https://github.com/ChenXu233/nonebot_plugin_dialectlist.git
synced 2026-01-26 05:32:10 +00:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07f85be527 | ||
|
|
f2cdfe7f8a | ||
|
|
e0ca7907c0 | ||
|
|
53b2a33923 | ||
|
|
4e339bcdb1 | ||
|
|
0b85f6fb34 | ||
|
|
83d9cda2eb | ||
|
|
dfde832dba | ||
|
|
987b34e282 | ||
|
|
49ce2ffb7c | ||
|
|
fcce94250c | ||
|
|
53e52eabb5 | ||
|
|
633ffbde9a | ||
|
|
d7baa9b04b | ||
|
|
df2cd495c0 | ||
|
|
d2e185c189 | ||
|
|
d42140cc95 | ||
|
|
a753305282 | ||
|
|
ad5f556229 | ||
|
|
3efaafe81c | ||
|
|
c6b87bf981 | ||
|
|
e62f2ed488 | ||
|
|
e5d107c520 | ||
|
|
bf577b5a31 | ||
|
|
d514d96db6 | ||
|
|
677ec56aeb | ||
|
|
4194097258 | ||
|
|
c94de918e9 | ||
|
|
a765267cf1 | ||
|
|
ea682ef18d |
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -11,8 +11,10 @@
|
|||||||
"dialectlist",
|
"dialectlist",
|
||||||
"displayname",
|
"displayname",
|
||||||
"htmlrender",
|
"htmlrender",
|
||||||
|
"httpx",
|
||||||
"localstore",
|
"localstore",
|
||||||
"parameterless",
|
"parameterless",
|
||||||
|
"postprocessor",
|
||||||
"pyecharts",
|
"pyecharts",
|
||||||
"pygal",
|
"pygal",
|
||||||
"sqlalchemy",
|
"sqlalchemy",
|
||||||
|
|||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 ChenXu233
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
70
README.md
70
README.md
@@ -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`
|
|
||||||
|
|
||||||
### ✅ 插件依赖于
|
### ✅ 插件依赖于
|
||||||
|
|
||||||
@@ -76,6 +77,8 @@ __!!注意!!__
|
|||||||
|
|
||||||
### 🎨一般用法
|
### 🎨一般用法
|
||||||
|
|
||||||
|
#### B话榜
|
||||||
|
|
||||||
-`/B话榜` ————看看有史以来(机器人存在以来)群友们发了多少消息! (好像没写)
|
-`/B话榜` ————看看有史以来(机器人存在以来)群友们发了多少消息! (好像没写)
|
||||||
|
|
||||||
-`/今日B话榜` ————看看今天的群友发了多少消息!
|
-`/今日B话榜` ————看看今天的群友发了多少消息!
|
||||||
@@ -94,11 +97,34 @@ __!!注意!!__
|
|||||||
|
|
||||||
-`/历史B话榜` ————看看历史上(机器人存在以来)的群友发了多少消息!
|
-`/历史B话榜` ————看看历史上(机器人存在以来)的群友发了多少消息!
|
||||||
|
|
||||||
|
#### 看看B话(kkb)
|
||||||
|
|
||||||
|
-`/看看B话 [@某人|QQ号]` ————看看这个b人在这个b群发了多少b话!
|
||||||
|
|
||||||
### 🚀进阶用法
|
### 🚀进阶用法
|
||||||
|
|
||||||
`/{时间类型(今日|年度)?}{B话榜|废话榜} {时间类型?} {ISO8601 格式时间} {群号}`
|
#### B话榜
|
||||||
|
|
||||||
如:`/B话榜 历史 2024-01-01~2024-01-02 12345678`
|
`/{时间类型(今日|年度)?}{B话榜|废话榜} {时间类型?} {ISO8601 格式时间 ?} {群号} {关键词}`
|
||||||
|
|
||||||
|
如:`/B话榜 历史 2024-01-01~2024-01-02 12345678 女装`
|
||||||
|
|
||||||
|
也可以 `/{时间类型(今日|年度)?}{B话榜|废话榜} {时间类型?} {ISO8601 格式时间 ?} -g {群号} -k {关键词}`
|
||||||
|
|
||||||
|
以下调用方法均合法:
|
||||||
|
|
||||||
|
`/今日B话榜 -g 12345678 -k 女装`
|
||||||
|
`/昨日B话榜 -k 女装`
|
||||||
|
`/本周B话榜 -g 12345678`
|
||||||
|
|
||||||
|
#### 看看B话
|
||||||
|
|
||||||
|
`/看看B话 {@|QQ号} {群号?} {关键词?}`
|
||||||
|
|
||||||
|
以下调用方法均合法:
|
||||||
|
|
||||||
|
`/kkb 114514 1919810 ♂`
|
||||||
|
`/kkb @man -k ♂`
|
||||||
|
|
||||||
## 💪 目前支持的平台
|
## 💪 目前支持的平台
|
||||||
|
|
||||||
@@ -134,6 +160,9 @@ __!!注意!!__
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
### 💕感谢
|
### 💕感谢
|
||||||
|
|
||||||
本插件的__init__.py 中的处理函数参考了词云中的方法 ~~(其实大部分都是 Ctrl+C Ctr+V)~~
|
本插件的__init__.py 中的处理函数参考了词云中的方法 ~~(其实大部分都是 Ctrl+C Ctr+V)~~
|
||||||
@@ -158,19 +187,18 @@ __!!注意!!__
|
|||||||
|
|
||||||
- [x] 私聊的查询(超级用户可以任意查询群聊的信息)一半完成
|
- [x] 私聊的查询(超级用户可以任意查询群聊的信息)一半完成
|
||||||
|
|
||||||
- [ ] 提供多样化的渲染器配置(html 渲染,pillow 渲染,统计绘图软件渲染)
|
- [x] 特殊的储存方案优化消息统计
|
||||||
|
|
||||||
- [ ] 使用管理员权限直接获取 QQ 官方统计的今日消息量以优化代码运行速度
|
- [x] 查询带某关键词的消息量
|
||||||
|
|
||||||
- [ ] 为 pillow 渲染方式提供插件的加载方式(什么?插件里的插件???)
|
- [x] 合并转发
|
||||||
|
|
||||||
- [ ] 特殊的储存方案优化消息统计
|
|
||||||
|
|
||||||
- [ ] 查询带某关键词的消息量
|
|
||||||
|
|
||||||
待补充。.....
|
待补充。.....
|
||||||
|
|
||||||
## 📖版本
|
## 📖版本日志
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>点我展开</summary>
|
||||||
|
|
||||||
### V1.0
|
### V1.0
|
||||||
|
|
||||||
@@ -205,5 +233,11 @@ __!!注意!!__
|
|||||||
|
|
||||||
- 优化代码,添加一些新的可配置项。
|
- 优化代码,添加一些新的可配置项。
|
||||||
|
|
||||||
|
### V2.4
|
||||||
|
|
||||||
|
- 添加一些新的可配置项。
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
### 👾题外话
|
### 👾题外话
|
||||||
~~整个项目快被我写成屎山了~~
|
~~整个项目快被我写成屎山了~~
|
||||||
|
|||||||
@@ -16,13 +16,17 @@ from datetime import datetime, timedelta
|
|||||||
from arclet.alconna import ArparmaBehavior
|
from arclet.alconna import ArparmaBehavior
|
||||||
from arclet.alconna.arparma import Arparma
|
from arclet.alconna.arparma import Arparma
|
||||||
|
|
||||||
|
from nonebot import on_command
|
||||||
from nonebot.log import logger
|
from nonebot.log import logger
|
||||||
from nonebot.typing import T_State
|
from nonebot.typing import T_State
|
||||||
from nonebot.params import Arg, Depends
|
from nonebot.params import Arg, Depends
|
||||||
from nonebot.adapters import Bot, Event
|
from nonebot.adapters import Bot, Event
|
||||||
from nonebot.plugin import PluginMetadata, inherit_supported_adapters
|
from nonebot.plugin import PluginMetadata, inherit_supported_adapters
|
||||||
from nonebot_plugin_alconna import (
|
from nonebot_plugin_alconna import (
|
||||||
|
At,
|
||||||
Args,
|
Args,
|
||||||
|
Field,
|
||||||
|
Match,
|
||||||
Option,
|
Option,
|
||||||
Alconna,
|
Alconna,
|
||||||
on_alconna,
|
on_alconna,
|
||||||
@@ -30,8 +34,7 @@ from nonebot_plugin_alconna import (
|
|||||||
from nonebot_plugin_chatrecorder import get_message_records
|
from nonebot_plugin_chatrecorder import get_message_records
|
||||||
from nonebot_plugin_session import Session, SessionIdType, extract_session
|
from nonebot_plugin_session import Session, SessionIdType, extract_session
|
||||||
|
|
||||||
# from . import migrations #抄词云的部分代码,还不知道这有什么用
|
from .storage import get_cache, build_cache
|
||||||
# from .function import *
|
|
||||||
from .config import Config, plugin_config
|
from .config import Config, plugin_config
|
||||||
from .usage import __usage__
|
from .usage import __usage__
|
||||||
from .time import (
|
from .time import (
|
||||||
@@ -45,7 +48,6 @@ from .utils import (
|
|||||||
get_rank_image,
|
get_rank_image,
|
||||||
persist_id2user_id,
|
persist_id2user_id,
|
||||||
get_user_infos,
|
get_user_infos,
|
||||||
# get_user_info2,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
@@ -58,7 +60,6 @@ __plugin_meta__ = PluginMetadata(
|
|||||||
"nonebot_plugin_chatrecorder", "nonebot_plugin_saa", "nonebot_plugin_alconna"
|
"nonebot_plugin_chatrecorder", "nonebot_plugin_saa", "nonebot_plugin_alconna"
|
||||||
),
|
),
|
||||||
config=Config,
|
config=Config,
|
||||||
# extra={"orm_version_location": migrations},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -77,6 +78,65 @@ def wrapper(slot: Union[int, str], content: Optional[str], context) -> str:
|
|||||||
return "" # pragma: no cover
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
b_cmd = on_alconna(
|
||||||
|
Alconna(
|
||||||
|
"看看B话",
|
||||||
|
Args["at", [str, At], Field(completion=lambda: "请想要查询的人的QQ号")],
|
||||||
|
Option("-g|--group_id", Args["group_id?", str]),
|
||||||
|
Option("-k|--keyword", Args["keyword?", str])
|
||||||
|
),
|
||||||
|
aliases={"kkb"},
|
||||||
|
use_cmd_start=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
@b_cmd.handle()
|
||||||
|
async def handle_b_cmd(
|
||||||
|
at:Match[str|At],
|
||||||
|
group_id: Match[str],
|
||||||
|
keyword: Match[str],
|
||||||
|
session: Session = Depends(extract_session),
|
||||||
|
):
|
||||||
|
id = at.result
|
||||||
|
if isinstance(id, At):
|
||||||
|
id = id.target
|
||||||
|
if group_id.available:
|
||||||
|
gid = group_id.result
|
||||||
|
else:
|
||||||
|
gid = session.id2
|
||||||
|
|
||||||
|
if not gid:
|
||||||
|
await b_cmd.finish("请指定群号。")
|
||||||
|
|
||||||
|
if keyword.available:
|
||||||
|
keywords = keyword.result
|
||||||
|
else:
|
||||||
|
keywords = None
|
||||||
|
|
||||||
|
messages = await get_message_records(
|
||||||
|
id1s=[id],
|
||||||
|
id2s=[gid],
|
||||||
|
id_type=SessionIdType.GROUP,
|
||||||
|
include_bot_id=False,
|
||||||
|
include_bot_type=False,
|
||||||
|
types=["message"], # 排除机器人自己发的消息
|
||||||
|
exclude_id1s=plugin_config.excluded_people,
|
||||||
|
)
|
||||||
|
d = msg_counter(messages, keywords)
|
||||||
|
rank = got_rank(d)
|
||||||
|
|
||||||
|
await saa.Text(f"该用户在群{gid}的B话数量为{rank[0][1]}。").send(reply=True)
|
||||||
|
|
||||||
|
|
||||||
rank_cmd = on_alconna(
|
rank_cmd = on_alconna(
|
||||||
Alconna(
|
Alconna(
|
||||||
"B话榜",
|
"B话榜",
|
||||||
@@ -85,6 +145,7 @@ rank_cmd = on_alconna(
|
|||||||
str,
|
str,
|
||||||
],
|
],
|
||||||
Option("-g|--group_id", Args["group_id?", str]),
|
Option("-g|--group_id", Args["group_id?", str]),
|
||||||
|
Option("-k|--keyword", Args["keyword?", str]),
|
||||||
behaviors=[SameTime()],
|
behaviors=[SameTime()],
|
||||||
),
|
),
|
||||||
aliases={"废话榜"},
|
aliases={"废话榜"},
|
||||||
@@ -121,6 +182,7 @@ async def _group_message(
|
|||||||
type: Optional[str] = None,
|
type: Optional[str] = None,
|
||||||
time: Optional[str] = None,
|
time: Optional[str] = None,
|
||||||
group_id: Optional[str] = None,
|
group_id: Optional[str] = None,
|
||||||
|
keyword: Optional[str] = None,
|
||||||
):
|
):
|
||||||
t1 = t.time()
|
t1 = t.time()
|
||||||
state["t1"] = t1
|
state["t1"] = t1
|
||||||
@@ -132,6 +194,8 @@ async def _group_message(
|
|||||||
if group_id:
|
if group_id:
|
||||||
state["group_id"] = group_id
|
state["group_id"] = group_id
|
||||||
|
|
||||||
|
state["keyword"] = keyword
|
||||||
|
|
||||||
if not type:
|
if not type:
|
||||||
await rank_cmd.finish(__plugin_meta__.usage)
|
await rank_cmd.finish(__plugin_meta__.usage)
|
||||||
|
|
||||||
@@ -187,8 +251,6 @@ async def _group_message(
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
await rank_cmd.finish("请输入正确的日期,不然我没法理解呢!")
|
await rank_cmd.finish("请输入正确的日期,不然我没法理解呢!")
|
||||||
|
|
||||||
logger.debug(f"命令解析花费时间:{t.time() - t1}")
|
|
||||||
|
|
||||||
|
|
||||||
@rank_cmd.got(
|
@rank_cmd.got(
|
||||||
"start",
|
"start",
|
||||||
@@ -220,9 +282,16 @@ async def handle_rank(
|
|||||||
if not id:
|
if not id:
|
||||||
await saa.Text("没有指定群哦").finish()
|
await saa.Text("没有指定群哦").finish()
|
||||||
|
|
||||||
|
keyword = state["keyword"]
|
||||||
|
|
||||||
if plugin_config.counting_cache:
|
if plugin_config.counting_cache:
|
||||||
raise Exception("我草缓存功能还没端上来呢,你怎么就先用上了")
|
if keyword:
|
||||||
|
await saa.Text("已开启缓存~缓存不支持关键词查询哦").finish()
|
||||||
|
t1 = t.time()
|
||||||
|
raw_rank = await get_cache(start, stop, id)
|
||||||
|
logger.debug(f"获取计数消息花费时间:{t.time() - t1}")
|
||||||
else:
|
else:
|
||||||
|
t1 = t.time()
|
||||||
messages = await get_message_records(
|
messages = await get_message_records(
|
||||||
id2s=[id],
|
id2s=[id],
|
||||||
id_type=SessionIdType.GROUP,
|
id_type=SessionIdType.GROUP,
|
||||||
@@ -233,14 +302,15 @@ async def handle_rank(
|
|||||||
time_stop=stop,
|
time_stop=stop,
|
||||||
exclude_id1s=plugin_config.excluded_people,
|
exclude_id1s=plugin_config.excluded_people,
|
||||||
)
|
)
|
||||||
|
raw_rank = msg_counter(messages, keyword)
|
||||||
|
logger.debug(f"获取计数消息花费时间:{t.time() - t1}")
|
||||||
|
|
||||||
if not messages:
|
if not raw_rank:
|
||||||
await saa.Text("明明这个时间段都没有人说话怎么会有话痨榜呢?").finish()
|
await saa.Text(
|
||||||
|
"没有获取到排行榜数据哦,请确认时间范围和群号是否正确或者关键词是否存在~"
|
||||||
raw_rank = msg_counter(messages)
|
).finish()
|
||||||
|
|
||||||
rank = got_rank(raw_rank)
|
rank = got_rank(raw_rank)
|
||||||
logger.debug(rank)
|
|
||||||
ids = await persist_id2user_id([int(i[0]) for i in rank])
|
ids = await persist_id2user_id([int(i[0]) for i in rank])
|
||||||
for i in range(len(rank)):
|
for i in range(len(rank)):
|
||||||
rank[i][0] = str(ids[i])
|
rank[i][0] = str(ids[i])
|
||||||
@@ -251,6 +321,13 @@ async def handle_rank(
|
|||||||
logger.debug(f"获取用户信息花费时间:{t.time() - t1}")
|
logger.debug(f"获取用户信息花费时间:{t.time() - t1}")
|
||||||
|
|
||||||
string: str = ""
|
string: str = ""
|
||||||
|
if plugin_config.show_text_rank:
|
||||||
|
|
||||||
|
if keyword:
|
||||||
|
string += f"关于{keyword}的话痨榜结果:\n"
|
||||||
|
else:
|
||||||
|
string += "话痨榜:\n"
|
||||||
|
|
||||||
for i in rank2:
|
for i in rank2:
|
||||||
logger.debug(i.user_name)
|
logger.debug(i.user_name)
|
||||||
for i in range(len(rank2)):
|
for i in range(len(rank2)):
|
||||||
@@ -262,24 +339,21 @@ async def handle_rank(
|
|||||||
string += str_example
|
string += str_example
|
||||||
|
|
||||||
msg = saa.Text(string)
|
msg = saa.Text(string)
|
||||||
t1 = t.time()
|
|
||||||
|
|
||||||
if plugin_config.visualization:
|
if plugin_config.visualization:
|
||||||
|
t1 = t.time()
|
||||||
image = await get_rank_image(rank2)
|
image = await get_rank_image(rank2)
|
||||||
msg += saa.Image(image)
|
msg += saa.Image(image)
|
||||||
|
logger.debug(f"群聊消息渲染图片花费时间:{t.time() - t1}")
|
||||||
|
|
||||||
if plugin_config.suffix:
|
if plugin_config.suffix:
|
||||||
timecost = t.time() - state["t1"]
|
timecost = t.time() - state["t1"]
|
||||||
suffix = saa.Text(plugin_config.string_suffix.format(timecost=timecost))
|
suffix = saa.Text(plugin_config.string_suffix.format(timecost=timecost))
|
||||||
msg += suffix
|
msg += suffix
|
||||||
|
if not msg:
|
||||||
|
await saa.Text("你把可视化都关了哪来的排行榜?").finish()
|
||||||
|
|
||||||
logger.debug(f"群聊消息渲染图片花费时间:{t.time() - t1}")
|
if plugin_config.aggregate_transmission:
|
||||||
|
await saa.AggregatedMessageFactory([msg]).finish(reply=True)
|
||||||
|
else:
|
||||||
await msg.finish(reply=True)
|
await msg.finish(reply=True)
|
||||||
|
|
||||||
|
|
||||||
# @scheduler.scheduled_job(
|
|
||||||
# "dialectlist", day="*/2", id="xxx", args=[1], kwargs={"arg2": 2}
|
|
||||||
# )
|
|
||||||
# async def __():
|
|
||||||
# pass
|
|
||||||
|
|||||||
@@ -6,12 +6,15 @@ from nonebot import get_driver, get_plugin_config
|
|||||||
class ScopedConfig(BaseModel):
|
class ScopedConfig(BaseModel):
|
||||||
get_num: int = 5 # 获取人数数量
|
get_num: int = 5 # 获取人数数量
|
||||||
font: str = "SimHei" # 字体格式
|
font: str = "SimHei" # 字体格式
|
||||||
suffix: bool = False # 是否显示后缀
|
suffix: bool = True # 是否显示后缀
|
||||||
excluded_self: bool = True
|
excluded_self: bool = True # 是否排除自己
|
||||||
visualization: bool = True # 是否可视化
|
visualization: bool = True # 是否可视化
|
||||||
counting_cache: bool = False # 计数缓存(没有完工)
|
show_text_rank: bool = True # 是否显示文本排名
|
||||||
|
counting_cache: bool = False # 计数缓存(能够提高回复速度)
|
||||||
excluded_people: List[str] = [] # 排除的人的QQ号
|
excluded_people: List[str] = [] # 排除的人的QQ号
|
||||||
timezone: Optional[str] = "Asia/Shanghai"
|
use_user_info_cache: bool = False # 是否使用用户信息缓存
|
||||||
|
aggregate_transmission:bool = False # 是否聚合转发消息
|
||||||
|
timezone: Optional[str] = "Asia/Shanghai" # 时区,影响统计时间
|
||||||
string_suffix: str = "统计花费时间:{timecost}秒" # 消息格式后缀
|
string_suffix: str = "统计花费时间:{timecost}秒" # 消息格式后缀
|
||||||
template_path: str = "./template/rank_template.j2" # 模板路径
|
template_path: str = "./template/rank_template.j2" # 模板路径
|
||||||
string_format: str = "第{index}名:\n{nickname},{chatdatanum}条消息\n" # 消息格式
|
string_format: str = "第{index}名:\n{nickname},{chatdatanum}条消息\n" # 消息格式
|
||||||
|
|||||||
@@ -14,14 +14,9 @@ class UserRankInfo(UserInfo):
|
|||||||
user_avatar_bytes: bytes
|
user_avatar_bytes: bytes
|
||||||
|
|
||||||
|
|
||||||
# class MsgCountDayData(BaseModel):
|
|
||||||
# session_id: str
|
|
||||||
# session_bnum: int
|
|
||||||
|
|
||||||
|
|
||||||
class MessageCountCache(Model):
|
class MessageCountCache(Model):
|
||||||
__table_args__ = {"extend_existing": True}
|
__table_args__ = {"extend_existing": True}
|
||||||
id: Mapped[int] = mapped_column(primary_key=True)
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||||
time: Mapped[datetime]
|
time: Mapped[datetime]
|
||||||
session_id: Mapped[int] = mapped_column(Integer)
|
session_id: Mapped[int] = mapped_column(Integer, index=True)
|
||||||
session_bnum: Mapped[int] = mapped_column(Integer)
|
session_bnum: Mapped[int] = mapped_column(Integer)
|
||||||
|
|||||||
@@ -1,2 +1,134 @@
|
|||||||
# TODO 使用计数缓存进行数据库查询优化,避免一次性查询过多消息导致内存爆炸。
|
import os
|
||||||
from nonebot_plugin_orm import Model
|
import json
|
||||||
|
from datetime import datetime
|
||||||
|
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.message import event_postprocessor
|
||||||
|
|
||||||
|
from .model import MessageCountCache
|
||||||
|
from .config import plugin_config
|
||||||
|
|
||||||
|
from nonebot_plugin_localstore import get_data_file
|
||||||
|
from nonebot_plugin_chatrecorder import get_message_records
|
||||||
|
from nonebot_plugin_chatrecorder.utils import remove_timezone
|
||||||
|
from nonebot_plugin_session import extract_session, Session
|
||||||
|
from nonebot_plugin_session_orm import SessionModel, get_session_persist_id
|
||||||
|
from nonebot_plugin_orm import get_session
|
||||||
|
|
||||||
|
|
||||||
|
async def get_cache(time_start: datetime, time_stop: datetime, group_id: str):
|
||||||
|
async with get_session() as db_session:
|
||||||
|
where = [or_(SessionModel.id2 == group_id)]
|
||||||
|
statement = select(SessionModel).where(*where)
|
||||||
|
|
||||||
|
sessions = (await db_session.scalars(statement)).all()
|
||||||
|
|
||||||
|
where = [
|
||||||
|
or_(*[MessageCountCache.session_id == session.id for session in sessions])
|
||||||
|
]
|
||||||
|
statement = select(MessageCountCache).where(*where)
|
||||||
|
where.append(or_(MessageCountCache.time >= remove_timezone(time_start)))
|
||||||
|
where.append(or_(MessageCountCache.time <= remove_timezone(time_stop)))
|
||||||
|
statement = select(MessageCountCache).where(*where)
|
||||||
|
|
||||||
|
user_caches = (await db_session.scalars(statement)).all()
|
||||||
|
raw_rank = {}
|
||||||
|
for i in user_caches:
|
||||||
|
raw_rank[i.session_id] = raw_rank.get(i.session_id, 0) + i.session_bnum
|
||||||
|
return raw_rank
|
||||||
|
|
||||||
|
|
||||||
|
async def build_cache():
|
||||||
|
async with get_session() as db_session:
|
||||||
|
await db_session.execute(delete(MessageCountCache))
|
||||||
|
await db_session.commit()
|
||||||
|
logger.info("先前可能存在的缓存已清空")
|
||||||
|
messages = await get_message_records(types=["message"])
|
||||||
|
async with get_session() as db_session:
|
||||||
|
for msg in messages:
|
||||||
|
msg_session_id = msg.session_persist_id
|
||||||
|
|
||||||
|
where = [or_(MessageCountCache.session_id == msg_session_id)]
|
||||||
|
where.append(
|
||||||
|
or_(
|
||||||
|
MessageCountCache.time
|
||||||
|
== remove_timezone(
|
||||||
|
msg.time.replace(hour=1, minute=0, second=0, microsecond=0)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
statement = select(MessageCountCache).where(*where)
|
||||||
|
|
||||||
|
user_cache = (await db_session.scalars(statement)).all()
|
||||||
|
|
||||||
|
if user_cache:
|
||||||
|
user_cache[0].session_bnum += 1
|
||||||
|
else:
|
||||||
|
user_cache = MessageCountCache(
|
||||||
|
session_id=msg.session_persist_id,
|
||||||
|
time=remove_timezone(
|
||||||
|
msg.time.replace(hour=1, minute=0, second=0, microsecond=0)
|
||||||
|
),
|
||||||
|
session_bnum=1,
|
||||||
|
)
|
||||||
|
db_session.add(user_cache)
|
||||||
|
await db_session.commit()
|
||||||
|
|
||||||
|
logger.info("缓存构建完成")
|
||||||
|
|
||||||
|
|
||||||
|
driver = get_driver()
|
||||||
|
|
||||||
|
|
||||||
|
@driver.on_startup
|
||||||
|
async def _():
|
||||||
|
if not plugin_config.counting_cache:
|
||||||
|
return
|
||||||
|
f_name = get_data_file("nonebot-plugin-dialectlist", "is-pre-cached.json")
|
||||||
|
if not os.path.exists(f_name):
|
||||||
|
with open(f_name, "w", encoding="utf-8") as f:
|
||||||
|
s = json.dumps({"is-pre-cached": False}, ensure_ascii=False, indent=4)
|
||||||
|
f.write(s)
|
||||||
|
|
||||||
|
with open(f_name, "r", encoding="utf-8") as f:
|
||||||
|
if json.load(f)["is-pre-cached"]:
|
||||||
|
return
|
||||||
|
logger.info("未检查到缓存,开始构建缓存")
|
||||||
|
with open(f_name, "w", encoding="utf-8") as f:
|
||||||
|
await build_cache()
|
||||||
|
json.dump({"is-pre-cached": True}, f, ensure_ascii=False, indent=4)
|
||||||
|
|
||||||
|
|
||||||
|
@event_postprocessor
|
||||||
|
async def _(bot: Bot, event: Event, session: Session = Depends(extract_session)):
|
||||||
|
if not plugin_config.counting_cache:
|
||||||
|
return
|
||||||
|
if not session.id2:
|
||||||
|
return
|
||||||
|
if event.get_type() != "message":
|
||||||
|
return
|
||||||
|
now = datetime.now()
|
||||||
|
now = now.replace(hour=1, minute=0, second=0, microsecond=0)
|
||||||
|
|
||||||
|
async with get_session() as db_session:
|
||||||
|
session_id = await get_session_persist_id(session)
|
||||||
|
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)
|
||||||
|
user_cache = (await db_session.scalars(statement)).first()
|
||||||
|
if user_cache:
|
||||||
|
user_cache.session_bnum += 1
|
||||||
|
else:
|
||||||
|
user_cache = MessageCountCache(
|
||||||
|
session_id=session_id,
|
||||||
|
time=remove_timezone(now),
|
||||||
|
session_bnum=1,
|
||||||
|
)
|
||||||
|
db_session.add(user_cache)
|
||||||
|
await db_session.commit()
|
||||||
|
logger.debug("已计入缓存")
|
||||||
|
|||||||
@@ -2,8 +2,59 @@ from inspect import cleandoc
|
|||||||
|
|
||||||
__usage__ = cleandoc(
|
__usage__ = cleandoc(
|
||||||
"""
|
"""
|
||||||
/今日B话榜 查看今天群里谁B话最多。
|
快速调用:
|
||||||
/昨日B话榜 顾名思义。
|
/今日B话榜 ————看看今天群友发了多少消息。
|
||||||
以此类推,有本周,本月,上周,上月,年度排行榜。
|
|
||||||
|
### 🎨一般用法
|
||||||
|
|
||||||
|
#### B话榜
|
||||||
|
|
||||||
|
-`/B话榜` ————看看有史以来(机器人存在以来)群友们发了多少消息! (好像没写)
|
||||||
|
|
||||||
|
-`/今日B话榜` ————看看今天的群友发了多少消息!
|
||||||
|
|
||||||
|
-`/昨日B话榜` ————看看昨天的群友发了多少消息!
|
||||||
|
|
||||||
|
-`/前日B话榜` ————看看前天的群友发了多少消息!
|
||||||
|
|
||||||
|
-`/本周B话榜` ————看看本周的群友发了多少消息!
|
||||||
|
|
||||||
|
-`/上周B话榜` ————看看上周的群友发了多少消息!
|
||||||
|
|
||||||
|
-`/本月B话榜` ————看看这个月的群友发了多少消息!
|
||||||
|
|
||||||
|
-`/年度B话榜` ————看看今年的群友发了多少消息!
|
||||||
|
|
||||||
|
-`/历史B话榜` ————看看历史上(机器人存在以来)的群友发了多少消息!
|
||||||
|
|
||||||
|
#### 看看B话(kkb)
|
||||||
|
|
||||||
|
-`/看看B话 [@某人|QQ号]` ————看看这个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`
|
||||||
|
|
||||||
|
#### 看看B话
|
||||||
|
|
||||||
|
`/看看B话 {@|QQ号} {群号?} {关键词?}`
|
||||||
|
|
||||||
|
以下调用方法均合法:
|
||||||
|
|
||||||
|
`/kkb 114514 1919810 ♂`
|
||||||
|
`/kkb @man -k ♂`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import os
|
import os
|
||||||
|
import httpx
|
||||||
import asyncio
|
import asyncio
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
from typing import Dict, List
|
from typing import Dict, List, Optional
|
||||||
from sqlalchemy import or_, select
|
from sqlalchemy import or_, select
|
||||||
from sqlalchemy.sql import ColumnElement
|
from sqlalchemy.sql import ColumnElement
|
||||||
|
|
||||||
@@ -15,6 +16,7 @@ from nonebot.adapters import Bot, Event
|
|||||||
from nonebot_plugin_orm import get_session
|
from nonebot_plugin_orm import get_session
|
||||||
from nonebot_plugin_session import Session, SessionLevel, extract_session
|
from nonebot_plugin_session import Session, SessionLevel, extract_session
|
||||||
from nonebot_plugin_userinfo import get_user_info, UserInfo
|
from nonebot_plugin_userinfo import get_user_info, UserInfo
|
||||||
|
from nonebot_plugin_userinfo.exception import NetworkError
|
||||||
from nonebot_plugin_localstore import get_cache_dir
|
from nonebot_plugin_localstore import get_cache_dir
|
||||||
from nonebot_plugin_htmlrender import template_to_pic
|
from nonebot_plugin_htmlrender import template_to_pic
|
||||||
from nonebot_plugin_session_orm import SessionModel
|
from nonebot_plugin_session_orm import SessionModel
|
||||||
@@ -26,16 +28,6 @@ from .config import plugin_config
|
|||||||
|
|
||||||
cache_path = get_cache_dir("nonebot_plugin_dialectlist")
|
cache_path = get_cache_dir("nonebot_plugin_dialectlist")
|
||||||
|
|
||||||
# 暂时不做考虑
|
|
||||||
# def admin_permission():
|
|
||||||
# permission = SUPERUSER
|
|
||||||
# with contextlib.suppress(ImportError):
|
|
||||||
# from nonebot.adapters.onebot.v11.permission import GROUP_ADMIN, GROUP_OWNER
|
|
||||||
|
|
||||||
# permission = permission | GROUP_ADMIN | GROUP_OWNER
|
|
||||||
|
|
||||||
# return permission
|
|
||||||
|
|
||||||
|
|
||||||
async def ensure_group(matcher: Matcher, session: Session = Depends(extract_session)):
|
async def ensure_group(matcher: Matcher, session: Session = Depends(extract_session)):
|
||||||
"""确保在群组中使用"""
|
"""确保在群组中使用"""
|
||||||
@@ -85,7 +77,9 @@ async def persist_id2group_id(ids: List[str]) -> List[str]:
|
|||||||
return [i.id2 for i in records]
|
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:
|
Args:
|
||||||
@@ -100,6 +94,10 @@ def msg_counter(msg_list: List[MessageRecord]) -> Dict[str, int]:
|
|||||||
logger.info("wow , there are {} msgs to count !!!".format(msg_len))
|
logger.info("wow , there are {} msgs to count !!!".format(msg_len))
|
||||||
|
|
||||||
for i in msg_list:
|
for i in msg_list:
|
||||||
|
# logger.debug(f"processing msg {i.plain_text}")
|
||||||
|
if keyword:
|
||||||
|
if keyword not in i.plain_text:
|
||||||
|
continue
|
||||||
try:
|
try:
|
||||||
lst[str(i.session_persist_id)] += 1
|
lst[str(i.session_persist_id)] += 1
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -203,6 +201,21 @@ async def _get_user_default_avatar() -> bytes:
|
|||||||
return img
|
return img
|
||||||
|
|
||||||
|
|
||||||
|
async def _get_user_avatar(user: UserInfo, client: httpx.AsyncClient) -> bytes:
|
||||||
|
if not user.user_avatar:
|
||||||
|
return await _get_user_default_avatar()
|
||||||
|
url = user.user_avatar.get_url()
|
||||||
|
for i in range(3):
|
||||||
|
try:
|
||||||
|
resp = await client.get(url, timeout=10)
|
||||||
|
resp.raise_for_status()
|
||||||
|
return resp.content
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Error downloading {url}, retry {i}/3: {e}")
|
||||||
|
await asyncio.sleep(3)
|
||||||
|
raise NetworkError(f"{url} 下载失败!")
|
||||||
|
|
||||||
|
|
||||||
def get_default_user_info() -> UserInfo:
|
def get_default_user_info() -> UserInfo:
|
||||||
user_info = UserInfo(
|
user_info = UserInfo(
|
||||||
user_id="114514",
|
user_id="114514",
|
||||||
@@ -212,21 +225,26 @@ def get_default_user_info() -> UserInfo:
|
|||||||
|
|
||||||
|
|
||||||
async def get_user_infos(
|
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]:
|
) -> List[UserRankInfo]:
|
||||||
|
|
||||||
user_ids = [i[0] for i in rank]
|
user_ids = [i[0] for i in rank]
|
||||||
pool = [get_user_info(bot, event, id, use_cache) for id in user_ids]
|
pool = [get_user_info(bot, event, id, use_cache) for id in user_ids]
|
||||||
user_infos = await asyncio.gather(*pool)
|
user_infos = await asyncio.gather(*pool)
|
||||||
|
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
pool = []
|
pool = []
|
||||||
for i in user_infos:
|
for i in user_infos:
|
||||||
if not i:
|
if not i:
|
||||||
pool.append(_get_user_default_avatar())
|
pool.append(_get_user_default_avatar())
|
||||||
continue
|
continue
|
||||||
if i.user_avatar:
|
if i.user_avatar:
|
||||||
pool.append(i.user_avatar.get_image())
|
pool.append(_get_user_avatar(i, client))
|
||||||
user_avatars = await asyncio.gather(*pool)
|
user_avatars = await asyncio.gather(*pool)
|
||||||
|
|
||||||
for i in user_avatars:
|
for i in user_avatars:
|
||||||
if not i:
|
if not i:
|
||||||
user_avatars[user_avatars.index(i)] = await _get_user_default_avatar()
|
user_avatars[user_avatars.index(i)] = await _get_user_default_avatar()
|
||||||
@@ -247,11 +265,13 @@ async def get_user_infos(
|
|||||||
user_nickname=_get_user_nickname(user_info),
|
user_nickname=_get_user_nickname(user_info),
|
||||||
user_avatar_bytes=user_avatars[i],
|
user_avatar_bytes=user_avatars[i],
|
||||||
)
|
)
|
||||||
user.user_gender = (
|
print(user.user_gender)
|
||||||
"♂"
|
if user.user_gender == "male":
|
||||||
if user.user_gender == "male"
|
user.user_gender = "♂"
|
||||||
else "♀" if user.user_gender == "female" else ""
|
elif user.user_gender == "female":
|
||||||
)
|
user.user_gender = "♀"
|
||||||
|
else:
|
||||||
|
user.user_gender = "🤔"
|
||||||
rank2.append(user)
|
rank2.append(user)
|
||||||
|
|
||||||
return rank2
|
return rank2
|
||||||
|
|||||||
10
pdm.lock
generated
10
pdm.lock
generated
@@ -5,10 +5,10 @@
|
|||||||
groups = ["default", "Test", "dev"]
|
groups = ["default", "Test", "dev"]
|
||||||
strategy = ["inherit_metadata"]
|
strategy = ["inherit_metadata"]
|
||||||
lock_version = "4.5.0"
|
lock_version = "4.5.0"
|
||||||
content_hash = "sha256:f31fbbd00506680ed623e0216a48314dcbb142cebec25ebe15f1dc9a50915b25"
|
content_hash = "sha256:41c1ac2ec7d9ce3eb477f442e6ccbeda65fb25eb4f22cd696d361dae97cb065c"
|
||||||
|
|
||||||
[[metadata.targets]]
|
[[metadata.targets]]
|
||||||
requires_python = "~=3.9"
|
requires_python = ">=3.9,<3.13"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aiofiles"
|
name = "aiofiles"
|
||||||
@@ -267,7 +267,7 @@ name = "charset-normalizer"
|
|||||||
version = "3.3.2"
|
version = "3.3.2"
|
||||||
requires_python = ">=3.7.0"
|
requires_python = ">=3.7.0"
|
||||||
summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
|
summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
|
||||||
groups = ["default", "dev"]
|
groups = ["dev"]
|
||||||
files = [
|
files = [
|
||||||
{file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
|
{file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
|
||||||
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
|
{file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
|
||||||
@@ -1876,7 +1876,7 @@ name = "requests"
|
|||||||
version = "2.32.3"
|
version = "2.32.3"
|
||||||
requires_python = ">=3.8"
|
requires_python = ">=3.8"
|
||||||
summary = "Python HTTP for Humans."
|
summary = "Python HTTP for Humans."
|
||||||
groups = ["default", "dev"]
|
groups = ["dev"]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"certifi>=2017.4.17",
|
"certifi>=2017.4.17",
|
||||||
"charset-normalizer<4,>=2",
|
"charset-normalizer<4,>=2",
|
||||||
@@ -2383,7 +2383,7 @@ name = "urllib3"
|
|||||||
version = "2.2.1"
|
version = "2.2.1"
|
||||||
requires_python = ">=3.8"
|
requires_python = ">=3.8"
|
||||||
summary = "HTTP library with thread-safe connection pooling, file post, and more."
|
summary = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||||
groups = ["default", "dev"]
|
groups = ["dev"]
|
||||||
files = [
|
files = [
|
||||||
{file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"},
|
{file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"},
|
||||||
{file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"},
|
{file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"},
|
||||||
|
|||||||
@@ -1,24 +1,23 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "nonebot-plugin-dialectlist"
|
name = "nonebot-plugin-dialectlist"
|
||||||
version = "2.3.1"
|
version = "2.5.1"
|
||||||
description = "看看你群群友有多能说"
|
description = "看看你群群友有多能说"
|
||||||
authors = [
|
authors = [
|
||||||
{name = "Chen_Xu233", email = "woyerpa@outlook.com"},
|
{name = "Chen_Xu233", email = "woyerpa@outlook.com"},
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nonebot-plugin-chatrecorder>=0.6.0",
|
"nonebot-plugin-chatrecorder>=0.6.0",
|
||||||
"requests>=2.32.3",
|
|
||||||
"nonebot-plugin-orm[default]",
|
"nonebot-plugin-orm[default]",
|
||||||
"nonebot-plugin-apscheduler>=0.4.0",
|
"nonebot-plugin-apscheduler>=0.4.0",
|
||||||
"nonebot-plugin-alconna>=0.50.2",
|
"nonebot-plugin-alconna>=0.50.2",
|
||||||
"nonebot-plugin-cesaa>=0.4.0",
|
"nonebot-plugin-cesaa>=0.4.0",
|
||||||
"nonebot-plugin-userinfo>=0.2.4",
|
"nonebot-plugin-userinfo>=0.2.6",
|
||||||
"nonebot-plugin-htmlrender>=0.3.3",
|
"nonebot-plugin-htmlrender>=0.3.3",
|
||||||
"nonebot2>=2.3.2",
|
"nonebot2>=2.3.2",
|
||||||
"pillow>=10.4.0",
|
"pillow>=10.4.0",
|
||||||
"nonebot-plugin-uninfo>=0.1.1",
|
"nonebot-plugin-uninfo>=0.1.1",
|
||||||
]
|
]
|
||||||
requires-python = ">=3.9,<4.0"
|
requires-python = ">=3.9,<3.13"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = {text = "MIT"}
|
license = {text = "MIT"}
|
||||||
|
|
||||||
|
|||||||
112
tests/render.py
112
tests/render.py
@@ -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
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user