tippero/tipbot/modules/pinata.py
2015-02-14 12:15:49 +00:00

194 lines
8.1 KiB
Python

#!/bin/python
#
# Cryptonote tipbot - pinata commands
# 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 sys
import os
import redis
import hashlib
import time
import string
import random
import math
import tipbot.config as config
from tipbot.log import log_error, log_warn, log_info, log_log
import tipbot.coinspecs as coinspecs
from tipbot.user import User
from tipbot.link import Link
from tipbot.utils import *
from tipbot.command_manager import *
from tipbot.redisdb import *
from tipbot.betutils import *
def GetTarget(units):
umod = long(config.pinata_target_increment * coinspecs.atomic_units + 0.5)
units = long(float(units)+0.5)
units -= units % umod
return units
def PreparePinata(reset=False,units=None):
p=redis_pipeline()
if reset or not redis_hexists('pinata','target'):
target=GetTarget((config.pinata_base_target+config.pinata_target_increment*random.randint(0,config.pinata_num_increments))*coinspecs.atomic_units)
log_log('PreparePinata: target %s' % target)
p.hset('pinata','target',target)
if reset or not redis_hexists('pinata','units'):
units=long(units or config.pinata_start_amount*coinspecs.atomic_units)
if units < config.pinata_start_amount*coinspecs.atomic_units:
units = long(config.pinata_start_amount*coinspecs.atomic_units)
p.hset('pinata','units',units)
p.hincrby('pinata','profit',-units)
p.hincrby('earmarked','pinata',units)
p.execute()
def Pinata(link,cmd):
# Make sure there is always one
PreparePinata()
identity=link.identity()
group=link.group
if not group:
link.send("There is no pinata around, they only appear in IRC channels")
return
try:
pinata_units = long(redis_hget('pinata','units'))
except Exception,e:
log_error('Failed to get pinata units: %s' % str(e))
link.send('An error occured')
return
min_target=GetTarget(config.pinata_base_target*coinspecs.atomic_units)
max_target=GetTarget((config.pinata_base_target+(config.pinata_num_increments-1)*config.pinata_target_increment)*coinspecs.atomic_units)
if not GetParam(cmd,1):
min_win_units = (pinata_units + config.pinata_base_target*coinspecs.atomic_units) * config.pinata_winner_base_share
link.send("The pinata is filled with %s, and can be hit with %s - %s (in increments of %s) - min win %s" % (AmountToString(pinata_units),AmountToString(min_target),AmountToString(max_target),AmountToString(config.pinata_target_increment*coinspecs.atomic_units),AmountToString(min_win_units)))
return
try:
amount=float(cmd[1])
units=long(amount*coinspecs.atomic_units+0.5)
aim = GetTarget(units)
except Exception,e:
link.send("Usage: !pinata <amount>")
return
if aim < min_target:
link.send("The pinata can only be hit by at least %s" % AmountToString(min_target))
return
if aim > max_target:
link.send("The pinata can only be hit by at most %s" % AmountToString(max_target))
return
try:
target = long(redis_hget('pinata','target'))
except Exception,e:
log_error('Failed to get pinata target: %s' % str(e))
link.send('An error occured')
return
if target < min_target or target > max_target:
log_error('Pinata target out of range: %s' % target)
link.send('An error occured')
return
account = GetAccount(identity)
log_info("Pinata: %s wants to swing %s at the pinata, aim is %d, target is %d" % (identity, amount, aim, target))
valid,reason = IsBetValid(link,amount,None,None,0,0,0)
if not valid:
log_info("Pinata: %s's bet refused: %s" % (identity, reason))
link.send("%s: %s" % (link.user.nick, reason))
return
try:
if target==aim:
log_info("Pinata: %s hits the pinata containing %s" % (identity, AmountToString(pinata_units)))
winner_ratio = config.pinata_winner_base_share * aim / min_target
rain_ratio = (1-winner_ratio) * config.pinata_rain_remainder_share
carry_ratio = (1-winner_ratio) * config.pinata_carry_remainder_share
winner_units = long(pinata_units * winner_ratio)
rain_units = long(pinata_units * rain_ratio)
carry_units = long(pinata_units * carry_ratio)
profit_units = pinata_units - winner_units - rain_units
log_log("Pinata: %s to winner, %s to rain, %s carry" % (AmountToString(winner_units),AmountToString(rain_units),AmountToString(carry_units)))
p=redis_pipeline()
p.hincrby('earmarked','pinata',-pinata_units)
p.hincrby('balances',account,winner_units)
link.send('%s swings at the pinata with %s and hits!' % (link.user.nick,AmountToString(units)))
link.send('%s gets splashed by %s' % (link.user.nick,AmountToString(winner_units)))
userlist=link.network.get_users(group.name)
log_log("users in %s: %s" % (group.name,str([user.identity() for user in userlist])))
userlist.remove(link)
for n in config.no_rain_to_nicks:
i=IdentityFromString(link,n)
l=Link(link.network,User(link.network,NickFromIdentity(i)),group)
if l in userlist:
userlist.remove(l)
if len(userlist) > 0:
user_units = long(rain_units / len(userlist))
log_log("The pinata rains %s on: %s" % (AmountToString(user_units),str([user.identity() for user in userlist])))
link.send('%s rains on everyone else' % (AmountToString(user_units)))
for n in userlist:
a = GetAccount(n)
p.hincrby('balances',a,user_units)
p.hincrby('pinata','games',1)
p.hincrby('pinata','profit',profit_units)
p.execute()
PreparePinata(True,carry_units)
link.send('A new pinata appears filled with %s!' % AmountToString(carry_units))
else:
p=redis_pipeline()
p.hincrby('balances',account,-units)
p.hincrby('pinata','units',units)
p.hincrby('earmarked','pinata',units)
p.execute()
link.send('%s swings at the pinata with %s and misses! The pinata now contains %s' % (link.user.nick,AmountToString(units),AmountToString(pinata_units+units)))
except Exception,e:
log_error('Pinata: error: %s' % str(e))
link.send('An error occured')
return
def PinataHelp(link):
link.send_private("A pinata full of %s is floating in the air. Swing some %s at it!" % (coinspecs.name,coinspecs.name))
link.send_private("This pinata can only be smashed by a secret amount of %s," % (coinspecs.name))
min_target=GetTarget(config.pinata_base_target*coinspecs.atomic_units)
max_target=GetTarget((config.pinata_base_target+(config.pinata_num_increments-1)*config.pinata_target_increment)*coinspecs.atomic_units)
link.send_private("between %s and %s (inclusive), in %s increments" % (AmountToString(min_target),AmountToString(max_target),AmountToString(config.pinata_target_increment*coinspecs.atomic_units)))
min_winner_share=config.pinata_winner_base_share
max_winner_share=config.pinata_winner_base_share*(config.pinata_base_target+(config.pinata_num_increments-1)*config.pinata_target_increment)/config.pinata_base_target
link.send_private("If you hit with the secret amount, it breaks and you get %u%% - %u%% of the %s in it," % (100*min_winner_share,100*max_winner_share,coinspecs.name))
link.send_private("%u%% of the rest rains down on others in the channel, and %u%% are placed" % (100*config.pinata_rain_remainder_share,100*config.pinata_carry_remainder_share))
link.send_private("in a new pinata. If you miss it, your %s end up in the pinata," % (coinspecs.name))
link.send_private("increasing the bounty for next attempt")
link.send_private("The winner's share is proportional to the amount of %s on the winning hit," % coinspecs.name)
link.send_private("so you don't lose out by trying higher amounts")
link.send_private("Remember, only one particular amount will manage to smash the pinata, try to find it!")
random.seed(time.time())
RegisterModule({
'name': __name__,
'help': PinataHelp,
})
RegisterCommand({
'module': __name__,
'name': 'pinata',
'parms': '<amount-in-monero>',
'function': Pinata,
'registered': True,
'help': "swing some %s at the pinata" % (coinspecs.name)
})