Skip to content

Commit c52da6e

Browse files
committed
Adds play pins and apps.
1 parent ea74b9f commit c52da6e

File tree

22 files changed

+555
-45
lines changed

22 files changed

+555
-45
lines changed

poetry.lock

Lines changed: 16 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ python-dateutil = "^2.8.2"
166166
python-dotenv = "^1.0.1"
167167
pytz = ">=2024.1"
168168
pyyaml = "^6.0.1"
169+
redis = "^5.2.1"
169170
requests = "^2.31.0"
170171
sqlalchemy = "^2.0.29"
171172
sqlalchemy-utils = "^0.41.2"

src/spellbot/actions/admin_action.py

Lines changed: 38 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@
1010
from spellbot.models import Channel, ChannelDict, GuildAward, GuildAwardDict
1111
from spellbot.operations import (
1212
safe_fetch_text_channel,
13-
safe_get_partial_message,
1413
safe_send_channel,
15-
safe_update_embed,
1614
safe_update_embed_origin,
1715
)
1816
from spellbot.services import GamesService
@@ -475,40 +473,41 @@ async def move_user(
475473
ephemeral=True,
476474
)
477475

478-
async def set_points(self, game_id: int, player_xid: int, points: int) -> None:
479-
found = await self.services.games.select(game_id)
480-
if not found:
481-
await safe_send_channel(
482-
self.interaction,
483-
"There is no game with that ID.",
484-
ephemeral=True,
485-
)
486-
return
487-
488-
if not await self.services.games.players_included(player_xid):
489-
await safe_send_channel(
490-
self.interaction,
491-
f"User <@{player_xid}> did not play in game SB{found.get('id')}.",
492-
ephemeral=True,
493-
)
494-
return
495-
496-
await self.services.games.add_points(player_xid, points)
497-
await self.services.games.confirm_points(player_xid)
498-
499-
for post in found.get("posts", []):
500-
guild_xid = post["guild_xid"]
501-
channel_xid = post["channel_xid"]
502-
channel = await safe_fetch_text_channel(self.bot, guild_xid, channel_xid)
503-
if channel:
504-
message_xid = post["message_xid"]
505-
message = safe_get_partial_message(channel, guild_xid, message_xid)
506-
if message:
507-
embed = await self.services.games.to_embed(self.guild)
508-
await safe_update_embed(message, embed=embed)
509-
510-
await safe_send_channel(
511-
self.interaction,
512-
f"Points for <@{player_xid}> for game SB{game_id} set to {points}.",
513-
ephemeral=True,
514-
)
476+
# TODO: Refactor how confirmation/points/ELO works.
477+
# async def set_points(self, game_id: int, player_xid: int, points: int) -> None:
478+
# found = await self.services.games.select(game_id)
479+
# if not found:
480+
# await safe_send_channel(
481+
# self.interaction,
482+
# "There is no game with that ID.",
483+
# ephemeral=True,
484+
# )
485+
# return
486+
#
487+
# if not await self.services.games.players_included(player_xid):
488+
# await safe_send_channel(
489+
# self.interaction,
490+
# f"User <@{player_xid}> did not play in game SB{found.get('id')}.",
491+
# ephemeral=True,
492+
# )
493+
# return
494+
#
495+
# await self.services.games.add_points(player_xid, points)
496+
# await self.services.games.confirm_points(player_xid)
497+
#
498+
# for post in found.get("posts", []):
499+
# guild_xid = post["guild_xid"]
500+
# channel_xid = post["channel_xid"]
501+
# channel = await safe_fetch_text_channel(self.bot, guild_xid, channel_xid)
502+
# if channel:
503+
# message_xid = post["message_xid"]
504+
# message = safe_get_partial_message(channel, guild_xid, message_xid)
505+
# if message:
506+
# embed = await self.services.games.to_embed(self.guild)
507+
# await safe_update_embed(message, embed=embed)
508+
#
509+
# await safe_send_channel(
510+
# self.interaction,
511+
# f"Points for <@{player_xid}> for game SB{game_id} set to {points}.",
512+
# ephemeral=True,
513+
# )
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""
2+
Adds play pins.
3+
4+
Revision ID: 2bf551e10a79
5+
Revises: a59f2a831a84
6+
Create Date: 2025-01-27 19:34:51.499841
7+
"""
8+
9+
import sqlalchemy as sa
10+
from alembic import op
11+
12+
# revision identifiers, used by Alembic.
13+
revision = "2bf551e10a79"
14+
down_revision = "a59f2a831a84"
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade() -> None:
20+
op.add_column("plays", sa.Column("pin", sa.String(length=6), nullable=True))
21+
22+
23+
def downgrade() -> None:
24+
op.drop_column("plays", "pin")
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""
2+
Adds api tokens.
3+
4+
Revision ID: a59f2a831a84
5+
Revises: 6f5fb731f4c1
6+
Create Date: 2025-01-27 19:23:45.776047
7+
"""
8+
9+
import sqlalchemy as sa
10+
from alembic import op
11+
12+
# revision identifiers, used by Alembic.
13+
revision = "a59f2a831a84"
14+
down_revision = "6f5fb731f4c1"
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade() -> None:
20+
op.create_table(
21+
"token",
22+
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
23+
sa.Column(
24+
"created_at",
25+
sa.DateTime(),
26+
server_default=sa.text("(now() at time zone 'utc')"),
27+
nullable=False,
28+
),
29+
sa.Column(
30+
"updated_at",
31+
sa.DateTime(),
32+
server_default=sa.text("(now() at time zone 'utc')"),
33+
nullable=False,
34+
),
35+
sa.Column("deleted_at", sa.DateTime(), nullable=True),
36+
sa.Column("key", sa.String(), nullable=False),
37+
sa.PrimaryKeyConstraint("id"),
38+
)
39+
op.create_index(op.f("ix_token_key"), "token", ["key"], unique=False)
40+
41+
42+
def downgrade() -> None:
43+
op.drop_index(op.f("ix_token_key"), table_name="token")
44+
op.drop_table("token")

src/spellbot/models/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ def import_models() -> None: # pragma: no cover
2626
from .post import Post, PostDict # noqa: E402
2727
from .queue import Queue, QueueDict # noqa: E402
2828
from .record import Record, RecordDict # noqa: E402
29+
from .token import Token, TokenDict # noqa: E402
2930
from .user import User, UserDict # noqa: E402
3031
from .verify import Verify, VerifyDict # noqa: E402
3132
from .watch import Watch, WatchDict # noqa: E402
@@ -53,6 +54,8 @@ def import_models() -> None: # pragma: no cover
5354
"QueueDict",
5455
"Record",
5556
"RecordDict",
57+
"Token",
58+
"TokenDict",
5659
"User",
5760
"UserAward",
5861
"UserAwardDict",

src/spellbot/models/play.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
from __future__ import annotations
22

3+
import secrets
34
from datetime import datetime
45
from typing import TYPE_CHECKING, TypedDict, cast
56

6-
from sqlalchemy import BigInteger, Column, DateTime, ForeignKey, Integer
7+
from sqlalchemy import BigInteger, Column, DateTime, ForeignKey, Integer, String
78

89
from . import Base, now
910

1011
if TYPE_CHECKING:
1112
from . import Game, User # noqa: F401
1213

1314

15+
def generate_pin() -> str:
16+
return "".join(secrets.choice("0123456789") for i in range(6))
17+
18+
1419
class PlayDict(TypedDict):
1520
created_at: datetime
1621
updated_at: datetime
@@ -19,6 +24,7 @@ class PlayDict(TypedDict):
1924
og_guild_xid: int
2025
points: int | None
2126
confirmed_at: datetime
27+
pin: str
2228

2329

2430
class Play(Base):
@@ -75,6 +81,12 @@ class Play(Base):
7581
default=None,
7682
doc="UTC timestamp when this play was confirmed",
7783
)
84+
pin = Column(
85+
String(6),
86+
nullable=True,
87+
default=generate_pin,
88+
doc="A generated PIN for users to identify this game",
89+
)
7890

7991
def to_dict(self) -> PlayDict:
8092
return {
@@ -85,4 +97,5 @@ def to_dict(self) -> PlayDict:
8597
"og_guild_xid": self.og_guild_xid,
8698
"points": self.points,
8799
"confirmed_at": self.confirmed_at,
100+
"pin": self.pin,
88101
}

src/spellbot/models/token.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
from __future__ import annotations
2+
3+
from datetime import datetime
4+
from typing import TypedDict
5+
6+
from sqlalchemy import Column, DateTime, Integer, String
7+
8+
from . import Base, now
9+
10+
11+
class TokenDict(TypedDict):
12+
id: int
13+
created_at: datetime
14+
updated_at: datetime
15+
deleted_at: datetime | None
16+
key: str
17+
18+
19+
class Token(Base):
20+
"""Token keys for access to the SpellBot API."""
21+
22+
__tablename__ = "token"
23+
24+
id = Column(
25+
Integer,
26+
autoincrement=True,
27+
nullable=False,
28+
primary_key=True,
29+
doc="A pk for this token",
30+
)
31+
created_at = Column(
32+
DateTime,
33+
nullable=False,
34+
default=datetime.utcnow,
35+
server_default=now,
36+
doc="UTC timestamp when this key was first created",
37+
)
38+
updated_at = Column(
39+
DateTime,
40+
nullable=False,
41+
default=datetime.utcnow,
42+
server_default=now,
43+
onupdate=datetime.utcnow,
44+
doc="UTC timestamp when this key was last updated",
45+
)
46+
deleted_at = Column(
47+
DateTime,
48+
nullable=True,
49+
index=True,
50+
doc="UTC timestamp when this key was deleted",
51+
)
52+
key = Column(
53+
String,
54+
nullable=False,
55+
index=True,
56+
doc="The API token key",
57+
)
58+
59+
def to_dict(self) -> TokenDict:
60+
return {
61+
"id": self.id,
62+
"created_at": self.created_at,
63+
"updated_at": self.updated_at,
64+
"deleted_at": self.deleted_at,
65+
"key": self.key,
66+
}

src/spellbot/services/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
from .apps import AppsService
34
from .awards import AwardsService, NewAward
45
from .channels import ChannelsService
56
from .games import GamesService
@@ -20,9 +21,11 @@ def __init__(self) -> None:
2021
self.users = UsersService()
2122
self.verifies = VerifiesService()
2223
self.watches = WatchesService()
24+
self.apps = AppsService()
2325

2426

2527
__all__ = [
28+
"AppsService",
2629
"AwardsService",
2730
"ChannelsService",
2831
"GamesService",

src/spellbot/services/apps.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from __future__ import annotations
2+
3+
from asgiref.sync import sync_to_async
4+
from ddtrace import tracer
5+
6+
from spellbot.database import DatabaseSession
7+
from spellbot.models import Token
8+
9+
10+
class AppsService:
11+
@sync_to_async()
12+
@tracer.wrap()
13+
def verify_token(self, key: str) -> bool:
14+
return bool(DatabaseSession.query(Token).filter(Token.key == key).count())

0 commit comments

Comments
 (0)