MarseyWorld/files/helpers/roulette.py

316 lines
8.2 KiB
Python
Raw Normal View History

import json
from enum import Enum
[DO NOT MERGE] import detanglation (#442) * move Base definition to files.classes.__init__.py * fix ImportError * move userpage listing to users.py * don't import the app from classes * consts: set default values to avoid crashes consts: warn if the secret key is the default config value * card view: sneed (user db schema) * cloudflare: use DEFAULT_CONFIG_VALUE * const: set default values * decouple media.py from __main__ * pass database to avoid imports * import cleanup and import request not in const, but in the requests mega import * move asset_submissions site check to __init__ * asset submissions feature flag * flag * g.is_tor * don't import request where it's not needed * i think this is fine * mail: move to own routes and helper * wrappers * required wrappers move * unfuck wrappers a bit * move snappy quotes and marseys to stateful consts * marsify * :pepodrool: * fix missing import * import cache * ...and settings.py * and static.py * static needs cache * route * lmao all of the jinja shit was in feeds.py amazing * classes should only import what they need from flask * import Response * hdjbjdhbhjf * ... * dfdfdfdf * make get a non-required import * isort imports (mostly) * but actually * configs * reload config on import * fgfgfgfg * config * config * initialize snappy and test * cookie of doom debug * edfjnkf * xikscdfd * debug config * set session cookie domain, i think this fixes the can't login bug * sdfbgnhvfdsghbnjfbdvvfghnn * hrsfxgf * dump the entire config on a request * kyskyskyskyskyskyskyskyskys * duifhdskfjdfd * dfdfdfdfdfdfdfdfdfdfdfdf * dfdfdfdf * imoprt all of the consts beacuse fuck it * 😭 * dfdfdfdfdfdfsdasdf * print the entire session * rffdfdfjkfksj * fgbhffh * not the secret keys * minor bug fixes * be helpful in the warning * gfgfgfg * move warning lower * isort main imports (i hope this doesn't fuck something up) * test * session cookie domain redux * dfdfdfd * try only importing Flask * formkeys fix * y * :pepodrool: * route helper * remove before flight * dfdfdfdfdf * isort classes * isort helpers * move check_for_alts to routehelpers and also sort imports and get rid of unused ones * that previous commit but actkally * readd the cache in a dozen places they were implicitly imported * use g.is_tor instead of request.headers. bla bla bla * upgrade streamers to their own route file * get rid of unused imports in __main__ * fgfgf * don't pull in the entire ORM where we don't need it * features * explicit imports for the get helper * explicit imports for the get helper redux * testing allroutes * remove unused import * decouple flask from classes * syntax fix also remember these have side fx for some reason (why?) * move side effects out of the class * posts * testing on devrama * settings * reloading * settingssdsdsds * streamer features * site settings * testing settings on devrama * import * fix modlog * remove debug stuff * revert commit 67275b21ab6e2f2520819e84d10bfc1c746a15b6 * archiveorg to _archiveorg * skhudkfkjfd * fix cron for PCM * fix bugs that snekky wants me to * Fix call to realbody passing db, standardize kwarg * test * import check_for_alts from the right place * cloudflare * testing on devrama * fix cron i think * shadow properly * tasks * Remove print which will surely be annoying in prod. * v and create new session * use files.classes * make errors import little and fix rare 500 in /allow_nsfw * Revert "use files.classes" This reverts commit 98c10b876cf86ce058b7fb955cf1ec0bfb9996c6. * pass v to media functions rather than using g * fix * dfdfdfdfd * cleanup, py type checking is dumb so don't use it where it causes issues * Fix some merge bugs, add DEFAULT_RATELIMIT to main. * Fix imports on sqlalchemy expressions. * `from random import random` is an error. * Fix replies db param. * errors: fix missing import * fix rare 500: only send to GIFT_NOTIF_ID if it exists, and send them the right text * Fix signup formkey. * fix 2 500s * propagate db to submissions * fix replies * dfdfdfdf * Fix verifiedcolor. * is_manual * can't use getters outside of an app context * don't attempt to do gumroad on sites where it's not enabled * don't attempt to do gumraod on sites's where it's unnecessary * Revert "don't attempt to do gumroad on sites where it's not enabled" This reverts commit 6f8a6331878655492dfaf1907b27f8be513c14d3. * fix 500 * validate media type Co-authored-by: TLSM <duolsm@outlook.com>
2022-11-15 09:19:08 +00:00
from random import randint
import time
from flask import g
from files.classes.casino_game import CasinoGame
[DO NOT MERGE] import detanglation (#442) * move Base definition to files.classes.__init__.py * fix ImportError * move userpage listing to users.py * don't import the app from classes * consts: set default values to avoid crashes consts: warn if the secret key is the default config value * card view: sneed (user db schema) * cloudflare: use DEFAULT_CONFIG_VALUE * const: set default values * decouple media.py from __main__ * pass database to avoid imports * import cleanup and import request not in const, but in the requests mega import * move asset_submissions site check to __init__ * asset submissions feature flag * flag * g.is_tor * don't import request where it's not needed * i think this is fine * mail: move to own routes and helper * wrappers * required wrappers move * unfuck wrappers a bit * move snappy quotes and marseys to stateful consts * marsify * :pepodrool: * fix missing import * import cache * ...and settings.py * and static.py * static needs cache * route * lmao all of the jinja shit was in feeds.py amazing * classes should only import what they need from flask * import Response * hdjbjdhbhjf * ... * dfdfdfdf * make get a non-required import * isort imports (mostly) * but actually * configs * reload config on import * fgfgfgfg * config * config * initialize snappy and test * cookie of doom debug * edfjnkf * xikscdfd * debug config * set session cookie domain, i think this fixes the can't login bug * sdfbgnhvfdsghbnjfbdvvfghnn * hrsfxgf * dump the entire config on a request * kyskyskyskyskyskyskyskyskys * duifhdskfjdfd * dfdfdfdfdfdfdfdfdfdfdfdf * dfdfdfdf * imoprt all of the consts beacuse fuck it * 😭 * dfdfdfdfdfdfsdasdf * print the entire session * rffdfdfjkfksj * fgbhffh * not the secret keys * minor bug fixes * be helpful in the warning * gfgfgfg * move warning lower * isort main imports (i hope this doesn't fuck something up) * test * session cookie domain redux * dfdfdfd * try only importing Flask * formkeys fix * y * :pepodrool: * route helper * remove before flight * dfdfdfdfdf * isort classes * isort helpers * move check_for_alts to routehelpers and also sort imports and get rid of unused ones * that previous commit but actkally * readd the cache in a dozen places they were implicitly imported * use g.is_tor instead of request.headers. bla bla bla * upgrade streamers to their own route file * get rid of unused imports in __main__ * fgfgf * don't pull in the entire ORM where we don't need it * features * explicit imports for the get helper * explicit imports for the get helper redux * testing allroutes * remove unused import * decouple flask from classes * syntax fix also remember these have side fx for some reason (why?) * move side effects out of the class * posts * testing on devrama * settings * reloading * settingssdsdsds * streamer features * site settings * testing settings on devrama * import * fix modlog * remove debug stuff * revert commit 67275b21ab6e2f2520819e84d10bfc1c746a15b6 * archiveorg to _archiveorg * skhudkfkjfd * fix cron for PCM * fix bugs that snekky wants me to * Fix call to realbody passing db, standardize kwarg * test * import check_for_alts from the right place * cloudflare * testing on devrama * fix cron i think * shadow properly * tasks * Remove print which will surely be annoying in prod. * v and create new session * use files.classes * make errors import little and fix rare 500 in /allow_nsfw * Revert "use files.classes" This reverts commit 98c10b876cf86ce058b7fb955cf1ec0bfb9996c6. * pass v to media functions rather than using g * fix * dfdfdfdfd * cleanup, py type checking is dumb so don't use it where it causes issues * Fix some merge bugs, add DEFAULT_RATELIMIT to main. * Fix imports on sqlalchemy expressions. * `from random import random` is an error. * Fix replies db param. * errors: fix missing import * fix rare 500: only send to GIFT_NOTIF_ID if it exists, and send them the right text * Fix signup formkey. * fix 2 500s * propagate db to submissions * fix replies * dfdfdfdf * Fix verifiedcolor. * is_manual * can't use getters outside of an app context * don't attempt to do gumroad on sites where it's not enabled * don't attempt to do gumraod on sites's where it's unnecessary * Revert "don't attempt to do gumroad on sites where it's not enabled" This reverts commit 6f8a6331878655492dfaf1907b27f8be513c14d3. * fix 500 * validate media type Co-authored-by: TLSM <duolsm@outlook.com>
2022-11-15 09:19:08 +00:00
from files.helpers.alerts import *
from files.helpers.get import get_account
from files.helpers.casino import distribute_wager_badges
2023-03-15 05:09:46 +00:00
from files.helpers.config.const import *
class RouletteAction(str, Enum):
2023-01-01 11:36:20 +00:00
STRAIGHT_UP_BET = "STRAIGHT_UP_BET",
LINE_BET = "LINE_BET"
COLUMN_BET = "COLUMN_BET"
DOZEN_BET = "DOZEN_BET"
EVEN_ODD_BET = "EVEN_ODD_BET"
RED_BLACK_BET = "RED_BLACK_BET"
HIGH_LOW_BET = "HIGH_LOW_BET"
@property
def validation_function(self):
if self == self.__class__.STRAIGHT_UP_BET: return lambda x: x is not None and x >= 0 and x <= 37
if self == self.__class__.LINE_BET: return lambda x: x in LINES
if self == self.__class__.COLUMN_BET: return lambda x: x in COLUMNS
if self == self.__class__.DOZEN_BET: return lambda x: x in DOZENS
if self == self.__class__.EVEN_ODD_BET: return lambda x: x in [y.value for y in RouletteEvenOdd]
if self == self.__class__.RED_BLACK_BET: return lambda x: x in [y.value for y in RouletteRedBlack]
if self == self.__class__.HIGH_LOW_BET: return lambda x: x in [y.value for y in RouletteHighLow]
raise ValueError("Unhandled validation function for RouletteAction")
class RouletteEvenOdd(str, Enum):
2022-10-28 23:39:31 +00:00
EVEN = "EVEN"
ODD = "ODD"
class RouletteRedBlack(str, Enum):
2022-10-28 23:39:31 +00:00
RED = "RED"
BLACK = "BLACK"
class RouletteHighLow(str, Enum):
2022-10-28 23:39:31 +00:00
HIGH = "HIGH"
LOW = "LOW"
REDS = (1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36)
BLACKS = (2, 4, 6, 8, 10, 11, 13, 15, 17, 20, 22, 24, 26, 28, 29, 31, 33, 35)
LINES = {
2022-10-28 23:39:31 +00:00
1: (1, 2, 3, 4, 5, 6),
2: (7, 8, 9, 10, 11, 12),
3: (13, 14, 15, 16, 17, 18),
4: (19, 20, 21, 22, 23, 24),
5: (25, 26, 27, 28, 29, 30),
6: (31, 32, 33, 34, 35, 36)
}
COLUMNS = {
2022-10-28 23:39:31 +00:00
1: (1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34),
2: (2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35),
3: (3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36)
}
DOZENS = {
2022-10-28 23:39:31 +00:00
1: (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12),
2: (13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24),
3: (25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36)
}
PAYOUT_MULITPLIERS = {
2022-10-28 23:39:31 +00:00
RouletteAction.STRAIGHT_UP_BET: 35,
RouletteAction.LINE_BET: 5,
RouletteAction.COLUMN_BET: 2,
RouletteAction.DOZEN_BET: 2,
RouletteAction.EVEN_ODD_BET: 1,
RouletteAction.RED_BLACK_BET: 1,
RouletteAction.HIGH_LOW_BET: 1,
}
def get_active_roulette_games():
2023-03-15 03:58:00 +00:00
return db.query(CasinoGame).filter(
CasinoGame.active == True,
CasinoGame.kind == 'roulette'
2022-10-28 23:39:31 +00:00
).all()
def charge_gambler(gambler, amount, currency):
2022-10-28 23:39:31 +00:00
charged = gambler.charge_account(currency, amount)
2022-10-28 23:39:31 +00:00
if not charged:
raise Exception("Gambler cannot afford charge.")
def gambler_placed_roulette_bet(gambler, bet, which, amount, currency):
2022-10-28 23:39:31 +00:00
if not bet in (
RouletteAction.STRAIGHT_UP_BET,
RouletteAction.LINE_BET,
RouletteAction.COLUMN_BET,
RouletteAction.DOZEN_BET,
RouletteAction.EVEN_ODD_BET,
RouletteAction.RED_BLACK_BET,
RouletteAction.HIGH_LOW_BET
):
raise Exception(
f'Illegal bet {bet} passed to Roulette#gambler_placed_roulette_bet')
active_games = get_active_roulette_games()
if len(active_games) == 0:
parent_id = int(time.time())
else:
2022-10-30 00:32:40 +00:00
parent_id = active_games[0].game_state_json['parent_id']
2022-10-28 23:39:31 +00:00
charge_gambler(gambler, amount, currency)
game = CasinoGame()
2022-10-28 23:39:31 +00:00
game.user_id = gambler.id
game.currency = currency
game.wager = amount
game.winnings = 0
game.kind = 'roulette'
game.game_state = json.dumps(
{"parent_id": parent_id, "bet": bet, "which": which})
game.active = True
2023-03-15 03:58:00 +00:00
db.add(game)
db.commit()
def get_roulette_bets_and_betters():
2022-10-28 23:39:31 +00:00
participants = []
bets = {
RouletteAction.STRAIGHT_UP_BET: [],
RouletteAction.LINE_BET: [],
RouletteAction.COLUMN_BET: [],
RouletteAction.DOZEN_BET: [],
RouletteAction.EVEN_ODD_BET: [],
RouletteAction.RED_BLACK_BET: [],
RouletteAction.HIGH_LOW_BET: [],
}
active_games = get_active_roulette_games()
for game in active_games:
if not game.user_id in participants:
participants.append(game.user_id)
user = get_account(game.user_id)
2022-10-30 00:32:40 +00:00
game_state = game.game_state_json
2022-10-28 23:39:31 +00:00
bet = game_state['bet']
bets[bet].append({
'game_id': game.id,
'gambler': game.user_id,
'gambler_username': user.username,
'gambler_profile_url': user.profile_url,
'bet': bet,
'which': game_state['which'],
'wager': {
'amount': game.wager,
'currency': game.currency
}
})
return participants, bets, active_games
def spin_roulette_wheel():
2022-10-28 23:39:31 +00:00
participants, bets, active_games = get_roulette_bets_and_betters()
2022-10-28 23:39:31 +00:00
if len(participants) > 0:
number = randint(0, 37) # 37 is 00
2022-09-13 03:14:55 +00:00
winners, payouts, rewards_by_game_id = determine_roulette_winners(number, bets)
if number == 37: number = '00'
2022-10-28 23:39:31 +00:00
# Pay out to the winners and send a notification.
for user_id in winners:
gambler = get_account(user_id)
gambler_payout = payouts[user_id]
coin_winnings = gambler_payout['coins']
procoin_winnings = gambler_payout['marseybux']
2022-10-28 23:39:31 +00:00
gambler.pay_account('coins', coin_winnings)
gambler.pay_account('marseybux', procoin_winnings)
2022-10-28 23:39:31 +00:00
# Notify the winners.
notification_text = f"Winning number: {number}\n\nCongratulations! One or more of your roulette bets paid off!\n\n"
2022-10-28 23:39:31 +00:00
if coin_winnings > 0:
notification_text = notification_text + \
f"* You received {coin_winnings} coins.\n\n"
2022-10-28 23:39:31 +00:00
if procoin_winnings > 0:
notification_text = notification_text + \
f"* You received {procoin_winnings} marseybux.\n\n"
2022-10-28 23:39:31 +00:00
send_repeatable_notification(user_id, notification_text)
2022-10-28 23:39:31 +00:00
# Give condolences.
for participant in participants:
if not participant in winners:
send_repeatable_notification(
participant, f"Winning number: {number}\n\nSorry, none of your recent roulette bets paid off.")
2023-03-15 03:58:00 +00:00
db.flush()
2022-10-28 23:39:31 +00:00
# Adjust game winnings.
for game in active_games:
if rewards_by_game_id.get(game.id):
game.winnings = rewards_by_game_id[game.id]
else:
game.winnings = -game.wager
distribute_wager_badges(game.user, game.wager, won=(game.winnings > 0))
2022-10-28 23:39:31 +00:00
game.active = False
2023-03-15 03:58:00 +00:00
db.add(game)
2022-10-28 23:39:31 +00:00
# Commit early when dirty because of long-running tasks after roulette
2023-03-15 03:58:00 +00:00
db.commit()
def determine_roulette_winners(number, bets):
2022-10-28 23:39:31 +00:00
winners = []
payouts = {}
rewards_by_game_id = {}
2022-10-28 23:39:31 +00:00
def add_to_winnings(bet):
game_id = int(bet['game_id'])
gambler_id = bet['gambler']
wager_amount = bet['wager']['amount']
bet_kind = bet['bet']
reward = wager_amount * PAYOUT_MULITPLIERS[bet_kind]
payout = wager_amount + reward
currency = bet['wager']['currency']
2022-10-28 23:39:31 +00:00
if not gambler_id in winners:
winners.append(gambler_id)
2022-10-28 23:39:31 +00:00
if not payouts.get(gambler_id):
payouts[gambler_id] = {
'coins': 0,
'marseybux': 0
2022-10-28 23:39:31 +00:00
}
2022-10-28 23:39:31 +00:00
if not rewards_by_game_id.get(game_id):
rewards_by_game_id[game_id] = reward
2022-10-28 23:39:31 +00:00
payouts[gambler_id][currency] += payout
2022-10-28 23:39:31 +00:00
# Straight-Up Bet
for bet in bets[RouletteAction.STRAIGHT_UP_BET]:
if int(bet['which']) == number:
add_to_winnings(bet)
if number == 0 or number == 37:
return winners, payouts, rewards_by_game_id
2022-11-21 21:51:47 +00:00
2022-10-28 23:39:31 +00:00
# Line Bet
line = -1
for i in range(1, 7):
if number in LINES[i]:
line = i
2022-10-28 23:39:31 +00:00
for bet in bets[RouletteAction.LINE_BET]:
if int(bet['which']) == line:
add_to_winnings(bet)
2022-10-28 23:39:31 +00:00
# Column Bet
column = -1
for i in range(1, 4):
if number in COLUMNS[i]:
column = i
2022-10-28 23:39:31 +00:00
for bet in bets[RouletteAction.COLUMN_BET]:
if int(bet['which']) == column:
add_to_winnings(bet)
2022-10-28 23:39:31 +00:00
# Dozen Bet
dozen = -1
for i in range(1, 4):
if number in DOZENS[i]:
dozen = i
2022-10-28 23:39:31 +00:00
for bet in bets[RouletteAction.DOZEN_BET]:
if int(bet['which']) == dozen:
add_to_winnings(bet)
2022-10-28 23:39:31 +00:00
# Even/Odd Bet
even_odd = RouletteEvenOdd.EVEN if number % 2 == 0 else RouletteEvenOdd.ODD
2022-10-28 23:39:31 +00:00
for bet in bets[RouletteAction.EVEN_ODD_BET]:
if bet['which'] == even_odd:
add_to_winnings(bet)
2022-10-28 23:39:31 +00:00
# Red/Black Bet
red_black = RouletteRedBlack.RED if number in REDS else RouletteRedBlack.BLACK
2022-10-28 23:39:31 +00:00
for bet in bets[RouletteAction.RED_BLACK_BET]:
if bet['which'] == red_black:
add_to_winnings(bet)
2022-10-28 23:39:31 +00:00
# High/Low Bet
high_low = RouletteHighLow.HIGH if number > 18 else RouletteHighLow.LOW
2022-10-28 23:39:31 +00:00
for bet in bets[RouletteAction.HIGH_LOW_BET]:
if bet['which'] == high_low:
add_to_winnings(bet)
2022-10-28 23:39:31 +00:00
return winners, payouts, rewards_by_game_id
def get_roulette_bets():
2022-10-28 23:39:31 +00:00
return get_roulette_bets_and_betters()[1]