nonebot-plugin-werewolf 1.1.5__py3-none-any.whl → 1.1.6__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 (31) hide show
  1. nonebot_plugin_werewolf/__init__.py +2 -1
  2. nonebot_plugin_werewolf/config.py +18 -55
  3. nonebot_plugin_werewolf/constant.py +14 -74
  4. nonebot_plugin_werewolf/exception.py +1 -1
  5. nonebot_plugin_werewolf/game.py +198 -216
  6. nonebot_plugin_werewolf/matchers/__init__.py +2 -0
  7. nonebot_plugin_werewolf/matchers/depends.py +17 -5
  8. nonebot_plugin_werewolf/matchers/edit_preset.py +263 -0
  9. nonebot_plugin_werewolf/matchers/message_in_game.py +8 -3
  10. nonebot_plugin_werewolf/matchers/start_game.py +140 -48
  11. nonebot_plugin_werewolf/matchers/superuser_ops.py +24 -0
  12. nonebot_plugin_werewolf/models.py +73 -0
  13. nonebot_plugin_werewolf/player_set.py +1 -1
  14. nonebot_plugin_werewolf/players/can_shoot.py +4 -3
  15. nonebot_plugin_werewolf/players/civilian.py +1 -1
  16. nonebot_plugin_werewolf/players/guard.py +2 -1
  17. nonebot_plugin_werewolf/players/hunter.py +1 -1
  18. nonebot_plugin_werewolf/players/idiot.py +1 -1
  19. nonebot_plugin_werewolf/players/joker.py +6 -2
  20. nonebot_plugin_werewolf/players/player.py +15 -24
  21. nonebot_plugin_werewolf/players/prophet.py +2 -1
  22. nonebot_plugin_werewolf/players/werewolf.py +16 -14
  23. nonebot_plugin_werewolf/players/witch.py +2 -1
  24. nonebot_plugin_werewolf/players/wolfking.py +1 -1
  25. nonebot_plugin_werewolf/utils.py +69 -5
  26. {nonebot_plugin_werewolf-1.1.5.dist-info → nonebot_plugin_werewolf-1.1.6.dist-info}/METADATA +67 -67
  27. nonebot_plugin_werewolf-1.1.6.dist-info/RECORD +34 -0
  28. {nonebot_plugin_werewolf-1.1.5.dist-info → nonebot_plugin_werewolf-1.1.6.dist-info}/WHEEL +1 -1
  29. nonebot_plugin_werewolf-1.1.5.dist-info/RECORD +0 -31
  30. {nonebot_plugin_werewolf-1.1.5.dist-info → nonebot_plugin_werewolf-1.1.6.dist-info}/LICENSE +0 -0
  31. {nonebot_plugin_werewolf-1.1.5.dist-info → nonebot_plugin_werewolf-1.1.6.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,8 @@
1
1
  from nonebot_plugin_alconna.uniseg import UniMessage
2
2
  from typing_extensions import override
3
3
 
4
- from ..constant import STOP_COMMAND_PROMPT, KillReason
4
+ from ..constant import STOP_COMMAND_PROMPT
5
+ from ..models import KillReason
5
6
  from .player import Player
6
7
 
7
8
 
@@ -13,9 +14,9 @@ class CanShoot(Player):
13
14
  return await super().post_kill()
14
15
 
15
16
  await self.game.send(
16
- UniMessage.text(f"🕵️{self.role_name} ")
17
+ UniMessage.text("🕵️玩家 ")
17
18
  .at(self.user_id)
18
- .text(f" 死了\n请{self.role_name}决定击杀目标...")
19
+ .text(" 死了\n请在私聊决定射杀目标...")
19
20
  )
20
21
 
21
22
  self.game.state.shoot = None
@@ -1,4 +1,4 @@
1
- from ..constant import Role, RoleGroup
1
+ from ..models import Role, RoleGroup
2
2
  from .player import Player
3
3
 
4
4
 
@@ -1,7 +1,8 @@
1
1
  from nonebot_plugin_alconna.uniseg import UniMessage
2
2
  from typing_extensions import override
3
3
 
4
- from ..constant import STOP_COMMAND_PROMPT, Role, RoleGroup
4
+ from ..constant import STOP_COMMAND_PROMPT
5
+ from ..models import Role, RoleGroup
5
6
  from .player import Player
6
7
 
7
8
 
@@ -1,4 +1,4 @@
1
- from ..constant import Role, RoleGroup
1
+ from ..models import Role, RoleGroup
2
2
  from .can_shoot import CanShoot
3
3
  from .player import Player
4
4
 
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING
5
5
  from nonebot_plugin_alconna.uniseg import UniMessage
6
6
  from typing_extensions import override
7
7
 
8
- from ..constant import KillReason, Role, RoleGroup
8
+ from ..models import KillReason, Role, RoleGroup
9
9
  from .player import Player
10
10
 
11
11
  if TYPE_CHECKING:
@@ -1,7 +1,9 @@
1
+ from typing import TYPE_CHECKING
2
+
1
3
  from typing_extensions import override
2
4
 
3
- from ..constant import GameStatus, KillReason, Role, RoleGroup
4
5
  from ..exception import GameFinished
6
+ from ..models import GameStatus, KillReason, Role, RoleGroup
5
7
  from .player import Player
6
8
 
7
9
 
@@ -16,6 +18,8 @@ class Joker(Player):
16
18
  async def kill(self, reason: KillReason, *killers: Player) -> bool:
17
19
  await super().kill(reason, *killers)
18
20
  if reason == KillReason.Vote:
19
- self.game.killed_players.append(self)
21
+ if TYPE_CHECKING:
22
+ assert self.kill_info is not None
23
+ self.game.killed_players.append((self.name, self.kill_info))
20
24
  raise GameFinished(GameStatus.Joker)
21
25
  return True
@@ -1,7 +1,6 @@
1
1
  import functools
2
2
  import weakref
3
3
  from collections.abc import Callable
4
- from dataclasses import dataclass
5
4
  from typing import TYPE_CHECKING, ClassVar, Final, TypeVar, final
6
5
 
7
6
  import anyio
@@ -11,16 +10,9 @@ from nonebot.utils import escape_tag
11
10
  from nonebot_plugin_alconna.uniseg import Receipt, Target, UniMessage
12
11
  from nonebot_plugin_uninfo import SceneType
13
12
 
14
- from ..constant import (
15
- STOP_COMMAND,
16
- STOP_COMMAND_PROMPT,
17
- KillReason,
18
- Role,
19
- RoleGroup,
20
- role_emoji,
21
- role_name_conv,
22
- )
23
- from ..utils import InputStore, as_player_set, check_index, link
13
+ from ..constant import STOP_COMMAND, STOP_COMMAND_PROMPT, role_emoji, role_name_conv
14
+ from ..models import KillInfo, KillReason, Role, RoleGroup
15
+ from ..utils import InputStore, check_index, link
24
16
 
25
17
  if TYPE_CHECKING:
26
18
  from ..game import Game
@@ -30,12 +22,6 @@ if TYPE_CHECKING:
30
22
  _P = TypeVar("_P", bound=type["Player"])
31
23
 
32
24
 
33
- @dataclass
34
- class KillInfo:
35
- reason: KillReason
36
- killers: "PlayerSet"
37
-
38
-
39
25
  class Player:
40
26
  __player_class: ClassVar[dict[Role, type["Player"]]] = {}
41
27
  role: ClassVar[Role]
@@ -140,7 +126,7 @@ class Player:
140
126
  return name
141
127
 
142
128
  @final
143
- async def _log(self, text: str) -> None:
129
+ def _log(self, text: str) -> None:
144
130
  text = text.replace("\n", "\\n")
145
131
  logger.opt(colors=True).info(
146
132
  f"{self.game.colored_name} | "
@@ -153,7 +139,7 @@ class Player:
153
139
  if isinstance(message, str):
154
140
  message = UniMessage.text(message)
155
141
 
156
- await self._log(f"<g>Send</g> | {escape_tag(str(message))}")
142
+ self._log(f"<g>Send</g> | {escape_tag(str(message))}")
157
143
  return await message.send(target=self.__user, bot=self.bot)
158
144
 
159
145
  @final
@@ -162,7 +148,7 @@ class Player:
162
148
  await self.send(prompt)
163
149
 
164
150
  result = await InputStore.fetch(self.user_id)
165
- await self._log(f"<y>Recv</y> | {escape_tag(str(result))}")
151
+ self._log(f"<y>Recv</y> | {escape_tag(str(result))}")
166
152
  return result
167
153
 
168
154
  @final
@@ -177,8 +163,9 @@ class Player:
177
163
  await self.send(f"⚙️你的身份: {role_emoji[self.role]}{self.role_name}")
178
164
 
179
165
  async def kill(self, reason: KillReason, *killers: "Player") -> bool:
180
- self.alive = False
181
- self.kill_info = KillInfo(reason=reason, killers=as_player_set(*killers))
166
+ if self.alive:
167
+ self.alive = False
168
+ self.kill_info = KillInfo(reason=reason, killers=[p.name for p in killers])
182
169
  return True
183
170
 
184
171
  async def post_kill(self) -> None:
@@ -213,9 +200,13 @@ class Player:
213
200
  self,
214
201
  players: "PlayerSet",
215
202
  *,
216
- on_stop: str | None = "ℹ️你选择了取消,回合结束",
217
- on_index_error: str = f"⚠️输入错误: 请发送玩家编号或 “{STOP_COMMAND_PROMPT}”",
203
+ on_stop: str | None = None,
204
+ on_index_error: str | None = None,
218
205
  ) -> "Player | None":
206
+ on_stop = on_stop or "ℹ️你选择了取消,回合结束"
207
+ on_index_error = (
208
+ on_index_error or f"⚠️输入错误: 请发送玩家编号或 “{STOP_COMMAND_PROMPT}”"
209
+ )
219
210
  selected = None
220
211
 
221
212
  while selected is None:
@@ -1,7 +1,8 @@
1
1
  from nonebot_plugin_alconna.uniseg import UniMessage
2
2
  from typing_extensions import override
3
3
 
4
- from ..constant import STOP_COMMAND_PROMPT, Role, RoleGroup
4
+ from ..constant import STOP_COMMAND_PROMPT
5
+ from ..models import Role, RoleGroup
5
6
  from .player import Player
6
7
 
7
8
 
@@ -1,12 +1,12 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
3
  import anyio
4
- from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
5
4
  from nonebot_plugin_alconna.uniseg import UniMessage
6
5
  from typing_extensions import override
7
6
 
8
- from ..constant import STOP_COMMAND, STOP_COMMAND_PROMPT, Role, RoleGroup
9
- from ..utils import check_index
7
+ from ..constant import STOP_COMMAND, STOP_COMMAND_PROMPT
8
+ from ..models import Role, RoleGroup
9
+ from ..utils import ObjectStream, check_index
10
10
  from .player import Player
11
11
 
12
12
  if TYPE_CHECKING:
@@ -28,8 +28,7 @@ class Werewolf(Player):
28
28
  async def _handle_interact(
29
29
  self,
30
30
  players: "PlayerSet",
31
- stream: MemoryObjectSendStream[str | UniMessage],
32
- finished: anyio.Event,
31
+ stream: ObjectStream[str | UniMessage],
33
32
  ) -> None:
34
33
  self.selected = None
35
34
 
@@ -46,7 +45,7 @@ class Werewolf(Player):
46
45
  if self.selected is not None:
47
46
  await self.send("✅你已结束当前回合")
48
47
  await stream.send(f"📝队友 {self.name} 结束当前回合")
49
- finished.set()
48
+ stream.close()
50
49
  return
51
50
  await self.send("⚠️当前未选择玩家,无法结束回合")
52
51
  else:
@@ -55,11 +54,15 @@ class Werewolf(Player):
55
54
  async def _handle_broadcast(
56
55
  self,
57
56
  partners: "PlayerSet",
58
- stream: MemoryObjectReceiveStream[str | UniMessage],
59
- finished: anyio.Event,
57
+ stream: ObjectStream[str | UniMessage],
60
58
  ) -> None:
61
- while not finished.is_set() or stream.statistics().tasks_waiting_receive:
62
- await partners.broadcast(await stream.receive())
59
+ while not stream.closed:
60
+ try:
61
+ message = await stream.recv()
62
+ except anyio.EndOfStream:
63
+ return
64
+
65
+ await partners.broadcast(message)
63
66
 
64
67
  @override
65
68
  async def interact(self) -> None:
@@ -81,9 +84,8 @@ class Werewolf(Player):
81
84
  .text("\n\n⚠️意见未统一将空刀")
82
85
  )
83
86
 
84
- send, recv = anyio.create_memory_object_stream[str | UniMessage]()
85
- finished = anyio.Event()
87
+ stream = ObjectStream[str | UniMessage](8)
86
88
 
87
89
  async with anyio.create_task_group() as tg:
88
- tg.start_soon(self._handle_interact, players, send, finished)
89
- tg.start_soon(self._handle_broadcast, partners, recv, finished)
90
+ tg.start_soon(self._handle_interact, players, stream)
91
+ tg.start_soon(self._handle_broadcast, partners, stream)
@@ -1,7 +1,8 @@
1
1
  from nonebot_plugin_alconna.uniseg import UniMessage
2
2
  from typing_extensions import override
3
3
 
4
- from ..constant import STOP_COMMAND_PROMPT, Role, RoleGroup
4
+ from ..constant import STOP_COMMAND_PROMPT
5
+ from ..models import Role, RoleGroup
5
6
  from ..utils import as_player_set
6
7
  from .player import Player
7
8
 
@@ -1,6 +1,6 @@
1
1
  from typing_extensions import override
2
2
 
3
- from ..constant import Role, RoleGroup
3
+ from ..models import Role, RoleGroup
4
4
  from .can_shoot import CanShoot
5
5
  from .player import Player
6
6
  from .werewolf import Werewolf
@@ -1,11 +1,15 @@
1
1
  import functools
2
+ import itertools
2
3
  from collections import defaultdict
3
- from typing import TYPE_CHECKING, Any, ClassVar, TypeVar
4
+ from typing import TYPE_CHECKING, Any, ClassVar, Generic, TypeVar, cast
4
5
 
5
6
  import anyio
7
+ import anyio.streams.memory
6
8
  from nonebot_plugin_alconna import UniMessage
7
9
  from nonebot_plugin_uninfo import Session
8
10
 
11
+ from .constant import STOP_COMMAND
12
+
9
13
  if TYPE_CHECKING:
10
14
  from .player_set import PlayerSet
11
15
  from .players import Player
@@ -53,22 +57,37 @@ class InputStore:
53
57
  locks: ClassVar[dict[str, anyio.Lock]] = defaultdict(anyio.Lock)
54
58
  tasks: ClassVar[dict[str, _InputTask]] = {}
55
59
 
60
+ @staticmethod
61
+ def _key(user_id: str, group_id: str | None) -> str:
62
+ return f"{group_id}_{user_id}"
63
+
56
64
  @classmethod
57
65
  async def fetch(cls, user_id: str, group_id: str | None = None) -> UniMessage[Any]:
58
- key = f"{group_id}_{user_id}"
66
+ key = cls._key(user_id, group_id)
59
67
  async with cls.locks[key]:
60
68
  cls.tasks[key] = task = _InputTask()
61
- return await task.wait()
69
+ try:
70
+ return await task.wait()
71
+ finally:
72
+ cls.tasks.pop(key, None)
73
+
74
+ @classmethod
75
+ async def fetch_until_stop(cls, user_id: str, group_id: str | None = None) -> None:
76
+ while True:
77
+ msg = await cls.fetch(user_id, group_id)
78
+ if msg.extract_plain_text().strip() == STOP_COMMAND:
79
+ return
62
80
 
63
81
  @classmethod
64
82
  def put(cls, msg: UniMessage, user_id: str, group_id: str | None = None) -> None:
65
- key = f"{group_id}_{user_id}"
83
+ key = cls._key(user_id, group_id)
66
84
  if task := cls.tasks.pop(key, None):
67
85
  task.set(msg)
68
86
 
69
87
  @classmethod
70
88
  def cleanup(cls, players: list[str], group_id: str) -> None:
71
- for key in (f"{g}_{p}" for p in players for g in (group_id, None)):
89
+ for p, g in itertools.product(players, (group_id, None)):
90
+ key = cls._key(p, g)
72
91
  if key in cls.locks:
73
92
  del cls.locks[key]
74
93
  if key in cls.tasks:
@@ -84,3 +103,48 @@ def cached_player_set() -> type["PlayerSet"]:
84
103
 
85
104
  def as_player_set(*player: "Player") -> "PlayerSet":
86
105
  return cached_player_set()(player)
106
+
107
+
108
+ class ObjectStream(Generic[T]):
109
+ __unset = object()
110
+ _send: anyio.streams.memory.MemoryObjectSendStream[T]
111
+ _recv: anyio.streams.memory.MemoryObjectReceiveStream[T]
112
+ _closed: anyio.Event
113
+
114
+ def __init__(self, max_buffer_size: float = 0) -> None:
115
+ self._send, self._recv = anyio.create_memory_object_stream(max_buffer_size)
116
+ self._closed = anyio.Event()
117
+
118
+ async def send(self, obj: T) -> None:
119
+ await self._send.send(obj)
120
+
121
+ async def recv(self) -> T:
122
+ result = self.__unset
123
+
124
+ async def _recv() -> None:
125
+ nonlocal result
126
+ result = await self._recv.receive()
127
+ tg.cancel_scope.cancel()
128
+
129
+ async def _cancel() -> None:
130
+ await self._closed.wait()
131
+ tg.cancel_scope.cancel()
132
+
133
+ async with anyio.create_task_group() as tg:
134
+ tg.start_soon(_recv)
135
+ tg.start_soon(_cancel)
136
+
137
+ if result is self.__unset:
138
+ raise anyio.EndOfStream
139
+
140
+ return cast(T, result)
141
+
142
+ def close(self) -> None:
143
+ self._closed.set()
144
+
145
+ @property
146
+ def closed(self) -> bool:
147
+ return self._closed.is_set()
148
+
149
+ async def wait_closed(self) -> None:
150
+ await self._closed.wait()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nonebot-plugin-werewolf
3
- Version: 1.1.5
3
+ Version: 1.1.6
4
4
  Summary: 适用于 Nonebot2 的狼人杀插件
5
5
  Author-email: wyf7685 <wyf7685@163.com>
6
6
  License: MIT
@@ -12,6 +12,7 @@ Description-Content-Type: text/markdown
12
12
  License-File: LICENSE
13
13
  Requires-Dist: nonebot2 >=2.3.3
14
14
  Requires-Dist: nonebot-plugin-alconna >=0.52.1
15
+ Requires-Dist: nonebot-plugin-localstore >=0.7.1
15
16
  Requires-Dist: nonebot-plugin-uninfo >=0.4.0
16
17
  Requires-Dist: nonebot-plugin-waiter >=0.7.1
17
18
  Requires-Dist: anyio >=4.6.0
@@ -44,6 +45,7 @@ _✨ 简单的狼人杀插件 ✨_
44
45
  [![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)
45
46
 
46
47
  <!-- https://github.com/lgc2333/nonebot-registry-badge -->
48
+
47
49
  [![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)
48
50
  [![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)
49
51
 
@@ -56,7 +58,7 @@ _✨ 简单的狼人杀插件 ✨_
56
58
  ## 💿 安装
57
59
 
58
60
  > [!note]
59
- >
61
+ >
60
62
  > 请确保 NoneBot2 使用的 Python 解释器版本 >=3.10
61
63
 
62
64
  <details open>
@@ -104,17 +106,16 @@ _✨ 简单的狼人杀插件 ✨_
104
106
 
105
107
  ## ⚙️ 配置
106
108
 
107
- 在 nonebot2 项目的 `.env` 文件中添加如下配置
109
+ 在 nonebot2 项目的 `.env` 文件中添加如下配置:
108
110
 
109
- | 配置项 | 必填 | 默认值 | 说明 |
110
- | :-----------------------------: | :--: | :----: | :-----------------------------------------------------------: |
111
- | `werewolf__enable_poke` | 否 | `True` | 是否使用戳一戳简化操作流程<br/>仅在 `OneBot V11` 适配器下生效 |
112
- | `werewolf__role_preset` | 否 | - | 覆写插件内置的职业预设 |
113
- | `werewolf__werewolf_priority` | 否 | - | 自定义狼人职业优先级 |
114
- | `werewolf__priesthood_proirity` | 否 | - | 自定义神职职业优先级 |
115
- | `werewolf__joker_probability` | 否 | `0.0` | 小丑职业替换平民的概率, 范围`[0,1]` |
111
+ | 配置项 | 必填 | 默认值 | 说明 |
112
+ | :-----------------------: | :---: | :-----: | :------------------------: |
113
+ | `werewolf__enable_poke` | 否 | `True` | 是否使用戳一戳简化操作流程 |
114
+ | `werewolf__enable_button` | 否 | `False` | 是否在交互中添加按钮 |
116
115
 
117
- `werewolf__role_preset`, `werewolf__werewolf_priority`, `werewolf__priesthood_proirity` 的配置格式请参考 [`游戏内容`](#游戏内容) 部分
116
+ `werewolf__enable_poke` 仅在 `OneBot V11` 适配器 / `Satori/chronocat` 下生效
117
+
118
+ `werewolf__enable_button` 仅在 `Telegram` 适配器下通过测试,不保证在其他适配器的可用性。如有疑问欢迎提出。
118
119
 
119
120
  ## 🎉 使用
120
121
 
@@ -133,24 +134,31 @@ _✨ 简单的狼人杀插件 ✨_
133
134
 
134
135
  而对于野生机器人,现有协议端通常不支持或不建议使用临时私聊消息。
135
136
 
136
- 在使用本插件前,应当确保机器人可以正常向玩家发送私聊消息。
137
+ 在使用本插件前,应当确保机器人可以正常向玩家发送私聊消息。~~即保证机器人与玩家为好友关系~~
137
138
 
138
139
  </details>
139
140
 
140
141
  ### 指令表
141
142
 
142
- | 指令 | 权限 | 需要@ | 范围 | 说明 |
143
- | :-----------------: | :--------: | :---: | :--: | :---------------------------------: |
144
- | `werewolf`/`狼人杀` | 群员 | 是 | 群聊 | 发起游戏 (进入准备阶段) |
145
- | `开始游戏` | 游戏发起者 | | 群聊 | _[准备阶段]_ 游戏发起者开始游戏 |
146
- | `结束游戏` | 游戏发起者 | | 群聊 | _[准备阶段]_ 游戏发起者结束游戏 |
147
- | `当前玩家` | 群员 | | 群聊 | _[准备阶段]_ 列出参与游戏的玩家列表 |
148
- | `加入游戏` | 群员 | | 群聊 | _[准备阶段]_ 玩家加入游戏 |
149
- | `退出游戏` | 群员 | | 群聊 | _[准备阶段]_ 玩家退出游戏 |
143
+ | 指令 | 权限 | 需要@ | 范围 | 说明 |
144
+ | :-----------------: | :-----------------: | :---: | :---: | :---------------------------------------: |
145
+ | `werewolf`/`狼人杀` | 群员 | 是 | 群聊 | 发起游戏 (进入准备阶段) |
146
+ | `开始游戏` | 游戏发起者 | | 群聊 | _[准备阶段]_ 游戏发起者开始游戏 |
147
+ | `结束游戏` | 游戏发起者/超级用户 | | 群聊 | _[准备阶段]_ 游戏发起者/超级用户 结束游戏 |
148
+ | `当前玩家` | 群员 | | 群聊 | _[准备阶段]_ 列出参与游戏的玩家列表 |
149
+ | `加入游戏` | 群员 | | 群聊 | _[准备阶段]_ 玩家加入游戏 |
150
+ | `退出游戏` | 群员 | | 群聊 | _[准备阶段]_ 玩家退出游戏 |
151
+ | `中止游戏` | 超级用户 | 是 | 群聊 | _[游戏内]_ 超级用户强制中止游戏 |
152
+ | `狼人杀预设` | 超级用户 | 否 | 任意 | _[游戏外]_ 超级用户编辑游戏预设 |
153
+
154
+ - 发起游戏时添加 `restart`/`重开`, 可加载上一次游戏的玩家列表, 快速发起游戏。例: `werewolf restart`/`狼人杀 重开`
150
155
 
151
- _其他交互参考游戏内提示_
156
+ - `狼人杀预设` 命令用法可通过 `狼人杀预设 --help` 获取,或参考 [游戏内容](#游戏内容) 部分的介绍
157
+
158
+ - 对于 `OneBot V11` 适配器和 `Satori` 适配器的 `chronocat`, 启用配置项 `werewolf__enable_poke` 后, 可以使用戳一戳代替 _准备阶段_ 的 `加入游戏` 操作 和 游戏内的 `stop` 命令
159
+
160
+ - _其他交互参考游戏内提示_
152
161
 
153
- 对于 `OneBot V11` 适配器和 `Satori` 适配器的 `chronocat`, 启用配置项 `werewolf__enable_poke` 后, 可以使用戳一戳代替 _准备阶段_ 的 `加入游戏` 操作 和 游戏内的 `stop` 命令
154
162
 
155
163
  ### 游戏内容
156
164
 
@@ -164,31 +172,22 @@ _其他交互参考游戏内提示_
164
172
 
165
173
  | 总人数 | 狼人 | 神职 | 平民 |
166
174
  | ------ | ---- | ---- | ---- |
167
- | 6     | 1  | 2   | 3   |
168
- | 7     | 2  | 2   | 3   |
169
- | 8     | 2  | 3   | 3   |
170
- | 9     | 2  | 4   | 3   |
171
- | 10   | 3  | 4   | 3   |
172
- | 11   | 3  | 5   | 3   |
173
- | 12   | 4  | 5   | 3   |
175
+ | 6 | 1 | 2 | 3 |
176
+ | 7 | 2 | 2 | 3 |
177
+ | 8 | 2 | 3 | 3 |
178
+ | 9 | 2 | 4 | 3 |
179
+ | 10 | 3 | 4 | 3 |
180
+ | 11 | 3 | 5 | 3 |
181
+ | 12 | 4 | 5 | 3 |
174
182
 
175
- 职业预设可以通过配置项 `werewolf__role_preset` 修改
183
+ 职业预设可以通过命令 `狼人杀预设 职业 ...` 修改
176
184
 
177
185
  <details>
178
186
  <summary>示例</summary>
179
187
 
180
- 配置项 `werewolf__role_preset`
188
+ - 命令: `狼人杀预设 职业 6 1 3 2`
181
189
 
182
- ```env
183
- werewolf__role_preset='
184
- [
185
- [6, 1, 3, 2],
186
- [7, 2, 3, 2]
187
- ]
188
- '
189
- ```
190
-
191
- 上述配置中,`[6, 1, 3, 2]` 表示当总人数为 6 时,狼人、神职、平民的数量分别为 1、3、2
190
+ - 上述命令指定当总人数为 6 时,狼人、神职、平民的数量分别为 1、3、2
192
191
 
193
192
  </details>
194
193
  <br/>
@@ -198,49 +197,43 @@ werewolf__role_preset='
198
197
  - `狼人`: `狼人`, `狼人`, `狼王`, `狼人`
199
198
  - `神职`: `女巫`, `预言家`, `猎人`, `守卫`, `白痴`
200
199
 
201
- 职业分配优先级可以通过配置项 `werewolf__werewolf_priority` `werewolf__priesthood_proirity` 修改
200
+ 职业分配优先级可以通过命令 `狼人杀预设 狼人/神职` 修改
202
201
 
203
202
  <details>
204
203
  <summary>示例</summary>
205
204
 
206
- #### 配置项 `werewolf__werewolf_priority`
205
+ #### 命令 `狼人杀预设 狼人`
207
206
 
208
- ```env
209
- werewolf__werewolf_priority=[1, 2, 1, 1]
210
- ```
207
+ - 命令: `狼人杀预设 狼人 狼 狼王 狼 狼`
211
208
 
212
- 上述配置中,`[1, 2, 1, 1]` 表示狼人的职业优先级为 `狼人`, `狼王`, `狼人`, `狼人`
209
+ - 上述命令指定狼人的职业优先级为 `狼人`, `狼王`, `狼人`, `狼人`
213
210
 
214
- #### 配置项 `werewolf__priesthood_proirity`
211
+ #### 命令 `狼人杀预设 神职`
215
212
 
216
- ```env
217
- werewolf__priesthood_proirity=[11, 12, 13, 14, 15]
218
- ```
213
+ - 命令: `狼人杀预设 神职 预言家 女巫 猎人 守卫 白痴`
219
214
 
220
- 上述配置中,`[11, 12, 13, 14, 15]` 表示神职的职业优先级为 `预言家`, `女巫`, `猎人`, `守卫`, `白痴`
215
+ - 上述命令指定狼人的职业优先级为 `预言家`, `女巫`, `猎人`, `守卫`, `白痴`
221
216
 
222
- #### 职业与数字的对应关系
217
+ > [!note]
218
+ >
219
+ > 以上两条命令均支持交互式输入
220
+ >
221
+ > 例:向机器人发送命令 `狼人杀预设 狼人`,在接下来的一条消息中发送 `狼人 狼王 狼人 狼人`
222
+ >
223
+ > 其效果等同于以上描述中的单条命令 `狼人杀预设 狼人 狼人 狼王 狼人 狼人`
223
224
 
224
- 上述配置示例中有大量~~意义不明的~~数字, 其对应的是 [`这里`](./nonebot_plugin_werewolf/constant.py) 的枚举类 `Role` 的值
225
+ </details>
226
+ <br/>
225
227
 
226
- 以下列出目前的枚举值供参考
228
+ 对于 `小丑` 职业,当预设中的平民数量大于或等于 2 时,将有 *一定概率* 将其中一个平民替换为小丑。
227
229
 
228
- | 职业 | 枚举值 |
229
- | -------- | ------ |
230
- | `狼人` | `1` |
231
- | `狼王` | `2` |
232
- | `预言家` | `11` |
233
- | `女巫` | `12` |
234
- | `猎人` | `13` |
235
- | `守卫` | `14` |
236
- | `白痴` | `15` |
237
- | `平民` | `0` |
230
+ 小丑属于第三方阵营,胜利条件为在投票阶段被票出,在预言家查验及游戏进程判断时视作平民。
238
231
 
239
- </details>
232
+ 小丑生成概率可以通过命令 `狼人杀预设 小丑 <概率>` 设置,默认值为 0 (不生成小丑)。
240
233
 
241
234
  ### 已知问题
242
235
 
243
- - 截止 chronocat v0.2.19, 调用 [`guild.member.get`](https://github.com/chrononeko/chronocat/blob/8558ad9ff4319395d86abbfda22136939bf66780/packages/engine-chronocat-api/src/api/guild/member/get.ts) / [`user.get`](https://github.com/chrononeko/chronocat/blob/8558ad9ff4319395d86abbfda22136939bf66780/packages/engine-chronocat-api/src/api/user/get.ts) 均无法获取用户名,这将导致在交互过程中的玩家名显示为用户ID
236
+ - 截止 chronocat v0.2.19, 调用 [`guild.member.get`](https://github.com/chrononeko/chronocat/blob/8558ad9ff4319395d86abbfda22136939bf66780/packages/engine-chronocat-api/src/api/guild/member/get.ts) / [`user.get`](https://github.com/chrononeko/chronocat/blob/8558ad9ff4319395d86abbfda22136939bf66780/packages/engine-chronocat-api/src/api/user/get.ts) 均无法获取用户名,这将导致在交互过程中的玩家名显示为用户 ID
244
237
 
245
238
  ## 📝 更新日志
246
239
 
@@ -249,6 +242,13 @@ werewolf__priesthood_proirity=[11, 12, 13, 14, 15]
249
242
 
250
243
  <!-- CHANGELOG -->
251
244
 
245
+ - 2024.10.31 v1.1.6
246
+
247
+ - 新增超级用户中止游戏 (#7)
248
+ - 新增快速发起上次游戏 (#8)
249
+ - 准备阶段添加可选的交互按钮
250
+ - 新增超级用户修改游戏预设 (#9)
251
+
252
252
  - 2024.10.23 v1.1.5
253
253
 
254
254
  - 添加对 chronocat:poke 的支持
@@ -0,0 +1,34 @@
1
+ nonebot_plugin_werewolf/__init__.py,sha256=ZomfjWnGRnFuh3I__spYbLRE1Z3wTUthwXzc5x8YPJw,931
2
+ nonebot_plugin_werewolf/config.py,sha256=bMRRqNVWNsFDKyijxZZYPN7DCAf9bYf2MdfbPXE239w,1287
3
+ nonebot_plugin_werewolf/constant.py,sha256=_ngw2xtXVZMTQXQ0gv4l9h4Lo1jq5v28fKLOGJ7G57o,1796
4
+ nonebot_plugin_werewolf/exception.py,sha256=2F2kZsMaRIa7jOiIQJiM10K7Z59ouCpaZENcnEcEEXQ,398
5
+ nonebot_plugin_werewolf/game.py,sha256=xU8IcrWQNRUjzfPIAbm1XCeshwfy8SWgxapUP1_NGOw,18925
6
+ nonebot_plugin_werewolf/models.py,sha256=ljWy6BaTVyIK4Ng-wUVRteBLZe2pQLf8l3yWFZzWXHQ,1505
7
+ nonebot_plugin_werewolf/player_set.py,sha256=84hZOOAaPjTyZZDje6mNy7zm4G5UH2nGDq1pvDNFT9w,2538
8
+ nonebot_plugin_werewolf/utils.py,sha256=PBgadYdF3IQbaVPjcmstmsOGddfPvj8rcbiYQllHFYY,4096
9
+ nonebot_plugin_werewolf/matchers/__init__.py,sha256=BnzEDmW8n1wkyI-un0DYXt2k_6CFv6NE8itZ2daw4zQ,174
10
+ nonebot_plugin_werewolf/matchers/depends.py,sha256=poMQJ7Mzd3IZFvh2MvnfYdnnb2c-r6XFK_IOr50PL3o,1321
11
+ nonebot_plugin_werewolf/matchers/edit_preset.py,sha256=gKd1csoR9u4VoeulTtxUUgxsOdgB0SDC1FMxMRdgw5I,8087
12
+ nonebot_plugin_werewolf/matchers/message_in_game.py,sha256=lW0TMC85B-g9wZQcL7Q-LQSYyvO4-uem3Ye1szX2hU8,837
13
+ nonebot_plugin_werewolf/matchers/start_game.py,sha256=YM4VGKpV8GQ29SgPh0jvWUHnx8onAvx_m4RWyeuRxmc,10187
14
+ nonebot_plugin_werewolf/matchers/superuser_ops.py,sha256=Xjsmwzkt6Y-M_QlQwUL0EXN_fDqEqKSM4fcLk9fTvDY,685
15
+ nonebot_plugin_werewolf/matchers/poke/__init__.py,sha256=gYysvGjztN3iDQpX6v5nkPT195FXnk7fqP9kzByTES0,220
16
+ nonebot_plugin_werewolf/matchers/poke/chronocat_poke.py,sha256=SrFN8dQELFVDR5US9sOcE2gaOnH7AFiRVVK2RNa-Y5o,4023
17
+ nonebot_plugin_werewolf/matchers/poke/ob11_poke.py,sha256=LROxtbauw4xbB8UKglsx6b6hkKWs_17HmgK4NTXW1HY,2640
18
+ nonebot_plugin_werewolf/players/__init__.py,sha256=djAI5XxR2I-XvnH-lVqX_YCHB_AiT-6jdmwFE1ffN_0,379
19
+ nonebot_plugin_werewolf/players/can_shoot.py,sha256=Z0AR_jCfcU1ps5629sWB_mH8O_iVLy8-DyePgBfznNA,1814
20
+ nonebot_plugin_werewolf/players/civilian.py,sha256=9m-bgzqHji-9Aejl2TnchSIRw_SZpKLqCGdaEWXhopc,155
21
+ nonebot_plugin_werewolf/players/guard.py,sha256=gHPlIfBXtQ8MSoWvucIIq0XrJQx9KIIKIVl-jwAHhgc,1132
22
+ nonebot_plugin_werewolf/players/hunter.py,sha256=q17IjSXt5knDAc49NbV0NCwNW7qASqODUNgC0L1zqeI,193
23
+ nonebot_plugin_werewolf/players/idiot.py,sha256=jz13BN4BsYcmiZdTwSlXUm4yc0KR8GUmQmq2GI7_Kzo,1431
24
+ nonebot_plugin_werewolf/players/joker.py,sha256=n_jlddGMpFJ0O0sc_1J6IybfPsPJSWeS4GC0S6IZoTk,826
25
+ nonebot_plugin_werewolf/players/player.py,sha256=hEbGRF5vrgKYKKEC5DB3JUIG6SEyvoHTYGZUqHPjf8Q,6953
26
+ nonebot_plugin_werewolf/players/prophet.py,sha256=wrTR8BnktiZ8aEI4VzYa5kt4GXyFnyi5wZmCO933-rE,917
27
+ nonebot_plugin_werewolf/players/werewolf.py,sha256=jZMzYE0LiwsxO8XHMlWRk25XO5gGe24t87NqzLc3I4A,3287
28
+ nonebot_plugin_werewolf/players/witch.py,sha256=FOM_MMKUjO6QpOQK9JvHq9zwa4GTVCVnW7UtPgT_BTw,2360
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.2.0)
2
+ Generator: setuptools (75.3.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5