tippero/tipbot/betutils.py
moneromooo 74c01f9bce Add a reserve_balance admin command
And move RetrieveHouseBalance to betutils as a side effect
2015-01-18 11:41:32 +00:00

263 lines
8.2 KiB
Python

#!/bin/python
#
# Cryptonote tipbot - bet utils
# Copyright 2015 moneromooo
#
# The Cryptonote tipbot is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
import random
import hashlib
import time
import tipbot.coinspecs as coinspecs
from tipbot.command_manager import *
from utils import *
def IsBetAmountValid(amount,minbet,maxbet,potential_loss,max_loss,max_loss_ratio):
try:
amount = float(amount)
except Exception,e:
return False, "Invalid amount"
units=long(amount*coinspecs.atomic_units)
if units <= 0:
return False, "Invalid amount"
if amount > maxbet:
return False, "Max bet is %s" % AmountToString(maxbet * coinspecs.atomic_units)
if amount < minbet:
return False, "Min bet is %s" % AmountToString(minbet * coinspecs.atomic_units)
if potential_loss > max_loss:
return False, "Max potential loss is %s" % AmountToString(max_loss * coinspecs.atomic_units)
try:
house_balance = RetrieveHouseBalance()
except Exception,e:
log_error('Failed to get house balance: %s' % str(e))
return False, "Failed to get house balance"
max_floating_loss = max_loss_ratio * house_balance / coinspecs.atomic_units
if potential_loss > max_floating_loss:
return False, "Potential loss too large for house balance"
return True, None
def IsPlayerBalanceAtLeast(nick,units):
try:
balance = redis_hget("balances",nick)
if balance == None:
balance = 0
balance=long(balance)
if units > balance:
log_error ('%s does not have enough balance' % nick)
return False, "You only have %s" % (AmountToString(balance))
except Exception,e:
log_error ('failed to query balance')
return False, "Failed to query balance"
return True, None
def SetServerSeed(nick,game,seed):
try:
redis_hset('%s:serverseed' % game,nick,seed)
log_info('%s\'s %s server seed set' % (nick, game))
except Exception,e:
log_error('Failed to set %s server seed for %s: %s' % (game, nick, str(e)))
raise
def GetServerSeed(nick,game):
EnsureServerSeed(nick,game)
try:
return redis_hget('%s:serverseed' % game,nick)
except Exception,e:
log_error('Failed to get %s server seed for %s: %s' % (game, nick, str(e)))
raise
def GenerateServerSeed(nick,game):
try:
salt="kfn3kjg4nkngvekjvn3u4vgb" + ":" + game
s=salt+":"+nick+":"+str(time.time())+":"+str(random.randint(0,1000000))
seed=hashlib.sha256(s).hexdigest()
SetServerSeed(nick,game,seed)
except Exception,e:
log_error('Failed to generate %s server seed for %s: %s' % (game,nick,str(e)))
raise
def EnsureServerSeed(nick,game):
if not redis_hexists('%s:serverseed' % game,nick):
GenerateServerSeed(nick,game)
def SetPlayerSeed(nick,game,seed):
try:
redis_hset('%s:playerseed' % game,nick,seed)
log_info('%s\'s %s playerseed set to %s' % (nick, game, seed))
except Exception,e:
log_error('Failed to set %s player seed for %s: %s' % (game, nick, str(e)))
raise
def GetPlayerSeed(nick,game):
try:
if not redis_hexists('%s:playerseed' % game,nick):
return ""
return redis_hget('%s:playerseed' % game,nick)
except Exception,e:
log_error('Failed to get %s player seed for %s: %s' % (game, nick, str(e)))
raise
def GetServerSeedHash(nick,game):
seed = GetServerSeed(nick,game)
return hashlib.sha256(seed).hexdigest()
def RecordGameResult(nick,chan,game,win,lose,units):
try:
p = redis_pipeline()
tname="%s:stats:"%game+nick
rtname="%s:stats:reset:"%game+nick
alltname="%s:stats:"%game
p.hincrby(tname,"bets",1)
p.hincrby(rtname,"bets",1)
p.hincrby(alltname,"bets",1)
p.hincrby(tname,"wagered",units)
p.hincrby(rtname,"wagered",units)
p.hincrby(alltname,"wagered",units)
if win:
p.hincrby("balances",nick,units)
p.hincrby(tname,"won",units)
p.hincrby(rtname,"won",units)
p.hincrby(alltname,"won",units)
p.hincrby(tname,"nwon",1)
p.hincrby(rtname,"nwon",1)
p.hincrby(alltname,"nwon",1)
if lose:
p.hincrby("balances",nick,-units)
p.hincrby(tname,"lost",units)
p.hincrby(rtname,"lost",units)
p.hincrby(alltname,"lost",units)
p.hincrby(tname,"nlost",1)
p.hincrby(rtname,"nlost",1)
p.hincrby(alltname,"nlost",1)
p.execute()
except Exception,e:
log_error('RecordGameResult: exception updating redis: %s' % str(e))
raise
def ShowGameStats(sendto,snick,title,game):
tname="%s:stats:"%game+snick
try:
bets=redis_hget(tname,"bets")
wagered=redis_hget(tname,"wagered")
won=redis_hget(tname,"won")
lost=redis_hget(tname,"lost")
nwon=redis_hget(tname,"nwon")
nlost=redis_hget(tname,"nlost")
except Exception,e:
log_error('Failed to retrieve %s stats for %s: %s' % (game, title, str(e)))
SendTo(sendto,"An error occured")
return
if not bets:
SendTo(sendto,"No %s stats available for %s" % (game,title))
return
bets = long(bets)
wagered = long(wagered)
won = long(won or 0)
lost = long(lost or 0)
nwon = long(nwon or 0)
nlost = long(nlost or 0)
if bets==0:
SenTo(sendto,"No %s stats available for %s" % (game,title))
return
swagered = AmountToString(wagered)
savg = AmountToString(wagered / bets)
swon = AmountToString(won)
slost = AmountToString(lost)
if won >= lost:
sov = "+" + AmountToString(won-lost)
else:
sov = "-" + AmountToString(lost-won)
SendTo(sendto,"%s: %d games %d won, %d lost, %s wagered (average %s per game), %s won, %s lost, overall %s" % (title, bets, nwon, nlost, swagered, savg, swon, slost, sov))
def ResetGameStats(sendto,snick,game):
try:
p = redis_pipeline()
tname="%s:stats:reset:"%game+snick
bets=p.hset(tname,"bets",0)
wagered=p.hset(tname,"wagered",0)
won=p.hset(tname,"won",0)
lost=p.hset(tname,"lost",0)
nwon=p.hset(tname,"nwon",0)
nlost=p.hset(tname,"nlost",0)
p.execute()
SendTo(sendto,"%s stats reset for %s" % (game,snick))
except Exception,e:
log_error('Error resetting %s stats for %s: %s' % (game,snick,str(e)))
raise
def RetrieveHouseBalance():
balance, unlocked_balance = RetrieveTipbotBalance()
nicks = redis_hgetall("balances")
for nick in nicks:
nb = redis_hget("balances", nick)
unlocked_balance = unlocked_balance - long(nb)
log_log('RetrieveHouseBalance: subtracting %s from %s to give %s' % (AmountToString(nb), nick, AmountToString(unlocked_balance)))
rbal=redis_get('reserve_balance')
if rbal:
unlocked_balance = unlocked_balance - long(rbal)
if unlocked_balance < 0:
raise RuntimeError('Negative house balance')
return
return unlocked_balance
def ReserveBalance(nick,chan,cmd):
sendto=GetSendTo(nick,chan)
rbal=GetParam(cmd,1)
if rbal:
try:
rbal=float(cmd[1])
if rbal < 0:
raise RuntimeError('negative balance')
rbal = long(rbal * coinspecs.atomic_units)
except Exception,e:
log_error('SetReserveBalance: invalid balance: %s' % str(e))
SendTo(sendto,"Invalid balance")
return
try:
current_rbal=long(redis_get('reserve_balance') or 0)
except Exception,e:
log_error('Failed to get current reserve balance: %s' % str(e))
SendTo(sendto,"Failed to get current reserve balance")
return
if rbal == None:
SendTo(sendto,"Reserve balance is %s" % AmountToString(current_rbal))
return
try:
house_balance = RetrieveHouseBalance()
except Exception,e:
log_error('Failed to get house balance: %s' % str(e))
SendTo(sendto,"Failed to get house balance")
return
if rbal > house_balance + current_rbal:
log_error('Cannot set reserve balance higher than max house balance')
SendTo(sendto,'Cannot set reserve balance higher than max house balance')
return
try:
redis_set('reserve_balance',rbal)
except Exception,e:
log_error('Failed to set reserve balance: %s' % str(e))
SendTo(sendto,"Failed to set reserve balance")
return
SendTo(sendto,"Reserve balance set")
RegisterCommand({
'module': 'betting',
'name': 'reserve_balance',
'parms': '[<amount>]',
'function': ReserveBalance,
'admin': True,
'help': "Set or get reserve balance (not part of the house balance)"
})