diff --git a/files/classes/__init__.py b/files/classes/__init__.py index 5c62a23f5..3265b5b01 100644 --- a/files/classes/__init__.py +++ b/files/classes/__init__.py @@ -18,4 +18,5 @@ from .sub_block import * from .saves import * from .views import * from .notifications import * -from .follows import * \ No newline at end of file +from .follows import * +from .lottery import * \ No newline at end of file diff --git a/files/classes/lottery.py b/files/classes/lottery.py new file mode 100644 index 000000000..560b51f5a --- /dev/null +++ b/files/classes/lottery.py @@ -0,0 +1,31 @@ +import time +from sqlalchemy import * +from files.__main__ import Base +from files.helpers.lazy import lazy +from files.helpers.const import * + + +class Lottery(Base): + __tablename__ = "lotteries" + + id = Column(Integer, primary_key=True) + is_active = Column(Boolean, default=False) + ends_at = Column(Integer) + prize = Column(Integer, default=0) + tickets_sold = Column(Integer, default=0) + + @property + @lazy + def timeleft(self): + if not self.is_active: + return 0 + + epoch_time = int(time.time()) + remaining_time = self.ends_at - epoch_time + + return 0 if remaining_time < 0 else remaining_time + + @property + @lazy + def stats(self): + return {"active": self.is_active, "timeLeft": self.timeleft, "prize": self.prize, "ticketsSoldThisSession": self.tickets_sold,} diff --git a/files/classes/user.py b/files/classes/user.py index e8e2cbe6a..b3ed8ceb1 100644 --- a/files/classes/user.py +++ b/files/classes/user.py @@ -126,6 +126,9 @@ class User(Base): original_username = deferred(Column(String)) referred_by = Column(Integer, ForeignKey("users.id")) subs_created = Column(Integer, default=0) + currently_held_lottery_tickets = Column(Integer, default=0) + total_held_lottery_tickets = Column(Integer, default=0) + total_lottery_winnings = Column(Integer, default=0) badges = relationship("Badge", viewonly=True) subscriptions = relationship("Subscription", viewonly=True) @@ -655,3 +658,8 @@ class User(Base): l = [i.strip() for i in self.custom_filter_list.split('\n')] if self.custom_filter_list else [] l = [i for i in l if i] return l + + @property + @lazy + def lottery_stats(self): + return { "winnings": self.total_lottery_winnings, "ticketsHeld": { "current": self.currently_held_lottery_tickets , "total": self.total_held_lottery_tickets } } \ No newline at end of file diff --git a/files/helpers/lottery.py b/files/helpers/lottery.py new file mode 100644 index 000000000..4ea4820d7 --- /dev/null +++ b/files/helpers/lottery.py @@ -0,0 +1,97 @@ +import time +from sqlalchemy import * +from files.helpers.wrappers import * + +LOTTERY_TICKET_COST = 12 + +# The amount of dramacoins permanently removed from the economy to reduce expected value +SINK_RATE = 3 + +# The amount of dramacoins the lottery founders receive +ROYALTY_RATE = 1 + +# The account in which royalties are to be deposited +ROYALTY_ACCOUNT_ID = 9 + +# The account in which the prize is held to be accessed by anyone +MANAGER_ACCOUNT_ID = 3 + + +def get_most_recent_lottery(g): + lotteries = g.db.query(Lottery).order_by(Lottery.id.desc()) + count = 0 + mostRecent = None + + for lottery in lotteries: + if count == 0: + mostRecent = lottery + count += 1 + + return mostRecent + + +def end_lottery_session(g): + # Check that a current session exists + most_recent_lottery = get_most_recent_lottery(g) + + if (most_recent_lottery is None or not most_recent_lottery.is_active): + return + + # Calculate players and odds + + + # Payout to winner + + # Send DM to all involved + + most_recent_lottery.is_active = False + + # Save changes + g.db.commit() + + +def start_new_lottery_session(g): + # Wrap up current session + end_lottery_session(g) + + # Create new session + lottery = Lottery() + epoch_time = int(time.time()) + one_week_from_now = epoch_time + 60 * 60 * 24 * 7 + lottery.ends_at = one_week_from_now + lottery.is_active = True + + # Save changes + g.db.add(lottery) + g.db.commit() + + +def purchase_lottery_ticket(g, v): + if (v.coins < LOTTERY_TICKET_COST): + return False, f'Lottery tickets cost {LOTTERY_TICKET_COST} dramacoins each.', None + + most_recent_lottery = get_most_recent_lottery(g) + if (most_recent_lottery is None or not most_recent_lottery.is_active): + return False, "There is no active lottery.", None + + # Charge the user and update the lottery + v.coins -= LOTTERY_TICKET_COST + v.currently_held_lottery_tickets += 1 + v.total_held_lottery_tickets += 1 + + net_ticket_value = LOTTERY_TICKET_COST - SINK_RATE - ROYALTY_RATE + most_recent_lottery.prize += net_ticket_value + most_recent_lottery.tickets_sold += 1 + + # Pass the holdings to the lottery manager + manager = g.db.query(User).get(MANAGER_ACCOUNT_ID) + manager.coins += net_ticket_value + + # Pay royalties + beneficiary = g.db.query(User).get(ROYALTY_ACCOUNT_ID) + beneficiary.coins += ROYALTY_RATE + + # Save changes + g.db.commit() + + return True, 'Successfully purchased a lottery ticket!', most_recent_lottery.stats diff --git a/files/routes/lottery.py b/files/routes/lottery.py index 281069bec..8b80d2aa1 100644 --- a/files/routes/lottery.py +++ b/files/routes/lottery.py @@ -3,47 +3,42 @@ from files.helpers.wrappers import * from files.helpers.alerts import * from files.helpers.get import * from files.helpers.const import * - - -@app.post("/lottery/start") -@auth_required -def lottery_start(v): - # Save changes - g.db.commit() - - return {"message": "Lottershe started."} +from files.helpers.lottery import * @app.post("/lottery/end") @auth_required def lottery_end(v): - # Save changes - g.db.commit() + end_lottery_session(g) + return {"message": "Lottery ended."} - return {"message": "Lottershe ended."} + +@app.post("/lottery/start") +@auth_required +def lottery_start(v): + start_new_lottery_session(g) + return {"message": "Lottery started."} @app.post("/lottery/buy") @limiter.limit("1/second;30/minute;200/hour;1000/day") @auth_required def lottery_buy(v): - if v.coins < 12: - return {"error": "Lottershe tickets cost 12 dramacoins each."}, 400 + success, message, lottery_stats = purchase_lottery_ticket(g, v) - # Charge user for ticket - v.coins -= 12 - - # Check for active lottery - pass - - # Save changes - g.db.commit() - - return {"message": "Lottershe ticket purchased!", "stats": {"sessionEnds": 0, "prize": 0, "ticketsSoldSession": 0, "ticketsSoldTotal": 0, "ticketsHeldSession": 0, "ticketsHeldTotal": 0, "totalWinnings": 0}} + if success: + return {"message": message, "stats": {"user": v.lottery_stats, "lottery": lottery_stats}} + else: + return {"error": message, "stats": {"user": v.lottery_stats, "lottery": lottery_stats}} -@app.get("/lottery/stats") +@app.get("/lottery/active") @limiter.limit("1/second;30/minute;200/hour;1000/day") @auth_required -def lottery_stats(v): - return {"message": {"sessionEnds": 0, "prize": 0, "ticketsSoldSession": 0, "ticketsSoldTotal": 0}} +def lottery_active(v): + most_recent_lottery = get_most_recent_lottery(g) + + if most_recent_lottery is None or not most_recent_lottery.is_active: + return {"message": "There is no active lottery."} + + return {"message": most_recent_lottery.stats} diff --git a/files/templates/lottery_modal.html b/files/templates/lottery_modal.html index 89d6726ca..7337e4d69 100644 --- a/files/templates/lottery_modal.html +++ b/files/templates/lottery_modal.html @@ -25,6 +25,37 @@ />