Abstract away communications networks

This commit is contained in:
moneromooo 2015-01-13 12:28:05 +00:00
parent 62c2adbaf6
commit ea25e9d41b
16 changed files with 1249 additions and 983 deletions

View file

@ -20,95 +20,93 @@ import tipbot.config as config
from tipbot.log import log_error, log_warn, log_info, log_log
import tipbot.coinspecs as coinspecs
from tipbot.utils import *
from tipbot.ircutils import *
from tipbot.command_manager import *
from tipbot.redisdb import *
from tipbot.betutils import *
def GetHouseBalance(nick,chan,cmd):
sendto=GetSendTo(nick,chan)
def GetHouseBalance(link,cmd):
try:
balance = RetrieveHouseBalance()
except Exception,e:
log_error('Failed to retrieve house balance: %s' % str(e))
SendTo(sendto, 'An error occured')
link.send('An error occured')
return
SendTo(sendto, 'House balance: %s' % AmountToString(balance))
link.send('House balance: %s' % AmountToString(balance))
def Roll(nick):
def Roll(link):
identity=link.identity()
try:
if redis_hexists('dice:rolls',nick):
rolls = redis_hget('dice:rolls',nick)
if redis_hexists('dice:rolls',identity):
rolls = redis_hget('dice:rolls',identity)
rolls = long(rolls) + 1
else:
rolls = 1
except Exception,e:
log_error('Failed to prepare roll for %s: %s' % (nick, str(e)))
log_error('Failed to prepare roll for %s: %s' % (identity, str(e)))
raise
try:
s = GetServerSeed(nick,'dice') + ":" + GetPlayerSeed(nick,'dice') + ":" + str(rolls)
s = GetServerSeed(link,'dice') + ":" + GetPlayerSeed(link,'dice') + ":" + str(rolls)
sh = hashlib.sha256(s).hexdigest()
roll = float(long(sh[0:8],base=16))/0x100000000
return rolls, roll
except Exception,e:
log_error('Failed to roll for %s: %s' % (nick,str(e)))
log_error('Failed to roll for %s: %s' % (identity,str(e)))
raise
def Dice(nick,chan,cmd):
sendto=GetSendTo(nick,chan)
def Dice(link,cmd):
identity=link.identity()
try:
amount=float(cmd[1])
units=long(amount*coinspecs.atomic_units)
multiplier = float(cmd[2])
overunder=GetParam(cmd,3)
except Exception,e:
SendTo(sendto, "Usage: dice amount multiplier [over|under]")
link.send("Usage: dice amount multiplier [over|under]")
return
if multiplier < 0.1 or multiplier > 10:
SendTo(sendto, "Invalid multiplier: should be between 0.1 and 10")
link.send("Invalid multiplier: should be between 0.1 and 10")
return
if overunder == "over":
under=False
elif overunder == "under" or not overunder:
under=True
else:
SendTo(sendto, "Usage: dice amount multiplier [over|under]")
link.send("Usage: dice amount multiplier [over|under]")
return
log_info("Dice: %s wants to bet %s at x%f, %s target" % (nick, AmountToString(units), multiplier, "under" if under else "over"))
log_info("Dice: %s wants to bet %s at x%f, %s target" % (identity, AmountToString(units), multiplier, "under" if under else "over"))
potential_loss = amount * multiplier
valid,reason = IsBetAmountValid(amount,config.dice_min_bet,config.dice_max_bet,potential_loss,config.dice_max_loss,config.dice_max_loss_ratio)
if not valid:
log_info("Dice: %s's bet refused: %s" % (nick, reason))
SendTo(sendto, "%s: %s" % (nick, reason))
log_info("Dice: %s's bet refused: %s" % (identity, reason))
link.send("%s: %s" % (link.user.nick, reason))
return
try:
balance = redis_hget("balances",nick)
balance = redis_hget("balances",identity)
if balance == None:
balance = 0
balance=long(balance)
if units > balance:
log_error ('%s does not have enough balance' % nick)
SendTo(sendto, "You only have %s" % (AmountToString(balance)))
log_error ('%s does not have enough balance' % identity)
link.send("You only have %s" % (AmountToString(balance)))
return
except Exception,e:
log_error ('failed to query balance')
SendTo(sendto, "Failed to query balance")
link.send("Failed to query balance")
return
try:
rolls, roll = Roll(nick)
rolls, roll = Roll(link)
except:
SendTo(sendto,"An error occured")
link.send("An error occured")
return
target = (1 - config.dice_edge) / (1+multiplier)
if not under:
target = 1 - target
log_info("Dice: %s's #%d roll: %.16g, target %s %.16g" % (nick, rolls, roll, "under" if under else "over", target))
log_info("Dice: %s's #%d roll: %.16g, target %s %.16g" % (identity, rolls, roll, "under" if under else "over", target))
lose_units = units
win_units = long(units * multiplier)
@ -118,129 +116,140 @@ def Dice(nick,chan,cmd):
else:
win = roll >= target
if win:
msg = "%s wins %s on roll #%d! %.16g %s %.16g" % (nick, AmountToString(win_units), rolls, roll, "<=" if under else ">=", target)
msg = "%s wins %s on roll #%d! %.16g %s %.16g" % (link.user.nick, AmountToString(win_units), rolls, roll, "<=" if under else ">=", target)
else:
msg = "%s loses %s on roll #%d. %.16g %s %.16g" % (nick, AmountToString(lose_units), rolls, roll, ">" if under else "<", target)
msg = "%s loses %s on roll #%d. %.16g %s %.16g" % (link.user.nick, AmountToString(lose_units), rolls, roll, ">" if under else "<", target)
try:
RecordGameResult(nick,chan,"dice",win,not win,win_units if win else lose_units)
RecordGameResult(link,"dice",win,not win,win_units if win else lose_units)
except:
return
redis_hset("dice:rolls",nick,rolls)
redis_hset("dice:rolls",identity,rolls)
SendTo(sendto, "%s" % msg)
link.send("%s" % msg)
def ShowDiceStats(sendto,snick,title):
return ShowGameStats(sendto,snick,title,"dice")
def ShowDiceStats(link,sidentity,title):
return ShowGameStats(link,sidentity,title,"dice")
def GetDiceStats(nick,chan,cmd):
sendto=GetSendTo(nick,chan)
snick = GetParam(cmd,1)
if snick and snick != nick:
if not IsAdmin(nick):
log_error('%s is not admin, cannot see dice stats for %s' % (nick, snick))
SendTo(sendto,'Access denied')
def GetDiceStats(link,cmd):
identity=link.identity()
sidentity = GetParam(cmd,1)
if sidentity:
sidentity=IdentityFromString(link,sidentity)
if sidentity and sidentity != identity:
if not IsAdmin(link):
log_error('%s is not admin, cannot see dice stats for %s' % (identity, sidentity))
link.send('Access denied')
return
else:
snick=nick
ShowDiceStats(sendto,snick,snick)
ShowDiceStats(sendto,"reset:"+snick,'%s since reset' % snick)
ShowDiceStats(sendto,'','overall')
sidentity=identity
ShowDiceStats(link,sidentity,sidentity)
ShowDiceStats(link,"reset:"+sidentity,'%s since reset' % sidentity)
ShowDiceStats(link,'','overall')
def ResetDiceStats(nick,chan,cmd):
sendto=GetSendTo(nick,chan)
snick = GetParam(cmd,1)
if snick and snick != nick:
if not IsAdmin(nick):
log_error('%s is not admin, cannot see dice stats for %s' % (nick, snick))
SendTo(sendto,'Access denied')
def ResetDiceStats(link,cmd):
identity=link.identity()
sidentity = GetParam(cmd,1)
if sidentity:
sidentity=IdentityFromString(link,sidentity)
if sidentity and sidentity != identity:
if not IsAdmin(link):
log_error('%s is not admin, cannot see dice stats for %s' % (identity, sidentity))
link.send('Access denied')
return
else:
snick=nick
sidentity=identity
try:
ResetGameStats(sendto,snick,"dice")
ResetGameStats(link,sidentity,"dice")
except Exception,e:
SendTo(sendto,"An error occured")
link.send("An error occured")
def PlayerSeed(nick,chan,cmd):
sendto=GetSendTo(nick,chan)
def PlayerSeed(link,cmd):
identity=link.identity()
fair_string = GetParam(cmd,1)
if not fair_string:
SendTo(nick, "Usage: !playerseed <string>")
link.send("Usage: !playerseed <string>")
return
try:
SetPlayerSeed(nick,'dice',fair_string)
SetPlayerSeed(link,'dice',fair_string)
except Exception,e:
log_error('Failed to save player seed for %s: %s' % (nick, str(e)))
SendTo(sendto, 'An error occured')
log_error('Failed to save player seed for %s: %s' % (identity, str(e)))
link.send('An error occured')
try:
ps = GetPlayerSeed(link,'dice')
except Exception,e:
log_error('Failed to retrieve newly set player seed for %s: %s' % (identity, str(e)))
link.send('An error occured')
return
link.send('Your new player seed is: %s' % ps)
def FairCheck(nick,chan,cmd):
sendto=GetSendTo(nick,chan)
def FairCheck(link,cmd):
identity=link.identity()
try:
seed = GetServerSeed(nick,'dice')
seed = GetServerSeed(link,'dice')
except Exception,e:
log_error('Failed to get server seed for %s: %s' % (nick,str(e)))
SendTo(seed,'An error has occured')
log_error('Failed to get server seed for %s: %s' % (identity,str(e)))
link.send('An error has occured')
return
try:
GenerateServerSeed(nick,'dice')
GenerateServerSeed(link,'dice')
except Exception,e:
log_error('Failed to generate server seed for %s: %s' % (nick,str(e)))
SendTo(seed,'An error has occured')
log_error('Failed to generate server seed for %s: %s' % (identity,str(e)))
link.send('An error has occured')
return
SendTo(sendto, 'Your server seed was %s - it has now been reset; see !fair for details' % str(seed))
link.send('Your server seed was %s - it has now been reset; see !fair for details' % str(seed))
def Seeds(nick,chan,cmd):
sendto=GetSendTo(nick,chan)
def Seeds(link,cmd):
identity=link.identity()
try:
sh = GetServerSeedHash(nick,'dice')
ps = GetPlayerSeed(nick,'dice')
sh = GetServerSeedHash(link,'dice')
ps = GetPlayerSeed(link,'dice')
except Exception,e:
log_error('Failed to get server seed for %s: %s' % (nick,str(e)))
SendTo(seed,'An error has occured')
log_error('Failed to get server seed for %s: %s' % (identity,str(e)))
link.send('An error has occured')
return
SendTo(sendto, 'Your server seed hash is %s' % str(sh))
link.send('Your server seed hash is %s' % str(sh))
if ps == "":
SendTo(sendto, 'Your have not set a player seed')
link.send('Your have not set a player seed')
else:
SendTo(sendto, 'Your player seed hash is %s' % str(ps))
link.send('Your player seed hash is %s' % str(ps))
def Fair(nick,chan,cmd):
SendTo(nick,"%s's dice betting is provably fair" % config.tipbot_name)
SendTo(nick,"Your rolls are determined by three pieces of information:")
SendTo(nick," - your server seed. You can see its hash with !seeds")
SendTo(nick," - your player seed. Empty by default, you can set it with !playerseed")
SendTo(nick," - the roll number, displayed with each bet you make")
SendTo(nick,"To verify past rolls were fair, use !faircheck")
SendTo(nick,"You will be given your server seed, and a new one will be generated")
SendTo(nick,"for future rolls. Then follow these steps:")
SendTo(nick,"Calculate the SHA-256 sum of serverseed:playerseed:rollnumber")
SendTo(nick,"Take the first 8 characters of this sum to make an hexadecimal")
SendTo(nick,"number, and divide it by 0x100000000. You will end up with a number")
SendTo(nick,"between 0 and 1 which was your roll for that particular bet")
SendTo(nick,"See !faircode for Python code implementing this check")
def Fair(link,cmd):
link.send("%s's dice betting is provably fair" % config.tipbot_name)
link.send("Your rolls are determined by three pieces of information:")
link.send(" - your server seed. You can see its hash with !seeds")
link.send(" - your player seed. Empty by default, you can set it with !playerseed")
link.send(" - the roll number, displayed with each bet you make")
link.send("To verify past rolls were fair, use !faircheck")
link.send("You will be given your server seed, and a new one will be generated")
link.send("for future rolls. Then follow these steps:")
link.send("Calculate the SHA-256 sum of serverseed:playerseed:rollnumber")
link.send("Take the first 8 characters of this sum to make an hexadecimal")
link.send("number, and divide it by 0x100000000. You will end up with a number")
link.send("between 0 and 1 which was your roll for that particular bet")
link.send("See !faircode for Python code implementing this check")
def FairCode(nick,chan,cmd):
SendTo(nick,"This Python 2 code takes the seeds and roll number and outputs the roll")
SendTo(nick,"for the corresponding game. Run it with three arguments: server seed,")
SendTo(nick,"player seed (use '' if you did not set any), and roll number.")
def FairCode(link,cmd):
link.send("This Python 2 code takes the seeds and roll number and outputs the roll")
link.send("for the corresponding game. Run it with three arguments: server seed,")
link.send("player seed (use '' if you did not set any), and roll number.")
SendTo(nick,"import sys,hashlib,random")
SendTo(nick,"try:")
SendTo(nick," s=hashlib.sha256(sys.argv[1]+':'+sys.argv[2]+':'+sys.argv[3]).hexdigest()")
SendTo(nick," roll = float(long(s[0:8],base=16))/0x100000000")
SendTo(nick," print '%.16g' % roll")
SendTo(nick,"except:")
SendTo(nick," print 'need serverseed, playerseed, and roll number'")
link.send("import sys,hashlib,random")
link.send("try:")
link.send(" s=hashlib.sha256(sys.argv[1]+':'+sys.argv[2]+':'+sys.argv[3]).hexdigest()")
link.send(" roll = float(long(s[0:8],base=16))/0x100000000")
link.send(" print '%.16g' % roll")
link.send("except:")
link.send(" print 'need serverseed, playerseed, and roll number'")
def DiceHelp(nick,chan):
SendTo(nick,"The dice module is a provably fair %s dice betting game" % coinspecs.name)
SendTo(nick,"Basic usage: !dice <amount> <multiplier> [over|under]")
SendTo(nick,"The goal is to get a roll under (or over, at your option) a target that depends")
SendTo(nick,"on your chosen profit multiplier (1 for even money)")
SendTo(nick,"See !fair and !faircode for a description of the provable fairness of the game")
SendTo(nick,"See !faircheck to get the server seed to check past rolls were fair")
def DiceHelp(link):
link.send("The dice module is a provably fair %s dice betting game" % coinspecs.name)
link.send("Basic usage: !dice <amount> <multiplier> [over|under]")
link.send("The goal is to get a roll under (or over, at your option) a target that depends")
link.send("on your chosen profit multiplier (1 for even money)")
link.send("See !fair and !faircode for a description of the provable fairness of the game")
link.send("See !faircheck to get the server seed to check past rolls were fair")

View file

@ -0,0 +1,54 @@
#!/bin/python
#
# Cryptonote tipbot - Freenode
# 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 string
import tipbot.config as config
from tipbot.log import log_error, log_warn, log_info, log_log
from tipbot.utils import *
from tipbot.command_manager import *
from irc import *
class FreenodeNetwork(IRCNetwork):
def __init__(self):
IRCNetwork.__init__(self,"freenode")
def login(self):
self.send_to("nickserv", "IDENTIFY %s" % self.password)
def identify(self,link):
nick = link.user.nick
log_info('Asking nickserv whether %s is identified' % nick)
self.send_to('nickserv', "ACC " + nick)
def on_notice(self,who,text):
if who == "NickServ!NickServ@services.":
if text.find(' ACC ') != -1:
stext = text.split(' ')
ns_nick = stext[0]
ns_acc = stext[1]
ns_status = stext[2]
if ns_acc == "ACC":
ns_link=Link(self,User(self,ns_nick),None)
if ns_status == "3":
log_info('NickServ says %s is identified' % ns_nick)
self.registered_users.add(ns_link.identity())
if self.on_identified:
self.on_identified(ns_link,True)
else:
log_info('NickServ says %s is not identified' % ns_nick)
self.registered_users.discard(ns_link.identity())
if self.on_identified:
self.on_identified(ns_link,False)
else:
log_error('ACC line not as expected...')
return True

View file

@ -1,7 +1,7 @@
#!/bin/python
#
# Cryptonote tipbot - IRC commands
# Copyright 2014 moneromooo
# 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
@ -10,40 +10,498 @@
#
import sys
import socket
import ssl
import select
import time
import string
import base64
import re
import tipbot.config as config
from tipbot.log import log_error, log_warn, log_info, log_log
from tipbot.log import log, log_error, log_warn, log_info, log_log
from tipbot.utils import *
from tipbot.ircutils import *
from tipbot.network import *
from tipbot.command_manager import *
def JoinChannel(nick,chan,cmd):
sendto=GetSendTo(nick,chan)
irc_min_send_delay = 0.05 # seconds
irc_max_send_delay = 1.1 # seconds
def GetNick(data): # Return Nickname
nick = data.split('!')[0]
nick = nick.replace(':', ' ')
nick = nick.replace(' ', '')
nick = nick.strip(' \t\n\r')
return nick
class IRCNetwork(Network):
def __init__(self,name):
Network.__init__(self,name)
self.userstable = dict()
self.registered_users = set()
self.last_send_time=0
self.last_ping_time=0
self.current_send_delay = irc_min_send_delay
self.quitting = False
self.buffered_data = ""
def connect(self,host,port,login,password,delay):
return self._connect(host,port,login,password,delay)
def disconnect(self):
self._irc_sendmsg ('QUIT')
if self.sslirc:
self.sslirc.close()
self.sslirc = None
self.irc.close()
self.irc = None
def send_to(self,where,msg):
for line in msg.split("\n"):
line=line.strip('\r')
if len(line)>0:
self._irc_sendmsg('PRIVMSG '+where+' :'+line)
def send_group(self,group,msg,data=None):
self.send_to(group.name,msg)
def send_user(self,user,msg,data=None):
self.send_to(user.nick,msg)
def is_identified(self,link):
return link.identity() in self.registered_users
def join(self,chan):
self._irc_sendmsg('JOIN '+chan)
def part(self,chan):
self._irc_sendmsg('PART '+chan)
def quit(self,msg=None):
self.quitting = True
if msg:
self._irc_sendmsg('QUIT%s '%msg)
else:
self._irc_sendmsg('QUIT')
def dump_users(self):
log_info('users on %s: %s' % (self.name,str(self.userstable)))
def update_users_list(self,chan):
if chan:
self._irc_sendmsg('WHO '+chan)
def update_last_active_time(self,chan,nick):
if chan[0] != '#':
return
if not chan in self.userstable:
log_error("IRCNetwork:update_last_active_time: %s spoke in %s, but %s not found in users table" % (nick, chan, chan))
self.userstable[chan] = dict()
if not nick in self.userstable[chan]:
log_error("IRCNetwork:update_last_active_time: %s spoke in %s, but was not found in that channel's users table" % (nick, chan))
self.userstable[chan][nick] = None
self.userstable[chan][nick] = time.time()
def get_last_active_time(self,nick,chan):
if not chan in self.userstable:
log_error("IRCNetwork:get_last_active_time: channel %s not found in users table" % chan)
return None
if not nick in self.userstable[chan]:
log_error("IRCNetwork:get_last_active_time: %s not found in channel %s's users table" % (nick, chan))
return None
return self.userstable[chan][nick]
def get_active_users(self,seconds,chan):
nicks = []
if not chan in self.userstable:
return []
now = time.time()
for nick in self.userstable[chan]:
t = self.userstable[chan][nick]
if t == None:
continue
dt = now - t
if dt < 0:
log_error("IRCNetwork:get_active_users: %s active in %s in the future" % (nick, chan))
continue
if dt < seconds:
nicks.append(Link(self,User(self,nick),Group(self,chan)))
return nicks
def get_users(self,chan):
nicks = []
if not chan in self.userstable:
return []
for nick in self.userstable[chan]:
nicks.append(Link(self,User(self,nick),Group(self,chan)))
return nicks
def is_acceptable_command_prefix(self,s):
s=s.strip()
log_log('checking whether %s is an acceptable command prefix' % s)
if s=="":
return True
if re.match("%s[\t ]*[:,]?$"%config.tipbot_name, s):
return True
return False
def update(self):
try:
data=self._getline()
except Exception,e:
log_warn('Exception from IRCNetwork:_getline, we were probably disconnected, reconnecting in %s seconds' % config.irc_timeout_seconds)
time.sleep(5)
self.last_ping_time = time.time()
self._reconnect()
return True
if data == None:
if time.time() - self.last_ping_time > config.irc_timeout_seconds:
log_warn('%s seconds without PING, reconnecting in 5 seconds' % config.irc_timeout_seconds)
time.sleep(5)
self.last_ping_time = time.time()
self._reconnect()
return True
data = data.strip("\r\n")
self._log_IRCRECV(data)
# consider any IRC data as a ping
self.last_ping_time = time.time()
if data.find ( config.irc_welcome_line ) != -1:
self.userstable = dict()
self.registered_users.clear()
if not config.irc_use_sasl:
self.login()
for chan in config.irc_channels:
self.join(chan)
#ScanWho(None,[chan])
if data.find ( 'PING' ) == 0:
log_log('Got PING, replying PONG')
self.last_ping_time = time.time()
self._irc_sendmsg ( 'PONG ' + data.split() [ 1 ])
return True
if data.startswith('AUTHENTICATE +'):
if config.irc_use_sasl:
authstring = config.irc_sasl_name + chr(0) + config.irc_sasl_name + chr(0) + self.password
self._irc_sendmsg('AUTHENTICATE %s' % base64.b64encode(authstring))
else:
log_warn('Got AUTHENTICATE while not using SASL')
if data.find('ERROR :Closing Link:') == 0:
if self.quitting:
log_info('IRC stopped, bye')
return False
log_warn('We were kicked from IRC, reconnecting in 5 seconds')
time.sleep(5)
self.last_ping_time = time.time()
self._reconnect()
return True
if data.find(':') == -1:
return True
try:
cparts = data.lstrip(':').split(' :')
if len(cparts) == 0:
log_warn('No separator found, ignoring line')
return True
#if len(cparts) >= 9:
# idx_colon = data.find(':',1)
# idx_space = data.find(' ')
# if idx_space and idx_colon < idx_space and re.search("@([0-9a-fA-F]+:){7}[0-9a-fA-F]+", data):
# log_info('Found IPv6 address in non-text, restructuring')
# idx = data.rfind(':')
# cparts = [ cparts[0], "".join(cparts[1:]) ]
if len(cparts) >= 2:
text = cparts[1]
else:
text = ""
parts = cparts[0].split(' ')
who = parts[0]
action = parts[1]
if len(parts) >= 3:
chan = parts[2]
else:
chan = None
except Exception, e:
log_error('main parser: Exception, ignoring line: %s' % str(e))
return True
if action == None:
return True
#print 'cparts: ', str(cparts)
#print 'parts: ', str(parts)
#print 'text: ', text
#print 'who: ', who
#print 'action: ', action
#print 'chan: ', chan
try:
if action == 'CAP':
if parts[2] == '*' and parts[3] == 'ACK':
log_info('CAP ACK received from server')
self._irc_sendmsg('AUTHENTICATE PLAIN')
elif parts[2] == '*' and parts[3] == 'NAK':
log_info('CAP NAK received from server')
log_error('Failed to negotiate SASL')
exit()
else:
log_warn('Unknown CAP line received from server: %s' % data)
if action == 'NOTICE':
if text.find ('throttled due to flooding') >= 0:
log_warn('Flood protection kicked in, outgoing messages lost')
ret = self.on_notice(who,text)
if ret:
return ret
elif action == '903':
log_info('SASL authentication success')
self._irc_sendmsg('CAP END')
elif action in ['902', '904', '905', '906']:
log_error('SASL authentication failed (%s)' % action)
elif action == '352':
try:
who_chan = parts[3]
who_chan_user = parts[7]
if not who_chan_user in self.userstable[who_chan]:
self.userstable[who_chan][who_chan_user] = None
log_log("New list of users in %s: %s" % (who_chan, str(self.userstable[who_chan].keys())))
except Exception,e:
log_error('Failed to parse "352" line: %s: %s' % (data, str(e)))
elif action == '353':
try:
who_chan = parts[4]
who_chan_users = cparts[1].split(" ")
log_info('who_chan: %s' % str(who_chan))
log_info('who_chan_users: %s' % str(who_chan_users))
for who_chan_user in who_chan_users:
if not who_chan_user in self.userstable[who_chan]:
if who_chan_user[0] == "@":
who_chan_user = who_chan_user[1:]
self.userstable[who_chan][who_chan_user] = None
log_log("New list of users in %s: %s" % (who_chan, str(self.userstable[who_chan].keys())))
except Exception,e:
log_error('Failed to parse "353" line: %s: %s' % (data, str(e)))
elif action == 'PRIVMSG':
self.update_last_active_time(chan,GetNick(who))
# resplit to avoid splitting text that contains ':'
text = data.split(':',2)[2]
exidx = text.find('!')
if exidx != -1 and len(text)>exidx+1 and text[exidx+1] in string.ascii_letters and self.is_acceptable_command_prefix(text[:exidx]):
cmd = text.split('!')[1]
cmd = cmd.split(' ')
cmd[0] = cmd[0].strip(' \t\n\r')
log_log('Found command from %s: "%s" in channel "%s"' % (who, str(cmd), str(chan)))
if self.on_command:
self.on_command(Link(self,User(self,GetNick(who)),Group(self,chan) if chan[0]=='#' else None),cmd)
return True
elif action == 'JOIN':
nick = GetNick(who)
log_info('%s joined the channel' % nick)
if not chan in self.userstable:
self.userstable[chan] = dict()
if nick in self.userstable[chan]:
log_warn('%s joined, but already in %s' % (nick, chan))
else:
self.userstable[chan][nick] = None
log_log("New list of users in %s: %s" % (chan, str(self.userstable[chan].keys())))
elif action == 'PART':
nick = GetNick(who)
log_info('%s left the channel' % nick)
if not nick in self.userstable[chan]:
log_warn('%s left, but was not in %s' % (nick, chan))
else:
del self.userstable[chan][nick]
log_log("New list of users in %s: %s" % (chan, str(self.userstable[chan].keys())))
elif action == 'QUIT':
nick = GetNick(who)
log_info('%s quit' % nick)
removed_list = ""
for chan in self.userstable:
log_log("Checking in %s" % chan)
if nick in self.userstable[chan]:
removed_list = removed_list + " " + chan
del self.userstable[chan][nick]
log_log("New list of users in %s: %s" % (chan, str(self.userstable[chan].keys())))
elif action == 'KICK':
nick = parts[3]
log_info('%s was kicked' % nick)
removed_list = ""
for chan in self.userstable:
log_log("Checking in %s" % chan)
if nick in self.userstable[chan]:
removed_list = removed_list + " " + chan
del self.userstable[chan][nick]
log_log("New list of users in %s: %s" % (chan, str(self.userstable[chan].keys())))
elif action == 'NICK':
nick = GetNick(who)
new_nick = cparts[len(cparts)-1]
log_info('%s renamed to %s' % (nick, new_nick))
for c in self.userstable:
log_log('checking %s' % c)
if nick in self.userstable[c]:
del self.userstable[c][nick]
if new_nick in self.userstable[c]:
log_warn('%s is the new name of %s, but was already in %s' % (new_nick, nick, c))
else:
self.userstable[c][new_nick] = None
log_log("New list of users in %s: %s" % (c, str(self.userstable[c].keys())))
except Exception,e:
log_error('Exception in top level action processing: %s' % str(e))
return True
def _log_IRCRECV(self,msg):
log("IRCRECV",msg)
def _log_IRCSEND(self,msg):
log("IRCSEND",msg)
def _irc_recv(self,size,flags=None):
if config.irc_use_ssl:
return self.sslirc.read(size)
else:
return self.irc.recv(size,flags)
def _irc_send(self,data):
if config.irc_use_ssl:
return self.sslirc.write(data)
else:
return self.irc.send(data)
def _irc_sendmsg(self,msg):
t = time.time()
dt = t - self.last_send_time
if dt < self.current_send_delay:
time.sleep (self.current_send_delay - dt)
self.current_send_delay = self.current_send_delay * 1.5
if self.current_send_delay > irc_max_send_delay:
self.current_send_delay = irc_max_send_delay
else:
while dt > self.current_send_delay * 1.5:
dt = dt - self.current_send_delay
self.current_send_delay = self.current_send_delay / 1.5
if self.current_send_delay < irc_min_send_delay:
self.current_send_delay = irc_min_send_delay
break
self._log_IRCSEND(msg)
self._irc_send(msg + '\r\n')
self.last_send_time = time.time()
def _getline(self):
idx = self.buffered_data.find("\n")
if idx == -1:
try:
(r,w,x)=select.select([self.irc.fileno()],[],[],1)
if self.irc.fileno() in r:
newdata=self._irc_recv(4096,socket.MSG_DONTWAIT)
else:
newdata = None
if self.irc.fileno() in x:
log_error('getline: IRC socket in exception set')
newdata = None
except Exception,e:
log_error('getline: Exception: %s' % str(e))
# Broken pipe when we get kicked for spam
if str(e).find("Broken pipe") != -1:
raise
newdata = None
if newdata == None:
return None
self.buffered_data+=newdata
idx = self.buffered_data.find("\n")
if idx == -1:
ret = self.buffered_data
self.buffered_data = ""
return ret
ret = self.buffered_data[0:idx+1]
self.buffered_data = self.buffered_data[idx+1:]
return ret
def _connect(self,host,port,login,password,delay):
self.host=host
self.port=port
self.login=login
self.password=password
self.line_delay=delay
log_info('Connecting to IRC at %s:%u' % (host, port))
self.last_send_time=0
self.last_ping_time = time.time()
self.quitting = False
self.buffered_data = ""
self.userstable=dict()
self.registered_users=set()
try:
self.irc = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
if config.irc_use_ssl:
try:
raise RuntimeError('')
self.irc_ssl_context = ssl.create_default_context()
self.sslirc = self.irc_ssl_context.wrap_socket(self.irc, host)
self.sslirc.connect ( ( host, port ) )
except Exception,e:
log_warn('Failed to create SSL context, using fallback code: %s' % str(e))
self.irc.connect ( ( host, port ) )
self.sslirc = socket.ssl(self.irc)
except Exception, e:
log_error( 'Error initializing IRC: %s' % str(e))
return False
self._log_IRCRECV(self._irc_recv(4096))
if config.irc_use_sasl:
self._irc_sendmsg('CAP REQ :sasl')
else:
self._irc_sendmsg ( 'PASS *********')
self._irc_sendmsg ( 'NICK %s' % login)
self._irc_sendmsg ( 'USER %s %s %s :%s' % (login, login, login, login))
return True
def _reconnect(self):
return self._connect(self.host,self.port,self.login,self.password,self.line_delay)
def JoinChannel(link,cmd):
jchan = GetParam(cmd,1)
if not jchan:
SendTo(sendto,'Usage: join <channel>')
rerurn
if jchan[0] != '#':
SendTo(sendto,'Channel name must start with #')
link.send('Usage: join <channel>')
return
Join(jchan)
if jchan[0] != '#':
link.send('Channel name must start with #')
return
network=GetNetworkByType(IRCNetwork)
if not network:
link.send('No IRC network found')
return
network.join(jchan)
def PartChannel(nick,chan,cmd):
sendto=GetSendTo(nick,chan)
def PartChannel(link,cmd):
pchan = GetParam(cmd,1)
if pchan:
if pchan[0] != '#':
SendTo(sendto,'Channel name must start with #')
link.send('Channel name must start with #')
return
else:
pchan = chan
Part(pchan)
def QuitIRC(nick,chan,cmd):
msg = ""
for w in cmd[1:]:
msg = msg + " " + w
Quit(msg)
network=GetNetworkByType(IRCNetwork)
if not network:
link.send('No IRC network found')
return
network.part(pchan)
RegisterCommand({
'module': __name__,
@ -61,10 +519,3 @@ RegisterCommand({
'admin': True,
'help': "Makes %s part from a channel" % (config.tipbot_name)
})
RegisterCommand({
'module': __name__,
'name': 'quit',
'function': QuitIRC,
'admin': True,
'help': "Makes %s quit IRC" % (config.tipbot_name)
})

View file

@ -35,10 +35,7 @@ def GetTipbotAddress():
log_error("GetTipbotAddress: Error retrieving %s's address: %s" % (config.tipbot_name, str(e)))
return "ERROR"
def UpdateCoin(param):
irc = param[0]
redisdb = param[1]
def UpdateCoin(data):
global last_wallet_update_time
if last_wallet_update_time == None:
last_wallet_update_time = 0
@ -87,11 +84,13 @@ def UpdateCoin(param):
tx_hash=p["tx_hash"]
amount=p["amount"]
try:
recipient = GetNickFromPaymentID(payment_id)
recipient = GetIdentityFromPaymentID(payment_id)
if not recipient:
raise RuntimeError('Payment ID %s not found' % payment_id)
log_info('UpdateCoin: Found payment %s to %s for %s' % (tx_hash,recipient, AmountToString(amount)))
pipe.hincrby("balances",recipient,amount);
except Exception,e:
log_error('UpdateCoin: No nick found for payment id %s, tx hash %s, amount %s' % (payment_id, tx_hash, amount))
log_error('UpdateCoin: No identity found for payment id %s, tx hash %s, amount %s: %s' % (payment_id, tx_hash, amount, str(e)))
log_log('UpdateCoin: Executing received payments pipeline')
pipe.execute()
except Exception,e:
@ -104,13 +103,13 @@ def UpdateCoin(param):
log_error('UpdateCoin: Failed to get bulk payments: %s' % str(e))
last_wallet_update_time = time.time()
def Deposit(nick,chan,cmd):
Help(nick,chan)
def Deposit(link,cmd):
Help(link)
def Help(nick,chan):
SendTo(nick, "You can send %s to your account:" % coinspecs.name);
SendTo(nick, " Address: %s" % GetTipbotAddress())
SendTo(nick, " Payment ID: %s" % GetPaymentID(nick))
def Help(link):
link.send("You can send %s to your account:" % coinspecs.name);
link.send(" Address: %s" % GetTipbotAddress())
link.send(" Payment ID: %s" % GetPaymentID(link))
RegisterModule({
'name': __name__,

View file

@ -23,184 +23,183 @@ import tipbot.config as config
from tipbot.log import log_error, log_warn, log_info, log_log
import tipbot.coinspecs as coinspecs
from tipbot.utils import *
from tipbot.ircutils import *
from tipbot.command_manager import *
from tipbot.redisdb import *
pending_confirmations=dict()
def PerformTip(nick,chan,who,units):
sendto=GetSendTo(nick,chan)
def PerformTip(link,whoid,units):
identity=link.identity()
try:
balance = redis_hget("balances",nick)
balance = redis_hget("balances",identity)
if balance == None:
balance = 0
balance=long(balance)
if units > balance:
SendTo(sendto, "You only have %s" % (AmountToString(balance)))
link.send("You only have %s" % (AmountToString(balance)))
return
log_info('Tip: %s tipping %s %u units, with balance %u' % (nick, who, units, balance))
log_info('Tip: %s tipping %s %u units, with balance %u' % (identity, whoid, units, balance))
try:
p = redis_pipeline()
p.incrby("tips_total_count",1);
p.incrby("tips_total_amount",units);
p.hincrby("tips_count",nick,1);
p.hincrby("tips_amount",nick,units);
p.hincrby("balances",nick,-units);
p.hincrby("balances",who,units)
p.hincrby("tips_count",identity,1);
p.hincrby("tips_amount",identity,units);
p.hincrby("balances",identity,-units);
p.hincrby("balances",whoid,units)
p.execute()
SendTo(sendto,"%s has tipped %s %s" % (nick, who, AmountToString(units)))
link.send("%s has tipped %s %s" % (NickFromIdentity(identity), NickFromIdentity(whoid), AmountToString(units)))
except Exception, e:
log_error("Tip: Error updating redis: %s" % str(e))
SendTo(sendto, "An error occured")
link.send("An error occured")
return
except Exception, e:
log_error('Tip: exception: %s' % str(e))
SendTo(sendto, "An error has occured")
link.send("An error has occured")
def Tip(nick,chan,cmd):
userstable = GetUsersTable()
sendto=GetSendTo(nick,chan)
def Tip(link,cmd):
identity=link.identity()
try:
who=cmd[1]
amount=float(cmd[2])
except Exception,e:
SendTo(sendto, "Usage: tip nick amount")
link.send("Usage: tip nick amount")
return
units=long(amount*coinspecs.atomic_units)
if units <= 0:
SendTo(sendto, "Invalid amount")
link.send("Invalid amount")
return
log_info("Tip: %s wants to tip %s %s" % (nick, who, AmountToString(units)))
if chan in userstable:
log_info('getting keys')
userlist = userstable[chan].keys()
log_info('testingwho')
if not who in userlist:
SendTo(sendto,"%s is not in %s: if you really intend to tip %s, type !confirmtip before tipping again" % (who, chan, who))
pending_confirmations[nick]={'who': who, 'units': units}
whoid = IdentityFromString(link,who)
log_info("Tip: %s wants to tip %s %s" % (identity, whoid, AmountToString(units)))
if link.group:
userlist=[user.identity() for user in link.network.get_users(link.group.name)]
log_log('users: %s' % str(userlist))
if not whoid in userlist:
link.send("%s is not in %s: if you really intend to tip %s, type !confirmtip before tipping again" % (who, link.group.name, who))
pending_confirmations[identity]={'who': whoid, 'units': units}
return
log_info('delk')
pending_confirmations.pop(nick,None)
PerformTip(nick,chan,who,units)
pending_confirmations.pop(identity,None)
PerformTip(link,whoid,units)
def ConfirmTip(nick,chan,cmd):
sendto=GetSendTo(nick,chan)
if not nick in pending_confirmations:
SendTo(sendto,"%s has no tip waiting confirmation" % nick)
def ConfirmTip(link,cmd):
identity=link.identity()
if not identity in pending_confirmations:
link.send("%s has no tip waiting confirmation" % NickFromIdentity(identity))
return
who=pending_confirmations[nick]['who']
units=pending_confirmations[nick]['units']
pending_confirmations.pop(nick,None)
PerformTip(nick,chan,who,units)
whoid=pending_confirmations[identity]['who']
units=pending_confirmations[identity]['units']
pending_confirmations.pop(identity,None)
PerformTip(link,whoid,units)
def Rain(nick,chan,cmd):
userstable = GetUsersTable()
def Rain(link,cmd):
identity=link.identity()
if chan[0] != '#':
SendTo(nick, "Raining can only be done in a channel")
group=link.group
if not group:
link.send("Raining can only be done in a group")
return
try:
amount=float(cmd[1])
except Exception,e:
SendTo(chan, "Usage: rain amount [users]")
link.send("Usage: rain amount [users]")
return
users = GetParam(cmd,2)
if users:
try:
users=long(users)
except Exception,e:
SendTo(chan, "Usage: rain amount [users]")
link.send("Usage: rain amount [users]")
return
if amount <= 0:
SendTo(chan, "Usage: rain amount [users]")
link.send("Usage: rain amount [users]")
return
if users != None and users <= 0:
SendTo(chan, "Usage: rain amount [users]")
link.send("Usage: rain amount [users]")
return
units = long(amount * coinspecs.atomic_units)
try:
balance = redis_hget("balances",nick)
balance = redis_hget("balances",identity)
if balance == None:
balance = 0
balance=long(balance)
if units > balance:
SendTo(chan, "You only have %s" % (AmountToString(balance)))
link.send("You only have %s" % (AmountToString(balance)))
return
log_log("userstable: %s" % str(userstable))
userlist = userstable[chan].keys()
userlist.remove(nick)
userlist=[user.identity() for user in link.network.get_users(group.name)]
log_log("users in %s: %s" % (group.name,str(userlist)))
userlist.remove(identity)
for n in config.no_rain_to_nicks:
userlist.remove(n)
userlist.remove(IdentityFromString(link,n))
if users == None or users > len(userlist):
users = len(userlist)
everyone = True
else:
everyone = False
if users == 0:
SendTo(chan, "Nobody eligible for rain")
link.send("Nobody eligible for rain")
return
if units < users:
SendTo(chan, "This would mean not even an atomic unit per nick")
link.send("This would mean not even an atomic unit per nick")
return
log_info("%s wants to rain %s on %s users in %s" % (nick, AmountToString(units), users, chan))
log_log("users in %s: %s" % (chan, str(userlist)))
log_info("%s wants to rain %s on %s users in %s" % (identity, AmountToString(units), users, group.name))
log_log("eligible users in %s: %s" % (group.name, str(userlist)))
random.shuffle(userlist)
userlist = userlist[0:users]
log_log("selected users in %s: %s" % (chan, userlist))
log_log("selected users in %s: %s" % (group.name, userlist))
user_units = long(units / users)
enumerate_users = False
if everyone:
msg = "%s rained %s on everyone in the channel" % (nick, AmountToString(user_units))
msg = "%s rained %s on everyone in the channel" % (link.user.nick, AmountToString(user_units))
elif len(userlist) > 16:
msg = "%s rained %s on %d nicks" % (nick, AmountToString(user_units), len(userlist))
msg = "%s rained %s on %d nicks" % (link.user.nick, AmountToString(user_units), len(userlist))
else:
msg = "%s rained %s on:" % (nick, AmountToString(user_units))
msg = "%s rained %s on:" % (link.user.nick, AmountToString(user_units))
enumerate_users = True
pipe = redis_pipeline()
pipe.hincrby("balances",nick,-units)
pipe.incrby("rain_total_count",1);
pipe.incrby("rain_total_amount",units);
pipe.hincrby("rain_count",nick,1);
pipe.hincrby("rain_amount",nick,units);
pipe.hincrby("balances",identity,-units)
pipe.incrby("rain_total_count",1)
pipe.incrby("rain_total_amount",units)
pipe.hincrby("rain_count",identity,1)
pipe.hincrby("rain_amount",identity,units)
for user in userlist:
pipe.hincrby("balances",user,user_units)
if enumerate_users:
msg = msg + " " + user
msg = msg + " " + NickFromIdentity(user)
pipe.execute()
SendTo(chan, "%s" % msg)
link.send("%s" % msg)
except Exception,e:
log_error('Rain: exception: %s' % str(e))
SendTo(chan, "An error has occured")
link.send("An error has occured")
return
def RainActive(nick,chan,cmd):
userstable = GetUsersTable()
def RainActive(link,cmd):
identity=link.identity()
amount=GetParam(cmd,1)
hours=GetParam(cmd,2)
minfrac=GetParam(cmd,3)
if chan[0] != '#':
SendTo(nick, "Raining can only be done in a channel")
group=link.group
if not group:
link.send("Raining can only be done in a channel")
return
if not amount or not hours:
SendTo(chan, "usage: !rainactive <amount> <hours> [<minfrac>]")
link.send("usage: !rainactive <amount> <hours> [<minfrac>]")
return
try:
amount=float(amount)
if amount <= 0:
raise RuntimeError("")
except Exception,e:
SendTo(chan, "Invalid amount")
link.send("Invalid amount")
return
try:
hours=float(hours)
@ -208,7 +207,7 @@ def RainActive(nick,chan,cmd):
raise RuntimeError("")
seconds = hours * 3600
except Exception,e:
SendTo(chan, "Invalid hours")
link.send("Invalid hours")
return
if minfrac:
try:
@ -216,7 +215,7 @@ def RainActive(nick,chan,cmd):
if minfrac < 0 or minfrac > 1:
raise RuntimeError("")
except Exception,e:
SendTo(chan, "minfrac must be a number between 0 and 1")
link.send("minfrac must be a number between 0 and 1")
return
else:
minfrac = 0
@ -224,23 +223,26 @@ def RainActive(nick,chan,cmd):
units = long(amount * coinspecs.atomic_units)
try:
balance = redis_hget("balances",nick)
balance = redis_hget("balances",identity)
if balance == None:
balance = 0
balance=long(balance)
if units > balance:
SendTo(chan, "You only have %s" % (AmountToString(balance)))
link.send("You only have %s" % (AmountToString(balance)))
return
now = time.time()
userlist = userstable[chan].keys()
userlist.remove(nick)
userlist = [user.identity() for user in link.network.get_active_users(seconds,group.name)]
log_log('userlist: %s' % str(userlist))
userlist.remove(link.identity())
for n in config.no_rain_to_nicks:
userlist.remove(n)
userlist.remove(IdentityFromString(link,n))
weights=dict()
weight=0
log_log('userlist to loop: %s' % str(userlist))
for n in userlist:
t = userstable[chan][n]
log_log('user to check: %s' % NickFromIdentity(n))
t = link.network.get_last_active_time(NickFromIdentity(n),group.name)
if t == None:
continue
dt = now - t
@ -250,15 +252,15 @@ def RainActive(nick,chan,cmd):
weight += w
if len(weights) == 0:
SendTo(chan, "Nobody eligible for rain")
link.send("Nobody eligible for rain")
return
pipe = redis_pipeline()
pipe.hincrby("balances",nick,-units)
pipe.hincrby("balances",identity,-units)
pipe.incrby("arain_total_count",1);
pipe.incrby("arain_total_amount",units);
pipe.hincrby("arain_count",nick,1);
pipe.hincrby("arain_amount",nick,units);
pipe.hincrby("arain_count",identity,1);
pipe.hincrby("arain_amount",identity,units);
rained_units = 0
nnicks = 0
minu=None
@ -267,7 +269,8 @@ def RainActive(nick,chan,cmd):
user_units = long(units * weights[n] / weight)
if user_units <= 0:
continue
log_info("%s rained %s on %s (last active %f hours ago)" % (nick, AmountToString(user_units),n,GetTimeSinceActive(chan,n)/3600))
act = now - link.network.get_last_active_time(NickFromIdentity(n),link.group.name)
log_info("%s rained %s on %s (last active %f hours ago)" % (identity, AmountToString(user_units),n,act/3600))
pipe.hincrby("balances",n,user_units)
rained_units += user_units
if not minu or user_units < minu:
@ -277,23 +280,23 @@ def RainActive(nick,chan,cmd):
nnicks = nnicks+1
if maxu == None:
SendTo(chan, "This would mean not even an atomic unit per nick")
link.send("This would mean not even an atomic unit per nick")
return
pipe.execute()
log_info("%s rained %s - %s (total %s, acc %s) on the %d nicks active in the last %s" % (nick, AmountToString(minu), AmountToString(maxu), AmountToString(units), AmountToString(rained_units), nnicks, TimeToString(seconds)))
SendTo(chan, "%s rained %s - %s on the %d nicks active in the last %s" % (nick, AmountToString(minu), AmountToString(maxu), nnicks, TimeToString(seconds)))
log_info("%s rained %s - %s (total %s, acc %s) on the %d nicks active in the last %f hours" % (identity, AmountToString(minu), AmountToString(maxu), AmountToString(units), AmountToString(rained_units), nnicks, TimeToString(seconds)))
link.send("%s rained %s - %s on the %d nicks active in the last %f hours" % (identity, AmountToString(minu), AmountToString(maxu), nnicks, TimeToString(seconds)))
except Exception,e:
log_error('Rain: exception: %s' % str(e))
SendTo(chan, "An error has occured")
link.send("An error has occured")
return
def Help(nick,chan):
SendTo(nick,'You can tip other people, or rain %s on them' % coinspecs.name)
SendTo(nick,'!tip tips a single person, while !rain shares equally between people in the channel')
SendTo(nick,'!rainactive tips all within the last N hours, with more recently active people')
SendTo(nick,'getting a larger share.')
def Help(link):
link.send('You can tip other people, or rain %s on them' % coinspecs.name)
link.send('!tip tips a single person, while !rain shares equally between people in the channel')
link.send('!rainactive tips all within the last N hours, with more recently active people')
link.send('getting a larger share.')
RegisterModule({

View file

@ -16,46 +16,47 @@ from tipbot.log import log_error, log_warn, log_info, log_log
import tipbot.coinspecs as coinspecs
import tipbot.config as config
from tipbot.utils import *
from tipbot.ircutils import *
from tipbot.redisdb import *
from tipbot.command_manager import *
withdraw_disabled = False
def DisableWithdraw(nick,chan,cmd):
def DisableWithdraw(link,cmd):
global withdraw_disabled
if nick:
log_warn('DisableWithdraw: disabled by %s' % nick)
if link:
log_warn('DisableWithdraw: disabled by %s' % link.identity())
else:
log_warn('DisableWithdraw: disabled')
withdraw_disabled = True
def EnableWithdraw(nick,chan,cmd):
def EnableWithdraw(link,cmd):
global withdraw_disabled
log_info('EnableWithdraw: enabled by %s' % nick)
log_info('EnableWithdraw: enabled by %s' % link.identity())
withdraw_disabled = False
def CheckDisableWithdraw():
if config.disable_withdraw_on_error:
DisableWithdraw(None,None,None)
def Withdraw(nick,chan,cmd):
def Withdraw(link,cmd):
identity=link.identity()
local_withdraw_fee = config.withdrawal_fee or coinspecs.min_withdrawal_fee
local_min_withdraw_amount = config.min_withdraw_amount or local_withdraw_fee
if local_min_withdraw_amount <= 0 or local_withdraw_fee <= 0 or local_min_withdraw_amount < local_withdraw_fee:
log_error('Withdraw: Inconsistent withdrawal settings')
SendTo(nick, "An error has occured")
link.send("An error has occured")
return
try:
address=cmd[1]
except Exception,e:
SendTo(nick, "Usage: withdraw address [amount]")
link.send("Usage: withdraw address [amount]")
return
if not IsValidAddress(address):
SendTo(nick, "Invalid address")
link.send("Invalid address")
return
amount = GetParam(cmd,2)
if amount:
@ -66,37 +67,37 @@ def Withdraw(nick,chan,cmd):
amount = long(famount * coinspecs.atomic_units)
amount += local_withdraw_fee
except Exception,e:
SendTo(nick, "Invalid amount")
link.send("Invalid amount")
return
log_info("Withdraw: %s wants to withdraw %s to %s" % (nick, AmountToString(amount) if amount else "all", address))
log_info("Withdraw: %s wants to withdraw %s to %s" % (identity, AmountToString(amount) if amount else "all", address))
if withdraw_disabled:
log_error('Withdraw: disabled')
SendTo(nick, "Sorry, withdrawal is disabled due to a wallet error which requires admin assistance")
link.send("Sorry, withdrawal is disabled due to a wallet error which requires admin assistance")
return
try:
balance = redis_hget("balances",nick)
balance = redis_hget("balances",identity)
if balance == None:
balance = 0
balance=long(balance)
except Exception, e:
log_error('Withdraw: exception: %s' % str(e))
SendTo(nick, "An error has occured")
link.send("An error has occured")
return
if amount:
if amount > balance:
log_info("Withdraw: %s trying to withdraw %s, but only has %s" % (nick,AmountToString(amount),AmountToString(balance)))
SendTo(nick, "You only have %s" % AmountToString(balance))
log_info("Withdraw: %s trying to withdraw %s, but only has %s" % (identity,AmountToString(amount),AmountToString(balance)))
link.send("You only have %s" % AmountToString(balance))
return
else:
amount = balance
if amount <= 0 or amount < local_min_withdraw_amount:
log_info("Withdraw: Minimum withdrawal balance: %s, %s cannot withdraw %s" % (AmountToString(config.min_withdraw_amount),nick,AmountToString(amount)))
SendTo(nick, "Minimum withdrawal balance: %s, cannot withdraw %s" % (AmountToString(config.min_withdraw_amount),AmountToString(amount)))
link.send("Minimum withdrawal balance: %s, cannot withdraw %s" % (AmountToString(config.min_withdraw_amount),AmountToString(amount)))
return
try:
fee = long(local_withdraw_fee)
@ -105,7 +106,7 @@ def Withdraw(nick,chan,cmd):
log_info('Withdraw: fee: %s, to pay: %s' % (AmountToString(fee), AmountToString(topay)))
params = {
'destinations': [{'address': address, 'amount': topay}],
'payment_id': GetPaymentID(nick),
'payment_id': GetPaymentID(link),
'fee': fee,
'mixin': config.withdrawal_mixin,
'unlock_time': 0,
@ -114,34 +115,34 @@ def Withdraw(nick,chan,cmd):
except Exception,e:
log_error('Withdraw: Error in transfer: %s' % str(e))
CheckDisableWithdraw()
SendTo(nick,"An error has occured")
link.send("An error has occured")
return
if not "result" in j:
log_error('Withdraw: No result in transfer reply')
CheckDisableWithdraw()
SendTo(nick,"An error has occured")
link.send("An error has occured")
return
result = j["result"]
if not "tx_hash" in result:
log_error('Withdraw: No tx_hash in transfer reply')
CheckDisableWithdraw()
SendTo(nick,"An error has occured")
link.send("An error has occured")
return
tx_hash = result["tx_hash"]
log_info('%s has withdrawn %s, tx hash %s' % (nick, amount, str(tx_hash)))
SendTo(nick, "Tx sent: %s" % tx_hash)
log_info('%s has withdrawn %s, tx hash %s' % (identity, amount, str(tx_hash)))
link.send( "Tx sent: %s" % tx_hash)
try:
redis_hincrby("balances",nick,-amount)
redis_hincrby("balances",identity,-amount)
except Exception, e:
log_error('Withdraw: FAILED TO SUBTRACT BALANCE: exception: %s' % str(e))
CheckDisableWithdraw()
def Help(nick,chan):
def Help(link):
fee = config.withdrawal_fee or coinspecs.min_withdrawal_fee
min_amount = config.min_withdraw_amount or fee
SendTo(nick, "Minimum withdrawal: %s" % AmountToString(min_amount))
SendTo(nick, "Withdrawal fee: %s" % AmountToString(fee))
link.send("Minimum withdrawal: %s" % AmountToString(min_amount))
link.send("Withdrawal fee: %s" % AmountToString(fee))