nonebot-plugin-dialectlist 2.6.2__tar.gz → 2.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. {nonebot_plugin_dialectlist-2.6.2/nonebot_plugin_dialectlist.egg-info → nonebot_plugin_dialectlist-2.7.0}/PKG-INFO +4 -3
  2. nonebot_plugin_dialectlist-2.7.0/nonebot_plugin_dialectlist/__init__.py +372 -0
  3. {nonebot_plugin_dialectlist-2.6.2 → nonebot_plugin_dialectlist-2.7.0}/nonebot_plugin_dialectlist/config.py +3 -2
  4. nonebot_plugin_dialectlist-2.7.0/nonebot_plugin_dialectlist/migrations/fb88e4d27eb8_update_dialectlist.py +47 -0
  5. {nonebot_plugin_dialectlist-2.6.2 → nonebot_plugin_dialectlist-2.7.0}/nonebot_plugin_dialectlist/model.py +2 -1
  6. {nonebot_plugin_dialectlist-2.6.2 → nonebot_plugin_dialectlist-2.7.0}/nonebot_plugin_dialectlist/storage.py +10 -11
  7. {nonebot_plugin_dialectlist-2.6.2 → nonebot_plugin_dialectlist-2.7.0}/nonebot_plugin_dialectlist/time.py +4 -5
  8. nonebot_plugin_dialectlist-2.7.0/nonebot_plugin_dialectlist/utils.py +263 -0
  9. {nonebot_plugin_dialectlist-2.6.2 → nonebot_plugin_dialectlist-2.7.0/nonebot_plugin_dialectlist.egg-info}/PKG-INFO +4 -3
  10. {nonebot_plugin_dialectlist-2.6.2 → nonebot_plugin_dialectlist-2.7.0}/nonebot_plugin_dialectlist.egg-info/SOURCES.txt +1 -0
  11. {nonebot_plugin_dialectlist-2.6.2 → nonebot_plugin_dialectlist-2.7.0}/nonebot_plugin_dialectlist.egg-info/requires.txt +1 -1
  12. {nonebot_plugin_dialectlist-2.6.2 → nonebot_plugin_dialectlist-2.7.0}/pyproject.toml +5 -9
  13. nonebot_plugin_dialectlist-2.6.2/nonebot_plugin_dialectlist/__init__.py +0 -369
  14. nonebot_plugin_dialectlist-2.6.2/nonebot_plugin_dialectlist/utils.py +0 -279
  15. {nonebot_plugin_dialectlist-2.6.2 → nonebot_plugin_dialectlist-2.7.0}/LICENSE +0 -0
  16. {nonebot_plugin_dialectlist-2.6.2 → nonebot_plugin_dialectlist-2.7.0}/MANIFEST.in +0 -0
  17. {nonebot_plugin_dialectlist-2.6.2 → nonebot_plugin_dialectlist-2.7.0}/README.md +0 -0
  18. {nonebot_plugin_dialectlist-2.6.2 → nonebot_plugin_dialectlist-2.7.0}/nonebot_plugin_dialectlist/template/avatar/default.jpg +0 -0
  19. {nonebot_plugin_dialectlist-2.6.2 → nonebot_plugin_dialectlist-2.7.0}/nonebot_plugin_dialectlist/template/css/style.css +0 -0
  20. {nonebot_plugin_dialectlist-2.6.2 → nonebot_plugin_dialectlist-2.7.0}/nonebot_plugin_dialectlist/template/rank_template.j2 +0 -0
  21. {nonebot_plugin_dialectlist-2.6.2 → nonebot_plugin_dialectlist-2.7.0}/nonebot_plugin_dialectlist/usage.py +0 -0
  22. {nonebot_plugin_dialectlist-2.6.2 → nonebot_plugin_dialectlist-2.7.0}/nonebot_plugin_dialectlist.egg-info/dependency_links.txt +0 -0
  23. {nonebot_plugin_dialectlist-2.6.2 → nonebot_plugin_dialectlist-2.7.0}/nonebot_plugin_dialectlist.egg-info/top_level.txt +0 -0
  24. {nonebot_plugin_dialectlist-2.6.2 → nonebot_plugin_dialectlist-2.7.0}/setup.cfg +0 -0
@@ -1,13 +1,13 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: nonebot-plugin-dialectlist
3
- Version: 2.6.2
3
+ Version: 2.7.0
4
4
  Summary: 看看你群群友有多能说
5
5
  Author-email: Chen_Xu233 <woyerpa@outlook.com>
6
6
  License: MIT
7
7
  Requires-Python: <4.0,>=3.9
8
8
  Description-Content-Type: text/markdown
9
9
  License-File: LICENSE
10
- Requires-Dist: nonebot-plugin-chatrecorder<0.7.0,>=0.6.0
10
+ Requires-Dist: nonebot-plugin-chatrecorder>=0.7.0
11
11
  Requires-Dist: nonebot-plugin-orm[default]
12
12
  Requires-Dist: nonebot-plugin-apscheduler>=0.4.0
13
13
  Requires-Dist: nonebot-plugin-alconna>=0.50.2
@@ -25,6 +25,7 @@ Requires-Dist: nb-cli>=0.7.6; extra == "dev"
25
25
  Requires-Dist: py-spy>=0.3.14; extra == "dev"
26
26
  Provides-Extra: test
27
27
  Requires-Dist: nonebot-adapter-onebot>=2.4.4; extra == "test"
28
+ Dynamic: license-file
28
29
 
29
30
  <div align="center">
30
31
  <a href="https://v2.nonebot.dev/store"><img src="./docs/NoneBotPlugin.svg" width="400" alt="NoneBotPluginLogo"></a>
@@ -0,0 +1,372 @@
1
+ from nonebot import require
2
+
3
+ require('nonebot_plugin_chatrecorder')
4
+ require('nonebot_plugin_apscheduler')
5
+ require('nonebot_plugin_htmlrender')
6
+ require('nonebot_plugin_userinfo')
7
+ require('nonebot_plugin_alconna')
8
+ require('nonebot_plugin_uninfo')
9
+ require('nonebot_plugin_cesaa')
10
+
11
+ import re
12
+ import time as t
13
+ from datetime import datetime, timedelta
14
+ from typing import Optional, Union
15
+
16
+ import nonebot_plugin_saa as saa
17
+ from arclet.alconna import ArparmaBehavior
18
+ from arclet.alconna.arparma import Arparma
19
+ from nonebot import on_command
20
+ from nonebot.adapters import Bot, Event
21
+ from nonebot.log import logger
22
+ from nonebot.params import Arg, Depends
23
+ from nonebot.plugin import PluginMetadata, inherit_supported_adapters
24
+ from nonebot.typing import T_State
25
+ from nonebot_plugin_alconna import (
26
+ Alconna,
27
+ Args,
28
+ At,
29
+ Field,
30
+ Match,
31
+ Option,
32
+ on_alconna,
33
+ )
34
+ from nonebot_plugin_chatrecorder import get_message_records
35
+ from nonebot_plugin_uninfo import Session, Uninfo, get_session
36
+
37
+ from .config import Config, plugin_config
38
+ from .storage import build_cache, get_cache
39
+ from .time import (
40
+ get_datetime_fromisoformat_with_timezone,
41
+ get_datetime_now_with_timezone,
42
+ parse_datetime,
43
+ )
44
+ from .usage import __usage__
45
+ from .utils import (
46
+ get_rank_image,
47
+ get_user_infos,
48
+ got_rank,
49
+ msg_counter,
50
+ persist_id2user_id,
51
+ )
52
+
53
+ __plugin_meta__ = PluginMetadata(
54
+ name='B话排行榜',
55
+ description='调查群U的B话数量,以一定的顺序排序后排序出来。',
56
+ usage=__usage__,
57
+ homepage='https://github.com/ChenXu233/nonebot_plugin_dialectlist',
58
+ type='application',
59
+ supported_adapters=inherit_supported_adapters(
60
+ 'nonebot_plugin_chatrecorder',
61
+ 'nonebot_plugin_saa',
62
+ 'nonebot_plugin_alconna',
63
+ ),
64
+ config=Config,
65
+ )
66
+
67
+
68
+ # 抄的词云,不过真的很适合B话榜。
69
+ class SameTime(ArparmaBehavior):
70
+ def operate(self, interface: Arparma):
71
+ type = interface.query('type')
72
+ time = interface.query('time')
73
+ if type is None and time:
74
+ interface.behave_fail()
75
+
76
+
77
+ def wrapper(slot: Union[int, str], content: Optional[str], context) -> str:
78
+ if slot == 'type' and content:
79
+ return content
80
+ return '' # pragma: no cover
81
+
82
+
83
+ build_cache_cmd = on_command('build_cache', aliases={'重建缓存'}, block=True)
84
+
85
+
86
+ @build_cache_cmd.handle()
87
+ async def _build_cache(bot: Bot, event: Event):
88
+ await saa.Text('正在重建缓存,请稍等。').send(reply=True)
89
+ await build_cache()
90
+ await saa.Text('重建缓存完成。').send(reply=True)
91
+
92
+
93
+ b_cmd = on_alconna(
94
+ Alconna(
95
+ '看看B话',
96
+ Args['at', [str, At], Field(completion=lambda: '请想要查询的人的QQ号')],
97
+ Option('-g|--group_id', Args['group_id?', str]),
98
+ Option('-k|--keyword', Args['keyword?', str]),
99
+ ),
100
+ aliases={'kkb'},
101
+ use_cmd_start=True,
102
+ )
103
+
104
+
105
+ @b_cmd.handle()
106
+ async def handle_b_cmd(
107
+ at: Match[str | At],
108
+ group_id: Match[str],
109
+ keyword: Match[str],
110
+ uninfo: Uninfo,
111
+ session: Session = Depends(get_session),
112
+ ):
113
+ id = at.result
114
+ if isinstance(id, At):
115
+ id = id.target
116
+ if group_id.available:
117
+ gid = group_id.result
118
+ else:
119
+ gid = session.scene.id
120
+
121
+ if not gid:
122
+ await b_cmd.finish('请指定群号。')
123
+
124
+ if keyword.available:
125
+ keywords = keyword.result
126
+ else:
127
+ keywords = None
128
+
129
+ messages = await get_message_records(
130
+ scene_ids=[gid],
131
+ user_ids=[id],
132
+ types=['message'], # 排除机器人自己发的消息
133
+ exclude_user_ids=plugin_config.excluded_people,
134
+ )
135
+ d = msg_counter(messages, keywords)
136
+ rank = got_rank(d)
137
+ if not rank:
138
+ await b_cmd.finish(
139
+ f'该用户在群“{uninfo.scene.name}”关于“{keyword.result}”的B话数量为0。'
140
+ )
141
+
142
+ await saa.Text(
143
+ f'该用户在群“{uninfo.scene.name}”关于“{keyword.result}”的B话数量为{rank[0][1]}。'
144
+ ).send(reply=True)
145
+
146
+
147
+ rank_cmd = on_alconna(
148
+ Alconna(
149
+ 'B话榜',
150
+ Args[
151
+ 'type?',
152
+ ['今日', '昨日', '本周', '上周', '本月', '上月', '年度', '历史'],
153
+ ][
154
+ 'time?',
155
+ str,
156
+ ],
157
+ Option('-g|--group_id', Args['group_id?', str]),
158
+ Option('-k|--keyword', Args['keyword?', str]),
159
+ behaviors=[SameTime()],
160
+ ),
161
+ aliases={'废话榜'},
162
+ use_cmd_start=True,
163
+ block=True,
164
+ )
165
+
166
+
167
+ rank_cmd.shortcut(
168
+ r'(?P<type>今日|昨日|本周|上周|本月|上月|年度|历史)B话榜',
169
+ {
170
+ 'prefix': True,
171
+ 'command': 'B话榜',
172
+ 'wrapper': wrapper,
173
+ 'args': ['{type}'],
174
+ },
175
+ )
176
+ rank_cmd.shortcut(
177
+ r'(?P<type>今日|昨日|本周|上周|本月|上月|年度|历史)废话榜',
178
+ {
179
+ 'prefix': True,
180
+ 'command': '废话榜',
181
+ 'wrapper': wrapper,
182
+ 'args': ['{type}'],
183
+ },
184
+ )
185
+
186
+
187
+ # 这段函数完全抄的词云
188
+ @rank_cmd.handle()
189
+ async def _group_message(
190
+ state: T_State,
191
+ session: Session = Depends(get_session),
192
+ type: Optional[str] = None,
193
+ time: Optional[str] = None,
194
+ group_id: Optional[str] = None,
195
+ keyword: Optional[str] = None,
196
+ ):
197
+ t1 = t.time()
198
+ state['t1'] = t1
199
+ dt = get_datetime_now_with_timezone()
200
+
201
+ if not group_id:
202
+ group_id = session.scene.id
203
+ logger.debug(f'session id2: {group_id}')
204
+ if group_id:
205
+ state['group_id'] = group_id
206
+
207
+ state['keyword'] = keyword
208
+
209
+ if not type:
210
+ await rank_cmd.finish(__plugin_meta__.usage)
211
+
212
+ dt = get_datetime_now_with_timezone()
213
+
214
+ if type == '今日':
215
+ state['start'] = dt.replace(hour=0, minute=0, second=0, microsecond=0)
216
+ state['stop'] = dt
217
+ elif type == '昨日':
218
+ state['stop'] = dt.replace(hour=0, minute=0, second=0, microsecond=0)
219
+ state['start'] = state['stop'] - timedelta(days=1)
220
+ elif type == '本周':
221
+ state['start'] = dt.replace(
222
+ hour=0, minute=0, second=0, microsecond=0
223
+ ) - timedelta(days=dt.weekday())
224
+ state['stop'] = dt
225
+ elif type == '上周':
226
+ state['stop'] = dt.replace(
227
+ hour=0, minute=0, second=0, microsecond=0
228
+ ) - timedelta(days=dt.weekday())
229
+ state['start'] = state['stop'] - timedelta(days=7)
230
+ elif type == '本月':
231
+ state['start'] = dt.replace(
232
+ day=1, hour=0, minute=0, second=0, microsecond=0
233
+ )
234
+ state['stop'] = dt
235
+ elif type == '上月':
236
+ state['stop'] = dt.replace(
237
+ day=1, hour=0, minute=0, second=0, microsecond=0
238
+ ) - timedelta(microseconds=1)
239
+ state['start'] = state['stop'].replace(
240
+ day=1, hour=0, minute=0, second=0, microsecond=0
241
+ )
242
+ elif type == '年度':
243
+ state['start'] = dt.replace(
244
+ month=1, day=1, hour=0, minute=0, second=0, microsecond=0
245
+ )
246
+ state['stop'] = dt
247
+ elif type == '历史':
248
+ if time:
249
+ plaintext = time
250
+ if match := re.match(r'^(.+?)(?:~(.+))?$', plaintext):
251
+ start = match[1]
252
+ stop = match[2]
253
+ try:
254
+ state['start'] = get_datetime_fromisoformat_with_timezone(
255
+ start
256
+ )
257
+ if stop:
258
+ state['stop'] = (
259
+ get_datetime_fromisoformat_with_timezone(stop)
260
+ )
261
+ else:
262
+ # 如果没有指定结束日期,则认为是所给日期的当天的词云
263
+ state['start'] = state['start'].replace(
264
+ hour=0, minute=0, second=0, microsecond=0
265
+ )
266
+ state['stop'] = state['start'] + timedelta(days=1)
267
+ except ValueError:
268
+ await rank_cmd.finish(
269
+ '请输入正确的日期,不然我没法理解呢!'
270
+ )
271
+
272
+
273
+ @rank_cmd.got(
274
+ 'start',
275
+ prompt='请输入你要查询的起始日期(如 2022-01-01)',
276
+ parameterless=[Depends(parse_datetime('start'))],
277
+ )
278
+ @rank_cmd.got(
279
+ 'stop',
280
+ prompt='请输入你要查询的结束日期(如 2022-02-22)',
281
+ parameterless=[Depends(parse_datetime('stop'))],
282
+ )
283
+ @rank_cmd.got('group_id', prompt='请输入你要查询的群号。')
284
+ async def handle_rank(
285
+ state: T_State,
286
+ bot: Bot,
287
+ event: Event,
288
+ session: Session = Depends(get_session),
289
+ start: datetime = Arg(),
290
+ stop: datetime = Arg(),
291
+ ):
292
+ if id := state['group_id']:
293
+ id = str(id)
294
+ logger.debug(f'group_id: {id}')
295
+ else:
296
+ id = session.scene.id
297
+ logger.debug(f'group_id: {id}')
298
+
299
+ if not id:
300
+ await saa.Text('没有指定群哦').finish()
301
+
302
+ keyword = state['keyword']
303
+
304
+ if plugin_config.counting_cache:
305
+ if keyword:
306
+ await saa.Text('已开启缓存~缓存不支持关键词查询哦').finish()
307
+ t1 = t.time()
308
+ raw_rank = await get_cache(start, stop, id)
309
+ logger.debug(f'获取计数消息花费时间:{t.time() - t1}')
310
+ else:
311
+ t1 = t.time()
312
+ messages = await get_message_records(
313
+ scene_ids=[id],
314
+ types=['message'], # 排除机器人自己发的消息
315
+ time_start=start,
316
+ time_stop=stop,
317
+ exclude_user_ids=plugin_config.excluded_people,
318
+ )
319
+ raw_rank = msg_counter(messages, keyword)
320
+ logger.debug(f'获取计数消息花费时间:{t.time() - t1}')
321
+
322
+ if not raw_rank:
323
+ await saa.Text(
324
+ '没有获取到排行榜数据哦,请确认时间范围和群号是否正确或者关键词是否存在~'
325
+ ).finish()
326
+
327
+ rank = got_rank(raw_rank)
328
+ ids = await persist_id2user_id([int(i[0]) for i in rank])
329
+ for i in range(len(rank)):
330
+ rank[i][0] = str(ids[i])
331
+ logger.debug(rank[i])
332
+
333
+ t1 = t.time()
334
+ rank2 = await get_user_infos(bot, event, rank)
335
+ logger.debug(f'获取用户信息花费时间:{t.time() - t1}')
336
+
337
+ string: str = ''
338
+ if plugin_config.show_text_rank:
339
+ if keyword:
340
+ string += f'关于{keyword}的话痨榜结果:\n'
341
+ else:
342
+ string += '话痨榜:\n'
343
+
344
+ for i in rank2:
345
+ logger.debug(i.user_name)
346
+ for i in range(len(rank2)):
347
+ str_example = plugin_config.string_format.format(
348
+ index=rank2[i].user_index,
349
+ nickname=rank2[i].user_nickname,
350
+ chatdatanum=rank2[i].user_bnum,
351
+ )
352
+ string += str_example
353
+
354
+ msg = saa.Text(string)
355
+
356
+ if plugin_config.visualization:
357
+ t1 = t.time()
358
+ image = await get_rank_image(rank2)
359
+ msg += saa.Image(image)
360
+ logger.debug(f'群聊消息渲染图片花费时间:{t.time() - t1}')
361
+
362
+ if plugin_config.suffix:
363
+ timecost = t.time() - state['t1']
364
+ suffix = saa.Text(plugin_config.string_suffix.format(timecost=timecost))
365
+ msg += suffix
366
+ if not msg:
367
+ await saa.Text('你把可视化都关了哪来的排行榜?').finish()
368
+
369
+ if plugin_config.aggregate_transmission:
370
+ await saa.AggregatedMessageFactory([msg]).finish()
371
+ else:
372
+ await msg.finish(reply=True)
@@ -1,6 +1,7 @@
1
- from pydantic import BaseModel
2
- from typing import Optional, List
1
+ from typing import List, Optional
2
+
3
3
  from nonebot import get_driver, get_plugin_config
4
+ from pydantic import BaseModel
4
5
 
5
6
 
6
7
  class ScopedConfig(BaseModel):
@@ -0,0 +1,47 @@
1
+ """update dialectlist
2
+
3
+ 迁移 ID: fb88e4d27eb8
4
+ 父迁移: 60daff81fcdc
5
+ 创建时间: 2025-04-20 11:00:50.931679
6
+
7
+ """
8
+ from __future__ import annotations
9
+
10
+ from collections.abc import Sequence
11
+
12
+ from alembic import op
13
+ import sqlalchemy as sa
14
+
15
+
16
+ revision: str = 'fb88e4d27eb8'
17
+ down_revision: str | Sequence[str] | None = '60daff81fcdc'
18
+ branch_labels: str | Sequence[str] | None = None
19
+ depends_on: str | Sequence[str] | None = None
20
+
21
+
22
+ def upgrade(name: str = "") -> None:
23
+ if name:
24
+ return
25
+ # ### commands auto generated by Alembic - please adjust! ###
26
+ op.create_table('sessionmodel',
27
+ sa.Column('id', sa.Integer(), nullable=False),
28
+ sa.Column('bot_id', sa.String(length=64), nullable=False),
29
+ sa.Column('bot_type', sa.String(length=32), nullable=False),
30
+ sa.Column('platform', sa.String(length=32), nullable=False),
31
+ sa.Column('level', sa.Integer(), nullable=False),
32
+ sa.Column('id1', sa.String(length=64), nullable=False),
33
+ sa.Column('id2', sa.String(length=64), nullable=False),
34
+ sa.Column('id3', sa.String(length=64), nullable=False),
35
+ sa.PrimaryKeyConstraint('id', name=op.f('pk_sessionmodel')),
36
+ sa.UniqueConstraint('bot_id', 'bot_type', 'platform', 'level', 'id1', 'id2', 'id3', name='unique_session'),
37
+ info={'bind_key': ''}
38
+ )
39
+ # ### end Alembic commands ###
40
+
41
+
42
+ def downgrade(name: str = "") -> None:
43
+ if name:
44
+ return
45
+ # ### commands auto generated by Alembic - please adjust! ###
46
+ op.drop_table('sessionmodel')
47
+ # ### end Alembic commands ###
@@ -1,8 +1,9 @@
1
1
  from datetime import datetime
2
2
  from typing import Union
3
- from sqlalchemy import Integer
3
+
4
4
  from nonebot_plugin_orm import Model
5
5
  from nonebot_plugin_userinfo import UserInfo
6
+ from sqlalchemy import Integer
6
7
  from sqlalchemy.orm import Mapped, mapped_column
7
8
 
8
9
 
@@ -1,23 +1,22 @@
1
- import os
2
1
  import json
2
+ import os
3
3
  from datetime import datetime
4
- from sqlalchemy import delete, or_, select
5
4
 
6
5
  from nonebot import get_driver
6
+ from nonebot.adapters import Bot, Event
7
7
  from nonebot.log import logger
8
- from nonebot.params import Depends
9
- from nonebot.adapters import Event, Bot
10
8
  from nonebot.message import event_postprocessor
11
-
12
- from .model import MessageCountCache
13
- from .config import plugin_config
14
-
15
- from nonebot_plugin_localstore import get_data_file
9
+ from nonebot.params import Depends
16
10
  from nonebot_plugin_chatrecorder import get_message_records
17
11
  from nonebot_plugin_chatrecorder.utils import remove_timezone
18
- from nonebot_plugin_session import extract_session, Session
19
- from nonebot_plugin_session_orm import SessionModel, get_session_persist_id
12
+ from nonebot_plugin_localstore import get_data_file
20
13
  from nonebot_plugin_orm import get_session
14
+ from nonebot_plugin_session import Session, extract_session
15
+ from nonebot_plugin_session_orm import SessionModel, get_session_persist_id
16
+ from sqlalchemy import delete, or_, select
17
+
18
+ from .config import plugin_config
19
+ from .model import MessageCountCache
21
20
 
22
21
 
23
22
  async def get_cache(time_start: datetime, time_stop: datetime, group_id: str):
@@ -1,13 +1,12 @@
1
- from zoneinfo import ZoneInfo
2
- from typing import Optional, Union
3
1
  from datetime import datetime, time, tzinfo
2
+ from typing import Optional, Union
3
+ from zoneinfo import ZoneInfo
4
4
 
5
+ from nonebot.adapters import Message
5
6
  from nonebot.params import Arg
6
7
  from nonebot.typing import T_State
7
- from nonebot.adapters import Message
8
-
9
- from nonebot_plugin_apscheduler import scheduler
10
8
  from nonebot_plugin_alconna import AlconnaMatcher
9
+ from nonebot_plugin_apscheduler import scheduler
11
10
 
12
11
  from .config import plugin_config
13
12