From ba86d4a6f2cf6649dc79c3339118cb6e5b4b2443 Mon Sep 17 00:00:00 2001 From: stijndcl Date: Sun, 3 Jul 2022 17:19:24 +0200 Subject: [PATCH] Make announcements async, work on bank upgrades --- .../632b69cdadde_add_missing_defaults.py | 28 +++++++++++++++ ...9_move_dinks_over_to_bank_add_invested_.py | 32 +++++++++++++++++ database/models.py | 13 +++---- didier/cogs/currency.py | 36 +++++++++++++++++-- didier/cogs/tasks.py | 4 +-- didier/data/embeds/ufora/announcements.py | 14 +++++--- didier/didier.py | 5 +++ didier/utils/math/__init__.py | 0 didier/utils/math/currency.py | 25 +++++++++++++ 9 files changed, 143 insertions(+), 14 deletions(-) create mode 100644 alembic/versions/632b69cdadde_add_missing_defaults.py create mode 100644 alembic/versions/8c4ad0a1d699_move_dinks_over_to_bank_add_invested_.py create mode 100644 didier/utils/math/__init__.py create mode 100644 didier/utils/math/currency.py diff --git a/alembic/versions/632b69cdadde_add_missing_defaults.py b/alembic/versions/632b69cdadde_add_missing_defaults.py new file mode 100644 index 0000000..0f326a7 --- /dev/null +++ b/alembic/versions/632b69cdadde_add_missing_defaults.py @@ -0,0 +1,28 @@ +"""Add missing defaults + +Revision ID: 632b69cdadde +Revises: 8c4ad0a1d699 +Create Date: 2022-07-03 16:29:07.387011 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '632b69cdadde' +down_revision = '8c4ad0a1d699' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/alembic/versions/8c4ad0a1d699_move_dinks_over_to_bank_add_invested_.py b/alembic/versions/8c4ad0a1d699_move_dinks_over_to_bank_add_invested_.py new file mode 100644 index 0000000..ad56f5e --- /dev/null +++ b/alembic/versions/8c4ad0a1d699_move_dinks_over_to_bank_add_invested_.py @@ -0,0 +1,32 @@ +"""Move dinks over to Bank & add invested amount + +Revision ID: 8c4ad0a1d699 +Revises: 0d03c226d881 +Create Date: 2022-07-03 16:27:11.330746 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '8c4ad0a1d699' +down_revision = '0d03c226d881' +branch_labels = None +depends_on = None + + +def upgrade() -> 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.BigInteger(), server_default='0', nullable=False)) + + # ### 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.drop_column('invested') + + # ### end Alembic commands ### diff --git a/database/models.py b/database/models.py index 0bc6370..ef2e439 100644 --- a/database/models.py +++ b/database/models.py @@ -17,16 +17,17 @@ class Bank(Base): bank_id: int = Column(Integer, primary_key=True) user_id: int = Column(BigInteger, ForeignKey("users.user_id")) - dinks: int = Column(BigInteger, default=0, nullable=False) + dinks: int = Column(BigInteger, server_default="0", nullable=False) + invested: int = Column(BigInteger, server_default="0", nullable=False) # Interest rate - interest_level: int = Column(Integer, default=1, nullable=False) + interest_level: int = Column(Integer, server_default="1", nullable=False) # Maximum amount that can be stored in the bank - capacity_level: int = Column(Integer, default=1, nullable=False) + capacity_level: int = Column(Integer, server_default="1", nullable=False) # Maximum amount that can be robbed - rob_level: int = Column(Integer, default=1, nullable=False) + rob_level: int = Column(Integer, server_default="1", nullable=False) user: User = relationship("User", uselist=False, back_populates="bank", lazy="selectin") @@ -67,7 +68,7 @@ class NightlyData(Base): nightly_id: int = Column(Integer, primary_key=True) user_id: int = Column(BigInteger, ForeignKey("users.user_id")) last_nightly: Optional[datetime] = Column(DateTime(timezone=True), nullable=True) - count: int = Column(Integer, default=0, nullable=False) + count: int = Column(Integer, server_default="0", nullable=False) user: User = relationship("User", back_populates="nightly_data", uselist=False, lazy="selectin") @@ -81,7 +82,7 @@ class UforaCourse(Base): name: str = Column(Text, nullable=False, unique=True) code: str = Column(Text, nullable=False, unique=True) year: int = Column(Integer, nullable=False) - log_announcements: bool = Column(Boolean, default=False, nullable=False) + log_announcements: bool = Column(Boolean, server_default="0", nullable=False) announcements: list[UforaAnnouncement] = relationship( "UforaAnnouncement", back_populates="course", cascade="all, delete-orphan", lazy="selectin" diff --git a/didier/cogs/currency.py b/didier/cogs/currency.py index 606a910..8cabb00 100644 --- a/didier/cogs/currency.py +++ b/didier/cogs/currency.py @@ -8,6 +8,7 @@ from database.exceptions.currency import DoubleNightly from didier import Didier from didier.utils.discord.checks import is_owner from didier.utils.discord.converters import abbreviated_number +from didier.utils.math.currency import capacity_upgrade_price, interest_upgrade_price, rob_upgrade_price from didier.utils.types.string import pluralize @@ -34,11 +35,42 @@ class Currency(commands.Cog): mention_author=False, ) - @commands.hybrid_group(name="bank", case_insensitive=True, invoke_without_command=True) + @commands.group(name="bank", aliases=["B"], case_insensitive=True, invoke_without_command=True) async def bank(self, ctx: commands.Context): """Show your Didier Bank information""" async with self.client.db_session as session: - await crud.get_bank(session, ctx.author.id) + bank = await crud.get_bank(session, ctx.author.id) + + embed = discord.Embed(colour=discord.Colour.blue()) + embed.set_author(name=f"Bank van {ctx.author.display_name}") + embed.set_thumbnail(url=ctx.author.avatar.url) + + embed.add_field(name="Interest level", value=bank.interest_level) + embed.add_field(name="Capaciteit level", value=bank.capacity_level) + embed.add_field(name="Momenteel geïnvesteerd", value=bank.invested, inline=False) + + await ctx.reply(embed=embed, mention_author=False) + + @bank.group(name="Upgrade", aliases=["U", "Upgrades"], case_insensitive=True, invoke_without_command=True) + async def bank_upgrades(self, ctx: commands.Context): + """List the upgrades you can buy & their prices""" + async with self.client.db_session as session: + bank = await crud.get_bank(session, ctx.author.id) + + embed = discord.Embed(colour=discord.Colour.blue()) + embed.set_author(name="Bank upgrades") + + embed.add_field( + name=f"Interest ({bank.interest_level})", value=str(interest_upgrade_price(bank.interest_level)) + ) + embed.add_field( + name=f"Capaciteit ({bank.capacity_level})", value=str(capacity_upgrade_price(bank.capacity_level)) + ) + embed.add_field(name=f"Rob ({bank.rob_level})", value=str(rob_upgrade_price(bank.rob_level))) + + embed.set_footer(text="Didier Bank Upgrade [Categorie]") + + await ctx.reply(embed=embed, mention_author=False) @commands.hybrid_command(name="dinks") async def dinks(self, ctx: commands.Context): diff --git a/didier/cogs/tasks.py b/didier/cogs/tasks.py index c37b8b8..285945c 100644 --- a/didier/cogs/tasks.py +++ b/didier/cogs/tasks.py @@ -29,9 +29,9 @@ class Tasks(commands.Cog): if settings.UFORA_RSS_TOKEN is None or settings.UFORA_ANNOUNCEMENTS_CHANNEL is None: return - async with self.client.db_session as session: + async with self.client.db_session as db_session: announcements_channel = self.client.get_channel(settings.UFORA_ANNOUNCEMENTS_CHANNEL) - announcements = await fetch_ufora_announcements(session) + announcements = await fetch_ufora_announcements(self.client.http_session, db_session) for announcement in announcements: await announcements_channel.send(embed=announcement.to_embed()) diff --git a/didier/data/embeds/ufora/announcements.py b/didier/data/embeds/ufora/announcements.py index 52d5849..138837e 100644 --- a/didier/data/embeds/ufora/announcements.py +++ b/didier/data/embeds/ufora/announcements.py @@ -3,9 +3,11 @@ from dataclasses import dataclass, field from datetime import datetime from typing import Optional +import async_timeout import discord import feedparser import pytz +from aiohttp import ClientSession from markdownify import markdownify as md from sqlalchemy.ext.asyncio import AsyncSession @@ -116,7 +118,9 @@ def parse_ids(url: str) -> Optional[tuple[int, int]]: return int(spl[0]), int(spl[1]) -async def fetch_ufora_announcements(session: AsyncSession) -> list[UforaNotification]: +async def fetch_ufora_announcements( + http_session: ClientSession, database_session: AsyncSession +) -> list[UforaNotification]: """Fetch all new announcements""" notifications: list[UforaNotification] = [] @@ -124,7 +128,7 @@ async def fetch_ufora_announcements(session: AsyncSession) -> list[UforaNotifica if settings.UFORA_RSS_TOKEN is None: return notifications - courses = await crud.get_courses_with_announcements(session) + courses = await crud.get_courses_with_announcements(database_session) for course in courses: course_announcement_ids = list(map(lambda announcement: announcement.announcement_id, course.announcements)) @@ -134,7 +138,9 @@ async def fetch_ufora_announcements(session: AsyncSession) -> list[UforaNotifica ) # Get the updated feed - feed = feedparser.parse(course_url) + with async_timeout.timeout(10): + async with http_session.get(course_url) as response: + feed = feedparser.parse(await response.text()) # Remove old notifications fresh_feed: list[dict] = [] @@ -161,6 +167,6 @@ async def fetch_ufora_announcements(session: AsyncSession) -> list[UforaNotifica notifications.append(notification) # Create new db entry - await crud.create_new_announcement(session, notification_id, course, notification.published_dt) + await crud.create_new_announcement(database_session, notification_id, course, notification.published_dt) return notifications diff --git a/didier/didier.py b/didier/didier.py index 1138b02..ff26e7a 100644 --- a/didier/didier.py +++ b/didier/didier.py @@ -1,6 +1,7 @@ import os import discord +from aiohttp import ClientSession from discord.ext import commands from sqlalchemy.ext.asyncio import AsyncSession @@ -14,6 +15,7 @@ class Didier(commands.Bot): """DIDIER <3""" initial_extensions: tuple[str, ...] = () + http_session: ClientSession def __init__(self): activity = discord.Activity(type=discord.ActivityType.playing, name=settings.DISCORD_STATUS_MESSAGE) @@ -43,6 +45,9 @@ class Didier(commands.Bot): await self._load_initial_extensions() await self._load_directory_extensions("didier/cogs") + # Create aiohttp session + self.http_session = ClientSession() + async def _load_initial_extensions(self): """Load all extensions that should be loaded before the others""" for extension in self.initial_extensions: diff --git a/didier/utils/math/__init__.py b/didier/utils/math/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/didier/utils/math/currency.py b/didier/utils/math/currency.py new file mode 100644 index 0000000..c6d36d0 --- /dev/null +++ b/didier/utils/math/currency.py @@ -0,0 +1,25 @@ +import math + + +def interest_upgrade_price(level: int) -> int: + """Calculate the price to upgrade your interest level""" + base_cost = 600 + growth_rate = 1.8 + + return math.floor(base_cost * (growth_rate**level)) + + +def capacity_upgrade_price(level: int) -> int: + """Calculate the price to upgrade your capacity level""" + base_cost = 800 + growth_rate = 1.6 + + return math.floor(base_cost * (growth_rate**level)) + + +def rob_upgrade_price(level: int) -> int: + """Calculate the price to upgrade your rob level""" + base_cost = 950 + growth_rate = 1.9 + + return math.floor(base_cost * (growth_rate**level))