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.
- nonebot_plugin_werewolf/__init__.py +9 -4
- nonebot_plugin_werewolf/config.py +11 -15
- nonebot_plugin_werewolf/constant.py +40 -3
- nonebot_plugin_werewolf/game.py +219 -181
- nonebot_plugin_werewolf/matchers/__init__.py +2 -0
- nonebot_plugin_werewolf/matchers/depends.py +38 -0
- nonebot_plugin_werewolf/matchers/message_in_game.py +25 -0
- nonebot_plugin_werewolf/matchers/poke/__init__.py +8 -0
- nonebot_plugin_werewolf/matchers/poke/chronocat_poke.py +117 -0
- nonebot_plugin_werewolf/matchers/poke/ob11_poke.py +80 -0
- nonebot_plugin_werewolf/matchers/start_game.py +202 -0
- nonebot_plugin_werewolf/player_set.py +37 -36
- nonebot_plugin_werewolf/players/__init__.py +10 -0
- nonebot_plugin_werewolf/players/can_shoot.py +53 -0
- nonebot_plugin_werewolf/players/civilian.py +7 -0
- nonebot_plugin_werewolf/players/guard.py +30 -0
- nonebot_plugin_werewolf/players/hunter.py +8 -0
- nonebot_plugin_werewolf/players/idiot.py +44 -0
- nonebot_plugin_werewolf/players/joker.py +21 -0
- nonebot_plugin_werewolf/players/player.py +233 -0
- nonebot_plugin_werewolf/players/prophet.py +22 -0
- nonebot_plugin_werewolf/players/werewolf.py +89 -0
- nonebot_plugin_werewolf/players/witch.py +66 -0
- nonebot_plugin_werewolf/players/wolfking.py +14 -0
- nonebot_plugin_werewolf/utils.py +58 -173
- {nonebot_plugin_werewolf-1.1.2.dist-info → nonebot_plugin_werewolf-1.1.5.dist-info}/METADATA +24 -4
- nonebot_plugin_werewolf-1.1.5.dist-info/RECORD +31 -0
- {nonebot_plugin_werewolf-1.1.2.dist-info → nonebot_plugin_werewolf-1.1.5.dist-info}/WHEEL +1 -1
- nonebot_plugin_werewolf/_timeout.py +0 -110
- nonebot_plugin_werewolf/matchers.py +0 -62
- nonebot_plugin_werewolf/ob11_ext.py +0 -72
- nonebot_plugin_werewolf/player.py +0 -462
- nonebot_plugin_werewolf-1.1.2.dist-info/RECORD +0 -16
- {nonebot_plugin_werewolf-1.1.2.dist-info → nonebot_plugin_werewolf-1.1.5.dist-info}/LICENSE +0 -0
- {nonebot_plugin_werewolf-1.1.2.dist-info → nonebot_plugin_werewolf-1.1.5.dist-info}/top_level.txt +0 -0
nonebot_plugin_werewolf/utils.py
CHANGED
@@ -1,16 +1,16 @@
|
|
1
|
-
import
|
2
|
-
import re
|
1
|
+
import functools
|
3
2
|
from collections import defaultdict
|
4
|
-
from typing import
|
3
|
+
from typing import TYPE_CHECKING, Any, ClassVar, TypeVar
|
5
4
|
|
6
|
-
import
|
7
|
-
from
|
8
|
-
from
|
9
|
-
from nonebot.rule import to_me
|
10
|
-
from nonebot_plugin_alconna import MsgTarget, UniMessage, UniMsg
|
11
|
-
from nonebot_plugin_userinfo import EventUserInfo, UserInfo
|
5
|
+
import anyio
|
6
|
+
from nonebot_plugin_alconna import UniMessage
|
7
|
+
from nonebot_plugin_uninfo import Session
|
12
8
|
|
13
|
-
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from .player_set import PlayerSet
|
11
|
+
from .players import Player
|
12
|
+
|
13
|
+
T = TypeVar("T")
|
14
14
|
|
15
15
|
|
16
16
|
def check_index(text: str, arrlen: int) -> int | None:
|
@@ -21,181 +21,66 @@ def check_index(text: str, arrlen: int) -> int | None:
|
|
21
21
|
return None
|
22
22
|
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
futures: ClassVar[dict[str, asyncio.Future[UniMessage]]] = {}
|
24
|
+
def link(text: str, url: str | None) -> str:
|
25
|
+
return text if url is None else f"\u001b]8;;{url}\u0007{text}\u001b]8;;\u0007"
|
27
26
|
|
28
|
-
@classmethod
|
29
|
-
async def fetch(cls, user_id: str, group_id: str | None = None) -> UniMessage[Any]:
|
30
|
-
key = f"{group_id}_{user_id}"
|
31
|
-
async with cls.locks[key]:
|
32
|
-
cls.futures[key] = asyncio.get_event_loop().create_future()
|
33
|
-
try:
|
34
|
-
return await cls.futures[key]
|
35
|
-
finally:
|
36
|
-
del cls.futures[key]
|
37
27
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
28
|
+
def extract_session_member_nick(session: Session) -> str | None:
|
29
|
+
return (
|
30
|
+
(session.member and session.member.nick)
|
31
|
+
or session.user.nick
|
32
|
+
or session.user.name
|
33
|
+
)
|
34
|
+
|
43
35
|
|
36
|
+
class _InputTask:
|
37
|
+
_event: anyio.Event
|
38
|
+
_msg: UniMessage
|
44
39
|
|
45
|
-
def
|
46
|
-
|
40
|
+
def __init__(self) -> None:
|
41
|
+
self._event = anyio.Event()
|
47
42
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
for game in games:
|
52
|
-
return any(user_id == player.user_id for player in game.players)
|
53
|
-
return False
|
43
|
+
def set(self, msg: UniMessage) -> None:
|
44
|
+
self._msg = msg
|
45
|
+
self._event.set()
|
54
46
|
|
47
|
+
async def wait(self) -> UniMessage:
|
48
|
+
await self._event.wait()
|
49
|
+
return self._msg
|
55
50
|
|
56
|
-
async def rule_in_game(event: Event, target: MsgTarget) -> bool:
|
57
|
-
from .game import running_games
|
58
51
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
52
|
+
class InputStore:
|
53
|
+
locks: ClassVar[dict[str, anyio.Lock]] = defaultdict(anyio.Lock)
|
54
|
+
tasks: ClassVar[dict[str, _InputTask]] = {}
|
55
|
+
|
56
|
+
@classmethod
|
57
|
+
async def fetch(cls, user_id: str, group_id: str | None = None) -> UniMessage[Any]:
|
58
|
+
key = f"{group_id}_{user_id}"
|
59
|
+
async with cls.locks[key]:
|
60
|
+
cls.tasks[key] = task = _InputTask()
|
61
|
+
return await task.wait()
|
66
62
|
|
63
|
+
@classmethod
|
64
|
+
def put(cls, msg: UniMessage, user_id: str, group_id: str | None = None) -> None:
|
65
|
+
key = f"{group_id}_{user_id}"
|
66
|
+
if task := cls.tasks.pop(key, None):
|
67
|
+
task.set(msg)
|
67
68
|
|
68
|
-
|
69
|
-
|
69
|
+
@classmethod
|
70
|
+
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)):
|
72
|
+
if key in cls.locks:
|
73
|
+
del cls.locks[key]
|
74
|
+
if key in cls.tasks:
|
75
|
+
del cls.tasks[key]
|
70
76
|
|
71
77
|
|
72
|
-
|
73
|
-
|
78
|
+
@functools.cache
|
79
|
+
def cached_player_set() -> type["PlayerSet"]:
|
80
|
+
from .player_set import PlayerSet
|
74
81
|
|
82
|
+
return PlayerSet
|
75
83
|
|
76
|
-
async def _prepare_game_receive(
|
77
|
-
queue: asyncio.Queue[tuple[str, str, str]],
|
78
|
-
event: Event,
|
79
|
-
group_id: str,
|
80
|
-
) -> None:
|
81
|
-
async def rule(target_: MsgTarget) -> bool:
|
82
|
-
return not target_.private and target_.id == group_id
|
83
84
|
|
84
|
-
|
85
|
-
|
86
|
-
keep_session=False,
|
87
|
-
rule=to_me() & rule & rule_not_in_game,
|
88
|
-
)
|
89
|
-
def wait(
|
90
|
-
event: Event,
|
91
|
-
info: Annotated[UserInfo | None, EventUserInfo()],
|
92
|
-
msg: UniMsg,
|
93
|
-
) -> tuple[str, str, str]:
|
94
|
-
return (
|
95
|
-
event.get_user_id(),
|
96
|
-
(
|
97
|
-
(info.user_displayname or info.user_name)
|
98
|
-
if info is not None
|
99
|
-
else event.get_user_id()
|
100
|
-
),
|
101
|
-
msg.extract_plain_text().strip(),
|
102
|
-
)
|
103
|
-
|
104
|
-
async for user, name, text in wait(default=(None, "", "")):
|
105
|
-
if user is None:
|
106
|
-
continue
|
107
|
-
await queue.put((user, re.sub(r"[\u2066-\u2069]", "", name), text))
|
108
|
-
|
109
|
-
|
110
|
-
async def _prepare_game_handle(
|
111
|
-
queue: asyncio.Queue[tuple[str, str, str]],
|
112
|
-
players: dict[str, str],
|
113
|
-
admin_id: str,
|
114
|
-
) -> None:
|
115
|
-
log = logger.opt(colors=True)
|
116
|
-
|
117
|
-
while True:
|
118
|
-
user, name, text = await queue.get()
|
119
|
-
msg = UniMessage.at(user)
|
120
|
-
colored = f"<y>{name}</y>(<c>{user}</c>)"
|
121
|
-
|
122
|
-
match (text, user == admin_id):
|
123
|
-
case ("开始游戏", True):
|
124
|
-
player_num = len(players)
|
125
|
-
role_preset = config.get_role_preset()
|
126
|
-
if player_num < min(role_preset):
|
127
|
-
await (
|
128
|
-
msg.text(f"游戏至少需要 {min(role_preset)} 人, ")
|
129
|
-
.text(f"当前已有 {player_num} 人")
|
130
|
-
.send()
|
131
|
-
)
|
132
|
-
elif player_num > max(role_preset):
|
133
|
-
await (
|
134
|
-
msg.text(f"游戏最多需要 {max(role_preset)} 人, ")
|
135
|
-
.text(f"当前已有 {player_num} 人")
|
136
|
-
.send()
|
137
|
-
)
|
138
|
-
elif player_num not in role_preset:
|
139
|
-
await (
|
140
|
-
msg.text(f"不存在总人数为 {player_num} 的预设, ")
|
141
|
-
.text("无法开始游戏")
|
142
|
-
.send()
|
143
|
-
)
|
144
|
-
else:
|
145
|
-
await msg.text("游戏即将开始...").send()
|
146
|
-
log.info(f"游戏发起者 {colored} 开始游戏")
|
147
|
-
return
|
148
|
-
|
149
|
-
case ("开始游戏", False):
|
150
|
-
await msg.text("只有游戏发起者可以开始游戏").send()
|
151
|
-
|
152
|
-
case ("结束游戏", True):
|
153
|
-
log.info(f"游戏发起者 {colored} 结束游戏")
|
154
|
-
await msg.text("已结束当前游戏").finish()
|
155
|
-
|
156
|
-
case ("结束游戏", False):
|
157
|
-
await msg.text("只有游戏发起者可以结束游戏").send()
|
158
|
-
|
159
|
-
case ("加入游戏", True):
|
160
|
-
await msg.text("游戏发起者已经加入游戏了").send()
|
161
|
-
|
162
|
-
case ("加入游戏", False):
|
163
|
-
if user not in players:
|
164
|
-
players[user] = name
|
165
|
-
log.info(f"玩家 {colored} 加入游戏")
|
166
|
-
await msg.text("成功加入游戏").send()
|
167
|
-
else:
|
168
|
-
await msg.text("你已经加入游戏了").send()
|
169
|
-
|
170
|
-
case ("退出游戏", True):
|
171
|
-
await msg.text("游戏发起者无法退出游戏").send()
|
172
|
-
|
173
|
-
case ("退出游戏", False):
|
174
|
-
if user in players:
|
175
|
-
del players[user]
|
176
|
-
log.info(f"玩家 {colored} 退出游戏")
|
177
|
-
await msg.text("成功退出游戏").send()
|
178
|
-
else:
|
179
|
-
await msg.text("你还没有加入游戏").send()
|
180
|
-
|
181
|
-
case ("当前玩家", _):
|
182
|
-
msg.text("\n当前玩家:\n")
|
183
|
-
for idx, name in enumerate(players.values(), 1):
|
184
|
-
msg.text(f"\n{idx}. {name}")
|
185
|
-
await msg.send()
|
186
|
-
|
187
|
-
|
188
|
-
async def prepare_game(event: Event, players: dict[str, str]) -> None:
|
189
|
-
from .game import starting_games
|
190
|
-
|
191
|
-
group_id = UniMessage.get_target(event).id
|
192
|
-
starting_games[group_id] = players
|
193
|
-
|
194
|
-
queue: asyncio.Queue[tuple[str, str, str]] = asyncio.Queue()
|
195
|
-
task_receive = asyncio.create_task(_prepare_game_receive(queue, event, group_id))
|
196
|
-
|
197
|
-
try:
|
198
|
-
await _prepare_game_handle(queue, players, event.get_user_id())
|
199
|
-
finally:
|
200
|
-
task_receive.cancel()
|
201
|
-
del starting_games[group_id]
|
85
|
+
def as_player_set(*player: "Player") -> "PlayerSet":
|
86
|
+
return cached_player_set()(player)
|
{nonebot_plugin_werewolf-1.1.2.dist-info → nonebot_plugin_werewolf-1.1.5.dist-info}/METADATA
RENAMED
@@ -1,16 +1,20 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: nonebot-plugin-werewolf
|
3
|
-
Version: 1.1.
|
3
|
+
Version: 1.1.5
|
4
4
|
Summary: 适用于 Nonebot2 的狼人杀插件
|
5
5
|
Author-email: wyf7685 <wyf7685@163.com>
|
6
6
|
License: MIT
|
7
|
+
Project-URL: homepage, https://github.com/wyf7685/nonebot-plugin-werewolf
|
8
|
+
Project-URL: repository, https://github.com/wyf7685/nonebot-plugin-werewolf
|
9
|
+
Project-URL: bug-tracker, https://github.com/wyf7685/nonebot-plugin-werewolf/issues
|
7
10
|
Requires-Python: >=3.10
|
8
11
|
Description-Content-Type: text/markdown
|
9
12
|
License-File: LICENSE
|
10
13
|
Requires-Dist: nonebot2 >=2.3.3
|
11
14
|
Requires-Dist: nonebot-plugin-alconna >=0.52.1
|
12
|
-
Requires-Dist: nonebot-plugin-
|
15
|
+
Requires-Dist: nonebot-plugin-uninfo >=0.4.0
|
13
16
|
Requires-Dist: nonebot-plugin-waiter >=0.7.1
|
17
|
+
Requires-Dist: anyio >=4.6.0
|
14
18
|
|
15
19
|
<div align="center">
|
16
20
|
<a href="https://v2.nonebot.dev/store">
|
@@ -39,6 +43,7 @@ _✨ 简单的狼人杀插件 ✨_
|
|
39
43
|
[](https://github.com/wyf7685/nonebot-plugin-werewolf/actions/workflows/pyright.yml)
|
40
44
|
[](https://github.com/wyf7685/nonebot-plugin-werewolf/actions/workflows/pypi-publish.yml)
|
41
45
|
|
46
|
+
<!-- https://github.com/lgc2333/nonebot-registry-badge -->
|
42
47
|
[](https://registry.nonebot.dev/plugin/nonebot-plugin-werewolf:nonebot_plugin_werewolf)
|
43
48
|
[](https://registry.nonebot.dev/plugin/nonebot-plugin-werewolf:nonebot_plugin_werewolf)
|
44
49
|
|
@@ -145,7 +150,7 @@ _✨ 简单的狼人杀插件 ✨_
|
|
145
150
|
|
146
151
|
_其他交互参考游戏内提示_
|
147
152
|
|
148
|
-
对于 `OneBot V11`
|
153
|
+
对于 `OneBot V11` 适配器和 `Satori` 适配器的 `chronocat`, 启用配置项 `werewolf__enable_poke` 后, 可以使用戳一戳代替 _准备阶段_ 的 `加入游戏` 操作 和 游戏内的 `stop` 命令
|
149
154
|
|
150
155
|
### 游戏内容
|
151
156
|
|
@@ -233,6 +238,10 @@ werewolf__priesthood_proirity=[11, 12, 13, 14, 15]
|
|
233
238
|
|
234
239
|
</details>
|
235
240
|
|
241
|
+
### 已知问题
|
242
|
+
|
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
|
244
|
+
|
236
245
|
## 📝 更新日志
|
237
246
|
|
238
247
|
<details>
|
@@ -240,6 +249,17 @@ werewolf__priesthood_proirity=[11, 12, 13, 14, 15]
|
|
240
249
|
|
241
250
|
<!-- CHANGELOG -->
|
242
251
|
|
252
|
+
- 2024.10.23 v1.1.5
|
253
|
+
|
254
|
+
- 添加对 chronocat:poke 的支持
|
255
|
+
- 游戏内 stop 命令使用 COMMAND_START
|
256
|
+
- 使用 `anyio` 重写并发逻辑
|
257
|
+
|
258
|
+
- 2024.10.06 v1.1.3
|
259
|
+
|
260
|
+
- 使用 `RF-Tar-Railt/nonebot-plugin-uninfo` 获取用户数据
|
261
|
+
- 优化交互文本
|
262
|
+
|
243
263
|
- 2024.09.18 v1.1.2
|
244
264
|
|
245
265
|
- 修改 Python 需求为 `>=3.10`
|
@@ -284,6 +304,6 @@ werewolf__priesthood_proirity=[11, 12, 13, 14, 15]
|
|
284
304
|
|
285
305
|
- [`nonebot/nonebot2`](https://github.com/nonebot/nonebot2): 跨平台 Python 异步机器人框架
|
286
306
|
- [`nonebot/plugin-alconna`](https://github.com/nonebot/plugin-alconna): 跨平台的消息处理接口
|
287
|
-
- [`
|
307
|
+
- [`RF-Tar-Railt/nonebot-plugin-uninfo`](https://github.com/RF-Tar-Railt/nonebot-plugin-uninfo): 用户信息获取
|
288
308
|
- [`RF-Tar-Railt/nonebot-plugin-waiter`](https://github.com/RF-Tar-Railt/nonebot-plugin-waiter): 灵活获取用户输入
|
289
309
|
- `热心群友`: 协助测试插件
|
@@ -0,0 +1,31 @@
|
|
1
|
+
nonebot_plugin_werewolf/__init__.py,sha256=v65nKZbsbmaJPR0o2ZrXuumUvCuEOvDVtVmw65CyVn8,894
|
2
|
+
nonebot_plugin_werewolf/config.py,sha256=FKQDkb57ujcBYJZX-sLgxWmTVqSeR7T1ywphp7GOCcE,2803
|
3
|
+
nonebot_plugin_werewolf/constant.py,sha256=d4Awy96YBe_AJoAZyd_6Wojo0qi2BTVM_LW-nDbsxQE,2902
|
4
|
+
nonebot_plugin_werewolf/exception.py,sha256=YSwxeogIB0YJqH9MP1bgxojiu-I_xQE44XnSk5bC1AQ,400
|
5
|
+
nonebot_plugin_werewolf/game.py,sha256=yUfJtLWH7--TRPnofAGfrUkUvd_3VW1J9j8zhTrN9hc,19106
|
6
|
+
nonebot_plugin_werewolf/player_set.py,sha256=J5KFWSugpu1Zd_gToqa-Gweoikj8kppLpckVjl-4XH0,2540
|
7
|
+
nonebot_plugin_werewolf/utils.py,sha256=6uFwgf4LAI1SZ_qH6I2ilHt2vbs6NfTj5YhMvC4GacE,2279
|
8
|
+
nonebot_plugin_werewolf/matchers/__init__.py,sha256=_MwAZsXlpBLXyzHWqNLTQdMWw9z_O01L5Yo02dzGC9I,88
|
9
|
+
nonebot_plugin_werewolf/matchers/depends.py,sha256=6JFLxQDgRjStbMkpoWe1tYZHs0wVfsyn4KikKNpgx0Q,1160
|
10
|
+
nonebot_plugin_werewolf/matchers/message_in_game.py,sha256=YkphERN2efaB0VdU3oTCvvYptrqDaKFKUb9moUyqIpY,780
|
11
|
+
nonebot_plugin_werewolf/matchers/start_game.py,sha256=ZNO2ypV8kMlImR0fZVhg0ZI8mrOsW3I-eioT6Lhf2Cw,7427
|
12
|
+
nonebot_plugin_werewolf/matchers/poke/__init__.py,sha256=gYysvGjztN3iDQpX6v5nkPT195FXnk7fqP9kzByTES0,220
|
13
|
+
nonebot_plugin_werewolf/matchers/poke/chronocat_poke.py,sha256=SrFN8dQELFVDR5US9sOcE2gaOnH7AFiRVVK2RNa-Y5o,4023
|
14
|
+
nonebot_plugin_werewolf/matchers/poke/ob11_poke.py,sha256=LROxtbauw4xbB8UKglsx6b6hkKWs_17HmgK4NTXW1HY,2640
|
15
|
+
nonebot_plugin_werewolf/players/__init__.py,sha256=djAI5XxR2I-XvnH-lVqX_YCHB_AiT-6jdmwFE1ffN_0,379
|
16
|
+
nonebot_plugin_werewolf/players/can_shoot.py,sha256=e9QtRUNxNZ9n3MxX7o0bUmQLLPDazTOZky3F_2m6UhM,1813
|
17
|
+
nonebot_plugin_werewolf/players/civilian.py,sha256=TbrIxjG4g74C9Cd_F3OskuM-ksKUkOLiE6OgPqmo0pA,157
|
18
|
+
nonebot_plugin_werewolf/players/guard.py,sha256=T2L7f0Dcx6jjlOrGcHUQxy9RawszvP6AKaI8Lz9nItI,1112
|
19
|
+
nonebot_plugin_werewolf/players/hunter.py,sha256=NaJNjngKCHC8RI1YsYI5XtVyFq9JP5U1ywOTbjjUHq4,195
|
20
|
+
nonebot_plugin_werewolf/players/idiot.py,sha256=Tpjf7RPW1dJ3J4ajr5HWcIustHO-h6PNicum4SiofDE,1433
|
21
|
+
nonebot_plugin_werewolf/players/joker.py,sha256=J_t-IPKjyHWq8azxFdN_cmJhbgoDcokiMLf-pMXQInE,691
|
22
|
+
nonebot_plugin_werewolf/players/player.py,sha256=JUCev-3jnAqApNR-cFaHRHkQVIgBfn5HhHhLq8OGWgw,6954
|
23
|
+
nonebot_plugin_werewolf/players/prophet.py,sha256=VQa4RJUbLpG9ntB055ORl09oEAmZ1uOUi4P4q_P_9TY,897
|
24
|
+
nonebot_plugin_werewolf/players/werewolf.py,sha256=MZOsHWNnKvh_fxKMEIWqa_yJIYHVjA1fM5W3jkg_CtU,3433
|
25
|
+
nonebot_plugin_werewolf/players/witch.py,sha256=XDCVSXhUA9afh0iCdxV8Shfc1YGDkXYhu1h0v1xpbmg,2340
|
26
|
+
nonebot_plugin_werewolf/players/wolfking.py,sha256=oBC5JW0UULDkthurDBvHRNo3rqArm-zQ_1jSuyeHpQU,440
|
27
|
+
nonebot_plugin_werewolf-1.1.5.dist-info/LICENSE,sha256=B_WbEqjGr6GYVNfEJPY31T1Opik7OtgOkhRs4Ig3e2M,1064
|
28
|
+
nonebot_plugin_werewolf-1.1.5.dist-info/METADATA,sha256=j-vwRZiK1lobgZ10-X30Upf6Vuo1ZYMraWgQvStKjvI,11435
|
29
|
+
nonebot_plugin_werewolf-1.1.5.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
|
30
|
+
nonebot_plugin_werewolf-1.1.5.dist-info/top_level.txt,sha256=wLTfg8sTKbH9lLT9LtU118C9cTspEBJareLsrYM52YA,24
|
31
|
+
nonebot_plugin_werewolf-1.1.5.dist-info/RECORD,,
|
@@ -1,110 +0,0 @@
|
|
1
|
-
import asyncio
|
2
|
-
import enum
|
3
|
-
import sys
|
4
|
-
from types import TracebackType
|
5
|
-
from typing import final
|
6
|
-
|
7
|
-
if sys.version_info >= (3, 11):
|
8
|
-
from asyncio.timeouts import timeout as timeout
|
9
|
-
|
10
|
-
else:
|
11
|
-
# ruff: noqa: S101
|
12
|
-
|
13
|
-
class _State(enum.Enum):
|
14
|
-
CREATED = "created"
|
15
|
-
ENTERED = "active"
|
16
|
-
EXPIRING = "expiring"
|
17
|
-
EXPIRED = "expired"
|
18
|
-
EXITED = "finished"
|
19
|
-
|
20
|
-
@final
|
21
|
-
class Timeout:
|
22
|
-
def __init__(self, when: float | None) -> None:
|
23
|
-
self._state = _State.CREATED
|
24
|
-
self._timeout_handler: asyncio.Handle | None = None
|
25
|
-
self._task: asyncio.Task | None = None
|
26
|
-
if when is not None:
|
27
|
-
when = asyncio.get_running_loop().time() + when
|
28
|
-
self._when = when
|
29
|
-
|
30
|
-
def when(self) -> float | None:
|
31
|
-
return self._when
|
32
|
-
|
33
|
-
def reschedule(self, when: float | None) -> None:
|
34
|
-
if self._state is not _State.ENTERED:
|
35
|
-
if self._state is _State.CREATED:
|
36
|
-
raise RuntimeError("Timeout has not been entered")
|
37
|
-
raise RuntimeError(
|
38
|
-
f"Cannot change state of {self._state.value} Timeout",
|
39
|
-
)
|
40
|
-
|
41
|
-
self._when = when
|
42
|
-
|
43
|
-
if self._timeout_handler is not None:
|
44
|
-
self._timeout_handler.cancel()
|
45
|
-
|
46
|
-
if when is None:
|
47
|
-
self._timeout_handler = None
|
48
|
-
else:
|
49
|
-
loop = asyncio.get_running_loop()
|
50
|
-
if when <= loop.time():
|
51
|
-
self._timeout_handler = loop.call_soon(self._on_timeout)
|
52
|
-
else:
|
53
|
-
self._timeout_handler = loop.call_at(when, self._on_timeout)
|
54
|
-
|
55
|
-
def expired(self) -> bool:
|
56
|
-
return self._state in (_State.EXPIRING, _State.EXPIRED)
|
57
|
-
|
58
|
-
def __repr__(self) -> str:
|
59
|
-
info = [""]
|
60
|
-
if self._state is _State.ENTERED:
|
61
|
-
when = round(self._when, 3) if self._when is not None else None
|
62
|
-
info.append(f"when={when}")
|
63
|
-
info_str = " ".join(info)
|
64
|
-
return f"<Timeout [{self._state.value}]{info_str}>"
|
65
|
-
|
66
|
-
async def __aenter__(self) -> "Timeout":
|
67
|
-
if self._state is not _State.CREATED:
|
68
|
-
raise RuntimeError("Timeout has already been entered")
|
69
|
-
task = asyncio.current_task()
|
70
|
-
if task is None:
|
71
|
-
raise RuntimeError("Timeout should be used inside a task")
|
72
|
-
self._state = _State.ENTERED
|
73
|
-
self._task = task
|
74
|
-
self.reschedule(self._when)
|
75
|
-
return self
|
76
|
-
|
77
|
-
async def __aexit__(
|
78
|
-
self,
|
79
|
-
exc_type: type[BaseException] | None,
|
80
|
-
exc_val: BaseException | None,
|
81
|
-
exc_tb: TracebackType | None,
|
82
|
-
) -> bool | None:
|
83
|
-
assert self._state in (_State.ENTERED, _State.EXPIRING)
|
84
|
-
|
85
|
-
if self._timeout_handler is not None:
|
86
|
-
self._timeout_handler.cancel()
|
87
|
-
self._timeout_handler = None
|
88
|
-
|
89
|
-
if self._state is _State.EXPIRING:
|
90
|
-
self._state = _State.EXPIRED
|
91
|
-
|
92
|
-
if exc_type is asyncio.CancelledError:
|
93
|
-
raise TimeoutError from exc_val
|
94
|
-
elif self._state is _State.ENTERED:
|
95
|
-
self._state = _State.EXITED
|
96
|
-
|
97
|
-
return None
|
98
|
-
|
99
|
-
def _on_timeout(self) -> None:
|
100
|
-
assert self._state is _State.ENTERED
|
101
|
-
assert self._task is not None
|
102
|
-
self._task.cancel()
|
103
|
-
self._state = _State.EXPIRING
|
104
|
-
self._timeout_handler = None
|
105
|
-
|
106
|
-
def timeout(delay: float | None) -> Timeout:
|
107
|
-
return Timeout(delay)
|
108
|
-
|
109
|
-
|
110
|
-
__all__ = ["timeout"]
|
@@ -1,62 +0,0 @@
|
|
1
|
-
from typing import Annotated
|
2
|
-
|
3
|
-
from nonebot import on_command, on_message
|
4
|
-
from nonebot.adapters import Bot, Event
|
5
|
-
from nonebot.exception import FinishedException
|
6
|
-
from nonebot.rule import to_me
|
7
|
-
from nonebot_plugin_alconna import MsgTarget, UniMessage, UniMsg
|
8
|
-
from nonebot_plugin_userinfo import EventUserInfo, UserInfo
|
9
|
-
|
10
|
-
from ._timeout import timeout
|
11
|
-
from .game import Game
|
12
|
-
from .ob11_ext import ob11_ext_enabled
|
13
|
-
from .utils import InputStore, is_group, prepare_game, rule_in_game, rule_not_in_game
|
14
|
-
|
15
|
-
in_game_message = on_message(rule=rule_in_game)
|
16
|
-
start_game = on_command(
|
17
|
-
"werewolf",
|
18
|
-
rule=to_me() & is_group & rule_not_in_game,
|
19
|
-
aliases={"狼人杀"},
|
20
|
-
)
|
21
|
-
|
22
|
-
|
23
|
-
@in_game_message.handle()
|
24
|
-
async def handle_input(event: Event, target: MsgTarget, msg: UniMsg) -> None:
|
25
|
-
if target.private:
|
26
|
-
InputStore.put(target.id, None, msg)
|
27
|
-
else:
|
28
|
-
InputStore.put(event.get_user_id(), target.id, msg)
|
29
|
-
|
30
|
-
|
31
|
-
@start_game.handle()
|
32
|
-
async def handle_start(
|
33
|
-
bot: Bot,
|
34
|
-
event: Event,
|
35
|
-
target: MsgTarget,
|
36
|
-
admin_info: Annotated[UserInfo, EventUserInfo()],
|
37
|
-
) -> None:
|
38
|
-
admin_id = event.get_user_id()
|
39
|
-
msg = (
|
40
|
-
UniMessage.at(admin_id)
|
41
|
-
.text("成功创建游戏\n")
|
42
|
-
.text("玩家请 @我 发送 “加入游戏”、“退出游戏”\n")
|
43
|
-
.text("玩家 @我 发送 “当前玩家” 可查看玩家列表\n")
|
44
|
-
.text("游戏发起者 @我 发送 “结束游戏” 可结束当前游戏\n")
|
45
|
-
.text("玩家均加入后,游戏发起者请 @我 发送 “开始游戏”\n")
|
46
|
-
)
|
47
|
-
if ob11_ext_enabled():
|
48
|
-
msg.text("\n可使用戳一戳代替游戏交互中的 “/stop” 命令")
|
49
|
-
await msg.text("\n\n游戏准备阶段限时5分钟,超时将自动结束").send()
|
50
|
-
|
51
|
-
players = {admin_id: admin_info.user_name}
|
52
|
-
|
53
|
-
try:
|
54
|
-
async with timeout(5 * 60):
|
55
|
-
await prepare_game(event, players)
|
56
|
-
except FinishedException:
|
57
|
-
raise
|
58
|
-
except TimeoutError:
|
59
|
-
await UniMessage.text("游戏准备超时,已自动结束").finish()
|
60
|
-
|
61
|
-
game = Game(bot, target, players)
|
62
|
-
game.start()
|
@@ -1,72 +0,0 @@
|
|
1
|
-
import contextlib
|
2
|
-
|
3
|
-
from nonebot import on_type
|
4
|
-
from nonebot.internal.matcher import current_bot
|
5
|
-
from nonebot_plugin_alconna import UniMessage
|
6
|
-
|
7
|
-
from .config import config
|
8
|
-
from .game import starting_games
|
9
|
-
from .utils import InputStore, user_in_game
|
10
|
-
|
11
|
-
|
12
|
-
def ob11_ext_enabled() -> bool:
|
13
|
-
return False
|
14
|
-
|
15
|
-
|
16
|
-
with contextlib.suppress(ImportError):
|
17
|
-
from nonebot.adapters.onebot.v11 import Bot, MessageSegment
|
18
|
-
from nonebot.adapters.onebot.v11.event import PokeNotifyEvent
|
19
|
-
|
20
|
-
# 游戏内戳一戳等效 "/stop"
|
21
|
-
async def _rule_poke_1(event: PokeNotifyEvent) -> bool:
|
22
|
-
if not config.enable_poke:
|
23
|
-
return False
|
24
|
-
|
25
|
-
user_id = str(event.user_id)
|
26
|
-
group_id = str(event.group_id) if event.group_id is not None else None
|
27
|
-
return (
|
28
|
-
config.enable_poke
|
29
|
-
and (event.target_id == event.self_id)
|
30
|
-
and user_in_game(user_id, group_id)
|
31
|
-
)
|
32
|
-
|
33
|
-
@on_type(PokeNotifyEvent, rule=_rule_poke_1).handle()
|
34
|
-
async def handle_poke_1(event: PokeNotifyEvent) -> None:
|
35
|
-
InputStore.put(
|
36
|
-
user_id=str(event.user_id),
|
37
|
-
group_id=str(event.group_id) if event.group_id is not None else None,
|
38
|
-
msg=UniMessage.text("/stop"),
|
39
|
-
)
|
40
|
-
|
41
|
-
# 准备阶段戳一戳等效加入游戏
|
42
|
-
async def _rule_poke_2(event: PokeNotifyEvent) -> bool:
|
43
|
-
if not config.enable_poke or event.group_id is None:
|
44
|
-
return False
|
45
|
-
|
46
|
-
user_id = str(event.user_id)
|
47
|
-
group_id = str(event.group_id)
|
48
|
-
return (
|
49
|
-
(event.target_id == event.self_id)
|
50
|
-
and not user_in_game(user_id, group_id)
|
51
|
-
and group_id in starting_games
|
52
|
-
)
|
53
|
-
|
54
|
-
@on_type(PokeNotifyEvent, rule=_rule_poke_2).handle()
|
55
|
-
async def handle_poke_2(bot: Bot, event: PokeNotifyEvent) -> None:
|
56
|
-
user_id = str(event.user_id)
|
57
|
-
group_id = str(event.group_id)
|
58
|
-
players = starting_games[group_id]
|
59
|
-
|
60
|
-
if user_id not in players:
|
61
|
-
res: dict[str, str] = await bot.get_group_member_info(
|
62
|
-
group_id=int(group_id),
|
63
|
-
user_id=int(user_id),
|
64
|
-
)
|
65
|
-
players[user_id] = res.get("card") or res.get("nickname") or user_id
|
66
|
-
await bot.send(event, MessageSegment.at(user_id) + "成功加入游戏")
|
67
|
-
|
68
|
-
def ob11_ext_enabled() -> bool:
|
69
|
-
if not config.enable_poke:
|
70
|
-
return False
|
71
|
-
|
72
|
-
return isinstance(current_bot.get(), Bot)
|