easterobot 1.0.0__py3-none-any.whl → 1.1.0__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.
- easterobot/alembic/env.py +91 -0
- easterobot/alembic/script.py.mako +28 -0
- easterobot/alembic/versions/2f0d4305e320_init_database.py +67 -0
- easterobot/alembic/versions/940c3b9c702d_add_lock_on_eggs.py +38 -0
- easterobot/bot.py +93 -462
- easterobot/cli.py +56 -17
- easterobot/commands/__init__.py +8 -0
- easterobot/commands/base.py +3 -7
- easterobot/commands/basket.py +10 -12
- easterobot/commands/edit.py +4 -4
- easterobot/commands/game.py +187 -0
- easterobot/commands/help.py +1 -1
- easterobot/commands/reset.py +1 -1
- easterobot/commands/search.py +3 -3
- easterobot/commands/top.py +7 -18
- easterobot/config.py +67 -3
- easterobot/games/__init__.py +14 -0
- easterobot/games/connect.py +206 -0
- easterobot/games/game.py +262 -0
- easterobot/games/rock_paper_scissor.py +206 -0
- easterobot/games/tic_tac_toe.py +168 -0
- easterobot/hunts/__init__.py +14 -0
- easterobot/hunts/hunt.py +428 -0
- easterobot/hunts/rank.py +82 -0
- easterobot/models.py +2 -1
- easterobot/resources/alembic.ini +87 -0
- easterobot/resources/config.example.yml +10 -2
- easterobot/resources/credits.txt +5 -1
- easterobot/resources/emotes/icons/arrow.png +0 -0
- easterobot/resources/emotes/icons/end.png +0 -0
- easterobot/resources/emotes/icons/versus.png +0 -0
- easterobot/resources/emotes/icons/wait.png +0 -0
- {easterobot-1.0.0.dist-info → easterobot-1.1.0.dist-info}/METADATA +11 -5
- easterobot-1.1.0.dist-info/RECORD +66 -0
- easterobot-1.0.0.dist-info/RECORD +0 -48
- /easterobot/resources/{eggs → emotes/eggs}/egg_01.png +0 -0
- /easterobot/resources/{eggs → emotes/eggs}/egg_02.png +0 -0
- /easterobot/resources/{eggs → emotes/eggs}/egg_03.png +0 -0
- /easterobot/resources/{eggs → emotes/eggs}/egg_04.png +0 -0
- /easterobot/resources/{eggs → emotes/eggs}/egg_05.png +0 -0
- /easterobot/resources/{eggs → emotes/eggs}/egg_06.png +0 -0
- /easterobot/resources/{eggs → emotes/eggs}/egg_07.png +0 -0
- /easterobot/resources/{eggs → emotes/eggs}/egg_08.png +0 -0
- /easterobot/resources/{eggs → emotes/eggs}/egg_09.png +0 -0
- /easterobot/resources/{eggs → emotes/eggs}/egg_10.png +0 -0
- /easterobot/resources/{eggs → emotes/eggs}/egg_11.png +0 -0
- /easterobot/resources/{eggs → emotes/eggs}/egg_12.png +0 -0
- /easterobot/resources/{eggs → emotes/eggs}/egg_13.png +0 -0
- /easterobot/resources/{eggs → emotes/eggs}/egg_14.png +0 -0
- /easterobot/resources/{eggs → emotes/eggs}/egg_15.png +0 -0
- /easterobot/resources/{eggs → emotes/eggs}/egg_16.png +0 -0
- /easterobot/resources/{eggs → emotes/eggs}/egg_17.png +0 -0
- /easterobot/resources/{eggs → emotes/eggs}/egg_18.png +0 -0
- /easterobot/resources/{eggs → emotes/eggs}/egg_19.png +0 -0
- /easterobot/resources/{eggs → emotes/eggs}/egg_20.png +0 -0
- {easterobot-1.0.0.dist-info → easterobot-1.1.0.dist-info}/WHEEL +0 -0
- {easterobot-1.0.0.dist-info → easterobot-1.1.0.dist-info}/entry_points.txt +0 -0
- {easterobot-1.0.0.dist-info → easterobot-1.1.0.dist-info}/licenses/LICENSE +0 -0
easterobot/cli.py
CHANGED
@@ -6,11 +6,17 @@ import sys
|
|
6
6
|
from collections.abc import Sequence
|
7
7
|
from typing import NoReturn, Optional
|
8
8
|
|
9
|
-
from .
|
9
|
+
from alembic.config import CommandLine
|
10
|
+
|
11
|
+
from easterobot.config import load_config_from_path
|
12
|
+
|
13
|
+
from .bot import Easterobot
|
14
|
+
from .config import DEFAULT_CONFIG_PATH
|
10
15
|
from .info import __issues__, __summary__, __version__
|
11
16
|
|
12
17
|
LOG_LEVELS = ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"]
|
13
18
|
logger = logging.getLogger(__name__)
|
19
|
+
cmd_alembic = CommandLine(prog="easterobot alembic")
|
14
20
|
|
15
21
|
|
16
22
|
class HelpArgumentParser(argparse.ArgumentParser):
|
@@ -61,7 +67,7 @@ def get_parser() -> argparse.ArgumentParser:
|
|
61
67
|
action="store_true",
|
62
68
|
)
|
63
69
|
|
64
|
-
# Parser
|
70
|
+
# Parser for run command
|
65
71
|
run_parser = subparsers.add_parser(
|
66
72
|
"run",
|
67
73
|
parents=[parent_parser],
|
@@ -74,19 +80,40 @@ def get_parser() -> argparse.ArgumentParser:
|
|
74
80
|
default=DEFAULT_CONFIG_PATH,
|
75
81
|
)
|
76
82
|
|
77
|
-
# Parser
|
78
|
-
|
83
|
+
# Parser for generate command
|
84
|
+
generate_parser = subparsers.add_parser(
|
79
85
|
"generate",
|
80
86
|
parents=[parent_parser],
|
81
87
|
help="generate a configuration.",
|
82
88
|
)
|
83
|
-
|
89
|
+
generate_parser.add_argument(
|
84
90
|
"-i",
|
85
91
|
"--interactive",
|
86
92
|
help="ask questions for create a ready to use config.",
|
87
93
|
action="store_true",
|
88
94
|
)
|
89
|
-
|
95
|
+
generate_parser.add_argument("destination", default=".")
|
96
|
+
|
97
|
+
# Parser for alembic
|
98
|
+
alembic_parser = subparsers.add_parser(
|
99
|
+
"alembic",
|
100
|
+
parents=[cmd_alembic.parser], # type: ignore[list-item]
|
101
|
+
help="use alembic with bot context.",
|
102
|
+
add_help=False,
|
103
|
+
)
|
104
|
+
for action in alembic_parser._actions: # noqa: SLF001
|
105
|
+
if "--config" in action.option_strings:
|
106
|
+
alembic_parser._handle_conflict_resolve( # noqa: SLF001
|
107
|
+
None, # type: ignore[arg-type]
|
108
|
+
[("--config", action), ("-c", action)],
|
109
|
+
)
|
110
|
+
break
|
111
|
+
alembic_parser.add_argument(
|
112
|
+
"-c",
|
113
|
+
"--config",
|
114
|
+
help=f"path to configuration, default to {DEFAULT_CONFIG_PATH}.",
|
115
|
+
default=DEFAULT_CONFIG_PATH,
|
116
|
+
)
|
90
117
|
return parser
|
91
118
|
|
92
119
|
|
@@ -101,27 +128,39 @@ def setup_logging(verbose: Optional[bool] = None) -> None:
|
|
101
128
|
|
102
129
|
def entrypoint(argv: Optional[Sequence[str]] = None) -> None:
|
103
130
|
"""Entrypoint for command line interface."""
|
131
|
+
args = list(sys.argv[1:] if argv is None else argv)
|
104
132
|
try:
|
105
133
|
parser = get_parser()
|
106
|
-
|
107
|
-
|
108
|
-
|
134
|
+
namespace = parser.parse_args(args)
|
135
|
+
if namespace.action == "run":
|
136
|
+
setup_logging(namespace.verbose)
|
109
137
|
bot = Easterobot.from_config(
|
110
|
-
|
111
|
-
token=
|
112
|
-
env=
|
138
|
+
namespace.config,
|
139
|
+
token=namespace.token,
|
140
|
+
env=namespace.env,
|
113
141
|
)
|
114
142
|
bot.auto_run()
|
115
|
-
elif
|
143
|
+
elif namespace.action == "generate":
|
144
|
+
setup_logging(namespace.verbose)
|
116
145
|
Easterobot.generate(
|
117
|
-
destination=
|
118
|
-
token=
|
119
|
-
env=
|
120
|
-
interactive=
|
146
|
+
destination=namespace.destination,
|
147
|
+
token=namespace.token,
|
148
|
+
env=namespace.env,
|
149
|
+
interactive=namespace.interactive,
|
121
150
|
)
|
151
|
+
elif namespace.action == "alembic":
|
152
|
+
if not hasattr(namespace, "cmd"):
|
153
|
+
# see http://bugs.python.org/issue9253, argparse
|
154
|
+
# behavior changed incompatibly in py3.3
|
155
|
+
parser.error("too few arguments")
|
156
|
+
else:
|
157
|
+
config = load_config_from_path(namespace.config)
|
158
|
+
cfg = config.alembic_config(namespace)
|
159
|
+
cmd_alembic.run_cmd(cfg, namespace)
|
122
160
|
else:
|
123
161
|
parser.error("No command specified") # pragma: no cover
|
124
162
|
except Exception as err: # NoQA: BLE001 # pragma: no cover
|
163
|
+
setup_logging(verbose=True)
|
125
164
|
logger.critical("Unexpected error", exc_info=err)
|
126
165
|
logger.critical("Please, report this error to %s.", __issues__)
|
127
166
|
sys.exit(1)
|
easterobot/commands/__init__.py
CHANGED
@@ -6,6 +6,11 @@ from easterobot.commands.basket import basket_command
|
|
6
6
|
from easterobot.commands.disable import disable_command
|
7
7
|
from easterobot.commands.edit import edit_command
|
8
8
|
from easterobot.commands.enable import enable_command
|
9
|
+
from easterobot.commands.game import (
|
10
|
+
connect4_command,
|
11
|
+
rockpaperscissor_command,
|
12
|
+
tictactoe_command,
|
13
|
+
)
|
9
14
|
from easterobot.commands.help import help_command
|
10
15
|
from easterobot.commands.reset import reset_command
|
11
16
|
from easterobot.commands.search import search_command
|
@@ -13,13 +18,16 @@ from easterobot.commands.top import top_command
|
|
13
18
|
|
14
19
|
__all__ = [
|
15
20
|
"basket_command",
|
21
|
+
"connect4_command",
|
16
22
|
"disable_command",
|
17
23
|
"edit_command",
|
18
24
|
"egg_command_group",
|
19
25
|
"enable_command",
|
20
26
|
"help_command",
|
21
27
|
"reset_command",
|
28
|
+
"rockpaperscissor_command",
|
22
29
|
"search_command",
|
30
|
+
"tictactoe_command",
|
23
31
|
"top_command",
|
24
32
|
]
|
25
33
|
|
easterobot/commands/base.py
CHANGED
@@ -33,8 +33,6 @@ InteractionChannel = Union[
|
|
33
33
|
discord.VoiceChannel,
|
34
34
|
discord.StageChannel,
|
35
35
|
discord.TextChannel,
|
36
|
-
discord.ForumChannel,
|
37
|
-
discord.CategoryChannel,
|
38
36
|
discord.Thread,
|
39
37
|
discord.DMChannel,
|
40
38
|
discord.GroupChannel,
|
@@ -98,10 +96,8 @@ def controlled_command(
|
|
98
96
|
have_perms = interaction.user.guild_permissions.is_superset(
|
99
97
|
needed_perms
|
100
98
|
)
|
101
|
-
|
102
|
-
|
103
|
-
interaction.user.id in admin_ids
|
104
|
-
or interaction.user.id == interaction.client.owner_id
|
99
|
+
is_super_admin = interaction.client.is_super_admin(
|
100
|
+
interaction.user
|
105
101
|
)
|
106
102
|
if not have_perms and not is_super_admin:
|
107
103
|
logger.warning("%s failed for wrong permissions", event_repr)
|
@@ -113,7 +109,7 @@ def controlled_command(
|
|
113
109
|
|
114
110
|
# Check command cooldown
|
115
111
|
cmd = interaction.command.name
|
116
|
-
if cooldown:
|
112
|
+
if cooldown and not is_super_admin:
|
117
113
|
available_at = None
|
118
114
|
async with (
|
119
115
|
AsyncSession(interaction.client.engine) as session,
|
easterobot/commands/basket.py
CHANGED
@@ -7,13 +7,14 @@ from discord import app_commands
|
|
7
7
|
from sqlalchemy import and_, func, select
|
8
8
|
from sqlalchemy.ext.asyncio import AsyncSession
|
9
9
|
|
10
|
-
from easterobot.bot import embed
|
11
10
|
from easterobot.commands.base import (
|
12
11
|
Context,
|
13
12
|
controlled_command,
|
14
13
|
egg_command_group,
|
15
14
|
)
|
16
15
|
from easterobot.config import agree
|
16
|
+
from easterobot.hunts.hunt import embed
|
17
|
+
from easterobot.hunts.rank import Ranking
|
17
18
|
from easterobot.models import Egg
|
18
19
|
|
19
20
|
|
@@ -41,10 +42,8 @@ async def basket_command(
|
|
41
42
|
async with AsyncSession(ctx.client.engine) as session:
|
42
43
|
morsels = []
|
43
44
|
missing = []
|
44
|
-
|
45
|
-
|
46
|
-
)
|
47
|
-
rank = user_egg_count[1] if user_egg_count else None
|
45
|
+
ranking = await Ranking.from_guild(session, ctx.guild_id)
|
46
|
+
hunter_rank = ranking.get(hunter.id)
|
48
47
|
res = await session.execute(
|
49
48
|
select(
|
50
49
|
Egg.emoji_id,
|
@@ -60,7 +59,7 @@ async def basket_command(
|
|
60
59
|
)
|
61
60
|
egg_counts: dict[int, int] = dict(res.all()) # type: ignore[arg-type]
|
62
61
|
egg_count = 0
|
63
|
-
for emoji in ctx.client.
|
62
|
+
for emoji in ctx.client.egg_emotes.choices:
|
64
63
|
try:
|
65
64
|
type_count = egg_counts.pop(emoji.id)
|
66
65
|
egg_count += type_count
|
@@ -73,12 +72,11 @@ async def basket_command(
|
|
73
72
|
egg_count += absent_count
|
74
73
|
morsels.insert(0, f"🥚 \xd7 {absent_count}")
|
75
74
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
)
|
75
|
+
il = ctx.client.config.conjugate("{Iel} est", hunter)
|
76
|
+
morsels.insert(
|
77
|
+
0,
|
78
|
+
f"**{'Tu es' if you else il} au rang** {hunter_rank.badge}\n",
|
79
|
+
)
|
82
80
|
|
83
81
|
their = "te" if you else "lui"
|
84
82
|
if missing:
|
easterobot/commands/edit.py
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
"""Module for edit command."""
|
2
2
|
|
3
3
|
import discord
|
4
|
+
from discord import app_commands
|
4
5
|
from sqlalchemy import and_, delete, select
|
5
6
|
from sqlalchemy.ext.asyncio import AsyncSession
|
6
7
|
|
7
|
-
from easterobot.bot import embed
|
8
8
|
from easterobot.config import RAND, agree
|
9
|
+
from easterobot.hunts.hunt import embed
|
9
10
|
from easterobot.models import Egg
|
10
11
|
|
11
12
|
from .base import Context, controlled_command, egg_command_group
|
@@ -18,10 +19,9 @@ from .base import Context, controlled_command, egg_command_group
|
|
18
19
|
async def edit_command(
|
19
20
|
ctx: Context,
|
20
21
|
user: discord.Member,
|
21
|
-
oeufs: int,
|
22
|
+
oeufs: app_commands.Range[int, 0, 10_000],
|
22
23
|
) -> None:
|
23
24
|
"""Edit command."""
|
24
|
-
oeufs = min(max(oeufs, 0), 100_000)
|
25
25
|
await ctx.response.defer(ephemeral=True)
|
26
26
|
async with AsyncSession(ctx.client.engine) as session:
|
27
27
|
eggs: list[Egg] = list(
|
@@ -52,7 +52,7 @@ async def edit_command(
|
|
52
52
|
guild_id=ctx.guild_id,
|
53
53
|
channel_id=ctx.channel_id,
|
54
54
|
user_id=user.id,
|
55
|
-
emoji_id=ctx.client.
|
55
|
+
emoji_id=ctx.client.egg_emotes.rand().id,
|
56
56
|
)
|
57
57
|
)
|
58
58
|
await session.commit()
|
@@ -0,0 +1,187 @@
|
|
1
|
+
"""Module for disable hunt."""
|
2
|
+
|
3
|
+
import asyncio
|
4
|
+
from typing import Callable, Optional
|
5
|
+
|
6
|
+
import discord
|
7
|
+
from discord import app_commands
|
8
|
+
from sqlalchemy import and_, func, not_, select
|
9
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
10
|
+
|
11
|
+
from easterobot.config import RAND
|
12
|
+
from easterobot.games.connect import Connect4
|
13
|
+
from easterobot.games.game import Game
|
14
|
+
from easterobot.games.rock_paper_scissor import RockPaperScissor
|
15
|
+
from easterobot.games.tic_tac_toe import TicTacToe
|
16
|
+
from easterobot.hunts.rank import Ranking
|
17
|
+
from easterobot.models import Egg
|
18
|
+
|
19
|
+
from .base import Context, controlled_command, egg_command_group
|
20
|
+
|
21
|
+
lock = asyncio.Lock()
|
22
|
+
|
23
|
+
|
24
|
+
async def get_unlocked_eggs(
|
25
|
+
session: AsyncSession, member: discord.Member, counter: int
|
26
|
+
) -> list[Egg]:
|
27
|
+
"""Get the count of unlocked eggs."""
|
28
|
+
return list(
|
29
|
+
(
|
30
|
+
await session.scalars(
|
31
|
+
select(Egg)
|
32
|
+
.where(
|
33
|
+
and_(
|
34
|
+
Egg.guild_id == member.guild.id,
|
35
|
+
Egg.user_id == member.id,
|
36
|
+
not_(Egg.lock),
|
37
|
+
)
|
38
|
+
)
|
39
|
+
.order_by(func.random()) # Randomize
|
40
|
+
.limit(counter)
|
41
|
+
)
|
42
|
+
).all()
|
43
|
+
)
|
44
|
+
|
45
|
+
|
46
|
+
async def game_dual( # noqa: C901, D103, PLR0912
|
47
|
+
ctx: Context,
|
48
|
+
member: Optional[discord.Member],
|
49
|
+
bet: int,
|
50
|
+
cls: Callable[[discord.Member, discord.Member, discord.Message], Game],
|
51
|
+
) -> None:
|
52
|
+
# If no member choose a random play in the guild with enough egg
|
53
|
+
if member is None:
|
54
|
+
if bet == 0:
|
55
|
+
members = [m for m in ctx.guild.members if m.id != ctx.user.id]
|
56
|
+
else:
|
57
|
+
# TODO(dashstrom): can chose member with locked eggs
|
58
|
+
async with AsyncSession(ctx.client.engine) as session:
|
59
|
+
ranking = await Ranking.from_guild(session, ctx.guild_id)
|
60
|
+
hunters = ranking.over(bet)
|
61
|
+
mapper_member = {m.id: m for m in ctx.guild.members}
|
62
|
+
members = [
|
63
|
+
mapper_member[h.member_id]
|
64
|
+
for h in hunters
|
65
|
+
if h.member_id != ctx.user.id and h.member_id in mapper_member
|
66
|
+
]
|
67
|
+
if members:
|
68
|
+
member = RAND.choice(members)
|
69
|
+
else:
|
70
|
+
await ctx.response.send_message(
|
71
|
+
"Aucun utilisateur trouvé !",
|
72
|
+
ephemeral=True,
|
73
|
+
)
|
74
|
+
return
|
75
|
+
|
76
|
+
# Validate user
|
77
|
+
if (member.bot or member == ctx.user) and not ctx.client.is_super_admin(
|
78
|
+
member
|
79
|
+
):
|
80
|
+
await ctx.response.send_message(
|
81
|
+
"L'utilisateur n'est pas valide !",
|
82
|
+
ephemeral=True,
|
83
|
+
)
|
84
|
+
return
|
85
|
+
|
86
|
+
# Check if user has enough eggs for ask
|
87
|
+
async with AsyncSession(ctx.client.engine) as session:
|
88
|
+
e1, e2 = await asyncio.gather(
|
89
|
+
get_unlocked_eggs(session, ctx.user, bet),
|
90
|
+
get_unlocked_eggs(session, member, bet),
|
91
|
+
)
|
92
|
+
if len(e1) < bet:
|
93
|
+
await ctx.response.send_message(
|
94
|
+
"Vous n'avez pas assez d'oeufs",
|
95
|
+
ephemeral=True,
|
96
|
+
)
|
97
|
+
return
|
98
|
+
if len(e2) < bet:
|
99
|
+
await ctx.response.send_message(
|
100
|
+
f"{member.mention} n'a pas assez d'oeufs",
|
101
|
+
ephemeral=True,
|
102
|
+
)
|
103
|
+
return
|
104
|
+
|
105
|
+
msg = await ctx.client.game.ask_dual(ctx, member, bet=bet)
|
106
|
+
if msg:
|
107
|
+
# Check if user still have enough eggs and lock them
|
108
|
+
async with AsyncSession(ctx.client.engine) as session:
|
109
|
+
async with lock:
|
110
|
+
e1, e2 = await asyncio.gather(
|
111
|
+
get_unlocked_eggs(session, ctx.user, bet),
|
112
|
+
get_unlocked_eggs(session, member, bet),
|
113
|
+
)
|
114
|
+
if len(e1) < bet:
|
115
|
+
await ctx.response.send_message(
|
116
|
+
"Vous n'avez plus assez d'oeufs",
|
117
|
+
ephemeral=True,
|
118
|
+
)
|
119
|
+
return
|
120
|
+
for e in e1:
|
121
|
+
e.lock = True
|
122
|
+
if len(e2) < bet:
|
123
|
+
await ctx.response.send_message(
|
124
|
+
f"{member.mention} n'a plus assez d'oeufs",
|
125
|
+
ephemeral=True,
|
126
|
+
)
|
127
|
+
return
|
128
|
+
for e in e2:
|
129
|
+
e.lock = True
|
130
|
+
await session.commit()
|
131
|
+
|
132
|
+
# Play the game
|
133
|
+
game = cls(ctx.user, member, msg)
|
134
|
+
await ctx.client.game.run(game)
|
135
|
+
winner = await game.wait_winner()
|
136
|
+
|
137
|
+
# Give eggs to the winner or remove previous one
|
138
|
+
async with lock:
|
139
|
+
for e in e1:
|
140
|
+
e.lock = False
|
141
|
+
if winner:
|
142
|
+
e.user_id = winner.id
|
143
|
+
for e in e2:
|
144
|
+
e.lock = False
|
145
|
+
if winner:
|
146
|
+
e.user_id = winner.id
|
147
|
+
await session.commit()
|
148
|
+
|
149
|
+
|
150
|
+
@egg_command_group.command(
|
151
|
+
name="connect4", description="Lancer une partie de puissance 4."
|
152
|
+
)
|
153
|
+
@controlled_command(cooldown=True)
|
154
|
+
async def connect4_command(
|
155
|
+
ctx: Context,
|
156
|
+
member: Optional[discord.Member] = None,
|
157
|
+
bet: app_commands.Range[int, 0] = 0,
|
158
|
+
) -> None:
|
159
|
+
"""Run a Connect4."""
|
160
|
+
await game_dual(ctx, member, bet, Connect4)
|
161
|
+
|
162
|
+
|
163
|
+
@egg_command_group.command(
|
164
|
+
name="tictactoe", description="Lancer une partie de morpion."
|
165
|
+
)
|
166
|
+
@controlled_command(cooldown=True)
|
167
|
+
async def tictactoe_command(
|
168
|
+
ctx: Context,
|
169
|
+
member: discord.Member,
|
170
|
+
bet: app_commands.Range[int, 0] = 0,
|
171
|
+
) -> None:
|
172
|
+
"""Run a tictactoe."""
|
173
|
+
await game_dual(ctx, member, bet, TicTacToe)
|
174
|
+
|
175
|
+
|
176
|
+
@egg_command_group.command(
|
177
|
+
name="rockpaperscissor",
|
178
|
+
description="Lancer une partie de pierre papier ciseaux.",
|
179
|
+
)
|
180
|
+
@controlled_command(cooldown=True)
|
181
|
+
async def rockpaperscissor_command(
|
182
|
+
ctx: Context,
|
183
|
+
member: discord.Member,
|
184
|
+
bet: app_commands.Range[int, 0] = 0,
|
185
|
+
) -> None:
|
186
|
+
"""Run a rockpaperscissor."""
|
187
|
+
await game_dual(ctx, member, bet, RockPaperScissor)
|
easterobot/commands/help.py
CHANGED
easterobot/commands/reset.py
CHANGED
@@ -7,7 +7,7 @@ import discord
|
|
7
7
|
from sqlalchemy import and_, delete
|
8
8
|
from sqlalchemy.ext.asyncio import AsyncSession
|
9
9
|
|
10
|
-
from easterobot.
|
10
|
+
from easterobot.hunts.hunt import embed
|
11
11
|
from easterobot.models import Cooldown, Egg, Hunt
|
12
12
|
|
13
13
|
from .base import Context, Interaction, controlled_command, egg_command_group
|
easterobot/commands/search.py
CHANGED
@@ -7,8 +7,8 @@ import discord
|
|
7
7
|
from sqlalchemy import and_, func, select
|
8
8
|
from sqlalchemy.ext.asyncio import AsyncSession
|
9
9
|
|
10
|
-
from easterobot.bot import embed
|
11
10
|
from easterobot.config import RAND, agree
|
11
|
+
from easterobot.hunts.hunt import embed
|
12
12
|
from easterobot.models import Egg, Hunt
|
13
13
|
|
14
14
|
from .base import (
|
@@ -81,7 +81,7 @@ async def search_command(ctx: Context) -> None:
|
|
81
81
|
) -> discord.Message:
|
82
82
|
return await ctx.followup.send(*args, **kwargs) # type: ignore[no-any-return]
|
83
83
|
|
84
|
-
await ctx.client.start_hunt(
|
84
|
+
await ctx.client.hunt.start_hunt(
|
85
85
|
ctx.channel_id,
|
86
86
|
ctx.client.config.spotted(ctx.user),
|
87
87
|
member_id=ctx.user.id,
|
@@ -89,7 +89,7 @@ async def search_command(ctx: Context) -> None:
|
|
89
89
|
)
|
90
90
|
else:
|
91
91
|
logger.info("found: %.2f > %.2f", prob_s, sample_s)
|
92
|
-
emoji = ctx.client.
|
92
|
+
emoji = ctx.client.egg_emotes.rand()
|
93
93
|
async with AsyncSession(ctx.client.engine) as session:
|
94
94
|
session.add(
|
95
95
|
Egg(
|
easterobot/commands/top.py
CHANGED
@@ -8,8 +8,8 @@ import discord
|
|
8
8
|
from sqlalchemy import distinct, func, select
|
9
9
|
from sqlalchemy.ext.asyncio import AsyncSession
|
10
10
|
|
11
|
-
from easterobot.
|
12
|
-
from easterobot.
|
11
|
+
from easterobot.hunts.hunt import embed
|
12
|
+
from easterobot.hunts.rank import Ranking
|
13
13
|
from easterobot.models import Egg
|
14
14
|
|
15
15
|
from .base import Context, Interaction, controlled_command, egg_command_group
|
@@ -18,15 +18,6 @@ PAGE_SIZE = 10
|
|
18
18
|
logger = logging.getLogger(__name__)
|
19
19
|
|
20
20
|
|
21
|
-
def record_top(rank: str, user_id: int, count: int) -> str:
|
22
|
-
"""Format the current user."""
|
23
|
-
return (
|
24
|
-
f"{rank} <@{user_id}>\n"
|
25
|
-
f"\u2004\u2004\u2004\u2004\u2004"
|
26
|
-
f"➥ {agree('{0} œuf', '{0} œufs', count)}"
|
27
|
-
)
|
28
|
-
|
29
|
-
|
30
21
|
async def embed_rank(
|
31
22
|
ctx: Context,
|
32
23
|
page: int,
|
@@ -34,13 +25,11 @@ async def embed_rank(
|
|
34
25
|
) -> tuple[discord.Embed, bool]:
|
35
26
|
"""Embed for rank."""
|
36
27
|
async with AsyncSession(ctx.client.engine) as session:
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
for user_id, rank, egg_count in egg_counts[:10]:
|
43
|
-
morsels.append(record_top(rank, user_id, egg_count))
|
28
|
+
ranking = await Ranking.from_guild(session, ctx.guild_id)
|
29
|
+
hunters = ranking.page(page, limit=PAGE_SIZE)
|
30
|
+
morsels: list[str] = []
|
31
|
+
if hunters:
|
32
|
+
morsels.extend(hunter.record for hunter in hunters)
|
44
33
|
else:
|
45
34
|
morsels.append("\n:spider_web: Personne n'a d'œuf")
|
46
35
|
total = await session.scalar(
|