nonebot-plugin-werewolf 1.1.0__py3-none-any.whl → 1.1.1__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.
@@ -8,7 +8,7 @@ require("nonebot_plugin_waiter")
8
8
  from . import matchers as matchers
9
9
  from .config import Config
10
10
 
11
- __version__ = "1.1.0"
11
+ __version__ = "1.1.1"
12
12
  __plugin_meta__ = PluginMetadata(
13
13
  name="狼人杀",
14
14
  description="适用于 Nonebot2 的狼人杀插件",
@@ -1,9 +1,8 @@
1
- from typing import Literal, overload
1
+ from typing import Literal, Self, overload
2
2
 
3
3
  from nonebot import get_plugin_config, logger
4
4
  from nonebot.compat import PYDANTIC_V2
5
5
  from pydantic import BaseModel, Field
6
- from typing_extensions import Self
7
6
 
8
7
  from .constant import (
9
8
  Role,
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from dataclasses import dataclass
3
+ import dataclasses
4
4
  from enum import Enum, auto
5
5
  from typing import TYPE_CHECKING
6
6
 
@@ -43,17 +43,17 @@ class KillReason(Enum):
43
43
  class GameStatus(Enum):
44
44
  GoodGuy = auto()
45
45
  Werewolf = auto()
46
- Unset = auto()
47
46
  Joker = auto()
48
47
 
49
48
 
50
- @dataclass
49
+ @dataclasses.dataclass
51
50
  class GameState:
52
51
  day: int
53
52
  killed: Player | None = None
54
53
  shoot: tuple[Player, Player] | tuple[None, None] = (None, None)
55
- protected: Player | None = None
56
- potion: tuple[Player | None, tuple[bool, bool]] = (None, (False, False))
54
+ antidote: set[Player] = dataclasses.field(default_factory=set)
55
+ poison: set[tuple[Player, Player]] = dataclasses.field(default_factory=set)
56
+ protected: set[Player] = dataclasses.field(default_factory=set)
57
57
 
58
58
 
59
59
  role_name_conv: dict[Role | RoleGroup, str] = {
@@ -3,8 +3,7 @@ from __future__ import annotations
3
3
  import asyncio
4
4
  import asyncio.timeouts
5
5
  import contextlib
6
- import random
7
- import time
6
+ import secrets
8
7
  from typing import TYPE_CHECKING, NoReturn
9
8
 
10
9
  from nonebot.log import logger
@@ -35,20 +34,19 @@ def init_players(bot: Bot, game: Game, players: dict[str, str]) -> PlayerSet:
35
34
  f"应为 {', '.join(map(str, role_preset))} 人, 传入{len(players)}人"
36
35
  )
37
36
 
37
+ w, p, c = preset
38
38
  roles: list[Role] = []
39
- roles.extend(config.werewolf_priority[: preset[0]])
40
- roles.extend(config.priesthood_proirity[: preset[1]])
41
- roles.extend([Role.Civilian] * preset[2])
39
+ roles.extend(config.werewolf_priority[:w])
40
+ roles.extend(config.priesthood_proirity[:p])
41
+ roles.extend([Role.Civilian] * c)
42
42
 
43
- r = random.Random(time.time()) # noqa: S311
44
-
45
- if roles.count(Role.Civilian) >= 2 and r.random() <= config.joker_probability:
43
+ if c >= 2 and secrets.randbelow(100) <= config.joker_probability * 100:
46
44
  roles.remove(Role.Civilian)
47
45
  roles.append(Role.Joker)
48
46
 
49
47
  shuffled: list[Role] = []
50
- for _ in range(len(players)):
51
- idx = r.randint(0, len(roles) - 1)
48
+ while roles:
49
+ idx = secrets.randbelow(len(roles))
52
50
  shuffled.append(roles.pop(idx))
53
51
 
54
52
  logger.debug(f"职业分配: {shuffled}")
@@ -107,7 +105,7 @@ class Game:
107
105
  msg.at(p.user_id)
108
106
  return msg
109
107
 
110
- def check_game_status(self) -> GameStatus:
108
+ def check_game_status(self) -> None:
111
109
  players = self.players.alive()
112
110
  w = players.select(RoleGroup.Werewolf)
113
111
  p = players.exclude(RoleGroup.Werewolf)
@@ -125,8 +123,6 @@ class Game:
125
123
  if not w.size:
126
124
  raise GameFinishedError(GameStatus.GoodGuy)
127
125
 
128
- return GameStatus.Unset
129
-
130
126
  def show_killed_players(self) -> str:
131
127
  msg = ""
132
128
 
@@ -220,7 +216,7 @@ class Game:
220
216
  await self.interact(Role.Witch, 60)
221
217
  # 否则等待 5-20s
222
218
  else:
223
- await asyncio.sleep(random.uniform(5, 20)) # noqa: S311
219
+ await asyncio.sleep(5 + secrets.randbelow(15))
224
220
 
225
221
  async def handle_new_dead(self, players: Player | PlayerSet) -> None:
226
222
  if isinstance(players, Player):
@@ -369,28 +365,28 @@ class Game:
369
365
  self.interact(Role.Prophet, 60),
370
366
  self.interact(Role.Guard, 60),
371
367
  players.select(Role.Witch).broadcast("请等待狼人决定目标..."),
372
- players.select(Role.Civilian, RoleGroup.Others).broadcast(
373
- "请等待其他玩家结束交互..."
374
- ),
368
+ players.exclude(
369
+ RoleGroup.Werewolf, Role.Prophet, Role.Witch, Role.Guard
370
+ ).broadcast("请等待其他玩家结束交互..."),
375
371
  )
376
372
 
377
373
  # 狼人击杀目标
378
- killed = self.state.killed
379
- # 守卫保护目标
380
- protected = self.state.protected
381
- # 女巫的操作目标和内容
382
- potioned, (antidote, poison) = self.state.potion
383
-
384
- # 狼人未空刀,除非守卫保护或女巫使用解药,否则狼人正常击杀玩家
385
- if killed is not None and (
386
- not ((killed is protected) or (antidote and potioned is killed))
374
+ if (
375
+ (killed := self.state.killed) # 狼人未空刀
376
+ and killed not in self.state.protected # 守卫保护
377
+ and killed not in self.state.antidote # 女巫使用解药
387
378
  ):
379
+ # 狼人正常击杀玩家
388
380
  await killed.kill(
389
- KillReason.Werewolf, *players.select(RoleGroup.Werewolf)
381
+ KillReason.Werewolf,
382
+ *players.select(RoleGroup.Werewolf),
390
383
  )
391
- # 如果女巫使用毒药且守卫未保护,杀死该玩家
392
- if poison and (potioned is not None) and (potioned is not protected):
393
- await potioned.kill(KillReason.Poison, *players.select(Role.Witch))
384
+
385
+ # 女巫操作目标
386
+ for witch, potioned in self.state.poison:
387
+ if potioned not in self.state.protected: # 守卫未保护
388
+ # 女巫毒杀玩家
389
+ await potioned.kill(KillReason.Poison, witch)
394
390
 
395
391
  day_count += 1
396
392
  msg = UniMessage.text(f"『第{day_count}天』天亮了...\n")
@@ -440,8 +436,6 @@ class Game:
440
436
  winner = "狼人"
441
437
  case GameStatus.Joker:
442
438
  winner = "小丑"
443
- case GameStatus.Unset:
444
- raise RuntimeError(f"错误的游戏状态: {status!r}")
445
439
 
446
440
  msg = UniMessage.text(f"🎉游戏结束,{winner}获胜\n\n")
447
441
  for p in sorted(self.players, key=lambda p: (p.role.value, p.user_id)):
@@ -307,24 +307,6 @@ class Witch(Player):
307
307
  antidote: int = 1
308
308
  poison: int = 1
309
309
 
310
- def set_state(
311
- self,
312
- *,
313
- antidote: Player | None = None,
314
- posion: Player | None = None,
315
- ) -> None:
316
- if antidote is not None:
317
- self.antidote = 0
318
- self.selected = antidote
319
- self.game.state.potion = (antidote, (True, False))
320
- elif posion is not None:
321
- self.poison = 0
322
- self.selected = posion
323
- self.game.state.potion = (posion, (False, True))
324
- else:
325
- self.selected = None
326
- self.game.state.potion = (None, (False, False))
327
-
328
310
  async def handle_killed(self) -> bool:
329
311
  msg = UniMessage()
330
312
  if (killed := self.game.state.killed) is not None:
@@ -343,7 +325,7 @@ class Witch(Player):
343
325
  text = await self.receive_text()
344
326
  if text == "1":
345
327
  self.antidote = 0
346
- self.set_state(antidote=killed)
328
+ self.game.state.antidote.add(killed)
347
329
  await self.send(f"你对 {killed.name} 使用了解药,回合结束")
348
330
  return True
349
331
  if text == "/stop":
@@ -357,7 +339,6 @@ class Witch(Player):
357
339
 
358
340
  if not self.poison:
359
341
  await self.send("你没有可以使用的药水,回合结束")
360
- self.set_state()
361
342
  return
362
343
 
363
344
  players = self.game.players.alive()
@@ -377,13 +358,12 @@ class Witch(Player):
377
358
  break
378
359
  if text == "/stop":
379
360
  await self.send("你选择不使用毒药,回合结束")
380
- self.set_state()
381
361
  return
382
362
  await self.send("输入错误: 请发送玩家编号或 “/stop”")
383
363
 
384
364
  self.poison = 0
385
- self.selected = player = players[selected]
386
- self.set_state(posion=player)
365
+ player = players[selected]
366
+ self.game.state.poison.add((self, player))
387
367
  await self.send(f"当前回合选择对玩家 {player.name} 使用毒药\n回合结束")
388
368
 
389
369
 
@@ -418,7 +398,8 @@ class Guard(Player):
418
398
  break
419
399
  await self.send("输入错误,请发送编号选择玩家")
420
400
 
421
- self.game.state.protected = self.selected = players[selected]
401
+ self.selected = players[selected]
402
+ self.game.state.protected.add(self.selected)
422
403
  await self.send(f"本回合保护的玩家: {self.selected.name}")
423
404
 
424
405
 
@@ -462,11 +443,11 @@ class Joker(Player):
462
443
 
463
444
  @override
464
445
  async def kill(self, reason: KillReason, *killers: Player) -> bool:
465
- result = await super().kill(reason, *killers)
446
+ await super().kill(reason, *killers)
466
447
  if reason == KillReason.Vote:
467
448
  self.game.killed_players.append(self)
468
449
  raise GameFinishedError(GameStatus.Joker)
469
- return result
450
+ return True
470
451
 
471
452
 
472
453
  @register_role(Role.Civilian, RoleGroup.GoodGuy)
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nonebot-plugin-werewolf
3
- Version: 1.1.0
3
+ Version: 1.1.1
4
4
  Summary: 适用于 Nonebot2 的狼人杀插件
5
5
  Author-email: wyf7685 <wyf7685@163.com>
6
6
  License: MIT
7
- Requires-Python: >=3.10
7
+ Requires-Python: >=3.11
8
8
  Description-Content-Type: text/markdown
9
9
  License-File: LICENSE
10
10
  Requires-Dist: nonebot2 >=2.3.3
@@ -26,13 +26,18 @@ _✨ 简单的狼人杀插件 ✨_
26
26
 
27
27
  [![license](https://img.shields.io/github/license/wyf7685/nonebot-plugin-werewolf.svg)](./LICENSE)
28
28
  [![pypi](https://img.shields.io/pypi/v/nonebot-plugin-werewolf?logo=python&logoColor=edb641)](https://pypi.python.org/pypi/nonebot-plugin-werewolf)
29
- [![python](https://img.shields.io/badge/python-3.10+-blue?logo=python&logoColor=edb641)](https://www.python.org/)
30
-
29
+ [![python](https://img.shields.io/badge/python-3.11+-blue?logo=python&logoColor=edb641)](https://www.python.org/)
31
30
  [![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
31
+ [![ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
32
32
  [![isort](https://img.shields.io/badge/%20imports-isort-%231674b1)](https://pycqa.github.io/isort/)
33
33
  [![black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
34
34
  [![pyright](https://img.shields.io/badge/types-pyright-797952.svg?logo=python&logoColor=edb641)](https://github.com/Microsoft/pyright)
35
- [![ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
35
+
36
+ [![commits](https://img.shields.io/github/commit-activity/w/wyf7685/nonebot-plugin-werewolf)](https://github.com/wyf7685/nonebot-plugin-werewolf/commits)
37
+ [![wakatime](https://wakatime.com/badge/user/b097681b-c224-44ec-8e04-e1cf71744655/project/70a7f68d-5625-4989-9476-be6877408332.svg)](https://wakatime.com/badge/user/b097681b-c224-44ec-8e04-e1cf71744655/project/70a7f68d-5625-4989-9476-be6877408332)
38
+ [![pre-commit](https://results.pre-commit.ci/badge/github/wyf7685/nonebot-plugin-werewolf/master.svg)](https://results.pre-commit.ci/latest/github/wyf7685/nonebot-plugin-werewolf/master)
39
+ [![pyright](https://github.com/wyf7685/nonebot-plugin-werewolf/actions/workflows/pyright.yml/badge.svg?branch=master&event=push)](https://github.com/wyf7685/nonebot-plugin-werewolf/actions/workflows/pyright.yml)
40
+ [![publish](https://github.com/wyf7685/nonebot-plugin-werewolf/actions/workflows/pypi-publish.yml/badge.svg)](https://github.com/wyf7685/nonebot-plugin-werewolf/actions/workflows/pypi-publish.yml)
36
41
 
37
42
  [![NoneBot Registry](https://img.shields.io/endpoint?url=https%3A%2F%2Fnbbdg.lgc2333.top%2Fplugin%2Fnonebot-plugin-werewolf)](https://registry.nonebot.dev/plugin/nonebot-plugin-werewolf:nonebot_plugin_werewolf)
38
43
  [![Supported Adapters](https://img.shields.io/endpoint?url=https%3A%2F%2Fnbbdg.lgc2333.top%2Fplugin-adapters%2Fnonebot-plugin-werewolf)](https://registry.nonebot.dev/plugin/nonebot-plugin-werewolf:nonebot_plugin_werewolf)
@@ -45,6 +50,10 @@ _✨ 简单的狼人杀插件 ✨_
45
50
 
46
51
  ## 💿 安装
47
52
 
53
+ > [!note]
54
+ >
55
+ > 请确保 NoneBot2 使用的 Python 解释器版本 >=3.11
56
+
48
57
  <details open>
49
58
  <summary>使用 nb-cli 安装</summary>
50
59
  在 nonebot2 项目的根目录下打开命令行, 输入以下指令即可安装
@@ -231,6 +240,11 @@ werewolf__priesthood_proirity=[11, 12, 13, 14, 15]
231
240
 
232
241
  <!-- CHANGELOG -->
233
242
 
243
+ - 2024.09.11 v1.1.1
244
+
245
+ - 修改 Python 需求为 `>=3.11`
246
+ - 优化交互结果处理 ~~_可以在一局游戏中加入多个女巫了_~~
247
+
234
248
  - 2024.09.09 v1.1.0
235
249
 
236
250
  - 新增职业 `小丑`
@@ -0,0 +1,15 @@
1
+ nonebot_plugin_werewolf/__init__.py,sha256=I6lFyJgJ4Tz4t8gS2lrsx-7paN2h_PNoGjGR8ryvSeU,734
2
+ nonebot_plugin_werewolf/config.py,sha256=gl_ujiY-8fxQ5hN9_gjL1WpJJR5UOBIRBGofbLcQzaE,2900
3
+ nonebot_plugin_werewolf/constant.py,sha256=1XNTcTicil90WSviI2e33FX0LstWXl1aV2ugbhsDlIk,1909
4
+ nonebot_plugin_werewolf/exception.py,sha256=Ui8rB1sBg6_p8JHSvrjCpUaXJlgrM_9XsLJ09k8F1bg,391
5
+ nonebot_plugin_werewolf/game.py,sha256=Y9MQASaNdm2uuNw37IjdQEQzikWmbql_PWLHaL7lcB8,17414
6
+ nonebot_plugin_werewolf/matchers.py,sha256=5FioDfARlxTEibGL1JMKUMzmTzOwljjWKAJxBnyzaYs,2106
7
+ nonebot_plugin_werewolf/ob11_ext.py,sha256=P8uc3AdN5K5MzJaK80WDK85VKFg_CK5avDHu7ueMkho,2418
8
+ nonebot_plugin_werewolf/player.py,sha256=ifqTBfNepQY5FCY7hZoUA6DcObkcFEiNmKEeRkitJqY,14749
9
+ nonebot_plugin_werewolf/player_set.py,sha256=AI8v6KjH9ACtP4lvrd5IJfayan0qALmpTRLC3s_3DFw,2754
10
+ nonebot_plugin_werewolf/utils.py,sha256=l7oNSK471SV5CaB1eEVyZm10XSBFf_TwNotvb30U6vE,6835
11
+ nonebot_plugin_werewolf-1.1.1.dist-info/LICENSE,sha256=B_WbEqjGr6GYVNfEJPY31T1Opik7OtgOkhRs4Ig3e2M,1064
12
+ nonebot_plugin_werewolf-1.1.1.dist-info/METADATA,sha256=5ikh-mdZTIObaFs9w7qgzrgsx31lCxB5Ag--C8stl0g,10447
13
+ nonebot_plugin_werewolf-1.1.1.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
14
+ nonebot_plugin_werewolf-1.1.1.dist-info/top_level.txt,sha256=wLTfg8sTKbH9lLT9LtU118C9cTspEBJareLsrYM52YA,24
15
+ nonebot_plugin_werewolf-1.1.1.dist-info/RECORD,,
@@ -1,15 +0,0 @@
1
- nonebot_plugin_werewolf/__init__.py,sha256=A2138SSGhd2FdZ8V9Op50k5-pdfIVSfT0xBNG6qWacM,734
2
- nonebot_plugin_werewolf/config.py,sha256=8HnSt7sk8WIwnQlHm0tLdiSk6OTwbysDwoQf8cvFgoE,2929
3
- nonebot_plugin_werewolf/constant.py,sha256=yrhyw67KTaZ7DoGWZl3USWheaHSsUI94kl6ChDs0phI,1829
4
- nonebot_plugin_werewolf/exception.py,sha256=Ui8rB1sBg6_p8JHSvrjCpUaXJlgrM_9XsLJ09k8F1bg,391
5
- nonebot_plugin_werewolf/game.py,sha256=t1Gq-adeD5xprDH4uxfGF70wV_N-YZKBXn2CrMEH5Sc,17761
6
- nonebot_plugin_werewolf/matchers.py,sha256=5FioDfARlxTEibGL1JMKUMzmTzOwljjWKAJxBnyzaYs,2106
7
- nonebot_plugin_werewolf/ob11_ext.py,sha256=P8uc3AdN5K5MzJaK80WDK85VKFg_CK5avDHu7ueMkho,2418
8
- nonebot_plugin_werewolf/player.py,sha256=IdkVKt31DMw6W_0idW04c-ix6JVtg7VPl7ZaegxUN0Y,15360
9
- nonebot_plugin_werewolf/player_set.py,sha256=AI8v6KjH9ACtP4lvrd5IJfayan0qALmpTRLC3s_3DFw,2754
10
- nonebot_plugin_werewolf/utils.py,sha256=l7oNSK471SV5CaB1eEVyZm10XSBFf_TwNotvb30U6vE,6835
11
- nonebot_plugin_werewolf-1.1.0.dist-info/LICENSE,sha256=B_WbEqjGr6GYVNfEJPY31T1Opik7OtgOkhRs4Ig3e2M,1064
12
- nonebot_plugin_werewolf-1.1.0.dist-info/METADATA,sha256=6hjSQYd7yYi9UgZ4OLqDUDwHhL6imH21MMDEu9hS6AA,9234
13
- nonebot_plugin_werewolf-1.1.0.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
14
- nonebot_plugin_werewolf-1.1.0.dist-info/top_level.txt,sha256=wLTfg8sTKbH9lLT9LtU118C9cTspEBJareLsrYM52YA,24
15
- nonebot_plugin_werewolf-1.1.0.dist-info/RECORD,,