.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):
|
||||
pass
|
||||
|
||||
class haxException(Exception):
|
||||
pass
|
||||
|
|
|
@ -127,7 +127,7 @@ def kick(fro, chan, message):
|
|||
def fokabotReconnect(fro, chan, message):
|
||||
# Check if fokabot is already connected
|
||||
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.connect()
|
||||
|
|
|
@ -130,16 +130,9 @@ def userStats(userID, force = False):
|
|||
# Get userID's token from tokens list
|
||||
userToken = glob.tokens.getTokenFromUserID(userID)
|
||||
if userToken == None:
|
||||
return bytes() # NOTE: ???
|
||||
if userToken.restricted == True and force == False:
|
||||
return bytes()
|
||||
# Stats are cached in token object
|
||||
#rankedScore = userHelper.getRankedScore(userID, userToken.gameMode)
|
||||
#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))
|
||||
if (userToken.restricted == True or userToken.irc == True) and force == False:
|
||||
return bytes()
|
||||
return packetHelper.buildPacket(packetIDs.server_userStats,
|
||||
[
|
||||
[userID, dataTypes.uInt32],
|
||||
|
|
|
@ -4,6 +4,7 @@ from constants import serverPackets
|
|||
from helpers import userHelper
|
||||
from helpers import logHelper as log
|
||||
from constants import actions
|
||||
from helpers import chatHelper as chat
|
||||
|
||||
def handle(userToken, packetData):
|
||||
# Get usertoken data
|
||||
|
@ -58,7 +59,7 @@ def handle(userToken, packetData):
|
|||
# NOTE: Remove this when osu!direct will be fixed
|
||||
if userToken.actionID == actions.osuDirect and userToken.osuDirectAlert == False:
|
||||
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
|
||||
|
|
|
@ -1,56 +1,7 @@
|
|||
"""
|
||||
Event called when someone joins a channel
|
||||
"""
|
||||
|
||||
from constants import clientPackets
|
||||
from helpers import consoleHelper
|
||||
from constants import bcolors
|
||||
from constants import serverPackets
|
||||
from objects import glob
|
||||
from constants import exceptions
|
||||
from helpers import logHelper as log
|
||||
from helpers import chatHelper as chat
|
||||
|
||||
def handle(userToken, packetData):
|
||||
# Channel join packet
|
||||
packetData = clientPackets.channelJoin(packetData)
|
||||
joinChannel(userToken, 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))
|
||||
chat.joinChannel(token=userToken, channel=packetData["channel"])
|
||||
|
|
|
@ -8,13 +8,14 @@ from objects import glob
|
|||
from constants import clientPackets
|
||||
from constants import serverPackets
|
||||
from helpers import logHelper as log
|
||||
from helpers import chatHelper as chat
|
||||
|
||||
def handle(userToken, packetData):
|
||||
# Channel part packet
|
||||
# Channel join packet
|
||||
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
|
||||
username = userToken.username
|
||||
userID = userToken.userID
|
||||
|
@ -34,4 +35,4 @@ def partChannel(userToken, channelName, kick = False):
|
|||
userToken.enqueue(serverPackets.channelKicked(channelName))
|
||||
|
||||
# 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 constants import exceptions
|
||||
from helpers import logHelper as log
|
||||
from helpers import chatHelper as chat
|
||||
|
||||
def handle(userToken, packetData):
|
||||
# read packet data
|
||||
|
@ -44,8 +45,7 @@ def joinMatch(userToken, matchID, password):
|
|||
|
||||
# Send packets
|
||||
userToken.enqueue(serverPackets.matchJoinSuccess(matchID))
|
||||
userToken.enqueue(serverPackets.channelJoinSuccess(userID, "#multiplayer"))
|
||||
#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)))
|
||||
chat.joinChannel(token=userToken, channel="#multi_{}".format(matchID))
|
||||
except exceptions.matchNotFoundException:
|
||||
userToken.enqueue(serverPackets.matchJoinFail())
|
||||
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
|
||||
import time
|
||||
from helpers import generalFunctions
|
||||
from events import channelJoinEvent
|
||||
import sys
|
||||
import traceback
|
||||
from helpers import requestHelper
|
||||
from helpers import discordBotHelper
|
||||
from helpers import logHelper as log
|
||||
from helpers import chatHelper as chat
|
||||
|
||||
def handle(tornadoRequest):
|
||||
# Data to return
|
||||
|
@ -30,6 +30,10 @@ def handle(tornadoRequest):
|
|||
# If true, print error to console
|
||||
err = False
|
||||
|
||||
# Make sure loginData is valid
|
||||
if len(loginData) < 3:
|
||||
raise exceptions.haxException()
|
||||
|
||||
# Try to get the ID from username
|
||||
username = str(loginData[0])
|
||||
userID = userHelper.getID(username)
|
||||
|
@ -59,6 +63,9 @@ def handle(tornadoRequest):
|
|||
responseToken = glob.tokens.addToken(userID, requestIP)
|
||||
responseTokenString = responseToken.token
|
||||
|
||||
# Check restricted mode (and eventually send message)
|
||||
responseToken.checkRestricted()
|
||||
|
||||
# Set silence end UNIX time in token
|
||||
responseToken.silenceEndTime = userHelper.getSilenceEnd(userID)
|
||||
|
||||
|
@ -101,12 +108,12 @@ def handle(tornadoRequest):
|
|||
responseToken.enqueue(serverPackets.channelInfoEnd())
|
||||
# Default opened channels
|
||||
# TODO: Configurable default channels
|
||||
channelJoinEvent.joinChannel(responseToken, "#osu")
|
||||
channelJoinEvent.joinChannel(responseToken, "#announce")
|
||||
chat.joinChannel(token=responseToken, channel="#osu")
|
||||
chat.joinChannel(token=responseToken, channel="#announce")
|
||||
|
||||
# Join admin channel if we are an admin
|
||||
if responseToken.admin == True:
|
||||
channelJoinEvent.joinChannel(responseToken, "#admin")
|
||||
chat.joinChannel(token=responseToken, channel="#admin")
|
||||
|
||||
# Output channels info
|
||||
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)
|
||||
err = True
|
||||
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:
|
||||
# Login banned error packet
|
||||
err = True
|
||||
|
@ -172,9 +185,14 @@ def handle(tornadoRequest):
|
|||
except exceptions.need2FAException:
|
||||
# User tried to log in from unknown IP
|
||||
responseData += serverPackets.needVerification()
|
||||
except:
|
||||
log.error("Unknown error!\n```\n{}\n{}```".format(sys.exc_info(), traceback.format_exc()))
|
||||
finally:
|
||||
# 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)
|
||||
|
||||
# Return token string and data
|
||||
|
|
|
@ -4,8 +4,9 @@ from constants import bcolors
|
|||
from constants import serverPackets
|
||||
import time
|
||||
from helpers import logHelper as log
|
||||
from helpers import chatHelper as chat
|
||||
|
||||
def handle(userToken, _):
|
||||
def handle(userToken, _=None):
|
||||
# get usertoken data
|
||||
userID = userToken.userID
|
||||
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 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 int(time.time()-userToken.loginTime) >= 5:
|
||||
if int(time.time()-userToken.loginTime) >= 5 or userToken.irc == True:
|
||||
# Stop spectating if needed
|
||||
# TODO: Call stopSpectatingEvent!!!!!!!!!
|
||||
if userToken.spectating != 0:
|
||||
# The user was spectating someone
|
||||
spectatorHostToken = glob.tokens.getTokenFromUserID(userToken.spectating)
|
||||
|
@ -26,13 +28,17 @@ def handle(userToken, _):
|
|||
|
||||
# Part all joined channels
|
||||
for i in userToken.joinedChannels:
|
||||
glob.channels.channels[i].userPart(userID)
|
||||
chat.partChannel(token=userToken, channel=i)
|
||||
|
||||
# TODO: Lobby left if joined
|
||||
|
||||
# Enqueue our disconnection to everyone else
|
||||
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
|
||||
glob.tokens.deleteToken(requestToken)
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from objects import glob
|
||||
from events import channelPartEvent
|
||||
from helpers import logHelper as log
|
||||
from helpers import chatHelper as chat
|
||||
|
||||
def handle(userToken, _):
|
||||
# Get usertoken data
|
||||
|
@ -11,7 +12,7 @@ def handle(userToken, _):
|
|||
glob.matches.lobbyUserPart(userID)
|
||||
|
||||
# Part lobby channel
|
||||
channelPartEvent.partChannel(userToken, "#lobby", True)
|
||||
chat.partChannel(channel="#lobby", token=userToken, kick=True)
|
||||
|
||||
# Console output
|
||||
log.info("{} has left multiplayer lobby".format(username))
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from objects import glob
|
||||
from helpers import chatHelper as chat
|
||||
from constants import serverPackets
|
||||
|
||||
def handle(userToken, _):
|
||||
|
@ -22,6 +23,8 @@ def handle(userToken, _):
|
|||
# Set slot to free
|
||||
match.userLeft(userID)
|
||||
|
||||
# Part #multiplayer channel
|
||||
chat.partChannel(token=userToken, channel="#multi_{}".format(matchID))
|
||||
|
||||
# Set usertoken match to -1
|
||||
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 serverPackets
|
||||
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
|
||||
from helpers import chatHelper as chat
|
||||
|
||||
def handle(userToken, packetData):
|
||||
"""
|
||||
Event called when someone sends a private 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
|
||||
# Send private message packet
|
||||
packetData = clientPackets.sendPrivateMessage(packetData)
|
||||
chat.sendMessage(token=userToken, to=packetData["to"], message=packetData["message"])
|
||||
|
|
|
@ -1,135 +1,7 @@
|
|||
from constants import exceptions
|
||||
from constants import clientPackets
|
||||
from objects import glob
|
||||
from objects import fokabot
|
||||
from constants import serverPackets
|
||||
from helpers import discordBotHelper
|
||||
from helpers import logHelper as log
|
||||
from helpers import userHelper
|
||||
import time
|
||||
from helpers import chatHelper as chat
|
||||
|
||||
def handle(userToken, packetData):
|
||||
"""
|
||||
Event called when someone sends a public 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
|
||||
# Send public message packet
|
||||
packetData = clientPackets.sendPublicMessage(packetData)
|
||||
chat.sendMessage(token=userToken, to=packetData["to"], message=packetData["message"])
|
||||
|
|
|
@ -4,6 +4,7 @@ from constants import exceptions
|
|||
from objects import glob
|
||||
from helpers import userHelper
|
||||
from helpers import logHelper as log
|
||||
from helpers import chatHelper as chat
|
||||
|
||||
def handle(userToken, packetData):
|
||||
try:
|
||||
|
@ -34,12 +35,12 @@ def handle(userToken, packetData):
|
|||
# Send spectator join packet to host
|
||||
targetToken.enqueue(serverPackets.addSpectator(userID))
|
||||
|
||||
# Join #spectator channel
|
||||
userToken.enqueue(serverPackets.channelJoinSuccess(userID, "#spectator"))
|
||||
|
||||
# Create and join #spectator (#spect_userid) channel
|
||||
glob.channels.addTempChannel("#spect_{}".format(targetToken.userID))
|
||||
chat.joinChannel(token=userToken, channel="#spect_{}".format(targetToken.userID))
|
||||
if len(targetToken.spectators) == 1:
|
||||
# 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
|
||||
for spec in targetToken.spectators:
|
||||
|
|
|
@ -2,6 +2,7 @@ from objects import glob
|
|||
from constants import serverPackets
|
||||
from constants import exceptions
|
||||
from helpers import logHelper as log
|
||||
from helpers import chatHelper as chat
|
||||
|
||||
def handle(userToken, _):
|
||||
try:
|
||||
|
@ -16,6 +17,9 @@ def handle(userToken, _):
|
|||
raise exceptions.tokenNotFoundException
|
||||
targetToken.removeSpectator(userID)
|
||||
|
||||
# Part #spectator channel
|
||||
chat.partChannel(token=userToken, channel="#spect_{}".format(target))
|
||||
|
||||
# Send the spectator left packet to host
|
||||
targetToken.enqueue(serverPackets.removeSpectator(userID))
|
||||
for c in targetToken.spectators:
|
||||
|
@ -25,8 +29,7 @@ def handle(userToken, _):
|
|||
#targetToken.enqueue(serverPackets.fellowSpectatorLeft(userID))
|
||||
|
||||
# Console output
|
||||
# TODO: Move messages in stop spectating
|
||||
log.info("{} are no longer spectating whoever they were spectating".format(username))
|
||||
log.info("{} are no longer spectating {}".format(username, target))
|
||||
except exceptions.tokenNotFoundException:
|
||||
log.warning("Spectator stop: token not found")
|
||||
finally:
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import datetime
|
||||
import gzip
|
||||
import time
|
||||
from helpers import generalFunctions
|
||||
from helpers import requestHelper
|
||||
from objects import glob
|
||||
from helpers import consoleHelper
|
||||
|
@ -257,7 +259,7 @@ class handler(SentryMixin, requestHelper.asyncRequestHandler):
|
|||
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)
|
||||
#yield tornado.gen.Task(self.captureMessage, "test")
|
||||
#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","gzip")
|
||||
self.config.get("server","gziplevel")
|
||||
self.config.get("server","localize")
|
||||
self.config.get("server","cikey")
|
||||
self.config.get("server","cloudflare")
|
||||
|
||||
|
@ -66,6 +65,12 @@ class config:
|
|||
self.config.get("discord","enable")
|
||||
self.config.get("discord","boturl")
|
||||
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
|
||||
except:
|
||||
return False
|
||||
|
@ -91,7 +96,6 @@ class config:
|
|||
self.config.set("server", "threads", "16")
|
||||
self.config.set("server", "gzip", "1")
|
||||
self.config.set("server", "gziplevel", "6")
|
||||
self.config.set("server", "localize", "1")
|
||||
self.config.set("server", "cikey", "changeme")
|
||||
self.config.set("server", "cloudflare", "0")
|
||||
|
||||
|
@ -109,6 +113,14 @@ class config:
|
|||
self.config.set("discord", "boturl", "")
|
||||
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
|
||||
self.config.write(f)
|
||||
f.close()
|
||||
|
|
|
@ -44,7 +44,7 @@ class db:
|
|||
self.lastWorker = 0
|
||||
self.workersNumber = workers
|
||||
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))
|
||||
|
||||
def getWorker(self):
|
||||
|
|
|
@ -21,7 +21,7 @@ def hexString(s):
|
|||
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):
|
||||
"""
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
import urllib.request
|
||||
import json
|
||||
from objects import glob
|
||||
|
||||
from helpers import logHelper as log
|
||||
|
||||
# API URL
|
||||
URL = "http://ip.zxq.co/"
|
||||
|
||||
|
||||
def getCountry(ip):
|
||||
"""
|
||||
Get country from IP address
|
||||
|
@ -17,7 +14,7 @@ def getCountry(ip):
|
|||
|
||||
try:
|
||||
# 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()
|
||||
except:
|
||||
log.error("Error in get country")
|
||||
|
@ -34,7 +31,7 @@ def getLocation(ip):
|
|||
|
||||
try:
|
||||
# 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])]
|
||||
except:
|
||||
log.error("Error in get position")
|
||||
|
|
|
@ -118,7 +118,7 @@ def packData(__data, __dataType):
|
|||
# Non empty string
|
||||
data += b"\x0B"
|
||||
data += uleb128Encode(len(__data))
|
||||
data += str.encode(__data, "latin_1")
|
||||
data += str.encode(__data, "latin_1", "ignore")
|
||||
elif __dataType == dataTypes.uInt16:
|
||||
packType = "<H"
|
||||
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:
|
||||
"""
|
||||
A chat channel
|
||||
|
@ -10,15 +12,7 @@ class channel:
|
|||
moderated -- bool
|
||||
"""
|
||||
|
||||
name = ""
|
||||
description = ""
|
||||
connectedUsers = []
|
||||
|
||||
publicRead = False
|
||||
publicWrite = False
|
||||
moderated = False
|
||||
|
||||
def __init__(self, __name, __description, __publicRead, __publicWrite):
|
||||
def __init__(self, __name, __description, __publicRead, __publicWrite, temp):
|
||||
"""
|
||||
Create a new chat channel object
|
||||
|
||||
|
@ -26,13 +20,23 @@ class channel:
|
|||
__description -- channel description
|
||||
__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
|
||||
temp -- if True, channel will be deleted when there's no one in the channel. Optional. Default = False.
|
||||
"""
|
||||
|
||||
self.name = __name
|
||||
self.description = __description
|
||||
self.publicRead = __publicRead
|
||||
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):
|
||||
|
@ -53,9 +57,13 @@ class channel:
|
|||
__userID -- user ID that left the channel
|
||||
"""
|
||||
|
||||
connectedUsers = self.connectedUsers
|
||||
if __userID in connectedUsers:
|
||||
connectedUsers.remove(__userID)
|
||||
if __userID in self.connectedUsers:
|
||||
self.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):
|
||||
|
@ -64,7 +72,6 @@ class channel:
|
|||
|
||||
return -- connectedUsers list
|
||||
"""
|
||||
|
||||
return self.connectedUsers
|
||||
|
||||
|
||||
|
@ -74,5 +81,4 @@ class channel:
|
|||
|
||||
return -- connected users number
|
||||
"""
|
||||
|
||||
return len(self.connectedUsers)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from objects import glob
|
||||
from objects import channel
|
||||
from helpers import logHelper as log
|
||||
|
||||
class channelList:
|
||||
"""
|
||||
|
@ -27,14 +28,40 @@ class channelList:
|
|||
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
|
||||
|
||||
__name -- channel name
|
||||
__description -- channel description
|
||||
__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
|
||||
name -- channel name
|
||||
description -- channel description
|
||||
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
|
||||
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:
|
||||
with open("version") as f:
|
||||
VERSION = f.read()
|
||||
if VERSION == "":
|
||||
raise
|
||||
except:
|
||||
VERSION = "¯\_(xd)_/¯"
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ from constants import serverPackets
|
|||
from constants import dataTypes
|
||||
from constants import matchTeams
|
||||
from helpers import logHelper as log
|
||||
from helpers import chatHelper as chat
|
||||
|
||||
class match:
|
||||
"""Multiplayer match object"""
|
||||
|
@ -61,6 +62,9 @@ class match:
|
|||
for _ in range(0,16):
|
||||
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):
|
||||
"""
|
||||
|
@ -577,11 +581,11 @@ class match:
|
|||
|
||||
# FokaBot is too busy
|
||||
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
|
||||
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):
|
||||
|
|
|
@ -9,6 +9,7 @@ import uuid
|
|||
import time
|
||||
import threading
|
||||
from helpers import logHelper as log
|
||||
from helpers import chatHelper as chat
|
||||
|
||||
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
|
||||
|
||||
|
@ -42,6 +43,7 @@ class token:
|
|||
token -- if passed, set token to that value
|
||||
if not passed, token will be generated
|
||||
ip -- client ip. optional.
|
||||
irc -- if True, set this token as IRC client. optional.
|
||||
"""
|
||||
|
||||
# Set stuff
|
||||
|
@ -49,6 +51,7 @@ class token:
|
|||
self.username = userHelper.getUsername(self.userID)
|
||||
self.privileges = userHelper.getPrivileges(self.userID)
|
||||
self.admin = userHelper.isInPrivilegeGroup(self.userID, "developer") or userHelper.isInPrivilegeGroup(self.userID, "community manager")
|
||||
self.irc = irc
|
||||
self.restricted = userHelper.isRestricted(self.userID)
|
||||
self.loginTime = int(time.time())
|
||||
self.pingTime = self.loginTime
|
||||
|
@ -78,7 +81,6 @@ class token:
|
|||
self.actionMd5 = ""
|
||||
self.actionMods = 0
|
||||
self.gameMode = gameModes.std
|
||||
|
||||
self.rankedScore = 0
|
||||
self.accuracy = 0.0
|
||||
self.playcount = 0
|
||||
|
@ -100,8 +102,9 @@ class token:
|
|||
userHelper.saveBanchoSession(self.userID, self.ip)
|
||||
|
||||
# If we are restricted, send message from FokaBot to user
|
||||
if self.restricted == True:
|
||||
self.setRestricted()
|
||||
# NOTE: Sent later
|
||||
#if self.restricted == True:
|
||||
# self.setRestricted()
|
||||
|
||||
|
||||
def enqueue(self, __bytes):
|
||||
|
@ -110,8 +113,8 @@ class token:
|
|||
|
||||
__bytes -- (packet) bytes to enqueue
|
||||
"""
|
||||
|
||||
self.queue += __bytes
|
||||
if self.irc == False:
|
||||
self.queue += __bytes
|
||||
|
||||
|
||||
def resetQueue(self):
|
||||
|
@ -228,11 +231,11 @@ class token:
|
|||
"""Set match to -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"""
|
||||
# Send packet to target
|
||||
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())
|
||||
|
||||
# Logout event
|
||||
|
@ -301,10 +304,22 @@ class token:
|
|||
self.gameRank = stats["gameRank"]
|
||||
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):
|
||||
"""
|
||||
Set this token as restricted, send FokaBot message to user
|
||||
and send offline packet to everyone
|
||||
"""
|
||||
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 = {}
|
||||
|
||||
def addToken(self, userID, ip = ""):
|
||||
def addToken(self, userID, ip = "", irc = False):
|
||||
"""
|
||||
Add a token object to tokens list
|
||||
|
||||
userID -- user id associated to that token
|
||||
irc -- if True, set this token as IRC client
|
||||
return -- token object
|
||||
"""
|
||||
|
||||
newToken = osuToken.token(userID, ip=ip)
|
||||
newToken = osuToken.token(userID, ip=ip, irc=irc)
|
||||
self.tokens[newToken.token] = newToken
|
||||
return newToken
|
||||
|
||||
|
@ -40,7 +41,8 @@ class tokenList:
|
|||
|
||||
if token in self.tokens:
|
||||
# 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
|
||||
self.tokens.pop(token)
|
||||
|
@ -108,13 +110,14 @@ class tokenList:
|
|||
"""
|
||||
|
||||
# Delete older tokens
|
||||
for key, value in self.tokens.items():
|
||||
for key, value in list(self.tokens.items()):
|
||||
if value.userID == userID:
|
||||
# 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
|
||||
#break
|
||||
|
||||
|
||||
def multipleEnqueue(self, packet, who, but = False):
|
||||
|
@ -136,8 +139,6 @@ class tokenList:
|
|||
if shouldEnqueue:
|
||||
value.enqueue(packet)
|
||||
|
||||
|
||||
|
||||
def enqueueAll(self, packet):
|
||||
"""
|
||||
Enqueue packet(s) to every connected user
|
||||
|
@ -162,7 +163,7 @@ class tokenList:
|
|||
timeoutLimit = time.time()-__timeoutTime
|
||||
for key, value in self.tokens.items():
|
||||
# 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
|
||||
# We can't delete it while iterating or items() throws an error
|
||||
timedOutTokens.append(key)
|
||||
|
@ -195,3 +196,18 @@ class tokenList:
|
|||
Call at bancho startup to delete old cached 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 os
|
||||
from multiprocessing.pool import ThreadPool
|
||||
import threading
|
||||
|
||||
# Tornado
|
||||
import tornado.ioloop
|
||||
|
@ -30,6 +31,7 @@ from handlers import apiOnlineUsersHandler
|
|||
from handlers import apiServerStatusHandler
|
||||
from handlers import ciTriggerHandler
|
||||
|
||||
from irc import ircserver
|
||||
|
||||
def make_app():
|
||||
return tornado.web.Application([
|
||||
|
@ -67,8 +69,9 @@ if __name__ == "__main__":
|
|||
|
||||
# Connect to db
|
||||
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"]))
|
||||
consoleHelper.printNoNl(" ")
|
||||
consoleHelper.printDone()
|
||||
except:
|
||||
# Exception while connecting to db
|
||||
|
@ -109,7 +112,7 @@ if __name__ == "__main__":
|
|||
consoleHelper.printDone()
|
||||
|
||||
# Initialize chat channels
|
||||
consoleHelper.printNoNl("> Initializing chat channels... ")
|
||||
print("> Initializing chat channels... ")
|
||||
glob.channels.loadChannels()
|
||||
consoleHelper.printDone()
|
||||
|
||||
|
@ -129,7 +132,7 @@ if __name__ == "__main__":
|
|||
consoleHelper.printDone()
|
||||
|
||||
# Localize warning
|
||||
glob.localize = generalFunctions.stringToBool(glob.conf.config["server"]["localize"])
|
||||
glob.localize = generalFunctions.stringToBool(glob.conf.config["localize"]["enable"])
|
||||
if glob.localize == False:
|
||||
consoleHelper.printColored("[!] Warning! Users localization is disabled!", bcolors.YELLOW)
|
||||
|
||||
|
@ -151,6 +154,20 @@ if __name__ == "__main__":
|
|||
if glob.debug == True:
|
||||
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
|
||||
try:
|
||||
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)
|
||||
|
||||
# Make app
|
||||
#application = tornado.httpserver.HTTPServer(make_app())
|
||||
application = make_app()
|
||||
|
||||
# Set up sentry
|
||||
|
@ -176,7 +192,7 @@ if __name__ == "__main__":
|
|||
|
||||
# Server start message and console output
|
||||
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
|
||||
application.listen(serverPort)
|
||||
|
|
Loading…
Reference in New Issue
Block a user