mirror of
https://git.wownero.com/wownero/tippero.git
synced 2024-08-15 00:33:14 +00:00
Abstract away communications networks
This commit is contained in:
parent
62c2adbaf6
commit
ea25e9d41b
16 changed files with 1249 additions and 983 deletions
223
tipbot.py
223
tipbot.py
|
@ -25,8 +25,10 @@ import importlib
|
|||
import tipbot.coinspecs as coinspecs
|
||||
import tipbot.config as config
|
||||
from tipbot.log import log_error, log_warn, log_info, log_log
|
||||
from tipbot.link import *
|
||||
from tipbot.user import *
|
||||
from tipbot.group import *
|
||||
from tipbot.utils import *
|
||||
from tipbot.ircutils import *
|
||||
from tipbot.redisdb import *
|
||||
from tipbot.command_manager import *
|
||||
|
||||
|
@ -73,6 +75,9 @@ if not selected_coin:
|
|||
|
||||
sys.path.append(os.path.join('tipbot','modules'))
|
||||
for modulename in modulenames:
|
||||
if modulename in sys.modules:
|
||||
log_error('A %s module already exists' % modulename)
|
||||
exit(1)
|
||||
log_info('Importing %s module' % modulename)
|
||||
try:
|
||||
__import__(modulename)
|
||||
|
@ -82,28 +87,28 @@ for modulename in modulenames:
|
|||
|
||||
|
||||
|
||||
def GetBalance(nick,chan,cmd):
|
||||
sendto=GetSendTo(nick,chan)
|
||||
log_log("GetBalance: checking %s" % nick)
|
||||
def GetBalance(link,cmd):
|
||||
nick=link.user.nick
|
||||
log_log("GetBalance: checking %s (%s)" % (link.identity(),str(link)))
|
||||
try:
|
||||
balance = redis_hget("balances",nick)
|
||||
balance = redis_hget("balances",link.identity())
|
||||
if balance == None:
|
||||
balance = 0
|
||||
balance = long(balance)
|
||||
sbalance = AmountToString(balance)
|
||||
if balance < coinspecs.atomic_units:
|
||||
if balance == 0:
|
||||
SendTo(sendto, "%s's balance is %s" % (nick, sbalance))
|
||||
link.send("%s's balance is %s" % (nick, sbalance))
|
||||
else:
|
||||
SendTo(sendto, "%s's balance is %s (%.16g %s)" % (nick, sbalance, float(balance) / coinspecs.atomic_units, coinspecs.name))
|
||||
link.send("%s's balance is %s (%.16g %s)" % (nick, sbalance, float(balance) / coinspecs.atomic_units, coinspecs.name))
|
||||
else:
|
||||
SendTo(sendto, "%s's balance is %s" % (nick, sbalance))
|
||||
link.send("%s's balance is %s" % (nick, sbalance))
|
||||
except Exception, e:
|
||||
log_error('GetBalance: exception: %s' % str(e))
|
||||
SendTo(sendto, "An error has occured")
|
||||
link.send("An error has occured")
|
||||
|
||||
def AddBalance(nick,chan,cmd):
|
||||
sendto = GetSendTo(nick,chan)
|
||||
def AddBalance(link,cmd):
|
||||
nick=link.user.nick
|
||||
if GetParam(cmd,2):
|
||||
anick = GetParam(cmd,1)
|
||||
amount = GetParam(cmd,2)
|
||||
|
@ -111,85 +116,91 @@ def AddBalance(nick,chan,cmd):
|
|||
anick = nick
|
||||
amount = GetParam(cmd,1)
|
||||
if not amount:
|
||||
SendTo(sendto, 'usage: !addbalance [<nick>] <amount>')
|
||||
link.send('usage: !addbalance [<nick>] <amount>')
|
||||
return
|
||||
try:
|
||||
units = long(float(amount)*coinspecs.atomic_units)
|
||||
except Exception,e:
|
||||
log_error('AddBalance: invalid amount: %s' % str(e))
|
||||
SendTo(sendto, 'usage: !addbalance [<nick>] <amount>')
|
||||
log_error('AddBalance: error converting amount: %s' % str(e))
|
||||
link.send('usage: !addbalance [<nick>] <amount>')
|
||||
return
|
||||
log_info("AddBalance: Adding %s to %s's balance" % (AmountToString(units),anick))
|
||||
if anick.find(':') == -1:
|
||||
network=link.network
|
||||
log_info('No network found in %s, using %s from command originator' % (anick,network.name))
|
||||
aidentity=Link(network,User(network,anick)).identity()
|
||||
else:
|
||||
aidentity=anick
|
||||
log_info("AddBalance: Adding %s to %s's balance" % (AmountToString(units),aidentity))
|
||||
try:
|
||||
balance = redis_hincrby("balances",anick,units)
|
||||
balance = redis_hincrby("balances",aidentity,units)
|
||||
except Exception, e:
|
||||
log_error('AddBalance: exception: %s' % str(e))
|
||||
SendTo(sendto, "An error has occured")
|
||||
SendTo(sendto,"%s's bvalance is now %s" % (anick,AmountToString(balance)))
|
||||
link.send( "An error has occured")
|
||||
link.send("%s's balance is now %s" % (aidentity,AmountToString(balance)))
|
||||
|
||||
def ScanWho(nick,chan,cmd):
|
||||
Who(chan)
|
||||
def ScanWho(link,cmd):
|
||||
link.network.update_users_list(link.group.name if link.group else None)
|
||||
|
||||
def GetHeight(nick,chan,cmd):
|
||||
log_info('GetHeight: %s wants to know block height' % nick)
|
||||
def GetHeight(link,cmd):
|
||||
log_info('GetHeight: %s wants to know block height' % str(link))
|
||||
try:
|
||||
j = SendDaemonHTMLCommand("getheight")
|
||||
except Exception,e:
|
||||
log_error('GetHeight: error: %s' % str(e))
|
||||
SendTo(nick,"An error has occured")
|
||||
link.send("An error has occured")
|
||||
return
|
||||
log_log('GetHeight: Got reply: %s' % str(j))
|
||||
if not "height" in j:
|
||||
log_error('GetHeight: Cannot see height in here')
|
||||
SendTo(nick, "Height not found")
|
||||
link.send("Height not found")
|
||||
return
|
||||
height=j["height"]
|
||||
log_info('GetHeight: height is %s' % str(height))
|
||||
SendTo(nick, "Height: %s" % str(height))
|
||||
link.send("Height: %s" % str(height))
|
||||
|
||||
def GetTipbotBalance(nick,chan,cmd):
|
||||
log_info('%s wants to know the tipbot balance' % nick)
|
||||
def GetTipbotBalance(link,cmd):
|
||||
log_info('%s wants to know the tipbot balance' % str(link))
|
||||
try:
|
||||
balance, unlocked_balance = RetrieveTipbotBalance()
|
||||
except Exception,e:
|
||||
SendTo(nick,"An error has occured")
|
||||
link.send("An error has occured")
|
||||
return
|
||||
pending = long(balance)-long(unlocked_balance)
|
||||
if pending == 0:
|
||||
log_info("GetTipbotBalance: Tipbot balance: %s" % AmountToString(balance))
|
||||
SendTo(nick,"Tipbot balance: %s" % AmountToString(balance))
|
||||
link.send("Tipbot balance: %s" % AmountToString(balance))
|
||||
else:
|
||||
log_info("GetTipbotBalance: Tipbot balance: %s (%s pending)" % (AmountToString(unlocked_balance), AmountToString(pending)))
|
||||
SendTo(nick,"Tipbot balance: %s (%s pending)" % (AmountToString(unlocked_balance), AmountToString(pending)))
|
||||
link.send("Tipbot balance: %s (%s pending)" % (AmountToString(unlocked_balance), AmountToString(pending)))
|
||||
|
||||
def DumpUsers(nick,chan,cmd):
|
||||
userstable = GetUsersTable()
|
||||
log_info(str(userstable))
|
||||
def DumpUsers(link,cmd):
|
||||
for network in networks:
|
||||
network.dump_users()
|
||||
|
||||
def Help(nick,chan,cmd):
|
||||
def Help(link,cmd):
|
||||
module = GetParam(cmd,1)
|
||||
if module:
|
||||
RunModuleHelpFunction(module,nick,chan)
|
||||
RunModuleHelpFunction(module,link)
|
||||
return
|
||||
|
||||
SendTo(nick, "See available commands with !commands or !commands <modulename>")
|
||||
SendTo(nick, "Available modules: %s" % ", ".join(GetModuleNameList(IsAdmin(nick))))
|
||||
SendTo(nick, "Get help on a particular module with !help <modulename>")
|
||||
link.send("See available commands with !commands or !commands <modulename>")
|
||||
link.send("Available modules: %s" % ", ".join(GetModuleNameList(IsAdmin(link))))
|
||||
link.send("Get help on a particular module with !help <modulename>")
|
||||
if coinspecs.web_wallet_url:
|
||||
SendTo(nick, "No %s address ? You can use %s" % (coinspecs.name, coinspecs.web_wallet_url))
|
||||
link.send("No %s address ? You can use %s" % (coinspecs.name, coinspecs.web_wallet_url))
|
||||
|
||||
def Info(nick,chan,cmd):
|
||||
SendTo(nick, "Info for %s:" % config.tipbot_name)
|
||||
SendTo(nick, "Copyright 2014,2015 moneromooo - http://duckpool.mooo.com/tipbot/")
|
||||
SendTo(nick, "Type !help, or !commands for a list of commands")
|
||||
SendTo(nick, "NO WARRANTY, YOU MAY LOSE YOUR COINS")
|
||||
SendTo(nick, "By sending your %s to %s, you are giving up their control" % (coinspecs.name, config.tipbot_name))
|
||||
SendTo(nick, "to whoever runs the tipbot. Any tip you make/receive using %s" % config.tipbot_name)
|
||||
SendTo(nick, "is obviously not anonymous. %s's wallet may end up corrupt, or be" % config.tipbot_name)
|
||||
SendTo(nick, "stolen, the server compromised, etc. While I hope this won't be the case,")
|
||||
SendTo(nick, "I will not offer any warranty whatsoever for the use of %s or the" % config.tipbot_name)
|
||||
SendTo(nick, "return of any %s. Use at your own risk." % coinspecs.name)
|
||||
SendTo(nick, "That being said, I hope you enjoy using it :)")
|
||||
def Info(link,cmd):
|
||||
link.send("Info for %s:" % config.tipbot_name)
|
||||
link.send("Copyright 2014,2015 moneromooo - http://duckpool.mooo.com/tipbot/")
|
||||
link.send("Type !help, or !commands for a list of commands")
|
||||
link.send("NO WARRANTY, YOU MAY LOSE YOUR COINS")
|
||||
link.send("By sending your %s to %s, you are giving up their control" % (coinspecs.name, config.tipbot_name))
|
||||
link.send("to whoever runs the tipbot. Any tip you make/receive using %s" % config.tipbot_name)
|
||||
link.send("is obviously not anonymous. %s's wallet may end up corrupt, or be" % config.tipbot_name)
|
||||
link.send("stolen, the server compromised, etc. While I hope this won't be the case,")
|
||||
link.send("I will not offer any warranty whatsoever for the use of %s or the" % config.tipbot_name)
|
||||
link.send("return of any %s. Use at your own risk." % coinspecs.name)
|
||||
link.send("That being said, I hope you enjoy using it :)")
|
||||
|
||||
def InitScanBlockHeight():
|
||||
try:
|
||||
|
@ -201,56 +212,83 @@ def InitScanBlockHeight():
|
|||
except Exception,e:
|
||||
log_error('Failed to initialize scan_block_height: %s' % str(e))
|
||||
|
||||
def ShowActivity(nick,chan,cmd):
|
||||
achan=cmd[1]
|
||||
anick=cmd[2]
|
||||
activity = GetTimeSinceActive(achan,anick)
|
||||
if activity:
|
||||
SendTo(nick,"%s was active in %s %f seconds ago" % (anick,achan,activity))
|
||||
def ShowActivity(link,cmd):
|
||||
anick=GetParam(cmd,1)
|
||||
achan=GetParam(cmd,2)
|
||||
if not anick or not achan:
|
||||
link.send('usage: !show_activity <nick> <chan>')
|
||||
return
|
||||
if anick.find(':') == -1:
|
||||
network = link.network
|
||||
else:
|
||||
SendTo(nick,"%s was never active in %s" % (anick,achan))
|
||||
parts=anick.split(':')
|
||||
network_name=parts[0]
|
||||
anick=parts[1]
|
||||
network = GetNetworkByName(network_name)
|
||||
if network:
|
||||
last_activity = network.get_last_active_time(anick,achan)
|
||||
if last_activity:
|
||||
link.send("%s was active in %s %f seconds ago" % (anick,achan,now-last_activity))
|
||||
else:
|
||||
link.send("%s was never active in %s" % (anick,achan))
|
||||
else:
|
||||
link.send("%s is not a valid network" % network)
|
||||
|
||||
def SendToNick(nick,chan,msg):
|
||||
SendTo(nick,msg)
|
||||
def SendToLink(link,msg):
|
||||
link.send(msg)
|
||||
|
||||
def IsRegistered(nick,chan,cmd):
|
||||
RunRegisteredCommand(nick,chan,SendToNick,"You are registered",SendToNick,"You are not registered")
|
||||
def IsRegistered(link,cmd):
|
||||
RunRegisteredCommand(link,SendToLink,"You are registered",SendToLink,"You are not registered")
|
||||
|
||||
def Reload(nick,chan,cmd):
|
||||
sendto=GetSendTo(nick,chan)
|
||||
def Reload(link,cmd):
|
||||
modulename=GetParam(cmd,1)
|
||||
if not modulename:
|
||||
SendTo(sendto,"Usage: reload <modulename>")
|
||||
link.send("Usage: reload <modulename>")
|
||||
return
|
||||
if modulename=="builtin":
|
||||
SendTo(sendto,"Cannot reload builtin module")
|
||||
link.send("Cannot reload builtin module")
|
||||
return
|
||||
if not modulename in sys.modules:
|
||||
link.send("%s is not a dynamic module" % modulename)
|
||||
return
|
||||
log_info('Unloading %s module' % modulename)
|
||||
UnregisterModule(modulename)
|
||||
log_info('Reloading %s module' % modulename)
|
||||
try:
|
||||
reload(sys.modules[modulename])
|
||||
SendTo(sendto,'%s reloaded' % modulename)
|
||||
link.send('%s reloaded' % modulename)
|
||||
except Exception,e:
|
||||
log_error('Failed to load module "%s": %s' % (modulename, str(e)))
|
||||
SendTo(sendto,'An error occured')
|
||||
link.send('An error occured')
|
||||
|
||||
def Disable(nick,chan,cmd):
|
||||
def Disable(link,cmd):
|
||||
global disabled
|
||||
sendto=GetSendTo(nick,chan)
|
||||
disabled = True
|
||||
SendTo(sendto,'%s disabled, will require restart' % config.tipbot_name)
|
||||
link.send('%s disabled, will require restart' % config.tipbot_name)
|
||||
|
||||
def OnIdle():
|
||||
if disabled:
|
||||
return
|
||||
RunIdleFunctions([irc,redisdb])
|
||||
|
||||
def OnIdentified(nick, identified):
|
||||
def Quit(link,cmd):
|
||||
global networks
|
||||
msg = ""
|
||||
for w in cmd[1:]:
|
||||
msg = msg + " " + w
|
||||
for network in networks:
|
||||
log_info('Quitting %s network' % network.name)
|
||||
network.quit()
|
||||
networks = []
|
||||
|
||||
def OnIdle():
|
||||
RunIdleFunctions()
|
||||
|
||||
def OnIdentified(link, identified):
|
||||
if disabled:
|
||||
log_info('Ignoring identified notification for %s while disabled' % str(nick))
|
||||
log_info('Ignoring identified notification for %s while disabled' % str(link.identity()))
|
||||
return
|
||||
RunNextCommand(nick, identified)
|
||||
RunNextCommand(link, identified)
|
||||
|
||||
def RegisterCommands():
|
||||
RegisterCommand({'module': 'builtin', 'name': 'help', 'parms': '[module]', 'function': Help, 'help': "Displays help about %s" % config.tipbot_name})
|
||||
|
@ -267,20 +305,43 @@ def RegisterCommands():
|
|||
RegisterCommand({'module': 'builtin', 'name': 'show_activity', 'function': ShowActivity, 'admin': True, 'help': "Show time since a user was last active"})
|
||||
RegisterCommand({'module': 'builtin', 'name': 'reload', 'function': Reload, 'admin': True, 'help': "Reload a module"})
|
||||
RegisterCommand({'module': 'builtin', 'name': 'disable', 'function': Disable, 'admin': True, 'help': "Disable %s"%config.tipbot_name})
|
||||
RegisterCommand({'module': 'builtin', 'name': 'quit', 'function': Quit, 'admin': True, 'help': "Quit"})
|
||||
|
||||
def OnCommandProxy(cmd,chan,who):
|
||||
def OnCommandProxy(link,cmd):
|
||||
if disabled:
|
||||
log_info('Ignoring command from %s while disabled: %s' % (str(who),str(cmd)))
|
||||
log_info('Ignoring command from %s while disabled: %s' % (str(link.identity()),str(cmd)))
|
||||
return
|
||||
OnCommand(cmd,chan,who,RunAdminCommand,RunRegisteredCommand)
|
||||
link.batch_send_start()
|
||||
try:
|
||||
OnCommand(link,cmd,RunAdminCommand,RunRegisteredCommand)
|
||||
except Exception,e:
|
||||
log_error('Exception running command %s: %s' % (str(cmd),str(e)))
|
||||
link.batch_send_done()
|
||||
|
||||
def MigrateBalances():
|
||||
balances=redis_hgetall('balances')
|
||||
for balance in balances:
|
||||
if balance.find(':') == -1:
|
||||
redis_hset('balances','freenode:'+balance,balances[balance])
|
||||
redis_hdel('balances',balance)
|
||||
|
||||
redisdb = connect_to_redis(config.redis_host,config.redis_port)
|
||||
irc = connect_to_irc(config.irc_network,config.irc_port,config.tipbot_name,GetPassword(),config.irc_send_delay)
|
||||
InitScanBlockHeight()
|
||||
RegisterCommands()
|
||||
redisdb = connect_to_redis(config.redis_host,config.redis_port)
|
||||
MigrateBalances()
|
||||
InitScanBlockHeight()
|
||||
|
||||
# TODO: make this be created when the module is loaded
|
||||
irc = sys.modules["freenode"].FreenodeNetwork()
|
||||
irc.set_callbacks(OnCommandProxy,OnIdentified)
|
||||
if irc.connect(config.irc_network,config.irc_port,config.tipbot_name,GetPassword(),config.irc_send_delay):
|
||||
AddNetwork(irc)
|
||||
|
||||
while len(networks)>0:
|
||||
for network in networks:
|
||||
network.update()
|
||||
|
||||
OnIdle()
|
||||
|
||||
IRCLoop(OnIdle,OnIdentified,OnCommandProxy)
|
||||
|
||||
log_info('shutting down redis')
|
||||
redisdb.shutdown
|
||||
|
|
|
@ -41,76 +41,82 @@ def IsBetAmountValid(amount,minbet,maxbet,potential_loss,max_loss,max_loss_ratio
|
|||
|
||||
return True, None
|
||||
|
||||
def IsPlayerBalanceAtLeast(nick,units):
|
||||
def IsPlayerBalanceAtLeast(link,units):
|
||||
try:
|
||||
balance = redis_hget("balances",nick)
|
||||
balance = redis_hget("balances",link.identity())
|
||||
if balance == None:
|
||||
balance = 0
|
||||
balance=long(balance)
|
||||
if units > balance:
|
||||
log_error ('%s does not have enough balance' % nick)
|
||||
log_error ('%s does not have enough balance' % link.user.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):
|
||||
def SetServerSeed(link,game,seed):
|
||||
identity=link.identity()
|
||||
try:
|
||||
redis_hset('%s:serverseed' % game,nick,seed)
|
||||
log_info('%s\'s %s server seed set' % (nick, game))
|
||||
redis_hset('%s:serverseed' % game,identity,seed)
|
||||
log_info('%s\'s %s serverseed set' % (identity, game))
|
||||
except Exception,e:
|
||||
log_error('Failed to set %s server seed for %s: %s' % (game, nick, str(e)))
|
||||
log_error('Failed to set %s server seed for %s: %s' % (game, identity, str(e)))
|
||||
raise
|
||||
|
||||
def GetServerSeed(nick,game):
|
||||
EnsureServerSeed(nick,game)
|
||||
def GetServerSeed(link,game):
|
||||
EnsureServerSeed(link,game)
|
||||
identity=link.identity()
|
||||
try:
|
||||
return redis_hget('%s:serverseed' % game,nick)
|
||||
return redis_hget('%s:serverseed' % game,identity)
|
||||
except Exception,e:
|
||||
log_error('Failed to get %s server seed for %s: %s' % (game, nick, str(e)))
|
||||
log_error('Failed to get %s server seed for %s: %s' % (game, identity, str(e)))
|
||||
raise
|
||||
|
||||
def GenerateServerSeed(nick,game):
|
||||
def GenerateServerSeed(link,game):
|
||||
identity=link.identity()
|
||||
try:
|
||||
salt="kfn3kjg4nkngvekjvn3u4vgb" + ":" + game
|
||||
s=salt+":"+nick+":"+str(time.time())+":"+str(random.randint(0,1000000))
|
||||
s=salt+":"+identity+":"+str(time.time())+":"+str(random.randint(0,1000000))
|
||||
seed=hashlib.sha256(s).hexdigest()
|
||||
SetServerSeed(nick,game,seed)
|
||||
SetServerSeed(link,game,seed)
|
||||
except Exception,e:
|
||||
log_error('Failed to generate %s server seed for %s: %s' % (game,nick,str(e)))
|
||||
log_error('Failed to generate %s server seed for %s: %s' % (game,identity,str(e)))
|
||||
raise
|
||||
|
||||
def EnsureServerSeed(nick,game):
|
||||
if not redis_hexists('%s:serverseed' % game,nick):
|
||||
GenerateServerSeed(nick,game)
|
||||
def EnsureServerSeed(link,game):
|
||||
if not redis_hexists('%s:serverseed' % game,link.identity()):
|
||||
GenerateServerSeed(link,game)
|
||||
|
||||
def SetPlayerSeed(nick,game,seed):
|
||||
def SetPlayerSeed(link,game,seed):
|
||||
identity=link.identity()
|
||||
try:
|
||||
redis_hset('%s:playerseed' % game,nick,seed)
|
||||
log_info('%s\'s %s playerseed set to %s' % (nick, game, seed))
|
||||
redis_hset('%s:playerseed' % game,identity,seed)
|
||||
log_info('%s\'s %s playerseed set to %s' % (identity, game, seed))
|
||||
except Exception,e:
|
||||
log_error('Failed to set %s player seed for %s: %s' % (game, nick, str(e)))
|
||||
log_error('Failed to set %s player seed for %s: %s' % (game, identity, str(e)))
|
||||
raise
|
||||
|
||||
def GetPlayerSeed(nick,game):
|
||||
def GetPlayerSeed(link,game):
|
||||
identity=link.identity()
|
||||
try:
|
||||
if not redis_hexists('%s:playerseed' % game,nick):
|
||||
if not redis_hexists('%s:playerseed' % game,identity):
|
||||
return ""
|
||||
return redis_hget('%s:playerseed' % game,nick)
|
||||
return redis_hget('%s:playerseed' % game,identity)
|
||||
except Exception,e:
|
||||
log_error('Failed to get %s player seed for %s: %s' % (game, nick, str(e)))
|
||||
log_error('Failed to get %s player seed for %s: %s' % (game, identity, str(e)))
|
||||
raise
|
||||
|
||||
def GetServerSeedHash(nick,game):
|
||||
seed = GetServerSeed(nick,game)
|
||||
def GetServerSeedHash(link,game):
|
||||
seed = GetServerSeed(link,game)
|
||||
return hashlib.sha256(seed).hexdigest()
|
||||
|
||||
def RecordGameResult(nick,chan,game,win,lose,units):
|
||||
def RecordGameResult(link,game,win,lose,units):
|
||||
identity=link.identity()
|
||||
try:
|
||||
p = redis_pipeline()
|
||||
tname="%s:stats:"%game+nick
|
||||
rtname="%s:stats:reset:"%game+nick
|
||||
tname="%s:stats:"%game+identity
|
||||
rtname="%s:stats:reset:"%game+identity
|
||||
alltname="%s:stats:"%game
|
||||
p.hincrby(tname,"bets",1)
|
||||
p.hincrby(rtname,"bets",1)
|
||||
|
@ -119,7 +125,7 @@ def RecordGameResult(nick,chan,game,win,lose,units):
|
|||
p.hincrby(rtname,"wagered",units)
|
||||
p.hincrby(alltname,"wagered",units)
|
||||
if win:
|
||||
p.hincrby("balances",nick,units)
|
||||
p.hincrby("balances",identity,units)
|
||||
p.hincrby(tname,"won",units)
|
||||
p.hincrby(rtname,"won",units)
|
||||
p.hincrby(alltname,"won",units)
|
||||
|
@ -127,7 +133,7 @@ def RecordGameResult(nick,chan,game,win,lose,units):
|
|||
p.hincrby(rtname,"nwon",1)
|
||||
p.hincrby(alltname,"nwon",1)
|
||||
if lose:
|
||||
p.hincrby("balances",nick,-units)
|
||||
p.hincrby("balances",identity,-units)
|
||||
p.hincrby(tname,"lost",units)
|
||||
p.hincrby(rtname,"lost",units)
|
||||
p.hincrby(alltname,"lost",units)
|
||||
|
@ -139,8 +145,9 @@ def RecordGameResult(nick,chan,game,win,lose,units):
|
|||
log_error('RecordGameResult: exception updating redis: %s' % str(e))
|
||||
raise
|
||||
|
||||
def ShowGameStats(sendto,snick,title,game):
|
||||
tname="%s:stats:"%game+snick
|
||||
def ShowGameStats(link,sidentity,title,game):
|
||||
identity=IdentityFromString(link,sidentity)
|
||||
tname="%s:stats:"%game+sidentity
|
||||
try:
|
||||
bets=redis_hget(tname,"bets")
|
||||
wagered=redis_hget(tname,"wagered")
|
||||
|
@ -150,10 +157,10 @@ def ShowGameStats(sendto,snick,title,game):
|
|||
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")
|
||||
link.send("An error occured")
|
||||
return
|
||||
if not bets:
|
||||
SendTo(sendto,"No %s stats available for %s" % (game,title))
|
||||
link.send("No %s stats available for %s" % (game,title))
|
||||
return
|
||||
|
||||
bets = long(bets)
|
||||
|
@ -164,7 +171,7 @@ def ShowGameStats(sendto,snick,title,game):
|
|||
nlost = long(nlost or 0)
|
||||
|
||||
if bets==0:
|
||||
SenTo(sendto,"No %s stats available for %s" % (game,title))
|
||||
link.send("No %s stats available for %s" % (game,title))
|
||||
return
|
||||
|
||||
swagered = AmountToString(wagered)
|
||||
|
@ -175,12 +182,13 @@ def ShowGameStats(sendto,snick,title,game):
|
|||
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))
|
||||
link.send("%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):
|
||||
def ResetGameStats(link,sidentity,game):
|
||||
identity=IdentityFromString(link,sidentity)
|
||||
try:
|
||||
p = redis_pipeline()
|
||||
tname="%s:stats:reset:"%game+snick
|
||||
tname="%s:stats:reset:"%game+sidentity
|
||||
bets=p.hset(tname,"bets",0)
|
||||
wagered=p.hset(tname,"wagered",0)
|
||||
won=p.hset(tname,"won",0)
|
||||
|
@ -188,31 +196,31 @@ def ResetGameStats(sendto,snick,game):
|
|||
nwon=p.hset(tname,"nwon",0)
|
||||
nlost=p.hset(tname,"nlost",0)
|
||||
p.execute()
|
||||
SendTo(sendto,"%s stats reset for %s" % (game,snick))
|
||||
link.send("%s stats reset for %s" % (game,sidentity))
|
||||
except Exception,e:
|
||||
log_error('Error resetting %s stats for %s: %s' % (game,snick,str(e)))
|
||||
log_error('Error resetting %s stats for %s: %s' % (game,sidentity,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)))
|
||||
identities = redis_hgetall("balances")
|
||||
for identity in identities:
|
||||
ib = redis_hget("balances", identity)
|
||||
unlocked_balance = unlocked_balance - long(ib)
|
||||
log_log('RetrieveHouseBalance: subtracting %s from %s to give %s' % (AmountToString(ib), identity, AmountToString(unlocked_balance)))
|
||||
|
||||
rbal=redis_get('reserve_balance')
|
||||
if rbal:
|
||||
unlocked_balance = unlocked_balance - long(rbal)
|
||||
log_log('RetrieveHouseBalance: subtracting %s reserve balance to give %s' % (AmountToString(rbal), AmountToString(unlocked_balance)))
|
||||
|
||||
if unlocked_balance < 0:
|
||||
raise RuntimeError('Negative house balance')
|
||||
return
|
||||
return unlocked_balance
|
||||
|
||||
def ReserveBalance(nick,chan,cmd):
|
||||
sendto=GetSendTo(nick,chan)
|
||||
def ReserveBalance(link,cmd):
|
||||
rbal=GetParam(cmd,1)
|
||||
if rbal:
|
||||
try:
|
||||
|
@ -222,36 +230,36 @@ def ReserveBalance(nick,chan,cmd):
|
|||
rbal = long(rbal * coinspecs.atomic_units)
|
||||
except Exception,e:
|
||||
log_error('SetReserveBalance: invalid balance: %s' % str(e))
|
||||
SendTo(sendto,"Invalid balance")
|
||||
link.send("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")
|
||||
link.send("Failed to get current reserve balance")
|
||||
return
|
||||
if rbal == None:
|
||||
SendTo(sendto,"Reserve balance is %s" % AmountToString(current_rbal))
|
||||
link.send("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")
|
||||
link.send("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')
|
||||
link.send('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")
|
||||
link.send("Failed to set reserve balance")
|
||||
return
|
||||
SendTo(sendto,"Reserve balance set")
|
||||
link.send("Reserve balance set")
|
||||
|
||||
RegisterCommand({
|
||||
'module': 'betting',
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
import tipbot.config as config
|
||||
from tipbot.utils import *
|
||||
from tipbot.ircutils import *
|
||||
|
||||
modules = dict()
|
||||
commands = dict()
|
||||
|
@ -19,48 +18,46 @@ calltable=dict()
|
|||
idles = []
|
||||
cleanup = dict()
|
||||
|
||||
def SendToProxy(nick,chan,msg):
|
||||
SendTo(GetSendTo(nick,chan),msg)
|
||||
def SendToProxy(link,msg):
|
||||
link.send(msg)
|
||||
|
||||
def RunRegisteredCommand(nick,chan,ifyes,yesdata,ifno,nodata):
|
||||
if nick not in calltable:
|
||||
calltable[nick] = []
|
||||
calltable[nick].append([chan,ifyes,yesdata,ifno,nodata])
|
||||
if nick in registered_users:
|
||||
RunNextCommand(nick,True)
|
||||
def RunRegisteredCommand(link,ifyes,yesdata,ifno,nodata):
|
||||
if link.identity() not in calltable:
|
||||
calltable[link.identity()] = []
|
||||
calltable[link.identity()].append([link,ifyes,yesdata,ifno,nodata])
|
||||
if link.network.is_identified(link):
|
||||
RunNextCommand(link,True)
|
||||
else:
|
||||
SendTo('nickserv', "ACC " + nick)
|
||||
link.network.identify(link)
|
||||
|
||||
def IsAdmin(nick):
|
||||
return nick in config.admins
|
||||
def IsAdmin(link):
|
||||
return link.identity() in config.admins
|
||||
|
||||
def RunAdminCommand(nick,chan,ifyes,yesdata,ifno,nodata):
|
||||
if not IsAdmin(nick):
|
||||
log_warn('RunAdminCommand: nick %s is not admin, cannot call %s with %s' % (str(nick),str(ifyes),str(yesdata)))
|
||||
SendTo(nick, "Access denied")
|
||||
def RunAdminCommand(link,ifyes,yesdata,ifno,nodata):
|
||||
if not IsAdmin(link):
|
||||
log_warn('RunAdminCommand: %s is not admin, cannot call %s with %s' % (str(link.identity()),str(ifyes),str(yesdata)))
|
||||
link.send("Access denied")
|
||||
return
|
||||
RunRegisteredCommand(nick,chan,ifyes,yesdata,ifno,nodata)
|
||||
RunRegisteredCommand(link,ifyes,yesdata,ifno,nodata)
|
||||
|
||||
def RunNextCommand(nick,registered):
|
||||
if registered:
|
||||
registered_users.add(nick)
|
||||
else:
|
||||
registered_users.discard(nick)
|
||||
if nick not in calltable:
|
||||
log_error( 'Nothing in queue for %s' % nick)
|
||||
def RunNextCommand(link,registered):
|
||||
identity = link.identity()
|
||||
if identity not in calltable:
|
||||
log_error('Nothing in queue for %s' % identity)
|
||||
return
|
||||
try:
|
||||
link=calltable[identity][0][0]
|
||||
if registered:
|
||||
calltable[nick][0][1](nick,calltable[nick][0][0],calltable[nick][0][2])
|
||||
calltable[identity][0][1](link,calltable[identity][0][2])
|
||||
else:
|
||||
calltable[nick][0][3](nick,calltable[nick][0][0],calltable[nick][0][4])
|
||||
del calltable[nick][0]
|
||||
calltable[identity][0][3](link,calltable[identity][0][4])
|
||||
del calltable[identity][0]
|
||||
except Exception, e:
|
||||
log_error('RunNextCommand: Exception in action, continuing: %s' % str(e))
|
||||
del calltable[nick][0]
|
||||
del calltable[identity][0]
|
||||
|
||||
def Commands(nick,chan,cmd):
|
||||
if IsAdmin(nick):
|
||||
def Commands(link,cmd):
|
||||
if IsAdmin(link):
|
||||
all = True
|
||||
else:
|
||||
all = False
|
||||
|
@ -68,9 +65,9 @@ def Commands(nick,chan,cmd):
|
|||
module_name = GetParam(cmd,1)
|
||||
|
||||
if module_name:
|
||||
SendTo(nick, "Commands for %s's %s module:" % (config.tipbot_name,module_name))
|
||||
link.send_private("Commands for %s's %s module:" % (config.tipbot_name,module_name))
|
||||
else:
|
||||
SendTo(nick, "Commands for %s (use !commands <modulename> for help about the module's commands):" % config.tipbot_name)
|
||||
link.send_private("Commands for %s (use !commands <modulename> for help about the module's commands):" % config.tipbot_name)
|
||||
|
||||
msgs = dict()
|
||||
for command_name in commands:
|
||||
|
@ -84,7 +81,7 @@ def Commands(nick,chan,cmd):
|
|||
synopsis = c['name']
|
||||
if 'parms' in c:
|
||||
synopsis = synopsis + " " + c['parms']
|
||||
SendTo(nick, "%s - %s" % (synopsis, c['help']))
|
||||
link.send_private("%s - %s" % (synopsis, c['help']))
|
||||
else:
|
||||
if module in msgs:
|
||||
msgs[module] = msgs[module] +(", ")
|
||||
|
@ -94,7 +91,7 @@ def Commands(nick,chan,cmd):
|
|||
|
||||
if not module_name:
|
||||
for msg in msgs:
|
||||
SendTo(nick, "%s" % msgs[msg])
|
||||
link.send_private("%s" % msgs[msg])
|
||||
|
||||
def RegisterModule(module):
|
||||
if module['name'] in modules:
|
||||
|
@ -131,7 +128,7 @@ def RegisterIdleFunction(module,function):
|
|||
def RegisterCleanupFunction(module,function):
|
||||
cleanup.append((module,function))
|
||||
|
||||
def OnCommand(cmd,chan,who,check_admin,check_registered):
|
||||
def OnCommand(link,cmd,check_admin,check_registered):
|
||||
cmdparts = cmd[0].split(':')
|
||||
log_log('cmdparts: %s' % str(cmdparts))
|
||||
if len(cmdparts) == 2:
|
||||
|
@ -141,7 +138,7 @@ def OnCommand(cmd,chan,who,check_admin,check_registered):
|
|||
modulename = None
|
||||
cmdname = cmdparts[0]
|
||||
else:
|
||||
SendTo(GetNick(who), "Invalid command, try !help")
|
||||
link.send("Invalid command, try !help")
|
||||
return
|
||||
log_log('modulename: %s, cmdname: %s' % (str(modulename),str(cmdname)))
|
||||
if cmdname in commands:
|
||||
|
@ -153,7 +150,7 @@ def OnCommand(cmd,chan,who,check_admin,check_registered):
|
|||
if msg != "":
|
||||
msg = msg + ", "
|
||||
msg = msg + c['module'] + ":" + cmd[0]
|
||||
SendTo(GetNick(who), "Ambiguous command, try one of: %s" % msg)
|
||||
link.send("Ambiguous command, try one of: %s" % msg)
|
||||
return
|
||||
c = None
|
||||
for command in commands[cmdname]:
|
||||
|
@ -161,34 +158,34 @@ def OnCommand(cmd,chan,who,check_admin,check_registered):
|
|||
c = command
|
||||
break
|
||||
if not c:
|
||||
SendTo(GetNick(who), "Invalid command, try !help")
|
||||
link.send("Invalid command, try !help")
|
||||
return
|
||||
else:
|
||||
c = commands[cmdname][0]
|
||||
if 'admin' in c and c['admin']:
|
||||
check_admin(GetNick(who),chan,c['function'],cmd,SendToProxy,"You must be admin")
|
||||
check_admin(link,c['function'],cmd,SendToProxy,"You must be admin")
|
||||
elif 'registered' in c and c['registered']:
|
||||
check_registered(GetNick(who),chan,c['function'],cmd,SendToProxy,"You must be registered with Freenode")
|
||||
check_registered(link,c['function'],cmd,SendToProxy,"You must be registered with Freenode")
|
||||
else:
|
||||
c['function'](GetNick(who),chan,cmd)
|
||||
c['function'](link,cmd)
|
||||
else:
|
||||
SendTo(GetNick(who), "Invalid command, try !help")
|
||||
link.send("Invalid command, try !help")
|
||||
|
||||
def RunIdleFunctions(param):
|
||||
def RunIdleFunctions(param=None):
|
||||
for f in idles:
|
||||
try:
|
||||
f[1](param)
|
||||
except Exception,e:
|
||||
log_error("Exception running idle function %s from module %s: %s" % (str(f[1]),str(f[2]),str(e)))
|
||||
|
||||
def RunModuleHelpFunction(module,nick,chan):
|
||||
def RunModuleHelpFunction(module,link):
|
||||
if module in modules:
|
||||
try:
|
||||
modules[module]['help'](nick,chan)
|
||||
modules[module]['help'](link)
|
||||
except Exception,e:
|
||||
log_error("Exception running help function %s from module %s: %s" % (str(modules[module]['help']),str(module),str(e)))
|
||||
else:
|
||||
SendTo(nick,'No help found for module %s' % module)
|
||||
link.send_private('No help found for module %s' % module)
|
||||
|
||||
def UnregisterModule(module):
|
||||
global commands
|
||||
|
|
|
@ -35,7 +35,7 @@ min_withdraw_amount = None # None defaults to the withdrawal fee
|
|||
withdrawal_mixin=0
|
||||
disable_withdraw_on_error = True
|
||||
|
||||
admins = ["moneromooo", "moneromoo"]
|
||||
admins = ["freenode:moneromooo", "freenode:moneromoo"]
|
||||
|
||||
# list of nicks to ignore for rains - bots, trolls, etc
|
||||
no_rain_to_nicks = []
|
||||
|
|
19
tipbot/group.py
Normal file
19
tipbot/group.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
#!/bin/python
|
||||
#
|
||||
# Cryptonote tipbot - group
|
||||
# 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.
|
||||
#
|
||||
|
||||
class Group:
|
||||
def __init__(self,network,name):
|
||||
self.network=network
|
||||
self.name=name
|
||||
|
||||
def send(self,msg):
|
||||
self.network.send_group(self,msg)
|
||||
|
|
@ -1,515 +0,0 @@
|
|||
#!/bin/python
|
||||
#
|
||||
# Cryptonote tipbot - IRC routines
|
||||
# Copyright 2014 moneromooo
|
||||
# Inspired by "Simple Python IRC bot" by berend
|
||||
#
|
||||
# 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 socket
|
||||
import ssl
|
||||
import select
|
||||
import time
|
||||
import string
|
||||
import base64
|
||||
import re
|
||||
import tipbot.config as config
|
||||
from tipbot.log import log, log_error, log_warn, log_info, log_log
|
||||
|
||||
irc_line_delay = 0
|
||||
irc = None
|
||||
sslirc = None
|
||||
irc_password = ""
|
||||
irc_min_send_delay = 0.05 # seconds
|
||||
irc_max_send_delay = 1.1 # seconds
|
||||
|
||||
last_ping_time = time.time()
|
||||
last_send_time = 0
|
||||
current_send_delay = irc_min_send_delay
|
||||
irc_network = None
|
||||
irc_port = None
|
||||
irc_name = None
|
||||
irc_quitting = False
|
||||
|
||||
userstable=dict()
|
||||
registered_users=set()
|
||||
|
||||
def log_IRCRECV(msg):
|
||||
log("IRCRECV",msg)
|
||||
|
||||
def log_IRCSEND(msg):
|
||||
log("IRCSEND",msg)
|
||||
|
||||
def SendIRC(msg):
|
||||
global last_send_time, current_send_delay
|
||||
|
||||
t = time.time()
|
||||
dt = t - last_send_time
|
||||
if dt < current_send_delay:
|
||||
time.sleep (current_send_delay - dt)
|
||||
current_send_delay = current_send_delay * 1.5
|
||||
if current_send_delay > irc_max_send_delay:
|
||||
current_send_delay = irc_max_send_delay
|
||||
else:
|
||||
while dt > current_send_delay * 1.5:
|
||||
dt = dt - current_send_delay
|
||||
current_send_delay = current_send_delay / 1.5
|
||||
if current_send_delay < irc_min_send_delay:
|
||||
current_send_delay = irc_min_send_delay
|
||||
break
|
||||
|
||||
log_IRCSEND(msg)
|
||||
irc_send(msg + '\r\n')
|
||||
last_send_time = time.time()
|
||||
|
||||
def irc_recv(size,flags=None):
|
||||
if config.irc_use_ssl:
|
||||
return sslirc.read(size)
|
||||
else:
|
||||
return irc.recv(size,flags)
|
||||
|
||||
def irc_send(data):
|
||||
if config.irc_use_ssl:
|
||||
return sslirc.write(data)
|
||||
else:
|
||||
return irc.send(data)
|
||||
|
||||
def connect_to_irc(network,port,name,password,delay):
|
||||
global irc
|
||||
global sslirc
|
||||
global irc_line_delay
|
||||
global irc_network
|
||||
global irc_port
|
||||
global irc_line_delay
|
||||
global irc_name
|
||||
global irc_password
|
||||
|
||||
irc_network=network
|
||||
irc_port=port
|
||||
irc_name=name
|
||||
irc_line_delay = delay
|
||||
irc_password=password
|
||||
log_info('Connecting to IRC at %s:%u' % (network, port))
|
||||
try:
|
||||
irc = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
|
||||
if config.irc_use_ssl:
|
||||
try:
|
||||
raise RuntimeError('')
|
||||
irc_ssl_context = ssl.create_default_context()
|
||||
sslirc = irc_ssl_context.wrap_socket(irc, network)
|
||||
sslirc.connect ( ( network, port ) )
|
||||
except Exception,e:
|
||||
log_warn('Failed to create SSL context, using fallback code')
|
||||
irc.connect ( ( network, port ) )
|
||||
sslirc = socket.ssl(irc)
|
||||
except Exception, e:
|
||||
log_error( 'Error initializing IRC: %s' % str(e))
|
||||
exit()
|
||||
log_IRCRECV(irc_recv(4096))
|
||||
if config.irc_use_sasl:
|
||||
SendIRC('CAP REQ :sasl')
|
||||
else:
|
||||
SendIRC ( 'PASS *********')
|
||||
SendIRC ( 'NICK %s' % name)
|
||||
SendIRC ( 'USER %s %s %s :%s' % (name, name, name, name))
|
||||
return irc
|
||||
|
||||
def reconnect_to_irc():
|
||||
connect_to_irc(irc_network,irc_port,irc_name,irc_password,irc_line_delay)
|
||||
|
||||
def SendTo(where,msg):
|
||||
SendIRC ('PRIVMSG ' + where + ' : ' + msg)
|
||||
|
||||
def Join(chan):
|
||||
SendIRC ( 'JOIN ' + chan)
|
||||
|
||||
def Part(chan):
|
||||
SendIRC ( 'PART ' + chan)
|
||||
|
||||
def Quit(msg):
|
||||
global irc_quitting
|
||||
irc_quitting = True
|
||||
SendIRC ( 'QUIT%s' % msg)
|
||||
|
||||
def Who(chan):
|
||||
userstable[chan] = dict()
|
||||
SendIRC ( 'WHO ' + chan)
|
||||
|
||||
def GetNick(data): # Return Nickname
|
||||
nick = data.split('!')[0]
|
||||
nick = nick.replace(':', ' ')
|
||||
nick = nick.replace(' ', '')
|
||||
nick = nick.strip(' \t\n\r')
|
||||
return nick
|
||||
|
||||
def GetSendTo(nick,chan):
|
||||
if chan[0] == '#':
|
||||
return chan
|
||||
return nick
|
||||
|
||||
def UpdateLastActiveTime(chan,nick):
|
||||
if chan[0] != '#':
|
||||
return
|
||||
if not chan in userstable:
|
||||
log_error("UpdateLastActiveTime: %s spoke in %s, but %s not found in users table" % (nick, chan, chan))
|
||||
userstable[chan] = dict()
|
||||
if not nick in userstable[chan]:
|
||||
log_error("UpdateLastActiveTime: %s spoke in %s, but was not found in that channel's users table" % (nick, chan))
|
||||
userstable[chan][nick] = None
|
||||
userstable[chan][nick] = time.time()
|
||||
|
||||
def GetTimeSinceActive(chan,nick):
|
||||
if not chan in userstable:
|
||||
log_error("GetTimeSinceActive: channel %s not found in users table" % chan)
|
||||
return None
|
||||
if not nick in userstable[chan]:
|
||||
log_error("GetTimeSinceActive: %s not found in channel %s's users table" % (nick, chan))
|
||||
return None
|
||||
t = userstable[chan][nick]
|
||||
if t == None:
|
||||
return None
|
||||
dt = time.time() - t
|
||||
if dt < 0:
|
||||
log_error("GetTimeSinceActive: %s active in %s in the future" % (nick, chan))
|
||||
return None
|
||||
return dt
|
||||
|
||||
def GetActiveNicks(chan,seconds):
|
||||
nicks = []
|
||||
if not chan in userstable:
|
||||
return []
|
||||
now = time.time()
|
||||
for nick in userstable[chan]:
|
||||
t = userstable[chan][nick]
|
||||
if t == None:
|
||||
continue
|
||||
dt = now - t
|
||||
if dt < 0:
|
||||
log_error("GetActiveNicks: %s active in %s in the future" % (nick, chan))
|
||||
continue
|
||||
if dt < seconds:
|
||||
nicks.append(nick)
|
||||
return nicks
|
||||
|
||||
def GetUsersTable():
|
||||
return userstable
|
||||
|
||||
def IsAcceptableCommandPrefix(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 Op(to_op, chan):
|
||||
# SendIRC( 'MODE ' + chan + ' +o: ' + to_op)
|
||||
#
|
||||
#def DeOp(to_deop, chan):
|
||||
# SendIRC( 'MODE ' + chan + ' -o: ' + to_deop)
|
||||
#
|
||||
#def Voice(to_v, chan):
|
||||
# SendIRC( 'MODE ' + chan + ' +v: ' + to_v)
|
||||
#
|
||||
#def DeVoice(to_dv, chan):
|
||||
# SendIRC( 'MODE ' + chan + ' -v: ' + to_dv)
|
||||
|
||||
buffered_data = ""
|
||||
def GetIRCLine():
|
||||
global buffered_data
|
||||
idx = buffered_data.find("\n")
|
||||
if idx == -1:
|
||||
try:
|
||||
(r,w,x)=select.select([irc.fileno()],[],[],1)
|
||||
if irc.fileno() in r:
|
||||
newdata=irc_recv(4096,socket.MSG_DONTWAIT)
|
||||
else:
|
||||
newdata = None
|
||||
if 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
|
||||
buffered_data+=newdata
|
||||
idx = buffered_data.find("\n")
|
||||
if idx == -1:
|
||||
ret = buffered_data
|
||||
buffered_data = ""
|
||||
return ret
|
||||
ret = buffered_data[0:idx+1]
|
||||
buffered_data = buffered_data[idx+1:]
|
||||
return ret
|
||||
|
||||
def IRCLoop(on_idle,on_identified,on_command):
|
||||
global userstable
|
||||
global registered_users
|
||||
global last_ping_time
|
||||
|
||||
while True:
|
||||
action = None
|
||||
try:
|
||||
data = GetIRCLine()
|
||||
except Exception,e:
|
||||
log_warn('Exception from GetIRCLine, we were probably disconnected, reconnecting in 5 seconds')
|
||||
time.sleep(5)
|
||||
last_ping_time = time.time()
|
||||
reconnect_to_irc()
|
||||
continue
|
||||
|
||||
# All that must be done even when nothing from IRC - data may be None here
|
||||
on_idle()
|
||||
|
||||
if data == None:
|
||||
if time.time() - last_ping_time > config.irc_timeout_seconds:
|
||||
log_warn('%s seconds without PING, reconnecting in 5 seconds' % config.irc_timeout_seconds)
|
||||
time.sleep(5)
|
||||
last_ping_time = time.time()
|
||||
reconnect_to_irc()
|
||||
continue
|
||||
|
||||
data = data.strip("\r\n")
|
||||
log_IRCRECV(data)
|
||||
|
||||
# consider any IRC data as a ping
|
||||
last_ping_time = time.time()
|
||||
|
||||
if data.find ( config.irc_welcome_line ) != -1:
|
||||
userstable = dict()
|
||||
registered_users.clear()
|
||||
if not config.irc_use_sasl:
|
||||
SendTo("nickserv", "IDENTIFY %s" % irc_password)
|
||||
for chan in config.irc_channels:
|
||||
Join(chan)
|
||||
#ScanWho(None,[chan])
|
||||
|
||||
if data.find ( 'PING' ) == 0:
|
||||
log_log('Got PING, replying PONG')
|
||||
last_ping_time = time.time()
|
||||
SendIRC ( 'PONG ' + data.split() [ 1 ])
|
||||
continue
|
||||
|
||||
if data.startswith('AUTHENTICATE +'):
|
||||
if config.irc_use_sasl:
|
||||
authstring = config.irc_sasl_name + chr(0) + config.irc_sasl_name + chr(0) + irc_password
|
||||
SendIRC('AUTHENTICATE %s' % base64.b64encode(authstring))
|
||||
else:
|
||||
log_warn('Got AUTHENTICATE while not using SASL')
|
||||
|
||||
if data.find('ERROR :Closing Link:') == 0:
|
||||
if irc_quitting:
|
||||
log_info('IRC stopped, bye')
|
||||
break
|
||||
log_warn('We were kicked from IRC, reconnecting in 5 seconds')
|
||||
time.sleep(5)
|
||||
last_ping_time = time.time()
|
||||
reconnect_to_irc()
|
||||
continue
|
||||
|
||||
#--------------------------- Action check --------------------------------#
|
||||
if data.find(':') == -1:
|
||||
continue
|
||||
|
||||
try:
|
||||
cparts = data.lstrip(':').split(' :')
|
||||
if len(cparts) == 0:
|
||||
continue
|
||||
#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, continuing: %s' % str(e))
|
||||
continue
|
||||
|
||||
if action == None:
|
||||
continue
|
||||
|
||||
#print 'cparts: ', str(cparts)
|
||||
#print 'parts: ', str(parts)
|
||||
#print 'text: ', text
|
||||
#print 'who: ', who
|
||||
#print 'action: ', action
|
||||
#print 'chan: ', chan
|
||||
|
||||
# if data.find('#') != -1:
|
||||
# action = data.split('#')[0]
|
||||
# action = action.split(' ')[1]
|
||||
|
||||
# if data.find('NICK') != -1:
|
||||
# if data.find('#') == -1:
|
||||
# action = 'NICK'
|
||||
|
||||
#----------------------------- Actions -----------------------------------#
|
||||
try:
|
||||
if action == 'CAP':
|
||||
if parts[2] == '*' and parts[3] == 'ACK':
|
||||
log_info('CAP ACK received from server')
|
||||
SendIRC('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')
|
||||
if who == "NickServ!NickServ@services.":
|
||||
#if text.find('Information on ') != -1:
|
||||
# ns_nick = text.split(' ')[2].strip("\002")
|
||||
# print 'NickServ says %s is registered' % ns_nick
|
||||
# PerformNextAction(ns_nick, True)
|
||||
#elif text.find(' is not registered') != -1:
|
||||
# ns_nick = text.split(' ')[0].strip("\002")
|
||||
# print 'NickServ says %s is not registered' % ns_nick
|
||||
# PerformNextAction(ns_nick, False)
|
||||
if text.find(' ACC ') != -1:
|
||||
stext = text.split(' ')
|
||||
ns_nick = stext[0]
|
||||
ns_acc = stext[1]
|
||||
ns_status = stext[2]
|
||||
if ns_acc == "ACC":
|
||||
if ns_status == "3":
|
||||
log_info('NickServ says %s is identified' % ns_nick)
|
||||
on_identified(ns_nick, True)
|
||||
else:
|
||||
log_info('NickServ says %s is not identified' % ns_nick)
|
||||
on_identified(ns_nick, False)
|
||||
else:
|
||||
log_error('ACC line not as expected...')
|
||||
|
||||
elif action == '903':
|
||||
log_info('SASL authentication success')
|
||||
SendIRC('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 userstable[who_chan]:
|
||||
userstable[who_chan][who_chan_user] = None
|
||||
log_log("New list of users in %s: %s" % (who_chan, str(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 userstable[who_chan]:
|
||||
if who_chan_user[0] == "@":
|
||||
who_chan_user = who_chan_user[1:]
|
||||
userstable[who_chan][who_chan_user] = None
|
||||
log_log("New list of users in %s: %s" % (who_chan, str(userstable[who_chan].keys())))
|
||||
except Exception,e:
|
||||
log_error('Failed to parse "353" line: %s: %s' % (data, str(e)))
|
||||
|
||||
elif action == 'PRIVMSG':
|
||||
UpdateLastActiveTime(chan,GetNick(who))
|
||||
# resplit to avoid splitting text that contains ':'
|
||||
text = data.split(':',2)[2]
|
||||
text=text.strip()
|
||||
exidx = text.find('!')
|
||||
if exidx != -1 and len(text)>exidx+1 and text[exidx+1] in string.ascii_letters and IsAcceptableCommandPrefix(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 cmd[0] == 'join':
|
||||
# Join('#' + cmd[1])
|
||||
#elif cmd[0] == 'part':
|
||||
# Part('#' + cmd[1])
|
||||
on_command(cmd,chan,who)
|
||||
|
||||
elif action == 'JOIN':
|
||||
nick = GetNick(who)
|
||||
log_info('%s joined the channel' % nick)
|
||||
if not chan in userstable:
|
||||
userstable[chan] = dict()
|
||||
if nick in userstable[chan]:
|
||||
log_warn('%s joined, but already in %s' % (nick, chan))
|
||||
else:
|
||||
userstable[chan][nick] = None
|
||||
log_log("New list of users in %s: %s" % (chan, str(userstable[chan].keys())))
|
||||
|
||||
elif action == 'PART':
|
||||
nick = GetNick(who)
|
||||
log_info('%s left the channel' % nick)
|
||||
if not nick in userstable[chan]:
|
||||
log_warn('%s left, but was not in %s' % (nick, chan))
|
||||
else:
|
||||
del userstable[chan][nick]
|
||||
log_log("New list of users in %s: %s" % (chan, str(userstable[chan].keys())))
|
||||
|
||||
elif action == 'QUIT':
|
||||
nick = GetNick(who)
|
||||
log_info('%s quit' % nick)
|
||||
removed_list = ""
|
||||
for chan in userstable:
|
||||
log_log("Checking in %s" % chan)
|
||||
if nick in userstable[chan]:
|
||||
removed_list = removed_list + " " + chan
|
||||
del userstable[chan][nick]
|
||||
log_log("New list of users in %s: %s" % (chan, str(userstable[chan].keys())))
|
||||
|
||||
elif action == 'KICK':
|
||||
nick = parts[3]
|
||||
log_info('%s was kicked' % nick)
|
||||
removed_list = ""
|
||||
for chan in userstable:
|
||||
log_log("Checking in %s" % chan)
|
||||
if nick in userstable[chan]:
|
||||
removed_list = removed_list + " " + chan
|
||||
del userstable[chan][nick]
|
||||
log_log("New list of users in %s: %s" % (chan, str(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 userstable:
|
||||
log_log('checking %s' % c)
|
||||
if nick in userstable[c]:
|
||||
del userstable[c][nick]
|
||||
if new_nick in userstable[c]:
|
||||
log_warn('%s is the new name of %s, but was already in %s' % (new_nick, nick, c))
|
||||
else:
|
||||
userstable[c][new_nick] = None
|
||||
log_log("New list of users in %s: %s" % (c, str(userstable[c].keys())))
|
||||
|
||||
except Exception,e:
|
||||
log_error('Exception in top level action processing: %s' % str(e))
|
||||
|
62
tipbot/link.py
Normal file
62
tipbot/link.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
#!/bin/python
|
||||
#
|
||||
# Cryptonote tipbot - link
|
||||
# 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.
|
||||
#
|
||||
|
||||
from tipbot.log import log_error, log_warn, log_info, log_log
|
||||
|
||||
class Link:
|
||||
def __init__(self,network,user,group=None,data=None):
|
||||
self.network=network
|
||||
self.user=user
|
||||
self.group=group
|
||||
self.data=data
|
||||
self.batch_message = None
|
||||
self.batch_message_private = None
|
||||
|
||||
def __repr__(self):
|
||||
return '<link: network %s, user %s, group %s, data %s>' % (str(self.network),str(self.user),str(self.group),str(self.data))
|
||||
|
||||
def identity(self):
|
||||
return self.network.name+":"+self.user.nick
|
||||
|
||||
def send(self,msg):
|
||||
if self.batch_message != None:
|
||||
self.batch_message.append(msg)
|
||||
else:
|
||||
return self._send(msg)
|
||||
|
||||
def send_private(self,msg):
|
||||
if self.batch_message_private != None:
|
||||
self.batch_message_private.append(msg)
|
||||
else:
|
||||
return self._send_private(msg)
|
||||
|
||||
def _send(self,msg):
|
||||
if self.group:
|
||||
self.network.send_group(self.group,msg,self.data)
|
||||
else:
|
||||
self.network.send_user(self.user,msg,self.data)
|
||||
|
||||
def _send_private(self,msg):
|
||||
self.network.send_user(self.user,msg,self.data)
|
||||
|
||||
def batch_send_start(self):
|
||||
self.batch_message = []
|
||||
self.batch_message_private = []
|
||||
|
||||
def batch_send_done(self):
|
||||
if self.batch_message != None:
|
||||
if len(self.batch_message)>0:
|
||||
self._send("\n".join(self.batch_message))
|
||||
self.batch_message = None
|
||||
if self.batch_message_private != None:
|
||||
if len(self.batch_message_private)>0:
|
||||
self._send_private("\n".join(self.batch_message_private))
|
||||
self.batch_message_private = None
|
|
@ -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")
|
||||
|
||||
|
||||
|
||||
|
|
54
tipbot/modules/freenode.py
Normal file
54
tipbot/modules/freenode.py
Normal 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
|
||||
|
|
@ -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)
|
||||
})
|
||||
|
|
|
@ -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__,
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
||||
|
||||
|
|
58
tipbot/network.py
Normal file
58
tipbot/network.py
Normal file
|
@ -0,0 +1,58 @@
|
|||
#!/bin/python
|
||||
#
|
||||
# Cryptonote tipbot - network
|
||||
# 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.
|
||||
#
|
||||
|
||||
from link import Link
|
||||
from user import User
|
||||
from group import Group
|
||||
|
||||
class Network:
|
||||
def __init__(self,name):
|
||||
self.name=name
|
||||
|
||||
def connect(self):
|
||||
pass
|
||||
|
||||
def disconnect(self):
|
||||
pass
|
||||
|
||||
def send_group(self,group,msg,data=None):
|
||||
pass
|
||||
|
||||
def send_user(self,user,msg,data=None):
|
||||
pass
|
||||
|
||||
def identify(self,link):
|
||||
pass
|
||||
|
||||
def dump_users(self):
|
||||
pass
|
||||
|
||||
def set_callbacks(self,on_command,on_identified):
|
||||
self.on_command=on_command
|
||||
self.on_identified=on_identified
|
||||
|
||||
def get_last_active_time(user_name,group_name=None):
|
||||
return None
|
||||
|
||||
def get_active_users(seconds,group_name=None):
|
||||
return []
|
||||
|
||||
def get_users(group_name=None):
|
||||
return []
|
||||
|
||||
def update_users_list(self,group_name=None):
|
||||
pass
|
||||
|
||||
def update(self):
|
||||
return True
|
||||
|
||||
def quit(self,msg=None):
|
||||
pass
|
24
tipbot/user.py
Normal file
24
tipbot/user.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
#!/bin/python
|
||||
#
|
||||
# Cryptonote tipbot - user
|
||||
# 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.
|
||||
#
|
||||
|
||||
class User:
|
||||
def __init__(self,network,nick):
|
||||
self.network=network
|
||||
self.nick=nick
|
||||
|
||||
def check_registered(self):
|
||||
pass
|
||||
|
||||
def is_registered(self):
|
||||
if not self.registered:
|
||||
self.check_registered()
|
||||
return self.registered
|
||||
|
|
@ -16,9 +16,9 @@ import httplib
|
|||
import tipbot.config as config
|
||||
import tipbot.coinspecs as coinspecs
|
||||
from tipbot.log import log_error, log_warn, log_info, log_log
|
||||
from tipbot.ircutils import *
|
||||
from tipbot.redisdb import *
|
||||
|
||||
networks=[]
|
||||
|
||||
def GetPassword():
|
||||
try:
|
||||
|
@ -40,19 +40,26 @@ def GetParam(parms,idx):
|
|||
return parms[idx]
|
||||
return None
|
||||
|
||||
def GetPaymentID(nick):
|
||||
def GetPaymentID(link):
|
||||
salt="2u3g55bkwrui32fi3g4bGR$j5g4ugnujb-"+coinspecs.name+"-";
|
||||
p = hashlib.sha256(salt+nick).hexdigest();
|
||||
p = hashlib.sha256(salt+link.identity()).hexdigest();
|
||||
try:
|
||||
redis_hset("paymentid",p,nick)
|
||||
redis_hset("paymentid",p,link.identity())
|
||||
except Exception,e:
|
||||
log_error('GetPaymentID: failed to set payment ID for %s to redis: %s' % (nick,str(e)))
|
||||
log_error('GetPaymentID: failed to set payment ID for %s to redis: %s' % (link.identity(),str(e)))
|
||||
return p
|
||||
|
||||
def GetNickFromPaymentID(p):
|
||||
nick = redis_hget("paymentid",p)
|
||||
log_log('PaymentID %s => %s' % (p, str(nick)))
|
||||
return nick
|
||||
def GetIdentityFromPaymentID(p):
|
||||
if not redis_hexists("paymentid",p):
|
||||
log_log('PaymentID %s not found' % p)
|
||||
return None
|
||||
identity = redis_hget("paymentid",p)
|
||||
log_log('PaymentID %s => %s' % (p, str(identity)))
|
||||
# HACK - grandfathering pre-network payment IDs
|
||||
if identity.index(':') == -1:
|
||||
log_warn('Pre-network payment ID found, assuming freenode')
|
||||
identity = "freenode:"+identity
|
||||
return identity
|
||||
|
||||
def IsValidAddress(address):
|
||||
if len(address) < coinspecs.address_length[0] or len(address) > coinspecs.address_length[1]:
|
||||
|
@ -205,3 +212,31 @@ def RetrieveTipbotBalance():
|
|||
return
|
||||
return balance, unlocked_balance
|
||||
|
||||
def IdentityFromString(link,s):
|
||||
if s.find(':') == -1:
|
||||
network = link.network.name
|
||||
nick=s
|
||||
else:
|
||||
parts=s.split(':')
|
||||
network=parts[0]
|
||||
nick=parts[1]
|
||||
return network+':'+nick
|
||||
|
||||
def NickFromIdentity(identity):
|
||||
return identity.split(':')[1]
|
||||
|
||||
def AddNetwork(network):
|
||||
networks.append(network)
|
||||
|
||||
def GetNetworkByName(name):
|
||||
for network in networks:
|
||||
if network.name==name:
|
||||
return network
|
||||
return None
|
||||
|
||||
def GetNetworkByType(type):
|
||||
for network in networks:
|
||||
if isinstance(network,type):
|
||||
return network
|
||||
return None
|
||||
|
||||
|
|
Loading…
Reference in a new issue