.HIDE. General refactoring and documentation

This commit is contained in:
Nyo
2016-11-17 19:13:06 +01:00
parent abad698fe3
commit a2ef03c887
38 changed files with 597 additions and 449 deletions

View File

@@ -1,20 +1,16 @@
from objects import glob
class channel:
"""
A chat channel
"""
def __init__(self, name, description, publicRead, publicWrite, temp, hidden):
"""
Create a new chat channel object
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
hidden -- if True, channel won't be shown in channels list
:param name: channel name
:param description: channel description
:param publicRead: if True, this channel can be read by everyone. If False, it can be read only by mods/admins
:param publicWrite: same as public read, but regards writing permissions
:param temp: if True, this channel will be deleted when there's no one in this channel
:param hidden: if True, thic channel won't be shown in channels list
"""
self.name = name
self.description = description
@@ -36,7 +32,8 @@ class channel:
"""
Add a user to connected users
userID -- user ID that joined the channel
:param userID:
:return:
"""
if userID not in self.connectedUsers:
self.connectedUsers.append(userID)
@@ -45,7 +42,8 @@ class channel:
"""
Remove a user from connected users
userID -- user ID that left the channel
:param userID:
:return:
"""
if userID in self.connectedUsers:
self.connectedUsers.remove(userID)
@@ -53,20 +51,4 @@ class channel:
# 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):
"""
Get connected user IDs list
return -- connectedUsers list
"""
return self.connectedUsers
def getConnectedUsersCount(self):
"""
Count connected users
return -- connected users number
"""
return len(self.connectedUsers)
glob.channels.removeChannel(self.name)

View File

@@ -4,18 +4,14 @@ from objects import glob
class channelList:
"""
Channel list
channels -- dictionary. key: channel name, value: channel object
"""
channels = {}
def __init__(self):
self.channels = {}
def loadChannels(self):
"""
Load chat channels from db and add them to channels dictionary
Load chat channels from db and add them to channels list
:return:
"""
# Get channels from DB
channels = glob.db.fetchAll("SELECT * FROM bancho_channels")
@@ -28,14 +24,15 @@ class channelList:
def addChannel(self, name, description, publicRead, publicWrite, temp = False, hidden = False):
"""
Add a channel object to channels dictionary
Add a channel to channels list
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.
hidden -- if True, channel will be hidden in channels list. Optional. Default = False.
:param name: channel name
:param description: channel description
:param publicRead: if True, this channel can be read by everyone. If False, it can be read only by mods/admins
:param publicWrite: same as public read, but regards writing permissions
:param temp: if True, this channel will be deleted when there's no one in this channel
:param hidden: if True, thic channel won't be shown in channels list
:return:
"""
self.channels[name] = channel.channel(name, description, publicRead, publicWrite, temp, hidden)
log.info("Created channel {}".format(name))
@@ -45,8 +42,8 @@ class channelList:
Add a temporary channel (like #spectator or #multiplayer), gets deleted when there's no one in the channel
and it's hidden in channels list
name -- channel name
return -- True if channel was created, False if failed
:param name: channel name
:return: True if the channel was created, otherwise False
"""
if name in self.channels:
return False
@@ -57,7 +54,8 @@ class channelList:
"""
Removes a channel from channels list
name -- channel name
:param name: channel name
:return:
"""
if name not in self.channels:
log.debug("{} is not in channels list".format(name))

View File

@@ -1,9 +1,20 @@
class chatFilters:
def __init__(self, fileName="filters.txt"):
"""
Initialize chat filters
:param fileName: name of the file containing filters. Default: filters.txt
"""
self.filters = {}
self.loadFilters(fileName)
def loadFilters(self, fileName="filters.txt"):
"""
Load filters from a file
:param fileName: name of the file containing filters. Default: filters.txt
:return:
"""
# Reset chat filters
self.filters = {}
@@ -19,6 +30,12 @@ class chatFilters:
self.filters[lineSplit[0].lower()] = lineSplit[1].replace("\n", "")
def filterMessage(self, message):
"""
Replace forbidden words with filtered ones
:param message: normal message
:return: filtered message
"""
# Split words by spaces
messageTemp = message.split(" ")

View File

@@ -12,29 +12,35 @@ from objects import glob
npRegex = re.compile("^https?:\\/\\/osu\\.ppy\\.sh\\/b\\/(\\d*)")
def connect():
"""Add FokaBot to connected users and send userpanel/stats packet to everyone"""
"""
Connect FokaBot to Bancho
:return:
"""
token = glob.tokens.addToken(999)
token.actionID = actions.IDLE
glob.streams.broadcast("main", serverPackets.userPanel(999))
glob.streams.broadcast("main", serverPackets.userStats(999))
def disconnect():
"""Remove FokaBot from connected users"""
"""
Disconnect FokaBot from Bancho
:return:
"""
glob.tokens.deleteToken(glob.tokens.getTokenFromUserID(999))
def fokabotResponse(fro, chan, message):
"""
Check if a message has triggered fokabot (and return its response)
Check if a message has triggered FokaBot
fro -- sender username (for permissions stuff with admin commands)
chan -- channel name
message -- message
return -- fokabot's response string or False
:param fro: sender username
:param chan: channel name (or receiver username)
:param message: chat mesage
:return: FokaBot's response or False if no response
"""
for i in fokabotCommands.commands:
# Loop though all commands
#if i["trigger"] in message:
if generalUtils.strContains(message, i["trigger"]):
# message has triggered a command

View File

@@ -13,7 +13,7 @@ try:
with open("version") as f:
VERSION = f.read()
if VERSION == "":
raise
raise Exception
except:
VERSION = "¯\_(xd)_/¯"

View File

@@ -13,7 +13,7 @@ from objects import glob
class slot:
def __init__(self):
self.status = slotStatuses.free
self.status = slotStatuses.FREE
self.team = 0
self.userID = -1
self.user = None
@@ -23,19 +23,18 @@ class slot:
self.complete = False
class match:
"""Multiplayer match object"""
def __init__(self, matchID, matchName, matchPassword, beatmapID, beatmapName, beatmapMD5, gameMode, hostUserID):
"""
Create a new match object
matchID -- match progressive identifier
matchName -- match name, string
matchPassword -- match md5 password. Leave empty for no password
beatmapID -- beatmap ID
beatmapName -- beatmap name, string
beatmapMD5 -- beatmap md5 hash, string
gameMode -- game mode ID. See gameModes.py
hostUserID -- user id of the host
:param matchID: match progressive identifier
:param matchName: match name, string
:param matchPassword: match md5 password. Leave empty for no password
:param beatmapID: beatmap ID
:param beatmapName: beatmap name, string
:param beatmapMD5: beatmap md5 hash, string
:param gameMode: game mode ID. See gameModes.py
:param hostUserID: user id of the host
"""
self.matchID = matchID
self.streamName = "multi/{}".format(self.matchID)
@@ -49,9 +48,9 @@ class match:
self.beatmapMD5 = beatmapMD5
self.hostUserID = hostUserID
self.gameMode = gameMode
self.matchScoringType = matchScoringTypes.score # default values
self.matchTeamType = matchTeamTypes.headToHead # default value
self.matchModMode = matchModModes.normal # default value
self.matchScoringType = matchScoringTypes.SCORE # default values
self.matchTeamType = matchTeamTypes.HEAD_TO_HEAD # default value
self.matchModMode = matchModModes.NORMAL # default value
self.seed = 0
self.matchDataCache = bytes()
@@ -70,6 +69,8 @@ class match:
def getMatchData(self):
"""
Return binary match data structure for packetHelper
:return:
"""
# General match info
# TODO: Test without safe copy, the error might have been caused by outdated python bytecode cache
@@ -109,7 +110,7 @@ class match:
])
# Slot mods if free mod is enabled
if safeMatch.matchModMode == matchModModes.freeMod:
if safeMatch.matchModMode == matchModModes.FREE_MOD:
for i in range(0,16):
struct.append([safeMatch.slots[i].mods, dataTypes.UINT32])
@@ -123,7 +124,8 @@ class match:
"""
Set room host to newHost and send him host packet
newHost -- new host userID
:param newHost: new host userID
:return:
"""
slotID = self.getUserSlotID(newHost)
if slotID is None or self.slots[slotID].user not in glob.tokens.tokens:
@@ -135,7 +137,21 @@ class match:
log.info("MPROOM{}: {} is now the host".format(self.matchID, token.username))
def setSlot(self, slotID, status = None, team = None, user = "", mods = None, loaded = None, skip = None, complete = None):
#self.setSlot(i, slotStatuses.notReady, 0, user, 0)
"""
Set data for a specific slot.
All fields but slotID are optional.
Skipped fields won't be edited.
:param slotID: slot ID
:param status: new status
:param team: new team
:param user: new user id
:param mods: new mods
:param loaded: new loaded status
:param skip: new skip value
:param complete: new completed value
:return:
"""
if status is not None:
self.slots[slotID].status = status
@@ -161,8 +177,9 @@ class match:
"""
Set slotID mods. Same as calling setSlot and then sendUpdate
slotID -- slot number
mods -- new mods
:param slotID: slot number
:param mods: new mods
:return:
"""
# Set new slot data and send update
self.setSlot(slotID, mods=mods)
@@ -174,14 +191,15 @@ class match:
Switch slotID ready/not ready status
Same as calling setSlot and then sendUpdate
slotID -- slot number
:param slotID: slot number
:return:
"""
# Update ready status and setnd update
oldStatus = self.slots[slotID].status
if oldStatus == slotStatuses.ready:
newStatus = slotStatuses.notReady
if oldStatus == slotStatuses.READY:
newStatus = slotStatuses.NOT_READY
else:
newStatus = slotStatuses.ready
newStatus = slotStatuses.READY
self.setSlot(slotID, newStatus)
self.sendUpdates()
log.info("MPROOM{}: Slot{} changed ready status to {}".format(self.matchID, slotID, self.slots[slotID].status))
@@ -191,13 +209,14 @@ class match:
Lock a slot
Same as calling setSlot and then sendUpdate
slotID -- slot number
:param slotID: slot number
:return:
"""
# Check if slot is already locked
if self.slots[slotID].status == slotStatuses.locked:
newStatus = slotStatuses.free
if self.slots[slotID].status == slotStatuses.LOCKED:
newStatus = slotStatuses.FREE
else:
newStatus = slotStatuses.locked
newStatus = slotStatuses.LOCKED
# Send updated settings to kicked user, so he returns to lobby
if self.slots[slotID].user is not None and self.slots[slotID].user in glob.tokens.tokens:
@@ -208,13 +227,14 @@ class match:
# Send updates to everyone else
self.sendUpdates()
log.info("MPROOM{}: Slot{} {}".format(self.matchID, slotID, "locked" if newStatus == slotStatuses.locked else "unlocked"))
log.info("MPROOM{}: Slot{} {}".format(self.matchID, slotID, "locked" if newStatus == slotStatuses.LOCKED else "unlocked"))
def playerLoaded(self, userID):
"""
Set a player loaded status to True
userID -- ID of user
:param userID: ID of user
:return:
"""
slotID = self.getUserSlotID(userID)
if slotID is None:
@@ -228,7 +248,7 @@ class match:
total = 0
loaded = 0
for i in range(0,16):
if self.slots[i].status == slotStatuses.playing:
if self.slots[i].status == slotStatuses.PLAYING:
total+=1
if self.slots[i].loaded:
loaded+=1
@@ -237,7 +257,11 @@ class match:
self.allPlayersLoaded()
def allPlayersLoaded(self):
"""Send allPlayersLoaded packet to every playing usr in match"""
"""
Send allPlayersLoaded packet to every playing usr in match
:return:
"""
glob.streams.broadcast(self.playingStreamName, serverPackets.allPlayersLoaded())
log.info("MPROOM{}: All players loaded! Match starting...".format(self.matchID))
@@ -245,7 +269,8 @@ class match:
"""
Set a player skip status to True
userID -- ID of user
:param userID: ID of user
:return:
"""
slotID = self.getUserSlotID(userID)
if slotID is None:
@@ -263,7 +288,7 @@ class match:
total = 0
skipped = 0
for i in range(0,16):
if self.slots[i].status == slotStatuses.playing:
if self.slots[i].status == slotStatuses.PLAYING:
total+=1
if self.slots[i].skip:
skipped+=1
@@ -272,7 +297,11 @@ class match:
self.allPlayersSkipped()
def allPlayersSkipped(self):
"""Send allPlayersSkipped packet to every playing usr in match"""
"""
Send allPlayersSkipped packet to every playing usr in match
:return:
"""
glob.streams.broadcast(self.playingStreamName, serverPackets.allPlayersSkipped())
log.info("MPROOM{}: All players have skipped!".format(self.matchID))
@@ -280,7 +309,7 @@ class match:
"""
Set userID's slot completed to True
userID -- ID of user
:param userID: ID of user
"""
slotID = self.getUserSlotID(userID)
if slotID is None:
@@ -294,7 +323,7 @@ class match:
total = 0
completed = 0
for i in range(0,16):
if self.slots[i].status == slotStatuses.playing:
if self.slots[i].status == slotStatuses.PLAYING:
total+=1
if self.slots[i].complete:
completed+=1
@@ -303,15 +332,18 @@ class match:
self.allPlayersCompleted()
def allPlayersCompleted(self):
"""Cleanup match stuff and send match end packet to everyone"""
"""
Cleanup match stuff and send match end packet to everyone
:return:
"""
# Reset inProgress
self.inProgress = False
# Reset slots
for i in range(0,16):
if self.slots[i].user is not None and self.slots[i].status == slotStatuses.playing:
self.slots[i].status = slotStatuses.notReady
if self.slots[i].user is not None and self.slots[i].status == slotStatuses.PLAYING:
self.slots[i].status = slotStatuses.NOT_READY
self.slots[i].loaded = False
self.slots[i].skip = False
self.slots[i].complete = False
@@ -332,7 +364,7 @@ class match:
"""
Get slot ID occupied by userID
return -- slot id if found, None if user is not in room
:return: slot id if found, None if user is not in room
"""
for i in range(0,16):
if self.slots[i].user is not None and self.slots[i].user in glob.tokens.tokens and glob.tokens.tokens[self.slots[i].user].userID == userID:
@@ -343,21 +375,20 @@ class match:
"""
Add someone to users in match
userID -- user id of the user
return -- True if join success, False if fail (room is full)
:param userID: user id of the user
:return: True if join success, False if fail (room is full)
"""
# Make sure we're not in this match
for i in range(0,16):
if self.slots[i].user == user.token:
# Set bugged slot to free
self.setSlot(i, slotStatuses.free, 0, None, 0)
self.setSlot(i, slotStatuses.FREE, 0, None, 0)
# Find first free slot
for i in range(0,16):
if self.slots[i].status == slotStatuses.free:
if self.slots[i].status == slotStatuses.FREE:
# Occupy slot
self.setSlot(i, slotStatuses.notReady, 0, user.token, 0)
self.setSlot(i, slotStatuses.NOT_READY, 0, user.token, 0)
# Send updated match data
self.sendUpdates()
@@ -372,7 +403,8 @@ class match:
"""
Remove someone from users in match
userID -- user if of the user
:param userID: user if of the user
:return:
"""
# Make sure the user is in room
slotID = self.getUserSlotID(user.userID)
@@ -380,7 +412,7 @@ class match:
return
# Set that slot to free
self.setSlot(slotID, slotStatuses.free, 0, None, 0)
self.setSlot(slotID, slotStatuses.FREE, 0, None, 0)
# Check if everyone left
if self.countUsers() == 0:
@@ -407,8 +439,9 @@ class match:
"""
Change userID slot to newSlotID
userID -- user that changed slot
newSlotID -- slot id of new slot
:param userID: user that changed slot
:param newSlotID: slot id of new slot
:return:
"""
# Make sure the user is in room
oldSlotID = self.getUserSlotID(userID)
@@ -416,7 +449,7 @@ class match:
return
# Make sure there is no one inside new slot
if self.slots[newSlotID].user is not None and self.slots[newSlotID].status != slotStatuses.free:
if self.slots[newSlotID].user is not None and self.slots[newSlotID].status != slotStatuses.FREE:
return
# Get old slot data
@@ -424,7 +457,7 @@ class match:
oldData = copy.deepcopy(self.slots[oldSlotID])
# Free old slot
self.setSlot(oldSlotID, slotStatuses.free, 0, None, 0, False, False, False)
self.setSlot(oldSlotID, slotStatuses.FREE, 0, None, 0, False, False, False)
# Occupy new slot
self.setSlot(newSlotID, oldData.status, oldData.team, oldData.user, oldData.mods)
@@ -439,13 +472,10 @@ class match:
"""
Change match password to newPassword
newPassword -- new password string
:param newPassword: new password string
:return:
"""
self.matchPassword = newPassword
#if newPassword != "":
# self.matchPassword = generalUtils.stringMd5(newPassword)
#else:
# self.matchPassword = ""
# Send password change to every user in match
glob.streams.broadcast(self.streamName, serverPackets.changeMatchPassword(self.matchPassword))
@@ -460,7 +490,8 @@ class match:
"""
Set match global mods
mods -- mods bitwise int thing
:param mods: mods bitwise int thing
:return:
"""
# Set new mods and send update
self.mods = mods
@@ -471,8 +502,9 @@ class match:
"""
Set no beatmap status for userID
userID -- ID of user
has -- True if has beatmap, false if not
:param userID: ID of user
:param has: True if has beatmap, false if not
:return:
"""
# Make sure the user is in room
slotID = self.getUserSlotID(userID)
@@ -480,7 +512,7 @@ class match:
return
# Set slot
self.setSlot(slotID, slotStatuses.noMap if not has else slotStatuses.notReady)
self.setSlot(slotID, slotStatuses.NO_MAP if not has else slotStatuses.NOT_READY)
# Send updates
self.sendUpdates()
@@ -489,7 +521,8 @@ class match:
"""
Transfer host to slotID
slotID -- ID of slot
:param slotID: ID of slot
:return:
"""
# Make sure there is someone in that slot
if self.slots[slotID].user is None or self.slots[slotID].user not in glob.tokens.tokens:
@@ -505,7 +538,8 @@ class match:
"""
Send userID's failed packet to everyone in match
userID -- ID of user
:param userID: ID of user
:return:
"""
# Make sure the user is in room
slotID = self.getUserSlotID(userID)
@@ -522,10 +556,10 @@ class match:
"""
Fro invites to in this match.
fro -- sender userID
to -- receiver userID
:param fro: sender userID
:param to: receiver userID
:return:
"""
# Get tokens
froToken = glob.tokens.getTokenFromUserID(fro)
toToken = glob.tokens.getTokenFromUserID(to)
@@ -544,7 +578,7 @@ class match:
"""
Return how many players are in that match
return -- number of users
:return: number of users
"""
c = 0
for i in range(0,16):
@@ -556,7 +590,8 @@ class match:
"""
Change userID's team
userID -- id of user
:param userID: id of user
:return:
"""
# Make sure the user is in room
slotID = self.getUserSlotID(userID)
@@ -564,11 +599,16 @@ class match:
return
# Update slot and send update
newTeam = matchTeams.blue if self.slots[slotID].team == matchTeams.red else matchTeams.red
newTeam = matchTeams.BLUE if self.slots[slotID].team == matchTeams.RED else matchTeams.RED
self.setSlot(slotID, None, newTeam)
self.sendUpdates()
def sendUpdates(self):
"""
Send match updates packet to everyone in lobby and room streams
:return:
"""
self.matchDataCache = serverPackets.updateMatch(self.matchID)
if self.matchDataCache is not None:
glob.streams.broadcast(self.streamName, self.matchDataCache)
@@ -580,16 +620,17 @@ class match:
"""
Check if match teams are valid
return -- True if valid, False if invalid
:return: True if valid, False if invalid
:return:
"""
if self.matchTeamType != matchTeamTypes.teamVs or self.matchTeamType != matchTeamTypes.tagTeamVs:
if self.matchTeamType != matchTeamTypes.TEAM_VS or self.matchTeamType != matchTeamTypes.TAG_TEAM_VS:
# Teams are always valid if we have no teams
return True
# We have teams, check if they are valid
firstTeam = -1
for i in range(0,16):
if self.slots[i].user is not None and (self.slots[i].status & slotStatuses.noMap) == 0:
if self.slots[i].user is not None and (self.slots[i].status & slotStatuses.NO_MAP) == 0:
if firstTeam == -1:
firstTeam = self.slots[i].team
elif firstTeam != self.slots[i].team:
@@ -600,6 +641,11 @@ class match:
return False
def start(self):
"""
Start the match
:return:
"""
# Make sure we have enough players
if self.countUsers() < 2 or not self.checkTeams():
return
@@ -613,8 +659,8 @@ class match:
# Set playing to ready players and set load, skip and complete to False
# Make clients join playing stream
for i in range(0, 16):
if (self.slots[i].status & slotStatuses.ready) > 0 and self.slots[i].user in glob.tokens.tokens:
self.slots[i].status = slotStatuses.playing
if (self.slots[i].status & slotStatuses.READY) > 0 and self.slots[i].user in glob.tokens.tokens:
self.slots[i].status = slotStatuses.PLAYING
self.slots[i].loaded = False
self.slots[i].skip = False
self.slots[i].complete = False

View File

@@ -13,14 +13,14 @@ class matchList:
"""
Add a new match to matches list
matchName -- match name, string
matchPassword -- match md5 password. Leave empty for no password
beatmapID -- beatmap ID
beatmapName -- beatmap name, string
beatmapMD5 -- beatmap md5 hash, string
gameMode -- game mode ID. See gameModes.py
hostUserID -- user id of who created the match
return -- match ID
:param matchName: match name, string
:param matchPassword: match md5 password. Leave empty for no password
:param beatmapID: beatmap ID
:param beatmapName: beatmap name, string
:param beatmapMD5: beatmap md5 hash, string
:param gameMode: game mode ID. See gameModes.py
:param hostUserID: user id of who created the match
:return: match ID
"""
# Add a new match to matches list and create its stream
matchID = self.lastID
@@ -32,7 +32,8 @@ class matchList:
"""
Destroy match object with id = matchID
matchID -- ID of match to dispose
:param matchID: ID of match to dispose
:return:
"""
# Make sure the match exists
if matchID not in self.matches:

View File

@@ -12,17 +12,17 @@ from objects import glob
class token:
def __init__(self, userID, token_ = None, ip ="", irc = False, timeOffset = 0, tournament = False):
"""
Create a token object and set userID and token
userID -- user associated to this 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.
timeOffset -- the time offset from UTC for this user. optional.
:param userID: user associated to this token
:param token_: if passed, set token to that value
if not passed, token will be generated
:param ip: client ip. optional.
:param irc: if True, set this token as IRC client. Default: False.
:param timeOffset: the time offset from UTC for this user. Default: 0.
:param tournament: if True, flag this client as a tournement client. Default: True.
"""
# Set stuff
self.userID = userID
@@ -95,25 +95,23 @@ class token:
"""
Add bytes (packets) to queue
bytes -- (packet) bytes to enqueue
:param bytes: (packet) bytes to enqueue
"""
if not self.irc:
if len(bytes_) < 10 * 10 ** 6:
self.queue += bytes_
else:
log.warning("{}'s packets buffer is above 10M!! Lost some data!".format(self.username))
# TODO: reduce max queue size
if len(bytes_) < 10 * 10 ** 6:
self.queue += bytes_
else:
log.warning("{}'s packets buffer is above 10M!! Lost some data!".format(self.username))
def resetQueue(self):
"""Resets the queue. Call when enqueued packets have been sent"""
self.queue = bytes()
def joinChannel(self, channel):
"""
Add channel to joined channels list
channel -- channel name
:param channel: channel name
"""
if channel not in self.joinedChannels:
self.joinedChannels.append(channel)
@@ -122,24 +120,24 @@ class token:
"""
Remove channel from joined channels list
channel -- channel name
:param channel: channel name
"""
if channel in self.joinedChannels:
self.joinedChannels.remove(channel)
def setLocation(self, location):
def setLocation(self, latitude, longitude):
"""
Set location (latitude and longitude)
Set client location
location -- [latitude, longitude]
:param location: [latitude, longitude]
"""
self.location = location
self.location = (latitude, longitude)
def getLatitude(self):
"""
Get latitude
return -- latitude
:return: latitude
"""
return self.location[0]
@@ -147,15 +145,16 @@ class token:
"""
Get longitude
return -- longitude
:return: longitude
"""
return self.location[1]
def startSpectating(self, host):
"""
Set the spectating user to userID
Set the spectating user to userID, join spectator stream and chat channel
and send required packets to host
user -- user object
:param host: host osuToken object
"""
# Stop spectating old client
self.stopSpectating()
@@ -195,6 +194,12 @@ class token:
log.info("{} is spectating {}".format(self.username, host.username))
def stopSpectating(self):
"""
Stop spectating, leave spectator stream and channel
and send required packets to host
:return:
"""
# Remove our userID from host's spectators
if self.spectating is None:
return
@@ -233,36 +238,20 @@ class token:
self.spectating = None
self.spectatingUserID = 0
def setCountry(self, countryID):
"""
Set country to countryID
countryID -- numeric country ID. See countryHelper.py
"""
self.country = countryID
def getCountry(self):
"""
Get numeric country ID
return -- numeric country ID. See countryHelper.py
"""
return self.country
def updatePingTime(self):
"""Update latest ping time"""
"""
Update latest ping time to current time
:return:
"""
self.pingTime = int(time.time())
def setAwayMessage(self, __awayMessage):
"""Set a new away message"""
self.awayMessage = __awayMessage
def joinMatch(self, matchID):
"""
Set match to matchID, join match stream and channel
matchID -- new match ID
:param matchID: new match ID
:return:
"""
# Make sure the match exists
if matchID not in glob.matches.matches:
@@ -322,7 +311,10 @@ class token:
"""
Kick this user from the server
message -- Notification message to send to this user. Optional.
:param message: Notification message to send to this user.
Default: "You have been kicked from the server. Please login again."
:param reason: Kick reason, used in logs. Default: "kick"
:return:
"""
# Send packet to target
log.info("{} has been disconnected. ({})".format(self.username, reason))
@@ -337,9 +329,10 @@ class token:
"""
Silences this user (db, packet and token)
seconds -- silence length in seconds
reason -- silence reason
author -- userID of who has silenced the target. Optional. Default: 999 (fokabot)
:param seconds: silence length in seconds
:param reason: silence reason
:param author: userID of who has silenced the user. Default: 999 (FokaBot)
:return:
"""
# Silence in db and token
self.silenceEndTime = int(time.time())+seconds
@@ -355,7 +348,8 @@ class token:
"""
Silences the user if is spamming.
increaseSpamRate -- pass True if the user has sent a new message. Optional. Default: True
:param increaseSpamRate: set to True if the user has sent a new message. Default: True
:return:
"""
# Increase the spam rate if needed
if increaseSpamRate:
@@ -369,7 +363,7 @@ class token:
"""
Returns True if this user is silenced, otherwise False
return -- True/False
:return: True if this user is silenced, otherwise False
"""
return self.silenceEndTime-int(time.time()) > 0
@@ -378,12 +372,16 @@ class token:
Returns the seconds left for this user's silence
(0 if user is not silenced)
return -- silence seconds left
:return: silence seconds left (or 0)
"""
return max(0, self.silenceEndTime-int(time.time()))
def updateCachedStats(self):
"""Update all cached stats for this token"""
"""
Update all cached stats for this token
:return:
"""
stats = userUtils.getUserStats(self.userID, self.gameMode)
log.debug(str(stats))
if stats is None:
@@ -400,8 +398,9 @@ class token:
"""
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
:param force: If True, get restricted value from db.
If False, get the cached one. Default: False
:return:
"""
if force:
self.restricted = userUtils.isRestricted(self.userID)
@@ -412,21 +411,40 @@ class token:
"""
Set this token as restricted, send FokaBot message to user
and send offline packet to everyone
:return:
"""
self.restricted = True
chat.sendMessage("FokaBot",self.username, "Your account is currently in restricted mode. Please visit ripple's website for more information.")
def joinStream(self, name):
"""
Join a packet stream, or create it if the stream doesn't exist.
:param name: stream name
:return:
"""
glob.streams.join(name, token=self.token)
if name not in self.streams:
self.streams.append(name)
def leaveStream(self, name):
"""
Leave a packets stream
:param name: stream name
:return:
"""
glob.streams.leave(name, token=self.token)
if name in self.streams:
self.streams.remove(name)
def leaveAllStreams(self):
"""
Leave all joined packet streams
:return:
"""
for i in self.streams:
self.leaveStream(i)

View File

@@ -8,27 +8,19 @@ from events import logoutEvent
from objects import glob
from objects import osuToken
class tokenList:
"""
List of connected osu tokens
tokens -- dictionary. key: token string, value: token object
"""
def __init__(self):
"""
Initialize a tokens list
"""
self.tokens = {}
def addToken(self, userID, ip = "", irc = False, timeOffset=0, tournament=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
:param userID: user id associated to that token
:param irc: if True, set this token as IRC client
:param timeOffset: the time offset from UTC for this user. Default: 0.
:param tournament: if True, flag this client as a tournement client. Default: True.
:return: token object
"""
newToken = osuToken.token(userID, ip=ip, irc=irc, timeOffset=timeOffset, tournament=tournament)
self.tokens[newToken.token] = newToken
@@ -38,7 +30,8 @@ class tokenList:
"""
Delete a token from token list if it exists
token -- token string
:param token: token string
:return:
"""
if token in self.tokens:
# Delete session from DB
@@ -52,8 +45,8 @@ class tokenList:
"""
Get user ID from a token
token -- token to find
return -- false if not found, userID if found
:param token: token to find
:return: False if not found, userID if found
"""
# Make sure the token exists
if token not in self.tokens:
@@ -66,8 +59,9 @@ class tokenList:
"""
Get token from a user ID
userID -- user ID to find
return -- False if not found, token object if found
:param userID: user ID to find
:param ignoreIRC: if True, consider bancho clients only and skip IRC clients
:return: False if not found, token object if found
"""
# Make sure the token exists
for _, value in self.tokens.items():
@@ -85,8 +79,8 @@ class tokenList:
:param username: normal username or safe username
:param ignoreIRC: if True, consider bancho clients only and skip IRC clients
:param safe: if True, username is a safe username,
compare it with token's safe username rather than normal username
:param safe: if True, username is a safe username,
compare it with token's safe username rather than normal username
:return: osuToken object or None
"""
# lowercase
@@ -106,7 +100,8 @@ class tokenList:
"""
Delete old userID's tokens if found
userID -- tokens associated to this user will be deleted
:param userID: tokens associated to this user will be deleted
:return:
"""
# Delete older tokens
for key, value in list(self.tokens.items()):
@@ -118,9 +113,10 @@ class tokenList:
"""
Enqueue a packet to multiple users
packet -- packet bytes to enqueue
who -- userIDs array
but -- if True, enqueue to everyone but users in who array
:param packet: packet bytes to enqueue
:param who: userIDs array
:param but: if True, enqueue to everyone but users in `who` array
:return:
"""
for _, value in self.tokens.items():
shouldEnqueue = False
@@ -136,19 +132,21 @@ class tokenList:
"""
Enqueue packet(s) to every connected user
packet -- packet bytes to enqueue
:param packet: packet bytes to enqueue
:return:
"""
for _, value in self.tokens.items():
value.enqueue(packet)
def usersTimeoutCheckLoop(self, timeoutTime = 100, checkTime = 100):
"""
Deletes all timed out users.
If called once, will recall after checkTime seconds and so on, forever
Start timed out users disconnect loop.
This function will be called every `checkTime` seconds and so on, forever.
CALL THIS FUNCTION ONLY ONCE!
timeoutTime - seconds of inactivity required to disconnect someone (Default: 100)
checkTime - seconds between loops (Default: 100)
:param timeoutTime: seconds of inactivity required to disconnect someone. Default: 100
:param checkTime: seconds between loops. Default: 100
:return:
"""
log.debug("Checking timed out clients")
timedOutTokens = [] # timed out users
@@ -172,8 +170,11 @@ class tokenList:
def spamProtectionResetLoop(self):
"""
Reset spam rate every 10 seconds.
Start spam protection reset loop.
Called every 10 seconds.
CALL THIS FUNCTION ONLY ONCE!
:return:
"""
# Reset spamRate for every token
for _, value in self.tokens.items():
@@ -186,20 +187,22 @@ class tokenList:
"""
Truncate bancho_sessions table.
Call at bancho startup to delete old cached sessions
:return:
"""
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
Check if a token exists
Use username or userid, not both at the same time.
:param username: Optional.
:param userID: Optional.
:return: True if it exists, otherwise False
"""
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
return True if self.getTokenFromUsername(username) is not None else False