nonebot-plugin-werewolf 1.1.6__py3-none-any.whl → 1.1.7__py3-none-any.whl

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.
@@ -10,7 +10,7 @@ from . import matchers as matchers
10
10
  from . import players as players
11
11
  from .config import Config
12
12
 
13
- __version__ = "1.1.6"
13
+ __version__ = "1.1.7"
14
14
  __plugin_meta__ = PluginMetadata(
15
15
  name="狼人杀",
16
16
  description="适用于 Nonebot2 的狼人杀插件",
@@ -1,6 +1,6 @@
1
1
  import json
2
2
 
3
- from nonebot import get_plugin_config, logger
3
+ import nonebot
4
4
  from nonebot.compat import model_dump, type_validate_json
5
5
  from nonebot_plugin_localstore import get_plugin_data_file
6
6
  from pydantic import BaseModel, Field
@@ -41,5 +41,5 @@ preset_data_file = get_plugin_data_file("preset.json")
41
41
  if not preset_data_file.exists():
42
42
  PresetData().save()
43
43
 
44
- config = get_plugin_config(Config).werewolf
45
- logger.debug(f"加载插件配置: {config}")
44
+ config = nonebot.get_plugin_config(Config).werewolf
45
+ nonebot.logger.debug(f"加载插件配置: {config}")
@@ -1,11 +1,12 @@
1
1
  import contextlib
2
+ import functools
2
3
  import secrets
3
4
  from typing import ClassVar, NoReturn
4
5
 
5
6
  import anyio
6
7
  import anyio.abc
8
+ import nonebot
7
9
  from nonebot.adapters import Bot
8
- from nonebot.log import logger
9
10
  from nonebot.utils import escape_tag
10
11
  from nonebot_plugin_alconna import At, Target, UniMessage
11
12
  from nonebot_plugin_alconna.uniseg.message import Receipt
@@ -20,9 +21,13 @@ from .player_set import PlayerSet
20
21
  from .players import Player
21
22
  from .utils import InputStore, ObjectStream, link
22
23
 
24
+ logger = nonebot.logger.opt(colors=True)
25
+
23
26
 
24
27
  def init_players(bot: Bot, game: "Game", players: set[str]) -> PlayerSet:
25
- logger.opt(colors=True).debug(f"初始化 <c>{game.group.id}</c> 的玩家职业")
28
+ # group.colored_name not available yet
29
+ logger.debug(f"初始化 <c>{game.group_id}</c> 的玩家职业")
30
+
26
31
  preset_data = PresetData.load()
27
32
  if (preset := preset_data.role_preset.get(len(players))) is None:
28
33
  raise ValueError(
@@ -47,7 +52,7 @@ def init_players(bot: Bot, game: "Game", players: set[str]) -> PlayerSet:
47
52
  player_set = PlayerSet(
48
53
  Player.new(_select_role(), bot, game, user_id) for user_id in players
49
54
  )
50
- logger.debug(f"职业分配完成: {player_set}")
55
+ logger.debug(f"职业分配完成: <e>{escape_tag(str(player_set))}</e>")
51
56
 
52
57
  return player_set
53
58
 
@@ -73,7 +78,7 @@ class DeadChannel:
73
78
  await self.finished.wait()
74
79
  self.task_group.cancel_scope.cancel()
75
80
 
76
- async def _handle_send(self) -> None:
81
+ async def _handle_send(self) -> NoReturn:
77
82
  while True:
78
83
  player, msg = await self.stream.recv()
79
84
  msg = f"玩家 {player.name}:\n" + msg
@@ -149,15 +154,19 @@ class Game:
149
154
  self._task_group = None
150
155
 
151
156
  async def _fetch_group_scene(self) -> None:
152
- scene = await self.interface.get_scene(SceneType.GROUP, self.group.id)
157
+ scene = await self.interface.get_scene(SceneType.GROUP, self.group_id)
153
158
  if scene is None:
154
- scene = await self.interface.get_scene(SceneType.GUILD, self.group.id)
159
+ scene = await self.interface.get_scene(SceneType.GUILD, self.group_id)
155
160
 
156
161
  self._scene = scene
157
162
 
163
+ @functools.cached_property
164
+ def group_id(self) -> str:
165
+ return self.group.id
166
+
158
167
  @property
159
168
  def colored_name(self) -> str:
160
- name = f"<b><e>{escape_tag(self.group.id)}</e></b>"
169
+ name = f"<b><e>{escape_tag(self.group_id)}</e></b>"
161
170
  if self._scene and self._scene.name is not None:
162
171
  name = f"<y>{escape_tag(self._scene.name)}</y>({name})"
163
172
  return link(name, self._scene and self._scene.avatar)
@@ -176,7 +185,7 @@ class Game:
176
185
  else:
177
186
  text += escape_tag(str(seg)).replace("\n", "\\n")
178
187
 
179
- logger.opt(colors=True).info(text)
188
+ logger.info(text)
180
189
  return await message.send(self.group, self.bot)
181
190
 
182
191
  def raise_for_status(self) -> None:
@@ -218,7 +227,7 @@ class Game:
218
227
  with anyio.move_on_after(timeout_secs):
219
228
  async with anyio.create_task_group() as tg:
220
229
  for p in players:
221
- tg.start_soon(InputStore.fetch_until_stop, p.user_id, self.group.id)
230
+ tg.start_soon(InputStore.fetch_until_stop, p.user_id, self.group_id)
222
231
 
223
232
  async def interact(
224
233
  self,
@@ -241,7 +250,7 @@ class Game:
241
250
  with anyio.fail_after(timeout_secs):
242
251
  await players.interact()
243
252
  except TimeoutError:
244
- logger.opt(colors=True).debug(f"{text}交互超时 (<y>{timeout_secs}</y>s)")
253
+ logger.debug(f"{text}交互超时 (<y>{timeout_secs}</y>s)")
245
254
  await players.broadcast(f"⚠️{text}交互超时")
246
255
 
247
256
  async def post_kill(self, players: Player | PlayerSet) -> None:
@@ -306,27 +315,27 @@ class Game:
306
315
  "ℹ️请等待其他玩家结束交互...",
307
316
  )
308
317
 
309
- # 狼人击杀目标
310
- if (
311
- (killed := self.state.killed) is not None # 狼人未空刀
312
- and killed not in self.state.protected # 守卫保护
313
- and killed not in self.state.antidote # 女巫使用解药
314
- ):
315
- # 狼人正常击杀玩家
316
- await killed.kill(
317
- KillReason.Werewolf,
318
- *players.select(RoleGroup.Werewolf),
319
- )
318
+ # 狼人击杀目标
319
+ if (
320
+ (killed := self.state.killed) is not None # 狼人未空刀
321
+ and killed not in self.state.protected # 守卫保护
322
+ and killed not in self.state.antidote # 女巫使用解药
323
+ ):
324
+ # 狼人正常击杀玩家
325
+ await killed.kill(
326
+ KillReason.Werewolf,
327
+ *players.select(RoleGroup.Werewolf),
328
+ )
320
329
 
321
- # 女巫操作目标
322
- for witch in self.state.poison:
323
- if witch.selected is None:
324
- continue
325
- if witch.selected not in self.state.protected: # 守卫未保护
326
- # 女巫毒杀玩家
327
- await witch.selected.kill(KillReason.Poison, witch)
330
+ # 女巫操作目标
331
+ for witch in self.state.poison:
332
+ if witch.selected is None:
333
+ continue
334
+ if witch.selected not in self.state.protected: # 守卫未保护
335
+ # 女巫毒杀玩家
336
+ await witch.selected.kill(KillReason.Poison, witch)
328
337
 
329
- return killed
338
+ return killed
330
339
 
331
340
  async def run_vote(self) -> None:
332
341
  # 筛选当前存活玩家
@@ -339,7 +348,7 @@ class Game:
339
348
  # 收集到的总票数
340
349
  total_votes = sum(map(len, vote_result.values()))
341
350
 
342
- logger.debug(f"投票结果: {vote_result}")
351
+ logger.debug(f"投票结果: {escape_tag(str(vote_result))}")
343
352
 
344
353
  # 投票结果公示
345
354
  msg = UniMessage.text("📊投票结果:\n")
@@ -461,12 +470,12 @@ class Game:
461
470
  try:
462
471
  await self.mainloop()
463
472
  except anyio.get_cancelled_exc_class():
464
- logger.warning(f"{self.group.id} 的狼人杀游戏进程被取消")
473
+ logger.warning(f"{self.colored_name} 的狼人杀游戏进程被取消")
465
474
  except GameFinished as result:
466
475
  await self.handle_game_finish(result.status)
467
- logger.info(f"{self.group.id} 的狼人杀游戏进程正常退出")
476
+ logger.info(f"{self.colored_name} 的狼人杀游戏进程正常退出")
468
477
  except Exception as err:
469
- msg = f"{self.group.id} 的狼人杀游戏进程出现未知错误: {err!r}"
478
+ msg = f"{self.colored_name} 的狼人杀游戏进程出现未知错误: {err!r}"
470
479
  logger.exception(msg)
471
480
  await self.send(f"❌狼人杀游戏进程出现未知错误: {err!r}")
472
481
  finally:
@@ -484,16 +493,16 @@ class Game:
484
493
  tg.start_soon(self.daemon, finished)
485
494
  tg.start_soon(dead_channel.run)
486
495
  except anyio.get_cancelled_exc_class():
487
- logger.warning(f"{self.group.id} 的狼人杀游戏进程被取消")
496
+ logger.warning(f"{self.colored_name} 的狼人杀游戏进程被取消")
488
497
  except Exception as err:
489
- msg = f"{self.group.id} 的狼人杀守护进程出现错误: {err!r}"
498
+ msg = f"{self.colored_name} 的狼人杀守护进程出现错误: {err!r}"
490
499
  logger.opt(exception=err).error(msg)
491
500
  finally:
492
501
  self._task_group = None
493
502
  self.running_games.discard(self)
494
- InputStore.cleanup(list(self._player_map), self.group.id)
503
+ InputStore.cleanup(list(self._player_map), self.group_id)
495
504
 
496
505
  def terminate(self) -> None:
497
506
  if self._task_group is not None:
498
- logger.warning(f"中止 {self.group.id} 的狼人杀游戏进程")
507
+ logger.warning(f"中止 {self.colored_name} 的狼人杀游戏进程")
499
508
  self._task_group.cancel_scope.cancel()
@@ -4,8 +4,8 @@ from collections.abc import Callable
4
4
  from typing import TYPE_CHECKING, ClassVar, Final, TypeVar, final
5
5
 
6
6
  import anyio
7
+ import nonebot
7
8
  from nonebot.adapters import Bot
8
- from nonebot.log import logger
9
9
  from nonebot.utils import escape_tag
10
10
  from nonebot_plugin_alconna.uniseg import Receipt, Target, UniMessage
11
11
  from nonebot_plugin_uninfo import SceneType
@@ -21,6 +21,8 @@ if TYPE_CHECKING:
21
21
 
22
22
  _P = TypeVar("_P", bound=type["Player"])
23
23
 
24
+ logger = nonebot.logger.opt(colors=True)
25
+
24
26
 
25
27
  class Player:
26
28
  __player_class: ClassVar[dict[Role, type["Player"]]] = {}
@@ -128,7 +130,7 @@ class Player:
128
130
  @final
129
131
  def _log(self, text: str) -> None:
130
132
  text = text.replace("\n", "\\n")
131
- logger.opt(colors=True).info(
133
+ logger.info(
132
134
  f"{self.game.colored_name} | "
133
135
  f"[<b><m>{self.role_name}</m></b>] "
134
136
  f"{self.colored_name} | {text}",
@@ -15,6 +15,8 @@ if TYPE_CHECKING:
15
15
 
16
16
  @Player.register_role(Role.Werewolf, RoleGroup.Werewolf)
17
17
  class Werewolf(Player):
18
+ stream: ObjectStream[str | UniMessage]
19
+
18
20
  @override
19
21
  async def notify_role(self) -> None:
20
22
  await super().notify_role()
@@ -25,11 +27,7 @@ class Werewolf(Player):
25
27
  + "\n".join(f" {p.role_name}: {p.name}" for p in partners)
26
28
  )
27
29
 
28
- async def _handle_interact(
29
- self,
30
- players: "PlayerSet",
31
- stream: ObjectStream[str | UniMessage],
32
- ) -> None:
30
+ async def _handle_interact(self, players: "PlayerSet") -> None:
33
31
  self.selected = None
34
32
 
35
33
  while True:
@@ -40,25 +38,21 @@ class Werewolf(Player):
40
38
  self.selected = players[index - 1]
41
39
  msg = f"当前选择玩家: {self.selected.name}"
42
40
  await self.send(f"🎯{msg}\n发送 “{STOP_COMMAND_PROMPT}” 结束回合")
43
- await stream.send(f"📝队友 {self.name} {msg}")
41
+ await self.stream.send(f"📝队友 {self.name} {msg}")
44
42
  if text == STOP_COMMAND:
45
43
  if self.selected is not None:
46
44
  await self.send("✅你已结束当前回合")
47
- await stream.send(f"📝队友 {self.name} 结束当前回合")
48
- stream.close()
45
+ await self.stream.send(f"📝队友 {self.name} 结束当前回合")
46
+ self.stream.close()
49
47
  return
50
48
  await self.send("⚠️当前未选择玩家,无法结束回合")
51
49
  else:
52
- await stream.send(UniMessage.text(f"💬队友 {self.name}:\n") + input_msg)
50
+ await self.stream.send(UniMessage(f"💬队友 {self.name}:\n") + input_msg)
53
51
 
54
- async def _handle_broadcast(
55
- self,
56
- partners: "PlayerSet",
57
- stream: ObjectStream[str | UniMessage],
58
- ) -> None:
59
- while not stream.closed:
52
+ async def _handle_broadcast(self, partners: "PlayerSet") -> None:
53
+ while not self.stream.closed:
60
54
  try:
61
- message = await stream.recv()
55
+ message = await self.stream.recv()
62
56
  except anyio.EndOfStream:
63
57
  return
64
58
 
@@ -84,8 +78,11 @@ class Werewolf(Player):
84
78
  .text("\n\n⚠️意见未统一将空刀")
85
79
  )
86
80
 
87
- stream = ObjectStream[str | UniMessage](8)
81
+ self.stream = ObjectStream[str | UniMessage](8)
88
82
 
89
- async with anyio.create_task_group() as tg:
90
- tg.start_soon(self._handle_interact, players, stream)
91
- tg.start_soon(self._handle_broadcast, partners, stream)
83
+ try:
84
+ async with anyio.create_task_group() as tg:
85
+ tg.start_soon(self._handle_interact, players)
86
+ tg.start_soon(self._handle_broadcast, partners)
87
+ finally:
88
+ del self.stream
@@ -1,7 +1,7 @@
1
1
  import functools
2
2
  import itertools
3
3
  from collections import defaultdict
4
- from typing import TYPE_CHECKING, Any, ClassVar, Generic, TypeVar, cast
4
+ from typing import TYPE_CHECKING, Any, ClassVar, Generic, TypeVar
5
5
 
6
6
  import anyio
7
7
  import anyio.streams.memory
@@ -106,7 +106,7 @@ def as_player_set(*player: "Player") -> "PlayerSet":
106
106
 
107
107
 
108
108
  class ObjectStream(Generic[T]):
109
- __unset = object()
109
+ __unset: Any = object()
110
110
  _send: anyio.streams.memory.MemoryObjectSendStream[T]
111
111
  _recv: anyio.streams.memory.MemoryObjectReceiveStream[T]
112
112
  _closed: anyio.Event
@@ -137,7 +137,7 @@ class ObjectStream(Generic[T]):
137
137
  if result is self.__unset:
138
138
  raise anyio.EndOfStream
139
139
 
140
- return cast(T, result)
140
+ return result
141
141
 
142
142
  def close(self) -> None:
143
143
  self._closed.set()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nonebot-plugin-werewolf
3
- Version: 1.1.6
3
+ Version: 1.1.7
4
4
  Summary: 适用于 Nonebot2 的狼人杀插件
5
5
  Author-email: wyf7685 <wyf7685@163.com>
6
6
  License: MIT
@@ -242,6 +242,10 @@ _✨ 简单的狼人杀插件 ✨_
242
242
 
243
243
  <!-- CHANGELOG -->
244
244
 
245
+ - 2024.10.31 v1.1.7
246
+
247
+ - *Bug fix*
248
+
245
249
  - 2024.10.31 v1.1.6
246
250
 
247
251
  - 新增超级用户中止游戏 (#7)
@@ -1,11 +1,11 @@
1
- nonebot_plugin_werewolf/__init__.py,sha256=ZomfjWnGRnFuh3I__spYbLRE1Z3wTUthwXzc5x8YPJw,931
2
- nonebot_plugin_werewolf/config.py,sha256=bMRRqNVWNsFDKyijxZZYPN7DCAf9bYf2MdfbPXE239w,1287
1
+ nonebot_plugin_werewolf/__init__.py,sha256=CMvcf07CmixM1tCOtCNQ50ksjjU-W_HxdNQHRFXratM,931
2
+ nonebot_plugin_werewolf/config.py,sha256=QKE_EomWI58L-LV6KvHth1C0n77jwZG9pFz_hMY4hjQ,1272
3
3
  nonebot_plugin_werewolf/constant.py,sha256=_ngw2xtXVZMTQXQ0gv4l9h4Lo1jq5v28fKLOGJ7G57o,1796
4
4
  nonebot_plugin_werewolf/exception.py,sha256=2F2kZsMaRIa7jOiIQJiM10K7Z59ouCpaZENcnEcEEXQ,398
5
- nonebot_plugin_werewolf/game.py,sha256=xU8IcrWQNRUjzfPIAbm1XCeshwfy8SWgxapUP1_NGOw,18925
5
+ nonebot_plugin_werewolf/game.py,sha256=V0RBr1rP6cQOFiUBelxAysschx3HDv41PUZhf_Wi4-Y,19046
6
6
  nonebot_plugin_werewolf/models.py,sha256=ljWy6BaTVyIK4Ng-wUVRteBLZe2pQLf8l3yWFZzWXHQ,1505
7
7
  nonebot_plugin_werewolf/player_set.py,sha256=84hZOOAaPjTyZZDje6mNy7zm4G5UH2nGDq1pvDNFT9w,2538
8
- nonebot_plugin_werewolf/utils.py,sha256=PBgadYdF3IQbaVPjcmstmsOGddfPvj8rcbiYQllHFYY,4096
8
+ nonebot_plugin_werewolf/utils.py,sha256=ekqyjXndl0_Ih-4pTTMXPOp7WMezlxUY7Xd6DcFjgpk,4086
9
9
  nonebot_plugin_werewolf/matchers/__init__.py,sha256=BnzEDmW8n1wkyI-un0DYXt2k_6CFv6NE8itZ2daw4zQ,174
10
10
  nonebot_plugin_werewolf/matchers/depends.py,sha256=poMQJ7Mzd3IZFvh2MvnfYdnnb2c-r6XFK_IOr50PL3o,1321
11
11
  nonebot_plugin_werewolf/matchers/edit_preset.py,sha256=gKd1csoR9u4VoeulTtxUUgxsOdgB0SDC1FMxMRdgw5I,8087
@@ -22,13 +22,13 @@ nonebot_plugin_werewolf/players/guard.py,sha256=gHPlIfBXtQ8MSoWvucIIq0XrJQx9KIIK
22
22
  nonebot_plugin_werewolf/players/hunter.py,sha256=q17IjSXt5knDAc49NbV0NCwNW7qASqODUNgC0L1zqeI,193
23
23
  nonebot_plugin_werewolf/players/idiot.py,sha256=jz13BN4BsYcmiZdTwSlXUm4yc0KR8GUmQmq2GI7_Kzo,1431
24
24
  nonebot_plugin_werewolf/players/joker.py,sha256=n_jlddGMpFJ0O0sc_1J6IybfPsPJSWeS4GC0S6IZoTk,826
25
- nonebot_plugin_werewolf/players/player.py,sha256=hEbGRF5vrgKYKKEC5DB3JUIG6SEyvoHTYGZUqHPjf8Q,6953
25
+ nonebot_plugin_werewolf/players/player.py,sha256=j6m75GxYx3dpsO1frfk48D6jFdSi8orqaIVB-jvRxU0,6962
26
26
  nonebot_plugin_werewolf/players/prophet.py,sha256=wrTR8BnktiZ8aEI4VzYa5kt4GXyFnyi5wZmCO933-rE,917
27
- nonebot_plugin_werewolf/players/werewolf.py,sha256=jZMzYE0LiwsxO8XHMlWRk25XO5gGe24t87NqzLc3I4A,3287
27
+ nonebot_plugin_werewolf/players/werewolf.py,sha256=qi6OLWnTWhe2O_XYEb80n9x_4j05qjWU2IwnG3vpMuA,3273
28
28
  nonebot_plugin_werewolf/players/witch.py,sha256=FOM_MMKUjO6QpOQK9JvHq9zwa4GTVCVnW7UtPgT_BTw,2360
29
29
  nonebot_plugin_werewolf/players/wolfking.py,sha256=Y_G1eDdFYGTip5Pl9Sz0OvPsP0aiiFH8NkfGiFu7r1E,438
30
- nonebot_plugin_werewolf-1.1.6.dist-info/LICENSE,sha256=B_WbEqjGr6GYVNfEJPY31T1Opik7OtgOkhRs4Ig3e2M,1064
31
- nonebot_plugin_werewolf-1.1.6.dist-info/METADATA,sha256=pBGQnG8aePnlLEcZBlF_BqrIe4Y4O8oMK5mFwPGLWwg,11954
32
- nonebot_plugin_werewolf-1.1.6.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
33
- nonebot_plugin_werewolf-1.1.6.dist-info/top_level.txt,sha256=wLTfg8sTKbH9lLT9LtU118C9cTspEBJareLsrYM52YA,24
34
- nonebot_plugin_werewolf-1.1.6.dist-info/RECORD,,
30
+ nonebot_plugin_werewolf-1.1.7.dist-info/LICENSE,sha256=B_WbEqjGr6GYVNfEJPY31T1Opik7OtgOkhRs4Ig3e2M,1064
31
+ nonebot_plugin_werewolf-1.1.7.dist-info/METADATA,sha256=lF1x5SYiaOUgjmow7_Kcb_eovQZbxvbN8dAJmc2aFkA,11990
32
+ nonebot_plugin_werewolf-1.1.7.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
33
+ nonebot_plugin_werewolf-1.1.7.dist-info/top_level.txt,sha256=wLTfg8sTKbH9lLT9LtU118C9cTspEBJareLsrYM52YA,24
34
+ nonebot_plugin_werewolf-1.1.7.dist-info/RECORD,,