diff --git a/files/assets/css/main.css b/files/assets/css/main.css index 7fabd8d49..ffa94348b 100644 --- a/files/assets/css/main.css +++ b/files/assets/css/main.css @@ -7758,3 +7758,11 @@ img[alpha]:not([alt*="#"]) { td[data-time] { white-space: pre; } + +.bg-green { + background-color: #0a6936 !important; +} + +.bg-red { + background-color: #71000b !important; +} diff --git a/files/assets/images/WPD/coins.webp b/files/assets/images/coins.webp similarity index 100% rename from files/assets/images/WPD/coins.webp rename to files/assets/images/coins.webp diff --git a/files/assets/images/rDrama/coins.webp b/files/assets/images/rDrama/coins.webp deleted file mode 100644 index b57f5ba8b..000000000 Binary files a/files/assets/images/rDrama/coins.webp and /dev/null differ diff --git a/files/classes/__init__.py b/files/classes/__init__.py index 88f8ab9fc..730f08481 100644 --- a/files/classes/__init__.py +++ b/files/classes/__init__.py @@ -34,6 +34,7 @@ from .push_subscriptions import * from .group import * from .group_membership import * from .orgy import * +from .currency_logs import * if FEATURES['IP_LOGGING']: from .ip_logs import * diff --git a/files/classes/comment.py b/files/classes/comment.py index 63359f393..de25267d0 100644 --- a/files/classes/comment.py +++ b/files/classes/comment.py @@ -324,7 +324,7 @@ class Comment(Base): @property @lazy def textlink(self): - return f"[this comment]({self.shortlink})" + return f'this comment' @property @lazy diff --git a/files/classes/currency_logs.py b/files/classes/currency_logs.py new file mode 100644 index 000000000..50c538c29 --- /dev/null +++ b/files/classes/currency_logs.py @@ -0,0 +1,29 @@ +import time +from sqlalchemy import Column, ForeignKey +from sqlalchemy.sql.sqltypes import * + +from files.classes import Base +from files.helpers.lazy import lazy +from files.helpers.sorting_and_time import make_age_string + +class CurrencyLog(Base): + __tablename__ = "currency_logs" + id = Column(Integer, primary_key=True) + user_id = Column(Integer, ForeignKey("users.id")) + created_utc = Column(Integer) + currency = Column(String) + amount = Column(Integer) + reason = Column(String) + balance = Column(Integer) + + def __init__(self, *args, **kwargs): + if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) + super().__init__(*args, **kwargs) + + def __repr__(self): + return f"<{self.__class__.__name__}(id={self.id})>" + + @property + @lazy + def age_string(self): + return make_age_string(self.created_utc) diff --git a/files/classes/post.py b/files/classes/post.py index a67b37a52..5b02bc8d3 100644 --- a/files/classes/post.py +++ b/files/classes/post.py @@ -156,7 +156,7 @@ class Post(Base): @property @lazy def textlink(self): - return f"[{self.title}]({self.shortlink})" + return f'{self.title}' @property @lazy diff --git a/files/classes/user.py b/files/classes/user.py index 68276b19a..f2e2112ac 100644 --- a/files/classes/user.py +++ b/files/classes/user.py @@ -14,6 +14,7 @@ from files.classes import Base from files.classes.casino_game import CasinoGame from files.classes.group import * from files.classes.hole import Hole +from files.classes.currency_logs import CurrencyLog from files.helpers.config.const import * from files.helpers.config.modaction_types import * from files.helpers.config.awards import AWARDS_ENABLED, HOUSE_AWARDS @@ -213,7 +214,7 @@ class User(Base): def __repr__(self): return f"<{self.__class__.__name__}(id={self.id}, username={self.username})>" - def pay_account(self, currency, amount): + def pay_account(self, currency, amount, reason=None): if self.id in {AUTOJANNY_ID, LONGPOSTBOT_ID, ZOZBOT_ID}: return @@ -238,7 +239,20 @@ class User(Base): else: user_query.update({ User.marseybux: User.marseybux + amount }) - def charge_account(self, currency, amount, **kwargs): + if reason and amount: + currency_log = CurrencyLog( + user_id=self.id, + currency=currency, + amount=amount, + reason=reason, + ) + g.db.add(currency_log) + if currency == 'coins': + currency_log.balance = self.coins + else: + currency_log.balance = self.marseybux + + def charge_account(self, currency, amount, reason=None, **kwargs): if self.admin_level >= PERMS['INFINITE_CURRENCY']: return (True, amount) @@ -256,12 +270,14 @@ class User(Base): user_query.update({ User.coins: User.coins - amount }) succeeded = True charged_coins = amount + logs = ('coins', amount) elif currency == 'marseybux': account_balance = self.marseybux if not should_check_balance or account_balance >= amount: user_query.update({ User.marseybux: User.marseybux - amount }) succeeded = True + logs = ('marseybux', amount) elif currency == 'coins/marseybux': if self.marseybux >= amount: subtracted_mbux = amount @@ -278,9 +294,24 @@ class User(Base): }) succeeded = True charged_coins = subtracted_coins + logs = (('coins', subtracted_coins), ('marseybux', subtracted_mbux)) if succeeded: g.db.add(self) + if reason: + for currency, amount in logs: + if not amount: continue + currency_log = CurrencyLog( + user_id=self.id, + currency=currency, + amount=-amount, + reason=reason, + ) + g.db.add(currency_log) + if currency == 'coins': + currency_log.balance = self.coins + else: + currency_log.balance = self.marseybux return (succeeded, charged_coins) diff --git a/files/helpers/actions.py b/files/helpers/actions.py index 3ef8e4390..94e0b98e7 100644 --- a/files/helpers/actions.py +++ b/files/helpers/actions.py @@ -166,7 +166,7 @@ def execute_snappy(post, v): g.db.add(award_object) awarded_coins = int(AWARDS["glowie"]['price'] * COSMETIC_AWARD_COIN_AWARD_PCT) - post.author.pay_account('coins', awarded_coins) + post.author.pay_account('coins', awarded_coins, f"glowie award on {post.textlink}") msg = f"@Snappy has given {post.textlink} the Glowie Award and you have received {awarded_coins} coins as a result!" send_repeatable_notification(post.author.id, msg) @@ -475,6 +475,7 @@ def execute_antispam_post_check(title, v, url): return True def execute_antispam_duplicate_comment_check(v, body_html): + return if v.admin_level >= PERMS['BYPASS_ANTISPAM_CHECKS']: return if v.id in ANTISPAM_BYPASS_IDS: @@ -496,6 +497,7 @@ def execute_antispam_duplicate_comment_check(v, body_html): abort(403, "Too much spam!") def execute_antispam_comment_check(body, v): + return if v.admin_level >= PERMS['BYPASS_ANTISPAM_CHECKS']: return diff --git a/files/helpers/alerts.py b/files/helpers/alerts.py index c77b925eb..c32ec1440 100644 --- a/files/helpers/alerts.py +++ b/files/helpers/alerts.py @@ -177,6 +177,7 @@ def NOTIFY_USERS(text, v, oldtext=None, ghost=False, obj=None, followers_ping=Tr if FEATURES['PING_GROUPS']: cost = 0 + cost_groups = [] coin_receivers = set() for i in group_mention_regex.finditer(text): @@ -194,8 +195,11 @@ def NOTIFY_USERS(text, v, oldtext=None, ghost=False, obj=None, followers_ping=Tr cost = g.db.query(User).count() * 5 if cost > v.coins + v.marseybux: abort(403, f"You need {cost} currency to mention these ping groups!") - - v.charge_account('coins/marseybux', cost) + + reason = f"group pinging cost (!everyone)" + if obj: + reason += f" on {obj.textlink}" + v.charge_account('coins/marseybux', cost, reason) if obj: obj.ping_cost += cost return 'everyone' @@ -231,6 +235,7 @@ def NOTIFY_USERS(text, v, oldtext=None, ghost=False, obj=None, followers_ping=Tr if group and group.name == 'verifiedrich': abort(403, f"Only !verifiedrich members can mention it!") cost += len(members) * 5 + cost_groups.append(group.name) if cost > v.coins + v.marseybux: abort(403, f"You need {cost} currency to mention these ping groups!") @@ -239,7 +244,10 @@ def NOTIFY_USERS(text, v, oldtext=None, ghost=False, obj=None, followers_ping=Tr if charge: if cost: - v.charge_account('coins/marseybux', cost) + reason = f"group pinging cost (!" + ", !".join(cost_groups) + ")" + if obj: + reason += f" on {obj.textlink}" + v.charge_account('coins/marseybux', cost, reason) if obj: obj.ping_cost += cost diff --git a/files/helpers/cron.py b/files/helpers/cron.py index 3c15ed668..5d03c61d1 100644 --- a/files/helpers/cron.py +++ b/files/helpers/cron.py @@ -353,7 +353,7 @@ def _unpin_expired(): def _give_marseybux_salary(): for u in g.db.query(User).filter(User.admin_level > 0).all(): marseybux_salary = u.admin_level * 10000 - u.pay_account('marseybux', marseybux_salary) + u.pay_account('marseybux', marseybux_salary, "janny salary") send_repeatable_notification(u.id, f"You have received your monthly janny salary of {marseybux_salary} Marseybux!") def _expire_blocks_mutes_exiles(): diff --git a/files/helpers/lottery.py b/files/helpers/lottery.py index 162eac028..f8eb3fab3 100644 --- a/files/helpers/lottery.py +++ b/files/helpers/lottery.py @@ -47,7 +47,7 @@ def end_lottery_session(): winner = random.choice(raffle) active_lottery.winner_id = winner winning_user = next(filter(lambda x: x.id == winner, participating_users)) - winning_user.pay_account('coins', active_lottery.prize) + winning_user.pay_account('coins', active_lottery.prize, "lottery winnings") winning_user.total_lottery_winnings += active_lottery.prize badge_grant(user=winning_user, badge_id=LOTTERY_WINNER_BADGE_ID) @@ -109,7 +109,7 @@ def purchase_lottery_tickets(v, quantity=1): if (most_recent_lottery is None): return False, "There is no active lottery!" - if not v.charge_account('coins', LOTTERY_TICKET_COST * quantity)[0]: + if not v.charge_account('coins', LOTTERY_TICKET_COST * quantity, f'cost of {quantity} lottery tickets')[0]: return False, "You don't have enough coins" v.currently_held_lottery_tickets += quantity diff --git a/files/helpers/roulette.py b/files/helpers/roulette.py index e491bf75f..496f7d7fc 100644 --- a/files/helpers/roulette.py +++ b/files/helpers/roulette.py @@ -85,7 +85,7 @@ def get_active_roulette_games(): def charge_gambler(gambler, amount, currency): - charged = gambler.charge_account(currency, amount)[0] + charged = gambler.charge_account(currency, amount, "cost of roulette bet")[0] if not charged: raise Exception("Gambler cannot afford charge.") @@ -179,8 +179,8 @@ def spin_roulette_wheel(): coin_winnings = gambler_payout['coins'] procoin_winnings = gambler_payout['marseybux'] - gambler.pay_account('coins', coin_winnings) - gambler.pay_account('marseybux', procoin_winnings) + gambler.pay_account('coins', coin_winnings, "roulette winnings") + gambler.pay_account('marseybux', procoin_winnings, "roulette winnings") # Notify the winners. notification_text = f"Winning number: {number}\n\nCongratulations! One or more of your roulette bets paid off!\n\n" diff --git a/files/helpers/slots.py b/files/helpers/slots.py index 996b8aa13..d34194fc5 100644 --- a/files/helpers/slots.py +++ b/files/helpers/slots.py @@ -51,6 +51,19 @@ def casino_slot_pull(gambler, wager_value, currency): g.db.add(casino_game) g.db.flush() + if casino_game.winnings: + currency_log = CurrencyLog( + user_id=gambler.id, + currency=currency, + amount=-casino_game.winnings, + reason="slots bet", + ) + g.db.add(currency_log) + if currency == 'coins': + currency_log.balance = gambler.coins + else: + currency_log.balance = gambler.marseybux + return casino_game.id, casino_game.game_state else: return None, "{}", diff --git a/files/helpers/treasure.py b/files/helpers/treasure.py index b58c0f391..889f1cc8d 100644 --- a/files/helpers/treasure.py +++ b/files/helpers/treasure.py @@ -40,5 +40,5 @@ def check_for_treasure(from_comment, in_text): from_comment.treasure_amount = f'l{ticket_count}' return - user.pay_account('coins', amount) + user.pay_account('coins', amount, f"found treasure in {from_comment.textlink}") from_comment.treasure_amount = str(amount) diff --git a/files/helpers/twentyone.py b/files/helpers/twentyone.py index 390157f1a..f27fde8b2 100644 --- a/files/helpers/twentyone.py +++ b/files/helpers/twentyone.py @@ -317,9 +317,21 @@ def handle_payout(gambler, state, game): elif split_status == BlackjackStatus.PUSHED: payout += game.wager - gambler.pay_account(game.currency, payout) + if game.winnings: + currency_log = CurrencyLog( + user_id=gambler.id, + currency=game.currency, + amount=-game.winnings, + reason="blackjack bet", + ) + g.db.add(currency_log) + if currency == 'coins': + currency_log.balance = gambler.coins + else: + currency_log.balance = gambler.marseybux + if status in {BlackjackStatus.BLACKJACK, BlackjackStatus.WON} or split_status in {BlackjackStatus.WON}: distribute_wager_badges(gambler, game.wager, won=True) elif status == BlackjackStatus.LOST or split_status == BlackjackStatus.LOST: diff --git a/files/routes/admin.py b/files/routes/admin.py index 8b25db09f..08b1f952e 100644 --- a/files/routes/admin.py +++ b/files/routes/admin.py @@ -188,7 +188,7 @@ def distribute(v, kind, option_id): cid = notif_comment(text) for vote in votes: u = vote.user - u.pay_account('coins', coinsperperson) + u.pay_account('coins', coinsperperson, f"bet winnings on {parent.textlink}") add_notif(cid, u.id, text, pushnotif_url=parent.permalink) text = f"You lost the {POLL_BET_COINS} coins you bet on {parent.textlink} :marseylaugh:" @@ -2163,7 +2163,7 @@ def mark_effortpost(pid, v): coins = (p.upvotes + p.downvotes) * mul - p.author.pay_account('coins', coins) + p.author.pay_account('coins', coins, f"retroactive efortpost gains of {post.textlink}") if v.id != p.author_id: send_repeatable_notification(p.author_id, f":marseyclapping: @{v.username} (a site admin) has marked {p.textlink} as an effortpost, it now gets x{mul} coins from votes. You have received {coins} coins retroactively, thanks! :!marseyclapping:") @@ -2200,7 +2200,7 @@ def unmark_effortpost(pid, v): coins = (p.upvotes + p.downvotes) * mul - p.author.charge_account('coins', coins) + p.author.charge_account('coins', coins, f"revocation of efortpost gains of {post.textlink}") if v.id != p.author_id: send_repeatable_notification(p.author_id, f":marseyitsover: @{v.username} (a site admin) has unmarked {p.textlink} as an effortpost. {coins} coins have been deducted from you. :!marseyitsover:") diff --git a/files/routes/asset_submissions.py b/files/routes/asset_submissions.py index 4711b31e4..ef25530ab 100644 --- a/files/routes/asset_submissions.py +++ b/files/routes/asset_submissions.py @@ -225,7 +225,7 @@ def approve_emoji(v, name): if 'pkmn' in emoji.tags: amount = 500 else: amount = 250 - author.pay_account('coins', amount) + author.pay_account('coins', amount, f"reward for making :{emoji.name}:") g.db.add(author) if v.id != author.id: diff --git a/files/routes/awards.py b/files/routes/awards.py index 7035f435a..e053c7662 100644 --- a/files/routes/awards.py +++ b/files/routes/awards.py @@ -64,7 +64,7 @@ def buy_award(v, kind, AWARDS): else: currency = 'coins/marseybux' - charged = v.charge_account(currency, price) + charged = v.charge_account(currency, price, f"{kind} award cost") if not charged[0]: abort(400, f"Not enough {currency}!") @@ -238,8 +238,8 @@ def award_thing(v, thing_type, id): if kind == 'shit': awarded_coins = int(AWARDS[kind]['price'] * COSMETIC_AWARD_COIN_AWARD_PCT) - v.charge_account('coins', awarded_coins, should_check_balance=False) - obj.author.pay_account('coins', awarded_coins) + v.charge_account('coins', awarded_coins, f"shit award deflected theft on {obj.textlink}", should_check_balance=False) + obj.author.pay_account('coins', awarded_coins, f"shit award deflected theft on {obj.textlink}") elif kind != 'spider': if AWARDS[kind]['cosmetic'] and not AWARDS[kind]['included_in_lootbox']: awarded_coins = int(AWARDS[kind]['price'] * COSMETIC_AWARD_COIN_AWARD_PCT) @@ -248,8 +248,8 @@ def award_thing(v, thing_type, id): if awarded_coins: if kind == 'shit': - author.charge_account('coins', awarded_coins, should_check_balance=False) - v.pay_account('coins', awarded_coins) + author.charge_account('coins', awarded_coins, f"shit award theft on {obj.textlink}", should_check_balance=False) + v.pay_account('coins', awarded_coins, f"shit award theft on {obj.textlink}") else: author.pay_account('coins', awarded_coins) @@ -479,7 +479,7 @@ def award_thing(v, thing_type, id): author.patron = 1 if author.patron_utc: author.patron_utc += 2629746 else: author.patron_utc = int(time.time()) + 2629746 - author.pay_account('marseybux', 1250) + author.pay_account('marseybux', 1250, f"benefactor award on {obj.textlink}") badge_grant(user=v, badge_id=103) elif kind == "rehab": if author.rehab: author.rehab += 86400 diff --git a/files/routes/groups.py b/files/routes/groups.py index 71b273692..baad74709 100644 --- a/files/routes/groups.py +++ b/files/routes/groups.py @@ -35,7 +35,7 @@ def create_group(v): if name in {'everyone', 'jannies', 'holejannies', 'followers', 'commenters'} or g.db.get(Group, name): abort(400, "This group already exists!") - if not v.charge_account('coins/marseybux', GROUP_COST)[0]: + if not v.charge_account('coins/marseybux', GROUP_COST, f"cost of creating !{name}")[0]: abort(403, "You don't have enough coins or marseybux!") g.db.add(v) diff --git a/files/routes/hats.py b/files/routes/hats.py index dc34b1471..3c0f65804 100644 --- a/files/routes/hats.py +++ b/files/routes/hats.py @@ -44,12 +44,12 @@ def buy_hat(v, hat_id): if not hat.is_purchasable: abort(403, "This hat is not for sale!") - charged = v.charge_account('coins/marseybux', hat.price) + charged = v.charge_account('coins/marseybux', hat.price, f"{hat.name} hat cost") if not charged[0]: abort(400, "Not enough coins/marseybux!") v.coins_spent_on_hats += charged[1] - hat.author.pay_account('coins', hat.price * 0.1) + hat.author.pay_account('coins', hat.price * 0.1, f"royalties for `{hat.name}`") new_hat = Hat(user_id=v.id, hat_id=hat.id) g.db.add(new_hat) diff --git a/files/routes/holes.py b/files/routes/holes.py index 897692e4b..47ee405d4 100644 --- a/files/routes/holes.py +++ b/files/routes/holes.py @@ -393,7 +393,7 @@ def create_sub2(v): if not hole_group_name_regex.fullmatch(name): abort(400, "Name does not match the required format!") - if not v.charge_account('coins/marseybux', HOLE_COST)[0]: + if not v.charge_account('coins/marseybux', HOLE_COST, f"cost of creating /h/{name}")[0]: abort(400, "You don't have enough coins or marseybux!") hole = get_hole(name, graceful=True) diff --git a/files/routes/polls.py b/files/routes/polls.py index 22a16b44f..41b16ae7c 100644 --- a/files/routes/polls.py +++ b/files/routes/polls.py @@ -26,7 +26,7 @@ def vote_option(option_id, v): if option.exclusive == 2: if option.parent.total_bet_voted(v): abort(403, "You can't participate in a closed bet!") - if not v.charge_account('coins/marseybux', POLL_BET_COINS)[0]: + if not v.charge_account('coins/marseybux', POLL_BET_COINS, f"cost of bet on {option.parent.textlink}")[0]: abort(400, f"You don't have {POLL_BET_COINS} coins or marseybux!") g.db.add(v) @@ -79,7 +79,7 @@ def vote_option_comment(option_id, v): if option.exclusive == 2: if option.parent.total_bet_voted(v): abort(403, "You can't participate in a closed bet!") - if not v.charge_account('coins/marseybux', POLL_BET_COINS)[0]: + if not v.charge_account('coins/marseybux', POLL_BET_COINS, f"cost of bet on {option.parent.textlink}")[0]: abort(400, f"You don't have {POLL_BET_COINS} coins or marseybux!") g.db.add(v) diff --git a/files/routes/settings.py b/files/routes/settings.py index 246657eca..28e0a7a8f 100644 --- a/files/routes/settings.py +++ b/files/routes/settings.py @@ -381,7 +381,7 @@ def settings_personal_post(v): else: cost = HOUSE_JOIN_COST - success = v.charge_account('coins/marseybux', cost)[0] + success = v.charge_account('coins/marseybux', cost, "cost of changing houses")[0] if not success: abort(403) if house == "None": diff --git a/files/routes/users.py b/files/routes/users.py index 128d1cda2..20fc1fbdb 100644 --- a/files/routes/users.py +++ b/files/routes/users.py @@ -78,7 +78,7 @@ def claim_rewards_all_users(): marseybux = int(marseybux) text = f"You have received {marseybux} Marseybux! You can use them to buy awards or hats in the [shop](/shop/awards) or gamble them in the [casino](/casino)." - user.pay_account('marseybux', marseybux) + user.pay_account('marseybux', marseybux, f"{patron.lower()} reward") send_repeatable_notification(user.id, text) g.db.add(user) @@ -147,13 +147,13 @@ def transfer_currency(v, username, currency_name, apply_tax): notif_text += f"\n\n> {reason}" log_message += f"\n\n> {reason}" - if not v.charge_account(currency_name, amount)[0]: + if not v.charge_account(currency_name, amount, f"gift to @{username}")[0]: abort(400, f"You don't have enough {currency_name}") if currency_name == 'marseybux': - receiver.pay_account('marseybux', amount - tax) + receiver.pay_account('marseybux', amount - tax, f"gift from @{v.username}") elif currency_name == 'coins': - receiver.pay_account('coins', amount - tax) + receiver.pay_account('coins', amount - tax, f"gift from @{v.username}") else: raise ValueError(f"Invalid currency '{currency_name}' got when transferring {amount} from {v.id} to {receiver.id}") @@ -1549,3 +1549,18 @@ def usersong(username): @auth_required def user_effortposts(v, username): return redirect(f'/search/posts?q=author:{username}+effortpost:true') + +@app.get("/bank_statement") +@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400) +@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID) +@auth_required +def currency_log(v): + page = get_page() + + logs = g.db.query(CurrencyLog).filter_by(user_id=v.id) + + total = logs.count() + + logs = logs.order_by(CurrencyLog.created_utc.desc()).offset(PAGE_SIZE * (page - 1)).limit(PAGE_SIZE).all() + + return render_template("bank_statement.html", v=v, logs=logs, page=page, total=total) diff --git a/files/templates/bank_statement.html b/files/templates/bank_statement.html new file mode 100644 index 000000000..c46bd2b97 --- /dev/null +++ b/files/templates/bank_statement.html @@ -0,0 +1,38 @@ +{% extends "default.html" %} +{% block pagetitle %}Bank Statement{% endblock %} +{% block content %} +
+
+
+

Bank Statement

+
+
Balance
+ +
+ {% for log in logs %} +
+
+
+ {{log.currency}} +
+
+
+
+ {{log.amount}} {{log.currency}}   {{log.reason | safe}} +
+
+ {{log.age_string}} +
+
+
+ {{log.balance}} {{log.currency}} +
+
+ {% else %} +
There's nothing here right now.
+ {% endfor %} +
+ {% include "pagination.html" %} +
+
+{% endblock %} diff --git a/files/templates/header.html b/files/templates/header.html index 3feb1e401..8cf0d477e 100644 --- a/files/templates/header.html +++ b/files/templates/header.html @@ -337,7 +337,7 @@
{{v.username}}
-
coins{{v.coins}}{% if not FEATURES['MARSEYBUX'] %} Coin{{macros.plural(v.coins)}}{% endif %}
+
coins{{v.coins}}{% if not FEATURES['MARSEYBUX'] %} Coin{{macros.plural(v.coins)}}{% endif %}
{% if FEATURES['MARSEYBUX'] %}
marseybux{{v.marseybux}}
{% endif %} @@ -415,7 +415,7 @@ {% if v %}
{{u.coins}} - coins + coins {% if FEATURES['MARSEYBUX'] %} {{u.marseybux}} @@ -415,7 +415,7 @@ {{u.coins}} - coins + coins {% if FEATURES['MARSEYBUX'] %} {{u.marseybux}} diff --git a/migrations/20240302-add-currency-logs.sql b/migrations/20240302-add-currency-logs.sql new file mode 100644 index 000000000..977d97def --- /dev/null +++ b/migrations/20240302-add-currency-logs.sql @@ -0,0 +1,26 @@ +create table currency_logs ( + id integer primary key, + user_id integer not null, + created_utc integer not null, + currency varchar(9) not null, + amount integer not null, + reason varchar(1000) not null, + balance integer not null +); + +CREATE SEQUENCE public.currency_logs_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE public.currency_logs_id_seq OWNED BY public.currency_logs.id; + +ALTER TABLE ONLY public.currency_logs ALTER COLUMN id SET DEFAULT nextval('public.currency_logs_id_seq'::regclass); + +alter table only currency_logs + add constraint currency_logs_user_fkey foreign key (user_id) references public.users(id); + +create index currency_logs_index on currency_logs using btree (user_id);