2016-10-02 20:48:14 +00:00
import json
2016-04-19 17:40:59 +00:00
import random
2016-12-11 22:12:06 +00:00
import re
2016-10-02 20:48:14 +00:00
import requests
2016-12-11 22:12:06 +00:00
import time
2016-10-02 20:48:14 +00:00
from common import generalUtils
from common . constants import mods
from common . log import logUtils as log
from common . ripple import userUtils
2016-05-18 17:12:46 +00:00
from constants import exceptions
2016-10-02 20:48:14 +00:00
from common . constants import gameModes
from common . constants import privileges
from constants import serverPackets
2016-05-18 17:12:46 +00:00
from helpers import systemHelper
2016-10-02 20:48:14 +00:00
from objects import fokabot
from objects import glob
2016-12-11 22:12:06 +00:00
from helpers import chatHelper as chat
2016-05-28 18:26:26 +00:00
2016-04-19 17:40:59 +00:00
"""
Commands callbacks
Must have fro , chan and messages as arguments
2016-11-17 18:13:06 +00:00
: param fro : username of who triggered the command
: param chan : channel " (or username, if PM) where the message was sent
: param message : list containing arguments passed from the message
[ 0 ] = first argument
[ 1 ] = second argument
. . .
2016-04-19 17:40:59 +00:00
return the message or * * False * * if there ' s no response by the bot
2016-09-02 10:41:19 +00:00
TODO : Change False to None , because False doesn ' t make any sense
2016-04-19 17:40:59 +00:00
"""
2016-08-28 15:25:47 +00:00
def instantRestart ( fro , chan , message ) :
2016-10-01 19:19:03 +00:00
glob . streams . broadcast ( " main " , serverPackets . notification ( " We are restarting Bancho. Be right back! " ) )
2016-08-28 15:25:47 +00:00
systemHelper . scheduleShutdown ( 0 , True , delay = 1 )
return False
2016-04-19 17:40:59 +00:00
def faq ( fro , chan , message ) :
2016-11-17 18:13:06 +00:00
# TODO: Unhardcode this
2016-04-19 17:40:59 +00:00
if message [ 0 ] == " rules " :
return " Please make sure to check (Ripple ' s rules)[http://ripple.moe/?p=23]. "
elif message [ 0 ] == " swearing " :
return " Please don ' t abuse swearing "
elif message [ 0 ] == " spam " :
return " Please don ' t spam "
elif message [ 0 ] == " offend " :
return " Please don ' t offend other players "
elif message [ 0 ] == " github " :
return " (Ripple ' s Github page!)[https://github.com/osuripple/ripple] "
elif message [ 0 ] == " discord " :
return " (Join Ripple ' s Discord!)[https://discord.gg/0rJcZruIsA6rXuIx] "
elif message [ 0 ] == " blog " :
return " You can find the latest Ripple news on the (blog)[https://ripple.moe/blog/]! "
elif message [ 0 ] == " changelog " :
return " Check the (changelog)[https://ripple.moe/index.php?p=17] ! "
elif message [ 0 ] == " status " :
return " Check the server status (here!)[https://ripple.moe/index.php?p=27] "
2016-05-19 18:33:46 +00:00
elif message [ 0 ] == " english " :
return " Please keep this channel in english. "
2016-05-28 18:26:26 +00:00
else :
return False
2016-04-19 17:40:59 +00:00
def roll ( fro , chan , message ) :
maxPoints = 100
if len ( message ) > = 1 :
if message [ 0 ] . isdigit ( ) == True and int ( message [ 0 ] ) > 0 :
maxPoints = int ( message [ 0 ] )
points = random . randrange ( 0 , maxPoints )
return " {} rolls {} points! " . format ( fro , str ( points ) )
2016-07-30 13:21:53 +00:00
#def ask(fro, chan, message):
# return random.choice(["yes", "no", "maybe"])
2016-04-19 17:40:59 +00:00
def alert ( fro , chan , message ) :
2016-10-01 19:19:03 +00:00
glob . streams . broadcast ( " main " , serverPackets . notification ( ' ' . join ( message [ : ] ) ) )
2016-04-19 17:40:59 +00:00
return False
2016-05-28 18:26:26 +00:00
def alertUser ( fro , chan , message ) :
target = message [ 0 ] . replace ( " _ " , " " )
targetToken = glob . tokens . getTokenFromUsername ( target )
2016-09-02 15:45:10 +00:00
if targetToken is not None :
2016-05-28 18:26:26 +00:00
targetToken . enqueue ( serverPackets . notification ( ' ' . join ( message [ 1 : ] ) ) )
return False
else :
return " User offline. "
2016-04-19 17:40:59 +00:00
def moderated ( fro , chan , message ) :
try :
# Make sure we are in a channel and not PM
2016-09-02 15:45:10 +00:00
if not chan . startswith ( " # " ) :
2016-04-19 17:40:59 +00:00
raise exceptions . moderatedPMException
# Get on/off
enable = True
if len ( message ) > = 1 :
if message [ 0 ] == " off " :
enable = False
# Turn on/off moderated mode
glob . channels . channels [ chan ] . moderated = enable
return " This channel is {} in moderated mode! " . format ( " now " if enable else " no longer " )
except exceptions . moderatedPMException :
return " You are trying to put a private chat in moderated mode. Are you serious?!? You ' re fired. "
def kickAll ( fro , chan , message ) :
# Kick everyone but mods/admins
toKick = [ ]
for key , value in glob . tokens . tokens . items ( ) :
2016-09-02 15:45:10 +00:00
if not value . admin :
2016-04-19 17:40:59 +00:00
toKick . append ( key )
# Loop though users to kick (we can't change dictionary size while iterating)
for i in toKick :
if i in glob . tokens . tokens :
glob . tokens . tokens [ i ] . kick ( )
return " Whoops! Rip everyone. "
def kick ( fro , chan , message ) :
# Get parameters
2016-12-11 10:39:01 +00:00
target = message [ 0 ] . lower ( ) . replace ( " _ " , " " )
if target == " fokabot " :
return " Nope. "
2016-04-19 17:40:59 +00:00
# Get target token and make sure is connected
2016-12-11 10:39:01 +00:00
tokens = glob . tokens . getTokenFromUsername ( target , all = True )
if len ( tokens ) == 0 :
2016-04-19 17:40:59 +00:00
return " {} is not online " . format ( target )
2016-12-11 10:39:01 +00:00
# Kick users
for i in tokens :
i . kick ( )
2016-04-19 17:40:59 +00:00
# Bot response
return " {} has been kicked from the server. " . format ( target )
def fokabotReconnect ( fro , chan , message ) :
# Check if fokabot is already connected
2016-09-02 15:45:10 +00:00
if glob . tokens . getTokenFromUserID ( 999 ) is not None :
2016-07-14 10:37:07 +00:00
return " Fokabot is already connected to Bancho "
2016-04-19 17:40:59 +00:00
# Fokabot is not connected, connect it
fokabot . connect ( )
return False
def silence ( fro , chan , message ) :
for i in message :
i = i . lower ( )
target = message [ 0 ] . replace ( " _ " , " " )
amount = message [ 1 ]
unit = message [ 2 ]
reason = ' ' . join ( message [ 3 : ] )
# Get target user ID
2016-10-02 20:48:14 +00:00
targetUserID = userUtils . getID ( target )
userID = userUtils . getID ( fro )
2016-04-19 17:40:59 +00:00
# Make sure the user exists
2016-09-02 15:45:10 +00:00
if not targetUserID :
2016-04-19 17:40:59 +00:00
return " {} : user not found " . format ( target )
# Calculate silence seconds
if unit == ' s ' :
silenceTime = int ( amount )
elif unit == ' m ' :
2016-11-17 18:13:06 +00:00
silenceTime = int ( amount ) * 60
2016-04-19 17:40:59 +00:00
elif unit == ' h ' :
2016-11-17 18:13:06 +00:00
silenceTime = int ( amount ) * 3600
2016-04-19 17:40:59 +00:00
elif unit == ' d ' :
2016-11-17 18:13:06 +00:00
silenceTime = int ( amount ) * 86400
2016-04-19 17:40:59 +00:00
else :
return " Invalid time unit (s/m/h/d). "
# Max silence time is 7 days
if silenceTime > 604800 :
return " Invalid silence time. Max silence time is 7 days. "
# Send silence packet to target if he's connected
targetToken = glob . tokens . getTokenFromUsername ( target )
2016-09-02 15:45:10 +00:00
if targetToken is not None :
2016-06-07 21:19:45 +00:00
# user online, silence both in db and with packet
targetToken . silence ( silenceTime , reason , userID )
else :
# User offline, silence user only in db
2016-10-02 20:48:14 +00:00
userUtils . silence ( targetUserID , silenceTime , reason , userID )
2016-04-19 17:40:59 +00:00
2016-06-02 17:22:02 +00:00
# Log message
msg = " {} has been silenced for the following reason: {} " . format ( target , reason )
return msg
2016-04-19 17:40:59 +00:00
def removeSilence ( fro , chan , message ) :
# Get parameters
for i in message :
i = i . lower ( )
target = message [ 0 ] . replace ( " _ " , " " )
# Make sure the user exists
2016-10-02 20:48:14 +00:00
targetUserID = userUtils . getID ( target )
userID = userUtils . getID ( fro )
2016-09-02 15:45:10 +00:00
if not targetUserID :
2016-04-19 17:40:59 +00:00
return " {} : user not found " . format ( target )
# Send new silence end packet to user if he's online
targetToken = glob . tokens . getTokenFromUsername ( target )
2016-09-02 15:45:10 +00:00
if targetToken is not None :
2016-06-07 21:19:45 +00:00
# User online, remove silence both in db and with packet
targetToken . silence ( 0 , " " , userID )
else :
# user offline, remove islene ofnlt from db
2016-10-02 20:48:14 +00:00
userUtils . silence ( targetUserID , 0 , " " , userID )
2016-04-19 17:40:59 +00:00
return " {} ' s silence reset " . format ( target )
2016-04-24 09:46:33 +00:00
def ban ( fro , chan , message ) :
# Get parameters
for i in message :
i = i . lower ( )
target = message [ 0 ] . replace ( " _ " , " " )
# Make sure the user exists
2016-10-02 20:48:14 +00:00
targetUserID = userUtils . getID ( target )
userID = userUtils . getID ( fro )
2016-09-02 15:45:10 +00:00
if not targetUserID :
2016-04-24 09:46:33 +00:00
return " {} : user not found " . format ( target )
# Set allowed to 0
2016-10-02 20:48:14 +00:00
userUtils . ban ( targetUserID )
2016-04-24 09:46:33 +00:00
# Send ban packet to the user if he's online
targetToken = glob . tokens . getTokenFromUsername ( target )
2016-09-02 15:45:10 +00:00
if targetToken is not None :
2016-04-24 09:46:33 +00:00
targetToken . enqueue ( serverPackets . loginBanned ( ) )
2016-06-08 09:33:27 +00:00
log . rap ( userID , " has banned {} " . format ( target ) , True )
2016-04-24 09:46:33 +00:00
return " RIP {} . You will not be missed. " . format ( target )
def unban ( fro , chan , message ) :
# Get parameters
for i in message :
i = i . lower ( )
target = message [ 0 ] . replace ( " _ " , " " )
# Make sure the user exists
2016-10-02 20:48:14 +00:00
targetUserID = userUtils . getID ( target )
userID = userUtils . getID ( fro )
2016-09-02 15:45:10 +00:00
if not targetUserID :
2016-04-24 09:46:33 +00:00
return " {} : user not found " . format ( target )
# Set allowed to 1
2016-10-02 20:48:14 +00:00
userUtils . unban ( targetUserID )
2016-04-24 09:46:33 +00:00
2016-06-08 09:33:27 +00:00
log . rap ( userID , " has unbanned {} " . format ( target ) , True )
2016-04-24 09:46:33 +00:00
return " Welcome back {} ! " . format ( target )
2016-07-03 18:51:19 +00:00
def restrict ( fro , chan , message ) :
# Get parameters
for i in message :
i = i . lower ( )
target = message [ 0 ] . replace ( " _ " , " " )
# Make sure the user exists
2016-10-02 20:48:14 +00:00
targetUserID = userUtils . getID ( target )
userID = userUtils . getID ( fro )
2016-09-02 15:45:10 +00:00
if not targetUserID :
2016-07-03 18:51:19 +00:00
return " {} : user not found " . format ( target )
# Put this user in restricted mode
2016-10-02 20:48:14 +00:00
userUtils . restrict ( targetUserID )
2016-07-03 18:51:19 +00:00
# Send restricted mode packet to this user if he's online
targetToken = glob . tokens . getTokenFromUsername ( target )
2016-09-02 15:45:10 +00:00
if targetToken is not None :
2016-07-03 18:51:19 +00:00
targetToken . setRestricted ( )
log . rap ( userID , " has put {} in restricted mode " . format ( target ) , True )
return " Bye bye {} . See you later, maybe. " . format ( target )
def unrestrict ( fro , chan , message ) :
# Get parameters
for i in message :
i = i . lower ( )
target = message [ 0 ] . replace ( " _ " , " " )
# Make sure the user exists
2016-10-02 20:48:14 +00:00
targetUserID = userUtils . getID ( target )
userID = userUtils . getID ( fro )
2016-09-02 15:45:10 +00:00
if not targetUserID :
2016-07-03 18:51:19 +00:00
return " {} : user not found " . format ( target )
# Set allowed to 1
2016-10-02 20:48:14 +00:00
userUtils . unrestrict ( targetUserID )
2016-07-03 18:51:19 +00:00
log . rap ( userID , " has removed restricted mode from {} " . format ( target ) , True )
return " Welcome back {} ! " . format ( target )
2016-04-19 17:40:59 +00:00
def restartShutdown ( restart ) :
""" Restart (if restart = True) or shutdown (if restart = False) pep.py safely """
msg = " We are performing some maintenance. Bancho will {} in 5 seconds. Thank you for your patience. " . format ( " restart " if restart else " shutdown " )
systemHelper . scheduleShutdown ( 5 , restart , msg )
return msg
def systemRestart ( fro , chan , message ) :
return restartShutdown ( True )
def systemShutdown ( fro , chan , message ) :
return restartShutdown ( False )
def systemReload ( fro , chan , message ) :
2016-11-20 10:31:51 +00:00
glob . banchoConf . reload ( )
2016-04-19 17:40:59 +00:00
return " Bancho settings reloaded! "
def systemMaintenance ( fro , chan , message ) :
# Turn on/off bancho maintenance
maintenance = True
# Get on/off
if len ( message ) > = 2 :
if message [ 1 ] == " off " :
maintenance = False
# Set new maintenance value in bancho_settings table
glob . banchoConf . setMaintenance ( maintenance )
2016-09-02 15:45:10 +00:00
if maintenance :
2016-04-19 17:40:59 +00:00
# We have turned on maintenance mode
# Users that will be disconnected
who = [ ]
# Disconnect everyone but mod/admins
for _ , value in glob . tokens . tokens . items ( ) :
2016-09-02 15:45:10 +00:00
if not value . admin :
2016-04-19 17:40:59 +00:00
who . append ( value . userID )
2016-10-01 19:19:03 +00:00
glob . streams . broadcast ( " main " , serverPackets . notification ( " Our bancho server is in maintenance mode. Please try to login again later. " ) )
2016-04-19 17:40:59 +00:00
glob . tokens . multipleEnqueue ( serverPackets . loginError ( ) , who )
msg = " The server is now in maintenance mode! "
else :
# We have turned off maintenance mode
# Send message if we have turned off maintenance mode
msg = " The server is no longer in maintenance mode! "
# Chat output
return msg
def systemStatus ( fro , chan , message ) :
# Print some server info
data = systemHelper . getSystemInfo ( )
# Final message
2016-11-17 19:11:11 +00:00
letsVersion = glob . redis . get ( " lets:version " )
if letsVersion is None :
letsVersion = " \ _(xd)_/ "
else :
letsVersion = letsVersion . decode ( " utf-8 " )
2016-08-28 15:25:47 +00:00
msg = " pep.py bancho server v {} \n " . format ( glob . VERSION )
2016-11-17 19:11:11 +00:00
msg + = " LETS scores server v {} \n " . format ( letsVersion )
2016-06-09 08:51:13 +00:00
msg + = " made by the Ripple team \n "
2016-04-19 17:40:59 +00:00
msg + = " \n "
msg + = " === BANCHO STATS === \n "
2016-06-09 08:51:13 +00:00
msg + = " Connected users: {} \n " . format ( data [ " connectedUsers " ] )
msg + = " Multiplayer matches: {} \n " . format ( data [ " matches " ] )
2016-08-28 15:25:47 +00:00
msg + = " Uptime: {} \n " . format ( data [ " uptime " ] )
2016-04-19 17:40:59 +00:00
msg + = " \n "
msg + = " === SYSTEM STATS === \n "
2016-06-09 08:51:13 +00:00
msg + = " CPU: {} % \n " . format ( data [ " cpuUsage " ] )
msg + = " RAM: {} GB/ {} GB \n " . format ( data [ " usedMemory " ] , data [ " totalMemory " ] )
2016-09-02 15:45:10 +00:00
if data [ " unix " ] :
2016-06-09 08:51:13 +00:00
msg + = " Load average: {} / {} / {} \n " . format ( data [ " loadAverage " ] [ 0 ] , data [ " loadAverage " ] [ 1 ] , data [ " loadAverage " ] [ 2 ] )
2016-04-19 17:40:59 +00:00
return msg
2016-05-19 18:17:20 +00:00
2016-06-14 17:51:54 +00:00
def getPPMessage ( userID , just_data = False ) :
2016-05-19 18:17:20 +00:00
try :
2016-05-19 20:10:50 +00:00
# Get user token
token = glob . tokens . getTokenFromUserID ( userID )
2016-09-02 15:45:10 +00:00
if token is None :
2016-05-19 20:10:50 +00:00
return False
2016-08-28 13:20:41 +00:00
currentMap = token . tillerino [ 0 ]
currentMods = token . tillerino [ 1 ]
currentAcc = token . tillerino [ 2 ]
2016-05-19 18:17:20 +00:00
# Send request to LETS api
2016-10-02 20:48:14 +00:00
resp = requests . get ( " http://127.0.0.1:5002/api/v1/pp?b= {} &m= {} " . format ( currentMap , currentMods , currentAcc ) , timeout = 10 ) . text
2016-05-19 18:17:20 +00:00
data = json . loads ( resp )
# Make sure status is in response data
if " status " not in data :
raise exceptions . apiException
# Make sure status is 200
if data [ " status " ] != 200 :
if " message " in data :
return " Error in LETS API call ( {} ). Please tell this to a dev. " . format ( data [ " message " ] )
else :
raise exceptions . apiException
2016-05-20 16:53:09 +00:00
2016-06-14 17:51:54 +00:00
if just_data :
return data
2016-05-19 18:17:20 +00:00
# Return response in chat
2016-05-19 20:10:50 +00:00
# Song name and mods
2016-10-02 20:48:14 +00:00
msg = " {song} {plus} {mods} " . format ( song = data [ " song_name " ] , plus = " + " if currentMods > 0 else " " , mods = generalUtils . readableMods ( currentMods ) )
2016-05-19 20:10:50 +00:00
# PP values
2016-08-28 13:21:03 +00:00
if currentAcc == - 1 :
2016-05-19 20:10:50 +00:00
msg + = " 95 % : {pp95} pp | 98 % : {pp98} pp | 99 % {pp99} pp | 100 % : {pp100} pp " . format ( pp100 = data [ " pp " ] [ 0 ] , pp99 = data [ " pp " ] [ 1 ] , pp98 = data [ " pp " ] [ 2 ] , pp95 = data [ " pp " ] [ 3 ] )
else :
msg + = " {acc:.2f} % : {pp} pp " . format ( acc = token . tillerino [ 2 ] , pp = data [ " pp " ] [ 0 ] )
2016-10-02 20:48:14 +00:00
2016-08-28 13:21:03 +00:00
originalAR = data [ " ar " ]
# calc new AR if HR/EZ is on
2016-10-02 20:48:14 +00:00
if ( currentMods & mods . EASY ) > 0 :
2016-08-28 13:21:03 +00:00
data [ " ar " ] = max ( 0 , data [ " ar " ] / 2 )
2016-10-02 20:48:14 +00:00
if ( currentMods & mods . HARDROCK ) > 0 :
2016-08-28 13:21:03 +00:00
data [ " ar " ] = min ( 10 , data [ " ar " ] * 1.4 )
2016-10-02 20:48:14 +00:00
2016-08-28 13:21:03 +00:00
arstr = " ( {} ) " . format ( originalAR ) if originalAR != data [ " ar " ] else " "
2016-10-02 20:48:14 +00:00
2016-05-19 20:10:50 +00:00
# Beatmap info
2016-08-28 13:21:03 +00:00
msg + = " | {bpm} BPM | AR {ar} {arstr} | {stars:.2f} stars " . format ( bpm = data [ " bpm " ] , stars = data [ " stars " ] , ar = data [ " ar " ] , arstr = arstr )
2016-05-19 20:10:50 +00:00
# Return final message
return msg
2016-05-19 18:17:20 +00:00
except requests . exceptions . RequestException :
# RequestException
2016-05-28 18:26:26 +00:00
return " API Timeout. Please try again in a few seconds. "
2016-05-19 18:17:20 +00:00
except exceptions . apiException :
# API error
return " Unknown error in LETS API call. Please tell this to a dev. "
2016-10-02 20:48:14 +00:00
#except:
2016-05-19 18:17:20 +00:00
# Unknown exception
# TODO: print exception
2016-10-02 20:48:14 +00:00
# return False
2016-05-19 18:17:20 +00:00
def tillerinoNp ( fro , chan , message ) :
try :
# Run the command in PM only
if chan . startswith ( " # " ) :
return False
2016-08-28 13:20:41 +00:00
playWatch = message [ 1 ] == " playing " or message [ 1 ] == " watching "
2016-05-19 18:17:20 +00:00
# Get URL from message
2016-05-19 21:31:48 +00:00
if message [ 1 ] == " listening " :
beatmapURL = str ( message [ 3 ] [ 1 : ] )
2016-08-28 13:20:41 +00:00
elif playWatch :
2016-05-19 21:31:48 +00:00
beatmapURL = str ( message [ 2 ] [ 1 : ] )
else :
return False
2016-05-19 18:17:20 +00:00
2016-08-28 13:20:41 +00:00
modsEnum = 0
mapping = {
2016-10-02 20:48:14 +00:00
" -Easy " : mods . EASY ,
" -NoFail " : mods . NOFAIL ,
" +Hidden " : mods . HIDDEN ,
" +HardRock " : mods . HARDROCK ,
" +Nightcore " : mods . NIGHTCORE ,
" +DoubleTime " : mods . DOUBLETIME ,
" -HalfTime " : mods . HALFTIME ,
" +Flashlight " : mods . FLASHLIGHT ,
" -SpunOut " : mods . SPUNOUT
2016-08-28 13:20:41 +00:00
}
if playWatch :
for part in message :
part = part . replace ( " \x01 " , " " )
if part in mapping . keys ( ) :
modsEnum + = mapping [ part ]
2016-05-19 18:17:20 +00:00
# Get beatmap id from URL
2016-05-20 16:53:09 +00:00
beatmapID = fokabot . npRegex . search ( beatmapURL ) . groups ( 0 ) [ 0 ]
2016-05-19 18:17:20 +00:00
# Update latest tillerino song for current token
token = glob . tokens . getTokenFromUsername ( fro )
2016-09-02 15:45:10 +00:00
if token is not None :
2016-08-28 13:20:41 +00:00
token . tillerino = [ int ( beatmapID ) , modsEnum , - 1.0 ]
2016-05-19 20:10:50 +00:00
userID = token . userID
2016-05-19 18:17:20 +00:00
# Return tillerino message
2016-05-19 20:10:50 +00:00
return getPPMessage ( userID )
2016-05-19 18:17:20 +00:00
except :
return False
def tillerinoMods ( fro , chan , message ) :
2016-05-19 20:10:50 +00:00
try :
# Run the command in PM only
if chan . startswith ( " # " ) :
return False
2016-05-19 18:17:20 +00:00
2016-05-19 20:10:50 +00:00
# Get token and user ID
token = glob . tokens . getTokenFromUsername ( fro )
2016-09-02 15:45:10 +00:00
if token is None :
2016-05-19 20:10:50 +00:00
return False
userID = token . userID
# Make sure the user has triggered the bot with /np command
if token . tillerino [ 0 ] == 0 :
return " Please give me a beatmap first with /np command. "
# Check passed mods and convert to enum
modsList = [ message [ 0 ] [ i : i + 2 ] . upper ( ) for i in range ( 0 , len ( message [ 0 ] ) , 2 ) ]
modsEnum = 0
for i in modsList :
if i not in [ " NO " , " NF " , " EZ " , " HD " , " HR " , " DT " , " HT " , " NC " , " FL " , " SO " ] :
return " Invalid mods. Allowed mods: NO, NF, EZ, HD, HR, DT, HT, NC, FL, SO. Do not use spaces for multiple mods. "
if i == " NO " :
modsEnum = 0
break
elif i == " NF " :
2016-10-02 20:48:14 +00:00
modsEnum + = mods . NOFAIL
2016-05-19 20:10:50 +00:00
elif i == " EZ " :
2016-10-02 20:48:14 +00:00
modsEnum + = mods . EASY
2016-05-19 20:10:50 +00:00
elif i == " HD " :
2016-10-02 20:48:14 +00:00
modsEnum + = mods . HIDDEN
2016-05-19 20:10:50 +00:00
elif i == " HR " :
2016-10-02 20:48:14 +00:00
modsEnum + = mods . HARDROCK
2016-05-19 20:10:50 +00:00
elif i == " DT " :
2016-10-02 20:48:14 +00:00
modsEnum + = mods . DOUBLETIME
2016-05-19 20:10:50 +00:00
elif i == " HT " :
2016-10-02 20:48:14 +00:00
modsEnum + = mods . HALFTIME
2016-05-19 20:10:50 +00:00
elif i == " NC " :
2016-10-02 20:48:14 +00:00
modsEnum + = mods . NIGHTCORE
2016-05-19 20:10:50 +00:00
elif i == " FL " :
2016-10-02 20:48:14 +00:00
modsEnum + = mods . FLASHLIGHT
2016-05-19 20:10:50 +00:00
elif i == " SO " :
2016-10-02 20:48:14 +00:00
modsEnum + = mods . SPUNOUT
2016-05-19 20:10:50 +00:00
# Set mods
token . tillerino [ 1 ] = modsEnum
# Return tillerino message for that beatmap with mods
return getPPMessage ( userID )
except :
2016-05-19 18:17:20 +00:00
return False
2016-05-19 20:10:50 +00:00
def tillerinoAcc ( fro , chan , message ) :
try :
# Run the command in PM only
if chan . startswith ( " # " ) :
return False
# Get token and user ID
token = glob . tokens . getTokenFromUsername ( fro )
2016-09-02 15:45:10 +00:00
if token is None :
2016-05-19 20:10:50 +00:00
return False
userID = token . userID
# Make sure the user has triggered the bot with /np command
if token . tillerino [ 0 ] == 0 :
return " Please give me a beatmap first with /np command. "
# Convert acc to float
acc = float ( message [ 0 ] )
# Set new tillerino list acc value
token . tillerino [ 2 ] = acc
# Return tillerino message for that beatmap with mods
return getPPMessage ( userID )
except ValueError :
return " Invalid acc value "
except :
return False
2016-05-19 18:17:20 +00:00
2016-05-25 14:23:53 +00:00
def tillerinoLast ( fro , chan , message ) :
try :
2016-06-06 17:10:32 +00:00
data = glob . db . fetch ( """ SELECT beatmaps.song_name as sn, scores.*,
2016-07-20 09:59:53 +00:00
beatmaps . beatmap_id as bid , beatmaps . difficulty_std , beatmaps . difficulty_taiko , beatmaps . difficulty_ctb , beatmaps . difficulty_mania , beatmaps . max_combo as fc
2016-05-25 14:23:53 +00:00
FROM scores
LEFT JOIN beatmaps ON beatmaps . beatmap_md5 = scores . beatmap_md5
2016-05-28 20:33:05 +00:00
LEFT JOIN users ON users . id = scores . userid
2016-05-31 20:49:30 +00:00
WHERE users . username = % s
2016-05-25 14:23:53 +00:00
ORDER BY scores . time DESC
LIMIT 1 """ , [fro])
2016-09-02 15:45:10 +00:00
if data is None :
2016-05-25 14:23:53 +00:00
return False
2016-06-06 17:10:32 +00:00
2016-07-20 09:59:53 +00:00
diffString = " difficulty_ {} " . format ( gameModes . getGameModeForDB ( data [ " play_mode " ] ) )
2016-10-02 20:48:14 +00:00
rank = generalUtils . getRank ( data [ " play_mode " ] , data [ " mods " ] , data [ " accuracy " ] ,
data [ " 300_count " ] , data [ " 100_count " ] , data [ " 50_count " ] , data [ " misses_count " ] )
2016-06-06 17:10:32 +00:00
2016-06-16 01:34:38 +00:00
ifPlayer = " {0} | " . format ( fro ) if chan != " FokaBot " else " "
2016-07-20 13:20:23 +00:00
ifFc = " (FC) " if data [ " max_combo " ] == data [ " fc " ] else " {0} x/ {1} x " . format ( data [ " max_combo " ] , data [ " fc " ] )
2016-06-16 01:34:38 +00:00
beatmapLink = " [http://osu.ppy.sh/b/ {1} {0} ] " . format ( data [ " sn " ] , data [ " bid " ] )
2016-10-02 20:48:14 +00:00
hasPP = data [ " play_mode " ] == gameModes . STD or data [ " play_mode " ] == gameModes . MANIA
2016-06-16 01:34:38 +00:00
2016-07-20 13:20:23 +00:00
msg = ifPlayer
msg + = beatmapLink
2016-10-02 20:48:14 +00:00
if data [ " play_mode " ] != gameModes . STD :
2016-07-20 13:20:23 +00:00
msg + = " < {0} > " . format ( gameModes . getGameModeForPrinting ( data [ " play_mode " ] ) )
if data [ " mods " ] :
2016-10-02 20:48:14 +00:00
msg + = ' + ' + generalUtils . readableMods ( data [ " mods " ] )
2016-07-20 13:20:23 +00:00
if not hasPP :
2016-06-16 01:34:38 +00:00
msg + = " | {0:,} " . format ( data [ " score " ] )
msg + = ifFc
msg + = " | {0:.2f} % , {1} " . format ( data [ " accuracy " ] , rank . upper ( ) )
msg + = " {{ {0} / {1} / {2} / {3} }} " . format ( data [ " 300_count " ] , data [ " 100_count " ] , data [ " 50_count " ] , data [ " misses_count " ] )
2016-07-20 09:59:53 +00:00
msg + = " | {0:.2f} stars " . format ( data [ diffString ] )
2016-06-16 01:34:38 +00:00
return msg
msg + = " ( {0:.2f} % , {1} ) " . format ( data [ " accuracy " ] , rank . upper ( ) )
msg + = ifFc
msg + = " | {0:.2f} pp " . format ( data [ " pp " ] )
2016-06-14 17:51:54 +00:00
2016-07-20 09:59:53 +00:00
stars = data [ diffString ]
2016-07-20 13:20:23 +00:00
if data [ " mods " ] :
2016-06-14 17:51:54 +00:00
token = glob . tokens . getTokenFromUsername ( fro )
2016-09-02 15:45:10 +00:00
if token is None :
2016-06-14 17:51:54 +00:00
return False
userID = token . userID
token . tillerino [ 0 ] = data [ " bid " ]
token . tillerino [ 1 ] = data [ " mods " ]
token . tillerino [ 2 ] = data [ " accuracy " ]
oppaiData = getPPMessage ( userID , just_data = True )
if " stars " in oppaiData :
stars = oppaiData [ " stars " ]
msg + = " | {0:.2f} stars " . format ( stars )
2016-06-06 17:10:32 +00:00
return msg
2016-05-25 14:23:53 +00:00
except Exception as a :
2016-06-04 10:44:54 +00:00
log . error ( a )
2016-05-25 14:23:53 +00:00
return False
2016-05-19 18:17:20 +00:00
2016-05-28 18:30:34 +00:00
def mm00 ( fro , chan , message ) :
random . seed ( )
return random . choice ( [ " meme " , " MA MAURO ESISTE? " ] )
2016-09-13 10:25:59 +00:00
def pp ( fro , chan , message ) :
if chan . startswith ( " # " ) :
return False
gameMode = None
if len ( message ) > = 1 :
gm = {
" standard " : 0 ,
" std " : 0 ,
" taiko " : 1 ,
" ctb " : 2 ,
" mania " : 3
}
if message [ 0 ] . lower ( ) not in gm :
return " What ' s that game mode? I ' ve never heard of it :/ "
else :
gameMode = gm [ message [ 0 ] . lower ( ) ]
token = glob . tokens . getTokenFromUsername ( fro )
if token is None :
return False
if gameMode is None :
gameMode = token . gameMode
2016-10-02 20:48:14 +00:00
if gameMode == gameModes . TAIKO or gameMode == gameModes . CTB :
2016-09-13 10:25:59 +00:00
return " PP for your current game mode is not supported yet. "
2016-10-02 20:48:14 +00:00
pp = userUtils . getPP ( token . userID , gameMode )
2016-09-13 10:25:59 +00:00
return " You have {:,} pp " . format ( pp )
2016-12-11 10:07:35 +00:00
def updateBeatmap ( fro , chan , message ) :
2016-10-15 21:21:17 +00:00
try :
# Run the command in PM only
if chan . startswith ( " # " ) :
return False
# Get token and user ID
token = glob . tokens . getTokenFromUsername ( fro )
if token is None :
return False
# Make sure the user has triggered the bot with /np command
if token . tillerino [ 0 ] == 0 :
return " Please give me a beatmap first with /np command. "
# Send request
beatmapData = glob . db . fetch ( " SELECT beatmapset_id, song_name FROM beatmaps WHERE beatmap_id = %s LIMIT 1 " , [ token . tillerino [ 0 ] ] )
if beatmapData is None :
return " Couldn ' t find beatmap data in database. Please load the beatmap ' s leaderboard and try again. "
2016-10-16 08:43:04 +00:00
response = requests . post ( " {} /api/v1/update_beatmap " . format ( glob . conf . config [ " mirror " ] [ " url " ] ) , {
2016-10-15 21:21:17 +00:00
" beatmap_set_id " : beatmapData [ " beatmapset_id " ] ,
" beatmap_name " : beatmapData [ " song_name " ] ,
" username " : token . username ,
" key " : glob . conf . config [ " mirror " ] [ " apikey " ]
} )
if response . status_code == 200 :
return " An update request for that beatmap has been queued. You ' ll receive a message once the beatmap has been updated on our mirror! "
elif response . status_code == 429 :
return " You are sending too many beatmaps update requests. Wait a bit and retry later. "
else :
return " Error in beatmap mirror API request. Tell this to a dev: {} " . format ( response . text )
except :
return False
2016-09-13 10:25:59 +00:00
2016-12-11 22:12:06 +00:00
def report ( fro , chan , message ) :
msg = " "
try :
# TODO: Rate limit
# Regex on message
reportRegex = re . compile ( " ^(.+) \ ((.+) \ ) \ :(?: )?(.+)?$ " )
result = reportRegex . search ( " " . join ( message ) )
# Make sure the message matches the regex
if result is None :
raise exceptions . invalidArgumentsException ( )
# Get username, report reason and report info
target , reason , additionalInfo = result . groups ( )
target = chat . fixUsernameForBancho ( target )
# Make sure the target is not foka
if target . lower ( ) == " fokabot " :
raise exceptions . invalidUserException ( )
# Make sure the user exists
targetID = userUtils . getID ( target )
if targetID == 0 :
raise exceptions . userNotFoundException ( )
# Make sure that the user has specified additional info if report reason is 'Other'
if reason . lower ( ) == " other " and additionalInfo is None :
raise exceptions . missingReportInfoException ( )
# Get the token if possible
chatlog = " "
token = glob . tokens . getTokenFromUsername ( target )
if token is not None :
chatlog = token . getMessagesBufferString ( )
# Everything is fine, submit report
glob . db . execute ( " INSERT INTO reports (id, from_uid, to_uid, reason, chatlog, time) VALUES (NULL, %s , %s , %s , %s , %s ) " , [ userUtils . getID ( fro ) , targetID , " {reason} - ingame {info} " . format ( reason = reason , info = " ( {} ) " . format ( additionalInfo ) if additionalInfo is not None else " " ) , chatlog , int ( time . time ( ) ) ] )
msg = " You ' ve reported {target} for {reason} {info} . A Community Manager will check your report as soon as possible. Every !report message you may see in chat wasn ' t sent to anyone, so nobody in chat, but admins, know about your report. Thank you for reporting! " . format ( target = target , reason = reason , info = " " if additionalInfo is None else " ( " + additionalInfo + " ) " )
adminMsg = " {user} has reported {target} for {reason} ( {info} ) " . format ( user = fro , target = target , reason = reason , info = additionalInfo )
# Log report in #admin and on discord
chat . sendMessage ( " FokaBot " , " #admin " , adminMsg )
log . warning ( adminMsg , discord = " cm " )
except exceptions . invalidUserException :
2016-12-12 21:57:00 +00:00
msg = " Hello, FokaBot here! You can ' t report me. I won ' t forget what you ' ve tried to do. Watch out. "
2016-12-11 22:12:06 +00:00
except exceptions . invalidArgumentsException :
msg = " Invalid report command syntax. To report an user, click on it and select ' Report user ' . "
except exceptions . userNotFoundException :
msg = " The user you ' ve tried to report doesn ' t exist. "
except exceptions . missingReportInfoException :
msg = " Please specify the reason of your report. "
except :
raise
finally :
if msg != " " :
2016-12-12 21:57:00 +00:00
token = glob . tokens . getTokenFromUsername ( fro )
if token is not None :
2016-12-12 21:59:58 +00:00
if token . irc :
2016-12-12 21:57:00 +00:00
chat . sendMessage ( " FokaBot " , fro , msg )
2016-12-12 21:59:58 +00:00
else :
token . enqueue ( serverPackets . notification ( msg ) )
2016-12-11 22:12:06 +00:00
return False
2016-04-19 17:40:59 +00:00
"""
Commands list
trigger : message that triggers the command
callback : function to call when the command is triggered . Optional .
response : text to return when the command is triggered . Optional .
syntax : command syntax . Arguments must be separated by spaces ( eg : < arg1 > < arg2 > )
2016-07-03 18:51:19 +00:00
privileges : privileges needed to execute the command . Optional .
2016-04-19 17:40:59 +00:00
"""
commands = [
{
" trigger " : " !roll " ,
" callback " : roll
} , {
" trigger " : " !faq " ,
" syntax " : " <name> " ,
" callback " : faq
} , {
" trigger " : " !report " ,
2016-12-11 22:12:06 +00:00
" callback " : report
2016-04-19 17:40:59 +00:00
} , {
" trigger " : " !help " ,
" response " : " Click (here)[https://ripple.moe/index.php?p=16&id=4] for FokaBot ' s full command list "
2016-07-30 13:21:53 +00:00
} , #{
#"trigger": "!ask",
#"syntax": "<question>",
#"callback": ask
#}, {
2016-07-30 13:23:47 +00:00
{
2016-04-19 17:40:59 +00:00
" trigger " : " !mm00 " ,
2016-05-28 18:30:34 +00:00
" callback " : mm00
2016-04-19 17:40:59 +00:00
} , {
" trigger " : " !alert " ,
" syntax " : " <message> " ,
2016-07-03 18:51:19 +00:00
" privileges " : privileges . ADMIN_SEND_ALERTS ,
2016-04-19 17:40:59 +00:00
" callback " : alert
2016-05-28 18:26:26 +00:00
} , {
" trigger " : " !alertuser " ,
" syntax " : " <username> <message> " ,
2016-07-03 18:51:19 +00:00
" privileges " : privileges . ADMIN_SEND_ALERTS ,
2016-05-28 18:26:26 +00:00
" callback " : alertUser ,
2016-04-19 17:40:59 +00:00
} , {
" trigger " : " !moderated " ,
2016-07-03 18:51:19 +00:00
" privileges " : privileges . ADMIN_CHAT_MOD ,
2016-04-19 17:40:59 +00:00
" callback " : moderated
} , {
" trigger " : " !kickall " ,
2016-12-07 20:15:23 +00:00
" privileges " : privileges . ADMIN_MANAGE_SERVERS ,
2016-04-19 17:40:59 +00:00
" callback " : kickAll
} , {
" trigger " : " !kick " ,
" syntax " : " <target> " ,
2016-07-03 18:51:19 +00:00
" privileges " : privileges . ADMIN_KICK_USERS ,
2016-04-19 17:40:59 +00:00
" callback " : kick
} , {
" trigger " : " !fokabot reconnect " ,
2016-07-03 18:51:19 +00:00
" privileges " : privileges . ADMIN_MANAGE_SERVERS ,
2016-04-19 17:40:59 +00:00
" callback " : fokabotReconnect
} , {
" trigger " : " !silence " ,
" syntax " : " <target> <amount> <unit(s/m/h/d)> <reason> " ,
2016-07-03 18:51:19 +00:00
" privileges " : privileges . ADMIN_SILENCE_USERS ,
2016-04-19 17:40:59 +00:00
" callback " : silence
} , {
" trigger " : " !removesilence " ,
" syntax " : " <target> " ,
2016-07-03 18:51:19 +00:00
" privileges " : privileges . ADMIN_SILENCE_USERS ,
2016-04-19 17:40:59 +00:00
" callback " : removeSilence
} , {
" trigger " : " !system restart " ,
2016-07-03 18:51:19 +00:00
" privileges " : privileges . ADMIN_MANAGE_SERVERS ,
2016-04-19 17:40:59 +00:00
" callback " : systemRestart
} , {
" trigger " : " !system shutdown " ,
2016-07-03 18:51:19 +00:00
" privileges " : privileges . ADMIN_MANAGE_SERVERS ,
2016-04-19 17:40:59 +00:00
" callback " : systemShutdown
} , {
" trigger " : " !system reload " ,
2016-07-03 18:51:19 +00:00
" privileges " : privileges . ADMIN_MANAGE_SETTINGS ,
2016-04-19 17:40:59 +00:00
" callback " : systemReload
} , {
" trigger " : " !system maintenance " ,
2016-07-03 18:51:19 +00:00
" privileges " : privileges . ADMIN_MANAGE_SERVERS ,
2016-04-19 17:40:59 +00:00
" callback " : systemMaintenance
} , {
" trigger " : " !system status " ,
2016-07-03 18:51:19 +00:00
" privileges " : privileges . ADMIN_MANAGE_SERVERS ,
2016-04-19 17:40:59 +00:00
" callback " : systemStatus
2016-04-24 09:46:33 +00:00
} , {
" trigger " : " !ban " ,
" syntax " : " <target> " ,
2016-07-03 18:51:19 +00:00
" privileges " : privileges . ADMIN_BAN_USERS ,
2016-04-24 09:46:33 +00:00
" callback " : ban
} , {
" trigger " : " !unban " ,
" syntax " : " <target> " ,
2016-07-03 18:51:19 +00:00
" privileges " : privileges . ADMIN_BAN_USERS ,
2016-04-24 09:46:33 +00:00
" callback " : unban
2016-07-03 18:51:19 +00:00
} , {
" trigger " : " !restrict " ,
" syntax " : " <target> " ,
" privileges " : privileges . ADMIN_BAN_USERS ,
" callback " : restrict
} , {
" trigger " : " !unrestrict " ,
" syntax " : " <target> " ,
" privileges " : privileges . ADMIN_BAN_USERS ,
" callback " : unrestrict
2016-05-19 18:17:20 +00:00
} , {
2016-05-29 12:14:27 +00:00
" trigger " : " \x01 ACTION is listening to " ,
2016-05-19 18:17:20 +00:00
" callback " : tillerinoNp
2016-05-19 21:31:48 +00:00
} , {
2016-05-29 12:14:27 +00:00
" trigger " : " \x01 ACTION is playing " ,
2016-05-19 21:31:48 +00:00
" callback " : tillerinoNp
2016-06-13 17:54:07 +00:00
} , {
" trigger " : " \x01 ACTION is watching " ,
" callback " : tillerinoNp
2016-05-19 18:17:20 +00:00
} , {
" trigger " : " !with " ,
" callback " : tillerinoMods ,
" syntax " : " <mods> "
2016-05-25 14:23:53 +00:00
} , {
" trigger " : " !last " ,
" callback " : tillerinoLast
2016-08-28 15:25:47 +00:00
} , {
" trigger " : " !ir " ,
" privileges " : privileges . ADMIN_MANAGE_SERVERS ,
" callback " : instantRestart
2016-09-13 10:25:59 +00:00
} , {
" trigger " : " !pp " ,
" callback " : pp
2016-10-15 21:21:17 +00:00
} , {
" trigger " : " !update " ,
2016-10-16 08:56:41 +00:00
" callback " : updateBeatmap
2016-05-25 14:23:53 +00:00
}
2016-05-19 20:10:50 +00:00
#
# "trigger": "!acc",
# "callback": tillerinoAcc,
# "syntax": "<accuarcy>"
#}
2016-04-19 17:40:59 +00:00
]
# Commands list default values
for cmd in commands :
cmd . setdefault ( " syntax " , " " )
2016-07-03 18:51:19 +00:00
cmd . setdefault ( " privileges " , None )
2016-04-19 17:40:59 +00:00
cmd . setdefault ( " callback " , None )
cmd . setdefault ( " response " , " u w0t m8? " )