2014-12-29 17:08:13 +00:00
#!/bin/python
#
# Cryptonote tipbot - tipping commands
2015-01-07 20:43:58 +00:00
# Copyright 2014, 2015 moneromooo
2014-12-29 17:08:13 +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.
#
import sys
import socket
import select
import random
import redis
import hashlib
import json
import httplib
import time
import string
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 *
2015-01-01 11:42:06 +00:00
from tipbot . ircutils import *
2014-12-29 17:08:13 +00:00
from tipbot . command_manager import *
from tipbot . redisdb import *
2015-01-17 15:43:50 +00:00
pending_confirmations = dict ( )
2014-12-29 17:08:13 +00:00
2015-01-17 15:43:50 +00:00
def PerformTip ( nick , chan , who , units ) :
2014-12-29 17:08:13 +00:00
sendto = GetSendTo ( nick , chan )
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
log_info ( ' Tip: %s tipping %s %u units, with balance %u ' % ( nick , who , units , balance ) )
try :
p = redis_pipeline ( )
2015-01-07 20:43:30 +00:00
p . incrby ( " tips_total_count " , 1 ) ;
p . incrby ( " tips_total_amount " , units ) ;
p . hincrby ( " tips_count " , nick , 1 ) ;
p . hincrby ( " tips_amount " , nick , units ) ;
2014-12-29 17:08:13 +00:00
p . hincrby ( " balances " , nick , - units ) ;
p . hincrby ( " balances " , who , units )
p . execute ( )
SendTo ( sendto , " %s has tipped %s %s " % ( nick , who , AmountToString ( units ) ) )
except Exception , e :
2015-01-08 09:02:21 +00:00
log_error ( " Tip: Error updating redis: %s " % str ( e ) )
2014-12-29 17:08:13 +00:00
SendTo ( sendto , " An error occured " )
return
except Exception , e :
log_error ( ' Tip: exception: %s ' % str ( e ) )
SendTo ( sendto , " An error has occured " )
2015-01-17 15:43:50 +00:00
def Tip ( nick , chan , cmd ) :
userstable = GetUsersTable ( )
sendto = GetSendTo ( nick , chan )
try :
who = cmd [ 1 ]
amount = float ( cmd [ 2 ] )
except Exception , e :
SendTo ( sendto , " Usage: tip nick amount " )
return
units = long ( amount * coinspecs . atomic_units )
if units < = 0 :
SendTo ( sendto , " 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 }
return
log_info ( ' delk ' )
pending_confirmations . pop ( nick , None )
PerformTip ( nick , chan , who , 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 )
return
who = pending_confirmations [ nick ] [ ' who ' ]
units = pending_confirmations [ nick ] [ ' units ' ]
pending_confirmations . pop ( nick , None )
PerformTip ( nick , chan , who , units )
2014-12-29 17:08:13 +00:00
def Rain ( nick , chan , cmd ) :
userstable = GetUsersTable ( )
if chan [ 0 ] != ' # ' :
SendTo ( nick , " Raining can only be done in a channel " )
return
try :
amount = float ( cmd [ 1 ] )
except Exception , e :
SendTo ( chan , " Usage: rain amount [users] " )
return
users = GetParam ( cmd , 2 )
if users :
try :
users = long ( users )
except Exception , e :
SendTo ( chan , " Usage: rain amount [users] " )
return
if amount < = 0 :
SendTo ( chan , " Usage: rain amount [users] " )
return
if users != None and users < = 0 :
SendTo ( chan , " Usage: rain amount [users] " )
return
units = long ( amount * coinspecs . atomic_units )
try :
balance = redis_hget ( " balances " , nick )
if balance == None :
balance = 0
balance = long ( balance )
if units > balance :
SendTo ( chan , " You only have %s " % ( AmountToString ( balance ) ) )
return
log_log ( " userstable: %s " % str ( userstable ) )
userlist = userstable [ chan ] . keys ( )
userlist . remove ( nick )
for n in config . no_rain_to_nicks :
userlist . remove ( 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 " )
return
if units < users :
SendTo ( chan , " 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 ) ) )
random . shuffle ( userlist )
userlist = userlist [ 0 : users ]
log_log ( " selected users in %s : %s " % ( chan , userlist ) )
user_units = long ( units / users )
2015-01-11 18:45:59 +00:00
enumerate_users = False
2014-12-29 17:08:13 +00:00
if everyone :
msg = " %s rained %s on everyone in the channel " % ( nick , AmountToString ( user_units ) )
2015-01-11 18:45:59 +00:00
elif len ( userlist ) > 16 :
msg = " %s rained %s on %d nicks " % ( nick , AmountToString ( user_units ) , len ( userlist ) )
2014-12-29 17:08:13 +00:00
else :
msg = " %s rained %s on: " % ( nick , AmountToString ( user_units ) )
2015-01-11 18:45:59 +00:00
enumerate_users = True
2014-12-29 17:08:13 +00:00
pipe = redis_pipeline ( )
pipe . hincrby ( " balances " , nick , - units )
2015-01-07 20:43:30 +00:00
pipe . incrby ( " rain_total_count " , 1 ) ;
pipe . incrby ( " rain_total_amount " , units ) ;
pipe . hincrby ( " rain_count " , nick , 1 ) ;
pipe . hincrby ( " rain_amount " , nick , units ) ;
2014-12-29 17:08:13 +00:00
for user in userlist :
pipe . hincrby ( " balances " , user , user_units )
2015-01-11 18:45:59 +00:00
if enumerate_users :
2014-12-29 17:08:13 +00:00
msg = msg + " " + user
pipe . execute ( )
SendTo ( chan , " %s " % msg )
except Exception , e :
log_error ( ' Rain: exception: %s ' % str ( e ) )
SendTo ( chan , " An error has occured " )
return
def RainActive ( nick , chan , cmd ) :
userstable = GetUsersTable ( )
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 " )
return
2015-01-10 14:03:07 +00:00
if not amount or not hours :
SendTo ( chan , " usage: !rainactive <amount> <hours> [<minfrac>] " )
return
2014-12-29 17:08:13 +00:00
try :
amount = float ( amount )
if amount < = 0 :
raise RuntimeError ( " " )
except Exception , e :
SendTo ( chan , " Invalid amount " )
return
try :
hours = float ( hours )
if hours < = 0 :
raise RuntimeError ( " " )
2015-01-01 15:18:42 +00:00
seconds = hours * 3600
2014-12-29 17:08:13 +00:00
except Exception , e :
SendTo ( chan , " Invalid hours " )
return
if minfrac :
try :
minfrac = float ( minfrac )
if minfrac < 0 or minfrac > 1 :
raise RuntimeError ( " " )
except Exception , e :
SendTo ( chan , " minfrac must be a number between 0 and 1 " )
return
else :
minfrac = 0
units = long ( amount * coinspecs . atomic_units )
try :
balance = redis_hget ( " balances " , nick )
if balance == None :
balance = 0
balance = long ( balance )
if units > balance :
SendTo ( chan , " You only have %s " % ( AmountToString ( balance ) ) )
return
now = time . time ( )
userlist = userstable [ chan ] . keys ( )
userlist . remove ( nick )
for n in config . no_rain_to_nicks :
userlist . remove ( n )
weights = dict ( )
weight = 0
for n in userlist :
t = userstable [ chan ] [ n ]
if t == None :
continue
dt = now - t
2015-01-01 15:18:42 +00:00
if dt < = seconds :
w = ( 1 * ( seconds - dt ) + minfrac * dt ) / ( seconds )
2014-12-29 17:08:13 +00:00
weights [ n ] = w
weight + = w
if len ( weights ) == 0 :
SendTo ( chan , " Nobody eligible for rain " )
return
pipe = redis_pipeline ( )
pipe . hincrby ( " balances " , nick , - units )
2015-01-07 20:43:30 +00:00
pipe . incrby ( " arain_total_count " , 1 ) ;
pipe . incrby ( " arain_total_amount " , units ) ;
pipe . hincrby ( " arain_count " , nick , 1 ) ;
pipe . hincrby ( " arain_amount " , nick , units ) ;
2014-12-29 17:08:13 +00:00
rained_units = 0
nnicks = 0
minu = None
maxu = None
for n in weights :
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 ) )
pipe . hincrby ( " balances " , n , user_units )
rained_units + = user_units
if not minu or user_units < minu :
minu = user_units
if not maxu or user_units > maxu :
maxu = user_units
nnicks = nnicks + 1
if maxu == None :
SendTo ( chan , " This would mean not even an atomic unit per nick " )
return
pipe . execute ( )
2015-01-18 22:18:46 +00:00
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 ) ) )
2014-12-29 17:08:13 +00:00
except Exception , e :
log_error ( ' Rain: exception: %s ' % str ( e ) )
SendTo ( chan , " An error has occured " )
return
2015-01-03 18:32:09 +00:00
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. ' )
2014-12-29 17:08:13 +00:00
2015-01-03 18:32:09 +00:00
RegisterModule ( {
' name ' : __name__ ,
' help ' : Help ,
} )
2014-12-29 17:08:13 +00:00
RegisterCommand ( {
2015-01-01 10:12:03 +00:00
' module ' : __name__ ,
2014-12-29 17:08:13 +00:00
' name ' : ' tip ' ,
' parms ' : ' <nick> <amount> ' ,
' function ' : Tip ,
' registered ' : True ,
' help ' : " tip another user "
} )
2015-01-17 15:43:50 +00:00
RegisterCommand ( {
' module ' : __name__ ,
' name ' : ' confirmtip ' ,
' function ' : ConfirmTip ,
' registered ' : True ,
' help ' : " confirm a tip to another user who is not in the channel "
} )
2014-12-29 17:08:13 +00:00
RegisterCommand ( {
2015-01-01 10:12:03 +00:00
' module ' : __name__ ,
2014-12-29 17:08:13 +00:00
' name ' : ' rain ' ,
' parms ' : ' <amount> [<users>] ' ,
' function ' : Rain ,
' registered ' : True ,
' help ' : " rain some coins on everyone (or just a few) "
} )
RegisterCommand ( {
2015-01-01 10:12:03 +00:00
' module ' : __name__ ,
2014-12-29 17:08:13 +00:00
' name ' : ' rainactive ' ,
2015-01-10 14:03:07 +00:00
' parms ' : ' <amount> <hours> [<minfrac>] ' ,
2014-12-29 17:08:13 +00:00
' function ' : RainActive ,
' registered ' : True ,
' help ' : " rain some coins on whoever was active recently "
} )