2014-12-09 10:00:19 +00:00
#!/bin/python
2014-12-21 18:50:24 +00:00
#
2014-12-22 21:52:37 +00:00
# Cryptonote tipbot
2015-01-01 17:33:07 +00:00
# Copyright 2014,2015 moneromooo
2014-12-21 18:50:24 +00:00
# Inspired by "Simple Python IRC bot" by berend
#
2014-12-23 10:47:56 +00:00
# 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.
#
2014-12-09 10:00:19 +00:00
2014-12-28 10:23:17 +00:00
import sys
2014-12-30 13:12:13 +00:00
import os
2014-12-09 10:00:19 +00:00
import socket
2014-12-21 18:50:24 +00:00
import select
import random
2014-12-09 10:00:19 +00:00
import redis
import hashlib
import json
import httplib
2014-12-21 18:50:24 +00:00
import time
2014-12-23 09:42:55 +00:00
import string
2014-12-29 17:08:13 +00:00
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 . utils import *
2015-01-01 11:42:06 +00:00
from tipbot . ircutils import *
2014-12-29 17:08:13 +00:00
from tipbot . redisdb import *
2014-12-31 10:30:07 +00:00
from tipbot . command_manager import *
2014-12-29 17:08:13 +00:00
2015-01-17 13:19:50 +00:00
disabled = False
2014-12-29 17:08:13 +00:00
selected_coin = None
modulenames = [ ]
argc = 1
while argc < len ( sys . argv ) :
arg = sys . argv [ argc ]
if arg == " -c " or arg == " --coin " :
if argc + 1 == len ( sys . argv ) :
log_error ( ' Usage: tipbot.py [-h|--help] [-m|--module modulename]* -c|--coin <coinname> ' )
exit ( 1 )
argc = argc + 1
selected_coin = sys . argv [ argc ]
try :
log_info ( ' Importing %s coin setup ' % selected_coin )
if not selected_coin in coinspecs . coinspecs :
log_error ( ' Unknown coin: %s ' % selected_coin )
exit ( 1 )
for field in coinspecs . coinspecs [ selected_coin ] :
setattr ( coinspecs , field , coinspecs . coinspecs [ selected_coin ] [ field ] )
except Exception , e :
log_error ( ' Failed to load coin setup for %s : %s ' % ( selected_coin , str ( e ) ) )
exit ( 1 )
elif arg == " -m " or arg == " --module " :
if argc + 1 == len ( sys . argv ) :
log_error ( ' Usage: tipbot.py [-m|--module modulename]* -c|--coin <coinname> ' )
exit ( 1 )
argc = argc + 1
modulenames . append ( sys . argv [ argc ] )
elif arg == " -h " or arg == " --help " :
log_info ( ' Usage: tipbot.py [-m|--module modulename]* -c|--coin <coinname> ' )
exit ( 0 )
else :
log_error ( ' Usage: tipbot.py [-m|--module modulename]* -c|--coin <coinname> ' )
exit ( 1 )
argc = argc + 1
2014-12-09 10:00:19 +00:00
2014-12-29 17:08:13 +00:00
if not selected_coin :
log_error ( ' Coin setup needs to be specified with -c. See --help ' )
2014-12-28 10:23:17 +00:00
exit ( 1 )
2014-12-21 18:50:24 +00:00
2014-12-30 13:12:13 +00:00
sys . path . append ( os . path . join ( ' tipbot ' , ' modules ' ) )
2014-12-29 17:08:13 +00:00
for modulename in modulenames :
log_info ( ' Importing %s module ' % modulename )
2014-12-21 18:50:24 +00:00
try :
2014-12-30 13:12:13 +00:00
__import__ ( modulename )
2014-12-21 18:50:24 +00:00
except Exception , e :
2014-12-29 17:08:13 +00:00
log_error ( ' Failed to load module " %s " : %s ' % ( modulename , str ( e ) ) )
exit ( 1 )
2014-12-09 10:00:19 +00:00
2014-12-29 17:08:13 +00:00
def GetBalance ( nick , chan , cmd ) :
sendto = GetSendTo ( nick , chan )
2014-12-21 18:50:24 +00:00
log_log ( " GetBalance: checking %s " % nick )
2014-12-09 10:00:19 +00:00
try :
2014-12-29 17:08:13 +00:00
balance = redis_hget ( " balances " , nick )
2014-12-09 10:00:19 +00:00
if balance == None :
balance = 0
2015-01-11 18:53:00 +00:00
balance = long ( balance )
2014-12-09 10:00:19 +00:00
sbalance = AmountToString ( balance )
2015-01-11 18:53:00 +00:00
if balance < coinspecs . atomic_units :
2015-01-13 23:32:11 +00:00
if balance == 0 :
SendTo ( sendto , " %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 ) )
2015-01-11 18:53:00 +00:00
else :
SendTo ( sendto , " %s ' s balance is %s " % ( nick , sbalance ) )
2014-12-09 10:00:19 +00:00
except Exception , e :
2014-12-21 18:50:24 +00:00
log_error ( ' GetBalance: exception: %s ' % str ( e ) )
SendTo ( sendto , " An error has occured " )
2014-12-09 10:00:19 +00:00
2014-12-29 17:08:13 +00:00
def AddBalance ( nick , chan , cmd ) :
2015-01-10 13:53:22 +00:00
sendto = GetSendTo ( nick , chan )
if GetParam ( cmd , 2 ) :
anick = GetParam ( cmd , 1 )
amount = GetParam ( cmd , 2 )
else :
anick = nick
amount = GetParam ( cmd , 1 )
if not amount :
2015-01-18 11:52:01 +00:00
SendTo ( sendto , ' usage: !addbalance [<nick>] <amount> ' )
2015-01-10 13:53:22 +00:00
return
try :
2015-01-18 11:52:01 +00:00
units = long ( float ( amount ) * coinspecs . atomic_units )
2015-01-10 13:53:22 +00:00
except Exception , e :
2015-01-18 11:52:01 +00:00
log_error ( ' AddBalance: invalid amount: %s ' % str ( e ) )
SendTo ( sendto , ' usage: !addbalance [<nick>] <amount> ' )
2015-01-10 13:53:22 +00:00
return
2015-01-18 11:52:01 +00:00
log_info ( " AddBalance: Adding %s to %s ' s balance " % ( AmountToString ( units ) , anick ) )
2014-12-09 10:00:19 +00:00
try :
2015-01-18 11:52:01 +00:00
balance = redis_hincrby ( " balances " , anick , units )
2014-12-09 10:00:19 +00:00
except Exception , e :
2014-12-21 18:50:24 +00:00
log_error ( ' AddBalance: exception: %s ' % str ( e ) )
2015-01-10 13:53:22 +00:00
SendTo ( sendto , " An error has occured " )
2015-01-18 11:52:01 +00:00
SendTo ( sendto , " %s ' s bvalance is now %s " % ( anick , AmountToString ( balance ) ) )
2014-12-09 10:00:19 +00:00
2014-12-29 17:08:13 +00:00
def ScanWho ( nick , chan , cmd ) :
2014-12-21 18:50:24 +00:00
Who ( chan )
2014-12-29 17:08:13 +00:00
def GetHeight ( nick , chan , cmd ) :
2014-12-21 18:50:24 +00:00
log_info ( ' GetHeight: %s wants to know block height ' % nick )
2014-12-09 10:00:19 +00:00
try :
2014-12-28 10:27:31 +00:00
j = SendDaemonHTMLCommand ( " getheight " )
2014-12-09 10:00:19 +00:00
except Exception , e :
2014-12-21 18:50:24 +00:00
log_error ( ' GetHeight: error: %s ' % str ( e ) )
2014-12-09 10:00:19 +00:00
SendTo ( nick , " An error has occured " )
return
2014-12-21 18:50:24 +00:00
log_log ( ' GetHeight: Got reply: %s ' % str ( j ) )
2014-12-09 10:00:19 +00:00
if not " height " in j :
2014-12-21 18:50:24 +00:00
log_error ( ' GetHeight: Cannot see height in here ' )
2014-12-09 10:00:19 +00:00
SendTo ( nick , " Height not found " )
return
2014-12-21 18:50:24 +00:00
height = j [ " height " ]
2014-12-29 17:08:13 +00:00
log_info ( ' GetHeight: height is %s ' % str ( height ) )
2014-12-21 18:50:24 +00:00
SendTo ( nick , " Height: %s " % str ( height ) )
2014-12-09 10:00:19 +00:00
2014-12-29 17:08:13 +00:00
def GetTipbotBalance ( nick , chan , cmd ) :
2014-12-21 18:50:24 +00:00
log_info ( ' %s wants to know the tipbot balance ' % nick )
2014-12-09 10:00:19 +00:00
try :
2015-01-01 17:33:07 +00:00
balance , unlocked_balance = RetrieveTipbotBalance ( )
2014-12-09 10:00:19 +00:00
except Exception , e :
SendTo ( nick , " An error has occured " )
return
pending = long ( balance ) - long ( unlocked_balance )
if pending == 0 :
2014-12-21 18:50:24 +00:00
log_info ( " GetTipbotBalance: Tipbot balance: %s " % AmountToString ( balance ) )
2014-12-09 10:00:19 +00:00
SendTo ( nick , " Tipbot balance: %s " % AmountToString ( balance ) )
else :
2014-12-21 18:50:24 +00:00
log_info ( " GetTipbotBalance: Tipbot balance: %s ( %s pending) " % ( AmountToString ( unlocked_balance ) , AmountToString ( pending ) ) )
2014-12-09 10:00:19 +00:00
SendTo ( nick , " Tipbot balance: %s ( %s pending) " % ( AmountToString ( unlocked_balance ) , AmountToString ( pending ) ) )
2014-12-29 17:08:13 +00:00
def DumpUsers ( nick , chan , cmd ) :
userstable = GetUsersTable ( )
2014-12-22 13:30:59 +00:00
log_info ( str ( userstable ) )
2014-12-29 17:08:13 +00:00
def Help ( nick , chan , cmd ) :
2015-01-03 18:32:09 +00:00
module = GetParam ( cmd , 1 )
if module :
RunModuleHelpFunction ( module , nick , chan )
return
2014-12-31 10:31:16 +00:00
SendTo ( nick , " See available commands with !commands or !commands <modulename> " )
2015-01-11 09:47:24 +00:00
SendTo ( nick , " Available modules: %s " % " , " . join ( GetModuleNameList ( IsAdmin ( nick ) ) ) )
2015-01-03 18:32:09 +00:00
SendTo ( nick , " Get help on a particular module with !help <modulename> " )
2014-12-29 17:08:13 +00:00
if coinspecs . web_wallet_url :
SendTo ( nick , " 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 )
2015-01-07 17:52:34 +00:00
SendTo ( nick , " Copyright 2014,2015 moneromooo - http://duckpool.mooo.com/tipbot/ " )
2014-12-31 11:42:27 +00:00
SendTo ( nick , " Type !help, or !commands for a list of commands " )
2014-12-09 10:00:19 +00:00
SendTo ( nick , " NO WARRANTY, YOU MAY LOSE YOUR COINS " )
2014-12-29 21:46:45 +00:00
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 )
2014-12-09 10:00:19 +00:00
SendTo ( nick , " stolen, the server compromised, etc. While I hope this won ' t be the case, " )
2014-12-29 21:46:45 +00:00
SendTo ( nick , " I will not offer any warranty whatsoever for the use of %s or the " % config . tipbot_name )
2014-12-29 17:08:13 +00:00
SendTo ( nick , " return of any %s . Use at your own risk. " % coinspecs . name )
2014-12-09 10:00:19 +00:00
SendTo ( nick , " That being said, I hope you enjoy using it :) " )
2014-12-22 13:30:59 +00:00
def InitScanBlockHeight ( ) :
try :
2014-12-29 17:08:13 +00:00
scan_block_height = redis_get ( " scan_block_height " )
2014-12-22 13:30:59 +00:00
scan_block_height = long ( scan_block_height )
except Exception , e :
try :
2014-12-29 17:08:13 +00:00
redis_set ( " scan_block_height " , 0 )
2014-12-22 13:30:59 +00:00
except Exception , e :
log_error ( ' Failed to initialize scan_block_height: %s ' % str ( e ) )
2014-12-29 17:08:13 +00:00
def ShowActivity ( nick , chan , cmd ) :
achan = cmd [ 1 ]
anick = cmd [ 2 ]
2014-12-26 16:57:03 +00:00
activity = GetTimeSinceActive ( achan , anick )
if activity :
SendTo ( nick , " %s was active in %s %f seconds ago " % ( anick , achan , activity ) )
else :
SendTo ( nick , " %s was never active in %s " % ( anick , achan ) )
2014-12-29 17:08:13 +00:00
def SendToNick ( nick , chan , msg ) :
SendTo ( nick , msg )
2014-12-21 18:50:24 +00:00
2014-12-29 17:08:13 +00:00
def IsRegistered ( nick , chan , cmd ) :
RunRegisteredCommand ( nick , chan , SendToNick , " You are registered " , SendToNick , " You are not registered " )
2014-12-21 18:50:24 +00:00
2015-01-01 10:06:09 +00:00
def Reload ( nick , chan , cmd ) :
sendto = GetSendTo ( nick , chan )
modulename = GetParam ( cmd , 1 )
if not modulename :
SendTo ( sendto , " Usage: reload <modulename> " )
return
if modulename == " builtin " :
SendTo ( sendto , " Cannot reload builtin module " )
return
log_info ( ' Unloading %s module ' % modulename )
2015-01-03 18:32:09 +00:00
UnregisterModule ( modulename )
2015-01-01 10:06:09 +00:00
log_info ( ' Reloading %s module ' % modulename )
try :
reload ( sys . modules [ modulename ] )
SendTo ( sendto , ' %s reloaded ' % modulename )
except Exception , e :
log_error ( ' Failed to load module " %s " : %s ' % ( modulename , str ( e ) ) )
SendTo ( sendto , ' An error occured ' )
2015-01-17 13:19:50 +00:00
def Disable ( nick , chan , cmd ) :
global disabled
sendto = GetSendTo ( nick , chan )
disabled = True
SendTo ( sendto , ' %s disabled, will require restart ' % config . tipbot_name )
2014-12-29 17:08:13 +00:00
def OnIdle ( ) :
2015-01-17 13:19:50 +00:00
if disabled :
return
2014-12-29 17:08:13 +00:00
RunIdleFunctions ( [ irc , redisdb ] )
2014-12-21 18:50:24 +00:00
2014-12-29 17:08:13 +00:00
def OnIdentified ( nick , identified ) :
2015-01-17 13:19:50 +00:00
if disabled :
log_info ( ' Ignoring identified notification for %s while disabled ' % str ( nick ) )
return
2014-12-29 17:08:13 +00:00
RunNextCommand ( nick , identified )
2014-12-21 18:50:24 +00:00
2014-12-29 17:08:13 +00:00
def RegisterCommands ( ) :
2015-01-03 18:32:09 +00:00
RegisterCommand ( { ' module ' : ' builtin ' , ' name ' : ' help ' , ' parms ' : ' [module] ' , ' function ' : Help , ' help ' : " Displays help about %s " % config . tipbot_name } )
2014-12-31 10:31:16 +00:00
RegisterCommand ( { ' module ' : ' builtin ' , ' name ' : ' commands ' , ' parms ' : ' [module] ' , ' function ' : Commands , ' help ' : " Displays list of commands " } )
RegisterCommand ( { ' module ' : ' builtin ' , ' name ' : ' isregistered ' , ' function ' : IsRegistered , ' help ' : " show whether you are currently registered with freenode " } )
RegisterCommand ( { ' module ' : ' builtin ' , ' name ' : ' balance ' , ' function ' : GetBalance , ' registered ' : True , ' help ' : " show your current balance " } )
RegisterCommand ( { ' module ' : ' builtin ' , ' name ' : ' info ' , ' function ' : Info , ' help ' : " infornmation about %s " % config . tipbot_name } )
2014-12-22 13:30:59 +00:00
2014-12-31 10:31:16 +00:00
RegisterCommand ( { ' module ' : ' builtin ' , ' name ' : ' height ' , ' function ' : GetHeight , ' admin ' : True , ' help ' : " Get current blockchain height " } )
RegisterCommand ( { ' module ' : ' builtin ' , ' name ' : ' tipbot_balance ' , ' function ' : GetTipbotBalance , ' admin ' : True , ' help ' : " Get current blockchain height " } )
2015-01-10 13:53:22 +00:00
RegisterCommand ( { ' module ' : ' builtin ' , ' name ' : ' addbalance ' , ' parms ' : ' <nick> <amount> ' , ' function ' : AddBalance , ' admin ' : True , ' help ' : " Add balance to your account " } )
2014-12-31 10:31:16 +00:00
RegisterCommand ( { ' module ' : ' builtin ' , ' name ' : ' scanwho ' , ' function ' : ScanWho , ' admin ' : True , ' help ' : " Refresh users list in a channel " } )
RegisterCommand ( { ' module ' : ' builtin ' , ' name ' : ' dump_users ' , ' function ' : DumpUsers , ' admin ' : True , ' help ' : " Dump users table to log " } )
RegisterCommand ( { ' module ' : ' builtin ' , ' name ' : ' show_activity ' , ' function ' : ShowActivity , ' admin ' : True , ' help ' : " Show time since a user was last active " } )
2015-01-01 10:06:09 +00:00
RegisterCommand ( { ' module ' : ' builtin ' , ' name ' : ' reload ' , ' function ' : Reload , ' admin ' : True , ' help ' : " Reload a module " } )
2015-01-17 13:19:50 +00:00
RegisterCommand ( { ' module ' : ' builtin ' , ' name ' : ' disable ' , ' function ' : Disable , ' admin ' : True , ' help ' : " Disable %s " % config . tipbot_name } )
2014-12-21 18:50:24 +00:00
2014-12-29 17:08:13 +00:00
def OnCommandProxy ( cmd , chan , who ) :
2015-01-17 13:19:50 +00:00
if disabled :
log_info ( ' Ignoring command from %s while disabled: %s ' % ( str ( who ) , str ( cmd ) ) )
return
2014-12-29 17:08:13 +00:00
OnCommand ( cmd , chan , who , RunAdminCommand , RunRegisteredCommand )
2014-12-21 18:50:24 +00:00
2014-12-29 17:08:13 +00:00
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 ( )
2014-12-09 10:00:19 +00:00
2014-12-29 17:08:13 +00:00
IRCLoop ( OnIdle , OnIdentified , OnCommandProxy )
2015-01-10 13:53:42 +00:00
log_info ( ' shutting down redis ' )
redisdb . shutdown
log_info ( ' exiting ' )