nonebot-plugin-werewolf 1.0.1__tar.gz → 1.0.3__tar.gz
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-1.0.1 → nonebot_plugin_werewolf-1.0.3}/PKG-INFO +12 -6
- {nonebot_plugin_werewolf-1.0.1 → nonebot_plugin_werewolf-1.0.3}/README.md +11 -5
- {nonebot_plugin_werewolf-1.0.1 → nonebot_plugin_werewolf-1.0.3}/nonebot_plugin_werewolf/__init__.py +1 -1
- {nonebot_plugin_werewolf-1.0.1 → nonebot_plugin_werewolf-1.0.3}/nonebot_plugin_werewolf/constant.py +1 -0
- {nonebot_plugin_werewolf-1.0.1 → nonebot_plugin_werewolf-1.0.3}/nonebot_plugin_werewolf/game.py +64 -32
- nonebot_plugin_werewolf-1.0.3/nonebot_plugin_werewolf/matchers.py +66 -0
- nonebot_plugin_werewolf-1.0.3/nonebot_plugin_werewolf/ob11_ext.py +72 -0
- {nonebot_plugin_werewolf-1.0.1 → nonebot_plugin_werewolf-1.0.3}/nonebot_plugin_werewolf/player.py +65 -128
- nonebot_plugin_werewolf-1.0.3/nonebot_plugin_werewolf/player_set.py +87 -0
- nonebot_plugin_werewolf-1.0.3/nonebot_plugin_werewolf/utils.py +188 -0
- {nonebot_plugin_werewolf-1.0.1 → nonebot_plugin_werewolf-1.0.3}/pyproject.toml +1 -1
- nonebot_plugin_werewolf-1.0.1/nonebot_plugin_werewolf/matchers.py +0 -240
- nonebot_plugin_werewolf-1.0.1/nonebot_plugin_werewolf/utils.py +0 -34
- {nonebot_plugin_werewolf-1.0.1 → nonebot_plugin_werewolf-1.0.3}/LICENSE +0 -0
- {nonebot_plugin_werewolf-1.0.1 → nonebot_plugin_werewolf-1.0.3}/nonebot_plugin_werewolf/config.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: nonebot-plugin-werewolf
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.3
|
4
4
|
Summary: Default template for PDM package
|
5
5
|
Author-Email: wyf7685 <wyf7685@163.com>
|
6
6
|
License: MIT
|
@@ -12,9 +12,9 @@ Requires-Dist: nonebot-plugin-waiter>=0.7.1
|
|
12
12
|
Description-Content-Type: text/markdown
|
13
13
|
|
14
14
|
<div align="center">
|
15
|
-
<a href="https://v2.nonebot.dev/store"
|
16
|
-
|
17
|
-
|
15
|
+
<a href="https://v2.nonebot.dev/store">
|
16
|
+
<img src="https://github.com/wyf7685/wyf7685/blob/main/assets/NoneBotPlugin.svg" width="300" alt="logo">
|
17
|
+
</a>
|
18
18
|
</div>
|
19
19
|
|
20
20
|
<div align="center">
|
@@ -24,7 +24,7 @@ Description-Content-Type: text/markdown
|
|
24
24
|
_✨ 简单的狼人杀插件 ✨_
|
25
25
|
|
26
26
|
[](./LICENSE)
|
27
|
-
[](https://pypi.python.org/pypi/nonebot-plugin-
|
27
|
+
[](https://pypi.python.org/pypi/nonebot-plugin-werewolf)
|
28
28
|
[](https://www.python.org/)
|
29
29
|
|
30
30
|
[](https://pdm-project.org)
|
@@ -143,7 +143,9 @@ _✨ 简单的狼人杀插件 ✨_
|
|
143
143
|
职业预设可以通过配置项 `werewolf__override_preset` 修改
|
144
144
|
|
145
145
|
<details>
|
146
|
-
|
146
|
+
<summary>示例</summary>
|
147
|
+
|
148
|
+
配置项 `werewolf__override_preset`
|
147
149
|
|
148
150
|
```env
|
149
151
|
werewolf__override_preset='
|
@@ -169,6 +171,10 @@ werewolf__override_preset='
|
|
169
171
|
<details>
|
170
172
|
<summary>更新日志</summary>
|
171
173
|
|
174
|
+
- 2024.09.01 v1.0.3
|
175
|
+
|
176
|
+
- 优化玩家交互体验
|
177
|
+
|
172
178
|
- 2024.08.31 v1.0.1
|
173
179
|
|
174
180
|
- 允许通过配置项修改职业预设
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<div align="center">
|
2
|
-
<a href="https://v2.nonebot.dev/store"
|
3
|
-
|
4
|
-
|
2
|
+
<a href="https://v2.nonebot.dev/store">
|
3
|
+
<img src="https://github.com/wyf7685/wyf7685/blob/main/assets/NoneBotPlugin.svg" width="300" alt="logo">
|
4
|
+
</a>
|
5
5
|
</div>
|
6
6
|
|
7
7
|
<div align="center">
|
@@ -11,7 +11,7 @@
|
|
11
11
|
_✨ 简单的狼人杀插件 ✨_
|
12
12
|
|
13
13
|
[](./LICENSE)
|
14
|
-
[](https://pypi.python.org/pypi/nonebot-plugin-
|
14
|
+
[](https://pypi.python.org/pypi/nonebot-plugin-werewolf)
|
15
15
|
[](https://www.python.org/)
|
16
16
|
|
17
17
|
[](https://pdm-project.org)
|
@@ -130,7 +130,9 @@ _✨ 简单的狼人杀插件 ✨_
|
|
130
130
|
职业预设可以通过配置项 `werewolf__override_preset` 修改
|
131
131
|
|
132
132
|
<details>
|
133
|
-
|
133
|
+
<summary>示例</summary>
|
134
|
+
|
135
|
+
配置项 `werewolf__override_preset`
|
134
136
|
|
135
137
|
```env
|
136
138
|
werewolf__override_preset='
|
@@ -156,6 +158,10 @@ werewolf__override_preset='
|
|
156
158
|
<details>
|
157
159
|
<summary>更新日志</summary>
|
158
160
|
|
161
|
+
- 2024.09.01 v1.0.3
|
162
|
+
|
163
|
+
- 优化玩家交互体验
|
164
|
+
|
159
165
|
- 2024.08.31 v1.0.1
|
160
166
|
|
161
167
|
- 允许通过配置项修改职业预设
|
{nonebot_plugin_werewolf-1.0.1 → nonebot_plugin_werewolf-1.0.3}/nonebot_plugin_werewolf/game.py
RENAMED
@@ -1,13 +1,16 @@
|
|
1
1
|
import asyncio
|
2
2
|
import asyncio.timeouts
|
3
3
|
import random
|
4
|
-
from collections.abc import Callable
|
5
4
|
|
6
5
|
from nonebot.adapters import Bot
|
7
6
|
from nonebot_plugin_alconna import Target, UniMessage
|
8
7
|
|
9
8
|
from .constant import GameState, GameStatus, KillReason, Role, RoleGroup, player_preset
|
10
|
-
from .player import Player
|
9
|
+
from .player import Player
|
10
|
+
from .player_set import PlayerSet
|
11
|
+
|
12
|
+
starting_games: dict[str, dict[str, str]] = {}
|
13
|
+
running_games: dict[str, tuple["Game", asyncio.Task[None]]] = {}
|
11
14
|
|
12
15
|
|
13
16
|
def init_players(bot: Bot, game: "Game", players: dict[str, str]) -> PlayerSet:
|
@@ -15,7 +18,7 @@ def init_players(bot: Bot, game: "Game", players: dict[str, str]) -> PlayerSet:
|
|
15
18
|
if preset is None:
|
16
19
|
raise ValueError(
|
17
20
|
f"玩家人数不符: "
|
18
|
-
f"应为{
|
21
|
+
f"应为 {', '.join(map(str, player_preset))} 人, 传入{len(players)}人"
|
19
22
|
)
|
20
23
|
|
21
24
|
roles: list[Role] = []
|
@@ -50,20 +53,18 @@ class Game:
|
|
50
53
|
group: Target
|
51
54
|
players: PlayerSet
|
52
55
|
state: GameState
|
53
|
-
|
56
|
+
killed_players: list[Player]
|
54
57
|
|
55
58
|
def __init__(
|
56
59
|
self,
|
57
60
|
bot: Bot,
|
58
61
|
group: Target,
|
59
62
|
players: dict[str, str],
|
60
|
-
on_exit: Callable[[], None],
|
61
63
|
) -> None:
|
62
64
|
self.bot = bot
|
63
65
|
self.group = group
|
64
66
|
self.players = init_players(bot, self, players)
|
65
|
-
self.state = GameState()
|
66
|
-
self._on_exit = on_exit
|
67
|
+
self.state = GameState(0)
|
67
68
|
|
68
69
|
async def send(self, message: str | UniMessage):
|
69
70
|
if isinstance(message, str):
|
@@ -78,27 +79,49 @@ class Game:
|
|
78
79
|
|
79
80
|
def check_game_status(self) -> GameStatus:
|
80
81
|
players = self.players.alive()
|
81
|
-
|
82
82
|
w = players.select(RoleGroup.狼人)
|
83
|
-
if not w.size:
|
84
|
-
return GameStatus.Good
|
85
|
-
|
86
83
|
p = players.exclude(RoleGroup.狼人)
|
84
|
+
|
87
85
|
if w.size >= p.size:
|
88
86
|
return GameStatus.Bad
|
89
|
-
if not
|
87
|
+
if not p.select(Role.平民):
|
90
88
|
return GameStatus.Bad
|
91
|
-
if not
|
89
|
+
if not p.exclude(Role.平民):
|
92
90
|
return GameStatus.Bad
|
91
|
+
if not w.size:
|
92
|
+
return GameStatus.Good
|
93
93
|
|
94
94
|
return GameStatus.Unset
|
95
95
|
|
96
|
+
def show_killed_players(self) -> str:
|
97
|
+
msg = ""
|
98
|
+
|
99
|
+
for player in self.killed_players:
|
100
|
+
if player.kill_info is None:
|
101
|
+
continue
|
102
|
+
|
103
|
+
msg += f"{player.name} 被 " + ", ".join(
|
104
|
+
p.name for p in player.kill_info.killers
|
105
|
+
)
|
106
|
+
match player.kill_info.reason:
|
107
|
+
case KillReason.Kill:
|
108
|
+
msg += " 刀了"
|
109
|
+
case KillReason.Poison:
|
110
|
+
msg += " 毒死"
|
111
|
+
case KillReason.Shoot:
|
112
|
+
msg += " 射杀"
|
113
|
+
case KillReason.Vote:
|
114
|
+
msg += " 投票放逐"
|
115
|
+
msg += "\n\n"
|
116
|
+
|
117
|
+
return msg.strip()
|
118
|
+
|
96
119
|
async def notify_player_role(self) -> None:
|
97
120
|
preset = player_preset[len(self.players)]
|
98
121
|
await asyncio.gather(
|
99
122
|
self.send(
|
100
123
|
self.at_all()
|
101
|
-
.text("\n正在分配职业,请注意查看私聊消息\n")
|
124
|
+
.text("\n\n正在分配职业,请注意查看私聊消息\n")
|
102
125
|
.text(f"当前玩家数: {len(self.players)}\n")
|
103
126
|
.text(f"职业分配: 狼人x{preset[0]}, 神职x{preset[1]}, 平民x{preset[2]}")
|
104
127
|
),
|
@@ -125,8 +148,10 @@ class Game:
|
|
125
148
|
type_.role.name # Player
|
126
149
|
if isinstance(type_, Player)
|
127
150
|
else (
|
128
|
-
type_.name
|
129
|
-
|
151
|
+
type_.name # Role
|
152
|
+
if isinstance(type_, Role)
|
153
|
+
else f"{type_.name}阵营" # RoleGroup
|
154
|
+
)
|
130
155
|
)
|
131
156
|
|
132
157
|
await players.broadcast(f"{text}交互开始,限时 {timeout_secs/60:.2f} 分钟")
|
@@ -176,9 +201,10 @@ class Game:
|
|
176
201
|
if not players:
|
177
202
|
return
|
178
203
|
|
179
|
-
for player in players:
|
204
|
+
for player in players.dead():
|
180
205
|
await player.post_kill()
|
181
206
|
await self.handle_new_dead(player)
|
207
|
+
self.killed_players.append(player)
|
182
208
|
|
183
209
|
(shooter, shoot) = self.state.shoot
|
184
210
|
if shooter is not None and shoot is not None:
|
@@ -189,27 +215,32 @@ class Game:
|
|
189
215
|
.text("限时1分钟, 发送 “/stop” 结束发言")
|
190
216
|
)
|
191
217
|
await self.wait_stop(shoot, 60)
|
218
|
+
await self.post_kill(shoot)
|
192
219
|
self.state.shoot = (None, None)
|
193
220
|
|
194
221
|
async def run_vote(self) -> None:
|
195
222
|
# 统计投票结果
|
196
|
-
|
223
|
+
players = self.players.alive()
|
224
|
+
|
225
|
+
# 被票玩家: [投票玩家]
|
226
|
+
vote_result: dict[Player, list[Player]] = await players.vote(60)
|
227
|
+
# 票数: [被票玩家]
|
197
228
|
vote_reversed: dict[int, list[Player]] = {}
|
198
|
-
|
199
|
-
|
229
|
+
# 收集到的总票数
|
230
|
+
total_votes = sum(map(len, vote_result.values()))
|
200
231
|
|
201
232
|
# 投票结果公示
|
202
233
|
msg = UniMessage.text("投票结果:\n")
|
203
234
|
for p, v in sorted(vote_result.items(), key=lambda x: x[1], reverse=True):
|
204
235
|
if p is not None:
|
205
236
|
msg.at(p.user_id).text(f": {v} 票\n")
|
206
|
-
vote_reversed[v] = [*vote_reversed.get(v, []), p]
|
207
|
-
if v :=
|
237
|
+
vote_reversed[len(v)] = [*vote_reversed.get(len(v), []), p]
|
238
|
+
if v := (len(players) - total_votes):
|
208
239
|
msg.text(f"弃票: {v} 票\n")
|
209
240
|
await self.send(msg)
|
210
241
|
|
211
242
|
# 全员弃票 # 不是哥们?
|
212
|
-
if
|
243
|
+
if total_votes == 0:
|
213
244
|
await self.send("没有人被票出")
|
214
245
|
return
|
215
246
|
|
@@ -224,7 +255,7 @@ class Game:
|
|
224
255
|
|
225
256
|
# 仅有一名玩家票数最高
|
226
257
|
voted = vs.pop()
|
227
|
-
if not await voted.kill(KillReason.Vote):
|
258
|
+
if not await voted.kill(KillReason.Vote, *vote_result[voted]):
|
228
259
|
# 投票放逐失败 (例: 白痴)
|
229
260
|
return
|
230
261
|
|
@@ -267,7 +298,7 @@ class Game:
|
|
267
298
|
|
268
299
|
while self.check_game_status() == GameStatus.Unset:
|
269
300
|
# 重置游戏状态,进入下一夜
|
270
|
-
self.state = GameState()
|
301
|
+
self.state = GameState(day_count)
|
271
302
|
players = self.players.alive()
|
272
303
|
await self.send("天黑请闭眼...")
|
273
304
|
|
@@ -290,19 +321,19 @@ class Game:
|
|
290
321
|
if killed is not None:
|
291
322
|
# 除非守卫保护或女巫使用解药,否则狼人正常击杀玩家
|
292
323
|
if not ((killed is protected) or (antidote and potioned is killed)):
|
293
|
-
await killed.kill(KillReason.Kill)
|
324
|
+
await killed.kill(KillReason.Kill, *players.select(RoleGroup.狼人))
|
294
325
|
# 如果女巫使用毒药且守卫未保护,杀死该玩家
|
295
326
|
if poison and (potioned is not None) and (potioned is not protected):
|
296
|
-
await potioned.kill(KillReason.Poison)
|
327
|
+
await potioned.kill(KillReason.Poison, *players.select(Role.女巫))
|
297
328
|
|
298
329
|
day_count += 1
|
299
|
-
msg = UniMessage.text(f"
|
330
|
+
msg = UniMessage.text(f"『第{day_count}天』天亮了...\n")
|
300
331
|
# 没有玩家死亡,平安夜
|
301
332
|
if not (dead := players.dead()):
|
302
333
|
await self.send(msg.text("昨晚是平安夜"))
|
303
334
|
# 有玩家死亡,执行死亡流程
|
304
335
|
else:
|
305
|
-
#
|
336
|
+
# 公布死者名单
|
306
337
|
msg.text("昨晚的死者是:")
|
307
338
|
for p in dead.sorted():
|
308
339
|
msg.text("\n").at(p.user_id)
|
@@ -336,8 +367,9 @@ class Game:
|
|
336
367
|
# 游戏结束
|
337
368
|
dead_channel.cancel()
|
338
369
|
winner = "好人" if self.check_game_status() == GameStatus.Good else "狼人"
|
339
|
-
msg = UniMessage.text(f"
|
340
|
-
for p in sorted(self.players, key=lambda p: (p.role.
|
370
|
+
msg = UniMessage.text(f"🎉游戏结束,{winner}获胜\n\n")
|
371
|
+
for p in sorted(self.players, key=lambda p: (p.role.value, p.user_id)):
|
341
372
|
msg.at(p.user_id).text(f": {p.role.name}\n")
|
373
|
+
msg.text(f"\n{self.show_killed_players()}")
|
342
374
|
await self.send(msg)
|
343
|
-
self.
|
375
|
+
running_games.pop(self.group.id, None)
|
@@ -0,0 +1,66 @@
|
|
1
|
+
import asyncio
|
2
|
+
import asyncio.timeouts
|
3
|
+
from typing import Annotated
|
4
|
+
|
5
|
+
from nonebot import on_command, on_message
|
6
|
+
from nonebot.adapters import Bot, Event
|
7
|
+
from nonebot.rule import to_me
|
8
|
+
from nonebot_plugin_alconna import MsgTarget, UniMessage, UniMsg
|
9
|
+
from nonebot_plugin_userinfo import EventUserInfo, UserInfo
|
10
|
+
|
11
|
+
from .game import Game, running_games, starting_games
|
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
|
+
if target.id in running_games:
|
39
|
+
await UniMessage.text("当前群聊内有正在进行的游戏,无法创建游戏").finish()
|
40
|
+
|
41
|
+
admin_id = event.get_user_id()
|
42
|
+
msg = (
|
43
|
+
UniMessage.at(admin_id)
|
44
|
+
.text("成功创建游戏\n")
|
45
|
+
.text("玩家请 @我 发送 “加入游戏”、“退出游戏”\n")
|
46
|
+
.text("玩家 @我 发送 “当前玩家” 可查看玩家列表\n")
|
47
|
+
.text("游戏发起者 @我 发送 “结束游戏” 可结束当前游戏\n")
|
48
|
+
.text("玩家均加入后,游戏发起者请 @我 发送 “开始游戏”\n")
|
49
|
+
)
|
50
|
+
if ob11_ext_enabled():
|
51
|
+
msg.text("\n可使用戳一戳代替游戏交互中的 “/stop” 命令")
|
52
|
+
await msg.text("\n\n游戏准备阶段限时5分钟,超时将自动结束").send()
|
53
|
+
|
54
|
+
players = starting_games[target.id] = {admin_id: admin_info.user_name}
|
55
|
+
|
56
|
+
try:
|
57
|
+
async with asyncio.timeouts.timeout(5 * 60):
|
58
|
+
await prepare_game(event, players)
|
59
|
+
except TimeoutError:
|
60
|
+
await UniMessage.text("游戏准备超时,已自动结束").finish()
|
61
|
+
finally:
|
62
|
+
del starting_games[target.id]
|
63
|
+
|
64
|
+
game = Game(bot=bot, group=target, players=players)
|
65
|
+
task = asyncio.create_task(game.run())
|
66
|
+
running_games[target.id] = (game, task)
|
@@ -0,0 +1,72 @@
|
|
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("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)
|