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
2017-08-03 23:04:26 +00:00
import threading
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
2017-08-07 21:21:49 +00:00
from constants import exceptions , slotStatuses , matchModModes , matchTeams
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
2017-01-16 20:24:15 +00:00
from common . web import cheesegull
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! " ) )
2017-08-10 23:14:21 +00:00
systemHelper . scheduleShutdown ( 0 , True , delay = 5 )
2016-08-28 15:25:47 +00:00
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. "
2017-02-16 21:11:15 +00:00
elif message [ 0 ] == " topic " :
return " Can you please drop the topic and talk about something else? "
elif message [ 0 ] == " lines " :
return " Please try to keep your sentences on a single line to avoid getting silenced. "
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 ) :
2017-07-23 01:40:51 +00:00
target = message [ 0 ] . lower ( )
targetToken = glob . tokens . getTokenFromUsername ( target , safe = True )
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
2017-07-23 01:40:51 +00:00
target = message [ 0 ] . lower ( )
2016-12-11 10:39:01 +00:00
if target == " fokabot " :
return " Nope. "
2016-04-19 17:40:59 +00:00
# Get target token and make sure is connected
2017-07-23 01:40:51 +00:00
tokens = glob . tokens . getTokenFromUsername ( target , safe = True , _all = True )
2016-12-11 10:39:01 +00:00
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 ( )
2017-07-23 01:40:51 +00:00
target = message [ 0 ]
2016-04-19 17:40:59 +00:00
amount = message [ 1 ]
unit = message [ 2 ]
reason = ' ' . join ( message [ 3 : ] )
# Get target user ID
2017-07-23 01:40:51 +00:00
targetUserID = userUtils . getIDSafe ( target )
2016-10-02 20:48:14 +00:00
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 ( )
2017-07-23 01:40:51 +00:00
target = message [ 0 ]
2016-04-19 17:40:59 +00:00
# Make sure the user exists
2017-07-23 01:40:51 +00:00
targetUserID = userUtils . getIDSafe ( target )
2016-10-02 20:48:14 +00:00
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 ( )
2017-07-23 01:40:51 +00:00
target = message [ 0 ]
2016-04-24 09:46:33 +00:00
# Make sure the user exists
2017-07-23 01:40:51 +00:00
targetUserID = userUtils . getIDSafe ( target )
2016-10-02 20:48:14 +00:00
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 ( )
2017-07-23 01:40:51 +00:00
target = message [ 0 ]
2016-04-24 09:46:33 +00:00
# Make sure the user exists
2017-07-23 01:40:51 +00:00
targetUserID = userUtils . getIDSafe ( target )
2016-10-02 20:48:14 +00:00
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 ( )
2017-07-23 01:40:51 +00:00
target = message [ 0 ]
2016-07-03 18:51:19 +00:00
# Make sure the user exists
2017-07-23 01:40:51 +00:00
targetUserID = userUtils . getIDSafe ( target )
2016-10-02 20:48:14 +00:00
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 ( )
2017-07-23 01:40:51 +00:00
target = message [ 0 ]
2016-07-03 18:51:19 +00:00
# Make sure the user exists
2017-07-23 01:40:51 +00:00
targetUserID = userUtils . getIDSafe ( target )
2016-10-02 20:48:14 +00:00
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-12-28 15:15:07 +00:00
resp = requests . get ( " http://127.0.0.1:5002/api/v1/pp?b= {} &m= {} " . format ( currentMap , currentMods ) , 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 :
2017-01-16 20:24:15 +00:00
return " Error in LETS API call ( {} ). " . format ( data [ " message " ] )
2016-05-19 18:17:20 +00:00
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
2017-01-16 20:24:15 +00:00
return " Unknown error in LETS API call. "
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 :
2017-01-06 11:01:07 +00:00
# Run the command in PM only
if chan . startswith ( " # " ) :
return False
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. "
2017-01-16 20:24:15 +00:00
# Send the request to cheesegull
ok , message = cheesegull . updateBeatmap ( token . tillerino [ 0 ] )
if ok :
return " An update request for that beatmap has been queued. Check back in a few minutes and the beatmap should be updated! "
2016-10-15 21:21:17 +00:00
else :
2017-01-16 20:24:15 +00:00
return " Error in beatmap mirror API request: {} " . format ( message )
2016-10-15 21:21:17 +00:00
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
2017-08-01 22:02:38 +00:00
def multiplayer ( fro , chan , message ) :
2017-08-01 22:39:16 +00:00
def getMatchIDFromChannel ( chan ) :
if not chan . lower ( ) . startswith ( " #multi_ " ) :
raise exceptions . wrongChannelException ( )
parts = chan . lower ( ) . split ( " _ " )
if len ( parts ) < 2 or not parts [ 1 ] . isdigit ( ) :
raise exceptions . wrongChannelException ( )
matchID = int ( parts [ 1 ] )
if matchID not in glob . matches . matches :
raise exceptions . matchNotFoundException ( )
return matchID
2017-08-01 22:02:38 +00:00
def mpMake ( ) :
2017-08-01 22:22:57 +00:00
if len ( message ) < 2 :
raise exceptions . invalidArgumentsException ( " Wrong syntax: !mp make <name> " )
matchID = glob . matches . createMatch ( " " . join ( message [ 1 : ] ) , generalUtils . stringMd5 ( generalUtils . randomString ( 32 ) ) , 0 , " Tournament " , " " , 0 , - 1 , isTourney = True )
2017-08-07 21:35:33 +00:00
glob . matches . matches [ matchID ] . sendUpdates ( )
2017-08-01 22:22:57 +00:00
return " Tourney match # {} created! " . format ( matchID )
def mpJoin ( ) :
if len ( message ) < 2 or not message [ 1 ] . isdigit ( ) :
2017-08-03 21:47:34 +00:00
raise exceptions . invalidArgumentsException ( " Wrong syntax: !mp join <id> " )
2017-08-01 22:22:57 +00:00
matchID = int ( message [ 1 ] )
userToken = glob . tokens . getTokenFromUsername ( fro , ignoreIRC = True )
userToken . joinMatch ( matchID )
2017-08-03 22:09:50 +00:00
return " Attempting to join match # {} ! " . format ( matchID )
2017-08-01 22:02:38 +00:00
def mpClose ( ) :
myToken = glob . tokens . getTokenFromUsername ( fro )
if myToken . matchID == - 1 :
return " You ' re not in a multiplayer match "
glob . matches . disposeMatch ( myToken . matchID )
return " Multiplayer match # {} disposed successfully " . format ( myToken . matchID )
2017-08-01 22:39:16 +00:00
def mpLock ( ) :
matchID = getMatchIDFromChannel ( chan )
glob . matches . matches [ matchID ] . isLocked = True
return " This match has been locked "
def mpUnlock ( ) :
matchID = getMatchIDFromChannel ( chan )
glob . matches . matches [ matchID ] . isLocked = False
return " This match has been unlocked "
2017-08-03 21:47:34 +00:00
def mpSize ( ) :
2017-08-03 21:56:04 +00:00
if len ( message ) < 2 or not message [ 1 ] . isdigit ( ) or int ( message [ 1 ] ) < 2 or int ( message [ 1 ] ) > 16 :
raise exceptions . invalidArgumentsException ( " Wrong syntax: !mp size <slots(2-16)> " )
2017-08-03 21:47:34 +00:00
matchSize = int ( message [ 1 ] )
_match = glob . matches . matches [ getMatchIDFromChannel ( chan ) ]
2017-08-07 18:51:16 +00:00
_match . forceSize ( matchSize )
2017-08-03 21:47:34 +00:00
return " Match size changed to {} " . format ( matchSize )
2017-08-03 22:09:50 +00:00
def mpMove ( ) :
if len ( message ) < 3 or not message [ 2 ] . isdigit ( ) or int ( message [ 2 ] ) < 0 or int ( message [ 2 ] ) > 16 :
raise exceptions . invalidArgumentsException ( " Wrong syntax: !mp move <username> <slot> " )
username = message [ 1 ]
newSlotID = int ( message [ 2 ] )
userID = userUtils . getIDSafe ( username )
2017-08-03 22:24:01 +00:00
if userID is None :
raise exceptions . userNotFoundException ( " No such user " )
2017-08-03 22:09:50 +00:00
_match = glob . matches . matches [ getMatchIDFromChannel ( chan ) ]
2017-08-03 22:24:01 +00:00
success = _match . userChangeSlot ( userID , newSlotID )
if success :
result = " Player {} moved to slot {} " . format ( username , newSlotID )
else :
result = " You can ' t use that slot: it ' s either already occupied by someone else or locked "
return result
def mpHost ( ) :
if len ( message ) < 2 :
raise exceptions . invalidArgumentsException ( " Wrong syntax: !mp host <username> " )
username = message [ 1 ]
userID = userUtils . getIDSafe ( username )
if userID is None :
raise exceptions . userNotFoundException ( " No such user " )
_match = glob . matches . matches [ getMatchIDFromChannel ( chan ) ]
success = _match . setHost ( userID )
return " {} is now the host " . format ( username ) if success else " Couldn ' t give host to {} " . format ( username )
def mpClearHost ( ) :
matchID = getMatchIDFromChannel ( chan )
glob . matches . matches [ matchID ] . removeHost ( )
2017-08-10 22:57:58 +00:00
return " Host has been removed from this match "
2017-08-03 22:09:50 +00:00
2017-08-03 23:04:26 +00:00
def mpStart ( ) :
def _start ( ) :
matchID = getMatchIDFromChannel ( chan )
success = glob . matches . matches [ matchID ] . start ( )
if not success :
chat . sendMessage ( " FokaBot " , chan , " Couldn ' t start match. Make sure there are enough players and "
" teams are valid. The match has been unlocked. " )
else :
chat . sendMessage ( " FokaBot " , chan , " Have fun! " )
def _decreaseTimer ( t ) :
if t < = 0 :
_start ( )
else :
if t % 10 == 0 or t < = 5 :
2017-08-07 19:41:48 +00:00
chat . sendMessage ( " FokaBot " , chan , " Match starts in {} seconds. " . format ( t ) )
2017-08-03 23:04:26 +00:00
threading . Timer ( 1.00 , _decreaseTimer , [ t - 1 ] ) . start ( )
if len ( message ) < 2 or not message [ 1 ] . isdigit ( ) :
startTime = 0
else :
startTime = int ( message [ 1 ] )
2017-08-10 22:57:58 +00:00
force = False if len ( message ) < 3 else message [ 2 ] . lower ( ) == " force "
2017-08-03 23:04:26 +00:00
_match = glob . matches . matches [ getMatchIDFromChannel ( chan ) ]
# Force everyone to ready
2017-08-10 22:57:58 +00:00
someoneNotReady = False
2017-08-03 23:04:26 +00:00
for i , slot in enumerate ( _match . slots ) :
if slot . status != slotStatuses . READY and slot . user is not None :
2017-08-10 22:57:58 +00:00
someoneNotReady = True
if force :
_match . toggleSlotReady ( i )
if someoneNotReady and not force :
return " Some users aren ' t ready yet. Use ' !mp start force ' if you want to start the match, " \
" even with non-ready players. "
2017-08-03 23:04:26 +00:00
if startTime == 0 :
_start ( )
return " Starting match "
else :
_match . isStarting = True
threading . Timer ( 1.00 , _decreaseTimer , [ startTime - 1 ] ) . start ( )
2017-08-07 19:41:48 +00:00
return " Match starts in {} seconds. The match has been locked. " \
" Please don ' t leave the match during the countdown " \
" or you might receive a penalty. " . format ( startTime )
2017-08-03 23:04:26 +00:00
2017-08-06 07:45:39 +00:00
def mpInvite ( ) :
if len ( message ) < 2 :
raise exceptions . invalidArgumentsException ( " Wrong syntax: !mp invite <username> " )
username = message [ 1 ]
userID = userUtils . getIDSafe ( username )
if userID is None :
raise exceptions . userNotFoundException ( " No such user " )
token = glob . tokens . getTokenFromUserID ( userID , ignoreIRC = True )
if token is None :
raise exceptions . invalidUserException ( " That user is not connected to bancho right now. " )
_match = glob . matches . matches [ getMatchIDFromChannel ( chan ) ]
_match . invite ( 999 , userID )
token . enqueue ( serverPackets . notification ( " Please accept the invite you ' ve just received from FokaBot to "
" enter your tourney match. " ) )
return " An invite to this match has been sent to {} " . format ( username )
2017-08-06 08:09:05 +00:00
def mpMap ( ) :
if len ( message ) < 2 or not message [ 1 ] . isdigit ( ) or ( len ( message ) == 3 and not message [ 2 ] . isdigit ( ) ) :
raise exceptions . invalidArgumentsException ( " Wrong syntax: !mp map <beatmapid> [<gamemode>] " )
beatmapID = int ( message [ 1 ] )
gameMode = int ( message [ 2 ] ) if len ( message ) == 3 else 0
if gameMode < 0 or gameMode > 3 :
raise exceptions . invalidArgumentsException ( " Gamemode must be 0, 1, 2 or 3 " )
beatmapData = glob . db . fetch ( " SELECT * FROM beatmaps WHERE beatmap_id = %s LIMIT 1 " , [ beatmapID ] )
if beatmapData is None :
raise exceptions . invalidArgumentsException ( " The beatmap you ' ve selected couldn ' t be found in the database. "
" If the beatmap id is valid, please load the scoreboard first in "
" order to cache it, then try again. " )
_match = glob . matches . matches [ getMatchIDFromChannel ( chan ) ]
_match . beatmapID = beatmapID
_match . beatmapName = beatmapData [ " song_name " ]
_match . beatmapMD5 = beatmapData [ " beatmap_md5 " ]
_match . gameMode = gameMode
_match . sendUpdates ( )
return " Match map has been updated "
2017-08-07 18:51:16 +00:00
def mpSet ( ) :
if len ( message ) < 2 or not message [ 1 ] . isdigit ( ) or \
( len ( message ) > = 3 and not message [ 2 ] . isdigit ( ) ) or \
( len ( message ) > = 4 and not message [ 3 ] . isdigit ( ) ) :
raise exceptions . invalidArgumentsException ( " Wrong syntax: !mp set <teammode> [<scoremode>] [<size>] " )
_match = glob . matches . matches [ getMatchIDFromChannel ( chan ) ]
matchTeamType = int ( message [ 1 ] )
matchScoringType = int ( message [ 2 ] ) if len ( message ) > = 3 else _match . matchScoringType
if not 0 < = matchTeamType < = 3 :
raise exceptions . invalidArgumentsException ( " Match team type must be between 0 and 3 " )
if not 0 < = matchScoringType < = 3 :
raise exceptions . invalidArgumentsException ( " Match scoring type must be between 0 and 3 " )
_match . matchTeamType = matchTeamType
_match . matchScoringType = matchScoringType
if len ( message ) > = 4 :
_match . forceSize ( int ( message [ 3 ] ) )
_match . sendUpdates ( )
return " Match settings have been updated! "
2017-08-07 19:38:18 +00:00
def mpAbort ( ) :
_match = glob . matches . matches [ getMatchIDFromChannel ( chan ) ]
_match . abort ( )
return " Match aborted! "
2017-08-07 19:48:09 +00:00
def mpKick ( ) :
if len ( message ) < 2 :
raise exceptions . invalidArgumentsException ( " Wrong syntax: !mp kick <username> " )
username = message [ 1 ]
userID = userUtils . getIDSafe ( username )
if userID is None :
raise exceptions . userNotFoundException ( " No such user " )
_match = glob . matches . matches [ getMatchIDFromChannel ( chan ) ]
slotID = _match . getUserSlotID ( userID )
if slotID is None :
raise exceptions . userNotFoundException ( " The specified user is not in this match " )
for i in range ( 0 , 2 ) :
_match . toggleSlotLocked ( slotID )
return " {} has been kicked from the match. " . format ( username )
2017-08-07 20:14:44 +00:00
def mpPassword ( ) :
password = " " if len ( message ) < 2 else message [ 1 ]
_match = glob . matches . matches [ getMatchIDFromChannel ( chan ) ]
_match . changePassword ( password )
return " Match password has been changed! "
def mpRandomPassword ( ) :
password = generalUtils . stringMd5 ( generalUtils . randomString ( 32 ) )
_match = glob . matches . matches [ getMatchIDFromChannel ( chan ) ]
_match . changePassword ( password )
return " Match password has been changed to a random one "
2017-08-07 20:30:44 +00:00
def mpMods ( ) :
if len ( message ) < 2 :
raise exceptions . invalidArgumentsException ( " Wrong syntax: !mp <mod1> [<mod2>] ... " )
_match = glob . matches . matches [ getMatchIDFromChannel ( chan ) ]
newMods = 0
freeMod = False
for _mod in message [ 1 : ] :
if _mod . lower ( ) . strip ( ) == " hd " :
newMods | = mods . HIDDEN
elif _mod . lower ( ) . strip ( ) == " hr " :
newMods | = mods . HARDROCK
elif _mod . lower ( ) . strip ( ) == " dt " :
newMods | = mods . DOUBLETIME
elif _mod . lower ( ) . strip ( ) == " fl " :
newMods | = mods . FLASHLIGHT
elif _mod . lower ( ) . strip ( ) == " fi " :
newMods | = mods . FADEIN
if _mod . lower ( ) . strip ( ) == " none " :
newMods = 0
if _mod . lower ( ) . strip ( ) == " freemod " :
freeMod = True
_match . matchModMode = matchModModes . FREE_MOD if freeMod else matchModModes . NORMAL
_match . changeMods ( newMods )
2017-08-07 21:21:49 +00:00
return " Match mods have been updated! "
def mpTeam ( ) :
if len ( message ) < 3 :
raise exceptions . invalidArgumentsException ( " Wrong syntax: !mp team <username> <colour> " )
username = message [ 1 ]
colour = message [ 2 ] . lower ( ) . strip ( )
if colour not in [ " red " , " blue " ] :
raise exceptions . invalidArgumentsException ( " Team colour must be red or blue " )
userID = userUtils . getIDSafe ( username )
if userID is None :
raise exceptions . userNotFoundException ( " No such user " )
_match = glob . matches . matches [ getMatchIDFromChannel ( chan ) ]
_match . changeTeam ( userID , matchTeams . BLUE if colour == " blue " else matchTeams . RED )
return " {} is now in {} team " . format ( username , colour )
2017-08-10 23:14:21 +00:00
def mpSettings ( ) :
_match = glob . matches . matches [ getMatchIDFromChannel ( chan ) ]
msg = " PLAYERS IN THIS MATCH: \n "
empty = None
for slot in _match . slots :
if slot . user is None :
continue
readableStatuses = {
slotStatuses . READY : " ready " ,
slotStatuses . NOT_READY : " not ready " ,
slotStatuses . NO_MAP : " no map " ,
slotStatuses . PLAYING : " playing " ,
}
if slot . status not in readableStatuses :
readableStatus = " ??? "
else :
readableStatus = readableStatuses [ slot . status ]
empty = False
msg + = " * [ {team} ] < {status} > ~ {username} {mods} \n " . format (
team = " red " if slot . team == 0 else " blue " ,
status = readableStatus ,
username = glob . tokens . tokens [ slot . user ] . username ,
mods = " (+ {} ) " . format ( generalUtils . readableMods ( slot . mods ) )
)
if empty :
msg + = " \n Nobody. "
return msg
2017-08-07 20:30:44 +00:00
2017-08-01 22:02:38 +00:00
try :
subcommands = {
" make " : mpMake ,
2017-08-03 22:10:26 +00:00
" close " : mpClose ,
2017-08-01 22:39:16 +00:00
" join " : mpJoin ,
" lock " : mpLock ,
2017-08-03 21:47:34 +00:00
" unlock " : mpUnlock ,
" size " : mpSize ,
2017-08-03 22:09:50 +00:00
" move " : mpMove ,
2017-08-03 22:24:01 +00:00
" host " : mpHost ,
" clearhost " : mpClearHost ,
2017-08-03 23:04:26 +00:00
" start " : mpStart ,
2017-08-06 07:45:39 +00:00
" invite " : mpInvite ,
2017-08-06 08:09:05 +00:00
" map " : mpMap ,
2017-08-07 18:51:16 +00:00
" set " : mpSet ,
2017-08-07 19:38:18 +00:00
" abort " : mpAbort ,
2017-08-07 19:48:09 +00:00
" kick " : mpKick ,
2017-08-07 20:14:44 +00:00
" password " : mpPassword ,
" randompassword " : mpRandomPassword ,
2017-08-07 20:30:44 +00:00
" mods " : mpMods ,
2017-08-07 21:21:49 +00:00
" team " : mpTeam ,
2017-08-10 23:14:21 +00:00
" settings " : mpSettings ,
2017-08-01 22:02:38 +00:00
}
requestedSubcommand = message [ 0 ] . lower ( ) . strip ( )
if requestedSubcommand not in subcommands :
raise exceptions . invalidArgumentsException ( " Invalid subcommand " )
return subcommands [ requestedSubcommand ] ( )
2017-08-06 07:45:39 +00:00
except ( exceptions . invalidArgumentsException , exceptions . userNotFoundException ) as e :
2017-08-01 22:02:38 +00:00
return str ( e )
2017-08-01 22:39:16 +00:00
except exceptions . wrongChannelException :
return " This command only works in multiplayer chat channels "
except exceptions . matchNotFoundException :
return " Match not found "
2017-08-01 22:02:38 +00:00
except :
raise
2017-08-01 23:36:37 +00:00
def switchServer ( fro , chan , message ) :
# Get target user ID
target = message [ 0 ]
newServer = message [ 1 ]
targetUserID = userUtils . getIDSafe ( target )
userID = userUtils . getID ( fro )
# Make sure the user exists
if not targetUserID :
return " {} : user not found " . format ( target )
# Connect the user to the end server
userToken = glob . tokens . getTokenFromUserID ( userID , ignoreIRC = True , _all = False )
userToken . enqueue ( serverPackets . switchServer ( newServer ) )
# Disconnect the user from the origin server
# userToken.kick()
return " {} has been connected to {} " . format ( target , newServer )
2017-08-01 22:02:38 +00:00
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
2017-08-01 22:02:38 +00:00
} , {
" trigger " : " !mp " ,
" privileges " : privileges . ADMIN_MANAGE_SERVERS , # TODO: replace with admin tournament privilege
" syntax " : " <subcommand> " ,
" callback " : multiplayer
2017-08-01 23:36:37 +00:00
} , {
" trigger " : " !switchserver " ,
" privileges " : privileges . ADMIN_MANAGE_SERVERS ,
" syntax " : " <username> <server_address> " ,
" callback " : switchServer
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? " )