Merge remote-tracking branch 'mccoxmaul/add-lottershe' into frost

remotes/1693045480750635534/spooky-22
Snakes 2022-05-29 23:52:04 -04:00
commit 5b6ba1ac82
15 changed files with 487 additions and 19 deletions

View File

@ -18,4 +18,5 @@ from .sub_block import *
from .saves import *
from .views import *
from .notifications import *
from .follows import *
from .follows import *
from .lottery import *

View File

@ -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,}

View File

@ -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 } }

View File

@ -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

View File

@ -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}
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}

View File

@ -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()

View File

@ -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)

View File

@ -137,4 +137,15 @@ def admin_level_required(x):
wrapper.__name__ = f.__name__
return wrapper
return wrapper_maker
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

View File

@ -15,4 +15,5 @@ from .votes import *
from .feeds import *
from .awards import *
from .giphy import *
from .subs import *
from .subs import *
from .lottery import *

View File

@ -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}}

View File

@ -7,6 +7,7 @@
{% if v %}
{% include "award_modal.html" %}
{% include "lottery_modal.html" %}
{% endif %}
<div style="display:none" id="popover">
@ -224,14 +225,18 @@
{% endif %}
{% if c.treasure_amount and c.treasure_amount != '0' %}
<img class="treasure" alt="treasure" src="/assets/images/chest.webp" width="20" />
{% if '-' in c.treasure_amount %}
<em>A Mimic Ate {{c.treasure_amount.replace('-', '')}} Coins!</em>
{% else %}
<em>Found {{c.treasure_amount}} Coins!</em>
{% endif %}
{% if c.treasure_amount.startswith('l') %}
<img class="treasure" alt="treasure" src="/assets/images/treasure_tickets.webp" width="20" />
<em>Found {{c.treasure_amount.replace('l', '')}} Lottershe Tickets!</em>
{% elif '-' in c.treasure_amount %}
<img class="treasure" alt="treasure" src="/assets/images/treasure_mimic.webp" width="20" />
<em>A Mimic Ate {{c.treasure_amount.replace('-', '')}} Coins!</em>
{% else %}
<img class="treasure" alt="treasure" src="/assets/images/treasure_coins.webp" width="20" />
<em>Found {{c.treasure_amount}} Coins!</em>
{% endif %}
{% endif %}
{% if c.slots_result %}
<em style="position: relative; top: 2px; margin-left: 0.5rem">{{c.slots_result}}</em>
{% endif %}

View File

@ -75,6 +75,17 @@
{% if not err %}
<a class="mobile-nav-icon d-md-none" href="/random_user"><i class="fas fa-music align-middle text-gray-500 black"></i></a>
<a class="mobile-nav-icon d-md-none" href="/random_post"><i class="fas fa-random align-middle text-gray-500 black"></i></a>
{% if v and LOTTERY_ENABLED %}
<a
href="#"
class="mobile-nav-icon d-md-none"
title="Lottershe"
data-bs-toggle="modal"
data-bs-dismiss="modal"
data-bs-target="#lotteryModal">
<i class="fas fa-ticket align-middle text-gray-500"></i>
</a>
{% endif %}
{% if v and v.admin_level > 1 %}
<a class="mobile-nav-icon d-md-none" href="/admin"><i class="fas fa-crown align-middle text-gray-500 black"></i></a>
{% endif %}
@ -101,6 +112,20 @@
<a class="nav-link" href="/random_post/" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Random post"><i class="fas fa-random"></i></a>
</li>
{% if v and LOTTERY_ENABLED %}
<li class="nav-item d-flex align-items-center justify-content-center text-center mx-1">
<a
href="#"
class="nav-link"
title="Lottershe"
data-bs-toggle="modal"
data-bs-dismiss="modal"
data-bs-target="#lotteryModal">
<i class="fas fa-ticket"></i>
</a>
</li>
{% endif %}
<li class="nav-item d-flex align-items-center justify-content-center text-center mx-1">
<a class="nav-link" href="/chat/" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Chat"><i class="fas fa-messages"></i></a>
</li>

View File

@ -0,0 +1,186 @@
{% if LOTTERY_ENABLED %}
<div
class="modal fade"
id="lotteryModal"
tabindex="-1"
role="dialog"
aria-labelledby="lotteryModalTitle"
aria-hidden="true"
>
<div class="modal-dialog lottery-modal--dialog" role="document">
<div class="modal-content lottery-modal--content">
<button
class="close lottery-modal--close"
data-bs-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true"><i class="far fa-times"></i></span>
</button>
<div class="lottery-modal--wrapper">
<div class="lottery-modal--image">
<img src="/assets/images/rDrama/lottery_modal.webp?v=2" />
<img
id="lotteryTicketPulled"
src="/assets/images/rDrama/lottery_modal_active.webp?v=2"
style="display: none"
/>
</div>
<div class="lottery-modal--stats">
{% if v.admin_level > 2 %}
<div
class="lottery-modal--stat"
style="position: relative; padding-top: 1rem"
>
<i
class="fas fa-broom"
style="
position: absolute;
top: -8px;
right: -8px;
font-size: 20px;
"
>
</i>
<button
type="button"
class="btn btn-danger"
id="endLotterySession"
style="display: none"
onclick="endLotterySession()"
>
End Current Session
</button>
<button
type="button"
class="btn btn-success"
id="startLotterySession"
style="display: none"
onclick="startLotterySession()"
>
Start New Session
</button>
</div>
{% endif %}
<div class="lottery-modal--stat">
<div class="lottery-modal--stat-keys">
<div>Prize</div>
<div>Time Remaining</div>
<div>Tickets Sold This Session</div>
<div>Participants This Session</div>
</div>
<div class="lottery-modal--stat-values">
<div>
<img
id="prize-image"
alt="coins"
class="mr-1 ml-1"
data-bs-toggle="tooltip"
data-bs-placement="bottom"
height="13"
src="/assets/images/rDrama/coins.webp?v=2"
title=""
aria-label="coins"
data-bs-original-title="coins"
style="display: none; position: relative; top: -2px"
/>
<span id="prize">-</span>
</div>
<div id="timeLeft">-</div>
<div id="ticketsSoldThisSession">-</div>
<div id="participantsThisSession">-</div>
</div>
</div>
<div class="lottery-modal--stat">
<div class="lottery-modal--stat-keys">
<div>Tickets Owned This Session</div>
<div>Total Tickets Owned</div>
<div>Total Winnings</div>
</div>
<div class="lottery-modal--stat-values">
<div id="ticketsHeldCurrent">-</div>
<div id="ticketsHeldTotal">-</div>
<div id="winnings">-</div>
</div>
</div>
<button
type="button"
class="btn btn-success lottery-modal--action"
id="purchaseTicket"
onclick="purchaseLotteryTicket()"
disabled="true"
>
Purchase 1 for
<img
alt="coins"
class="mr-1 ml-1"
data-bs-toggle="tooltip"
data-bs-placement="bottom"
height="13"
src="/assets/images/rDrama/coins.webp?v=2"
title=""
aria-label="coins"
data-bs-original-title="coins"
/>
12
</button>
</div>
</div>
</div>
</div>
<!-- Success -->
<div
class="toast"
id="lottery-post-success"
style="
position: fixed;
bottom: 1.5rem;
margin: 0 auto;
left: 0;
right: 0;
width: 275px;
z-index: 1000;
"
role="alert"
aria-live="assertive"
aria-atomic="true"
data-bs-animation="true"
data-bs-autohide="true"
data-bs-delay="5000"
>
<div class="toast-body bg-success text-center text-white">
<i class="fas fa-comment-alt-smile mr-2"></i
><span id="lottery-post-success-text"></span>
</div>
</div>
<!-- Error -->
<div
class="toast"
id="lottery-post-error"
style="
position: fixed;
bottom: 1.5rem;
margin: 0 auto;
left: 0;
right: 0;
width: 275px;
z-index: 1000;
"
role="alert"
aria-live="assertive"
aria-atomic="true"
data-bs-animation="true"
data-bs-autohide="true"
data-bs-delay="5000"
>
<div class="toast-body bg-danger text-center text-white">
<i class="fas fa-exclamation-circle mr-2"></i
><span id="lottery-post-error-text"></span>
</div>
</div>
</div>
<script src="/assets/js/lottery_modal.js?v=249" data-cfasync="false"></script>
{% endif %}

View File

@ -2,6 +2,7 @@
{% if v %}
{% include "award_modal.html" %}
{% include "lottery_modal.html" %}
{% endif %}
{% if request.host == 'pcmemes.net' %}

View File

@ -1849,5 +1849,4 @@ ALTER TABLE ONLY public.votes
--
ALTER TABLE ONLY public.votes
ADD CONSTRAINT vote_user_fkey FOREIGN KEY (user_id) REFERENCES public.users(id);
ADD CONSTRAINT vote_user_fkey FOREIGN KEY (user_id) REFERENCES public.users(id);