mirror of https://github.com/stijndcl/didier
Add stats for cf
parent
de7b5cd960
commit
a051423203
|
@ -0,0 +1,76 @@
|
||||||
|
"""Currency updates
|
||||||
|
|
||||||
|
Revision ID: 8f7e8384cec3
|
||||||
|
Revises: 09128b6e34dd
|
||||||
|
Create Date: 2024-03-03 17:48:36.205106
|
||||||
|
|
||||||
|
"""
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = "8f7e8384cec3"
|
||||||
|
down_revision = "09128b6e34dd"
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table(
|
||||||
|
"cf_stats",
|
||||||
|
sa.Column("cf_stats_id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("games_won", sa.BigInteger(), server_default="0", nullable=False),
|
||||||
|
sa.Column("games_lost", sa.BigInteger(), server_default="0", nullable=False),
|
||||||
|
sa.Column("dinks_won", sa.BigInteger(), server_default="0", nullable=False),
|
||||||
|
sa.Column("dinks_lost", sa.BigInteger(), server_default="0", nullable=False),
|
||||||
|
sa.Column("user_id", sa.BigInteger(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["user_id"],
|
||||||
|
["users.user_id"],
|
||||||
|
),
|
||||||
|
sa.PrimaryKeyConstraint("cf_stats_id"),
|
||||||
|
)
|
||||||
|
op.create_table(
|
||||||
|
"jail",
|
||||||
|
sa.Column("jail_entry_i", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("user_id", sa.BigInteger(), nullable=False),
|
||||||
|
sa.Column("until", sa.DateTime(timezone=True), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["user_id"],
|
||||||
|
["users.user_id"],
|
||||||
|
),
|
||||||
|
sa.PrimaryKeyConstraint("jail_entry_i"),
|
||||||
|
)
|
||||||
|
op.create_table(
|
||||||
|
"savings",
|
||||||
|
sa.Column("savings_id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("user_id", sa.BigInteger(), nullable=False),
|
||||||
|
sa.Column("saved", sa.BigInteger(), server_default="0", nullable=False),
|
||||||
|
sa.Column("daily_minimum", sa.BigInteger(), server_default="0", nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["user_id"],
|
||||||
|
["users.user_id"],
|
||||||
|
),
|
||||||
|
sa.PrimaryKeyConstraint("savings_id"),
|
||||||
|
)
|
||||||
|
with op.batch_alter_table("bank", schema=None) as batch_op:
|
||||||
|
batch_op.drop_column("invested")
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table("bank", schema=None) as batch_op:
|
||||||
|
batch_op.add_column(
|
||||||
|
sa.Column(
|
||||||
|
"invested", sa.BIGINT(), server_default=sa.text("'0'::bigint"), autoincrement=False, nullable=False
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
op.drop_table("savings")
|
||||||
|
op.drop_table("jail")
|
||||||
|
op.drop_table("cf_stats")
|
||||||
|
# ### end Alembic commands ###
|
|
@ -0,0 +1,34 @@
|
||||||
|
from sqlalchemy import select
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from database.schemas import CFStats
|
||||||
|
|
||||||
|
__all__ = ["get_cf_stats", "update_cf_stats"]
|
||||||
|
|
||||||
|
|
||||||
|
async def get_cf_stats(session: AsyncSession, user_id: int) -> CFStats:
|
||||||
|
"""Get a user's coinflip stats"""
|
||||||
|
statement = select(CFStats).where(CFStats.user_id == user_id)
|
||||||
|
result = (await session.execute(statement)).scalar_one_or_none()
|
||||||
|
|
||||||
|
if result is None:
|
||||||
|
result = CFStats(user_id=user_id)
|
||||||
|
session.add(result)
|
||||||
|
await session.commit()
|
||||||
|
await session.refresh(result)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
async def update_cf_stats(session: AsyncSession, user_id: int, outcome: int):
|
||||||
|
"""Update a user's coinflip stats"""
|
||||||
|
stats = await get_cf_stats(session, user_id)
|
||||||
|
if outcome < 0:
|
||||||
|
stats.games_lost += 1
|
||||||
|
stats.dinks_lost += abs(outcome)
|
||||||
|
else:
|
||||||
|
stats.games_won += 1
|
||||||
|
stats.dinks_won += outcome
|
||||||
|
|
||||||
|
session.add(stats)
|
||||||
|
await session.commit()
|
|
@ -15,6 +15,7 @@ __all__ = [
|
||||||
"BankSavings",
|
"BankSavings",
|
||||||
"Birthday",
|
"Birthday",
|
||||||
"Bookmark",
|
"Bookmark",
|
||||||
|
"CFStats",
|
||||||
"CommandStats",
|
"CommandStats",
|
||||||
"CustomCommand",
|
"CustomCommand",
|
||||||
"CustomCommandAlias",
|
"CustomCommandAlias",
|
||||||
|
@ -106,6 +107,21 @@ class Bookmark(Base):
|
||||||
user: Mapped[User] = relationship(back_populates="bookmarks", uselist=False, lazy="selectin")
|
user: Mapped[User] = relationship(back_populates="bookmarks", uselist=False, lazy="selectin")
|
||||||
|
|
||||||
|
|
||||||
|
class CFStats(Base):
|
||||||
|
"""A user's coinflipping stats"""
|
||||||
|
|
||||||
|
__tablename__ = "cf_stats"
|
||||||
|
|
||||||
|
cf_stats_id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
games_won: Mapped[int] = mapped_column(BigInteger, server_default="0", nullable=False)
|
||||||
|
games_lost: Mapped[int] = mapped_column(BigInteger, server_default="0", nullable=False)
|
||||||
|
dinks_won: Mapped[int] = mapped_column(BigInteger, server_default="0", nullable=False)
|
||||||
|
dinks_lost: Mapped[int] = mapped_column(BigInteger, server_default="0", nullable=False)
|
||||||
|
user_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("users.user_id"))
|
||||||
|
|
||||||
|
user: Mapped[User] = relationship(back_populates="cf_stats", uselist=False, lazy="selectin")
|
||||||
|
|
||||||
|
|
||||||
class CommandStats(Base):
|
class CommandStats(Base):
|
||||||
"""Metrics on how often commands are used"""
|
"""Metrics on how often commands are used"""
|
||||||
|
|
||||||
|
@ -349,6 +365,9 @@ class User(Base):
|
||||||
bookmarks: Mapped[List[Bookmark]] = relationship(
|
bookmarks: Mapped[List[Bookmark]] = relationship(
|
||||||
back_populates="user", uselist=True, lazy="selectin", cascade="all, delete-orphan"
|
back_populates="user", uselist=True, lazy="selectin", cascade="all, delete-orphan"
|
||||||
)
|
)
|
||||||
|
cf_stats: Mapped[CFStats] = relationship(
|
||||||
|
back_populates="user", uselist=True, lazy="selectin", cascade="all, delete-orphan"
|
||||||
|
)
|
||||||
command_stats: Mapped[List[CommandStats]] = relationship(
|
command_stats: Mapped[List[CommandStats]] = relationship(
|
||||||
back_populates="user", uselist=True, lazy="selectin", cascade="all, delete-orphan"
|
back_populates="user", uselist=True, lazy="selectin", cascade="all, delete-orphan"
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,6 +3,7 @@ from typing import Annotated, Optional, Union
|
||||||
|
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
|
||||||
|
from database.crud.cf_stats import update_cf_stats
|
||||||
from database.crud.currency import gamble_dinks
|
from database.crud.currency import gamble_dinks
|
||||||
from didier import Didier
|
from didier import Didier
|
||||||
from didier.utils.discord.converters import abbreviated_number
|
from didier.utils.discord.converters import abbreviated_number
|
||||||
|
@ -17,6 +18,7 @@ class Gambling(commands.Cog):
|
||||||
def __init__(self, client: Didier):
|
def __init__(self, client: Didier):
|
||||||
self.client = client
|
self.client = client
|
||||||
|
|
||||||
|
@commands.max_concurrency(1, commands.BucketType.user, wait=True)
|
||||||
@commands.command(name="coinflip", aliases=["cf", "flip"]) # type: ignore[arg-type]
|
@commands.command(name="coinflip", aliases=["cf", "flip"]) # type: ignore[arg-type]
|
||||||
async def coinflip(
|
async def coinflip(
|
||||||
self,
|
self,
|
||||||
|
@ -60,6 +62,9 @@ class Gambling(commands.Cog):
|
||||||
if received == 0:
|
if received == 0:
|
||||||
return await ctx.reply("You don't have any Didier Dinks to wager.", mention_author=False)
|
return await ctx.reply("You don't have any Didier Dinks to wager.", mention_author=False)
|
||||||
|
|
||||||
|
sign = 1 if won else -1
|
||||||
|
await update_cf_stats(session, ctx.author.id, received * sign)
|
||||||
|
|
||||||
plural = pluralize("Didier Dink", received)
|
plural = pluralize("Didier Dink", received)
|
||||||
|
|
||||||
if won:
|
if won:
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
from discord.ext import commands
|
||||||
|
|
||||||
|
from didier import Didier
|
||||||
|
|
||||||
|
|
||||||
|
class Leaderboards(commands.Cog):
|
||||||
|
"""Cog for various leaderboards"""
|
||||||
|
|
||||||
|
client: Didier
|
||||||
|
|
||||||
|
def __init__(self, client: Didier):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
@commands.hybrid_group(name="leaderboard", aliases=["lb"], invoke_without_command=True)
|
||||||
|
async def leaderboard(self, ctx: commands.Context):
|
||||||
|
"""List the top X for a given category"""
|
||||||
|
# TODO
|
||||||
|
|
||||||
|
@leaderboard.command(name="dinks", aliases=["d"])
|
||||||
|
async def dinks(self, ctx: commands.Context):
|
||||||
|
"""See the users with the most Didier Dinks"""
|
||||||
|
# TODO
|
||||||
|
|
||||||
|
|
||||||
|
async def setup(client: Didier):
|
||||||
|
"""Load the cog"""
|
||||||
|
await client.add_cog(Leaderboards(client))
|
|
@ -0,0 +1,54 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import discord
|
||||||
|
from discord.ext import commands
|
||||||
|
|
||||||
|
from database.crud.cf_stats import get_cf_stats
|
||||||
|
from didier import Didier
|
||||||
|
|
||||||
|
|
||||||
|
class Stats(commands.Cog):
|
||||||
|
"""Cog for various stats that Didier tracks"""
|
||||||
|
|
||||||
|
client: Didier
|
||||||
|
|
||||||
|
def __init__(self, client: Didier):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
@commands.hybrid_group(name="stats", invoke_without_command=True)
|
||||||
|
async def stats(self, ctx: commands.Context):
|
||||||
|
"""See stats about yourself or another user"""
|
||||||
|
|
||||||
|
@stats.command(name="cf", aliases=["coinflip"])
|
||||||
|
async def _cf_stats(self, ctx: commands.Context, user: Optional[discord.User] = None):
|
||||||
|
"""See a user's `Didier CF` stats"""
|
||||||
|
async with ctx.typing(), self.client.postgres_session as session:
|
||||||
|
user = user or ctx.author
|
||||||
|
cf_stats = await get_cf_stats(session, user.id)
|
||||||
|
|
||||||
|
embed = discord.Embed(title="Didier CF Stats", colour=discord.Colour.blue())
|
||||||
|
embed.set_author(name=user.display_name)
|
||||||
|
|
||||||
|
if user.avatar is not None:
|
||||||
|
embed.set_thumbnail(url=user.avatar.url)
|
||||||
|
|
||||||
|
played = cf_stats.games_won + cf_stats.games_lost
|
||||||
|
|
||||||
|
if played == 0:
|
||||||
|
return await ctx.reply("This user hasn't played any games yet.", mention_author=False)
|
||||||
|
|
||||||
|
embed.add_field(name="Games played", value=played)
|
||||||
|
embed.add_field(
|
||||||
|
name="Winrate", value=f"{round(100 * cf_stats.games_won / played, 2)}% ({cf_stats.games_won}/{played})"
|
||||||
|
)
|
||||||
|
|
||||||
|
embed.add_field(name="Dinks won", value=cf_stats.dinks_won)
|
||||||
|
embed.add_field(name="Dinks lost", value=cf_stats.dinks_lost)
|
||||||
|
embed.add_field(name="Profit", value=cf_stats.dinks_won - cf_stats.dinks_lost)
|
||||||
|
|
||||||
|
await ctx.reply(embed=embed, mention_author=False)
|
||||||
|
|
||||||
|
|
||||||
|
async def setup(client: Didier):
|
||||||
|
"""Load the cog"""
|
||||||
|
await client.add_cog(Stats(client))
|
|
@ -0,0 +1,11 @@
|
||||||
|
from didier.menus.common import Menu
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
|
||||||
|
|
||||||
|
class Leaderboard(Menu):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DinksLeaderboard(Leaderboard):
|
||||||
|
pass
|
|
@ -12,10 +12,11 @@ from didier.utils.types.datetime import tz_aware_now
|
||||||
|
|
||||||
__all__ = ["Timer"]
|
__all__ = ["Timer"]
|
||||||
|
|
||||||
|
|
||||||
REMINDER_PREDELAY = timedelta(minutes=settings.REMINDER_PRE)
|
REMINDER_PREDELAY = timedelta(minutes=settings.REMINDER_PRE)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO make this generic
|
||||||
|
# TODO add timer for jail freeing
|
||||||
class Timer:
|
class Timer:
|
||||||
"""Class for scheduled timers"""
|
"""Class for scheduled timers"""
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue