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..35d163278 --- /dev/null +++ b/files/classes/lottery.py @@ -0,0 +1,32 @@ +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) + winner_id = Column(Integer, ForeignKey("users.id")) + + @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 e245b5ff6..fa1b7e543 100644 --- a/files/classes/user.py +++ b/files/classes/user.py @@ -127,6 +127,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) @@ -649,3 +652,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/const.py b/files/helpers/const.py index 4ec821272..3f532ef2c 100644 --- a/files/helpers/const.py +++ b/files/helpers/const.py @@ -173,6 +173,7 @@ if SITE in {'rdrama.net','devrama.xyz'}: "6": "947236351445725204", "7": "886781932430565418", } + elif SITE == "pcmemes.net": HOLE_COST = 10000 NOTIFICATIONS_ID = 1046 @@ -1013,3 +1014,19 @@ procoins_li = (0,2500,5000,10000,25000,50000,125000,250000) linefeeds_regex = re.compile("([^\n])\n([^\n])", flags=re.A) def make_name(*args, **kwargs): return request.base_url + +# Lottery +if SITE_NAME == 'rDrama': + LOTTERY_ENABLED = True + LOTTERY_TICKET_COST = 12 + LOTTERY_SINK_RATE = 3 + LOTTERY_ROYALTY_RATE = 1 + LOTTERY_ROYALTY_ACCOUNT_ID = 8239 # (McCoxmaul) + LOTTERY_MANAGER_ACCOUNT_ID = 11651 # (Lottershe) +else: + LOTTERY_ENABLED = False + LOTTERY_TICKET_COST = 0 + LOTTERY_SINK_RATE = 0 + LOTTERY_ROYALTY_RATE = 0 + LOTTERY_ROYALTY_ACCOUNT_ID = 0 + LOTTERY_MANAGER_ACCOUNT_ID = 0 \ No newline at end of file diff --git a/files/helpers/jinja2.py b/files/helpers/jinja2.py index d9e33cb6f..49701af9f 100644 --- a/files/helpers/jinja2.py +++ b/files/helpers/jinja2.py @@ -50,4 +50,9 @@ def timestamp(timestamp): @app.context_processor def inject_constants(): - return {"environ":environ, "SITE":SITE, "SITE_NAME":SITE_NAME, "SITE_FULL":SITE_FULL, "AUTOJANNY_ID":AUTOJANNY_ID, "NOTIFICATIONS_ID":NOTIFICATIONS_ID, "PUSHER_ID":PUSHER_ID, "CC":CC, "CC_TITLE":CC_TITLE, "listdir":listdir, "MOOSE_ID":MOOSE_ID, "AEVANN_ID":AEVANN_ID, "PIZZASHILL_ID":PIZZASHILL_ID, "config":app.config.get, "DEFAULT_COLOR":DEFAULT_COLOR, "COLORS":COLORS, "ADMIGGERS":ADMIGGERS, "datetime":datetime, "time":time} \ No newline at end of file + return {"environ":environ, "SITE":SITE, "SITE_NAME":SITE_NAME, "SITE_FULL":SITE_FULL, + "AUTOJANNY_ID":AUTOJANNY_ID, "NOTIFICATIONS_ID":NOTIFICATIONS_ID, "PUSHER_ID":PUSHER_ID, + "CC":CC, "CC_TITLE":CC_TITLE, "listdir":listdir, "MOOSE_ID":MOOSE_ID, "AEVANN_ID":AEVANN_ID, + "PIZZASHILL_ID":PIZZASHILL_ID, "config":app.config.get, "DEFAULT_COLOR":DEFAULT_COLOR, + "COLORS":COLORS, "ADMIGGERS":ADMIGGERS, "datetime":datetime, "time":time, + "LOTTERY_ENABLED": LOTTERY_ENABLED} \ No newline at end of file diff --git a/files/helpers/lottery.py b/files/helpers/lottery.py new file mode 100644 index 000000000..af24a51e0 --- /dev/null +++ b/files/helpers/lottery.py @@ -0,0 +1,114 @@ +import time +from random import choice +from sqlalchemy import * +from files.helpers.alerts import * +from files.helpers.wrappers import * +from flask import g +from .const import * + + +def get_active_lottery(): + return g.db.query(Lottery).order_by(Lottery.id.desc()).filter(Lottery.is_active).one_or_none() + + +def get_users_participating_in_lottery(): + return g.db.query(User).filter(User.currently_held_lottery_tickets > 0).all() + + +def get_active_lottery_stats(): + active_lottery = get_active_lottery() + participating_users = get_users_participating_in_lottery() + + return None if active_lottery is None else active_lottery.stats, len(participating_users) + + +def end_lottery_session(): + active_lottery = get_active_lottery() + + if (active_lottery is None): + return False, "There is no active lottery." + + participating_users = get_users_participating_in_lottery() + raffle = [] + for user in participating_users: + for _ in range(user.currently_held_lottery_tickets): + raffle.append(user.id) + + winner = choice(raffle) + active_lottery.winner_id = winner + winning_user = next(filter(lambda x: x.id == winner, participating_users)) + winning_user.coins += active_lottery.prize + winning_user.total_lottery_winnings += active_lottery.prize + + for user in participating_users: + chance_to_win = user.currently_held_lottery_tickets / len(raffle) * 100 + notification_text = f'You won {active_lottery.prize} dramacoins in the lottery! Congratulations!\nOdds of winning: {chance_to_win}%' if user.id == winner else "You did not win the lottery. Better luck next time!\nOdds of winning: {chance_to_win}%" + send_repeatable_notification(user.id, notification_text) + user.currently_held_lottery_tickets = 0 + + active_lottery.is_active = False + + manager = g.db.query(User).get(LOTTERY_MANAGER_ACCOUNT_ID) + manager.coins -= active_lottery.prize + + g.db.commit() + + return True, f'{winning_user.username} won {active_lottery.prize} dramacoins!' + + +def start_new_lottery_session(): + end_lottery_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 + + g.db.add(lottery) + g.db.commit() + + +def purchase_lottery_ticket(v): + if (v.coins < LOTTERY_TICKET_COST): + return False, f'Lottery tickets cost {LOTTERY_TICKET_COST} dramacoins each.' + + most_recent_lottery = get_active_lottery() + if (most_recent_lottery is None): + return False, "There is no active lottery." + + v.coins -= LOTTERY_TICKET_COST + v.currently_held_lottery_tickets += 1 + v.total_held_lottery_tickets += 1 + + net_ticket_value = LOTTERY_TICKET_COST - LOTTERY_SINK_RATE - LOTTERY_ROYALTY_RATE + most_recent_lottery.prize += net_ticket_value + most_recent_lottery.tickets_sold += 1 + + grant_lottery_proceeds_to_manager(net_ticket_value) + + beneficiary = g.db.query(User).get(LOTTERY_ROYALTY_ACCOUNT_ID) + beneficiary.coins += LOTTERY_ROYALTY_RATE + + g.db.commit() + + return True, 'Successfully purchased a lottery ticket!' + +def grant_lottery_proceeds_to_manager(amount): + manager = g.db.query(User).get(LOTTERY_MANAGER_ACCOUNT_ID) + manager.coins += amount + +def grant_lottery_tickets_to_user(v, amount): + active_lottery = get_active_lottery() + prize_value = amount * LOTTERY_TICKET_COST + + if active_lottery: + v.currently_held_lottery_tickets += amount + v.total_held_lottery_tickets += amount + + active_lottery.prize += prize_value + active_lottery.tickets_sold += amount + + grant_lottery_proceeds_to_manager(amount) + + g.db.commit() \ No newline at end of file diff --git a/files/helpers/treasure.py b/files/helpers/treasure.py index b671622f4..a17f2f277 100644 --- a/files/helpers/treasure.py +++ b/files/helpers/treasure.py @@ -1,22 +1,26 @@ -import random +from random import randint +from math import floor +from files.helpers.const import * +from files.helpers.lottery import * special_min = 100 special_max = 200 standard_min = 10 standard_max = 100 +lotterizer_rate = 33 def check_for_treasure(in_text, from_comment): if '!slots' not in in_text and '!blackjack' not in in_text and '!wordle' not in in_text: - seed = random.randint(1, 1000) + seed = randint(1, 1000) is_special = seed == 1000 is_standard = seed >= 990 amount = 0 if is_special: - amount = random.randint(special_min, special_max) + amount = randint(special_min, special_max) elif is_standard: - amount = random.randint(standard_min, standard_max) - if random.randint(1, 100) > 90: + amount = randint(standard_min, standard_max) + if randint(1, 100) > 90: user = from_comment.author if amount > user.coins: amount = user.coins amount = -amount @@ -24,6 +28,18 @@ def check_for_treasure(in_text, from_comment): if amount != 0: user = from_comment.author - user.coins += amount + if amount > 0: + active_lottery = get_active_lottery() + lottery_tickets_seed = randint(1, 100) + lottery_tickets_instead = lottery_tickets_seed <= lotterizer_rate + + if active_lottery and lottery_tickets_instead: + ticket_count = floor(amount / LOTTERY_TICKET_COST) + grant_lottery_tickets_to_user(user, ticket_count) + from_comment.treasure_amount = f'l{ticket_count}' + return + + + user.coins += amount from_comment.treasure_amount = str(amount) \ No newline at end of file diff --git a/files/helpers/wrappers.py b/files/helpers/wrappers.py index 7861ca3eb..b57299d9e 100644 --- a/files/helpers/wrappers.py +++ b/files/helpers/wrappers.py @@ -137,4 +137,15 @@ def admin_level_required(x): wrapper.__name__ = f.__name__ return wrapper - return wrapper_maker \ No newline at end of file + return wrapper_maker + +def lottery_required(f): + def wrapper(*args, **kwargs): + v = get_logged_in_user() + + if not LOTTERY_ENABLED: abort(404) + + return make_response(f(v=v)) + + wrapper.__name__ = f.__name__ + return wrapper \ No newline at end of file diff --git a/files/routes/__init__.py b/files/routes/__init__.py index 6d73bd1fc..46a9ae564 100644 --- a/files/routes/__init__.py +++ b/files/routes/__init__.py @@ -15,4 +15,5 @@ from .votes import * from .feeds import * from .awards import * from .giphy import * -from .subs import * \ No newline at end of file +from .subs import * +from .lottery import * \ No newline at end of file diff --git a/files/routes/lottery.py b/files/routes/lottery.py new file mode 100644 index 000000000..670f8f55a --- /dev/null +++ b/files/routes/lottery.py @@ -0,0 +1,47 @@ +from files.__main__ import app, limiter +from files.helpers.wrappers import * +from files.helpers.alerts import * +from files.helpers.get import * +from files.helpers.const import * +from files.helpers.wrappers import * +from files.helpers.lottery import * + + +@app.post("/lottery/end") +@admin_level_required(3) +@lottery_required +def lottery_end(v): + success, message = end_lottery_session() + return {"message": message} if success else {"error": message} + + +@app.post("/lottery/start") +@admin_level_required(3) +@lottery_required +def lottery_start(v): + start_new_lottery_session() + return {"message": "Lottery started."} + + +@app.post("/lottery/buy") +@limiter.limit("1/second;30/minute;200/hour;1000/day") +@auth_required +@lottery_required +def lottery_buy(v): + success, message = purchase_lottery_ticket(v) + lottery, participants = get_active_lottery_stats() + + if success: + return {"message": message, "stats": {"user": v.lottery_stats, "lottery": lottery, "participants": participants}} + else: + return {"error": message, "stats": {"user": v.lottery_stats, "lottery": lottery, "participants": participants}} + + +@app.get("/lottery/active") +@limiter.limit("1/second;30/minute;200/hour;1000/day") +@auth_required +@lottery_required +def lottery_active(v): + lottery, participants = get_active_lottery_stats() + + return {"message": "", "stats": {"user": v.lottery_stats, "lottery": lottery, "participants": participants}} diff --git a/files/templates/comments.html b/files/templates/comments.html index 10446a24a..05e58afdd 100644 --- a/files/templates/comments.html +++ b/files/templates/comments.html @@ -7,6 +7,7 @@ {% if v %} {% include "award_modal.html" %} + {% include "lottery_modal.html" %} {% endif %}