Rain, withdraw, wallet scanning, logs, lots of cleanup

I should commit more often.
This commit is contained in:
moneromooo 2014-12-21 18:50:24 +00:00
parent ca3f55bc35
commit f8cf06c197

702
tipbot.py
View file

@ -1,56 +1,81 @@
#!/bin/python #!/bin/python
#
# Monero tipbot
# Copyright 2014 moneromooo
# Inspired by "Simple Python IRC bot" by berend
#
import socket import socket
import select
import sys import sys
from random import randint import random
import re import re
import redis import redis
import hashlib import hashlib
import json import json
import httplib import httplib
import time
#----------------------------------- Settings --------------------------------------# irc_network = 'irc.freenode.net'
network = 'irc.freenode.net' irc_port = 6667
port = 6667 irc_homechan = '#txtptest000'
homechan = '#txtptest000'
try:
irc = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
irc.connect ( ( network, port ) )
except Exception, e:
print 'Error initializing IRC: ' + str(e)
exit()
print irc.recv ( 4096 )
irc.send ( 'PASS *********\r\n')
irc.send ( 'NICK txtptest\r\n' )
irc.send ( 'USER txtptest txtptest txtptest :txtptest\r\n' )
#----------------------------------------------------------------------------------#
calltable=dict()
redis_host="127.0.0.1" redis_host="127.0.0.1"
redis_port=7777 redis_port=7777
try:
redis = redis.Redis(host=redis_host,port=redis_port)
except Exception, e:
print 'Error initializing redis: ' + str(e)
exit()
#----------------------------------------------------------------------------------#
tipbot_address="TODO"
bitmonerod_host = '127.0.0.1' bitmonerod_host = '127.0.0.1'
bitmonerod_port = 6060 bitmonerod_port = 6060
wallet_host = '127.0.0.1' wallet_host = '127.0.0.1'
wallet_port = 6061 wallet_port = 6061
#---------------------------------- Functions -------------------------------------# wallet_update_time = 30 # seconds
def readAdmin(host): # Return status 0/1 withdrawal_fee = 10000000000
bestand = open('admins.txt', 'r') min_withdraw_amount = 2*withdrawal_fee
for line in bestand: withdraw_disabled = False
if host in line:
status = 1 userstable=dict()
return status calltable=dict()
else: last_wallet_update_time = None
status = 0 last_ping_time = time.time()
return status
def log(stype,msg):
print '%s\t%s\t%s' % (time.ctime(time.time()),stype,str(msg))
def log_error(msg):
log("ERROR",msg)
def log_warn(msg):
log("WARNING",msg)
def log_info(msg):
log("INFO",msg)
def log_log(msg):
log("LOG",msg)
def log_IRCRECV(msg):
log("IRCRECV",msg)
def connect_to_irc(network,port):
global irc
try:
irc = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
irc.connect ( ( network, port ) )
except Exception, e:
log_error( 'Error initializing IRC: %s' % str(e))
exit()
log_IRCRECV(irc.recv ( 4096 ))
irc.send ( 'PASS *********\r\n')
irc.send ( 'NICK monero-tipbot\r\n' )
irc.send ( 'USER monero-tipbot monero-tipbot monero-tipbot :monero-tipbot\r\n' )
def connect_to_redis(host,port):
try:
redis = redis.Redis(host=host,port=port)
except Exception, e:
log_error( 'Error initializing redis: %s' % str(e))
exit()
def GetHost(host): # Return Host def GetHost(host): # Return Host
host = host.split('@')[1] host = host.split('@')[1]
@ -71,8 +96,20 @@ def GetNick(data): # Return Nickname
nick = nick.strip(' \t\n\r') nick = nick.strip(' \t\n\r')
return nick return nick
def GetPassword():
try:
f = open('tipbot-password.txt', 'r')
for p in f:
p = p.strip("\r\n")
f.close()
return p
except Exception,e:
log_error('could not fetch password: %s' % str(e))
raise
return "xxx"
def Send(msg): def Send(msg):
irc.send('PRIVMSG ' + homechan + ' : ' + msg + '\r\n') irc.send('PRIVMSG ' + irc_homechan + ' : ' + msg + '\r\n')
def SendTo(where,msg): def SendTo(where,msg):
irc.send('PRIVMSG ' + where + ' : ' + msg + '\r\n') irc.send('PRIVMSG ' + where + ' : ' + msg + '\r\n')
@ -83,21 +120,32 @@ def Join(chan):
def Part(chan): def Part(chan):
irc.send ( 'PART ' + chan + '\r\n' ) irc.send ( 'PART ' + chan + '\r\n' )
def Who(chan):
irc.send ( 'WHO ' + chan + '\r\n' )
def CheckRegistered(nick,ifyes,yesdata,ifno,nodata): def CheckRegistered(nick,ifyes,yesdata,ifno,nodata):
if nick not in calltable: if nick not in calltable:
calltable[nick] = [] calltable[nick] = []
calltable[nick].append([ifyes,yesdata,ifno,nodata]) calltable[nick].append([ifyes,yesdata,ifno,nodata])
SendTo('nickserv', "INFO " + nick) SendTo('nickserv', "ACC " + nick)
def IsAdmin(nick):
if nick == "moneromooo":
return True
if nick == "moneromoo":
return True
return False
def CheckAdmin(nick,ifyes,yesdata,ifno,nodata): def CheckAdmin(nick,ifyes,yesdata,ifno,nodata):
if nick != "moneromooo" and nick != "moneromoo": if not IsAdmin(nick):
log_warn('CheckAdmin: nick %s is not admin, cannot call %s with %s' % (str(nick),str(ifyes),str(yesdata)))
SendTo(nick, "Access denied") SendTo(nick, "Access denied")
return return
CheckRegistered(nick,ifyes,yesdata,ifno,nodata) CheckRegistered(nick,ifyes,yesdata,ifno,nodata)
def PerformNextAction(nick,registered): def PerformNextAction(nick,registered):
if nick not in calltable: if nick not in calltable:
print 'Nothing in queue for ', nick log_error( 'Nothing in queue for %s' % nick)
return return
try: try:
if registered: if registered:
@ -106,18 +154,36 @@ def PerformNextAction(nick,registered):
calltable[nick][0][2](nick,calltable[nick][0][3]) calltable[nick][0][2](nick,calltable[nick][0][3])
del calltable[nick][0] del calltable[nick][0]
except Exception, e: except Exception, e:
print 'Exception in action, continuing: ' + str(e) log_error('PerformNextAction: Exception in action, continuing: %s' % str(e))
del calltable[nick][0] del calltable[nick][0]
def GetPaymentID(nick): def GetPaymentID(nick):
salt="2u3g55bkwrui32fi3g4bGR$j5g4ugnujb"; salt="2u3g55bkwrui32fi3g4bGR$j5g4ugnujb";
p = hashlib.sha256(salt+nick).hexdigest(); p = hashlib.sha256(salt+nick).hexdigest();
redis.hset("paymentid",p,nick) try:
redis.hset("paymentid",p,nick)
except Exception,e:
log_error('GetPaymentID: failed to set payment ID for %s to redis: %s' % (nick,str(e)))
return p return p
def GetTipbotAddress():
try:
j = SendWalletJSONRPCCommand("getaddress",None)
if not "result" in j:
log_error('GetTipbotAddress: No result found in getaddress reply')
return ERROR
result = j["result"]
if not "address" in result:
log_error('GetTipbotAddress: No address found in getaddress reply')
return ERROR
return result["address"]
except Exception,e:
log_error("GetTipbotAddress: Error retrieving tipbot address: %s" % str(e))
return "ERROR"
def GetNickFromPaymendID(p): def GetNickFromPaymendID(p):
nick = redis.hget("paymentid",p) nick = redis.hget("paymentid",p)
print 'PaymendID %s => %s' % (p, str(nick)) log_log('PaymendID %s => %s' % (p, str(nick)))
return nick return nick
def AmountToString(amount): def AmountToString(amount):
@ -127,43 +193,45 @@ def AmountToString(amount):
if lamount < 1000000: if lamount < 1000000:
samount = "%u tacoshi" % lamount samount = "%u tacoshi" % lamount
elif lamount < 1000000000: elif lamount < 1000000000:
samount = " %f micromonero" % (float(lamount) / 1e6) samount = " %.16g micromonero" % (float(lamount) / 1e6)
elif lamount < 1: elif lamount < 1000000000000:
samount = " %f millimonero" % (float(lamount) / 1e9) samount = " %.16g millimonero" % (float(lamount) / 1e9)
else: else:
samount = "%f monero" % (float(lamount) / 1e12) samount = "%.16g monero" % (float(lamount) / 1e12)
return samount return samount
def GetBalance(nick,data): def GetBalance(nick,data):
print "GetBalance: checking", nick log_log("GetBalance: checking %s" % nick)
sendto=data[0]
try: try:
balance = redis.hget("balances",nick) balance = redis.hget("balances",nick)
if balance == None: if balance == None:
balance = 0 balance = 0
sbalance = AmountToString(balance) sbalance = AmountToString(balance)
SendTo(nick, "%s's balance is %s" % (nick, sbalance)) SendTo(sendto, "%s's balance is %s" % (nick, sbalance))
except Exception, e: except Exception, e:
print 'GetBalance: exception: ' + str(e) log_error('GetBalance: exception: %s' % str(e))
SendTo(nick, "An error has occured") SendTo(sendto, "An error has occured")
def AddBalance(nick,data): def AddBalance(nick,data):
amount=data amount=data
print 'Adding ' + amount + " to " + nick + "'s balance" log_info("AddBalance: Adding %s to %s's balance" % (AmountToString(amount),nick))
try: try:
balance = redis.hincrby("balances",nick,amount) balance = redis.hincrby("balances",nick,amount)
except Exception, e: except Exception, e:
print 'AddBalance: exception: ' + str(e) log_error('AddBalance: exception: %s' % str(e))
SendTo(nick, "An error has occured") SendTo(nick, "An error has occured")
def Tip(nick,data): def Tip(nick,data):
who=data[0] sendto=data[0]
who=data[1]
try: try:
amount=float(data[1]) amount=float(data[2])
except Exception,e: except Exception,e:
SendTo(nick, "Usage: tip nick amount") SendTo(sendto, "Usage: tip nick amount")
return return
print "%s wants to tip %s %.12f monero" % (nick, who, amount) log_info("Tip: %s wants to tip %s %.16g monero" % (nick, who, amount))
try: try:
balance = redis.hget("balances",nick) balance = redis.hget("balances",nick)
if balance == None: if balance == None:
@ -171,58 +239,210 @@ def Tip(nick,data):
balance=long(balance) balance=long(balance)
units=long(amount*1e12) units=long(amount*1e12)
if units <= 0: if units <= 0:
SendTo(nick, "Invalid amount") SendTo(sendto, "Invalid amount")
return return
if units > balance: if units > balance:
SendTo(nick, "You only have %.12f" % (balance / 1e12)) SendTo(sendto, "You only have %.16g" % (balance / 1e12))
return return
print '%s tipping %s %u units, with balance %u' % (nick, who, units, balance) log_info('Tip: %s tipping %s %u units, with balance %u' % (nick, who, units, balance))
try: try:
p = redis.pipeline() p = redis.pipeline()
p.hincrby("balances",nick,-units); p.hincrby("balances",nick,-units);
p.hincrby("balances",who,units) p.hincrby("balances",who,units)
p.execute() p.execute()
SendTo(nick,"%s has tipped %s %.12f monero" % (nick, who, amount)) SendTo(sendto,"%s has tipped %s %.16g monero" % (nick, who, amount))
except Exception, e: except Exception, e:
SendTo(nick, "An error occured") SendTo(sendto, "An error occured")
return return
except Exception, e: except Exception, e:
print 'Tip: exception: ' + str(e) log_error('Tip: exception: %s' % str(e))
SendTo(sendto, "An error has occured")
def ScanWho(nick,data):
chan=data[0]
userstable[chan] = []
Who(chan)
def Rain(nick,data):
chan=data[0]
try:
amount=float(data[1])
except Exception,e:
SendTo(sendto, "Usage: rain amount [users]")
return
try:
if data[2] == None:
users = None
else:
users=long(data[2])
except Exception,e:
SendTo(sendto, "Usage: rain amount [users]")
return
if amount <= 0:
SendTo(sendto, "Usage: rain amount [users]")
return
if users != None and users <= 0:
SendTo(sendto, "Usage: rain amount [users]")
return
units = long(amount * 1e12)
try:
balance = redis.hget("balances",nick)
if balance == None:
balance = 0
balance=long(balance)
if units > balance:
SendTo(sendto, "You only have %s" % (AmountToString(balance)))
return
userlist = userstable[chan][:]
userlist.remove(nick)
if users == None or users > len(userlist):
users = len(userlist)
everyone = True
else:
everyone = False
if units < users:
SendTo(sendto, "This would mean not even a tacoshi 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)))
random.shuffle(userlist)
userlist = userlist[0:users]
log_log("selected users in %s: %s" % (chan, userlist))
user_units = long(units / users)
if everyone:
msg = "%s rained %s on everyone in the channel" % (nick, AmountToString(user_units))
else:
msg = "%s rained %s on:" % (nick, AmountToString(user_units))
pipe = redis.pipeline()
pipe.hincrby("balances",nick,-units)
for user in userlist:
pipe.hincrby("balances",user,user_units)
if not everyone:
msg = msg + " " + user
pipe.execute()
SendTo(sendto, "%s" % msg)
except Exception,e:
log_error('Rain: exception: %s' % str(e))
SendTo(sendto, "An error has occured")
return
def DisableWithdraw():
log_warn('DisableWithdraw: disabled')
withdraw_disabled = True
def Withdraw(nick,data):
address=data[0]
if len(address) != 95:
SendTo(nick, "Invalid address")
return
if address[0] != '4' and address[0] != '9':
SendTo(nick, "Invalid address")
return
if min_withdraw_amount <= 0 or withdrawal_fee <= 0 or min_withdraw_amount < withdrawal_fee:
log_error('Withdraw: Inconsistent withdrawal settings')
SendTo(nick, "An error has occured") SendTo(nick, "An error has occured")
return
log_info("Withdraw: %s wants to withdraw to %s" % (nick, address))
if withdraw_disabled:
log_error('Withdraw: disabled')
SendTo(nick, "Sorry, withdrawal is disabled due to a wallet error which requires admin assistance")
return
try:
balance = redis.hget("balances",nick)
if balance == None:
balance = 0
balance=long(balance)
except Exception, e:
log_error('Withdraw: exception: %s' % str(e))
SendTo(nick, "An error has occured")
return
if balance <= 0 or balance < min_withdraw_amount:
log_info("Withdraw: Minimum withdrawal balance: %s, %s only has %s" % (AmountToString(min_withdraw_amount),nick,AmountToString(balance)))
SendTo(nick, "Minimum withdrawal balance: %s, you only have %s" % (AmountToString(min_withdraw_amount),AmountToString(balance)))
return
try:
fee = long(withdrawal_fee)
topay = long(balance - fee)
log_info('Withdraw: Raw: fee: %s, to pay: %s' % (str(fee), str(topay)))
log_info('Withdraw: fee: %s, to pay: %s' % (AmountToString(fee), AmountToString(topay)))
params = {
'destinations': [{'address': address, 'amount': topay}],
'payment_id': GetPaymentID(nick),
'fee': fee,
'mixin': 0,
'unlock_time': 0,
}
j = SendWalletJSONRPCCommand("transfer",params)
except Exception,e:
log_error('Withdraw: Error in transfer: %s' % str(e))
DisableWithdraw()
SendTo(nick,"An error has occured")
return
if not "result" in j:
log_error('Withdraw: No result in transfer reply')
DisableWithdraw()
SendTo(nick,"An error has occured")
return
result = j["result"]
if not "tx_hash" in result:
log_error('Withdraw: No tx_hash in transfer reply')
DisableWithdraw()
SendTo(nick,"An error has occured")
return
tx_hash = result["tx_hash"]
log_info('%s has withdrawn %s, tx hash %s' % (nick, balance, str(tx_hash)))
try:
redis.hincrby("balances",nick,-balance)
except Exception, e:
log_error('Withdraw: FAILED TO SUBTRACT BALANCE: exception: %s' % str(e))
DisableWithdraw()
log_info('%s has withdrawn %s, tx hash %s' % (nick, balance, str(tx_hash)))
SendTo(nick, "Tx sent: %s" % tx_hash)
def SendJSONRPCCommand(host,port,method,params): def SendJSONRPCCommand(host,port,method,params):
try: try:
http = httplib.HTTPConnection(host,port) http = httplib.HTTPConnection(host,port)
except Exception,e: except Exception,e:
print 'Error connecting to %s:%u: %s' % (host, port, str(e)) log_error('SendJSONRPCCommand: Error connecting to %s:%u: %s' % (host, port, str(e)))
raise raise
d = dict(id="0",jsonrpc="2.0",method=method,params=params) d = dict(id="0",jsonrpc="2.0",method=method,params=params)
try: try:
j = json.dumps(d).encode() j = json.dumps(d).encode()
except Exception,e: except Exception,e:
print 'Failed to encode JSON: ' + str(e) log_error('SendJSONRPCCommand: Failed to encode JSON: %s' % str(e))
http.close() http.close()
raise raise
print 'Sending json as body: ', j log_log('SendJSONRPCCommand: Sending json as body: %s' % j)
headers = None headers = None
try: try:
http.request("POST","/json_rpc",body=j) http.request("POST","/json_rpc",body=j)
except Exception,e: except Exception,e:
print 'Failed to post request: ' + str(e) log_error('SendJSONRPCCommand: Failed to post request: %s' % str(e))
http.close() http.close()
raise raise
response = http.getresponse() response = http.getresponse()
print 'Received reply status: ', response.status log_log('SendJSONRPCCommand: Received reply status: %s' % response.status)
if response.status != 200: if response.status != 200:
print 'Error, not 200' log_error('SendJSONRPCCommand: Error, not 200: %s' % str(response.status))
http.close() http.close()
raise RuntimeError("Error "+response.status) raise RuntimeError("Error "+response.status)
s = response.read() s = response.read()
print 'Received reply: ', s log_log('SendJSONRPCCommand: Received reply: %s' % str(s))
try: try:
j = json.loads(s) j = json.loads(s)
except Exception,e: except Exception,e:
print 'Failed to decode JSON: ' + str(e) log_error('SendJSONRPCCommand: Failed to decode JSON: %s' % str(e))
http.close() http.close()
raise raise
http.close() http.close()
@ -232,27 +452,27 @@ def SendHTMLCommand(host,port,method):
try: try:
http = httplib.HTTPConnection(host,port) http = httplib.HTTPConnection(host,port)
except Exception,e: except Exception,e:
print 'Error connecting to %s:%u: %s' % (host, port, str(e)) log_error('SendHTMLCommand: Error connecting to %s:%u: %s' % (host, port, str(e)))
raise raise
headers = None headers = None
try: try:
http.request("POST","/"+method) http.request("POST","/"+method)
except Exception,e: except Exception,e:
print 'Failed to post request: ' + str(e) log_error('SendHTMLCommand: Failed to post request: %s' % str(e))
http.close() http.close()
raise raise
response = http.getresponse() response = http.getresponse()
print 'Received reply status: ', response.status log_log('SendHTMLCommand: Received reply status: %s' % response.status)
if response.status != 200: if response.status != 200:
print 'Error, not 200' log_error('SendHTMLCommand: Error, not 200: %s' % str(response.status))
http.close() http.close()
raise RuntimeError("Error "+response.status) raise RuntimeError("Error "+response.status)
s = response.read() s = response.read()
print 'Received reply: ', s log_log('SendHTMLCommand: Received reply: %s' % s)
try: try:
j = json.loads(s) j = json.loads(s)
except Exception,e: except Exception,e:
print 'Failed to decode JSON: ' + str(e) log_error('SendHTMLCommand: Failed to decode JSON: %s' % str(e))
http.close() http.close()
raise raise
http.close() http.close()
@ -268,64 +488,77 @@ def SendBitmonerodHTMLCommand(method):
return SendHTMLCommand(bitmonerod_host,bitmonerod_port,method) return SendHTMLCommand(bitmonerod_host,bitmonerod_port,method)
def GetHeight(nick,data): def GetHeight(nick,data):
print '%s wants to know block height' % nick log_info('GetHeight: %s wants to know block height' % nick)
try: try:
j = SendBitmonerodHTMLCommand("getheight") j = SendBitmonerodHTMLCommand("getheight")
except Exception,e: except Exception,e:
log_error('GetHeight: error: %s' % str(e))
SendTo(nick,"An error has occured") SendTo(nick,"An error has occured")
return return
print 'Got reply: ' + str(j) log_log('GetHeight: Got reply: %s' % str(j))
if not "height" in j: if not "height" in j:
print 'Cannot see height in here' log_error('GetHeight: Cannot see height in here')
SendTo(nick, "Height not found") SendTo(nick, "Height not found")
return return
SendTo(nick, "Height: %s" % str(j["height"])) height=j["height"]
log_info('GetHeight: geight is %s' % str(height))
SendTo(nick, "Height: %s" % str(height))
def GetTipbotBalance(nick,data): def GetTipbotBalance(nick,data):
print '%s wants to know the tipbot balance' % nick log_info('%s wants to know the tipbot balance' % nick)
try: try:
j = SendWalletJSONRPCCommand("getbalance",None) j = SendWalletJSONRPCCommand("getbalance",None)
except Exception,e: except Exception,e:
SendTo(nick,"An error has occured") SendTo(nick,"An error has occured")
return return
if not "result" in j: if not "result" in j:
print 'result not found in reply' log_error('GetTipbotBalance: result not found in reply')
SendTo(nick, "An error has occured") SendTo(nick, "An error has occured")
return return
result = j["result"] result = j["result"]
if not "balance" in result: if not "balance" in result:
print 'balance not found in result' log_error('GetTipbotBalance: balance not found in result')
SendTo(nick, "An error has occured") SendTo(nick, "An error has occured")
return return
if not "unlocked_balance" in result: if not "unlocked_balance" in result:
print 'unlocked_balance not found in result' log_error('GetTipbotBalance: unlocked_balance not found in result')
SendTo(nick, "An error has occured") SendTo(nick, "An error has occured")
return return
balance = result["balance"] balance = result["balance"]
unlocked_balance = result["unlocked_balance"] unlocked_balance = result["unlocked_balance"]
print 'balance: %s' % str(balance) log_log('GetTipbotBalance: balance: %s' % str(balance))
print 'unlocked_balance: %s' % str(unlocked_balance) log_log('GetTipbotBalance: unlocked_balance: %s' % str(unlocked_balance))
pending = long(balance)-long(unlocked_balance) pending = long(balance)-long(unlocked_balance)
if pending < 0: if pending < 0:
print 'Negative pending balance! balance %s, unlocked %s' log_error('GetTipbotBalance: Negative pending balance! balance %s, unlocked %s' % (str(balance),str(unlocked)))
SendTo(nick, "An error has occured") SendTo(nick, "An error has occured")
return return
if pending == 0: if pending == 0:
log_info("GetTipbotBalance: Tipbot balance: %s" % AmountToString(balance))
SendTo(nick,"Tipbot balance: %s" % AmountToString(balance)) SendTo(nick,"Tipbot balance: %s" % AmountToString(balance))
else: 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))) SendTo(nick,"Tipbot balance: %s (%s pending)" % (AmountToString(unlocked_balance), AmountToString(pending)))
def EnableWithdraw(nick,data):
log_info('EnableWithdraw: enabled by %s' % nick)
withdraw_disabled = False
def Help(nick): def Help(nick):
SendTo(nick, "Help for the monero tipbot:") SendTo(nick, "Help for the monero tipbot:")
SendTo(nick, "!isregistered - show whether you are currently registered with freenode") SendTo(nick, "!isregistered - show whether you are currently registered with freenode")
SendTo(nick, "!balance - show your current balance") SendTo(nick, "!balance - show your current balance")
SendTo(nick, "!tip <nick> <amount> - tip another user") SendTo(nick, "!tip <nick> <amount> - tip another user")
SendTo(nick, "!rain <amount> [<users>] - rain some monero on everyone (or just a few)")
SendTo(nick, "!withdraw <address> - withdraw your balance")
SendTo(nick, "!info - information about the tipbot") SendTo(nick, "!info - information about the tipbot")
SendTo(nick, "You can send monero to your tipbot account:"); SendTo(nick, "You can send monero to your tipbot account:");
SendTo(nick, " Address: %s" % tipbot_address) SendTo(nick, " Address: %s" % GetTipbotAddress())
SendTo(nick, " Payment ID: %s" % GetPaymentID(nick)) SendTo(nick, " Payment ID: %s" % GetPaymentID(nick))
SendTo(nick, "NO WARRANTY, YOU MAY LOSE YOUR COINS") SendTo(nick, "NO WARRANTY, YOU MAY LOSE YOUR COINS")
SendTo(nick, "Minimum withdrawal: 0.1 monero") SendTo(nick, "Minimum withdrawal: %s" % AmountToString(min_withdraw_amount))
SendTo(nick, "Withdrawal fee: %s" % AmountToString(withdrawal_fee))
SendTo(nick, "No Monero address ? You can use https://mymonero.com/")
def Info(nick): def Info(nick):
SendTo(nick, "Info for the monero tipbot:") SendTo(nick, "Info for the monero tipbot:")
@ -339,6 +572,72 @@ def Info(nick):
SendTo(nick, "return of any monero. Use at your own risk.") SendTo(nick, "return of any monero. Use at your own risk.")
SendTo(nick, "That being said, I hope you enjoy using it :)") SendTo(nick, "That being said, I hope you enjoy using it :)")
def UpdateCoin():
global last_wallet_update_time
if last_wallet_update_time == None:
last_wallet_update_time = 0
t=time.time()
dt = t - last_wallet_update_time
if dt < wallet_update_time:
return
try:
try:
scan_block_height = redis.get("scan_block_height")
scan_block_height = long(scan_block_height)
except Exception,e:
log_error('Failed to get scan_block_height: %s' % str(e))
last_wallet_update_time = time.time()
return
full_payment_ids = redis.hgetall("paymentid")
#print 'Got full payment ids: %s' % str(full_payment_ids)
payment_ids = []
for pid in full_payment_ids:
payment_ids.append(pid)
#print 'Got payment ids: %s' % str(payment_ids)
params = {
"payment_ids": payment_ids,
"min_block_height": scan_block_height
}
j = SendWalletJSONRPCCommand("get_bulk_payments",params)
#print 'Got j: %s' % str(j)
if "result" in j:
result = j["result"]
if "payments" in result:
payments = result["payments"]
log_info('UpdateCoin: Got %d payments' % len(payments))
for p in payments:
log_log('UpdateCoin: Looking at payment %s' % str(p))
bh = p["block_height"]
if bh > scan_block_height:
scan_block_height = bh
log_log('UpdateCoin: seen payments up to block %d' % scan_block_height)
try:
pipe = redis.pipeline()
pipe.set("scan_block_height", scan_block_height)
log_log('UpdateCoin: processing payments')
for p in payments:
payment_id=p["payment_id"]
tx_hash=p["tx_hash"]
amount=p["amount"]
try:
recipient = GetNickFromPaymendID(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_log('UpdateCoin: Executing received payments pipeline')
pipe.execute()
except Exception,e:
log_error('UpdateCoin: failed to set scan_block_height: %s' % str(e))
else:
log_log('UpdateCoin: No payments in get_bulk_payments reply')
else:
log_error('UpdateCoin: No results in get_bulk_payments reply')
except Exception,e:
log_error('UpdateCoin: Failed to get bulk payments: %s' % str(e))
last_wallet_update_time = time.time()
#def Op(to_op, chan): #def Op(to_op, chan):
# irc.send( 'MODE ' + chan + ' +o: ' + to_op + '\r\n') # irc.send( 'MODE ' + chan + ' +o: ' + to_op + '\r\n')
# #
@ -357,7 +656,21 @@ def getline(s):
global buffered_data global buffered_data
idx = buffered_data.find("\n") idx = buffered_data.find("\n")
if idx == -1: if idx == -1:
buffered_data+=s.recv(4096) try:
(r,w,x)=select.select([s.fileno()],[],[],1)
if s.fileno() in r:
newdata=s.recv(4096,socket.MSG_DONTWAIT)
else:
newdata = None
if s.fileno() in x:
log_error('getline: IRC socket in exception set')
newdata = None
except Exception,e:
log_error('getline: Exception: %s' % str(e))
newdata = None
if newdata == None:
return None
buffered_data+=newdata
idx = buffered_data.find("\n") idx = buffered_data.find("\n")
if idx == -1: if idx == -1:
ret = buffered_data ret = buffered_data
@ -367,42 +680,66 @@ def getline(s):
buffered_data = buffered_data[idx+1:] buffered_data = buffered_data[idx+1:]
return ret return ret
connect_to_irc(irc_network,irc_port)
connect_to_redis(irc_network,irc_port)
while True: while True:
action = None action = None
data = getline(irc) data = getline(irc)
# All that must be done even when nothing from IRC - data may be None here
UpdateCoin()
if data == None:
if time.time() - last_ping_time > 60:
log_warn('60 seconds without PING, reconnecting')
last_ping_time = time.time()
connect_to_irc(irc_network,irc_port)
continue
data = data.strip("\r\n") data = data.strip("\r\n")
print data log_IRCRECV(data)
if data.find ( 'Welcome to the freenode Internet Relay Chat Network' ) != -1: if data.find ( 'Welcome to the freenode Internet Relay Chat Network' ) != -1:
Join(homechan) SendTo("nickserv", "IDENTIFY %s" % GetPassword())
Join(irc_homechan)
#ScanWho(None,[irc_homechan])
if data.find ( 'PING' ) != -1: if data.find ( 'PING' ) == 0:
irc.send ( 'PONG ' + data.split() [ 1 ] + '\r\n' ) log_log('Got PING, replying PONG')
last_ping_time = time.time()
irc.send ( 'PONG ' + data.split() [ 1 ] + '\r\n' )
continue
#--------------------------- Action check --------------------------------# #--------------------------- Action check --------------------------------#
if data.find(':') == -1: if data.find(':') == -1:
continue continue
try: try:
parts = data.split(':') parts = data.split(':')
if len(parts) < 3: if len(parts) < 2:
continue continue
text = parts[2] if len(parts) >= 3:
text = parts[2]
else:
text = ""
parts = parts[1].split(' ') parts = parts[1].split(' ')
who = parts[0] who = parts[0]
action = parts[1] action = parts[1]
chan = parts[2] chan = parts[2]
except Exception, e: except Exception, e:
print 'Exception, continuing: ' + str(e) log_error('main parser: Exception, continuing: %s' % str(e))
continue continue
if action == None: if action == None:
continue continue
print 'text: ', text #print 'text: ', text
print 'who: ', who #print 'who: ', who
print 'action: ', action #print 'action: ', action
print 'chan: ', chan #print 'chan: ', chan
# if data.find('#') != -1: # if data.find('#') != -1:
# action = data.split('#')[0] # action = data.split('#')[0]
@ -413,70 +750,135 @@ while True:
# action = 'NICK' # action = 'NICK'
#----------------------------- Actions -----------------------------------# #----------------------------- Actions -----------------------------------#
if action == 'NOTICE': try:
if action == 'NOTICE':
if who == "NickServ!NickServ@services.": if who == "NickServ!NickServ@services.":
if text.find('Information on ') != -1: #if text.find('Information on ') != -1:
ns_nick = text.split(' ')[2].strip("\002") # ns_nick = text.split(' ')[2].strip("\002")
print 'NickServ says %s is registered' % ns_nick # print 'NickServ says %s is registered' % ns_nick
PerformNextAction(ns_nick, True) # PerformNextAction(ns_nick, True)
elif text.find(' is not registered') != -1: #elif text.find(' is not registered') != -1:
ns_nick = text.split(' ')[0].strip("\002") # ns_nick = text.split(' ')[0].strip("\002")
print 'NickServ says %s is not registered' % ns_nick # print 'NickServ says %s is not registered' % ns_nick
PerformNextAction(ns_nick, False) # 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)
PerformNextAction(ns_nick, True)
else:
log_info('NickServ says %s is not identified' % ns_nick)
PerformNextAction(ns_nick, False)
else:
log_error('ACC line not as expected...')
if action == 'PRIVMSG': 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].append(who_chan_user)
log_log("New list of users in %s: %s" % (who_chan, str(userstable[who_chan])))
except Exception,e:
log_error('Failed to parse "who" line: %s: %s' % (data, str(e)))
elif action == 'PRIVMSG':
if text.find('!') != -1: if text.find('!') != -1:
cmd = text.split('!')[1] cmd = text.split('!')[1]
cmd = cmd.split(' ') cmd = cmd.split(' ')
cmd[0] = cmd[0].strip(' \t\n\r') cmd[0] = cmd[0].strip(' \t\n\r')
#print 'XXX found command: "%s"' % str(cmd)
if cmd[0] == 'join': if chan[0] == '#':
Join('#' + cmd[1]) sendto=chan
elif cmd[0] == 'part': else:
Part('#' + cmd[1]) sendto=GetNick(who)
elif cmd[0] == 'version': log_log('Found command: "%s" in channel "%s", replying to %s' % (str(cmd), str(chan), sendto))
SendTo(chan, 'I am a monero tipbot, more info if you type !help')
#if cmd[0] == 'join':
# Join('#' + cmd[1])
#elif cmd[0] == 'part':
# Part('#' + cmd[1])
if cmd[0] == 'help':
Help(GetNick(who))
elif cmd[0] == 'isregistered': elif cmd[0] == 'isregistered':
CheckRegistered(GetNick(who),SendTo,"You are registered",SendTo,"You are not registered") CheckRegistered(GetNick(who),SendTo,"You are registered",SendTo,"You are not registered")
elif cmd[0] == 'balance': elif cmd[0] == 'balance':
CheckRegistered(GetNick(who),GetBalance,None,SendTo,"You must be registered with Freenode to query balance") CheckRegistered(GetNick(who),GetBalance,[sendto],SendTo,"You must be registered with Freenode to query balance")
elif cmd[0] == 'tip': elif cmd[0] == 'tip':
if len(cmd) == 3: if len(cmd) == 3:
CheckRegistered(GetNick(who),Tip,[cmd[1],cmd[2]],SendTo,"You must be registered with Freenode to tip") CheckRegistered(GetNick(who),Tip,[sendto,cmd[1],cmd[2]],SendTo,"You must be registered with Freenode to tip")
else: else:
SendTo(GetNick(who), "Usage: !tip nick amount"); SendTo(GetNick(who), "Usage: !tip nick amount");
elif cmd[0] == 'withdraw':
if len(cmd) == 2:
CheckRegistered(GetNick(who),Withdraw,[cmd[1]],SendTo,"You must be registered with Freenode to withdraw")
else:
SendTo(GetNick(who), "Usage: !withdraw address");
elif cmd[0] == 'info':
Info(GetNick(who))
elif cmd[0] == 'rain':
if chan[0] == '#':
if len(cmd) == 2 or len(cmd) == 3:
users = None
if len(cmd) == 3:
users = cmd[2]
CheckRegistered(GetNick(who),Rain,[chan,cmd[1],users],SendTo,"You must be registered with Freenode to rain")
else:
SendTo(sendto, "Usage: !rain amount [users]");
else:
SendTo(sendto, "Raining can only be done in a channel")
# admin commands
elif cmd[0] == 'height': elif cmd[0] == 'height':
CheckAdmin(GetNick(who),GetHeight,None,SendTo,"You must be admin") CheckAdmin(GetNick(who),GetHeight,None,SendTo,"You must be admin")
elif cmd[0] == 'tipbot_balance': elif cmd[0] == 'tipbot_balance':
CheckAdmin(GetNick(who),GetTipbotBalance,None,SendTo,"You must be admin") CheckAdmin(GetNick(who),GetTipbotBalance,None,SendTo,"You must be admin")
#elif cmd[0] == 'addbalance': elif cmd[0] == 'addbalance':
# CheckRegistered(GetNick(who),AddBalance,cmd[1],SendTo,"You must be registered with Freenode to add balance") CheckAdmin(GetNick(who),AddBalance,cmd[1],SendTo,"You must be admin")
elif cmd[0] == 'info': elif cmd[0] == 'scanwho':
Info(GetNick(who)) CheckAdmin(GetNick(who),ScanWho,[chan],SendTo,"You must be admin")
elif cmd[0] == 'help': elif cmd[0] == 'enable_withdraw':
Help(GetNick(who)) CheckAdmin(GetNick(who),EnableWithdraw,None,SendTo,"You must be admin")
else:
SendTo(GetNick(who), "Invalid command, try !help")
elif action == 'JOIN':
nick = GetNick(who)
log_info('%s joined the channel' % nick)
if not chan in userstable:
userstable[chan] = []
if nick in userstable[chan]:
log_warn('%s joined, but already in %s' % (nick, chan))
else:
userstable[chan].append(nick)
log_log("New list of users in %s: %s" % (chan, str(userstable[chan])))
elif action == 'PART':
nick = GetNick(who)
log_info('%s joined the channel' % nick)
if not nick in userstable[chan]:
log_warn('%s parted, but was not in %s' % (nick, chan))
else:
userstable[chan].remove(nick)
log_log("New list of users in %s: %s" % (chan, str(userstable[chan])))
elif action == 'NICK':
nick = GetNick(who)
new_nick = text
log_info('%s renamed to %s' % (nick, new_nick))
for c in userstable:
log_log('checking %s' % c)
if nick in userstable[c]:
userstable[c].remove(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].append(new_nick)
log_log("New list of users in %s: %s" % (c, str(userstable[c])))
except Exception,e:
log_error('Exception in top level action processing: %s' % str(e))
# if action == 'xxx_______MODE':
# Host = GetHost(data)
# status = readAdmin(Host)
# if status == 0:
# if data.find('-o') != -1:
# to_op = data.split('-o')[1]
# chan = GetChannel(data)
# chan = chan.split('-o')[0]
# Op(to_op, chan)
#
# if data.find('+o') != -1:
# to_deop = data.split('+o')[1]
# chan = GetChannel(data)
# chan = chan.split('+o')[0]
# DeOp(to_deop, chan)
#
# if action == 'xxx_______JOIN':
# Host = GetHost(data)
# status = readAdmin(Host)
# if status == 1:
# chan = GetChannel(data)
# nick = GetNick(data)
# Op(nick, chan)