nonebot-plugin-werewolf 1.1.2__py3-none-any.whl → 1.1.5__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.
Files changed (35) hide show
  1. nonebot_plugin_werewolf/__init__.py +9 -4
  2. nonebot_plugin_werewolf/config.py +11 -15
  3. nonebot_plugin_werewolf/constant.py +40 -3
  4. nonebot_plugin_werewolf/game.py +219 -181
  5. nonebot_plugin_werewolf/matchers/__init__.py +2 -0
  6. nonebot_plugin_werewolf/matchers/depends.py +38 -0
  7. nonebot_plugin_werewolf/matchers/message_in_game.py +25 -0
  8. nonebot_plugin_werewolf/matchers/poke/__init__.py +8 -0
  9. nonebot_plugin_werewolf/matchers/poke/chronocat_poke.py +117 -0
  10. nonebot_plugin_werewolf/matchers/poke/ob11_poke.py +80 -0
  11. nonebot_plugin_werewolf/matchers/start_game.py +202 -0
  12. nonebot_plugin_werewolf/player_set.py +37 -36
  13. nonebot_plugin_werewolf/players/__init__.py +10 -0
  14. nonebot_plugin_werewolf/players/can_shoot.py +53 -0
  15. nonebot_plugin_werewolf/players/civilian.py +7 -0
  16. nonebot_plugin_werewolf/players/guard.py +30 -0
  17. nonebot_plugin_werewolf/players/hunter.py +8 -0
  18. nonebot_plugin_werewolf/players/idiot.py +44 -0
  19. nonebot_plugin_werewolf/players/joker.py +21 -0
  20. nonebot_plugin_werewolf/players/player.py +233 -0
  21. nonebot_plugin_werewolf/players/prophet.py +22 -0
  22. nonebot_plugin_werewolf/players/werewolf.py +89 -0
  23. nonebot_plugin_werewolf/players/witch.py +66 -0
  24. nonebot_plugin_werewolf/players/wolfking.py +14 -0
  25. nonebot_plugin_werewolf/utils.py +58 -173
  26. {nonebot_plugin_werewolf-1.1.2.dist-info → nonebot_plugin_werewolf-1.1.5.dist-info}/METADATA +24 -4
  27. nonebot_plugin_werewolf-1.1.5.dist-info/RECORD +31 -0
  28. {nonebot_plugin_werewolf-1.1.2.dist-info → nonebot_plugin_werewolf-1.1.5.dist-info}/WHEEL +1 -1
  29. nonebot_plugin_werewolf/_timeout.py +0 -110
  30. nonebot_plugin_werewolf/matchers.py +0 -62
  31. nonebot_plugin_werewolf/ob11_ext.py +0 -72
  32. nonebot_plugin_werewolf/player.py +0 -462
  33. nonebot_plugin_werewolf-1.1.2.dist-info/RECORD +0 -16
  34. {nonebot_plugin_werewolf-1.1.2.dist-info → nonebot_plugin_werewolf-1.1.5.dist-info}/LICENSE +0 -0
  35. {nonebot_plugin_werewolf-1.1.2.dist-info → nonebot_plugin_werewolf-1.1.5.dist-info}/top_level.txt +0 -0
@@ -1,462 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import asyncio
4
- import weakref
5
- from dataclasses import dataclass
6
- from typing import TYPE_CHECKING, ClassVar, TypeVar, final
7
-
8
- from nonebot.log import logger
9
- from nonebot_plugin_alconna.uniseg import Receipt, Target, UniMessage
10
- from typing_extensions import override
11
-
12
- from .constant import GameStatus, KillReason, Role, RoleGroup, role_name_conv
13
- from .exception import GameFinished
14
- from .utils import InputStore, check_index
15
-
16
- if TYPE_CHECKING:
17
- from collections.abc import Callable
18
-
19
- from nonebot.adapters import Bot
20
-
21
- from .game import Game
22
- from .player_set import PlayerSet
23
-
24
-
25
- P = TypeVar("P", bound=type["Player"])
26
- PLAYER_CLASS: dict[Role, type[Player]] = {}
27
-
28
-
29
- def register_role(role: Role, role_group: RoleGroup, /) -> Callable[[P], P]:
30
- def decorator(cls: P, /) -> P:
31
- cls.role = role
32
- cls.role_group = role_group
33
- PLAYER_CLASS[role] = cls
34
- return cls
35
-
36
- return decorator
37
-
38
-
39
- @dataclass
40
- class KillInfo:
41
- reason: KillReason
42
- killers: PlayerSet
43
-
44
-
45
- class Player:
46
- role: ClassVar[Role]
47
- role_group: ClassVar[RoleGroup]
48
-
49
- bot: Bot
50
- _game_ref: weakref.ReferenceType[Game]
51
- user: Target
52
- name: str
53
- alive: bool = True
54
- killed: asyncio.Event
55
- kill_info: KillInfo | None = None
56
- selected: Player | None = None
57
-
58
- @final
59
- def __init__(self, bot: Bot, game: Game, user: Target, name: str) -> None:
60
- self.bot = bot
61
- self._game_ref = weakref.ref(game)
62
- self.user = user
63
- self.name = name
64
- self.killed = asyncio.Event()
65
-
66
- @final
67
- @classmethod
68
- def new(
69
- cls,
70
- role: Role,
71
- bot: Bot,
72
- game: Game,
73
- user: Target,
74
- name: str,
75
- ) -> Player:
76
- if role not in PLAYER_CLASS:
77
- raise ValueError(f"Unexpected role: {role!r}")
78
-
79
- return PLAYER_CLASS[role](bot, game, user, name)
80
-
81
- def __repr__(self) -> str:
82
- return f"<{self.role_name}: user={self.name!r} alive={self.alive}>"
83
-
84
- @property
85
- def game(self) -> Game:
86
- if game := self._game_ref():
87
- return game
88
- raise ValueError("Game not exist")
89
-
90
- @property
91
- def user_id(self) -> str:
92
- return self.user.id
93
-
94
- @property
95
- def role_name(self) -> str:
96
- return role_name_conv[self.role]
97
-
98
- @final
99
- def _log(self, text: str) -> None:
100
- text = text.replace("\n", "\\n")
101
- logger.opt(colors=True).info(
102
- f"<b><e>{self.game.group.id}</e></b> | "
103
- f"[<b><m>{self.role_name}</m></b>] "
104
- f"<y>{self.name}</y>(<b><e>{self.user_id}</e></b>) | "
105
- f"{text}",
106
- )
107
-
108
- @final
109
- async def send(self, message: str | UniMessage) -> Receipt:
110
- if isinstance(message, str):
111
- message = UniMessage.text(message)
112
-
113
- self._log(f"<g>Send</g> | {message}")
114
- return await message.send(target=self.user, bot=self.bot)
115
-
116
- @final
117
- async def receive(self, prompt: str | UniMessage | None = None) -> UniMessage:
118
- if prompt:
119
- await self.send(prompt)
120
-
121
- result = await InputStore.fetch(self.user.id)
122
- self._log(f"<y>Recv</y> | {result}")
123
- return result
124
-
125
- @final
126
- async def receive_text(self) -> str:
127
- return (await self.receive()).extract_plain_text()
128
-
129
- async def interact(self) -> None:
130
- return
131
-
132
- async def notify_role(self) -> None:
133
- await self.send(f"你的身份: {self.role_name}")
134
-
135
- async def kill(self, reason: KillReason, *killers: Player) -> bool:
136
- from .player_set import PlayerSet
137
-
138
- self.alive = False
139
- self.kill_info = KillInfo(reason, PlayerSet(killers))
140
- return True
141
-
142
- async def post_kill(self) -> None:
143
- self.killed.set()
144
-
145
- async def vote(self, players: PlayerSet) -> tuple[Player, Player] | None:
146
- await self.send(
147
- f"请选择需要投票的玩家:\n{players.show()}"
148
- "\n\n发送编号选择玩家\n发送 “/stop” 弃票"
149
- )
150
-
151
- while True:
152
- text = await self.receive_text()
153
- if text == "/stop":
154
- await self.send("你选择了弃票")
155
- return None
156
- index = check_index(text, len(players))
157
- if index is not None:
158
- selected = index - 1
159
- break
160
- await self.send("输入错误,请发送编号选择玩家")
161
-
162
- player = players[selected]
163
- await self.send(f"投票的玩家: {player.name}")
164
- return self, player
165
-
166
-
167
- class CanShoot(Player):
168
- @override
169
- async def post_kill(self) -> None:
170
- if self.kill_info and self.kill_info.reason == KillReason.Poison:
171
- await self.send("你昨晚被女巫毒杀,无法使用技能")
172
- return await super().post_kill()
173
-
174
- await self.game.send(
175
- UniMessage.text(f"{self.role_name} ")
176
- .at(self.user_id)
177
- .text(f" 死了\n请{self.role_name}决定击杀目标...")
178
- )
179
-
180
- self.game.state.shoot = (None, None)
181
- shoot = await self.shoot()
182
-
183
- if shoot is not None:
184
- self.game.state.shoot = (self, shoot)
185
- await self.send(
186
- UniMessage.text(f"{self.role_name} ")
187
- .at(self.user_id)
188
- .text(" 射杀了玩家 ")
189
- .at(shoot.user_id)
190
- )
191
- await shoot.kill(KillReason.Shoot, self)
192
- else:
193
- await self.send(f"{self.role_name}选择了取消技能")
194
- return await super().post_kill()
195
-
196
- async def shoot(self) -> Player | None:
197
- players = self.game.players.alive().exclude(self)
198
- await self.send(
199
- "请选择需要射杀的玩家:\n"
200
- + players.show()
201
- + "\n\n发送编号选择玩家"
202
- + "\n发送 “/stop” 取消技能"
203
- )
204
-
205
- while True:
206
- text = await self.receive_text()
207
- if text == "/stop":
208
- await self.send("已取消技能")
209
- return None
210
- index = check_index(text, len(players))
211
- if index is not None:
212
- selected = index - 1
213
- break
214
- await self.send("输入错误,请发送编号选择玩家")
215
-
216
- await self.send(f"选择射杀的玩家: {players[selected].name}")
217
- return players[selected]
218
-
219
-
220
- @register_role(Role.Werewolf, RoleGroup.Werewolf)
221
- class Werewolf(Player):
222
- @override
223
- async def notify_role(self) -> None:
224
- await super().notify_role()
225
- partners = self.game.players.alive().select(RoleGroup.Werewolf).exclude(self)
226
- if partners:
227
- await self.send(
228
- "你的队友:\n"
229
- + "\n".join(f" {p.role_name}: {p.name}" for p in partners)
230
- )
231
-
232
- @override
233
- async def interact(self) -> None:
234
- players = self.game.players.alive()
235
- partners = players.select(RoleGroup.Werewolf).exclude(self)
236
-
237
- # 避免阻塞
238
- def broadcast(msg: str | UniMessage) -> asyncio.Task[None]:
239
- return asyncio.create_task(partners.broadcast(msg))
240
-
241
- msg = UniMessage()
242
- if partners:
243
- msg = (
244
- msg.text("你的队友:\n")
245
- .text("\n".join(f" {p.role_name}: {p.name}" for p in partners))
246
- .text("\n所有私聊消息将被转发至队友\n\n")
247
- )
248
- await self.send(
249
- msg.text("请选择今晚的目标:\n")
250
- .text(players.show())
251
- .text("\n\n发送编号选择玩家")
252
- .text("\n发送 “/stop” 结束回合")
253
- .text("\n\n意见未统一将空刀")
254
- )
255
-
256
- selected = None
257
- finished = False
258
- while selected is None or not finished:
259
- input_msg = await self.receive()
260
- text = input_msg.extract_plain_text()
261
- index = check_index(text, len(players))
262
- if index is not None:
263
- selected = index - 1
264
- msg = f"当前选择玩家: {players[selected].name}"
265
- await self.send(f"{msg}\n发送 “/stop” 结束回合")
266
- broadcast(f"队友 {self.name} {msg}")
267
- if text == "/stop":
268
- if selected is not None:
269
- finished = True
270
- await self.send("你已结束当前回合")
271
- broadcast(f"队友 {self.name} 结束当前回合")
272
- else:
273
- await self.send("当前未选择玩家,无法结束回合")
274
- broadcast(UniMessage.text(f"队友 {self.name}:\n") + input_msg)
275
-
276
- self.selected = players[selected]
277
-
278
-
279
- @register_role(Role.WolfKing, RoleGroup.Werewolf)
280
- class WolfKing(CanShoot, Werewolf):
281
- @override
282
- async def notify_role(self) -> None:
283
- await super().notify_role()
284
- await self.send("作为狼王,你可以在死后射杀一名玩家")
285
-
286
-
287
- @register_role(Role.Prophet, RoleGroup.GoodGuy)
288
- class Prophet(Player):
289
- @override
290
- async def interact(self) -> None:
291
- players = self.game.players.alive().exclude(self)
292
- await self.send(
293
- UniMessage.text("请选择需要查验身份的玩家:\n")
294
- .text(players.show())
295
- .text("\n\n发送编号选择玩家")
296
- )
297
-
298
- while True:
299
- text = await self.receive_text()
300
- index = check_index(text, len(players))
301
- if index is not None:
302
- selected = index - 1
303
- break
304
- await self.send("输入错误,请发送编号选择玩家")
305
-
306
- player = players[selected]
307
- result = "狼人" if player.role_group == RoleGroup.Werewolf else "好人"
308
- await self.send(f"玩家 {player.name} 的阵营是『{result}』")
309
-
310
-
311
- @register_role(Role.Witch, RoleGroup.GoodGuy)
312
- class Witch(Player):
313
- antidote: int = 1
314
- poison: int = 1
315
-
316
- async def handle_killed(self) -> bool:
317
- msg = UniMessage()
318
- if (killed := self.game.state.killed) is not None:
319
- msg.text(f"今晚 {killed.name} 被刀了\n\n")
320
- else:
321
- await self.send("今晚没有人被刀")
322
- return False
323
-
324
- if not self.antidote:
325
- await self.send(msg.text("你已经用过解药了"))
326
- return False
327
-
328
- await self.send(msg.text("使用解药请发送 “1”\n不使用解药请发送 “/stop”"))
329
-
330
- while True:
331
- text = await self.receive_text()
332
- if text == "1":
333
- self.antidote = 0
334
- self.selected = killed
335
- self.game.state.antidote.add(killed)
336
- await self.send(f"你对 {killed.name} 使用了解药,回合结束")
337
- return True
338
- if text == "/stop":
339
- return False
340
- await self.send("输入错误: 请输入 “1” 或 “/stop”")
341
-
342
- @override
343
- async def interact(self) -> None:
344
- if await self.handle_killed():
345
- return
346
-
347
- if not self.poison:
348
- await self.send("你没有可以使用的药水,回合结束")
349
- return
350
-
351
- players = self.game.players.alive()
352
- await self.send(
353
- UniMessage.text("你有一瓶毒药\n")
354
- .text("玩家列表:\n")
355
- .text(players.show())
356
- .text("\n\n发送玩家编号使用毒药")
357
- .text("\n发送 “/stop” 结束回合(不使用药水)")
358
- )
359
-
360
- while True:
361
- text = await self.receive_text()
362
- index = check_index(text, len(players))
363
- if index is not None:
364
- selected = index - 1
365
- break
366
- if text == "/stop":
367
- await self.send("你选择不使用毒药,回合结束")
368
- return
369
- await self.send("输入错误: 请发送玩家编号或 “/stop”")
370
-
371
- self.poison = 0
372
- self.selected = players[selected]
373
- self.game.state.poison.add(self)
374
- await self.send(f"当前回合选择对玩家 {self.selected.name} 使用毒药\n回合结束")
375
-
376
-
377
- @register_role(Role.Hunter, RoleGroup.GoodGuy)
378
- class Hunter(CanShoot, Player):
379
- pass
380
-
381
-
382
- @register_role(Role.Guard, RoleGroup.GoodGuy)
383
- class Guard(Player):
384
- @override
385
- async def interact(self) -> None:
386
- players = self.game.players.alive()
387
- await self.send(
388
- UniMessage.text("请选择需要保护的玩家:\n")
389
- .text(players.show())
390
- .text("\n\n发送编号选择玩家")
391
- .text("\n发送 “/stop” 结束回合")
392
- )
393
-
394
- while True:
395
- text = await self.receive_text()
396
- if text == "/stop":
397
- await self.send("你选择了取消,回合结束")
398
- return
399
- index = check_index(text, len(players))
400
- if index is not None:
401
- selected = index - 1
402
- if players[selected] is self.selected:
403
- await self.send("守卫不能连续两晚保护同一目标")
404
- continue
405
- break
406
- await self.send("输入错误,请发送编号选择玩家")
407
-
408
- self.selected = players[selected]
409
- self.game.state.protected.add(self.selected)
410
- await self.send(f"本回合保护的玩家: {self.selected.name}")
411
-
412
-
413
- @register_role(Role.Idiot, RoleGroup.GoodGuy)
414
- class Idiot(Player):
415
- voted: bool = False
416
-
417
- @override
418
- async def notify_role(self) -> None:
419
- await super().notify_role()
420
- await self.send(
421
- "作为白痴,你可以在首次被投票放逐时免疫放逐,但在之后的投票中无法继续投票"
422
- )
423
-
424
- @override
425
- async def kill(self, reason: KillReason, *killers: Player) -> bool:
426
- if reason == KillReason.Vote and not self.voted:
427
- self.voted = True
428
- await self.game.send(
429
- UniMessage.at(self.user_id)
430
- .text(" 的身份是白痴\n")
431
- .text("免疫本次投票放逐,且接下来无法参与投票"),
432
- )
433
- return False
434
- return await super().kill(reason, *killers)
435
-
436
- @override
437
- async def vote(self, players: PlayerSet) -> tuple[Player, Player] | None:
438
- if self.voted:
439
- await self.send("你已经发动过白痴身份的技能,无法参与本次投票")
440
- return None
441
- return await super().vote(players)
442
-
443
-
444
- @register_role(Role.Joker, RoleGroup.Others)
445
- class Joker(Player):
446
- @override
447
- async def notify_role(self) -> None:
448
- await super().notify_role()
449
- await self.send("你的胜利条件: 被投票放逐")
450
-
451
- @override
452
- async def kill(self, reason: KillReason, *killers: Player) -> bool:
453
- await super().kill(reason, *killers)
454
- if reason == KillReason.Vote:
455
- self.game.killed_players.append(self)
456
- raise GameFinished(GameStatus.Joker)
457
- return True
458
-
459
-
460
- @register_role(Role.Civilian, RoleGroup.GoodGuy)
461
- class Civilian(Player):
462
- pass
@@ -1,16 +0,0 @@
1
- nonebot_plugin_werewolf/__init__.py,sha256=qCS-gVMiMZcF9yT3SIMe8np-wmKX2vGT5DxYJ2jRxgY,734
2
- nonebot_plugin_werewolf/_timeout.py,sha256=MVkA5oMoxOTV8Luc0BH2QPP8wwz4Tr9CJjeYgiPu_O4,3649
3
- nonebot_plugin_werewolf/config.py,sha256=a5dyF21_3T1o6AFrK6onIiwXUNyEwj5Yg-c7V9qoueI,2923
4
- nonebot_plugin_werewolf/constant.py,sha256=gUApZs-N3DRSwV2_B05mPi6fCmynSzVZb28fgqdLX6E,1894
5
- nonebot_plugin_werewolf/exception.py,sha256=YSwxeogIB0YJqH9MP1bgxojiu-I_xQE44XnSk5bC1AQ,400
6
- nonebot_plugin_werewolf/game.py,sha256=FqYAs8CalCRUKoyPTpqvKK5iAelV9ZQ5EmefNo5wE9E,17459
7
- nonebot_plugin_werewolf/matchers.py,sha256=Xnq1n4xobdr1T_8ilsQfXns_iEsNx_h_-aQ5jZwpWx0,2080
8
- nonebot_plugin_werewolf/ob11_ext.py,sha256=P8uc3AdN5K5MzJaK80WDK85VKFg_CK5avDHu7ueMkho,2418
9
- nonebot_plugin_werewolf/player.py,sha256=2jWlJOIWNK3JVVKNaqROfvKPRQfopDXv7KAXGtXNmh4,14973
10
- nonebot_plugin_werewolf/player_set.py,sha256=7f4GHklp-wBGROddVqGUQAMeMzKlN2iec_w5UJQXvz0,2726
11
- nonebot_plugin_werewolf/utils.py,sha256=LiNitH3UYNqXnCOOAzjKifVpJNhurIt4wYgWVBUW1Zo,6811
12
- nonebot_plugin_werewolf-1.1.2.dist-info/LICENSE,sha256=B_WbEqjGr6GYVNfEJPY31T1Opik7OtgOkhRs4Ig3e2M,1064
13
- nonebot_plugin_werewolf-1.1.2.dist-info/METADATA,sha256=0W7Nb6qqwJvW7xVdLHxM4643DuJ3YN9683owdDoUOhs,10363
14
- nonebot_plugin_werewolf-1.1.2.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
15
- nonebot_plugin_werewolf-1.1.2.dist-info/top_level.txt,sha256=wLTfg8sTKbH9lLT9LtU118C9cTspEBJareLsrYM52YA,24
16
- nonebot_plugin_werewolf-1.1.2.dist-info/RECORD,,