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 *
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-13 12:28:05 +00:00
def PerformTip ( link , whoid , units ) :
identity = link . identity ( )
2014-12-29 17:08:13 +00:00
try :
2015-01-31 23:40:13 +00:00
account = GetAccount ( identity )
who_account = GetAccount ( whoid )
balance = redis_hget ( " balances " , account )
2014-12-29 17:08:13 +00:00
if balance == None :
balance = 0
balance = long ( balance )
if units > balance :
2015-01-13 12:28:05 +00:00
link . send ( " You only have %s " % ( AmountToString ( balance ) ) )
2014-12-29 17:08:13 +00:00
return
2015-01-13 12:28:05 +00:00
log_info ( ' Tip: %s tipping %s %u units, with balance %u ' % ( identity , whoid , units , balance ) )
2014-12-29 17:08:13 +00:00
try :
p = redis_pipeline ( )
2015-01-07 20:43:30 +00:00
p . incrby ( " tips_total_count " , 1 ) ;
p . incrby ( " tips_total_amount " , units ) ;
2015-01-13 12:28:05 +00:00
p . hincrby ( " tips_count " , identity , 1 ) ;
p . hincrby ( " tips_amount " , identity , units ) ;
2015-01-31 23:40:13 +00:00
p . hincrby ( " balances " , account , - units ) ;
p . hincrby ( " balances " , who_account , units )
2014-12-29 17:08:13 +00:00
p . execute ( )
2015-01-13 12:28:05 +00:00
link . send ( " %s has tipped %s %s " % ( NickFromIdentity ( identity ) , NickFromIdentity ( whoid ) , AmountToString ( units ) ) )
2014-12-29 17:08:13 +00:00
except Exception , e :
2015-01-08 09:02:21 +00:00
log_error ( " Tip: Error updating redis: %s " % str ( e ) )
2015-01-13 12:28:05 +00:00
link . send ( " An error occured " )
2014-12-29 17:08:13 +00:00
return
except Exception , e :
log_error ( ' Tip: exception: %s ' % str ( e ) )
2015-01-13 12:28:05 +00:00
link . send ( " An error has occured " )
2014-12-29 17:08:13 +00:00
2015-01-13 12:28:05 +00:00
def Tip ( link , cmd ) :
identity = link . identity ( )
2015-01-17 15:43:50 +00:00
try :
who = cmd [ 1 ]
amount = float ( cmd [ 2 ] )
except Exception , e :
2015-01-13 12:28:05 +00:00
link . send ( " Usage: tip nick amount " )
2015-01-17 15:43:50 +00:00
return
units = long ( amount * coinspecs . atomic_units )
if units < = 0 :
2015-01-13 12:28:05 +00:00
link . send ( " Invalid amount " )
2015-01-17 15:43:50 +00:00
return
2015-01-13 12:28:05 +00:00
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 }
2015-01-17 15:43:50 +00:00
return
2015-01-13 12:28:05 +00:00
pending_confirmations . pop ( identity , None )
PerformTip ( link , whoid , units )
2015-01-17 15:43:50 +00:00
2015-01-13 12:28:05 +00:00
def ConfirmTip ( link , cmd ) :
identity = link . identity ( )
if not identity in pending_confirmations :
link . send ( " %s has no tip waiting confirmation " % NickFromIdentity ( identity ) )
2015-01-17 15:43:50 +00:00
return
2015-01-13 12:28:05 +00:00
whoid = pending_confirmations [ identity ] [ ' who ' ]
units = pending_confirmations [ identity ] [ ' units ' ]
pending_confirmations . pop ( identity , None )
PerformTip ( link , whoid , units )
2015-01-17 15:43:50 +00:00
2015-01-13 12:28:05 +00:00
def Rain ( link , cmd ) :
identity = link . identity ( )
2014-12-29 17:08:13 +00:00
2015-01-13 12:28:05 +00:00
group = link . group
if not group :
link . send ( " Raining can only be done in a group " )
2014-12-29 17:08:13 +00:00
return
try :
amount = float ( cmd [ 1 ] )
except Exception , e :
2015-01-13 12:28:05 +00:00
link . send ( " Usage: rain amount [users] " )
2014-12-29 17:08:13 +00:00
return
users = GetParam ( cmd , 2 )
if users :
try :
users = long ( users )
except Exception , e :
2015-01-13 12:28:05 +00:00
link . send ( " Usage: rain amount [users] " )
2014-12-29 17:08:13 +00:00
return
if amount < = 0 :
2015-01-13 12:28:05 +00:00
link . send ( " Usage: rain amount [users] " )
2014-12-29 17:08:13 +00:00
return
if users != None and users < = 0 :
2015-01-13 12:28:05 +00:00
link . send ( " Usage: rain amount [users] " )
2014-12-29 17:08:13 +00:00
return
units = long ( amount * coinspecs . atomic_units )
try :
2015-01-31 23:40:13 +00:00
account = GetAccount ( identity )
balance = redis_hget ( " balances " , account )
2014-12-29 17:08:13 +00:00
if balance == None :
balance = 0
balance = long ( balance )
if units > balance :
2015-01-13 12:28:05 +00:00
link . send ( " You only have %s " % ( AmountToString ( balance ) ) )
2014-12-29 17:08:13 +00:00
return
2015-01-31 23:40:13 +00:00
userlist = link . network . get_users ( group . name )
log_log ( " users in %s : %s " % ( group . name , str ( [ user . identity ( ) for user in userlist ] ) ) )
userlist . remove ( link )
2014-12-29 17:08:13 +00:00
for n in config . no_rain_to_nicks :
2015-01-31 23:40:13 +00:00
i = IdentityFromString ( link , n )
l = Link ( link . network , User ( link . network , NickFromIdentity ( i ) ) , group )
if l in userlist :
userlist . remove ( l )
2014-12-29 17:08:13 +00:00
if users == None or users > len ( userlist ) :
users = len ( userlist )
everyone = True
else :
everyone = False
if users == 0 :
2015-01-13 12:28:05 +00:00
link . send ( " Nobody eligible for rain " )
2014-12-29 17:08:13 +00:00
return
if units < users :
2015-01-13 12:28:05 +00:00
link . send ( " This would mean not even an atomic unit per nick " )
2014-12-29 17:08:13 +00:00
return
2015-01-13 12:28:05 +00:00
log_info ( " %s wants to rain %s on %s users in %s " % ( identity , AmountToString ( units ) , users , group . name ) )
2015-01-31 23:40:13 +00:00
log_log ( " eligible users in %s : %s " % ( group . name , str ( [ user . identity ( ) for user in userlist ] ) ) )
2014-12-29 17:08:13 +00:00
random . shuffle ( userlist )
userlist = userlist [ 0 : users ]
2015-01-31 23:40:13 +00:00
log_log ( " selected users in %s : %s " % ( group . name , [ user . identity ( ) for user in userlist ] ) )
2014-12-29 17:08:13 +00:00
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 :
2015-01-13 12:28:05 +00:00
msg = " %s rained %s on everyone in the channel " % ( link . user . nick , AmountToString ( user_units ) )
2015-01-11 18:45:59 +00:00
elif len ( userlist ) > 16 :
2015-01-13 12:28:05 +00:00
msg = " %s rained %s on %d nicks " % ( link . user . nick , AmountToString ( user_units ) , len ( userlist ) )
2014-12-29 17:08:13 +00:00
else :
2015-01-13 12:28:05 +00:00
msg = " %s rained %s on: " % ( link . user . 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 ( )
2015-01-31 23:40:13 +00:00
pipe . hincrby ( " balances " , account , - units )
2015-01-13 12:28:05 +00:00
pipe . incrby ( " rain_total_count " , 1 )
pipe . incrby ( " rain_total_amount " , units )
pipe . hincrby ( " rain_count " , identity , 1 )
pipe . hincrby ( " rain_amount " , identity , units )
2014-12-29 17:08:13 +00:00
for user in userlist :
2015-01-31 23:40:13 +00:00
a = GetAccount ( user )
pipe . hincrby ( " balances " , a , user_units )
2015-01-11 18:45:59 +00:00
if enumerate_users :
2015-01-13 12:28:05 +00:00
msg = msg + " " + NickFromIdentity ( user )
2014-12-29 17:08:13 +00:00
pipe . execute ( )
2015-01-13 12:28:05 +00:00
link . send ( " %s " % msg )
2014-12-29 17:08:13 +00:00
except Exception , e :
log_error ( ' Rain: exception: %s ' % str ( e ) )
2015-01-13 12:28:05 +00:00
link . send ( " An error has occured " )
2014-12-29 17:08:13 +00:00
return
2015-01-13 12:28:05 +00:00
def RainActive ( link , cmd ) :
identity = link . identity ( )
2014-12-29 17:08:13 +00:00
amount = GetParam ( cmd , 1 )
hours = GetParam ( cmd , 2 )
minfrac = GetParam ( cmd , 3 )
2015-01-13 12:28:05 +00:00
group = link . group
if not group :
link . send ( " Raining can only be done in a channel " )
2014-12-29 17:08:13 +00:00
return
2015-01-10 14:03:07 +00:00
if not amount or not hours :
2015-01-13 12:28:05 +00:00
link . send ( " usage: !rainactive <amount> <hours> [<minfrac>] " )
2015-01-10 14:03:07 +00:00
return
2014-12-29 17:08:13 +00:00
try :
amount = float ( amount )
if amount < = 0 :
raise RuntimeError ( " " )
except Exception , e :
2015-01-13 12:28:05 +00:00
link . send ( " Invalid amount " )
2014-12-29 17:08:13 +00:00
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 :
2015-01-13 12:28:05 +00:00
link . send ( " Invalid hours " )
2014-12-29 17:08:13 +00:00
return
if minfrac :
try :
minfrac = float ( minfrac )
if minfrac < 0 or minfrac > 1 :
raise RuntimeError ( " " )
except Exception , e :
2015-01-13 12:28:05 +00:00
link . send ( " minfrac must be a number between 0 and 1 " )
2014-12-29 17:08:13 +00:00
return
else :
minfrac = 0
units = long ( amount * coinspecs . atomic_units )
try :
2015-01-31 23:40:13 +00:00
account = GetAccount ( link )
balance = redis_hget ( " balances " , account )
2014-12-29 17:08:13 +00:00
if balance == None :
balance = 0
balance = long ( balance )
if units > balance :
2015-01-13 12:28:05 +00:00
link . send ( " You only have %s " % ( AmountToString ( balance ) ) )
2014-12-29 17:08:13 +00:00
return
now = time . time ( )
2015-01-31 23:40:13 +00:00
userlist = link . network . get_active_users ( seconds , group . name )
log_log ( ' userlist: %s ' % str ( [ user . identity ( ) for user in userlist ] ) )
userlist . remove ( link )
2014-12-29 17:08:13 +00:00
for n in config . no_rain_to_nicks :
2015-01-31 23:40:13 +00:00
i = IdentityFromString ( link , n )
l = Link ( link . network , User ( link . network , NickFromIdentity ( i ) ) , group )
if l in userlist :
userlist . remove ( l )
2015-02-01 15:51:08 +00:00
weights = [ ]
2014-12-29 17:08:13 +00:00
weight = 0
2015-01-31 23:40:13 +00:00
log_log ( ' userlist to loop: %s ' % str ( [ user . identity ( ) for user in userlist ] ) )
2014-12-29 17:08:13 +00:00
for n in userlist :
2015-01-31 23:40:13 +00:00
log_log ( ' user to check: %s ' % n . user . nick )
t = link . network . get_last_active_time ( n . user . nick , group . name )
2014-12-29 17:08:13 +00:00
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 )
2015-02-01 15:51:08 +00:00
weights . append ( ( n , w ) )
2014-12-29 17:08:13 +00:00
weight + = w
if len ( weights ) == 0 :
2015-01-13 12:28:05 +00:00
link . send ( " Nobody eligible for rain " )
2014-12-29 17:08:13 +00:00
return
pipe = redis_pipeline ( )
2015-01-31 23:40:13 +00:00
pipe . hincrby ( " balances " , account , - units )
2015-01-07 20:43:30 +00:00
pipe . incrby ( " arain_total_count " , 1 ) ;
pipe . incrby ( " arain_total_amount " , units ) ;
2015-01-13 12:28:05 +00:00
pipe . hincrby ( " arain_count " , identity , 1 ) ;
pipe . hincrby ( " arain_amount " , identity , units ) ;
2014-12-29 17:08:13 +00:00
rained_units = 0
nnicks = 0
minu = None
maxu = None
2015-02-01 15:51:08 +00:00
for n , w in weights :
user_units = long ( units * w / weight )
2014-12-29 17:08:13 +00:00
if user_units < = 0 :
continue
2015-01-31 23:40:13 +00:00
act = now - link . network . get_last_active_time ( n . user . nick , link . group . name )
log_info ( " %s rained %s on %s (last active %f hours ago) " % ( identity , AmountToString ( user_units ) , n . identity ( ) , act / 3600 ) )
a = GetAccount ( n )
pipe . hincrby ( " balances " , a , user_units )
2014-12-29 17:08:13 +00:00
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 :
2015-01-13 12:28:05 +00:00
link . send ( " This would mean not even an atomic unit per nick " )
2014-12-29 17:08:13 +00:00
return
pipe . execute ( )
2015-01-20 14:56:41 +00:00
log_info ( " %s rained %s - %s (total %s , acc %s ) on the %d nicks active in the last %s " % ( 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 %s " % ( link . user . 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 ) )
2015-01-13 12:28:05 +00:00
link . send ( " An error has occured " )
2014-12-29 17:08:13 +00:00
return
2015-01-13 12:28:05 +00:00
def Help ( link ) :
2015-01-20 17:18:15 +00:00
link . send_private ( ' You can tip other people, or rain %s on them ' % coinspecs . name )
link . send_private ( ' !tip tips a single person, while !rain shares equally between people in the channel ' )
link . send_private ( ' !rainactive tips all within the last N hours, with more recently active people ' )
link . send_private ( ' 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 "
} )