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 fe963336e..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) @@ -259,13 +262,6 @@ class User(Base): def age(self): return int(time.time()) - self.created_utc - @property - @lazy - def ban_reason_link(self): - if self.ban_reason: - if self.ban_reason.startswith("/post/"): return self.ban_reason.split(None, 1)[0] - if self.ban_reason.startswith("/comment/"): return self.ban_reason.split(None, 1)[0] + "?context=8#context" - @property @lazy def alts_unique(self): @@ -656,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/sanitize.py b/files/helpers/sanitize.py index 1e92dbe6a..fdc34ea2a 100644 --- a/files/helpers/sanitize.py +++ b/files/helpers/sanitize.py @@ -247,7 +247,7 @@ def sanitize(sanitized, alert=False, comment=False, edit=False): for rd in ["://reddit.com", "://new.reddit.com", "://www.reddit.com", "://redd.it", "://libredd.it", "://teddit.net"]: sanitized = sanitized.replace(rd, "://old.reddit.com") - sanitized = normalize_url(sanitized) + sanitized = sanitize_url(sanitized) sanitized = sanitized.replace('&','&') @@ -377,7 +377,8 @@ def filter_emojis_only(title, edit=False, graceful=False): if len(title) > 1500 and not graceful: abort(400) else: return title -def normalize_url(url): +def sanitize_url(url): + # NB: Used in this file to sanitize all URLs in bulk text. url = url.replace("nitter.net", "twitter.com") \ .replace("old.reddit.com/gallery", "reddit.com/gallery") \ .replace("https://youtu.be/", "https://youtube.com/watch?v=") \ @@ -392,6 +393,11 @@ def normalize_url(url): .replace("https://www.instagram", "https://instagram") \ .replace("https://www.tiktok", "https://tiktok") + return url + +def normalize_url(url): + url = sanitize_url(url) + if "/i.imgur.com/" in url: url = url.replace(".png", ".webp").replace(".jpg", ".webp").replace(".jpeg", ".webp") elif "/media.giphy.com/" in url or "/c.tenor.com/" in url: 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/admin.py b/files/routes/admin.py index 1920bd496..673461ce6 100644 --- a/files/routes/admin.py +++ b/files/routes/admin.py @@ -932,18 +932,16 @@ def admin_removed_comments(v): def agendaposter(user_id, v): user = g.db.query(User).filter_by(id=user_id).one_or_none() - days = min(float(request.values.get("days", 30.0)), 30.0) + days = request.values.get("days") + if not days: days = 30.0 + days = float(days) + days = min(days, 30.0) expiry = int(time.time() + days*60*60*24) user.agendaposter = expiry g.db.add(user) - for alt in user.alts: - if alt.admin_level: return {"error": "User is an admin!"} - alt.agendaposter = expiry - g.db.add(alt) - note = f"for {days} days" ma = ModAction( @@ -1123,16 +1121,19 @@ def ban_user(user_id, v): days = float(request.values.get("days")) if request.values.get('days') else 0 reason = request.values.get("reason", "").strip()[:256] - passed_reason = filter_emojis_only(reason) + reason = filter_emojis_only(reason) - if len(passed_reason) > 256: passed_reason = reason + if reason.startswith("/") and '\\' not in reason: + reason = f'{reason}' - user.ban(admin=v, reason=passed_reason, days=days) + if len(reason) > 256: reason = reason + + user.ban(admin=v, reason=reason, days=days) if request.values.get("alts"): for x in user.alts: if x.admin_level: break - x.ban(admin=v, reason=passed_reason, days=days) + x.ban(admin=v, reason=reason, days=days) if days: if reason: text = f"@{v.username} has banned you for **{days}** days for the following reason:\n\n> {reason}" @@ -1268,6 +1269,10 @@ def ban_post(post_id, v): v.coins += 1 g.db.add(v) + if SITE == 'rdrama.net' and v.id != AEVANN_ID: + message = f"@{v.username} has removed [{post.title}]({post.shortlink})" + send_repeatable_notification(AEVANN_ID, message) + requests.post(f'https://api.cloudflare.com/client/v4/zones/{CF_ZONE}/purge_cache', headers=CF_HEADERS, json={'files': [f"{SITE_FULL}/logged_out/"]}, timeout=5) g.db.commit() @@ -1470,6 +1475,11 @@ def api_ban_comment(c_id, v): target_comment_id=comment.id, ) g.db.add(ma) + + if SITE == 'rdrama.net' and v.id != AEVANN_ID: + message = f"@{v.username} has removed [comment]({comment.shortlink})" + send_repeatable_notification(AEVANN_ID, message) + g.db.commit() return {"message": "Comment removed!"} diff --git a/files/routes/comments.py b/files/routes/comments.py index 9b51cf640..843311b02 100644 --- a/files/routes/comments.py +++ b/files/routes/comments.py @@ -413,7 +413,8 @@ def api_comment(v): level=level+1, body_html=filter_emojis_only(option), upvotes=0, - is_bot=True + is_bot=True, + ghost=c.ghost ) g.db.add(c_option) @@ -425,7 +426,8 @@ def api_comment(v): level=level+1, body_html=filter_emojis_only(choice), upvotes=0, - is_bot=True + is_bot=True, + ghost=c.ghost ) g.db.add(c_choice) @@ -454,7 +456,7 @@ def api_comment(v): is_bot=True, body_html=body_based_html, top_comment_id=c.top_comment_id, - ghost=parent_post.ghost + ghost=c.ghost ) g.db.add(c_based) @@ -486,7 +488,7 @@ def api_comment(v): is_bot=True, body_html=body_jannied_html, top_comment_id=c.top_comment_id, - ghost=parent_post.ghost + ghost=c.ghost ) g.db.add(c_jannied) @@ -522,7 +524,7 @@ def api_comment(v): is_bot=True, body_html=body_html2, top_comment_id=c.top_comment_id, - ghost=parent_post.ghost + ghost=c.ghost ) g.db.add(c2) @@ -553,7 +555,7 @@ def api_comment(v): is_bot=True, body_html=body_html2, top_comment_id=c.top_comment_id, - ghost=parent_post.ghost, + ghost=c.ghost, distinguish_level=6 ) @@ -578,7 +580,7 @@ def api_comment(v): is_bot=True, body_html=body_html2, top_comment_id=c.top_comment_id, - ghost=parent_post.ghost, + ghost=c.ghost, distinguish_level=6 ) @@ -596,7 +598,7 @@ def api_comment(v): is_bot=True, body_html=body_html2, top_comment_id=c.top_comment_id, - ghost=parent_post.ghost, + ghost=c.ghost, distinguish_level=6 ) @@ -708,7 +710,8 @@ def edit_comment(cid, v): level=c.level+1, body_html=filter_emojis_only(i.group(1)), upvotes=0, - is_bot=True + is_bot=True, + ghost=c.ghost ) g.db.add(c_option) @@ -720,7 +723,8 @@ def edit_comment(cid, v): level=c.level+1, body_html=filter_emojis_only(i.group(1)), upvotes=0, - is_bot=True + is_bot=True, + ghost=c.ghost ) g.db.add(c_choice) diff --git a/files/routes/lottery.py b/files/routes/lottery.py new file mode 100644 index 000000000..d5bbe4703 --- /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("3/second;100/minute;500/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("3/second;100/minute;500/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/routes/posts.py b/files/routes/posts.py index 09062fa49..3daacbaa5 100644 --- a/files/routes/posts.py +++ b/files/routes/posts.py @@ -483,7 +483,8 @@ def edit_post(pid, v): level=1, body_html=filter_emojis_only(i.group(1)), upvotes=0, - is_bot=True + is_bot=True, + ghost=p.ghost ) g.db.add(c) @@ -494,7 +495,8 @@ def edit_post(pid, v): level=1, body_html=filter_emojis_only(i.group(1)), upvotes=0, - is_bot=True + is_bot=True, + ghost=p.ghost ) g.db.add(c) @@ -1138,7 +1140,8 @@ def submit_post(v, sub=None): level=1, body_html=filter_emojis_only(option), upvotes=0, - is_bot=True + is_bot=True, + ghost=post.ghost ) g.db.add(c) @@ -1148,7 +1151,8 @@ def submit_post(v, sub=None): level=1, body_html=filter_emojis_only(choice), upvotes=0, - is_bot=True + is_bot=True, + ghost=post.ghost ) g.db.add(c) @@ -1227,6 +1231,7 @@ def submit_post(v, sub=None): stickied='AutoJanny', distinguish_level=6, body_html=body_jannied_html, + ghost=post.ghost ) g.db.add(c_jannied) @@ -1329,7 +1334,8 @@ def submit_post(v, sub=None): over_18=False, is_bot=True, app_id=None, - body_html=body_html + body_html=body_html, + ghost=post.ghost ) g.db.add(c) diff --git a/files/routes/static.py b/files/routes/static.py index c17a9b272..b6005844d 100644 --- a/files/routes/static.py +++ b/files/routes/static.py @@ -140,9 +140,9 @@ def stats(site=None): stats.update(stats2) - bots = g.db.query(User).filter(User.id.in_(bots)) + accs = g.db.query(User).filter(User.id.in_(bots)) - for u in bots: + for u in accs: g.db.add(u) if u.patron_utc and u.patron_utc < time.time(): @@ -232,8 +232,16 @@ def cached_chart(kind, site): ) today_cutoff = calendar.timegm(midnight_this_morning) - if kind == "daily": day_cutoffs = [today_cutoff - 86400 * i for i in range(55)][1:] - else: day_cutoffs = [today_cutoff - 86400 * 7 * i for i in range(55)][1:] + if SITE == 'rdrama.net': + time_diff = time.time() - 1619827200 + num_of_weeks = int(time_diff / 604800) + chart_width = int(num_of_weeks/1.4) + else: + num_of_weeks = 30 + chart_width = 30 + + if kind == "daily": day_cutoffs = [today_cutoff - 86400 * i for i in range(num_of_weeks)][1:] + else: day_cutoffs = [today_cutoff - 86400 * 7 * i for i in range(num_of_weeks)][1:] day_cutoffs.insert(0, calendar.timegm(now)) @@ -245,11 +253,11 @@ def cached_chart(kind, site): comment_stats = [g.db.query(Comment).filter(Comment.created_utc < day_cutoffs[i], Comment.created_utc > day_cutoffs[i + 1],Comment.is_banned == False, Comment.author_id.notin_((AUTOJANNY_ID,NOTIFICATIONS_ID))).count() for i in range(len(day_cutoffs) - 1)][::-1] - plt.rcParams["figure.figsize"] = (30, 20) + plt.rcParams["figure.figsize"] = (chart_width, 20) - signup_chart = plt.subplot2grid((30, 20), (0, 0), rowspan=6, colspan=30) - posts_chart = plt.subplot2grid((30, 20), (10, 0), rowspan=6, colspan=30) - comments_chart = plt.subplot2grid((30, 20), (20, 0), rowspan=6, colspan=30) + signup_chart = plt.subplot2grid((chart_width, 20), (0, 0), rowspan=6, colspan=chart_width) + posts_chart = plt.subplot2grid((chart_width, 20), (10, 0), rowspan=6, colspan=chart_width) + comments_chart = plt.subplot2grid((chart_width, 20), (20, 0), rowspan=6, colspan=chart_width) signup_chart.grid(), posts_chart.grid(), comments_chart.grid() diff --git a/files/templates/admin/removed_comments.html b/files/templates/admin/removed_comments.html index bac408d79..8399ee022 100644 --- a/files/templates/admin/removed_comments.html +++ b/files/templates/admin/removed_comments.html @@ -1,7 +1,7 @@ {% extends "admin/removed_posts.html" %} {% block title %} -Comments +Removed Comments {% endblock %} {% block listing %} diff --git a/files/templates/admin/reported_comments.html b/files/templates/admin/reported_comments.html index 93f624f7f..3ee534eaf 100644 --- a/files/templates/admin/reported_comments.html +++ b/files/templates/admin/reported_comments.html @@ -1,7 +1,7 @@ {% extends "admin/reported_posts.html" %} {% block title %} -Removed Comments +Reported Comments {% endblock %} {% block listing %} diff --git a/files/templates/comments.html b/files/templates/comments.html index 1c5f265b9..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 %}
{% if u.is_suspended %} -
BANNED USER{% if u.ban_reason %}: - {% if u.ban_reason_link %}{% endif %} +
BANNED USER + {% if u.ban_reason %}: {{u.ban_reason | safe}} - {% if u.ban_reason_link %}{% endif %} - {% endif %}
- {% if u.unban_utc %}
{{u.unban_string}}
{% endif %} + {% endif %} + + {% if u.unban_utc %} +
{{u.unban_string}}
+ {% endif %} {% endif %}

{{u.username}}

@@ -68,7 +70,7 @@ {% endif %} - {% if u.admin_level > 1 or (u.admin_level == 1 and not(v and v.admin_level > 1)) %} + {% if u.admin_level > 1 %} @@ -388,7 +390,7 @@   {% endif %} - {% if u.admin_level > 1 or (u.admin_level == 1 and not(v and v.admin_level > 1)) %} + {% if u.admin_level > 1 %} diff --git a/files/templates/util/assetcache.html b/files/templates/util/assetcache.html index f20e157e9..082e8d95e 100644 --- a/files/templates/util/assetcache.html +++ b/files/templates/util/assetcache.html @@ -1,6 +1,6 @@ {%- set CACHE_VER = { - 'css/main.css': 281, + 'css/main.css': 283, 'js/award_modal.js': 251, 'js/bootstrap.js': 257, 'js/header.js': 268, diff --git a/schema.sql b/schema.sql index 9446a7b83..edf0c6801 100644 --- a/schema.sql +++ b/schema.sql @@ -356,6 +356,40 @@ CREATE TABLE public.follows ( ); +-- +-- Name: lotteries; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.lotteries ( + id integer NOT NULL, + is_active boolean DEFAULT false NOT NULL, + ends_at integer DEFAULT 0 NOT NULL, + prize integer DEFAULT 0 NOT NULL, + tickets_sold integer DEFAULT 0 NOT NULL, + winner_id integer +); + + +-- +-- Name: lotteries_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.lotteries_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: lotteries_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.lotteries_id_seq OWNED BY public.lotteries.id; + + -- -- Name: marseys; Type: TABLE; Schema: public; Owner: - -- @@ -630,7 +664,10 @@ CREATE TABLE public.users ( subs_created integer DEFAULT 0 NOT NULL, deflector integer, reddit character varying(15) NOT NULL, - animations boolean DEFAULT true NOT NULL + animations boolean DEFAULT true NOT NULL, + currently_held_lottery_tickets integer DEFAULT 0 NOT NULL, + total_held_lottery_tickets integer DEFAULT 0 NOT NULL, + total_lottery_winnings integer DEFAULT 0 NOT NULL ); @@ -700,6 +737,13 @@ ALTER TABLE ONLY public.badge_defs ALTER COLUMN id SET DEFAULT nextval('public.b ALTER TABLE ONLY public.comments ALTER COLUMN id SET DEFAULT nextval('public.comments_id_seq'::regclass); +-- +-- Name: lotteries id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.lotteries ALTER COLUMN id SET DEFAULT nextval('public.lotteries_id_seq'::regclass); + + -- -- Name: modactions id; Type: DEFAULT; Schema: public; Owner: - -- @@ -848,6 +892,14 @@ ALTER TABLE ONLY public.follows ADD CONSTRAINT follows_pkey PRIMARY KEY (target_id, user_id); +-- +-- Name: lotteries lotteries_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.lotteries + ADD CONSTRAINT lotteries_pkey PRIMARY KEY (id); + + -- -- Name: marseys marseys_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -1636,6 +1688,14 @@ ALTER TABLE ONLY public.exiles ADD CONSTRAINT exile_user_fkey FOREIGN KEY (user_id) REFERENCES public.users(id); +-- +-- Name: lotteries fk_winner; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.lotteries + ADD CONSTRAINT fk_winner FOREIGN KEY (winner_id) REFERENCES public.users(id); + + -- -- Name: flags flags_post_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - --