.BANCHO. Add IRC support, internal changes
This commit is contained in:
parent
34be190aa3
commit
95df629e1c
|
@ -77,3 +77,6 @@ class need2FAException(Exception):
|
||||||
|
|
||||||
class userRestrictedException(Exception):
|
class userRestrictedException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class haxException(Exception):
|
||||||
|
pass
|
||||||
|
|
|
@ -127,7 +127,7 @@ def kick(fro, chan, message):
|
||||||
def fokabotReconnect(fro, chan, message):
|
def fokabotReconnect(fro, chan, message):
|
||||||
# Check if fokabot is already connected
|
# Check if fokabot is already connected
|
||||||
if glob.tokens.getTokenFromUserID(999) != None:
|
if glob.tokens.getTokenFromUserID(999) != None:
|
||||||
return"Fokabot is already connected to Bancho"
|
return "Fokabot is already connected to Bancho"
|
||||||
|
|
||||||
# Fokabot is not connected, connect it
|
# Fokabot is not connected, connect it
|
||||||
fokabot.connect()
|
fokabot.connect()
|
||||||
|
|
|
@ -130,16 +130,9 @@ def userStats(userID, force = False):
|
||||||
# Get userID's token from tokens list
|
# Get userID's token from tokens list
|
||||||
userToken = glob.tokens.getTokenFromUserID(userID)
|
userToken = glob.tokens.getTokenFromUserID(userID)
|
||||||
if userToken == None:
|
if userToken == None:
|
||||||
return bytes() # NOTE: ???
|
|
||||||
if userToken.restricted == True and force == False:
|
|
||||||
return bytes()
|
return bytes()
|
||||||
# Stats are cached in token object
|
if (userToken.restricted == True or userToken.irc == True) and force == False:
|
||||||
#rankedScore = userHelper.getRankedScore(userID, userToken.gameMode)
|
return bytes()
|
||||||
#accuracy = userHelper.getAccuracy(userID, userToken.gameMode)/100
|
|
||||||
#playcount = userHelper.getPlaycount(userID, userToken.gameMode)
|
|
||||||
#totalScore = userHelper.getTotalScore(userID, userToken.gameMode)
|
|
||||||
#gameRank = userHelper.getGameRank(userID, userToken.gameMode)
|
|
||||||
#pp = int(userHelper.getPP(userID, userToken.gameMode))
|
|
||||||
return packetHelper.buildPacket(packetIDs.server_userStats,
|
return packetHelper.buildPacket(packetIDs.server_userStats,
|
||||||
[
|
[
|
||||||
[userID, dataTypes.uInt32],
|
[userID, dataTypes.uInt32],
|
||||||
|
|
|
@ -4,6 +4,7 @@ from constants import serverPackets
|
||||||
from helpers import userHelper
|
from helpers import userHelper
|
||||||
from helpers import logHelper as log
|
from helpers import logHelper as log
|
||||||
from constants import actions
|
from constants import actions
|
||||||
|
from helpers import chatHelper as chat
|
||||||
|
|
||||||
def handle(userToken, packetData):
|
def handle(userToken, packetData):
|
||||||
# Get usertoken data
|
# Get usertoken data
|
||||||
|
@ -58,7 +59,7 @@ def handle(userToken, packetData):
|
||||||
# NOTE: Remove this when osu!direct will be fixed
|
# NOTE: Remove this when osu!direct will be fixed
|
||||||
if userToken.actionID == actions.osuDirect and userToken.osuDirectAlert == False:
|
if userToken.actionID == actions.osuDirect and userToken.osuDirectAlert == False:
|
||||||
userToken.osuDirectAlert = True
|
userToken.osuDirectAlert = True
|
||||||
userToken.enqueue(serverPackets.sendMessage("FokaBot", userToken.username, "Sup! osu!direct works, but you'll need to update the switcher to have the Download button working. If you didn't update the switcher yet, please do!"))
|
chat.sendMessage("FokaBot", userToken.username, "Sup! osu!direct works, but you'll need to update the switcher to have the Download button working. If you didn't update the switcher yet, please do!")
|
||||||
|
|
||||||
|
|
||||||
# Console output
|
# Console output
|
||||||
|
|
|
@ -1,56 +1,7 @@
|
||||||
"""
|
|
||||||
Event called when someone joins a channel
|
|
||||||
"""
|
|
||||||
|
|
||||||
from constants import clientPackets
|
from constants import clientPackets
|
||||||
from helpers import consoleHelper
|
from helpers import chatHelper as chat
|
||||||
from constants import bcolors
|
|
||||||
from constants import serverPackets
|
|
||||||
from objects import glob
|
|
||||||
from constants import exceptions
|
|
||||||
from helpers import logHelper as log
|
|
||||||
|
|
||||||
def handle(userToken, packetData):
|
def handle(userToken, packetData):
|
||||||
# Channel join packet
|
# Channel join packet
|
||||||
packetData = clientPackets.channelJoin(packetData)
|
packetData = clientPackets.channelJoin(packetData)
|
||||||
joinChannel(userToken, packetData["channel"])
|
chat.joinChannel(token=userToken, channel=packetData["channel"])
|
||||||
|
|
||||||
def joinChannel(userToken, channelName):
|
|
||||||
'''
|
|
||||||
Join a channel
|
|
||||||
|
|
||||||
userToken -- user token object of user that joins the chanlle
|
|
||||||
channelName -- name of channel
|
|
||||||
'''
|
|
||||||
try:
|
|
||||||
# Get usertoken data
|
|
||||||
username = userToken.username
|
|
||||||
userID = userToken.userID
|
|
||||||
|
|
||||||
# Check spectator channel
|
|
||||||
# If it's spectator channel, skip checks and list stuff
|
|
||||||
if channelName != "#spectator" and channelName != "#multiplayer":
|
|
||||||
# Normal channel, do check stuff
|
|
||||||
# Make sure the channel exists
|
|
||||||
if channelName not in glob.channels.channels:
|
|
||||||
raise exceptions.channelUnknownException
|
|
||||||
|
|
||||||
# Check channel permissions
|
|
||||||
if glob.channels.channels[channelName].publicRead == False and userToken.admin == False:
|
|
||||||
raise exceptions.channelNoPermissionsException
|
|
||||||
|
|
||||||
# Add our userID to users in that channel
|
|
||||||
glob.channels.channels[channelName].userJoin(userID)
|
|
||||||
|
|
||||||
# Add the channel to our joined channel
|
|
||||||
userToken.joinChannel(channelName)
|
|
||||||
|
|
||||||
# Send channel joined
|
|
||||||
userToken.enqueue(serverPackets.channelJoinSuccess(userID, channelName))
|
|
||||||
|
|
||||||
# Console output
|
|
||||||
log.info("{} joined channel {}".format(username, channelName))
|
|
||||||
except exceptions.channelNoPermissionsException:
|
|
||||||
log.warning("{} attempted to join channel {}, but they have no read permissions".format(username, channelName))
|
|
||||||
except exceptions.channelUnknownException:
|
|
||||||
log.warning("{} attempted to join an unknown channel ({})".format(username, channelName))
|
|
||||||
|
|
|
@ -8,13 +8,14 @@ from objects import glob
|
||||||
from constants import clientPackets
|
from constants import clientPackets
|
||||||
from constants import serverPackets
|
from constants import serverPackets
|
||||||
from helpers import logHelper as log
|
from helpers import logHelper as log
|
||||||
|
from helpers import chatHelper as chat
|
||||||
|
|
||||||
def handle(userToken, packetData):
|
def handle(userToken, packetData):
|
||||||
# Channel part packet
|
# Channel join packet
|
||||||
packetData = clientPackets.channelPart(packetData)
|
packetData = clientPackets.channelPart(packetData)
|
||||||
partChannel(userToken, packetData["channel"])
|
chat.partChannel(token=userToken, channel=packetData["channel"])
|
||||||
|
|
||||||
def partChannel(userToken, channelName, kick = False):
|
"""def partChannel(userToken, channelName, kick = False):
|
||||||
# Get usertoken data
|
# Get usertoken data
|
||||||
username = userToken.username
|
username = userToken.username
|
||||||
userID = userToken.userID
|
userID = userToken.userID
|
||||||
|
@ -34,4 +35,4 @@ def partChannel(userToken, channelName, kick = False):
|
||||||
userToken.enqueue(serverPackets.channelKicked(channelName))
|
userToken.enqueue(serverPackets.channelKicked(channelName))
|
||||||
|
|
||||||
# Console output
|
# Console output
|
||||||
log.info("{} parted channel {}".format(username, channelName))
|
log.info("{} parted channel {}".format(username, channelName))"""
|
||||||
|
|
|
@ -3,6 +3,7 @@ from constants import serverPackets
|
||||||
from objects import glob
|
from objects import glob
|
||||||
from constants import exceptions
|
from constants import exceptions
|
||||||
from helpers import logHelper as log
|
from helpers import logHelper as log
|
||||||
|
from helpers import chatHelper as chat
|
||||||
|
|
||||||
def handle(userToken, packetData):
|
def handle(userToken, packetData):
|
||||||
# read packet data
|
# read packet data
|
||||||
|
@ -44,8 +45,7 @@ def joinMatch(userToken, matchID, password):
|
||||||
|
|
||||||
# Send packets
|
# Send packets
|
||||||
userToken.enqueue(serverPackets.matchJoinSuccess(matchID))
|
userToken.enqueue(serverPackets.matchJoinSuccess(matchID))
|
||||||
userToken.enqueue(serverPackets.channelJoinSuccess(userID, "#multiplayer"))
|
chat.joinChannel(token=userToken, channel="#multi_{}".format(matchID))
|
||||||
#userToken.enqueue(serverPackets.sendMessage("FokaBot", "#multiplayer", "Hi {}, and welcome to Ripple's multiplayer mode! This feature is still WIP and might have some issues. If you find any bugs, please report them (by clicking here)[https://ripple.moe/index.php?p=22].".format(username)))
|
|
||||||
except exceptions.matchNotFoundException:
|
except exceptions.matchNotFoundException:
|
||||||
userToken.enqueue(serverPackets.matchJoinFail())
|
userToken.enqueue(serverPackets.matchJoinFail())
|
||||||
log.warning("{} has tried to join a mp room, but it doesn't exist".format(userToken.username))
|
log.warning("{} has tried to join a mp room, but it doesn't exist".format(userToken.username))
|
||||||
|
|
|
@ -8,12 +8,12 @@ from helpers import locationHelper
|
||||||
from helpers import countryHelper
|
from helpers import countryHelper
|
||||||
import time
|
import time
|
||||||
from helpers import generalFunctions
|
from helpers import generalFunctions
|
||||||
from events import channelJoinEvent
|
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
from helpers import requestHelper
|
from helpers import requestHelper
|
||||||
from helpers import discordBotHelper
|
from helpers import discordBotHelper
|
||||||
from helpers import logHelper as log
|
from helpers import logHelper as log
|
||||||
|
from helpers import chatHelper as chat
|
||||||
|
|
||||||
def handle(tornadoRequest):
|
def handle(tornadoRequest):
|
||||||
# Data to return
|
# Data to return
|
||||||
|
@ -30,6 +30,10 @@ def handle(tornadoRequest):
|
||||||
# If true, print error to console
|
# If true, print error to console
|
||||||
err = False
|
err = False
|
||||||
|
|
||||||
|
# Make sure loginData is valid
|
||||||
|
if len(loginData) < 3:
|
||||||
|
raise exceptions.haxException()
|
||||||
|
|
||||||
# Try to get the ID from username
|
# Try to get the ID from username
|
||||||
username = str(loginData[0])
|
username = str(loginData[0])
|
||||||
userID = userHelper.getID(username)
|
userID = userHelper.getID(username)
|
||||||
|
@ -59,6 +63,9 @@ def handle(tornadoRequest):
|
||||||
responseToken = glob.tokens.addToken(userID, requestIP)
|
responseToken = glob.tokens.addToken(userID, requestIP)
|
||||||
responseTokenString = responseToken.token
|
responseTokenString = responseToken.token
|
||||||
|
|
||||||
|
# Check restricted mode (and eventually send message)
|
||||||
|
responseToken.checkRestricted()
|
||||||
|
|
||||||
# Set silence end UNIX time in token
|
# Set silence end UNIX time in token
|
||||||
responseToken.silenceEndTime = userHelper.getSilenceEnd(userID)
|
responseToken.silenceEndTime = userHelper.getSilenceEnd(userID)
|
||||||
|
|
||||||
|
@ -101,12 +108,12 @@ def handle(tornadoRequest):
|
||||||
responseToken.enqueue(serverPackets.channelInfoEnd())
|
responseToken.enqueue(serverPackets.channelInfoEnd())
|
||||||
# Default opened channels
|
# Default opened channels
|
||||||
# TODO: Configurable default channels
|
# TODO: Configurable default channels
|
||||||
channelJoinEvent.joinChannel(responseToken, "#osu")
|
chat.joinChannel(token=responseToken, channel="#osu")
|
||||||
channelJoinEvent.joinChannel(responseToken, "#announce")
|
chat.joinChannel(token=responseToken, channel="#announce")
|
||||||
|
|
||||||
# Join admin channel if we are an admin
|
# Join admin channel if we are an admin
|
||||||
if responseToken.admin == True:
|
if responseToken.admin == True:
|
||||||
channelJoinEvent.joinChannel(responseToken, "#admin")
|
chat.joinChannel(token=responseToken, channel="#admin")
|
||||||
|
|
||||||
# Output channels info
|
# Output channels info
|
||||||
for key, value in glob.channels.channels.items():
|
for key, value in glob.channels.channels.items():
|
||||||
|
@ -156,6 +163,12 @@ def handle(tornadoRequest):
|
||||||
# (we don't use enqueue because we don't have a token since login has failed)
|
# (we don't use enqueue because we don't have a token since login has failed)
|
||||||
err = True
|
err = True
|
||||||
responseData += serverPackets.loginFailed()
|
responseData += serverPackets.loginFailed()
|
||||||
|
except exceptions.haxException:
|
||||||
|
# Invalid POST data
|
||||||
|
# (we don't use enqueue because we don't have a token since login has failed)
|
||||||
|
err = True
|
||||||
|
responseData += serverPackets.loginFailed()
|
||||||
|
responseData += serverPackets.notification("I see what you're doing...")
|
||||||
except exceptions.loginBannedException:
|
except exceptions.loginBannedException:
|
||||||
# Login banned error packet
|
# Login banned error packet
|
||||||
err = True
|
err = True
|
||||||
|
@ -172,9 +185,14 @@ def handle(tornadoRequest):
|
||||||
except exceptions.need2FAException:
|
except exceptions.need2FAException:
|
||||||
# User tried to log in from unknown IP
|
# User tried to log in from unknown IP
|
||||||
responseData += serverPackets.needVerification()
|
responseData += serverPackets.needVerification()
|
||||||
|
except:
|
||||||
|
log.error("Unknown error!\n```\n{}\n{}```".format(sys.exc_info(), traceback.format_exc()))
|
||||||
finally:
|
finally:
|
||||||
# Console and discord log
|
# Console and discord log
|
||||||
msg = "Bancho login request from {} for user {} ({})".format(requestIP, loginData[0], "failed" if err == True else "success")
|
if len(loginData) < 3:
|
||||||
|
msg = "Invalid bancho login request from **{}** (insufficient POST data)".format(requestIP)
|
||||||
|
else:
|
||||||
|
msg = "Bancho login request from **{}** for user **{}** ({}) **({})**".format(requestIP, loginData[0], loginData[2], "failed" if err == True else "success")
|
||||||
log.info(msg, True)
|
log.info(msg, True)
|
||||||
|
|
||||||
# Return token string and data
|
# Return token string and data
|
||||||
|
|
|
@ -4,8 +4,9 @@ from constants import bcolors
|
||||||
from constants import serverPackets
|
from constants import serverPackets
|
||||||
import time
|
import time
|
||||||
from helpers import logHelper as log
|
from helpers import logHelper as log
|
||||||
|
from helpers import chatHelper as chat
|
||||||
|
|
||||||
def handle(userToken, _):
|
def handle(userToken, _=None):
|
||||||
# get usertoken data
|
# get usertoken data
|
||||||
userID = userToken.userID
|
userID = userToken.userID
|
||||||
username = userToken.username
|
username = userToken.username
|
||||||
|
@ -15,8 +16,9 @@ def handle(userToken, _):
|
||||||
# the old logout packet will still be in the queue and will be sent to
|
# the old logout packet will still be in the queue and will be sent to
|
||||||
# the server, so we accept logout packets sent at least 5 seconds after login
|
# the server, so we accept logout packets sent at least 5 seconds after login
|
||||||
# if the user logs out before 5 seconds, he will be disconnected later with timeout check
|
# if the user logs out before 5 seconds, he will be disconnected later with timeout check
|
||||||
if int(time.time()-userToken.loginTime) >= 5:
|
if int(time.time()-userToken.loginTime) >= 5 or userToken.irc == True:
|
||||||
# Stop spectating if needed
|
# Stop spectating if needed
|
||||||
|
# TODO: Call stopSpectatingEvent!!!!!!!!!
|
||||||
if userToken.spectating != 0:
|
if userToken.spectating != 0:
|
||||||
# The user was spectating someone
|
# The user was spectating someone
|
||||||
spectatorHostToken = glob.tokens.getTokenFromUserID(userToken.spectating)
|
spectatorHostToken = glob.tokens.getTokenFromUserID(userToken.spectating)
|
||||||
|
@ -26,13 +28,17 @@ def handle(userToken, _):
|
||||||
|
|
||||||
# Part all joined channels
|
# Part all joined channels
|
||||||
for i in userToken.joinedChannels:
|
for i in userToken.joinedChannels:
|
||||||
glob.channels.channels[i].userPart(userID)
|
chat.partChannel(token=userToken, channel=i)
|
||||||
|
|
||||||
# TODO: Lobby left if joined
|
# TODO: Lobby left if joined
|
||||||
|
|
||||||
# Enqueue our disconnection to everyone else
|
# Enqueue our disconnection to everyone else
|
||||||
glob.tokens.enqueueAll(serverPackets.userLogout(userID))
|
glob.tokens.enqueueAll(serverPackets.userLogout(userID))
|
||||||
|
|
||||||
|
# Disconnect from IRC if needed
|
||||||
|
if userToken.irc == True and glob.irc == True:
|
||||||
|
glob.ircServer.forceDisconnection(userToken.username)
|
||||||
|
|
||||||
# Delete token
|
# Delete token
|
||||||
glob.tokens.deleteToken(requestToken)
|
glob.tokens.deleteToken(requestToken)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from objects import glob
|
from objects import glob
|
||||||
from events import channelPartEvent
|
from events import channelPartEvent
|
||||||
from helpers import logHelper as log
|
from helpers import logHelper as log
|
||||||
|
from helpers import chatHelper as chat
|
||||||
|
|
||||||
def handle(userToken, _):
|
def handle(userToken, _):
|
||||||
# Get usertoken data
|
# Get usertoken data
|
||||||
|
@ -11,7 +12,7 @@ def handle(userToken, _):
|
||||||
glob.matches.lobbyUserPart(userID)
|
glob.matches.lobbyUserPart(userID)
|
||||||
|
|
||||||
# Part lobby channel
|
# Part lobby channel
|
||||||
channelPartEvent.partChannel(userToken, "#lobby", True)
|
chat.partChannel(channel="#lobby", token=userToken, kick=True)
|
||||||
|
|
||||||
# Console output
|
# Console output
|
||||||
log.info("{} has left multiplayer lobby".format(username))
|
log.info("{} has left multiplayer lobby".format(username))
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from objects import glob
|
from objects import glob
|
||||||
|
from helpers import chatHelper as chat
|
||||||
from constants import serverPackets
|
from constants import serverPackets
|
||||||
|
|
||||||
def handle(userToken, _):
|
def handle(userToken, _):
|
||||||
|
@ -22,6 +23,8 @@ def handle(userToken, _):
|
||||||
# Set slot to free
|
# Set slot to free
|
||||||
match.userLeft(userID)
|
match.userLeft(userID)
|
||||||
|
|
||||||
|
# Part #multiplayer channel
|
||||||
|
chat.partChannel(token=userToken, channel="#multi_{}".format(matchID))
|
||||||
|
|
||||||
# Set usertoken match to -1
|
# Set usertoken match to -1
|
||||||
userToken.partMatch()
|
userToken.partMatch()
|
||||||
userToken.enqueue(serverPackets.channelKicked("#multiplayer"))
|
|
||||||
|
|
|
@ -1,79 +1,7 @@
|
||||||
from helpers import consoleHelper
|
|
||||||
from constants import bcolors
|
|
||||||
from constants import clientPackets
|
from constants import clientPackets
|
||||||
from constants import serverPackets
|
from helpers import chatHelper as chat
|
||||||
from objects import glob
|
|
||||||
from objects import fokabot
|
|
||||||
from constants import exceptions
|
|
||||||
from constants import messageTemplates
|
|
||||||
from helpers import generalFunctions
|
|
||||||
from helpers import userHelper
|
|
||||||
from helpers import logHelper as log
|
|
||||||
import time
|
|
||||||
|
|
||||||
def handle(userToken, packetData):
|
def handle(userToken, packetData):
|
||||||
"""
|
# Send private message packet
|
||||||
Event called when someone sends a private message
|
packetData = clientPackets.sendPrivateMessage(packetData)
|
||||||
|
chat.sendMessage(token=userToken, to=packetData["to"], message=packetData["message"])
|
||||||
userToken -- request user token
|
|
||||||
packetData -- request data bytes
|
|
||||||
"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Get usertoken username
|
|
||||||
username = userToken.username
|
|
||||||
userID = userToken.userID
|
|
||||||
|
|
||||||
# Make sure the user is not in restricted mode
|
|
||||||
if userToken.restricted == True:
|
|
||||||
raise exceptions.userRestrictedException
|
|
||||||
|
|
||||||
# Private message packet
|
|
||||||
packetData = clientPackets.sendPrivateMessage(packetData)
|
|
||||||
|
|
||||||
# Make sure the user is not silenced
|
|
||||||
if userToken.isSilenced() == True:
|
|
||||||
raise exceptions.userSilencedException
|
|
||||||
|
|
||||||
# Check message length
|
|
||||||
packetData["message"] = packetData["message"][:2048]+"..." if len(packetData["message"]) > 2048 else packetData["message"]
|
|
||||||
|
|
||||||
if packetData["to"] == "FokaBot":
|
|
||||||
# FokaBot command check
|
|
||||||
fokaMessage = fokabot.fokabotResponse(username, packetData["to"], packetData["message"])
|
|
||||||
if fokaMessage != False:
|
|
||||||
userToken.enqueue(serverPackets.sendMessage("FokaBot", username, fokaMessage))
|
|
||||||
log.pm("FokaBot -> {}: {}".format(packetData["to"], str(fokaMessage.encode("UTF-8"))))
|
|
||||||
else:
|
|
||||||
# Send packet message to target if it exists
|
|
||||||
token = glob.tokens.getTokenFromUsername(packetData["to"])
|
|
||||||
if token == None:
|
|
||||||
raise exceptions.tokenNotFoundException()
|
|
||||||
|
|
||||||
# Check message templates (mods/admins only)
|
|
||||||
if packetData["message"] in messageTemplates.templates and userToken.admin == True:
|
|
||||||
packetData["message"] = messageTemplates.templates[packetData["message"]]
|
|
||||||
|
|
||||||
# Send message to target
|
|
||||||
token.enqueue(serverPackets.sendMessage(username, packetData["to"], packetData["message"]))
|
|
||||||
|
|
||||||
# Send away message to sender if needed
|
|
||||||
if token.awayMessage != "":
|
|
||||||
userToken.enqueue(serverPackets.sendMessage(packetData["to"], username, "This user is away: {}".format(token.awayMessage)))
|
|
||||||
|
|
||||||
# Spam protection
|
|
||||||
userToken.spamProtection()
|
|
||||||
|
|
||||||
# Console and file output
|
|
||||||
log.pm("{} -> {}: {}".format(username, packetData["to"], packetData["message"]))
|
|
||||||
except exceptions.userSilencedException:
|
|
||||||
userToken.enqueue(serverPackets.silenceEndTime(userToken.getSilenceSecondsLeft()))
|
|
||||||
log.warning("{} tried to send a message during silence".format(username))
|
|
||||||
except exceptions.tokenNotFoundException:
|
|
||||||
# Token not found, user disconnected
|
|
||||||
log.warning("{} tried to send a message to {}, but their token couldn't be found".format(username, packetData["to"]))
|
|
||||||
except exceptions.messageTooLongException:
|
|
||||||
# Message > 256 silence
|
|
||||||
userToken.silence(2*3600, "Sending messages longer than 256 characters")
|
|
||||||
except exceptions.userRestrictedException:
|
|
||||||
pass
|
|
||||||
|
|
|
@ -1,135 +1,7 @@
|
||||||
from constants import exceptions
|
|
||||||
from constants import clientPackets
|
from constants import clientPackets
|
||||||
from objects import glob
|
from helpers import chatHelper as chat
|
||||||
from objects import fokabot
|
|
||||||
from constants import serverPackets
|
|
||||||
from helpers import discordBotHelper
|
|
||||||
from helpers import logHelper as log
|
|
||||||
from helpers import userHelper
|
|
||||||
import time
|
|
||||||
|
|
||||||
def handle(userToken, packetData):
|
def handle(userToken, packetData):
|
||||||
"""
|
# Send public message packet
|
||||||
Event called when someone sends a public message
|
packetData = clientPackets.sendPublicMessage(packetData)
|
||||||
|
chat.sendMessage(token=userToken, to=packetData["to"], message=packetData["message"])
|
||||||
userToken -- request user token
|
|
||||||
packetData -- request data bytes
|
|
||||||
"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Get userToken data
|
|
||||||
userID = userToken.userID
|
|
||||||
username = userToken.username
|
|
||||||
|
|
||||||
# Make sure the user is not in restricted mode
|
|
||||||
if userToken.restricted == True:
|
|
||||||
raise exceptions.userRestrictedException
|
|
||||||
|
|
||||||
# Public chat packet
|
|
||||||
packetData = clientPackets.sendPublicMessage(packetData)
|
|
||||||
|
|
||||||
# Receivers
|
|
||||||
who = []
|
|
||||||
|
|
||||||
# Make sure the user is not silenced
|
|
||||||
if userToken.isSilenced() == True:
|
|
||||||
raise exceptions.userSilencedException
|
|
||||||
|
|
||||||
# Check message length
|
|
||||||
packetData["message"] = packetData["message"][:2048]+"..." if len(packetData["message"]) > 2048 else packetData["message"]
|
|
||||||
|
|
||||||
# Get receivers list
|
|
||||||
# Check #spectator
|
|
||||||
if packetData["to"] == "#spectator":
|
|
||||||
# Spectator channel
|
|
||||||
# Send this packet to every spectator and host
|
|
||||||
if userToken.spectating == 0:
|
|
||||||
# We have sent to send a message to our #spectator channel
|
|
||||||
targetToken = userToken
|
|
||||||
who = targetToken.spectators[:]
|
|
||||||
# No need to remove us because we are the host so we are not in spectators list
|
|
||||||
else:
|
|
||||||
# We have sent a message to someone else's #spectator
|
|
||||||
targetToken = glob.tokens.getTokenFromUserID(userToken.spectating)
|
|
||||||
who = targetToken.spectators[:]
|
|
||||||
|
|
||||||
# Remove us
|
|
||||||
if userID in who:
|
|
||||||
who.remove(userID)
|
|
||||||
|
|
||||||
# Add host
|
|
||||||
who.append(targetToken.userID)
|
|
||||||
elif packetData["to"] == "#multiplayer":
|
|
||||||
# Multiplayer Channel
|
|
||||||
# Get match ID and match object
|
|
||||||
matchID = userToken.matchID
|
|
||||||
|
|
||||||
# Make sure we are in a match
|
|
||||||
if matchID == -1:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Make sure the match exists
|
|
||||||
if matchID not in glob.matches.matches:
|
|
||||||
return
|
|
||||||
|
|
||||||
# The match exists, get object
|
|
||||||
match = glob.matches.matches[matchID]
|
|
||||||
|
|
||||||
# Create targets list
|
|
||||||
who = []
|
|
||||||
for i in range(0,16):
|
|
||||||
uid = match.slots[i]["userID"]
|
|
||||||
if uid > -1 and uid != userID:
|
|
||||||
who.append(uid)
|
|
||||||
else:
|
|
||||||
# Standard channel
|
|
||||||
# Make sure the channel exists
|
|
||||||
if packetData["to"] not in glob.channels.channels:
|
|
||||||
raise exceptions.channelUnknownException
|
|
||||||
|
|
||||||
# Make sure the channel is not in moderated mode
|
|
||||||
if glob.channels.channels[packetData["to"]].moderated == True and userToken.admin == False:
|
|
||||||
raise exceptions.channelModeratedException
|
|
||||||
|
|
||||||
# Make sure we have write permissions
|
|
||||||
if glob.channels.channels[packetData["to"]].publicWrite == False and userToken.admin == False:
|
|
||||||
raise exceptions.channelNoPermissionsException
|
|
||||||
|
|
||||||
# Send this packet to everyone in that channel except us
|
|
||||||
who = glob.channels.channels[packetData["to"]].getConnectedUsers()[:]
|
|
||||||
if userID in who:
|
|
||||||
who.remove(userID)
|
|
||||||
|
|
||||||
# We have receivers
|
|
||||||
# Send packet to required users
|
|
||||||
glob.tokens.multipleEnqueue(serverPackets.sendMessage(username, packetData["to"], packetData["message"]), who, False)
|
|
||||||
|
|
||||||
# Fokabot command check
|
|
||||||
fokaMessage = fokabot.fokabotResponse(username, packetData["to"], packetData["message"])
|
|
||||||
if fokaMessage != False:
|
|
||||||
who.append(userID)
|
|
||||||
glob.tokens.multipleEnqueue(serverPackets.sendMessage("FokaBot", packetData["to"], fokaMessage), who, False)
|
|
||||||
log.chat("FokaBot @ {}: {}".format(packetData["to"], str(fokaMessage.encode("UTF-8"))))
|
|
||||||
|
|
||||||
# Spam protection
|
|
||||||
userToken.spamProtection()
|
|
||||||
|
|
||||||
# Console and file log
|
|
||||||
log.chat("{fro} @ {to}: {message}".format(fro=username, to=packetData["to"], message=str(packetData["message"].encode("utf-8"))))
|
|
||||||
|
|
||||||
# Discord log
|
|
||||||
discordBotHelper.sendChatlog("**{fro} @ {to}:** {message}".format(fro=username, to=packetData["to"], message=str(packetData["message"].encode("utf-8"))[2:-1]))
|
|
||||||
except exceptions.userSilencedException:
|
|
||||||
userToken.enqueue(serverPackets.silenceEndTime(userToken.getSilenceSecondsLeft()))
|
|
||||||
log.warning("{} tried to send a message during silence".format(username))
|
|
||||||
except exceptions.channelModeratedException:
|
|
||||||
log.warning("{} tried to send a message to a channel that is in moderated mode ({})".format(username, packetData["to"]))
|
|
||||||
except exceptions.channelUnknownException:
|
|
||||||
log.warning("{} tried to send a message to an unknown channel ({})".format(username, packetData["to"]))
|
|
||||||
except exceptions.channelNoPermissionsException:
|
|
||||||
log.warning("{} tried to send a message to channel {}, but they have no write permissions".format(username, packetData["to"]))
|
|
||||||
except exceptions.messageTooLongException:
|
|
||||||
# Message > 256 silence
|
|
||||||
userToken.silence(2*3600, "Sending messages longer than 256 characters")
|
|
||||||
except exceptions.userRestrictedException:
|
|
||||||
pass
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ from constants import exceptions
|
||||||
from objects import glob
|
from objects import glob
|
||||||
from helpers import userHelper
|
from helpers import userHelper
|
||||||
from helpers import logHelper as log
|
from helpers import logHelper as log
|
||||||
|
from helpers import chatHelper as chat
|
||||||
|
|
||||||
def handle(userToken, packetData):
|
def handle(userToken, packetData):
|
||||||
try:
|
try:
|
||||||
|
@ -34,12 +35,12 @@ def handle(userToken, packetData):
|
||||||
# Send spectator join packet to host
|
# Send spectator join packet to host
|
||||||
targetToken.enqueue(serverPackets.addSpectator(userID))
|
targetToken.enqueue(serverPackets.addSpectator(userID))
|
||||||
|
|
||||||
# Join #spectator channel
|
# Create and join #spectator (#spect_userid) channel
|
||||||
userToken.enqueue(serverPackets.channelJoinSuccess(userID, "#spectator"))
|
glob.channels.addTempChannel("#spect_{}".format(targetToken.userID))
|
||||||
|
chat.joinChannel(token=userToken, channel="#spect_{}".format(targetToken.userID))
|
||||||
if len(targetToken.spectators) == 1:
|
if len(targetToken.spectators) == 1:
|
||||||
# First spectator, send #spectator join to host too
|
# First spectator, send #spectator join to host too
|
||||||
targetToken.enqueue(serverPackets.channelJoinSuccess(userID, "#spectator"))
|
chat.joinChannel(token=targetToken, channel="#spect_{}".format(targetToken.userID))
|
||||||
|
|
||||||
# send fellowSpectatorJoined to all spectators
|
# send fellowSpectatorJoined to all spectators
|
||||||
for spec in targetToken.spectators:
|
for spec in targetToken.spectators:
|
||||||
|
|
|
@ -2,6 +2,7 @@ from objects import glob
|
||||||
from constants import serverPackets
|
from constants import serverPackets
|
||||||
from constants import exceptions
|
from constants import exceptions
|
||||||
from helpers import logHelper as log
|
from helpers import logHelper as log
|
||||||
|
from helpers import chatHelper as chat
|
||||||
|
|
||||||
def handle(userToken, _):
|
def handle(userToken, _):
|
||||||
try:
|
try:
|
||||||
|
@ -16,6 +17,9 @@ def handle(userToken, _):
|
||||||
raise exceptions.tokenNotFoundException
|
raise exceptions.tokenNotFoundException
|
||||||
targetToken.removeSpectator(userID)
|
targetToken.removeSpectator(userID)
|
||||||
|
|
||||||
|
# Part #spectator channel
|
||||||
|
chat.partChannel(token=userToken, channel="#spect_{}".format(target))
|
||||||
|
|
||||||
# Send the spectator left packet to host
|
# Send the spectator left packet to host
|
||||||
targetToken.enqueue(serverPackets.removeSpectator(userID))
|
targetToken.enqueue(serverPackets.removeSpectator(userID))
|
||||||
for c in targetToken.spectators:
|
for c in targetToken.spectators:
|
||||||
|
@ -25,8 +29,7 @@ def handle(userToken, _):
|
||||||
#targetToken.enqueue(serverPackets.fellowSpectatorLeft(userID))
|
#targetToken.enqueue(serverPackets.fellowSpectatorLeft(userID))
|
||||||
|
|
||||||
# Console output
|
# Console output
|
||||||
# TODO: Move messages in stop spectating
|
log.info("{} are no longer spectating {}".format(username, target))
|
||||||
log.info("{} are no longer spectating whoever they were spectating".format(username))
|
|
||||||
except exceptions.tokenNotFoundException:
|
except exceptions.tokenNotFoundException:
|
||||||
log.warning("Spectator stop: token not found")
|
log.warning("Spectator stop: token not found")
|
||||||
finally:
|
finally:
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import datetime
|
import datetime
|
||||||
import gzip
|
import gzip
|
||||||
|
import time
|
||||||
|
from helpers import generalFunctions
|
||||||
from helpers import requestHelper
|
from helpers import requestHelper
|
||||||
from objects import glob
|
from objects import glob
|
||||||
from helpers import consoleHelper
|
from helpers import consoleHelper
|
||||||
|
@ -257,7 +259,7 @@ class handler(SentryMixin, requestHelper.asyncRequestHandler):
|
||||||
html += " \\\"\"\"\"\"\"\"\"\"\"\"\"\"\"/<br>"
|
html += " \\\"\"\"\"\"\"\"\"\"\"\"\"\"\"/<br>"
|
||||||
html += " \\ . .. .. . /<br>"
|
html += " \\ . .. .. . /<br>"
|
||||||
html += "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^<br>"
|
html += "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^<br>"
|
||||||
html += "</marquee><br><strike>reverse engineering a protocol impossible to reverse engineer since always</strike><br>we are actually reverse engineering bancho successfully. for the third time.</pre></body></html>"
|
html += "</marquee><br><strike>reverse engineering a protocol impossible to reverse engineer since always</strike><br>we are actually reverse engineering bancho successfully. for the third time.<br><br><i>© Ripple team, 2016</i></pre></body></html>"
|
||||||
self.write(html)
|
self.write(html)
|
||||||
#yield tornado.gen.Task(self.captureMessage, "test")
|
#yield tornado.gen.Task(self.captureMessage, "test")
|
||||||
#self.finish()
|
#self.finish()
|
||||||
|
|
335
helpers/chatHelper.py
Normal file
335
helpers/chatHelper.py
Normal file
|
@ -0,0 +1,335 @@
|
||||||
|
from objects import glob
|
||||||
|
from helpers import logHelper as log
|
||||||
|
from constants import exceptions
|
||||||
|
from constants import serverPackets
|
||||||
|
from objects import fokabot
|
||||||
|
from helpers import discordBotHelper
|
||||||
|
from helpers import userHelper
|
||||||
|
from events import logoutEvent
|
||||||
|
from events import channelJoinEvent
|
||||||
|
from constants import messageTemplates
|
||||||
|
|
||||||
|
def joinChannel(userID = 0, channel = "", token = None, toIRC = True):
|
||||||
|
"""
|
||||||
|
Join a channel
|
||||||
|
|
||||||
|
userID -- user ID of the user that joins the channel. Optional.
|
||||||
|
token can be used instead.
|
||||||
|
token -- user token object of user that joins the channel. Optional.
|
||||||
|
userID can be used instead.
|
||||||
|
channel -- name of channe
|
||||||
|
toIRC -- if True, send this channel join event to IRC. Must be true if joining from bancho.
|
||||||
|
Optional. Defaukt: True
|
||||||
|
return -- returns 0 if joined or other IRC code in case of error. Needed only on IRC-side
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Get token if not defined
|
||||||
|
if token == None:
|
||||||
|
token = glob.tokens.getTokenFromUserID(userID)
|
||||||
|
# Make sure the token exists
|
||||||
|
if token == None:
|
||||||
|
raise exceptions.userNotFoundException
|
||||||
|
else:
|
||||||
|
token = token
|
||||||
|
userID = token.userID
|
||||||
|
|
||||||
|
# Get usertoken data
|
||||||
|
username = token.username
|
||||||
|
|
||||||
|
# Normal channel, do check stuff
|
||||||
|
# Make sure the channel exists
|
||||||
|
if channel not in glob.channels.channels:
|
||||||
|
raise exceptions.channelUnknownException
|
||||||
|
|
||||||
|
# Check channel permissions
|
||||||
|
channelObject = glob.channels.channels[channel]
|
||||||
|
if channelObject.publicRead == False and token.admin == False:
|
||||||
|
raise exceptions.channelNoPermissionsException
|
||||||
|
|
||||||
|
# Add our userID to users in that channel
|
||||||
|
channelObject.userJoin(userID)
|
||||||
|
|
||||||
|
# Add the channel to our joined channel
|
||||||
|
token.joinChannel(channel)
|
||||||
|
|
||||||
|
# Send channel joined (bancho). We use clientName here because of #multiplayer and #spectator channels
|
||||||
|
token.enqueue(serverPackets.channelJoinSuccess(userID, channelObject.clientName))
|
||||||
|
|
||||||
|
# Send channel joined (IRC)
|
||||||
|
if glob.irc == True and toIRC == True:
|
||||||
|
glob.ircServer.banchoJoinChannel(username, channel)
|
||||||
|
|
||||||
|
# Console output
|
||||||
|
log.info("{} joined channel {}".format(username, channel))
|
||||||
|
|
||||||
|
# IRC code return
|
||||||
|
return 0
|
||||||
|
except exceptions.channelNoPermissionsException:
|
||||||
|
log.warning("{} attempted to join channel {}, but they have no read permissions".format(username, channel))
|
||||||
|
return 403
|
||||||
|
except exceptions.channelUnknownException:
|
||||||
|
log.warning("{} attempted to join an unknown channel ({})".format(username, channel))
|
||||||
|
return 403
|
||||||
|
except exceptions.userNotFoundException:
|
||||||
|
log.warning("User not connected to IRC/Bancho")
|
||||||
|
return 403 # idk
|
||||||
|
|
||||||
|
def partChannel(userID = 0, channel = "", token = None, toIRC = True, kick = False):
|
||||||
|
"""
|
||||||
|
Part a channel
|
||||||
|
|
||||||
|
userID -- user ID of the user that parts the channel. Optional.
|
||||||
|
token can be used instead.
|
||||||
|
token -- user token object of user that parts the channel. Optional.
|
||||||
|
userID can be used instead.
|
||||||
|
channel -- name of channel
|
||||||
|
toIRC -- if True, send this channel join event to IRC. Must be true if joining from bancho.
|
||||||
|
Optional. Defaukt: True
|
||||||
|
kick -- if True, channel tab will be closed on client. Used when leaving lobby. Optional. Default: False
|
||||||
|
return -- returns 0 if joined or other IRC code in case of error. Needed only on IRC-side
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Get token if not defined
|
||||||
|
if token == None:
|
||||||
|
token = glob.tokens.getTokenFromUserID(userID)
|
||||||
|
# Make sure the token exists
|
||||||
|
if token == None:
|
||||||
|
raise exceptions.userNotFoundException
|
||||||
|
else:
|
||||||
|
token = token
|
||||||
|
userID = token.userID
|
||||||
|
|
||||||
|
# Get usertoken data
|
||||||
|
username = token.username
|
||||||
|
|
||||||
|
# Determine internal/client name if needed
|
||||||
|
# (toclient is used clientwise for #multiplayer and #spectator channels)
|
||||||
|
channelClient = channel
|
||||||
|
if channel == "#spectator":
|
||||||
|
if token.spectating == 0:
|
||||||
|
s = userID
|
||||||
|
else:
|
||||||
|
s = token.spectating
|
||||||
|
channel = "#spect_{}".format(s)
|
||||||
|
elif channel == "#multiplayer":
|
||||||
|
channel = "#multi_{}".format(token.matchID)
|
||||||
|
elif channel.startswith("#spect_"):
|
||||||
|
channelClient = "#spectator"
|
||||||
|
elif channel.startswith("#multi_"):
|
||||||
|
channelClient = "#multiplayer"
|
||||||
|
|
||||||
|
# Make sure the channel exists
|
||||||
|
if channel not in glob.channels.channels:
|
||||||
|
raise exceptions.channelUnknownException
|
||||||
|
|
||||||
|
# Part channel (token-side and channel-side)
|
||||||
|
channelObject = glob.channels.channels[channel]
|
||||||
|
token.partChannel(channel)
|
||||||
|
channelObject.userPart(userID)
|
||||||
|
|
||||||
|
# Force close tab if needed
|
||||||
|
# NOTE: Maybe always needed, will check later
|
||||||
|
if kick == True:
|
||||||
|
token.enqueue(serverPackets.channelKicked(channelObject.clientName))
|
||||||
|
|
||||||
|
# IRC part
|
||||||
|
if glob.irc == True and toIRC == True:
|
||||||
|
glob.ircServer.banchoPartChannel(username, channel)
|
||||||
|
|
||||||
|
# Console output
|
||||||
|
log.info("{} parted channel {}".format(username, channel))
|
||||||
|
|
||||||
|
# Return IRC code
|
||||||
|
return 0
|
||||||
|
except exceptions.channelUnknownException:
|
||||||
|
log.warning("{} attempted to part an unknown channel ({})".format(username, channel))
|
||||||
|
return 403
|
||||||
|
except exceptions.userNotFoundException:
|
||||||
|
log.warning("User not connected to IRC/Bancho")
|
||||||
|
return 442 # idk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
|
||||||
|
"""
|
||||||
|
Send a message to osu!bancho and IRC server
|
||||||
|
|
||||||
|
fro -- sender username. Optional.
|
||||||
|
You can use token instead of this if you wish.
|
||||||
|
to -- receiver channel (if starts with #) or username
|
||||||
|
message -- text of the message
|
||||||
|
token -- sender token object.
|
||||||
|
You can use this instead of fro if you are sending messages from bancho.
|
||||||
|
Optional.
|
||||||
|
toIRC -- if True, send the message to IRC. If False, send it to Bancho only.
|
||||||
|
Optional. Default: True
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
tokenString = ""
|
||||||
|
# Get token object if not passed
|
||||||
|
if token == None:
|
||||||
|
token = glob.tokens.getTokenFromUsername(fro)
|
||||||
|
if token == None:
|
||||||
|
raise exceptions.userNotFoundException
|
||||||
|
else:
|
||||||
|
# token object alredy passed, get its string and its username (fro)
|
||||||
|
fro = token.username
|
||||||
|
tokenString = token.token
|
||||||
|
|
||||||
|
# Set some variables
|
||||||
|
userID = token.userID
|
||||||
|
username = token.username
|
||||||
|
recipients = []
|
||||||
|
|
||||||
|
# Make sure the user is not in restricted mode
|
||||||
|
if token.restricted == True:
|
||||||
|
raise exceptions.userRestrictedException
|
||||||
|
|
||||||
|
# Make sure the user is not silenced
|
||||||
|
if token.isSilenced() == True:
|
||||||
|
raise exceptions.userSilencedException
|
||||||
|
|
||||||
|
# Determine internal name if needed
|
||||||
|
# (toclient is used clientwise for #multiplayer and #spectator channels)
|
||||||
|
toClient = to
|
||||||
|
if to == "#spectator":
|
||||||
|
if token.spectating == 0:
|
||||||
|
s = userID
|
||||||
|
else:
|
||||||
|
s = token.spectating
|
||||||
|
to = "#spect_{}".format(s)
|
||||||
|
elif to == "#multiplayer":
|
||||||
|
to = "#multi_{}".format(token.matchID)
|
||||||
|
elif to.startswith("#spect_"):
|
||||||
|
toClient = "#spectator"
|
||||||
|
elif to.startswith("#multi_"):
|
||||||
|
toClient = "#multiplayer"
|
||||||
|
|
||||||
|
# Truncate message if > 2048 characters
|
||||||
|
message = message[:2048]+"..." if len(message) > 2048 else message
|
||||||
|
|
||||||
|
# Build packet bytes
|
||||||
|
packet = serverPackets.sendMessage(username, toClient, message)
|
||||||
|
|
||||||
|
# Send the message
|
||||||
|
isChannel = to.startswith("#")
|
||||||
|
if isChannel == True:
|
||||||
|
# CHANNEL
|
||||||
|
# Make sure the channel exists
|
||||||
|
if to not in glob.channels.channels:
|
||||||
|
raise exceptions.channelUnknownException
|
||||||
|
|
||||||
|
# Make sure the channel is not in moderated mode
|
||||||
|
if glob.channels.channels[to].moderated == True and token.admin == False:
|
||||||
|
raise exceptions.channelModeratedException
|
||||||
|
|
||||||
|
# Make sure we have write permissions
|
||||||
|
if glob.channels.channels[to].publicWrite == False and token.admin == False:
|
||||||
|
raise exceptions.channelNoPermissionsException
|
||||||
|
|
||||||
|
# Everything seems fine, build recipients list and send packet
|
||||||
|
recipients = glob.channels.channels[to].getConnectedUsers()[:]
|
||||||
|
for key, value in glob.tokens.tokens.items():
|
||||||
|
# Skip our client and irc clients
|
||||||
|
if key == tokenString or value.irc == True:
|
||||||
|
continue
|
||||||
|
# Send to this client if it's connected to the channel
|
||||||
|
if value.userID in recipients:
|
||||||
|
value.enqueue(packet)
|
||||||
|
else:
|
||||||
|
# USER
|
||||||
|
# Make sure recipient user is connected
|
||||||
|
recipientToken = glob.tokens.getTokenFromUsername(to)
|
||||||
|
if recipientToken == None:
|
||||||
|
raise exceptions.userNotFoundException
|
||||||
|
|
||||||
|
# Make sure the recipient is not restricted or we are FokaBot
|
||||||
|
if recipientToken.restricted == True and fro.lower() != "fokabot":
|
||||||
|
raise exceptions.userRestrictedException
|
||||||
|
|
||||||
|
# TODO: Make sure the recipient has not disabled PMs for non-friends or he's our friend
|
||||||
|
|
||||||
|
# Check message templates (mods/admins only)
|
||||||
|
if message in messageTemplates.templates and token.admin == True:
|
||||||
|
sendMessage(fro, to, messageTemplates.templates[message])
|
||||||
|
|
||||||
|
# Everything seems fine, send packet
|
||||||
|
recipientToken.enqueue(packet)
|
||||||
|
|
||||||
|
# Send the message to IRC
|
||||||
|
if glob.irc == True and toIRC == True:
|
||||||
|
glob.ircServer.banchoMessage(fro, to, message)
|
||||||
|
|
||||||
|
# Spam protection (ignore FokaBot)
|
||||||
|
if userID > 999:
|
||||||
|
token.spamProtection()
|
||||||
|
|
||||||
|
# Fokabot message
|
||||||
|
if isChannel == True or to.lower() == "fokabot":
|
||||||
|
fokaMessage = fokabot.fokabotResponse(username, to, message)
|
||||||
|
if fokaMessage != False:
|
||||||
|
sendMessage("FokaBot", to if isChannel else fro, fokaMessage)
|
||||||
|
|
||||||
|
# File and discord logs (public chat only)
|
||||||
|
if to.startswith("#") == True:
|
||||||
|
log.chat("{fro} @ {to}: {message}".format(fro=username, to=to, message=str(message.encode("utf-8"))))
|
||||||
|
discordBotHelper.sendChatlog("**{fro} @ {to}:** {message}".format(fro=username, to=to, message=str(message.encode("utf-8"))[2:-1]))
|
||||||
|
return 0
|
||||||
|
except exceptions.userSilencedException:
|
||||||
|
token.enqueue(serverPackets.silenceEndTime(token.getSilenceSecondsLeft()))
|
||||||
|
log.warning("{} tried to send a message during silence".format(username))
|
||||||
|
return 404
|
||||||
|
except exceptions.channelModeratedException:
|
||||||
|
log.warning("{} tried to send a message to a channel that is in moderated mode ({})".format(username, to))
|
||||||
|
return 404
|
||||||
|
except exceptions.channelUnknownException:
|
||||||
|
log.warning("{} tried to send a message to an unknown channel ({})".format(username, to))
|
||||||
|
return 403
|
||||||
|
except exceptions.channelNoPermissionsException:
|
||||||
|
log.warning("{} tried to send a message to channel {}, but they have no write permissions".format(username, to))
|
||||||
|
return 404
|
||||||
|
except exceptions.userRestrictedException:
|
||||||
|
log.warning("{} tried to send a message {}, but the recipient is in restricted mode".format(username, to))
|
||||||
|
return 404
|
||||||
|
except exceptions.userNotFoundException:
|
||||||
|
log.warning("User not connected to IRC/Bancho")
|
||||||
|
return 401
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
""" IRC-Bancho Connect/Disconnect/Join/Part interfaces"""
|
||||||
|
def IRCConnect(username):
|
||||||
|
userID = userHelper.getID(username)
|
||||||
|
if userID == False:
|
||||||
|
log.warning("{} doesn't exist".format(username))
|
||||||
|
return
|
||||||
|
glob.tokens.deleteOldTokens(userID)
|
||||||
|
glob.tokens.addToken(userID, irc=True)
|
||||||
|
glob.tokens.enqueueAll(serverPackets.userPanel(userID))
|
||||||
|
log.info("{} logged in from IRC".format(username))
|
||||||
|
|
||||||
|
def IRCDisconnect(username):
|
||||||
|
token = glob.tokens.getTokenFromUsername(username)
|
||||||
|
if token == None:
|
||||||
|
log.warning("{} doesn't exist".format(username))
|
||||||
|
return
|
||||||
|
logoutEvent.handle(token)
|
||||||
|
log.info("{} disconnected from IRC".format(username))
|
||||||
|
|
||||||
|
def IRCJoinChannel(username, channel):
|
||||||
|
userID = userHelper.getID(username)
|
||||||
|
if userID == False:
|
||||||
|
log.warning("{} doesn't exist".format(username))
|
||||||
|
return
|
||||||
|
# NOTE: This should have also `toIRC` = False` tho,
|
||||||
|
# since we send JOIN message later on ircserver.py.
|
||||||
|
# Will test this later
|
||||||
|
return joinChannel(userID, channel)
|
||||||
|
|
||||||
|
def IRCPartChannel(username, channel):
|
||||||
|
userID = userHelper.getID(username)
|
||||||
|
if userID == False:
|
||||||
|
log.warning("{} doesn't exist".format(username))
|
||||||
|
return
|
||||||
|
return partChannel(userID, channel)
|
|
@ -52,7 +52,6 @@ class config:
|
||||||
self.config.get("server","threads")
|
self.config.get("server","threads")
|
||||||
self.config.get("server","gzip")
|
self.config.get("server","gzip")
|
||||||
self.config.get("server","gziplevel")
|
self.config.get("server","gziplevel")
|
||||||
self.config.get("server","localize")
|
|
||||||
self.config.get("server","cikey")
|
self.config.get("server","cikey")
|
||||||
self.config.get("server","cloudflare")
|
self.config.get("server","cloudflare")
|
||||||
|
|
||||||
|
@ -66,6 +65,12 @@ class config:
|
||||||
self.config.get("discord","enable")
|
self.config.get("discord","enable")
|
||||||
self.config.get("discord","boturl")
|
self.config.get("discord","boturl")
|
||||||
self.config.get("discord","devgroup")
|
self.config.get("discord","devgroup")
|
||||||
|
|
||||||
|
self.config.get("irc","enable")
|
||||||
|
self.config.get("irc","port")
|
||||||
|
|
||||||
|
self.config.get("localize","enable")
|
||||||
|
self.config.get("localize","ipapiurl")
|
||||||
return True
|
return True
|
||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
|
@ -91,7 +96,6 @@ class config:
|
||||||
self.config.set("server", "threads", "16")
|
self.config.set("server", "threads", "16")
|
||||||
self.config.set("server", "gzip", "1")
|
self.config.set("server", "gzip", "1")
|
||||||
self.config.set("server", "gziplevel", "6")
|
self.config.set("server", "gziplevel", "6")
|
||||||
self.config.set("server", "localize", "1")
|
|
||||||
self.config.set("server", "cikey", "changeme")
|
self.config.set("server", "cikey", "changeme")
|
||||||
self.config.set("server", "cloudflare", "0")
|
self.config.set("server", "cloudflare", "0")
|
||||||
|
|
||||||
|
@ -109,6 +113,14 @@ class config:
|
||||||
self.config.set("discord", "boturl", "")
|
self.config.set("discord", "boturl", "")
|
||||||
self.config.set("discord", "devgroup", "")
|
self.config.set("discord", "devgroup", "")
|
||||||
|
|
||||||
|
self.config.add_section("irc")
|
||||||
|
self.config.set("irc", "enable", "1")
|
||||||
|
self.config.set("irc", "port", "6667")
|
||||||
|
|
||||||
|
self.config.add_section("localize")
|
||||||
|
self.config.set("localize", "enable", "1")
|
||||||
|
self.config.set("localize", "ipapiurl", "http://ip.zxq.co")
|
||||||
|
|
||||||
# Write ini to file and close
|
# Write ini to file and close
|
||||||
self.config.write(f)
|
self.config.write(f)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
|
@ -44,7 +44,7 @@ class db:
|
||||||
self.lastWorker = 0
|
self.lastWorker = 0
|
||||||
self.workersNumber = workers
|
self.workersNumber = workers
|
||||||
for i in range(0,self.workersNumber):
|
for i in range(0,self.workersNumber):
|
||||||
print("> Spawning MySQL pettirosso meme {}".format(i))
|
print(".", end="")
|
||||||
self.workers.append(mysqlWorker(i, host, username, password, database))
|
self.workers.append(mysqlWorker(i, host, username, password, database))
|
||||||
|
|
||||||
def getWorker(self):
|
def getWorker(self):
|
||||||
|
|
|
@ -21,7 +21,7 @@ def hexString(s):
|
||||||
return -- string with hex value
|
return -- string with hex value
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return ":".join("{:02x}".format(ord(c)) for c in s)
|
return ":".join("{:02x}".format(ord(str(c))) for c in s)
|
||||||
|
|
||||||
def readableMods(__mods):
|
def readableMods(__mods):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
import urllib.request
|
import urllib.request
|
||||||
import json
|
import json
|
||||||
|
from objects import glob
|
||||||
|
|
||||||
from helpers import logHelper as log
|
from helpers import logHelper as log
|
||||||
|
|
||||||
# API URL
|
|
||||||
URL = "http://ip.zxq.co/"
|
|
||||||
|
|
||||||
|
|
||||||
def getCountry(ip):
|
def getCountry(ip):
|
||||||
"""
|
"""
|
||||||
Get country from IP address
|
Get country from IP address
|
||||||
|
@ -17,7 +14,7 @@ def getCountry(ip):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Try to get country from Pikolo Aul's Go-Sanic ip API
|
# Try to get country from Pikolo Aul's Go-Sanic ip API
|
||||||
result = json.loads(urllib.request.urlopen("{}/{}".format(URL, ip), timeout=3).read().decode())["country"]
|
result = json.loads(urllib.request.urlopen("{}/{}".format(glob.conf.config["localize"]["ipapiurl"], ip), timeout=3).read().decode())["country"]
|
||||||
return result.upper()
|
return result.upper()
|
||||||
except:
|
except:
|
||||||
log.error("Error in get country")
|
log.error("Error in get country")
|
||||||
|
@ -34,7 +31,7 @@ def getLocation(ip):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Try to get position from Pikolo Aul's Go-Sanic ip API
|
# Try to get position from Pikolo Aul's Go-Sanic ip API
|
||||||
result = json.loads(urllib.request.urlopen("{}/{}".format(URL, ip), timeout=3).read().decode())["loc"].split(",")
|
result = json.loads(urllib.request.urlopen("{}/{}".format(glob.conf.config["localize"]["ipapiurl"], ip), timeout=3).read().decode())["loc"].split(",")
|
||||||
return [float(result[0]), float(result[1])]
|
return [float(result[0]), float(result[1])]
|
||||||
except:
|
except:
|
||||||
log.error("Error in get position")
|
log.error("Error in get position")
|
||||||
|
|
|
@ -118,7 +118,7 @@ def packData(__data, __dataType):
|
||||||
# Non empty string
|
# Non empty string
|
||||||
data += b"\x0B"
|
data += b"\x0B"
|
||||||
data += uleb128Encode(len(__data))
|
data += uleb128Encode(len(__data))
|
||||||
data += str.encode(__data, "latin_1")
|
data += str.encode(__data, "latin_1", "ignore")
|
||||||
elif __dataType == dataTypes.uInt16:
|
elif __dataType == dataTypes.uInt16:
|
||||||
packType = "<H"
|
packType = "<H"
|
||||||
elif __dataType == dataTypes.sInt16:
|
elif __dataType == dataTypes.sInt16:
|
||||||
|
|
0
irc/__init__.py
Normal file
0
irc/__init__.py
Normal file
627
irc/ircserver.py
Normal file
627
irc/ircserver.py
Normal file
|
@ -0,0 +1,627 @@
|
||||||
|
import sys
|
||||||
|
import socket
|
||||||
|
import select
|
||||||
|
import time
|
||||||
|
import re
|
||||||
|
import hashlib
|
||||||
|
from helpers import logHelper as log
|
||||||
|
|
||||||
|
from objects import glob
|
||||||
|
from helpers import chatHelper as chat
|
||||||
|
|
||||||
|
class Client:
|
||||||
|
"""
|
||||||
|
IRC Client object
|
||||||
|
"""
|
||||||
|
__linesep_regexp = re.compile(r"\r?\n")
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, server, sock):
|
||||||
|
"""
|
||||||
|
Initialize a Client object
|
||||||
|
|
||||||
|
server -- server object
|
||||||
|
sock -- socket connection object
|
||||||
|
"""
|
||||||
|
self.__timestamp = time.time()
|
||||||
|
self.__readbuffer = ""
|
||||||
|
self.__writebuffer = ""
|
||||||
|
self.__sentPing = False
|
||||||
|
self.__handleCommand = self.passHandler
|
||||||
|
|
||||||
|
self.server = server
|
||||||
|
self.socket = sock
|
||||||
|
(self.ip, self.port) = sock.getpeername()
|
||||||
|
self.username = ""
|
||||||
|
self.supposedUsername = ""
|
||||||
|
self.joinedChannels = []
|
||||||
|
|
||||||
|
def messageChannel(self, channel, command, message, includeSelf=False):
|
||||||
|
line = ":{} {}".format(command, message)
|
||||||
|
for _, value in self.server.clients.items():
|
||||||
|
if channel in value.joinedChannels and (value != self or includeSelf):
|
||||||
|
value.message(line)
|
||||||
|
|
||||||
|
def message(self, msg):
|
||||||
|
"""
|
||||||
|
Add a message (basic string) to client buffer.
|
||||||
|
This is the lowest possible level.
|
||||||
|
|
||||||
|
msg -- message to add
|
||||||
|
"""
|
||||||
|
self.__writebuffer += msg + "\r\n"
|
||||||
|
|
||||||
|
|
||||||
|
def writeBufferSize(self):
|
||||||
|
"""
|
||||||
|
Return this client's write buffer size
|
||||||
|
|
||||||
|
return -- write buffer size
|
||||||
|
"""
|
||||||
|
return len(self.__writebuffer)
|
||||||
|
|
||||||
|
|
||||||
|
def reply(self, msg):
|
||||||
|
"""
|
||||||
|
Add an IRC-like message to client buffer.
|
||||||
|
|
||||||
|
msg -- message (without IRC stuff)
|
||||||
|
"""
|
||||||
|
self.message(":{} {}".format(self.server.host, msg))
|
||||||
|
|
||||||
|
|
||||||
|
def replyCode(self, code, message, nickname="", channel=""):
|
||||||
|
"""
|
||||||
|
Add an IRC-like message to client buffer with code
|
||||||
|
|
||||||
|
code -- response code
|
||||||
|
message -- response message
|
||||||
|
nickname -- receiver nickname
|
||||||
|
channel -- optional
|
||||||
|
"""
|
||||||
|
if nickname == "":
|
||||||
|
nickname = self.username
|
||||||
|
if channel != "":
|
||||||
|
channel = " "+channel
|
||||||
|
self.reply("{code:03d} {nickname}{channel} :{message}".format(code=code, nickname=nickname, channel=channel, message=message))
|
||||||
|
|
||||||
|
|
||||||
|
def reply403(self, channel):
|
||||||
|
"""
|
||||||
|
Add a 403 reply (no such channel) to client buffer.
|
||||||
|
|
||||||
|
channel -- meh
|
||||||
|
"""
|
||||||
|
self.replyCode(403, "{} :No such channel".format(channel))
|
||||||
|
|
||||||
|
|
||||||
|
def reply461(self, command):
|
||||||
|
"""
|
||||||
|
Add a 461 reply (not enough parameters) to client buffer
|
||||||
|
|
||||||
|
command -- command that had not enough parameters
|
||||||
|
"""
|
||||||
|
self.replyCode(403, "{} :Not enough parameters".format(command))
|
||||||
|
|
||||||
|
|
||||||
|
def disconnect(self, quitmsg = "Client quit", callLogout = True):
|
||||||
|
"""
|
||||||
|
Disconnects this client from the IRC server
|
||||||
|
|
||||||
|
quitmsg -- IRC quit message. Default: 'Client quit'
|
||||||
|
callLogout -- if True, call logoutEvent on bancho
|
||||||
|
"""
|
||||||
|
# Send error to client and close socket
|
||||||
|
self.message("ERROR :{}".format(quitmsg))
|
||||||
|
self.socket.close()
|
||||||
|
log.info("[IRC] Disconnected connection from {}:{} ({})".format(self.ip, self.port, quitmsg))
|
||||||
|
|
||||||
|
# Remove socket from server
|
||||||
|
self.server.removeClient(self, quitmsg)
|
||||||
|
|
||||||
|
# Bancho logout
|
||||||
|
if callLogout == True:
|
||||||
|
chat.IRCDisconnect(self.username)
|
||||||
|
|
||||||
|
|
||||||
|
def readSocket(self):
|
||||||
|
"""Read data coming from this client socket"""
|
||||||
|
try:
|
||||||
|
# Try to read incoming data from socket
|
||||||
|
data = self.socket.recv(2 ** 10)
|
||||||
|
log.debug("[IRC] [{}:{}] -> {}".format(self.ip, self.port, data))
|
||||||
|
quitmsg = "EOT"
|
||||||
|
except socket.error as x:
|
||||||
|
# Error while reading data, this client will be disconnected
|
||||||
|
data = ""
|
||||||
|
quitmsg = x
|
||||||
|
|
||||||
|
if data:
|
||||||
|
# Parse received data if needed
|
||||||
|
self.__readbuffer += data.decode("latin_1")
|
||||||
|
self.parseBuffer()
|
||||||
|
self.__timestamp = time.time()
|
||||||
|
self.__sentPing = False
|
||||||
|
else:
|
||||||
|
# No data, disconnect this socket
|
||||||
|
self.disconnect(quitmsg)
|
||||||
|
|
||||||
|
|
||||||
|
def parseBuffer(self):
|
||||||
|
"""Parse self.__readbuffer, get command, arguments and call its handler"""
|
||||||
|
# Get lines from buffer
|
||||||
|
lines = self.__linesep_regexp.split(self.__readbuffer)
|
||||||
|
self.__readbuffer = lines[-1]
|
||||||
|
lines = lines[:-1]
|
||||||
|
|
||||||
|
# Process every line
|
||||||
|
for line in lines:
|
||||||
|
if not line:
|
||||||
|
# Empty line. Ignore.
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Get arguments
|
||||||
|
x = line.split(" ", 1)
|
||||||
|
|
||||||
|
# Command is the first argument, always uppercase
|
||||||
|
command = x[0].upper()
|
||||||
|
|
||||||
|
if len(x) == 1:
|
||||||
|
# Command only, no arguments
|
||||||
|
arguments = []
|
||||||
|
else:
|
||||||
|
# We have some arguments
|
||||||
|
# Weird sorcery
|
||||||
|
if len(x[1]) > 0 and x[1][0] == ":":
|
||||||
|
arguments = [x[1][1:]]
|
||||||
|
else:
|
||||||
|
y = x[1].split(" :", 1)
|
||||||
|
arguments = y[0].split()
|
||||||
|
if len(y) == 2:
|
||||||
|
arguments.append(y[1])
|
||||||
|
|
||||||
|
# Handle command with its arguments
|
||||||
|
self.__handleCommand(command, arguments)
|
||||||
|
|
||||||
|
|
||||||
|
def writeSocket(self):
|
||||||
|
"""Write buffer to socket"""
|
||||||
|
try:
|
||||||
|
sent = self.socket.send(self.__writebuffer.encode())
|
||||||
|
log.debug("[IRC] [{}:{}] <- {}".format(self.ip, self.port, self.__writebuffer[:sent]))
|
||||||
|
self.__writebuffer = self.__writebuffer[sent:]
|
||||||
|
except socket.error as x:
|
||||||
|
self.disconnect(x)
|
||||||
|
|
||||||
|
|
||||||
|
def checkAlive(self):
|
||||||
|
"""Check if this client is still connected"""
|
||||||
|
now = time.time()
|
||||||
|
if self.__timestamp + 180 < now:
|
||||||
|
self.disconnect("ping timeout")
|
||||||
|
return
|
||||||
|
if not self.__sentPing and self.__timestamp + 90 < now:
|
||||||
|
if self.__handleCommand == self.mainHandler:
|
||||||
|
# Registered.
|
||||||
|
self.message("PING :{}".format(self.server.host))
|
||||||
|
self.__sentPing = True
|
||||||
|
else:
|
||||||
|
# Not registered.
|
||||||
|
self.disconnect("ping timeout")
|
||||||
|
|
||||||
|
|
||||||
|
def sendLusers(self):
|
||||||
|
"""Send lusers response to this client"""
|
||||||
|
self.replyCode(251, "There are {} users and 0 services on 1 server".format(len(glob.tokens.tokens)))
|
||||||
|
|
||||||
|
def sendMotd(self):
|
||||||
|
"""Send MOTD to this client"""
|
||||||
|
self.replyCode(375, "- {} Message of the day - ".format(self.server.host))
|
||||||
|
if len(self.server.motd) == 0:
|
||||||
|
self.replyCode(422, "MOTD File is missing")
|
||||||
|
else:
|
||||||
|
for i in self.server.motd:
|
||||||
|
self.replyCode(372, "- {}".format(i))
|
||||||
|
self.replyCode(376, "End of MOTD command")
|
||||||
|
|
||||||
|
"""""""""
|
||||||
|
HANDLERS
|
||||||
|
"""""""""
|
||||||
|
def dummyHandler(self, command, arguments):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def passHandler(self, command, arguments):
|
||||||
|
"""PASS command handler"""
|
||||||
|
if command == "PASS":
|
||||||
|
if len(arguments) == 0:
|
||||||
|
self.reply461("PASS")
|
||||||
|
else:
|
||||||
|
# IRC token check
|
||||||
|
m = hashlib.md5()
|
||||||
|
m.update(arguments[0].encode("utf-8"))
|
||||||
|
tokenHash = m.hexdigest()
|
||||||
|
supposedUsername = glob.db.fetch("SELECT users.username FROM users LEFT JOIN irc_tokens ON users.id = irc_tokens.userid WHERE irc_tokens.token = %s LIMIT 1", [tokenHash])
|
||||||
|
if supposedUsername:
|
||||||
|
self.supposedUsername = supposedUsername["username"]
|
||||||
|
self.__handleCommand = self.registerHandler
|
||||||
|
else:
|
||||||
|
# Wrong IRC Token
|
||||||
|
self.reply("464 :Password incorrect")
|
||||||
|
elif command == "QUIT":
|
||||||
|
self.disconnect()
|
||||||
|
|
||||||
|
|
||||||
|
def registerHandler(self, command, arguments):
|
||||||
|
"""NICK and USER commands handler"""
|
||||||
|
if command == "NICK":
|
||||||
|
if len(arguments) < 1:
|
||||||
|
self.reply("431 :No nickname given")
|
||||||
|
return
|
||||||
|
nick = arguments[0]
|
||||||
|
|
||||||
|
# Make sure this is the first time we set our nickname
|
||||||
|
if self.username != "":
|
||||||
|
self.reply("432 * %s :Erroneous nickname" % nick)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Make sure the IRC token was correct:
|
||||||
|
if nick.lower() != self.supposedUsername.lower():
|
||||||
|
self.reply("464 :Password incorrect")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Make sure we are not connected to Bancho
|
||||||
|
token = glob.tokens.getTokenFromUsername(nick)
|
||||||
|
if token != None:
|
||||||
|
self.reply("433 * {} :Nickname is already in use".format(nick))
|
||||||
|
return
|
||||||
|
|
||||||
|
# Make sure we are not already connected from IRC with that name
|
||||||
|
for _, value in self.server.clients.items():
|
||||||
|
if value.username == self.username and value != self:
|
||||||
|
self.reply("433 * {} :Nickname is already in use".format(nick))
|
||||||
|
return
|
||||||
|
|
||||||
|
# Everything seems fine, set username (nickname)
|
||||||
|
self.username = nick
|
||||||
|
elif command == "USER":
|
||||||
|
# Ignore USER command, we use nickname only
|
||||||
|
return
|
||||||
|
elif command == "QUIT":
|
||||||
|
# Disconnect if we have received a QUIT command
|
||||||
|
self.disconnect()
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
# Ignore any other command while logging in
|
||||||
|
return
|
||||||
|
|
||||||
|
# If we now have a valid username, connect to bancho and send IRC welcome stuff
|
||||||
|
if self.username != "":
|
||||||
|
# Bancho connection
|
||||||
|
chat.IRCConnect(self.username)
|
||||||
|
|
||||||
|
# IRC reply
|
||||||
|
self.replyCode(1, "Welcome to the Internet Relay Network")
|
||||||
|
self.replyCode(2, "Your host is {}, running version pep.py-{}".format(self.server.host, glob.VERSION))
|
||||||
|
self.replyCode(3, "This server was created since the beginning")
|
||||||
|
self.replyCode(4, "{} pep.py-{} o o".format(self.server.host, glob.VERSION))
|
||||||
|
self.sendLusers()
|
||||||
|
self.sendMotd()
|
||||||
|
self.__handleCommand = self.mainHandler
|
||||||
|
|
||||||
|
def quitHandler(self, command, arguments):
|
||||||
|
"""QUIT command handler"""
|
||||||
|
self.disconnect(self.username if len(arguments) < 1 else arguments[0])
|
||||||
|
|
||||||
|
def joinHandler(self, command, arguments):
|
||||||
|
"""JOIN command handler"""
|
||||||
|
if len(arguments) < 1:
|
||||||
|
self.reply461("JOIN")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get bancho token object
|
||||||
|
token = glob.tokens.getTokenFromUsername(self.username)
|
||||||
|
if token == None:
|
||||||
|
return
|
||||||
|
|
||||||
|
# TODO: Part all channels
|
||||||
|
if arguments[0] == "0":
|
||||||
|
return
|
||||||
|
'''for (channelname, channel) in self.channels.items():
|
||||||
|
self.message_channel(channel, "PART", channelname, True)
|
||||||
|
self.channel_log(channel, "left", meta=True)
|
||||||
|
server.remove_member_from_channel(self, channelname)
|
||||||
|
self.channels = {}
|
||||||
|
return'''
|
||||||
|
|
||||||
|
# Get channels to join list
|
||||||
|
channels = arguments[0].split(",")
|
||||||
|
|
||||||
|
for channel in channels:
|
||||||
|
# Make sure we are not already in that channel
|
||||||
|
# (we already check this bancho-side, but we need to do it
|
||||||
|
# also here k maron)
|
||||||
|
if channel.lower() in token.joinedChannels:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Attempt to join the channel
|
||||||
|
response = chat.IRCJoinChannel(self.username, channel)
|
||||||
|
if response == 0:
|
||||||
|
# Joined successfully
|
||||||
|
self.joinedChannels.append(channel)
|
||||||
|
|
||||||
|
# Let everyone in this channel know that we've joined
|
||||||
|
self.messageChannel(channel, "{} JOIN".format(self.username), channel, True)
|
||||||
|
|
||||||
|
# Send channel description (topic)
|
||||||
|
description = glob.channels.channels[channel].description
|
||||||
|
if description == "":
|
||||||
|
self.replyCode(331, "No topic is set", channel=channel)
|
||||||
|
else:
|
||||||
|
self.replyCode(332, description, channel=channel)
|
||||||
|
|
||||||
|
# Build connected users list
|
||||||
|
users = glob.channels.channels[channel].getConnectedUsers()[:]
|
||||||
|
usernames = []
|
||||||
|
for user in users:
|
||||||
|
token = glob.tokens.getTokenFromUserID(user)
|
||||||
|
if token == None:
|
||||||
|
continue
|
||||||
|
usernames.append(token.username)
|
||||||
|
usernames = " ".join(usernames)
|
||||||
|
|
||||||
|
# Send IRC users lis
|
||||||
|
self.replyCode(353, usernames, channel="= {}".format(channel))
|
||||||
|
self.replyCode(366, "End of NAMES list", channel=channel)
|
||||||
|
elif response == 403:
|
||||||
|
# Channel doesn't exist (or no read permissions)
|
||||||
|
self.reply403(channel)
|
||||||
|
continue
|
||||||
|
|
||||||
|
def partHandler(self, command, arguments):
|
||||||
|
"""PART command handler"""
|
||||||
|
if len(arguments) < 1:
|
||||||
|
self.reply461("PART")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get bancho token object
|
||||||
|
token = glob.tokens.getTokenFromUsername(self.username)
|
||||||
|
if token == None:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get channels to part list
|
||||||
|
channels = arguments[0].split(",")
|
||||||
|
|
||||||
|
for channel in channels:
|
||||||
|
# Make sure we in that channel
|
||||||
|
# (we already check this bancho-side, but we need to do it
|
||||||
|
# also here k maron)
|
||||||
|
if channel.lower() not in token.joinedChannels:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Attempt to part the channel
|
||||||
|
response = chat.IRCPartChannel(self.username, channel)
|
||||||
|
if response == 0:
|
||||||
|
# No errors, remove channel from joinedChannels
|
||||||
|
self.joinedChannels.remove(channel)
|
||||||
|
elif response == 403:
|
||||||
|
self.reply403(channel)
|
||||||
|
elif response == 442:
|
||||||
|
self.replyCode(442, "You're not on that channel", channel=channel)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def noticePrivmsgHandler(self, command, arguments):
|
||||||
|
"""NOTICE and PRIVMSG commands handler (same syntax)"""
|
||||||
|
# Syntax check
|
||||||
|
if len(arguments) == 0:
|
||||||
|
self.replyCode(411, "No recipient given ({})".format(command))
|
||||||
|
return
|
||||||
|
if len(arguments) == 1:
|
||||||
|
self.replyCode(412, "No text to send")
|
||||||
|
return
|
||||||
|
recipient = arguments[0]
|
||||||
|
message = arguments[1]
|
||||||
|
|
||||||
|
# Send the message to bancho and reply
|
||||||
|
response = chat.sendMessage(self.username, recipient, message, toIRC=False)
|
||||||
|
if response == 404:
|
||||||
|
self.replyCode(404, "Cannot send to channel", channel=recipient)
|
||||||
|
return
|
||||||
|
elif response == 403:
|
||||||
|
self.replyCode(403, "No such channel", channel=recipient)
|
||||||
|
return
|
||||||
|
elif response == 401:
|
||||||
|
self.replyCode(401, "No such nick/channel", channel=recipient)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Send the message to IRC and bancho
|
||||||
|
if recipient.startswith("#"):
|
||||||
|
# Public message (IRC)
|
||||||
|
if recipient not in glob.channels.channels:
|
||||||
|
self.replyCode(401, "No such nick/channel", channel=recipient)
|
||||||
|
return
|
||||||
|
for _, value in self.server.clients.items():
|
||||||
|
if recipient in value.joinedChannels and value != self:
|
||||||
|
value.message(":{} PRIVMSG {} :{}".format(self.username, recipient, message))
|
||||||
|
#self.messageChannel(recipient, command, "{} :{}".format(recipient, message))
|
||||||
|
else:
|
||||||
|
# Private message (IRC)
|
||||||
|
for _, value in self.server.clients.items():
|
||||||
|
if value.username == recipient:
|
||||||
|
value.message(":{} PRIVMSG {} :{}".format(self.username, recipient, message))
|
||||||
|
|
||||||
|
def motdHandler(self, command, arguments):
|
||||||
|
"""MOTD command handler"""
|
||||||
|
self.sendMotd()
|
||||||
|
|
||||||
|
def lusersHandler(self, command, arguments):
|
||||||
|
"""LUSERS command handler"""
|
||||||
|
self.sendLusers()
|
||||||
|
|
||||||
|
def pingHandler(self, command, arguments):
|
||||||
|
"""PING command handler"""
|
||||||
|
if len(arguments) < 1:
|
||||||
|
self.replyCode(409, "No origin specified")
|
||||||
|
return
|
||||||
|
self.reply("PONG {} :{}".format(self.server.host, arguments[0]))
|
||||||
|
|
||||||
|
def pongHandler(self, command, arguments):
|
||||||
|
"""(fake) PONG command handler"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def mainHandler(self, command, arguments):
|
||||||
|
"""Handler for post-login commands"""
|
||||||
|
handlers = {
|
||||||
|
#"AWAY": away_handler,
|
||||||
|
#"ISON": ison_handler,
|
||||||
|
"JOIN": self.joinHandler,
|
||||||
|
#"LIST": list_handler,
|
||||||
|
"LUSERS": self.lusersHandler,
|
||||||
|
#"MODE": mode_handler,
|
||||||
|
"MOTD": self.motdHandler,
|
||||||
|
#"NICK": nick_handler,
|
||||||
|
#"NOTICE": notice_and_privmsg_handler,
|
||||||
|
"PART": self.partHandler,
|
||||||
|
"PING": self.pingHandler,
|
||||||
|
"PONG": self.pongHandler,
|
||||||
|
"PRIVMSG": self.noticePrivmsgHandler,
|
||||||
|
"QUIT": self.quitHandler,
|
||||||
|
#"TOPIC": topic_handler,
|
||||||
|
#"WALLOPS": wallops_handler,
|
||||||
|
#"WHO": who_handler,
|
||||||
|
#"WHOIS": whois_handler,
|
||||||
|
"USER": self.dummyHandler,
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
handlers[command](command, arguments)
|
||||||
|
except KeyError:
|
||||||
|
self.replyCode(421, "Unknown command ({})".format(command))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Server:
|
||||||
|
def __init__(self, port):
|
||||||
|
self.host = socket.getfqdn("127.0.0.1")[:63]
|
||||||
|
self.port = port
|
||||||
|
self.clients = {} # Socket --> Client instance.
|
||||||
|
self.motd = ["Welcome to pep.py's embedded IRC server!", "This is a VERY simple IRC server and it's still in beta.", "Expect things to crash and not work as expected :("]
|
||||||
|
|
||||||
|
def forceDisconnection(self, username):
|
||||||
|
"""
|
||||||
|
Disconnect someone from IRC if connected
|
||||||
|
|
||||||
|
username -- victim
|
||||||
|
"""
|
||||||
|
for _, value in self.clients.items():
|
||||||
|
if value.username == username:
|
||||||
|
value.disconnect(callLogout=False)
|
||||||
|
break# or dictionary changes size during iteration
|
||||||
|
|
||||||
|
def banchoJoinChannel(self, username, channel):
|
||||||
|
"""
|
||||||
|
Let every IRC client connected to a specific client know that 'username' joined the channel from bancho
|
||||||
|
|
||||||
|
username -- username of bancho user
|
||||||
|
channel -- joined channel name
|
||||||
|
"""
|
||||||
|
for _, value in self.clients.items():
|
||||||
|
if channel in value.joinedChannels:
|
||||||
|
value.message(":{} JOIN {}".format(username, channel))
|
||||||
|
|
||||||
|
def banchoPartChannel(self, username, channel):
|
||||||
|
"""
|
||||||
|
Let every IRC client connected to a specific client know that 'username' parted the channel from bancho
|
||||||
|
|
||||||
|
username -- username of bancho user
|
||||||
|
channel -- joined channel name
|
||||||
|
"""
|
||||||
|
for _, value in self.clients.items():
|
||||||
|
if channel in value.joinedChannels:
|
||||||
|
value.message(":{} PART {}".format(username, channel))
|
||||||
|
|
||||||
|
def banchoMessage(self, fro, to, message):
|
||||||
|
"""
|
||||||
|
Send a message to IRC when someone sends it from bancho
|
||||||
|
|
||||||
|
fro -- sender username
|
||||||
|
to -- receiver username
|
||||||
|
message -- text of the message
|
||||||
|
"""
|
||||||
|
if to.startswith("#"):
|
||||||
|
# Public message
|
||||||
|
for _, value in self.clients.items():
|
||||||
|
if to in value.joinedChannels and value.username != fro:
|
||||||
|
value.message(":{} PRIVMSG {} :{}".format(fro, to, message))
|
||||||
|
else:
|
||||||
|
# Private message
|
||||||
|
for _, value in self.clients.items():
|
||||||
|
if value.username == to and value.username != fro:
|
||||||
|
value.message(":{} PRIVMSG {} :{}".format(fro, to, message))
|
||||||
|
|
||||||
|
|
||||||
|
def removeClient(self, client, quitmsg):
|
||||||
|
"""
|
||||||
|
Remove a client from connected clients
|
||||||
|
|
||||||
|
client -- client object
|
||||||
|
quitmsg -- QUIT argument, useless atm
|
||||||
|
"""
|
||||||
|
if client.socket in self.clients:
|
||||||
|
del self.clients[client.socket]
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""Start IRC server main loop"""
|
||||||
|
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
try:
|
||||||
|
serversocket.bind(("127.0.0.1", self.port))
|
||||||
|
except socket.error as e:
|
||||||
|
log.error("[IRC] Could not bind port {}:{}".format(self.port, e))
|
||||||
|
sys.exit(1)
|
||||||
|
serversocket.listen(5)
|
||||||
|
lastAliveCheck = time.time()
|
||||||
|
|
||||||
|
# Main server loop
|
||||||
|
while True:
|
||||||
|
(iwtd, owtd, ewtd) = select.select(
|
||||||
|
[serversocket] + [x.socket for x in self.clients.values()],
|
||||||
|
[x.socket for x in self.clients.values()
|
||||||
|
if x.writeBufferSize() > 0],
|
||||||
|
[],
|
||||||
|
2)
|
||||||
|
|
||||||
|
# Handle incoming connections
|
||||||
|
for x in iwtd:
|
||||||
|
if x in self.clients:
|
||||||
|
self.clients[x].readSocket()
|
||||||
|
else:
|
||||||
|
(conn, addr) = x.accept()
|
||||||
|
try:
|
||||||
|
self.clients[conn] = Client(self, conn)
|
||||||
|
log.info("[IRC] Accepted connection from {}:{}".format(addr[0], addr[1]))
|
||||||
|
except socket.error as e:
|
||||||
|
try:
|
||||||
|
conn.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Handle outgoing connections
|
||||||
|
for x in owtd:
|
||||||
|
if x in self.clients: # client may have been disconnected
|
||||||
|
self.clients[x].writeSocket()
|
||||||
|
|
||||||
|
# Make sure all IRC clients are still connected
|
||||||
|
now = time.time()
|
||||||
|
if lastAliveCheck + 10 < now:
|
||||||
|
for client in list(self.clients.values()):
|
||||||
|
client.checkAlive()
|
||||||
|
lastAliveCheck = now
|
||||||
|
|
||||||
|
|
||||||
|
def main(port=6667):
|
||||||
|
glob.ircServer = Server(port)
|
||||||
|
glob.ircServer.start()
|
|
@ -1,3 +1,5 @@
|
||||||
|
from objects import glob
|
||||||
|
|
||||||
class channel:
|
class channel:
|
||||||
"""
|
"""
|
||||||
A chat channel
|
A chat channel
|
||||||
|
@ -10,15 +12,7 @@ class channel:
|
||||||
moderated -- bool
|
moderated -- bool
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = ""
|
def __init__(self, __name, __description, __publicRead, __publicWrite, temp):
|
||||||
description = ""
|
|
||||||
connectedUsers = []
|
|
||||||
|
|
||||||
publicRead = False
|
|
||||||
publicWrite = False
|
|
||||||
moderated = False
|
|
||||||
|
|
||||||
def __init__(self, __name, __description, __publicRead, __publicWrite):
|
|
||||||
"""
|
"""
|
||||||
Create a new chat channel object
|
Create a new chat channel object
|
||||||
|
|
||||||
|
@ -26,13 +20,23 @@ class channel:
|
||||||
__description -- channel description
|
__description -- channel description
|
||||||
__publicRead -- bool, if true channel can be read by everyone, if false it can be read only by mods/admins
|
__publicRead -- bool, if true channel can be read by everyone, if false it can be read only by mods/admins
|
||||||
__publicWrite -- bool, same as public read but relative to write permissions
|
__publicWrite -- bool, same as public read but relative to write permissions
|
||||||
|
temp -- if True, channel will be deleted when there's no one in the channel. Optional. Default = False.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.name = __name
|
self.name = __name
|
||||||
self.description = __description
|
self.description = __description
|
||||||
self.publicRead = __publicRead
|
self.publicRead = __publicRead
|
||||||
self.publicWrite = __publicWrite
|
self.publicWrite = __publicWrite
|
||||||
self.connectedUsers = []
|
self.moderated = False
|
||||||
|
self.temp = temp
|
||||||
|
self.connectedUsers = [999] # Fokabot is always connected to every channels (otherwise it doesn't show up in IRC users list)
|
||||||
|
|
||||||
|
# Client name (#spectator/#multiplayer)
|
||||||
|
self.clientName = self.name
|
||||||
|
if self.name.startswith("#spect_"):
|
||||||
|
self.clientName = "#spectator"
|
||||||
|
elif self.name.startswith("#multi_"):
|
||||||
|
self.clientName = "#multiplayer"
|
||||||
|
|
||||||
|
|
||||||
def userJoin(self, __userID):
|
def userJoin(self, __userID):
|
||||||
|
@ -53,9 +57,13 @@ class channel:
|
||||||
__userID -- user ID that left the channel
|
__userID -- user ID that left the channel
|
||||||
"""
|
"""
|
||||||
|
|
||||||
connectedUsers = self.connectedUsers
|
if __userID in self.connectedUsers:
|
||||||
if __userID in connectedUsers:
|
self.connectedUsers.remove(__userID)
|
||||||
connectedUsers.remove(__userID)
|
|
||||||
|
# Remove temp channels if empty or there's only fokabot connected
|
||||||
|
l = len(self.connectedUsers)
|
||||||
|
if self.temp == True and ((l == 0) or (l == 1 and 999 in self.connectedUsers)):
|
||||||
|
glob.channels.removeChannel(self.name)
|
||||||
|
|
||||||
|
|
||||||
def getConnectedUsers(self):
|
def getConnectedUsers(self):
|
||||||
|
@ -64,7 +72,6 @@ class channel:
|
||||||
|
|
||||||
return -- connectedUsers list
|
return -- connectedUsers list
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.connectedUsers
|
return self.connectedUsers
|
||||||
|
|
||||||
|
|
||||||
|
@ -74,5 +81,4 @@ class channel:
|
||||||
|
|
||||||
return -- connected users number
|
return -- connected users number
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return len(self.connectedUsers)
|
return len(self.connectedUsers)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from objects import glob
|
from objects import glob
|
||||||
from objects import channel
|
from objects import channel
|
||||||
|
from helpers import logHelper as log
|
||||||
|
|
||||||
class channelList:
|
class channelList:
|
||||||
"""
|
"""
|
||||||
|
@ -27,14 +28,40 @@ class channelList:
|
||||||
self.addChannel(i["name"], i["description"], publicRead, publicWrite)
|
self.addChannel(i["name"], i["description"], publicRead, publicWrite)
|
||||||
|
|
||||||
|
|
||||||
def addChannel(self, __name, __description, __publicRead, __publicWrite):
|
def addChannel(self, name, description, publicRead, publicWrite, temp = False):
|
||||||
"""
|
"""
|
||||||
Add a channel object to channels dictionary
|
Add a channel object to channels dictionary
|
||||||
|
|
||||||
__name -- channel name
|
name -- channel name
|
||||||
__description -- channel description
|
description -- channel description
|
||||||
__publicRead -- bool, if true channel can be read by everyone, if false it can be read only by mods/admins
|
publicRead -- bool, if true channel can be read by everyone, if false it can be read only by mods/admins
|
||||||
__publicWrite -- bool, same as public read but relative to write permissions
|
publicWrite -- bool, same as public read but relative to write permissions
|
||||||
|
temp -- if True, channel will be deleted when there's no one in the channel. Optional. Default = False.
|
||||||
"""
|
"""
|
||||||
|
self.channels[name] = channel.channel(name, description, publicRead, publicWrite, temp)
|
||||||
|
log.info("Created channel {}".format(name))
|
||||||
|
|
||||||
self.channels[__name] = channel.channel(__name, __description, __publicRead, __publicWrite)
|
|
||||||
|
def addTempChannel(self, name):
|
||||||
|
"""
|
||||||
|
Add a temporary channel (like #spectator or #multiplayer), gets deleted when there's no one in the channel
|
||||||
|
|
||||||
|
name -- channel name
|
||||||
|
return -- True if channel was created, False if failed
|
||||||
|
"""
|
||||||
|
if name in self.channels:
|
||||||
|
return False
|
||||||
|
self.channels[name] = channel.channel(name, "Chat", True, True, True)
|
||||||
|
log.info("Created temp channel {}".format(name))
|
||||||
|
|
||||||
|
def removeChannel(self, name):
|
||||||
|
"""
|
||||||
|
Removes a channel from channels list
|
||||||
|
|
||||||
|
name -- channel name
|
||||||
|
"""
|
||||||
|
if name not in self.channels:
|
||||||
|
log.debug("{} is not in channels list".format(name))
|
||||||
|
return
|
||||||
|
self.channels.pop(name)
|
||||||
|
log.info("Removed channel {}".format(name))
|
||||||
|
|
|
@ -9,6 +9,8 @@ from raven import Client
|
||||||
try:
|
try:
|
||||||
with open("version") as f:
|
with open("version") as f:
|
||||||
VERSION = f.read()
|
VERSION = f.read()
|
||||||
|
if VERSION == "":
|
||||||
|
raise
|
||||||
except:
|
except:
|
||||||
VERSION = "¯\_(xd)_/¯"
|
VERSION = "¯\_(xd)_/¯"
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ from constants import serverPackets
|
||||||
from constants import dataTypes
|
from constants import dataTypes
|
||||||
from constants import matchTeams
|
from constants import matchTeams
|
||||||
from helpers import logHelper as log
|
from helpers import logHelper as log
|
||||||
|
from helpers import chatHelper as chat
|
||||||
|
|
||||||
class match:
|
class match:
|
||||||
"""Multiplayer match object"""
|
"""Multiplayer match object"""
|
||||||
|
@ -61,6 +62,9 @@ class match:
|
||||||
for _ in range(0,16):
|
for _ in range(0,16):
|
||||||
self.slots.append({"status": slotStatuses.free, "team": 0, "userID": -1, "mods": 0, "loaded": False, "skip": False, "complete": False})
|
self.slots.append({"status": slotStatuses.free, "team": 0, "userID": -1, "mods": 0, "loaded": False, "skip": False, "complete": False})
|
||||||
|
|
||||||
|
# Create #multiplayer channel
|
||||||
|
glob.channels.addTempChannel("#multi_{}".format(self.matchID))
|
||||||
|
|
||||||
|
|
||||||
def getMatchData(self):
|
def getMatchData(self):
|
||||||
"""
|
"""
|
||||||
|
@ -577,11 +581,11 @@ class match:
|
||||||
|
|
||||||
# FokaBot is too busy
|
# FokaBot is too busy
|
||||||
if to == 999:
|
if to == 999:
|
||||||
froToken.enqueue(serverPackets.sendMessage("FokaBot", froToken.username, "I would love to join your match, but I'm busy keeping ripple up and running. Sorry. Beep Boop."))
|
chat.sendMessage("FokaBot", froToken.username, "I would love to join your match, but I'm busy keeping ripple up and running. Sorry. Beep Boop.")
|
||||||
|
|
||||||
# Send message
|
# Send message
|
||||||
message = "Come join my multiplayer match: \"[osump://{}/{} {}]\"".format(self.matchID, self.matchPassword.replace(" ", "_"), self.matchName)
|
message = "Come join my multiplayer match: \"[osump://{}/{} {}]\"".format(self.matchID, self.matchPassword.replace(" ", "_"), self.matchName)
|
||||||
toToken.enqueue(serverPackets.sendMessage(froToken.username, toToken.username, message))
|
chat.sendMessage(token=froToken, to=toToken.username, message=message)
|
||||||
|
|
||||||
|
|
||||||
def countUsers(self):
|
def countUsers(self):
|
||||||
|
|
|
@ -9,6 +9,7 @@ import uuid
|
||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
from helpers import logHelper as log
|
from helpers import logHelper as log
|
||||||
|
from helpers import chatHelper as chat
|
||||||
|
|
||||||
class token:
|
class token:
|
||||||
"""
|
"""
|
||||||
|
@ -34,7 +35,7 @@ class token:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, __userID, token = None, ip = ""):
|
def __init__(self, __userID, token = None, ip = "", irc = False):
|
||||||
"""
|
"""
|
||||||
Create a token object and set userID and token
|
Create a token object and set userID and token
|
||||||
|
|
||||||
|
@ -42,6 +43,7 @@ class token:
|
||||||
token -- if passed, set token to that value
|
token -- if passed, set token to that value
|
||||||
if not passed, token will be generated
|
if not passed, token will be generated
|
||||||
ip -- client ip. optional.
|
ip -- client ip. optional.
|
||||||
|
irc -- if True, set this token as IRC client. optional.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Set stuff
|
# Set stuff
|
||||||
|
@ -49,6 +51,7 @@ class token:
|
||||||
self.username = userHelper.getUsername(self.userID)
|
self.username = userHelper.getUsername(self.userID)
|
||||||
self.privileges = userHelper.getPrivileges(self.userID)
|
self.privileges = userHelper.getPrivileges(self.userID)
|
||||||
self.admin = userHelper.isInPrivilegeGroup(self.userID, "developer") or userHelper.isInPrivilegeGroup(self.userID, "community manager")
|
self.admin = userHelper.isInPrivilegeGroup(self.userID, "developer") or userHelper.isInPrivilegeGroup(self.userID, "community manager")
|
||||||
|
self.irc = irc
|
||||||
self.restricted = userHelper.isRestricted(self.userID)
|
self.restricted = userHelper.isRestricted(self.userID)
|
||||||
self.loginTime = int(time.time())
|
self.loginTime = int(time.time())
|
||||||
self.pingTime = self.loginTime
|
self.pingTime = self.loginTime
|
||||||
|
@ -78,7 +81,6 @@ class token:
|
||||||
self.actionMd5 = ""
|
self.actionMd5 = ""
|
||||||
self.actionMods = 0
|
self.actionMods = 0
|
||||||
self.gameMode = gameModes.std
|
self.gameMode = gameModes.std
|
||||||
|
|
||||||
self.rankedScore = 0
|
self.rankedScore = 0
|
||||||
self.accuracy = 0.0
|
self.accuracy = 0.0
|
||||||
self.playcount = 0
|
self.playcount = 0
|
||||||
|
@ -100,8 +102,9 @@ class token:
|
||||||
userHelper.saveBanchoSession(self.userID, self.ip)
|
userHelper.saveBanchoSession(self.userID, self.ip)
|
||||||
|
|
||||||
# If we are restricted, send message from FokaBot to user
|
# If we are restricted, send message from FokaBot to user
|
||||||
if self.restricted == True:
|
# NOTE: Sent later
|
||||||
self.setRestricted()
|
#if self.restricted == True:
|
||||||
|
# self.setRestricted()
|
||||||
|
|
||||||
|
|
||||||
def enqueue(self, __bytes):
|
def enqueue(self, __bytes):
|
||||||
|
@ -110,8 +113,8 @@ class token:
|
||||||
|
|
||||||
__bytes -- (packet) bytes to enqueue
|
__bytes -- (packet) bytes to enqueue
|
||||||
"""
|
"""
|
||||||
|
if self.irc == False:
|
||||||
self.queue += __bytes
|
self.queue += __bytes
|
||||||
|
|
||||||
|
|
||||||
def resetQueue(self):
|
def resetQueue(self):
|
||||||
|
@ -228,11 +231,11 @@ class token:
|
||||||
"""Set match to -1"""
|
"""Set match to -1"""
|
||||||
self.matchID = -1
|
self.matchID = -1
|
||||||
|
|
||||||
def kick(self):
|
def kick(self, message="You have been kicked from the server. Please login again."):
|
||||||
"""Kick this user from the server"""
|
"""Kick this user from the server"""
|
||||||
# Send packet to target
|
# Send packet to target
|
||||||
log.info("{} has been disconnected. (kick)".format(self.username))
|
log.info("{} has been disconnected. (kick)".format(self.username))
|
||||||
self.enqueue(serverPackets.notification("You have been kicked from the server. Please login again."))
|
self.enqueue(serverPackets.notification(message))
|
||||||
self.enqueue(serverPackets.loginFailed())
|
self.enqueue(serverPackets.loginFailed())
|
||||||
|
|
||||||
# Logout event
|
# Logout event
|
||||||
|
@ -301,10 +304,22 @@ class token:
|
||||||
self.gameRank = stats["gameRank"]
|
self.gameRank = stats["gameRank"]
|
||||||
self.pp = stats["pp"]
|
self.pp = stats["pp"]
|
||||||
|
|
||||||
|
def checkRestricted(self, force=False):
|
||||||
|
"""
|
||||||
|
Check if this token is restricted. If so, send fokabot message
|
||||||
|
|
||||||
|
force -- If True, get restricted value from db.
|
||||||
|
If false, get the cached one. Optional. Default: False
|
||||||
|
"""
|
||||||
|
if force == True:
|
||||||
|
self.restricted = userHelper.isRestricted(self.userID)
|
||||||
|
if self.restricted == True:
|
||||||
|
self.setRestricted()
|
||||||
|
|
||||||
def setRestricted(self):
|
def setRestricted(self):
|
||||||
"""
|
"""
|
||||||
Set this token as restricted, send FokaBot message to user
|
Set this token as restricted, send FokaBot message to user
|
||||||
and send offline packet to everyone
|
and send offline packet to everyone
|
||||||
"""
|
"""
|
||||||
self.restricted = True
|
self.restricted = True
|
||||||
self.enqueue(serverPackets.sendMessage("FokaBot", self.username, "Your account is currently in restricted mode. Please visit ripple's website for more information."))
|
chat.sendMessage("FokaBot",self.username, "Your account is currently in restricted mode. Please visit ripple's website for more information.")
|
||||||
|
|
|
@ -19,15 +19,16 @@ class tokenList:
|
||||||
"""
|
"""
|
||||||
self.tokens = {}
|
self.tokens = {}
|
||||||
|
|
||||||
def addToken(self, userID, ip = ""):
|
def addToken(self, userID, ip = "", irc = False):
|
||||||
"""
|
"""
|
||||||
Add a token object to tokens list
|
Add a token object to tokens list
|
||||||
|
|
||||||
userID -- user id associated to that token
|
userID -- user id associated to that token
|
||||||
|
irc -- if True, set this token as IRC client
|
||||||
return -- token object
|
return -- token object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
newToken = osuToken.token(userID, ip=ip)
|
newToken = osuToken.token(userID, ip=ip, irc=irc)
|
||||||
self.tokens[newToken.token] = newToken
|
self.tokens[newToken.token] = newToken
|
||||||
return newToken
|
return newToken
|
||||||
|
|
||||||
|
@ -40,7 +41,8 @@ class tokenList:
|
||||||
|
|
||||||
if token in self.tokens:
|
if token in self.tokens:
|
||||||
# Delete session from DB
|
# Delete session from DB
|
||||||
userHelper.deleteBanchoSessions(self.tokens[token].userID, self.tokens[token].ip)
|
if self.tokens[token].ip != "":
|
||||||
|
userHelper.deleteBanchoSessions(self.tokens[token].userID, self.tokens[token].ip)
|
||||||
|
|
||||||
# Pop token from list
|
# Pop token from list
|
||||||
self.tokens.pop(token)
|
self.tokens.pop(token)
|
||||||
|
@ -108,13 +110,14 @@ class tokenList:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Delete older tokens
|
# Delete older tokens
|
||||||
for key, value in self.tokens.items():
|
for key, value in list(self.tokens.items()):
|
||||||
if value.userID == userID:
|
if value.userID == userID:
|
||||||
# Delete this token from the dictionary
|
# Delete this token from the dictionary
|
||||||
self.tokens.pop(key)
|
self.tokens[key].kick("You have logged in from somewhere else. You can't connect to Bancho/IRC from more than one device at the same time.")
|
||||||
|
#self.tokens.pop(key)
|
||||||
|
|
||||||
# break or items() function throws errors
|
# break or items() function throws errors
|
||||||
break
|
#break
|
||||||
|
|
||||||
|
|
||||||
def multipleEnqueue(self, packet, who, but = False):
|
def multipleEnqueue(self, packet, who, but = False):
|
||||||
|
@ -136,8 +139,6 @@ class tokenList:
|
||||||
if shouldEnqueue:
|
if shouldEnqueue:
|
||||||
value.enqueue(packet)
|
value.enqueue(packet)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def enqueueAll(self, packet):
|
def enqueueAll(self, packet):
|
||||||
"""
|
"""
|
||||||
Enqueue packet(s) to every connected user
|
Enqueue packet(s) to every connected user
|
||||||
|
@ -162,7 +163,7 @@ class tokenList:
|
||||||
timeoutLimit = time.time()-__timeoutTime
|
timeoutLimit = time.time()-__timeoutTime
|
||||||
for key, value in self.tokens.items():
|
for key, value in self.tokens.items():
|
||||||
# Check timeout (fokabot is ignored)
|
# Check timeout (fokabot is ignored)
|
||||||
if value.pingTime < timeoutLimit and value.userID != 999:
|
if value.pingTime < timeoutLimit and value.userID != 999 and value.irc == False:
|
||||||
# That user has timed out, add to disconnected tokens
|
# That user has timed out, add to disconnected tokens
|
||||||
# We can't delete it while iterating or items() throws an error
|
# We can't delete it while iterating or items() throws an error
|
||||||
timedOutTokens.append(key)
|
timedOutTokens.append(key)
|
||||||
|
@ -195,3 +196,18 @@ class tokenList:
|
||||||
Call at bancho startup to delete old cached sessions
|
Call at bancho startup to delete old cached sessions
|
||||||
"""
|
"""
|
||||||
glob.db.execute("TRUNCATE TABLE bancho_sessions")
|
glob.db.execute("TRUNCATE TABLE bancho_sessions")
|
||||||
|
|
||||||
|
def tokenExists(self, username = "", userID = -1):
|
||||||
|
"""
|
||||||
|
Check if a token exists (aka check if someone is connected)
|
||||||
|
|
||||||
|
username -- Optional.
|
||||||
|
userID -- Optional.
|
||||||
|
return -- True if it exists, otherwise False
|
||||||
|
|
||||||
|
Use username or userid, not both at the same time.
|
||||||
|
"""
|
||||||
|
if userID > -1:
|
||||||
|
return True if self.getTokenFromUserID(userID) is not None else False
|
||||||
|
else:
|
||||||
|
return True if self.getTokenFromUsername(username) is not None else False
|
||||||
|
|
26
pep.py
26
pep.py
|
@ -2,6 +2,7 @@
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
from multiprocessing.pool import ThreadPool
|
from multiprocessing.pool import ThreadPool
|
||||||
|
import threading
|
||||||
|
|
||||||
# Tornado
|
# Tornado
|
||||||
import tornado.ioloop
|
import tornado.ioloop
|
||||||
|
@ -30,6 +31,7 @@ from handlers import apiOnlineUsersHandler
|
||||||
from handlers import apiServerStatusHandler
|
from handlers import apiServerStatusHandler
|
||||||
from handlers import ciTriggerHandler
|
from handlers import ciTriggerHandler
|
||||||
|
|
||||||
|
from irc import ircserver
|
||||||
|
|
||||||
def make_app():
|
def make_app():
|
||||||
return tornado.web.Application([
|
return tornado.web.Application([
|
||||||
|
@ -67,8 +69,9 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
# Connect to db
|
# Connect to db
|
||||||
try:
|
try:
|
||||||
print("> Connecting to MySQL db... ")
|
consoleHelper.printNoNl("> Connecting to MySQL db")
|
||||||
glob.db = databaseHelperNew.db(glob.conf.config["db"]["host"], glob.conf.config["db"]["username"], glob.conf.config["db"]["password"], glob.conf.config["db"]["database"], int(glob.conf.config["db"]["workers"]))
|
glob.db = databaseHelperNew.db(glob.conf.config["db"]["host"], glob.conf.config["db"]["username"], glob.conf.config["db"]["password"], glob.conf.config["db"]["database"], int(glob.conf.config["db"]["workers"]))
|
||||||
|
consoleHelper.printNoNl(" ")
|
||||||
consoleHelper.printDone()
|
consoleHelper.printDone()
|
||||||
except:
|
except:
|
||||||
# Exception while connecting to db
|
# Exception while connecting to db
|
||||||
|
@ -109,7 +112,7 @@ if __name__ == "__main__":
|
||||||
consoleHelper.printDone()
|
consoleHelper.printDone()
|
||||||
|
|
||||||
# Initialize chat channels
|
# Initialize chat channels
|
||||||
consoleHelper.printNoNl("> Initializing chat channels... ")
|
print("> Initializing chat channels... ")
|
||||||
glob.channels.loadChannels()
|
glob.channels.loadChannels()
|
||||||
consoleHelper.printDone()
|
consoleHelper.printDone()
|
||||||
|
|
||||||
|
@ -129,7 +132,7 @@ if __name__ == "__main__":
|
||||||
consoleHelper.printDone()
|
consoleHelper.printDone()
|
||||||
|
|
||||||
# Localize warning
|
# Localize warning
|
||||||
glob.localize = generalFunctions.stringToBool(glob.conf.config["server"]["localize"])
|
glob.localize = generalFunctions.stringToBool(glob.conf.config["localize"]["enable"])
|
||||||
if glob.localize == False:
|
if glob.localize == False:
|
||||||
consoleHelper.printColored("[!] Warning! Users localization is disabled!", bcolors.YELLOW)
|
consoleHelper.printColored("[!] Warning! Users localization is disabled!", bcolors.YELLOW)
|
||||||
|
|
||||||
|
@ -151,6 +154,20 @@ if __name__ == "__main__":
|
||||||
if glob.debug == True:
|
if glob.debug == True:
|
||||||
consoleHelper.printColored("[!] Warning! Server running in debug mode!", bcolors.YELLOW)
|
consoleHelper.printColored("[!] Warning! Server running in debug mode!", bcolors.YELLOW)
|
||||||
|
|
||||||
|
# IRC start message and console output
|
||||||
|
glob.irc = generalFunctions.stringToBool(glob.conf.config["irc"]["enable"])
|
||||||
|
if glob.irc == True:
|
||||||
|
# IRC port
|
||||||
|
try:
|
||||||
|
ircPort = int(glob.conf.config["irc"]["port"])
|
||||||
|
except:
|
||||||
|
consoleHelper.printColored("[!] Invalid IRC port! Please check your config.ini and run the server again", bcolors.RED)
|
||||||
|
log.logMessage("IRC server started!", discord=True, of="info.txt", stdout=False)
|
||||||
|
consoleHelper.printColored("> IRC server listening on 127.0.0.1:{}...".format(ircPort), bcolors.GREEN)
|
||||||
|
threading.Thread(target=lambda: ircserver.main(port=ircPort)).start()
|
||||||
|
else:
|
||||||
|
consoleHelper.printColored("[!] Warning! IRC server is disabled!", bcolors.YELLOW)
|
||||||
|
|
||||||
# Server port
|
# Server port
|
||||||
try:
|
try:
|
||||||
serverPort = int(glob.conf.config["server"]["port"])
|
serverPort = int(glob.conf.config["server"]["port"])
|
||||||
|
@ -158,7 +175,6 @@ if __name__ == "__main__":
|
||||||
consoleHelper.printColored("[!] Invalid server port! Please check your config.ini and run the server again", bcolors.RED)
|
consoleHelper.printColored("[!] Invalid server port! Please check your config.ini and run the server again", bcolors.RED)
|
||||||
|
|
||||||
# Make app
|
# Make app
|
||||||
#application = tornado.httpserver.HTTPServer(make_app())
|
|
||||||
application = make_app()
|
application = make_app()
|
||||||
|
|
||||||
# Set up sentry
|
# Set up sentry
|
||||||
|
@ -176,7 +192,7 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
# Server start message and console output
|
# Server start message and console output
|
||||||
log.logMessage("Server started!", discord=True, of="info.txt", stdout=False)
|
log.logMessage("Server started!", discord=True, of="info.txt", stdout=False)
|
||||||
consoleHelper.printColored("> Tornado listening for clients on 127.0.0.1:{}...".format(serverPort), bcolors.GREEN)
|
consoleHelper.printColored("> Tornado listening for HTTP(s) clients on 127.0.0.1:{}...".format(serverPort), bcolors.GREEN)
|
||||||
|
|
||||||
# Start tornado
|
# Start tornado
|
||||||
application.listen(serverPort)
|
application.listen(serverPort)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user