Compare commits
No commits in common. "master" and "tornado-gevent" have entirely different histories.
master
...
tornado-ge
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -1,10 +1,4 @@
|
|||
**/__pycache__
|
||||
**/build
|
||||
config.ini
|
||||
filters.txt
|
||||
.data
|
||||
.idea
|
||||
redistest.py
|
||||
*.c
|
||||
*.so
|
||||
.pyenv
|
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +0,0 @@
|
|||
[submodule "common"]
|
||||
path = common
|
||||
url = https://github.com/osufx/ripple-python-common.git
|
|
@ -1,7 +0,0 @@
|
|||
python-targets:
|
||||
- 3
|
||||
pep8:
|
||||
none: true
|
||||
pylint:
|
||||
disable:
|
||||
- cyclic-import
|
27
README.md
27
README.md
|
@ -1,8 +1,4 @@
|
|||
## pep.py [![Code Health](https://landscape.io/github/osuripple/pep.py/master/landscape.svg?style=flat)](https://landscape.io/github/osuripple/pep.py/master)
|
||||
|
||||
- Origin: https://git.zxq.co/ripple/pep.py
|
||||
- Mirror: https://github.com/osuripple/pep.py
|
||||
|
||||
## pep.py
|
||||
This is Ripple's bancho server. It handles:
|
||||
- Client login
|
||||
- Online users listing and statuses
|
||||
|
@ -13,30 +9,19 @@ This is Ripple's bancho server. It handles:
|
|||
|
||||
## Requirements
|
||||
- Python 3.5
|
||||
- Cython
|
||||
- C compiler
|
||||
- MySQLdb (`mysqlclient`)
|
||||
- MySQLdb (`mysqlclient` or `mysql-python`)
|
||||
- Tornado
|
||||
- Gevent
|
||||
- Bcrypt
|
||||
- Raven
|
||||
|
||||
## How to set up pep.py
|
||||
First of all, initialize and update the submodules
|
||||
First of all, install all the dependencies
|
||||
```
|
||||
$ git submodule init && git submodule update
|
||||
$ pip install mysqlclient tornado gevent bcrypt
|
||||
```
|
||||
afterwards, install the required dependencies with pip
|
||||
```
|
||||
$ pip install -r requirements.txt
|
||||
```
|
||||
then, compile all `*.pyx` files to `*.so` or `*.dll` files using `setup.py` (distutils file)
|
||||
```
|
||||
$ python3 setup.py build_ext --inplace
|
||||
```
|
||||
finally, run pep.py once to create the default config file and edit it
|
||||
then, run pep.py once to create the default config file and edit it
|
||||
```
|
||||
$ python3 pep.py
|
||||
...
|
||||
$ nano config.ini
|
||||
```
|
||||
you can run pep.py by typing
|
||||
|
|
1
common
1
common
|
@ -1 +0,0 @@
|
|||
Subproject commit 6103fe96a79cd8f5cbabe24b5fac9cf2a5cacb4a
|
17
constants/actions.py
Normal file
17
constants/actions.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
"""Contains user actions"""
|
||||
#TODO: Uppercase
|
||||
idle = 0
|
||||
afk = 1
|
||||
playing = 2
|
||||
editing = 3
|
||||
modding = 4
|
||||
multiplayer = 5
|
||||
watching = 6
|
||||
unknown = 7
|
||||
testing = 8
|
||||
submitting = 9
|
||||
paused = 10
|
||||
lobby = 11
|
||||
multiplaying= 12
|
||||
osuDirect = 13
|
||||
none = 14
|
9
constants/bcolors.py
Normal file
9
constants/bcolors.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
"""Console colors"""
|
||||
PINK = '\033[95m'
|
||||
BLUE = '\033[94m'
|
||||
GREEN = '\033[92m'
|
||||
YELLOW = '\033[93m'
|
||||
RED = '\033[91m'
|
||||
ENDC = '\033[0m'
|
||||
BOLD = '\033[1m'
|
||||
UNDERLINE = '\033[4m'
|
|
@ -1,3 +1,4 @@
|
|||
""" Contains functions used to read specific client packets from byte stream """
|
||||
from constants import dataTypes
|
||||
from helpers import packetHelper
|
||||
from constants import slotStatuses
|
||||
|
@ -7,86 +8,86 @@ from constants import slotStatuses
|
|||
def userActionChange(stream):
|
||||
return packetHelper.readPacketData(stream,
|
||||
[
|
||||
["actionID", dataTypes.BYTE],
|
||||
["actionText", dataTypes.STRING],
|
||||
["actionMd5", dataTypes.STRING],
|
||||
["actionMods", dataTypes.UINT32],
|
||||
["gameMode", dataTypes.BYTE],
|
||||
["beatmapID", dataTypes.SINT32]
|
||||
["actionID", dataTypes.byte],
|
||||
["actionText", dataTypes.string],
|
||||
["actionMd5", dataTypes.string],
|
||||
["actionMods", dataTypes.uInt32],
|
||||
["gameMode", dataTypes.byte]
|
||||
])
|
||||
|
||||
def userStatsRequest(stream):
|
||||
return packetHelper.readPacketData(stream, [["users", dataTypes.INT_LIST]])
|
||||
return packetHelper.readPacketData(stream, [["users", dataTypes.intList]])
|
||||
|
||||
def userPanelRequest(stream):
|
||||
return packetHelper.readPacketData(stream, [["users", dataTypes.INT_LIST]])
|
||||
return packetHelper.readPacketData(stream, [["users", dataTypes.intList]])
|
||||
|
||||
|
||||
""" Client chat packets """
|
||||
def sendPublicMessage(stream):
|
||||
return packetHelper.readPacketData(stream,
|
||||
[
|
||||
["unknown", dataTypes.STRING],
|
||||
["message", dataTypes.STRING],
|
||||
["to", dataTypes.STRING]
|
||||
["unknown", dataTypes.string],
|
||||
["message", dataTypes.string],
|
||||
["to", dataTypes.string]
|
||||
])
|
||||
|
||||
def sendPrivateMessage(stream):
|
||||
return packetHelper.readPacketData(stream,
|
||||
[
|
||||
["unknown", dataTypes.STRING],
|
||||
["message", dataTypes.STRING],
|
||||
["to", dataTypes.STRING],
|
||||
["unknown2", dataTypes.UINT32]
|
||||
["unknown", dataTypes.string],
|
||||
["message", dataTypes.string],
|
||||
["to", dataTypes.string],
|
||||
["unknown2", dataTypes.uInt32]
|
||||
])
|
||||
|
||||
def setAwayMessage(stream):
|
||||
return packetHelper.readPacketData(stream,
|
||||
[
|
||||
["unknown", dataTypes.STRING],
|
||||
["awayMessage", dataTypes.STRING]
|
||||
["unknown", dataTypes.string],
|
||||
["awayMessage", dataTypes.string]
|
||||
])
|
||||
|
||||
def channelJoin(stream):
|
||||
return packetHelper.readPacketData(stream, [["channel", dataTypes.STRING]])
|
||||
return packetHelper.readPacketData(stream,[["channel", dataTypes.string]])
|
||||
|
||||
def channelPart(stream):
|
||||
return packetHelper.readPacketData(stream, [["channel", dataTypes.STRING]])
|
||||
return packetHelper.readPacketData(stream,[["channel", dataTypes.string]])
|
||||
|
||||
def addRemoveFriend(stream):
|
||||
return packetHelper.readPacketData(stream, [["friendID", dataTypes.SINT32]])
|
||||
return packetHelper.readPacketData(stream, [["friendID", dataTypes.sInt32]])
|
||||
|
||||
|
||||
""" Spectator packets """
|
||||
|
||||
""" SPECTATOR PACKETS """
|
||||
def startSpectating(stream):
|
||||
return packetHelper.readPacketData(stream, [["userID", dataTypes.SINT32]])
|
||||
return packetHelper.readPacketData(stream,[["userID", dataTypes.sInt32]])
|
||||
|
||||
|
||||
""" Multiplayer packets """
|
||||
""" MULTIPLAYER PACKETS """
|
||||
def matchSettings(stream):
|
||||
# Data to return, will be merged later
|
||||
data = []
|
||||
|
||||
# Some settings
|
||||
struct = [
|
||||
["matchID", dataTypes.UINT16],
|
||||
["inProgress", dataTypes.BYTE],
|
||||
["unknown", dataTypes.BYTE],
|
||||
["mods", dataTypes.UINT32],
|
||||
["matchName", dataTypes.STRING],
|
||||
["matchPassword", dataTypes.STRING],
|
||||
["beatmapName", dataTypes.STRING],
|
||||
["beatmapID", dataTypes.UINT32],
|
||||
["beatmapMD5", dataTypes.STRING]
|
||||
["matchID", dataTypes.uInt16],
|
||||
["inProgress", dataTypes.byte],
|
||||
["unknown", dataTypes.byte],
|
||||
["mods", dataTypes.uInt32],
|
||||
["matchName", dataTypes.string],
|
||||
["matchPassword", dataTypes.string],
|
||||
["beatmapName", dataTypes.string],
|
||||
["beatmapID", dataTypes.uInt32],
|
||||
["beatmapMD5", dataTypes.string]
|
||||
]
|
||||
|
||||
# Slot statuses (not used)
|
||||
for i in range(0,16):
|
||||
struct.append(["slot{}Status".format(str(i)), dataTypes.BYTE])
|
||||
struct.append(["slot{}Status".format(str(i)), dataTypes.byte])
|
||||
|
||||
# Slot statuses (not used)
|
||||
for i in range(0,16):
|
||||
struct.append(["slot{}Team".format(str(i)), dataTypes.BYTE])
|
||||
struct.append(["slot{}Team".format(str(i)), dataTypes.byte])
|
||||
|
||||
# Read first part
|
||||
data.append(packetHelper.readPacketData(stream, struct))
|
||||
|
@ -99,21 +100,24 @@ def matchSettings(stream):
|
|||
start += 2
|
||||
for i in range(0,16):
|
||||
s = data[0]["slot{}Status".format(str(i))]
|
||||
if s != slotStatuses.FREE and s != slotStatuses.LOCKED:
|
||||
if s != slotStatuses.free and s != slotStatuses.locked:
|
||||
start += 4
|
||||
|
||||
# Other settings
|
||||
struct = [
|
||||
["hostUserID", dataTypes.SINT32],
|
||||
["gameMode", dataTypes.BYTE],
|
||||
["scoringType", dataTypes.BYTE],
|
||||
["teamType", dataTypes.BYTE],
|
||||
["freeMods", dataTypes.BYTE],
|
||||
["hostUserID", dataTypes.sInt32],
|
||||
["gameMode", dataTypes.byte],
|
||||
["scoringType", dataTypes.byte],
|
||||
["teamType", dataTypes.byte],
|
||||
["freeMods", dataTypes.byte],
|
||||
]
|
||||
|
||||
# Read last part
|
||||
data.append(packetHelper.readPacketData(stream[start:], struct, False))
|
||||
|
||||
# Mods if freemod (not used)
|
||||
#if data[1]["freeMods"] == 1:
|
||||
|
||||
result = {}
|
||||
for i in data:
|
||||
result.update(i)
|
||||
|
@ -126,48 +130,19 @@ def changeMatchSettings(stream):
|
|||
return matchSettings(stream)
|
||||
|
||||
def changeSlot(stream):
|
||||
return packetHelper.readPacketData(stream, [["slotID", dataTypes.UINT32]])
|
||||
return packetHelper.readPacketData(stream, [["slotID", dataTypes.uInt32]])
|
||||
|
||||
def joinMatch(stream):
|
||||
return packetHelper.readPacketData(stream, [["matchID", dataTypes.UINT32], ["password", dataTypes.STRING]])
|
||||
return packetHelper.readPacketData(stream, [["matchID", dataTypes.uInt32], ["password", dataTypes.string]])
|
||||
|
||||
def changeMods(stream):
|
||||
return packetHelper.readPacketData(stream, [["mods", dataTypes.UINT32]])
|
||||
return packetHelper.readPacketData(stream, [["mods", dataTypes.uInt32]])
|
||||
|
||||
def lockSlot(stream):
|
||||
return packetHelper.readPacketData(stream, [["slotID", dataTypes.UINT32]])
|
||||
return packetHelper.readPacketData(stream, [["slotID", dataTypes.uInt32]])
|
||||
|
||||
def transferHost(stream):
|
||||
return packetHelper.readPacketData(stream, [["slotID", dataTypes.UINT32]])
|
||||
return packetHelper.readPacketData(stream, [["slotID", dataTypes.uInt32]])
|
||||
|
||||
def matchInvite(stream):
|
||||
return packetHelper.readPacketData(stream, [["userID", dataTypes.UINT32]])
|
||||
|
||||
def matchFrames(stream):
|
||||
return packetHelper.readPacketData(stream,
|
||||
[
|
||||
["time", dataTypes.SINT32],
|
||||
["id", dataTypes.BYTE],
|
||||
["count300", dataTypes.UINT16],
|
||||
["count100", dataTypes.UINT16],
|
||||
["count50", dataTypes.UINT16],
|
||||
["countGeki", dataTypes.UINT16],
|
||||
["countKatu", dataTypes.UINT16],
|
||||
["countMiss", dataTypes.UINT16],
|
||||
["totalScore", dataTypes.SINT32],
|
||||
["maxCombo", dataTypes.UINT16],
|
||||
["currentCombo", dataTypes.UINT16],
|
||||
["perfect", dataTypes.BYTE],
|
||||
["currentHp", dataTypes.BYTE],
|
||||
["tagByte", dataTypes.BYTE],
|
||||
["usingScoreV2", dataTypes.BYTE]
|
||||
])
|
||||
|
||||
def tournamentMatchInfoRequest(stream):
|
||||
return packetHelper.readPacketData(stream, [["matchID", dataTypes.UINT32]])
|
||||
|
||||
def tournamentJoinMatchChannel(stream):
|
||||
return packetHelper.readPacketData(stream, [["matchID", dataTypes.UINT32]])
|
||||
|
||||
def tournamentLeaveMatchChannel(stream):
|
||||
return packetHelper.readPacketData(stream, [["matchID", dataTypes.UINT32]])
|
||||
return packetHelper.readPacketData(stream, [["userID", dataTypes.uInt32]])
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
"""Bancho packets data types"""
|
||||
BYTE = 0
|
||||
UINT16 = 1
|
||||
SINT16 = 2
|
||||
UINT32 = 3
|
||||
SINT32 = 4
|
||||
UINT64 = 5
|
||||
SINT64 = 6
|
||||
STRING = 7
|
||||
FFLOAT = 8 # because float is a keyword
|
||||
BBYTES = 9
|
||||
INT_LIST = 10 # TODO: Maybe there are some packets that still use uInt16 + uInt32 thing somewhere.
|
||||
#TODO: Uppercase, maybe?
|
||||
byte = 0
|
||||
uInt16 = 1
|
||||
sInt16 = 2
|
||||
uInt32 = 3
|
||||
sInt32 = 4
|
||||
uInt64 = 5
|
||||
sInt64 = 6
|
||||
string = 7
|
||||
ffloat = 8 # because float is a keyword
|
||||
bbytes = 9
|
||||
intList = 10 # TODO: Maybe there are some packets that still use uInt16 + uInt32 thing somewhere.
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
"""Bancho exceptions"""
|
||||
# TODO: Prints in exceptions
|
||||
class loginFailedException(Exception):
|
||||
pass
|
||||
|
||||
|
@ -81,30 +83,3 @@ class haxException(Exception):
|
|||
|
||||
class forceUpdateException(Exception):
|
||||
pass
|
||||
|
||||
class loginLockedException(Exception):
|
||||
pass
|
||||
|
||||
class unknownStreamException(Exception):
|
||||
pass
|
||||
|
||||
class userTournamentException(Exception):
|
||||
pass
|
||||
|
||||
class userAlreadyInChannelException(Exception):
|
||||
pass
|
||||
|
||||
class userNotInChannelException(Exception):
|
||||
pass
|
||||
|
||||
class missingReportInfoException(Exception):
|
||||
pass
|
||||
|
||||
class invalidUserException(Exception):
|
||||
pass
|
||||
|
||||
class wrongChannelException(Exception):
|
||||
pass
|
||||
|
||||
class periodicLoopException(Exception):
|
||||
pass
|
File diff suppressed because it is too large
Load Diff
41
constants/gameModes.py
Normal file
41
constants/gameModes.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
"""Contains readable gamemodes with their codes"""
|
||||
std = 0
|
||||
taiko = 1
|
||||
ctb = 2
|
||||
mania = 3
|
||||
|
||||
def getGameModeForDB(gameMode):
|
||||
"""
|
||||
Convert a gamemode number to string for database table/column
|
||||
|
||||
gameMode -- gameMode int or variable (ex: gameMode.std)
|
||||
|
||||
return -- game mode readable string for db
|
||||
"""
|
||||
|
||||
if gameMode == std:
|
||||
return "std"
|
||||
elif gameMode == taiko:
|
||||
return "taiko"
|
||||
elif gameMode == ctb:
|
||||
return "ctb"
|
||||
else:
|
||||
return "mania"
|
||||
|
||||
def getGameModeForPrinting(gameMode):
|
||||
"""
|
||||
Convert a gamemode number to string for showing to a user (e.g. !last)
|
||||
|
||||
gameMode -- gameMode int or variable (ex: gameMode.std)
|
||||
|
||||
return -- game mode readable string for a human
|
||||
"""
|
||||
|
||||
if gameMode == std:
|
||||
return "osu!"
|
||||
elif gameMode == taiko:
|
||||
return "Taiko"
|
||||
elif gameMode == ctb:
|
||||
return "CatchTheBeat"
|
||||
else:
|
||||
return "osu!mania"
|
|
@ -1,2 +1,2 @@
|
|||
NORMAL = 0
|
||||
FREE_MOD = 1
|
||||
normal = 0
|
||||
freeMod = 1
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
SCORE = 0
|
||||
ACCURACY = 1
|
||||
COMBO = 2
|
||||
SCORE_V2 = 3
|
||||
score = 0
|
||||
accuracy = 1
|
||||
combo = 2
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
HEAD_TO_HEAD = 0
|
||||
TAG_COOP = 1
|
||||
TEAM_VS = 2
|
||||
TAG_TEAM_VS = 3
|
||||
headToHead = 0
|
||||
tagCoop = 1
|
||||
teamVs = 2
|
||||
tagTeamVs = 3
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
NO_TEAM = 0
|
||||
BLUE = 1
|
||||
RED = 2
|
||||
noTeam = 0
|
||||
blue = 1
|
||||
red = 2
|
||||
|
|
30
constants/mods.py
Normal file
30
constants/mods.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
Nomod = 0
|
||||
NoFail = 1
|
||||
Easy = 2
|
||||
NoVideo = 4
|
||||
Hidden = 8
|
||||
HardRock = 16
|
||||
SuddenDeath = 32
|
||||
DoubleTime = 64
|
||||
Relax = 128
|
||||
HalfTime = 256
|
||||
Nightcore = 512
|
||||
Flashlight = 1024
|
||||
Autoplay = 2048
|
||||
SpunOut = 4096
|
||||
Relax2 = 8192
|
||||
Perfect = 16384
|
||||
Key4 = 32768
|
||||
Key5 = 65536
|
||||
Key6 = 131072
|
||||
Key7 = 262144
|
||||
Key8 = 524288
|
||||
keyMod = 1015808
|
||||
FadeIn = 1048576
|
||||
Random = 2097152
|
||||
LastMod = 4194304
|
||||
Key9 = 16777216
|
||||
Key10 = 33554432
|
||||
Key1 = 67108864
|
||||
Key3 = 134217728
|
||||
Key2 = 268435456
|
|
@ -74,11 +74,7 @@ server_channelInfoEnd = 89
|
|||
client_matchChangePassword = 90
|
||||
server_matchChangePassword = 91
|
||||
server_silenceEnd = 92
|
||||
client_specialMatchInfoRequest = 93
|
||||
server_userSilenced = 94
|
||||
server_userPresenceBundle = 96
|
||||
client_userPanelRequest = 97
|
||||
client_tournamentMatchInfoRequest = 93
|
||||
server_matchAbort = 106
|
||||
server_switchServer = 107
|
||||
client_tournamentJoinMatchChannel = 108
|
||||
client_tournamentLeaveMatchChannel = 109
|
21
constants/privileges.py
Normal file
21
constants/privileges.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
USER_PUBLIC = 1
|
||||
USER_NORMAL = 2 << 0
|
||||
USER_DONOR = 2 << 1
|
||||
ADMIN_ACCESS_RAP = 2 << 2
|
||||
ADMIN_MANAGE_USERS = 2 << 3
|
||||
ADMIN_BAN_USERS = 2 << 4
|
||||
ADMIN_SILENCE_USERS = 2 << 5
|
||||
ADMIN_WIPE_USERS = 2 << 6
|
||||
ADMIN_MANAGE_BEATMAPS = 2 << 7
|
||||
ADMIN_MANAGE_SERVERS = 2 << 8
|
||||
ADMIN_MANAGE_SETTINGS = 2 << 9
|
||||
ADMIN_MANAGE_BETAKEYS = 2 << 10
|
||||
ADMIN_MANAGE_REPORTS = 2 << 11
|
||||
ADMIN_MANAGE_DOCS = 2 << 12
|
||||
ADMIN_MANAGE_BADGES = 2 << 13
|
||||
ADMIN_VIEW_RAP_LOGS = 2 << 14
|
||||
ADMIN_MANAGE_PRIVILEGES = 2 << 15
|
||||
ADMIN_SEND_ALERTS = 2 << 16
|
||||
ADMIN_CHAT_MOD = 2 << 17
|
||||
ADMIN_KICK_USERS = 2 << 18
|
||||
USER_PENDING_VERIFICATION = 2 << 19
|
|
@ -1,65 +1,69 @@
|
|||
""" Contains functions used to write specific server packets to byte streams """
|
||||
from common.constants import privileges
|
||||
from common.ripple import userUtils
|
||||
from constants import dataTypes
|
||||
from constants import packetIDs
|
||||
from constants import userRanks
|
||||
from helpers import packetHelper
|
||||
from constants import dataTypes
|
||||
from helpers import userHelper
|
||||
from objects import glob
|
||||
from constants import userRanks
|
||||
from constants import packetIDs
|
||||
|
||||
""" Login errors packets """
|
||||
""" Login errors packets
|
||||
(userID packets derivates) """
|
||||
def loginFailed():
|
||||
return packetHelper.buildPacket(packetIDs.server_userID, [[-1, dataTypes.SINT32]])
|
||||
return packetHelper.buildPacket(packetIDs.server_userID, [[-1, dataTypes.sInt32]])
|
||||
|
||||
def forceUpdate():
|
||||
return packetHelper.buildPacket(packetIDs.server_userID, [[-2, dataTypes.SINT32]])
|
||||
return packetHelper.buildPacket(packetIDs.server_userID, [[-2, dataTypes.sInt32]])
|
||||
|
||||
def loginBanned():
|
||||
packets = packetHelper.buildPacket(packetIDs.server_userID, [[-1, dataTypes.SINT32]])
|
||||
packets += notification("You are banned. You can appeal after one month since your ban by sending an email to {} from the email address you've used to sign up.".format(glob.conf.extra["pep.py"]["support-email"]))
|
||||
return packets
|
||||
|
||||
def loginLocked():
|
||||
packets = packetHelper.buildPacket(packetIDs.server_userID, [[-1, dataTypes.SINT32]])
|
||||
packets += notification("Your account is locked. You can't log in, but your profile and scores are still visible from the website. If you want to unlock your account, send an email to {} from the email address you've used to sign up.".format(glob.conf.extra["pep.py"]["support-email"]))
|
||||
packets = packetHelper.buildPacket(packetIDs.server_userID, [[-1, dataTypes.sInt32]])
|
||||
packets += notification("You are banned. You can ask to get unbanned after 1 month since your ban by contacting support@ripple.moe")
|
||||
return packets
|
||||
#return packetHelper.buildPacket(packetIDs.server_userID, [[-3, dataTypes.sInt32]])
|
||||
|
||||
def loginError():
|
||||
return packetHelper.buildPacket(packetIDs.server_userID, [[-5, dataTypes.SINT32]])
|
||||
return packetHelper.buildPacket(packetIDs.server_userID, [[-5, dataTypes.sInt32]])
|
||||
|
||||
def needSupporter():
|
||||
return packetHelper.buildPacket(packetIDs.server_userID, [[-6, dataTypes.SINT32]])
|
||||
return packetHelper.buildPacket(packetIDs.server_userID, [[-6, dataTypes.sInt32]])
|
||||
|
||||
def needVerification():
|
||||
return packetHelper.buildPacket(packetIDs.server_userID, [[-8, dataTypes.SINT32]])
|
||||
|
||||
return packetHelper.buildPacket(packetIDs.server_userID, [[-8, dataTypes.sInt32]])
|
||||
|
||||
""" Login packets """
|
||||
def userID(uid):
|
||||
return packetHelper.buildPacket(packetIDs.server_userID, [[uid, dataTypes.SINT32]])
|
||||
return packetHelper.buildPacket(packetIDs.server_userID, [[uid, dataTypes.sInt32]])
|
||||
|
||||
def silenceEndTime(seconds):
|
||||
return packetHelper.buildPacket(packetIDs.server_silenceEnd, [[seconds, dataTypes.UINT32]])
|
||||
return packetHelper.buildPacket(packetIDs.server_silenceEnd, [[seconds, dataTypes.uInt32]])
|
||||
|
||||
def protocolVersion(version = 19):
|
||||
return packetHelper.buildPacket(packetIDs.server_protocolVersion, [[version, dataTypes.UINT32]])
|
||||
return packetHelper.buildPacket(packetIDs.server_protocolVersion, [[version, dataTypes.uInt32]])
|
||||
|
||||
def mainMenuIcon(icon):
|
||||
return packetHelper.buildPacket(packetIDs.server_mainMenuIcon, [[icon, dataTypes.STRING]])
|
||||
return packetHelper.buildPacket(packetIDs.server_mainMenuIcon, [[icon, dataTypes.string]])
|
||||
|
||||
def userSupporterGMT(supporter, GMT, tournamentStaff):
|
||||
def userSupporterGMT(supporter, GMT):
|
||||
result = 1
|
||||
if supporter:
|
||||
result |= userRanks.SUPPORTER
|
||||
if GMT:
|
||||
result |= userRanks.BAT
|
||||
if tournamentStaff:
|
||||
result |= userRanks.TOURNAMENT_STAFF
|
||||
return packetHelper.buildPacket(packetIDs.server_supporterGMT, [[result, dataTypes.UINT32]])
|
||||
if supporter == True:
|
||||
result += 4
|
||||
if GMT == True:
|
||||
result += 2
|
||||
return packetHelper.buildPacket(packetIDs.server_supporterGMT, [[result, dataTypes.uInt32]])
|
||||
|
||||
def friendList(userID):
|
||||
friends = userUtils.getFriendList(userID)
|
||||
return packetHelper.buildPacket(packetIDs.server_friendsList, [[friends, dataTypes.INT_LIST]])
|
||||
friendsData = []
|
||||
|
||||
# Get friend IDs from db
|
||||
friends = userHelper.getFriendList(userID)
|
||||
|
||||
# Friends number
|
||||
friendsData.append([len(friends), dataTypes.uInt16])
|
||||
|
||||
# Add all friend user IDs to friendsData
|
||||
for i in friends:
|
||||
friendsData.append([i, dataTypes.sInt32])
|
||||
|
||||
return packetHelper.buildPacket(packetIDs.server_friendsList, friendsData)
|
||||
|
||||
def onlineUsers():
|
||||
userIDs = []
|
||||
|
@ -67,25 +71,27 @@ def onlineUsers():
|
|||
|
||||
# Create list with all connected (and not restricted) users
|
||||
for _, value in users.items():
|
||||
if not value.restricted:
|
||||
if value.restricted == False:
|
||||
userIDs.append(value.userID)
|
||||
|
||||
return packetHelper.buildPacket(packetIDs.server_userPresenceBundle, [[userIDs, dataTypes.INT_LIST]])
|
||||
return packetHelper.buildPacket(packetIDs.server_userPresenceBundle, [[userIDs, dataTypes.intList]])
|
||||
|
||||
|
||||
""" Users packets """
|
||||
def userLogout(userID):
|
||||
return packetHelper.buildPacket(packetIDs.server_userLogout, [[userID, dataTypes.SINT32], [0, dataTypes.BYTE]])
|
||||
return packetHelper.buildPacket(packetIDs.server_userLogout, [[userID, dataTypes.sInt32], [0, dataTypes.byte]])
|
||||
|
||||
def userPanel(userID, force = False):
|
||||
# Connected and restricted check
|
||||
userToken = glob.tokens.getTokenFromUserID(userID)
|
||||
if userToken is None or ((userToken.restricted) and not force):
|
||||
if userToken == None:
|
||||
return bytes()
|
||||
if userToken.restricted == True and force == False:
|
||||
return bytes()
|
||||
|
||||
# Get user data
|
||||
username = userToken.username
|
||||
timezone = 24+userToken.timeOffset
|
||||
timezone = 24+userToken.timeOffset # TODO: Timezone
|
||||
country = userToken.country
|
||||
gameRank = userToken.gameRank
|
||||
latitude = userToken.getLatitude()
|
||||
|
@ -93,144 +99,134 @@ def userPanel(userID, force = False):
|
|||
|
||||
# Get username color according to rank
|
||||
# Only admins and normal users are currently supported
|
||||
userRank = 0
|
||||
if username == glob.BOT_NAME:
|
||||
userRank |= userRanks.MOD
|
||||
elif userUtils.isInPrivilegeGroup(userID, "developer"):
|
||||
userRank |= userRanks.ADMIN
|
||||
elif userUtils.isInPrivilegeGroup(userID, "chat mod"):
|
||||
userRank |= userRanks.MOD
|
||||
elif (userToken.privileges & privileges.USER_DONOR) > 0:
|
||||
userRank |= userRanks.SUPPORTER
|
||||
if username == "FokaBot":
|
||||
userRank = userRanks.MOD
|
||||
elif userHelper.isInPrivilegeGroup(userID, "community manager") == True:
|
||||
userRank = userRanks.MOD
|
||||
elif userHelper.isInPrivilegeGroup(userID, "developer") == True:
|
||||
userRank = userRanks.ADMIN
|
||||
elif userHelper.isInPrivilegeGroup(userID, "donor") == True:
|
||||
userRank = userRanks.SUPPORTER
|
||||
else:
|
||||
userRank |= userRanks.NORMAL
|
||||
userRank = userRanks.NORMAL
|
||||
|
||||
return packetHelper.buildPacket(packetIDs.server_userPanel,
|
||||
[
|
||||
[userID, dataTypes.SINT32],
|
||||
[username, dataTypes.STRING],
|
||||
[timezone, dataTypes.BYTE],
|
||||
[country, dataTypes.BYTE],
|
||||
[userRank, dataTypes.BYTE],
|
||||
[longitude, dataTypes.FFLOAT],
|
||||
[latitude, dataTypes.FFLOAT],
|
||||
[gameRank, dataTypes.UINT32]
|
||||
[userID, dataTypes.sInt32],
|
||||
[username, dataTypes.string],
|
||||
[timezone, dataTypes.byte],
|
||||
[country, dataTypes.byte],
|
||||
[userRank, dataTypes.byte],
|
||||
[longitude, dataTypes.ffloat],
|
||||
[latitude, dataTypes.ffloat],
|
||||
[gameRank, dataTypes.uInt32]
|
||||
])
|
||||
|
||||
|
||||
def userStats(userID, force = False):
|
||||
# Get userID's token from tokens list
|
||||
userToken = glob.tokens.getTokenFromUserID(userID)
|
||||
if userToken is None or ((userToken.restricted or userToken.irc or userToken.tournament) and not force):
|
||||
if userToken == None:
|
||||
return bytes()
|
||||
if (userToken.restricted == True or userToken.irc == True) and force == False:
|
||||
return bytes()
|
||||
|
||||
return packetHelper.buildPacket(packetIDs.server_userStats,
|
||||
[
|
||||
[userID, dataTypes.UINT32],
|
||||
[userToken.actionID, dataTypes.BYTE],
|
||||
[userToken.actionText, dataTypes.STRING],
|
||||
[userToken.actionMd5, dataTypes.STRING],
|
||||
[userToken.actionMods, dataTypes.SINT32],
|
||||
[userToken.gameMode, dataTypes.BYTE],
|
||||
[userToken.beatmapID, dataTypes.SINT32],
|
||||
[userToken.rankedScore, dataTypes.UINT64],
|
||||
[userToken.accuracy, dataTypes.FFLOAT],
|
||||
[userToken.playcount, dataTypes.UINT32],
|
||||
[userToken.totalScore, dataTypes.UINT64],
|
||||
[userToken.gameRank, dataTypes.UINT32],
|
||||
[userToken.pp if 65535 >= userToken.pp > 0 else 0, dataTypes.UINT16]
|
||||
[userID, dataTypes.uInt32],
|
||||
[userToken.actionID, dataTypes.byte],
|
||||
[userToken.actionText, dataTypes.string],
|
||||
[userToken.actionMd5, dataTypes.string],
|
||||
[userToken.actionMods, dataTypes.sInt32],
|
||||
[userToken.gameMode, dataTypes.byte],
|
||||
[0, dataTypes.sInt32],
|
||||
[userToken.rankedScore, dataTypes.uInt64],
|
||||
[userToken.accuracy, dataTypes.ffloat],
|
||||
[userToken.playcount, dataTypes.uInt32],
|
||||
[userToken.totalScore, dataTypes.uInt64],
|
||||
[userToken.gameRank, dataTypes.uInt32],
|
||||
[userToken.pp, dataTypes.uInt16]
|
||||
])
|
||||
|
||||
|
||||
""" Chat packets """
|
||||
def sendMessage(fro, to, message):
|
||||
return packetHelper.buildPacket(packetIDs.server_sendMessage, [
|
||||
[fro, dataTypes.STRING],
|
||||
[message, dataTypes.STRING],
|
||||
[to, dataTypes.STRING],
|
||||
[userUtils.getID(fro), dataTypes.SINT32]
|
||||
])
|
||||
return packetHelper.buildPacket(packetIDs.server_sendMessage, [[fro, dataTypes.string], [message, dataTypes.string], [to, dataTypes.string], [userHelper.getID(fro), dataTypes.sInt32]])
|
||||
|
||||
def channelJoinSuccess(userID, chan):
|
||||
return packetHelper.buildPacket(packetIDs.server_channelJoinSuccess, [[chan, dataTypes.STRING]])
|
||||
return packetHelper.buildPacket(packetIDs.server_channelJoinSuccess, [[chan, dataTypes.string]])
|
||||
|
||||
def channelInfo(chan):
|
||||
if chan not in glob.channels.channels:
|
||||
return bytes()
|
||||
channel = glob.channels.channels[chan]
|
||||
return packetHelper.buildPacket(packetIDs.server_channelInfo, [
|
||||
[channel.name, dataTypes.STRING],
|
||||
[channel.description, dataTypes.STRING],
|
||||
[len(glob.streams.streams["chat/{}".format(chan)].clients), dataTypes.UINT16]
|
||||
])
|
||||
return packetHelper.buildPacket(packetIDs.server_channelInfo, [[chan, dataTypes.string], [channel.description, dataTypes.string], [channel.getConnectedUsersCount(), dataTypes.uInt16]])
|
||||
|
||||
def channelInfoEnd():
|
||||
return packetHelper.buildPacket(packetIDs.server_channelInfoEnd, [[0, dataTypes.UINT32]])
|
||||
return packetHelper.buildPacket(packetIDs.server_channelInfoEnd, [[0, dataTypes.uInt32]])
|
||||
|
||||
def channelKicked(chan):
|
||||
return packetHelper.buildPacket(packetIDs.server_channelKicked, [[chan, dataTypes.STRING]])
|
||||
return packetHelper.buildPacket(packetIDs.server_channelKicked, [[chan, dataTypes.string]])
|
||||
|
||||
def userSilenced(userID):
|
||||
return packetHelper.buildPacket(packetIDs.server_userSilenced, [[userID, dataTypes.UINT32]])
|
||||
return packetHelper.buildPacket(packetIDs.server_userSilenced, [[userID, dataTypes.uInt32]])
|
||||
|
||||
|
||||
""" Spectator packets """
|
||||
def addSpectator(userID):
|
||||
return packetHelper.buildPacket(packetIDs.server_spectatorJoined, [[userID, dataTypes.SINT32]])
|
||||
return packetHelper.buildPacket(packetIDs.server_spectatorJoined, [[userID, dataTypes.sInt32]])
|
||||
|
||||
def removeSpectator(userID):
|
||||
return packetHelper.buildPacket(packetIDs.server_spectatorLeft, [[userID, dataTypes.SINT32]])
|
||||
return packetHelper.buildPacket(packetIDs.server_spectatorLeft, [[userID, dataTypes.sInt32]])
|
||||
|
||||
def spectatorFrames(data):
|
||||
return packetHelper.buildPacket(packetIDs.server_spectateFrames, [[data, dataTypes.BBYTES]])
|
||||
return packetHelper.buildPacket(packetIDs.server_spectateFrames, [[data, dataTypes.bbytes]])
|
||||
|
||||
def noSongSpectator(userID):
|
||||
return packetHelper.buildPacket(packetIDs.server_spectatorCantSpectate, [[userID, dataTypes.SINT32]])
|
||||
return packetHelper.buildPacket(packetIDs.server_spectatorCantSpectate, [[userID, dataTypes.sInt32]])
|
||||
|
||||
def fellowSpectatorJoined(userID):
|
||||
return packetHelper.buildPacket(packetIDs.server_fellowSpectatorJoined, [[userID, dataTypes.SINT32]])
|
||||
return packetHelper.buildPacket(packetIDs.server_fellowSpectatorJoined, [[userID, dataTypes.sInt32]])
|
||||
|
||||
def fellowSpectatorLeft(userID):
|
||||
return packetHelper.buildPacket(packetIDs.server_fellowSpectatorLeft, [[userID, dataTypes.SINT32]])
|
||||
return packetHelper.buildPacket(packetIDs.server_fellowSpectatorLeft, [[userID, dataTypes.sInt32]])
|
||||
|
||||
|
||||
""" Multiplayer Packets """
|
||||
def createMatch(matchID):
|
||||
# Make sure the match exists
|
||||
if matchID not in glob.matches.matches:
|
||||
return bytes()
|
||||
return None
|
||||
|
||||
# Get match binary data and build packet
|
||||
match = glob.matches.matches[matchID]
|
||||
matchData = match.getMatchData(censored=True)
|
||||
return packetHelper.buildPacket(packetIDs.server_newMatch, matchData)
|
||||
return packetHelper.buildPacket(packetIDs.server_newMatch, match.getMatchData())
|
||||
|
||||
# TODO: Add match object argument to save some CPU
|
||||
def updateMatch(matchID, censored = False):
|
||||
|
||||
def updateMatch(matchID):
|
||||
# Make sure the match exists
|
||||
if matchID not in glob.matches.matches:
|
||||
return bytes()
|
||||
return None
|
||||
|
||||
# Get match binary data and build packet
|
||||
match = glob.matches.matches[matchID]
|
||||
return packetHelper.buildPacket(packetIDs.server_updateMatch, match.getMatchData(censored=censored))
|
||||
return packetHelper.buildPacket(packetIDs.server_updateMatch, match.getMatchData())
|
||||
|
||||
|
||||
def matchStart(matchID):
|
||||
# Make sure the match exists
|
||||
if matchID not in glob.matches.matches:
|
||||
return bytes()
|
||||
return None
|
||||
|
||||
# Get match binary data and build packet
|
||||
match = glob.matches.matches[matchID]
|
||||
return packetHelper.buildPacket(packetIDs.server_matchStart, match.getMatchData())
|
||||
|
||||
|
||||
def disposeMatch(matchID):
|
||||
return packetHelper.buildPacket(packetIDs.server_disposeMatch, [[matchID, dataTypes.UINT32]])
|
||||
return packetHelper.buildPacket(packetIDs.server_disposeMatch, [[matchID, dataTypes.uInt16]])
|
||||
|
||||
def matchJoinSuccess(matchID):
|
||||
# Make sure the match exists
|
||||
if matchID not in glob.matches.matches:
|
||||
return bytes()
|
||||
return None
|
||||
|
||||
# Get match binary data and build packet
|
||||
match = glob.matches.matches[matchID]
|
||||
|
@ -241,41 +237,32 @@ def matchJoinFail():
|
|||
return packetHelper.buildPacket(packetIDs.server_matchJoinFail)
|
||||
|
||||
def changeMatchPassword(newPassword):
|
||||
return packetHelper.buildPacket(packetIDs.server_matchChangePassword, [[newPassword, dataTypes.STRING]])
|
||||
return packetHelper.buildPacket(packetIDs.server_matchChangePassword, [[newPassword, dataTypes.string]])
|
||||
|
||||
def allPlayersLoaded():
|
||||
return packetHelper.buildPacket(packetIDs.server_matchAllPlayersLoaded)
|
||||
|
||||
def playerSkipped(userID):
|
||||
return packetHelper.buildPacket(packetIDs.server_matchPlayerSkipped, [[userID, dataTypes.SINT32]])
|
||||
return packetHelper.buildPacket(packetIDs.server_matchPlayerSkipped, [[userID, dataTypes.sInt32]])
|
||||
|
||||
def allPlayersSkipped():
|
||||
return packetHelper.buildPacket(packetIDs.server_matchSkip)
|
||||
|
||||
def matchFrames(slotID, data):
|
||||
return packetHelper.buildPacket(packetIDs.server_matchScoreUpdate, [[data[7:11], dataTypes.BBYTES], [slotID, dataTypes.BYTE], [data[12:], dataTypes.BBYTES]])
|
||||
return packetHelper.buildPacket(packetIDs.server_matchScoreUpdate, [[data[7:11], dataTypes.bbytes], [slotID, dataTypes.byte], [data[12:], dataTypes.bbytes]])
|
||||
|
||||
def matchComplete():
|
||||
return packetHelper.buildPacket(packetIDs.server_matchComplete)
|
||||
|
||||
def playerFailed(slotID):
|
||||
return packetHelper.buildPacket(packetIDs.server_matchPlayerFailed, [[slotID, dataTypes.UINT32]])
|
||||
return packetHelper.buildPacket(packetIDs.server_matchPlayerFailed, [[slotID, dataTypes.uInt32]])
|
||||
|
||||
def matchTransferHost():
|
||||
return packetHelper.buildPacket(packetIDs.server_matchTransferHost)
|
||||
|
||||
def matchAbort():
|
||||
return packetHelper.buildPacket(packetIDs.server_matchAbort)
|
||||
|
||||
def switchServer(address):
|
||||
return packetHelper.buildPacket(packetIDs.server_switchServer, [[address, dataTypes.STRING]])
|
||||
|
||||
""" Other packets """
|
||||
def notification(message):
|
||||
return packetHelper.buildPacket(packetIDs.server_notification, [[message, dataTypes.STRING]])
|
||||
return packetHelper.buildPacket(packetIDs.server_notification, [[message, dataTypes.string]])
|
||||
|
||||
def banchoRestart(msUntilReconnection):
|
||||
return packetHelper.buildPacket(packetIDs.server_restart, [[msUntilReconnection, dataTypes.UINT32]])
|
||||
|
||||
def rtx(message):
|
||||
return packetHelper.buildPacket(0x69, [[message, dataTypes.STRING]])
|
||||
return packetHelper.buildPacket(packetIDs.server_restart, [[msUntilReconnection, dataTypes.uInt32]])
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
FREE = 1
|
||||
LOCKED = 2
|
||||
NOT_READY = 4
|
||||
READY = 8
|
||||
NO_MAP = 16
|
||||
PLAYING = 32
|
||||
OCCUPIED = 124
|
||||
PLAYING_QUIT = 128
|
||||
free = 1
|
||||
locked = 2
|
||||
notReady = 4
|
||||
ready = 8
|
||||
noMap = 16
|
||||
playing = 32
|
||||
occupied = 124
|
||||
playingQuit = 128
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
"""Bancho user ranks"""
|
||||
NORMAL = 0
|
||||
PLAYER = 1
|
||||
BAT = 2
|
||||
SUPPORTER = 4
|
||||
MOD = 6
|
||||
PEPPY = 8
|
||||
ADMIN = 16
|
||||
TOURNAMENT_STAFF = 32
|
||||
TOURNAMENTSTAFF = 32
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
from common.log import logUtils as log
|
||||
from constants import exceptions
|
||||
from constants import serverPackets
|
||||
from objects import glob
|
||||
from constants import serverPackets
|
||||
from constants import exceptions
|
||||
from helpers import logHelper as log
|
||||
|
||||
def handle(userToken, packetData):
|
||||
# get usertoken data
|
||||
userID = userToken.userID
|
||||
|
||||
def handle(userToken, _):
|
||||
try:
|
||||
# We don't have the beatmap, we can't spectate
|
||||
if userToken.spectating not in glob.tokens.tokens:
|
||||
raise exceptions.tokenNotFoundException()
|
||||
target = userToken.spectating
|
||||
targetToken = glob.tokens.getTokenFromUserID(target)
|
||||
|
||||
# Send the packet to host
|
||||
glob.tokens.tokens[userToken.spectating].enqueue(serverPackets.noSongSpectator(userToken.userID))
|
||||
targetToken.enqueue(serverPackets.noSongSpectator(userID))
|
||||
except exceptions.tokenNotFoundException:
|
||||
# Stop spectating if token not found
|
||||
log.warning("Spectator can't spectate: token not found")
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
from common.log import logUtils as log
|
||||
from objects import glob
|
||||
from constants import clientPackets
|
||||
from constants import serverPackets
|
||||
from objects import glob
|
||||
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
|
||||
|
@ -9,55 +12,55 @@ def handle(userToken, packetData):
|
|||
username = userToken.username
|
||||
|
||||
# Make sure we are not banned
|
||||
#if userUtils.isBanned(userID):
|
||||
# userToken.enqueue(serverPackets.loginBanned())
|
||||
# return
|
||||
if userHelper.isBanned(userID) == True:
|
||||
userToken.enqueue(serverPackets.loginBanned())
|
||||
return
|
||||
|
||||
# Send restricted message if needed
|
||||
#if userToken.restricted:
|
||||
# userToken.checkRestricted(True)
|
||||
if userToken.restricted == False:
|
||||
if userHelper.isRestricted(userID) == True:
|
||||
userToken.setRestricted()
|
||||
|
||||
# Change action packet
|
||||
packetData = clientPackets.userActionChange(packetData)
|
||||
|
||||
# If we are not in spectate status but we're spectating someone, stop spectating
|
||||
'''
|
||||
if userToken.spectating != 0 and userToken.actionID != actions.WATCHING and userToken.actionID != actions.IDLE and userToken.actionID != actions.AFK:
|
||||
userToken.stopSpectating()
|
||||
|
||||
# If we are not in multiplayer but we are in a match, part match
|
||||
if userToken.matchID != -1 and userToken.actionID != actions.MULTIPLAYING and userToken.actionID != actions.MULTIPLAYER and userToken.actionID != actions.AFK:
|
||||
userToken.partMatch()
|
||||
'''
|
||||
|
||||
# Update cached stats if our pp changed if we've just submitted a score or we've changed gameMode
|
||||
#if (userToken.actionID == actions.PLAYING or userToken.actionID == actions.MULTIPLAYING) or (userToken.pp != userUtils.getPP(userID, userToken.gameMode)) or (userToken.gameMode != packetData["gameMode"]):
|
||||
|
||||
# Update cached stats if we've changed gamemode
|
||||
if userToken.gameMode != packetData["gameMode"]:
|
||||
# Update cached stats if our pp changedm if we've just submitted a score or we've changed gameMode
|
||||
if (userToken.actionID == actions.playing or userToken.actionID == actions.multiplaying) or (userToken.pp != userHelper.getPP(userID, userToken.gameMode)) or (userToken.gameMode != packetData["gameMode"]):
|
||||
log.debug("!!!! UPDATING CACHED STATS !!!!")
|
||||
# Always update game mode, or we'll cache stats from the wrong game mode if we've changed it
|
||||
userToken.gameMode = packetData["gameMode"]
|
||||
userToken.updateCachedStats()
|
||||
|
||||
# Always update action id, text, md5 and beatmapID
|
||||
# Always update action id, text and md5
|
||||
userToken.actionID = packetData["actionID"]
|
||||
userToken.actionText = packetData["actionText"]
|
||||
userToken.actionMd5 = packetData["actionMd5"]
|
||||
userToken.actionMods = packetData["actionMods"]
|
||||
userToken.beatmapID = packetData["beatmapID"]
|
||||
|
||||
# Enqueue our new user panel and stats to us and our spectators
|
||||
recipients = [userToken]
|
||||
recipients = [userID]
|
||||
if len(userToken.spectators) > 0:
|
||||
for i in userToken.spectators:
|
||||
if i in glob.tokens.tokens:
|
||||
recipients.append(glob.tokens.tokens[i])
|
||||
recipients += userToken.spectators
|
||||
|
||||
for i in recipients:
|
||||
if i is not None:
|
||||
if i == userID:
|
||||
# Save some loops
|
||||
token = userToken
|
||||
else:
|
||||
token = glob.tokens.getTokenFromUserID(i)
|
||||
|
||||
if token != None:
|
||||
# Force our own packet
|
||||
force = True if i == userToken else False
|
||||
i.enqueue(serverPackets.userPanel(userID, force))
|
||||
i.enqueue(serverPackets.userStats(userID, force))
|
||||
force = True if token.userID == userID else False
|
||||
token.enqueue(serverPackets.userPanel(userID, force))
|
||||
token.enqueue(serverPackets.userStats(userID, force))
|
||||
|
||||
# Send osu!direct alert if needed
|
||||
# NOTE: Remove this when osu!direct will be fixed
|
||||
if userToken.actionID == actions.osuDirect and userToken.osuDirectAlert == False:
|
||||
userToken.osuDirectAlert = True
|
||||
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
|
||||
log.info("{} changed action: {} [{}][{}][{}]".format(username, str(userToken.actionID), userToken.actionText, userToken.actionMd5, userToken.beatmapID))
|
||||
log.info("{} changed action: {} [{}][{}]".format(username, str(userToken.actionID), userToken.actionText, userToken.actionMd5))
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
from common.constants import mods
|
||||
from objects import glob
|
||||
from constants import clientPackets
|
||||
from constants import matchModModes
|
||||
from objects import glob
|
||||
|
||||
from constants import mods
|
||||
|
||||
def handle(userToken, packetData):
|
||||
# Get token data
|
||||
|
@ -15,29 +14,30 @@ def handle(userToken, packetData):
|
|||
matchID = userToken.matchID
|
||||
if matchID not in glob.matches.matches:
|
||||
return
|
||||
match = glob.matches.matches[matchID]
|
||||
|
||||
# Set slot or match mods according to modType
|
||||
with glob.matches.matches[matchID] as match:
|
||||
if match.matchModMode == matchModModes.FREE_MOD:
|
||||
# Freemod
|
||||
# Host can set global DT/HT
|
||||
if userID == match.hostUserID:
|
||||
# If host has selected DT/HT and Freemod is enabled, set DT/HT as match mod
|
||||
if (packetData["mods"] & mods.DOUBLETIME) > 0:
|
||||
match.changeMods(mods.DOUBLETIME)
|
||||
# Nightcore
|
||||
if (packetData["mods"] & mods.NIGHTCORE) > 0:
|
||||
match.changeMods(match.mods + mods.NIGHTCORE)
|
||||
elif (packetData["mods"] & mods.HALFTIME) > 0:
|
||||
match.changeMods(mods.HALFTIME)
|
||||
else:
|
||||
# No DT/HT, set global mods to 0 (we are in freemod mode)
|
||||
match.changeMods(0)
|
||||
if match.matchModMode == matchModModes.freeMod:
|
||||
# Freemod
|
||||
|
||||
# Set slot mods
|
||||
slotID = match.getUserSlotID(userID)
|
||||
if slotID is not None:
|
||||
match.setSlotMods(slotID, packetData["mods"])
|
||||
else:
|
||||
# Not freemod, set match mods
|
||||
match.changeMods(packetData["mods"])
|
||||
# Host can set global DT/HT
|
||||
if userID == match.hostUserID:
|
||||
# If host has selected DT/HT and Freemod is enabled, set DT/HT as match mod
|
||||
if (packetData["mods"] & mods.DoubleTime) > 0:
|
||||
match.changeMatchMods(mods.DoubleTime)
|
||||
# Nighcore
|
||||
if (packetData["mods"] & mods.Nightcore) > 0:
|
||||
match.changeMatchMods(match.mods+mods.Nightcore)
|
||||
elif (packetData["mods"] & mods.HalfTime) > 0:
|
||||
match.changeMatchMods(mods.HalfTime)
|
||||
else:
|
||||
# No DT/HT, set global mods to 0 (we are in freemod mode)
|
||||
match.changeMatchMods(0)
|
||||
|
||||
# Set slot mods
|
||||
slotID = match.getUserSlotID(userID)
|
||||
if slotID != None:
|
||||
match.setSlotMods(slotID, packetData["mods"])
|
||||
else:
|
||||
# Not freemod, set match mods
|
||||
match.changeMatchMods(packetData["mods"])
|
||||
|
|
|
@ -10,10 +10,8 @@ def handle(userToken, packetData):
|
|||
if matchID not in glob.matches.matches:
|
||||
return
|
||||
|
||||
with glob.matches.matches[matchID] as match:
|
||||
# Host check
|
||||
if userToken.userID != match.hostUserID:
|
||||
return
|
||||
# Get our match
|
||||
match = glob.matches.matches[matchID]
|
||||
|
||||
# Update match password
|
||||
match.changePassword(packetData["matchPassword"])
|
||||
# Update match password
|
||||
match.changePassword(packetData["matchPassword"])
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import random
|
||||
|
||||
from common import generalUtils
|
||||
from common.log import logUtils as log
|
||||
from objects import glob
|
||||
from constants import clientPackets
|
||||
from constants import matchModModes
|
||||
from helpers import consoleHelper
|
||||
from constants import bcolors
|
||||
import random
|
||||
from constants import matchTeamTypes
|
||||
from constants import matchTeams
|
||||
from constants import slotStatuses
|
||||
from objects import glob
|
||||
|
||||
from helpers import logHelper as log
|
||||
|
||||
def handle(userToken, packetData):
|
||||
# Read new settings
|
||||
|
@ -21,84 +20,91 @@ def handle(userToken, packetData):
|
|||
if matchID not in glob.matches.matches:
|
||||
return
|
||||
|
||||
# Host check
|
||||
with glob.matches.matches[matchID] as match:
|
||||
if userToken.userID != match.hostUserID:
|
||||
return
|
||||
# Get match object
|
||||
match = glob.matches.matches[matchID]
|
||||
|
||||
# Some dank memes easter egg
|
||||
memeTitles = [
|
||||
"RWC 2020",
|
||||
"Fokabot is a duck",
|
||||
"Dank memes",
|
||||
"1337ms Ping",
|
||||
"Iscriviti a Xenotoze",
|
||||
"...e i marò?",
|
||||
"Superman dies",
|
||||
"The brace is on fire",
|
||||
"print_foot()",
|
||||
"#FREEZEBARKEZ",
|
||||
"Ripple devs are actually cats",
|
||||
"Thank Mr Shaural",
|
||||
"NEVER GIVE UP",
|
||||
"T I E D W I T H U N I T E D",
|
||||
"HIGHEST HDHR LOBBY OF ALL TIME",
|
||||
"This is gasoline and I set myself on fire",
|
||||
"Everyone is cheating apparently",
|
||||
"Kurwa mac",
|
||||
"TATOE",
|
||||
"This is not your drama landfill.",
|
||||
"I like cheese",
|
||||
"NYO IS NOT A CAT HE IS A DO(N)G",
|
||||
"Datingu startuato"
|
||||
]
|
||||
# Some dank memes easter egg
|
||||
memeTitles = [
|
||||
"RWC 2020",
|
||||
"Fokabot is a duck",
|
||||
"Dank memes",
|
||||
"1337ms Ping",
|
||||
"Iscriviti a Xenotoze",
|
||||
"...e i marò?",
|
||||
"Superman dies",
|
||||
"The brace is on fire",
|
||||
"print_foot()",
|
||||
"#FREEZEBARKEZ",
|
||||
"Ripple devs are actually cats",
|
||||
"Thank Mr Shaural",
|
||||
"NEVER GIVE UP",
|
||||
"T I E D W I T H U N I T E D",
|
||||
"HIGHEST HDHR LOBBY OF ALL TIME",
|
||||
"This is gasoline and I set myself on fire",
|
||||
"Everyone is cheating apparently",
|
||||
"Kurwa mac",
|
||||
"TATOE",
|
||||
"This is not your drama landfill.",
|
||||
"I like cheese",
|
||||
"NYO IS NOT A CAT HE IS A DO(N)G",
|
||||
"Datingu startuato"
|
||||
]
|
||||
|
||||
# Set match name
|
||||
match.matchName = packetData["matchName"] if packetData["matchName"] != "meme" else random.choice(memeTitles)
|
||||
# Set match name
|
||||
match.matchName = packetData["matchName"] if packetData["matchName"] != "meme" else random.choice(memeTitles)
|
||||
|
||||
# Update match settings
|
||||
match.inProgress = packetData["inProgress"]
|
||||
if packetData["matchPassword"] != "":
|
||||
match.matchPassword = generalUtils.stringMd5(packetData["matchPassword"])
|
||||
else:
|
||||
match.matchPassword = ""
|
||||
match.beatmapName = packetData["beatmapName"]
|
||||
match.beatmapID = packetData["beatmapID"]
|
||||
match.hostUserID = packetData["hostUserID"]
|
||||
match.gameMode = packetData["gameMode"]
|
||||
# Update match settings
|
||||
match.inProgress = packetData["inProgress"]
|
||||
match.matchPassword = packetData["matchPassword"]
|
||||
match.beatmapName = packetData["beatmapName"]
|
||||
match.beatmapID = packetData["beatmapID"]
|
||||
match.hostUserID = packetData["hostUserID"]
|
||||
match.gameMode = packetData["gameMode"]
|
||||
|
||||
oldBeatmapMD5 = match.beatmapMD5
|
||||
oldMods = match.mods
|
||||
oldMatchTeamType = match.matchTeamType
|
||||
oldBeatmapMD5 = match.beatmapMD5
|
||||
oldMods = match.mods
|
||||
|
||||
match.mods = packetData["mods"]
|
||||
match.beatmapMD5 = packetData["beatmapMD5"]
|
||||
match.matchScoringType = packetData["scoringType"]
|
||||
match.matchTeamType = packetData["teamType"]
|
||||
match.matchModMode = packetData["freeMods"]
|
||||
match.mods = packetData["mods"]
|
||||
match.beatmapMD5 = packetData["beatmapMD5"]
|
||||
match.matchScoringType = packetData["scoringType"]
|
||||
match.matchTeamType = packetData["teamType"]
|
||||
match.matchModMode = packetData["freeMods"]
|
||||
|
||||
# Reset ready if needed
|
||||
if oldMods != match.mods or oldBeatmapMD5 != match.beatmapMD5:
|
||||
match.resetReady()
|
||||
# Reset ready if needed
|
||||
if oldMods != match.mods or oldBeatmapMD5 != match.beatmapMD5:
|
||||
for i in range(0,16):
|
||||
if match.slots[i]["status"] == slotStatuses.ready:
|
||||
match.slots[i]["status"] = slotStatuses.notReady
|
||||
|
||||
# Reset mods if needed
|
||||
if match.matchModMode == matchModModes.NORMAL:
|
||||
# Reset slot mods if not freeMods
|
||||
match.resetMods()
|
||||
else:
|
||||
# Reset match mods if freemod
|
||||
match.mods = 0
|
||||
# Reset mods if needed
|
||||
if match.matchModMode == matchModModes.normal:
|
||||
# Reset slot mods if not freeMods
|
||||
for i in range(0,16):
|
||||
match.slots[i]["mods"] = 0
|
||||
else:
|
||||
# Reset match mods if freemod
|
||||
match.mods = 0
|
||||
|
||||
# Initialize teams if team type changed
|
||||
if match.matchTeamType != oldMatchTeamType:
|
||||
match.initializeTeams()
|
||||
# Set/reset teams
|
||||
if match.matchTeamType == matchTeamTypes.teamVs or match.matchTeamType == matchTeamTypes.tagTeamVs:
|
||||
# Set teams
|
||||
c=0
|
||||
for i in range(0,16):
|
||||
if match.slots[i]["team"] == matchTeams.noTeam:
|
||||
match.slots[i]["team"] = matchTeams.red if c % 2 == 0 else matchTeams.blue
|
||||
c+=1
|
||||
else:
|
||||
# Reset teams
|
||||
for i in range(0,16):
|
||||
match.slots[i]["team"] = matchTeams.noTeam
|
||||
|
||||
# Force no freemods if tag coop
|
||||
if match.matchTeamType == matchTeamTypes.TAG_COOP or match.matchTeamType == matchTeamTypes.TAG_TEAM_VS:
|
||||
match.matchModMode = matchModModes.NORMAL
|
||||
# Force no freemods if tag coop
|
||||
if match.matchTeamType == matchTeamTypes.tagCoop or match.matchTeamType == matchTeamTypes.tagTeamVs:
|
||||
match.matchModMode = matchModModes.normal
|
||||
|
||||
# Send updated settings
|
||||
match.sendUpdates()
|
||||
# Send updated settings
|
||||
match.sendUpdate()
|
||||
|
||||
# Console output
|
||||
log.info("MPROOM{}: Updated room settings".format(match.matchID))
|
||||
# Console output
|
||||
log.info("MPROOM{}: Updated room settings".format(match.matchID))
|
||||
#consoleHelper.printColored("> MPROOM{}: DEBUG: Host is {}".format(match.matchID, match.hostUserID), bcolors.PINK)
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
from constants import clientPackets
|
||||
from objects import glob
|
||||
from helpers import consoleHelper
|
||||
from constants import bcolors
|
||||
|
||||
def handle(userToken, packetData):
|
||||
# Get usertoken data
|
||||
userID = userToken.userID
|
||||
username = userToken.username
|
||||
|
||||
# Read packet data
|
||||
packetData = clientPackets.changeSlot(packetData)
|
||||
|
||||
with glob.matches.matches[userToken.matchID] as match:
|
||||
# Change slot
|
||||
match.userChangeSlot(userID, packetData["slotID"])
|
||||
# Get match
|
||||
match = glob.matches.matches[userToken.matchID]
|
||||
|
||||
# Change slot
|
||||
match.userChangeSlot(userID, packetData["slotID"])
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
"""
|
||||
Event called when someone parts a channel
|
||||
"""
|
||||
from constants import clientPackets
|
||||
from helpers import chatHelper as chat
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
from common.log import logUtils as log
|
||||
from constants import clientPackets, serverPackets
|
||||
from constants import exceptions
|
||||
from constants import serverPackets
|
||||
from constants import clientPackets
|
||||
from objects import glob
|
||||
|
||||
from events import joinMatchEvent
|
||||
from constants import exceptions
|
||||
from helpers import logHelper as log
|
||||
|
||||
def handle(userToken, packetData):
|
||||
try:
|
||||
|
@ -12,32 +13,31 @@ def handle(userToken, packetData):
|
|||
# Read packet data
|
||||
packetData = clientPackets.createMatch(packetData)
|
||||
|
||||
# Make sure the name is valid
|
||||
matchName = packetData["matchName"].strip()
|
||||
if not matchName:
|
||||
raise exceptions.matchCreateError()
|
||||
|
||||
# Create a match object
|
||||
# TODO: Player number check (Dirty hack below)
|
||||
matchID = glob.matches.createMatch(matchName, packetData["matchPassword"].strip(), packetData["beatmapID"], packetData["beatmapName"], packetData["beatmapMD5"], packetData["gameMode"], userID)
|
||||
# TODO: Player number check
|
||||
matchID = glob.matches.createMatch(packetData["matchName"], packetData["matchPassword"], packetData["beatmapID"], packetData["beatmapName"], packetData["beatmapMD5"], packetData["gameMode"], userID)
|
||||
|
||||
# Make sure the match has been created
|
||||
if matchID not in glob.matches.matches:
|
||||
raise exceptions.matchCreateError()
|
||||
raise exceptions.matchCreateError
|
||||
|
||||
with glob.matches.matches[matchID] as match:
|
||||
# Join that match
|
||||
userToken.joinMatch(matchID)
|
||||
# Get match object
|
||||
match = glob.matches.matches[matchID]
|
||||
|
||||
# Disable slots (Dirty)
|
||||
for i in range(0,16):
|
||||
if match.slots[i].status is not 4:
|
||||
match.slots[i].status = packetData["slot{}Status".format(i)]
|
||||
# Join that match
|
||||
joinMatchEvent.joinMatch(userToken, matchID, packetData["matchPassword"])
|
||||
|
||||
# Give host to match creator
|
||||
match.setHost(userID)
|
||||
match.sendUpdates()
|
||||
match.changePassword(packetData["matchPassword"])
|
||||
# Give host to match creator
|
||||
match.setHost(userID)
|
||||
|
||||
# Send match create packet to everyone in lobby
|
||||
for i in glob.matches.usersInLobby:
|
||||
# Make sure this user is still connected
|
||||
token = glob.tokens.getTokenFromUserID(i)
|
||||
if token != None:
|
||||
token.enqueue(serverPackets.createMatch(matchID))
|
||||
|
||||
# Console output
|
||||
log.info("MPROOM{}: Room created!".format(matchID))
|
||||
except exceptions.matchCreateError:
|
||||
log.error("Error while creating match!")
|
||||
userToken.enqueue(serverPackets.matchJoinFail())
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
from common.log import logUtils as log
|
||||
from common.ripple import userUtils
|
||||
from helpers import userHelper
|
||||
from constants import clientPackets
|
||||
|
||||
from helpers import logHelper as log
|
||||
|
||||
def handle(userToken, packetData):
|
||||
# Friend add packet
|
||||
packetData = clientPackets.addRemoveFriend(packetData)
|
||||
userUtils.addFriend(userToken.userID, packetData["friendID"])
|
||||
userHelper.addFriend(userToken.userID, packetData["friendID"])
|
||||
|
||||
# Console output
|
||||
log.info("{} have added {} to their friends".format(userToken.username, str(packetData["friendID"])))
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
from common.log import logUtils as log
|
||||
from common.ripple import userUtils
|
||||
from helpers import userHelper
|
||||
from constants import clientPackets
|
||||
|
||||
from helpers import logHelper as log
|
||||
|
||||
def handle(userToken, packetData):
|
||||
# Friend remove packet
|
||||
packetData = clientPackets.addRemoveFriend(packetData)
|
||||
userUtils.removeFriend(userToken.userID, packetData["friendID"])
|
||||
userHelper.removeFriend(userToken.userID, packetData["friendID"])
|
||||
|
||||
# Console output
|
||||
log.info("{} have removed {} from their friends".format(userToken.username, str(packetData["friendID"])))
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
from common.log import logUtils as log
|
||||
from constants import serverPackets
|
||||
from objects import glob
|
||||
|
||||
from helpers import logHelper as log
|
||||
|
||||
def handle(userToken, _):
|
||||
# Get userToken data
|
||||
username = userToken.username
|
||||
userID = userToken.userID
|
||||
|
||||
# Add user to users in lobby
|
||||
userToken.joinStream("lobby")
|
||||
glob.matches.lobbyUserJoin(userID)
|
||||
|
||||
# Send matches data
|
||||
for key, _ in glob.matches.matches.items():
|
||||
|
|
|
@ -1,33 +1,57 @@
|
|||
from common.log import logUtils as log
|
||||
from constants import clientPackets
|
||||
from constants import exceptions
|
||||
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
|
||||
packetData = clientPackets.joinMatch(packetData)
|
||||
matchID = packetData["matchID"]
|
||||
password = packetData["password"]
|
||||
|
||||
# Get match from ID
|
||||
joinMatch(userToken, packetData["matchID"], packetData["password"])
|
||||
|
||||
def joinMatch(userToken, matchID, password):
|
||||
try:
|
||||
# TODO: leave other matches
|
||||
# TODO: Stop spectating
|
||||
|
||||
# get usertoken data
|
||||
userID = userToken.userID
|
||||
|
||||
# Make sure the match exists
|
||||
if matchID not in glob.matches.matches:
|
||||
return
|
||||
raise exceptions.matchNotFoundException
|
||||
|
||||
# Hash password if needed
|
||||
# if password != "":
|
||||
# password = generalUtils.stringMd5(password)
|
||||
# Match exists, get object
|
||||
match = glob.matches.matches[matchID]
|
||||
|
||||
# Check password
|
||||
with glob.matches.matches[matchID] as match:
|
||||
if match.matchPassword != "" and match.matchPassword != password:
|
||||
raise exceptions.matchWrongPasswordException()
|
||||
# TODO: Admins can enter every match
|
||||
if match.matchPassword != "":
|
||||
if match.matchPassword != password:
|
||||
raise exceptions.matchWrongPasswordException
|
||||
|
||||
# Password is correct, join match
|
||||
userToken.joinMatch(matchID)
|
||||
# Password is correct, join match
|
||||
result = match.userJoin(userID)
|
||||
|
||||
# Check if we've joined the match successfully
|
||||
if result == False:
|
||||
raise exceptions.matchJoinErrorException
|
||||
|
||||
# Match joined, set matchID for usertoken
|
||||
userToken.joinMatch(matchID)
|
||||
|
||||
# Send packets
|
||||
userToken.enqueue(serverPackets.matchJoinSuccess(matchID))
|
||||
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))
|
||||
except exceptions.matchWrongPasswordException:
|
||||
userToken.enqueue(serverPackets.matchJoinFail())
|
||||
log.warning("{} has tried to join a mp room, but he typed the wrong password".format(userToken.username))
|
||||
except exceptions.matchJoinErrorException:
|
||||
userToken.enqueue(serverPackets.matchJoinFail())
|
||||
log.warning("{} has tried to join a mp room, but an error has occured".format(userToken.username))
|
||||
|
|
|
@ -1,21 +1,23 @@
|
|||
import sys
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from common.constants import privileges
|
||||
from common.log import logUtils as log
|
||||
from common.ripple import userUtils
|
||||
from constants import exceptions
|
||||
from helpers import userHelper
|
||||
from constants import serverPackets
|
||||
from helpers import chatHelper as chat
|
||||
from helpers import countryHelper
|
||||
from helpers import locationHelper
|
||||
from constants import exceptions
|
||||
from objects import glob
|
||||
|
||||
from helpers import consoleHelper
|
||||
from constants import bcolors
|
||||
from helpers import locationHelper
|
||||
from helpers import countryHelper
|
||||
import time
|
||||
from helpers import generalFunctions
|
||||
import sys
|
||||
import traceback
|
||||
from helpers import requestHelper
|
||||
from helpers import discordBotHelper
|
||||
from helpers import logHelper as log
|
||||
from helpers import chatHelper as chat
|
||||
from constants import privileges
|
||||
|
||||
def handle(tornadoRequest):
|
||||
# Data to return
|
||||
responseToken = None
|
||||
responseTokenString = "ayy"
|
||||
responseData = bytes()
|
||||
|
||||
|
@ -30,9 +32,12 @@ def handle(tornadoRequest):
|
|||
# 2:-3 thing is because requestData has some escape stuff that we don't need
|
||||
loginData = str(tornadoRequest.request.body)[2:-3].split("\\n")
|
||||
try:
|
||||
# If true, print error to console
|
||||
err = False
|
||||
|
||||
# Make sure loginData is valid
|
||||
if len(loginData) < 3:
|
||||
raise exceptions.invalidArgumentsException()
|
||||
raise exceptions.haxException()
|
||||
|
||||
# Get HWID, MAC address and more
|
||||
# Structure (new line = "|", already split)
|
||||
|
@ -44,30 +49,29 @@ def handle(tornadoRequest):
|
|||
splitData = loginData[2].split("|")
|
||||
osuVersion = splitData[0]
|
||||
timeOffset = int(splitData[1])
|
||||
print(str(timeOffset))
|
||||
clientData = splitData[3].split(":")[:5]
|
||||
if len(clientData) < 4:
|
||||
raise exceptions.forceUpdateException()
|
||||
|
||||
# Try to get the ID from username
|
||||
username = str(loginData[0])
|
||||
userID = userUtils.getID(username)
|
||||
userID = userHelper.getID(username)
|
||||
|
||||
if not userID:
|
||||
if userID == False:
|
||||
# Invalid username
|
||||
raise exceptions.loginFailedException()
|
||||
if not userUtils.checkLogin(userID, loginData[1]):
|
||||
if userHelper.checkLogin(userID, loginData[1]) == False:
|
||||
# Invalid password
|
||||
raise exceptions.loginFailedException()
|
||||
|
||||
# Make sure we are not banned or locked
|
||||
priv = userUtils.getPrivileges(userID)
|
||||
if userUtils.isBanned(userID) and priv & privileges.USER_PENDING_VERIFICATION == 0:
|
||||
# Make sure we are not banned
|
||||
priv = userHelper.getPrivileges(userID)
|
||||
if userHelper.isBanned(userID) == True and priv & privileges.USER_PENDING_VERIFICATION == 0:
|
||||
raise exceptions.loginBannedException()
|
||||
if userUtils.isLocked(userID) and priv & privileges.USER_PENDING_VERIFICATION == 0:
|
||||
raise exceptions.loginLockedException()
|
||||
|
||||
# 2FA check
|
||||
if userUtils.check2FA(userID, requestIP):
|
||||
if userHelper.check2FA(userID, requestIP) == True:
|
||||
log.warning("Need 2FA check for user {}".format(loginData[0]))
|
||||
raise exceptions.need2FAException()
|
||||
|
||||
|
@ -75,8 +79,8 @@ def handle(tornadoRequest):
|
|||
|
||||
# Verify this user (if pending activation)
|
||||
firstLogin = False
|
||||
if priv & privileges.USER_PENDING_VERIFICATION > 0 or not userUtils.hasVerifiedHardware(userID):
|
||||
if userUtils.verifyUser(userID, clientData):
|
||||
if priv & privileges.USER_PENDING_VERIFICATION > 0 or userHelper.hasVerifiedHardware(userID) == False:
|
||||
if userHelper.verifyUser(userID, clientData) == True:
|
||||
# Valid account
|
||||
log.info("Account {} verified successfully!".format(userID))
|
||||
glob.verifiedCache[str(userID)] = 1
|
||||
|
@ -89,41 +93,27 @@ def handle(tornadoRequest):
|
|||
|
||||
|
||||
# Save HWID in db for multiaccount detection
|
||||
hwAllowed = userUtils.logHardware(userID, clientData, firstLogin)
|
||||
hwAllowed = userHelper.logHardware(userID, clientData, firstLogin)
|
||||
|
||||
# This is false only if HWID is empty
|
||||
# if HWID is banned, we get restricted so there's no
|
||||
# need to deny bancho access
|
||||
if not hwAllowed:
|
||||
if hwAllowed == False:
|
||||
raise exceptions.haxException()
|
||||
|
||||
# Log user IP
|
||||
userUtils.logIP(userID, requestIP)
|
||||
userHelper.logIP(userID, requestIP)
|
||||
|
||||
# Delete old tokens for that user and generate a new one
|
||||
isTournament = "tourney" in osuVersion
|
||||
if not isTournament:
|
||||
glob.tokens.deleteOldTokens(userID)
|
||||
responseToken = glob.tokens.addToken(userID, requestIP, timeOffset=timeOffset, tournament=isTournament)
|
||||
glob.tokens.deleteOldTokens(userID)
|
||||
responseToken = glob.tokens.addToken(userID, requestIP, timeOffset=timeOffset)
|
||||
responseTokenString = responseToken.token
|
||||
|
||||
# Check restricted mode (and eventually send message)
|
||||
responseToken.checkRestricted()
|
||||
|
||||
# Send message if donor expires soon
|
||||
if responseToken.privileges & privileges.USER_DONOR > 0:
|
||||
expireDate = userUtils.getDonorExpire(responseToken.userID)
|
||||
if expireDate-int(time.time()) <= 86400*3:
|
||||
expireDays = round((expireDate-int(time.time()))/86400)
|
||||
expireIn = "{} days".format(expireDays) if expireDays > 1 else "less than 24 hours"
|
||||
responseToken.enqueue(serverPackets.notification("Your donor tag expires in {}! When your donor tag expires, you won't have any of the donor privileges, like yellow username, custom badge and discord custom role and username color! If you wish to keep supporting Ripple and you don't want to lose your donor privileges, you can donate again by clicking on 'Support us' on Ripple's website.".format(expireIn)))
|
||||
|
||||
# Deprecate telegram 2fa and send alert
|
||||
if userUtils.deprecateTelegram2Fa(userID):
|
||||
responseToken.enqueue(serverPackets.notification("As stated on our blog, Telegram 2FA has been deprecated on 29th June 2018. Telegram 2FA has just been disabled from your account. If you want to keep your account secure with 2FA, please enable TOTP-based 2FA from our website https://ripple.moe. Thank you for your patience."))
|
||||
|
||||
# Set silence end UNIX time in token
|
||||
responseToken.silenceEndTime = userUtils.getSilenceEnd(userID)
|
||||
responseToken.silenceEndTime = userHelper.getSilenceEnd(userID)
|
||||
|
||||
# Get only silence remaining seconds
|
||||
silenceSeconds = responseToken.getSilenceSecondsLeft()
|
||||
|
@ -131,14 +121,11 @@ def handle(tornadoRequest):
|
|||
# Get supporter/GMT
|
||||
userGMT = False
|
||||
userSupporter = True
|
||||
userTournament = False
|
||||
if responseToken.admin:
|
||||
if responseToken.admin == True:
|
||||
userGMT = True
|
||||
if responseToken.privileges & privileges.USER_TOURNAMENT_STAFF > 0:
|
||||
userTournament = True
|
||||
|
||||
# Server restarting check
|
||||
if glob.restarting:
|
||||
if glob.restarting == True:
|
||||
raise exceptions.banchoRestartingException()
|
||||
|
||||
# Send login notification before maintenance message
|
||||
|
@ -146,8 +133,8 @@ def handle(tornadoRequest):
|
|||
responseToken.enqueue(serverPackets.notification(glob.banchoConf.config["loginNotification"]))
|
||||
|
||||
# Maintenance check
|
||||
if glob.banchoConf.config["banchoMaintenance"]:
|
||||
if not userGMT:
|
||||
if glob.banchoConf.config["banchoMaintenance"] == True:
|
||||
if userGMT == False:
|
||||
# We are not mod/admin, delete token, send notification and logout
|
||||
glob.tokens.deleteToken(responseTokenString)
|
||||
raise exceptions.banchoMaintenanceException()
|
||||
|
@ -159,7 +146,7 @@ def handle(tornadoRequest):
|
|||
responseToken.enqueue(serverPackets.silenceEndTime(silenceSeconds))
|
||||
responseToken.enqueue(serverPackets.userID(userID))
|
||||
responseToken.enqueue(serverPackets.protocolVersion())
|
||||
responseToken.enqueue(serverPackets.userSupporterGMT(userSupporter, userGMT, userTournament))
|
||||
responseToken.enqueue(serverPackets.userSupporterGMT(userSupporter, userGMT))
|
||||
responseToken.enqueue(serverPackets.userPanel(userID, True))
|
||||
responseToken.enqueue(serverPackets.userStats(userID, True))
|
||||
|
||||
|
@ -171,12 +158,12 @@ def handle(tornadoRequest):
|
|||
chat.joinChannel(token=responseToken, channel="#announce")
|
||||
|
||||
# Join admin channel if we are an admin
|
||||
if responseToken.admin:
|
||||
if responseToken.admin == True:
|
||||
chat.joinChannel(token=responseToken, channel="#admin")
|
||||
|
||||
# Output channels info
|
||||
for key, value in glob.channels.channels.items():
|
||||
if value.publicRead and not value.hidden:
|
||||
if value.publicRead == True and value.hidden == False:
|
||||
responseToken.enqueue(serverPackets.channelInfo(key))
|
||||
|
||||
# Send friends list
|
||||
|
@ -186,37 +173,33 @@ def handle(tornadoRequest):
|
|||
if glob.banchoConf.config["menuIcon"] != "":
|
||||
responseToken.enqueue(serverPackets.mainMenuIcon(glob.banchoConf.config["menuIcon"]))
|
||||
|
||||
# Send online users' panels
|
||||
with glob.tokens:
|
||||
for _, token in glob.tokens.tokens.items():
|
||||
if not token.restricted:
|
||||
responseToken.enqueue(serverPackets.userPanel(token.userID))
|
||||
# Send online users IDs array
|
||||
responseToken.enqueue(serverPackets.onlineUsers())
|
||||
|
||||
# Get location and country from ip.zxq.co or database
|
||||
if glob.localize:
|
||||
if glob.localize == True:
|
||||
# Get location and country from IP
|
||||
latitude, longitude = locationHelper.getLocation(requestIP)
|
||||
location = locationHelper.getLocation(requestIP)
|
||||
countryLetters = locationHelper.getCountry(requestIP)
|
||||
country = countryHelper.getCountryID(countryLetters)
|
||||
else:
|
||||
# Set location to 0,0 and get country from db
|
||||
log.warning("Location skipped")
|
||||
latitude = 0
|
||||
longitude = 0
|
||||
location = [0,0]
|
||||
countryLetters = "XX"
|
||||
country = countryHelper.getCountryID(userUtils.getCountry(userID))
|
||||
country = countryHelper.getCountryID(userHelper.getCountry(userID))
|
||||
|
||||
# Set location and country
|
||||
responseToken.setLocation(latitude, longitude)
|
||||
responseToken.country = country
|
||||
responseToken.setLocation(location)
|
||||
responseToken.setCountry(country)
|
||||
|
||||
# Set country in db if user has no country (first bancho login)
|
||||
if userUtils.getCountry(userID) == "XX":
|
||||
userUtils.setCountry(userID, countryLetters)
|
||||
if userHelper.getCountry(userID) == "XX":
|
||||
userHelper.setCountry(userID, countryLetters)
|
||||
|
||||
# Send to everyone our userpanel if we are not restricted or tournament
|
||||
if not responseToken.restricted:
|
||||
glob.streams.broadcast("main", serverPackets.userPanel(userID))
|
||||
# Send to everyone our userpanel if we are not restricted
|
||||
if responseToken.restricted == False:
|
||||
glob.tokens.enqueueAll(serverPackets.userPanel(userID))
|
||||
|
||||
# Set reponse data to right value and reset our queue
|
||||
responseData = responseToken.queue
|
||||
|
@ -224,23 +207,21 @@ def handle(tornadoRequest):
|
|||
except exceptions.loginFailedException:
|
||||
# Login failed error packet
|
||||
# (we don't use enqueue because we don't have a token since login has failed)
|
||||
err = True
|
||||
responseData += serverPackets.loginFailed()
|
||||
except exceptions.invalidArgumentsException:
|
||||
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
|
||||
responseData += serverPackets.loginBanned()
|
||||
except exceptions.loginLockedException:
|
||||
# Login banned error packet
|
||||
responseData += serverPackets.loginLocked()
|
||||
except exceptions.banchoMaintenanceException:
|
||||
# Bancho is in maintenance mode
|
||||
responseData = bytes()
|
||||
if responseToken is not None:
|
||||
responseData = responseToken.queue
|
||||
responseData = responseToken.queue
|
||||
responseData += serverPackets.notification("Our bancho server is in maintenance mode. Please try to login again later.")
|
||||
responseData += serverPackets.loginFailed()
|
||||
except exceptions.banchoRestartingException:
|
||||
|
@ -253,14 +234,18 @@ def handle(tornadoRequest):
|
|||
except exceptions.haxException:
|
||||
# Using oldoldold client, we don't have client data. Force update.
|
||||
# (we don't use enqueue because we don't have a token since login has failed)
|
||||
err = True
|
||||
responseData += serverPackets.forceUpdate()
|
||||
responseData += serverPackets.notification("Hory shitto, your client is TOO old! Nice prehistory! Please turn update it from the settings!")
|
||||
responseData += serverPackets.notification("Hory shitto, your client is TOO old! Nice preistoria! Please turn off the switcher and update it.")
|
||||
except:
|
||||
log.error("Unknown error!\n```\n{}\n{}```".format(sys.exc_info(), traceback.format_exc()))
|
||||
finally:
|
||||
# Console and discord log
|
||||
if len(loginData) < 3:
|
||||
log.info("Invalid bancho login request from **{}** (insufficient POST data)".format(requestIP), "bunker")
|
||||
msg = "Invalid bancho login request from **{}** (insufficient POST data)".format(requestIP)
|
||||
else:
|
||||
msg = "Bancho login request from **{}** for user **{}** ({})".format(requestIP, loginData[0], "failed" if err == True else "success")
|
||||
log.info(msg, "bunker")
|
||||
|
||||
# Return token string and data
|
||||
return responseTokenString, responseData
|
||||
return (responseTokenString, responseData)
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import time
|
||||
import json
|
||||
|
||||
from common.log import logUtils as log
|
||||
from constants import serverPackets
|
||||
from helpers import chatHelper as chat
|
||||
from objects import glob
|
||||
from helpers import consoleHelper
|
||||
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, _=None, deleteToken=True):
|
||||
def handle(userToken, _=None):
|
||||
# get usertoken data
|
||||
userID = userToken.userID
|
||||
username = userToken.username
|
||||
|
@ -17,41 +16,31 @@ def handle(userToken, _=None, deleteToken=True):
|
|||
# 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 or userToken.irc:
|
||||
# Stop spectating
|
||||
userToken.stopSpectating()
|
||||
|
||||
# Part matches
|
||||
userToken.leaveMatch()
|
||||
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)
|
||||
if spectatorHostToken != None:
|
||||
# The host is still online, send removeSpectator to him
|
||||
spectatorHostToken.enqueue(serverPackets.removeSpectator(userID))
|
||||
|
||||
# Part all joined channels
|
||||
for i in userToken.joinedChannels:
|
||||
chat.partChannel(token=userToken, channel=i)
|
||||
|
||||
# Leave all joined streams
|
||||
userToken.leaveAllStreams()
|
||||
# TODO: Lobby left if joined
|
||||
|
||||
# Enqueue our disconnection to everyone else
|
||||
glob.streams.broadcast("main", serverPackets.userLogout(userID))
|
||||
glob.tokens.enqueueAll(serverPackets.userLogout(userID))
|
||||
|
||||
# Disconnect from IRC if needed
|
||||
if userToken.irc and glob.irc:
|
||||
if userToken.irc == True and glob.irc == True:
|
||||
glob.ircServer.forceDisconnection(userToken.username)
|
||||
|
||||
# Delete token
|
||||
if deleteToken:
|
||||
glob.tokens.deleteToken(requestToken)
|
||||
else:
|
||||
userToken.kicked = True
|
||||
|
||||
# Change username if needed
|
||||
newUsername = glob.redis.get("ripple:change_username_pending:{}".format(userID))
|
||||
if newUsername is not None:
|
||||
log.debug("Sending username change request for user {}".format(userID))
|
||||
glob.redis.publish("peppy:change_username", json.dumps({
|
||||
"userID": userID,
|
||||
"newUsername": newUsername.decode("utf-8")
|
||||
}))
|
||||
glob.tokens.deleteToken(requestToken)
|
||||
|
||||
# Console output
|
||||
log.info("{} has been disconnected. (logout)".format(username))
|
||||
log.info("{} has been disconnected.".format(username))
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from objects import glob
|
||||
|
||||
def handle(userToken, _, has):
|
||||
def handle(userToken, packetData, has):
|
||||
# Get usertoken data
|
||||
userID = userToken.userID
|
||||
|
||||
|
@ -15,6 +15,8 @@ def handle(userToken, _, has):
|
|||
if matchID not in glob.matches.matches:
|
||||
return
|
||||
|
||||
# The match exists, get object
|
||||
match = glob.matches.matches[matchID]
|
||||
|
||||
# Set has beatmap/no beatmap
|
||||
with glob.matches.matches[matchID] as match:
|
||||
match.userHasBeatmap(userID, has)
|
||||
match.userHasBeatmap(userID, has)
|
||||
|
|
|
@ -15,6 +15,8 @@ def handle(userToken, _):
|
|||
if matchID not in glob.matches.matches:
|
||||
return
|
||||
|
||||
# Get match object
|
||||
match = glob.matches.matches[matchID]
|
||||
|
||||
# Change team
|
||||
with glob.matches.matches[matchID] as match:
|
||||
match.changeTeam(userID)
|
||||
match.changeTeam(userID)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from objects import glob
|
||||
|
||||
def handle(userToken, _):
|
||||
def handle(userToken, packetData):
|
||||
# Get usertoken data
|
||||
userID = userToken.userID
|
||||
|
||||
|
@ -15,6 +15,8 @@ def handle(userToken, _):
|
|||
if matchID not in glob.matches.matches:
|
||||
return
|
||||
|
||||
# The match exists, get object
|
||||
match = glob.matches.matches[matchID]
|
||||
|
||||
# Set our match complete
|
||||
with glob.matches.matches[matchID] as match:
|
||||
match.playerCompleted(userID)
|
||||
match.playerCompleted(userID)
|
||||
|
|
|
@ -15,6 +15,8 @@ def handle(userToken, _):
|
|||
if matchID not in glob.matches.matches:
|
||||
return
|
||||
|
||||
# Match exists, get object
|
||||
match = glob.matches.matches[matchID]
|
||||
|
||||
# Fail user
|
||||
with glob.matches.matches[matchID] as match:
|
||||
match.playerFailed(userID)
|
||||
match.playerFailed(userID)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
from objects import glob
|
||||
from constants import serverPackets, clientPackets
|
||||
from constants import slotStatuses
|
||||
from constants import serverPackets
|
||||
from helpers import logHelper as log
|
||||
|
||||
def handle(userToken, packetData):
|
||||
# Get usertoken data
|
||||
|
@ -16,16 +18,15 @@ def handle(userToken, packetData):
|
|||
if matchID not in glob.matches.matches:
|
||||
return
|
||||
|
||||
# Parse the data
|
||||
data = clientPackets.matchFrames(packetData)
|
||||
# The match exists, get object
|
||||
match = glob.matches.matches[matchID]
|
||||
|
||||
with glob.matches.matches[matchID] as match:
|
||||
# Change slot id in packetData
|
||||
slotID = match.getUserSlotID(userID)
|
||||
# Change slot id in packetData
|
||||
slotID = match.getUserSlotID(userID)
|
||||
|
||||
# Update the score
|
||||
match.updateScore(slotID, data["totalScore"])
|
||||
match.updateHP(slotID, data["currentHp"])
|
||||
|
||||
# Enqueue frames to who's playing
|
||||
glob.streams.broadcast(match.playingStreamName, serverPackets.matchFrames(slotID, packetData))
|
||||
# Enqueue frames to who's playing
|
||||
for i in range(0,16):
|
||||
if match.slots[i]["userID"] > -1 and match.slots[i]["status"] == slotStatuses.playing:
|
||||
token = glob.tokens.getTokenFromUserID(match.slots[i]["userID"])
|
||||
if token != None:
|
||||
token.enqueue(serverPackets.matchFrames(slotID, packetData))
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
from events import matchBeatmapEvent
|
||||
|
||||
def handle(userToken, packetData):
|
||||
matchBeatmapEvent.handle(userToken, packetData, True)
|
||||
|
|
|
@ -17,6 +17,8 @@ def handle(userToken, packetData):
|
|||
if matchID not in glob.matches.matches:
|
||||
return
|
||||
|
||||
# Get match object
|
||||
match = glob.matches.matches[matchID]
|
||||
|
||||
# Send invite
|
||||
with glob.matches.matches[matchID] as match:
|
||||
match.invite(userID, packetData["userID"])
|
||||
match.invite(userID, packetData["userID"])
|
||||
|
|
|
@ -12,16 +12,12 @@ def handle(userToken, packetData):
|
|||
matchID = userToken.matchID
|
||||
if matchID not in glob.matches.matches:
|
||||
return
|
||||
match = glob.matches.matches[matchID]
|
||||
|
||||
with glob.matches.matches[matchID] as match:
|
||||
# Host check
|
||||
if userID != match.hostUserID:
|
||||
return
|
||||
# Make sure we aren't locking our slot
|
||||
ourSlot = match.getUserSlotID(userID)
|
||||
if packetData["slotID"] == ourSlot:
|
||||
return
|
||||
|
||||
# Make sure we aren't locking our slot
|
||||
ourSlot = match.getUserSlotID(userID)
|
||||
if packetData["slotID"] == ourSlot:
|
||||
return
|
||||
|
||||
# Lock/Unlock slot
|
||||
match.toggleSlotLocked(packetData["slotID"])
|
||||
# Lock/Unlock slot
|
||||
match.toggleSlotLock(packetData["slotID"])
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
from events import matchBeatmapEvent
|
||||
|
||||
def handle(userToken, packetData):
|
||||
matchBeatmapEvent.handle(userToken, packetData, False)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from objects import glob
|
||||
|
||||
def handle(userToken, _):
|
||||
def handle(userToken, packetData):
|
||||
# Get userToken data
|
||||
userID = userToken.userID
|
||||
|
||||
|
@ -15,6 +15,8 @@ def handle(userToken, _):
|
|||
if matchID not in glob.matches.matches:
|
||||
return
|
||||
|
||||
# The match exists, get object
|
||||
match = glob.matches.matches[matchID]
|
||||
|
||||
# Set our load status
|
||||
with glob.matches.matches[matchID] as match:
|
||||
match.playerLoaded(userID)
|
||||
match.playerLoaded(userID)
|
||||
|
|
|
@ -8,14 +8,9 @@ def handle(userToken, _):
|
|||
matchID = userToken.matchID
|
||||
if matchID not in glob.matches.matches:
|
||||
return
|
||||
match = glob.matches.matches[matchID]
|
||||
|
||||
with glob.matches.matches[matchID] as match:
|
||||
# Get our slotID and change ready status
|
||||
slotID = match.getUserSlotID(userID)
|
||||
if slotID is not None:
|
||||
match.toggleSlotReady(slotID)
|
||||
|
||||
# If this is a tournament match, we should send the current status of ready
|
||||
# players.
|
||||
if match.isTourney:
|
||||
match.sendReadyStatus()
|
||||
# Get our slotID and change ready status
|
||||
slotID = match.getUserSlotID(userID)
|
||||
if slotID != None:
|
||||
match.toggleSlotReady(slotID)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from objects import glob
|
||||
|
||||
def handle(userToken, _):
|
||||
def handle(userToken, packetData):
|
||||
# Get userToken data
|
||||
userID = userToken.userID
|
||||
|
||||
|
@ -15,6 +15,8 @@ def handle(userToken, _):
|
|||
if matchID not in glob.matches.matches:
|
||||
return
|
||||
|
||||
# The match exists, get object
|
||||
match = glob.matches.matches[matchID]
|
||||
|
||||
# Skip
|
||||
with glob.matches.matches[matchID] as match:
|
||||
match.playerSkip(userID)
|
||||
match.playerSkip(userID)
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
from objects import glob
|
||||
from constants import slotStatuses
|
||||
from constants import serverPackets
|
||||
|
||||
def handle(userToken, _):
|
||||
# TODO: Host check
|
||||
|
||||
# Get match ID and match object
|
||||
matchID = userToken.matchID
|
||||
|
@ -13,9 +16,32 @@ def handle(userToken, _):
|
|||
if matchID not in glob.matches.matches:
|
||||
return
|
||||
|
||||
with glob.matches.matches[matchID] as match:
|
||||
# Host check
|
||||
if userToken.userID != match.hostUserID:
|
||||
return
|
||||
# The match exists, get object
|
||||
match = glob.matches.matches[matchID]
|
||||
|
||||
match.start()
|
||||
force = False # TODO: Force thing
|
||||
|
||||
# Make sure we have enough players
|
||||
if (match.countUsers() < 2 or not match.checkTeams()) and not force:
|
||||
return
|
||||
|
||||
# Change inProgress value
|
||||
match.inProgress = True
|
||||
|
||||
# Set playing to ready players and set load, skip and complete to False
|
||||
for i in range(0,16):
|
||||
if (match.slots[i]["status"] & slotStatuses.ready) > 0:
|
||||
match.slots[i]["status"] = slotStatuses.playing
|
||||
match.slots[i]["loaded"] = False
|
||||
match.slots[i]["skip"] = False
|
||||
match.slots[i]["complete"] = False
|
||||
|
||||
# Send match start packet
|
||||
for i in range(0,16):
|
||||
if (match.slots[i]["status"] & slotStatuses.playing) > 0 and match.slots[i]["userID"] != -1:
|
||||
token = glob.tokens.getTokenFromUserID(match.slots[i]["userID"])
|
||||
if token != None:
|
||||
token.enqueue(serverPackets.matchStart(matchID))
|
||||
|
||||
# Send updates
|
||||
match.sendUpdate()
|
||||
|
|
|
@ -16,10 +16,8 @@ def handle(userToken, packetData):
|
|||
if matchID not in glob.matches.matches:
|
||||
return
|
||||
|
||||
# Host check
|
||||
with glob.matches.matches[matchID] as match:
|
||||
if userToken.userID != match.hostUserID:
|
||||
return
|
||||
# Match exists, get object
|
||||
match = glob.matches.matches[matchID]
|
||||
|
||||
# Transfer host
|
||||
match.transferHost(packetData["slotID"])
|
||||
# Transfer host
|
||||
match.transferHost(packetData["slotID"])
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
from common.log import logUtils as log
|
||||
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
|
||||
userID = userToken.userID
|
||||
username = userToken.username
|
||||
|
||||
# Remove user from users in lobby
|
||||
userToken.leaveStream("lobby")
|
||||
glob.matches.lobbyUserPart(userID)
|
||||
|
||||
# Part lobby channel
|
||||
# Done automatically by the client
|
||||
chat.partChannel(channel="#lobby", token=userToken, kick=True)
|
||||
|
||||
# Console output
|
||||
|
|
|
@ -1,2 +1,29 @@
|
|||
def handle(userToken, _=None):
|
||||
userToken.leaveMatch()
|
||||
from objects import glob
|
||||
from helpers import chatHelper as chat
|
||||
|
||||
def handle(userToken, _):
|
||||
# get data from usertoken
|
||||
userID = userToken.userID
|
||||
|
||||
# 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]
|
||||
|
||||
# Set slot to free
|
||||
match.userLeft(userID)
|
||||
|
||||
# Part #multiplayer channel
|
||||
#chat.partChannel(token=userToken, channel="#multi_{}".format(matchID), kick=True)
|
||||
|
||||
# Set usertoken match to -1
|
||||
userToken.partMatch()
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
from constants import clientPackets
|
||||
from constants import serverPackets
|
||||
|
||||
from helpers import userHelper
|
||||
from helpers import logHelper as log
|
||||
|
||||
def handle(userToken, packetData):
|
||||
log.debug("Requested status update")
|
||||
|
||||
# Update cache and send new stats
|
||||
userToken.updateCachedStats()
|
||||
userToken.enqueue(serverPackets.userStats(userToken.userID))
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
from common.log import logUtils as log
|
||||
from constants import clientPackets
|
||||
from constants import serverPackets
|
||||
from objects import glob
|
||||
|
||||
from helpers import logHelper as log
|
||||
|
||||
def handle(userToken, packetData):
|
||||
# get token data
|
||||
|
@ -12,12 +10,12 @@ def handle(userToken, packetData):
|
|||
packetData = clientPackets.setAwayMessage(packetData)
|
||||
|
||||
# Set token away message
|
||||
userToken.awayMessage = packetData["awayMessage"]
|
||||
userToken.setAwayMessage(packetData["awayMessage"])
|
||||
|
||||
# Send private message from fokabot
|
||||
if packetData["awayMessage"] == "":
|
||||
fokaMessage = "Your away message has been reset"
|
||||
else:
|
||||
fokaMessage = "Your away message is now: {}".format(packetData["awayMessage"])
|
||||
userToken.enqueue(serverPackets.sendMessage(glob.BOT_NAME, username, fokaMessage))
|
||||
userToken.enqueue(serverPackets.sendMessage("FokaBot", username, fokaMessage))
|
||||
log.info("{} has changed their away message to: {}".format(username, packetData["awayMessage"]))
|
||||
|
|
|
@ -1,15 +1,30 @@
|
|||
from objects import glob
|
||||
from constants import serverPackets
|
||||
from common.log import logUtils as log
|
||||
from constants import exceptions
|
||||
|
||||
def handle(userToken, packetData):
|
||||
# get token data
|
||||
userID = userToken.userID
|
||||
|
||||
# Send spectator frames to every spectator
|
||||
streamName = "spect/{}".format(userID)
|
||||
glob.streams.broadcast(streamName, serverPackets.spectatorFrames(packetData[7:]))
|
||||
log.debug("Broadcasting {}'s frames to {} clients".format(
|
||||
userID,
|
||||
len(glob.streams.streams[streamName].clients))
|
||||
)
|
||||
for i in userToken.spectators:
|
||||
# Send to every user but host
|
||||
if i != userID:
|
||||
try:
|
||||
# Get spectator token object
|
||||
spectatorToken = glob.tokens.getTokenFromUserID(i)
|
||||
|
||||
# Make sure the token exists
|
||||
if spectatorToken == None:
|
||||
raise exceptions.stopSpectating
|
||||
|
||||
# Make sure this user is spectating us
|
||||
if spectatorToken.spectating != userID:
|
||||
raise exceptions.stopSpectating
|
||||
|
||||
# Everything seems fine, send spectator frames to this spectator
|
||||
spectatorToken.enqueue(serverPackets.spectatorFrames(packetData[7:]))
|
||||
except exceptions.stopSpectating:
|
||||
# Remove this user from spectators
|
||||
userToken.removeSpectator(i)
|
||||
userToken.enqueue(serverPackets.removeSpectator(i))
|
||||
|
|
|
@ -1,25 +1,56 @@
|
|||
from common.log import logUtils as log
|
||||
from constants import clientPackets
|
||||
from constants import serverPackets
|
||||
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:
|
||||
# Get usertoken data
|
||||
userID = userToken.userID
|
||||
username = userToken.username
|
||||
|
||||
# Start spectating packet
|
||||
packetData = clientPackets.startSpectating(packetData)
|
||||
|
||||
# If the user id is less than 0, treat this as a stop spectating packet
|
||||
if packetData["userID"] < 0:
|
||||
# Stop spectating old user if needed
|
||||
if userToken.spectating != 0:
|
||||
oldTargetToken = glob.tokens.getTokenFromUserID(userToken.spectating)
|
||||
oldTargetToken.enqueue(serverPackets.removeSpectator(userID))
|
||||
userToken.stopSpectating()
|
||||
return
|
||||
|
||||
# Start spectating new user
|
||||
userToken.startSpectating(packetData["userID"])
|
||||
|
||||
# Get host token
|
||||
targetToken = glob.tokens.getTokenFromUserID(packetData["userID"])
|
||||
if targetToken is None:
|
||||
if targetToken == None:
|
||||
raise exceptions.tokenNotFoundException
|
||||
|
||||
# Start spectating new user
|
||||
userToken.startSpectating(targetToken)
|
||||
# Add us to host's spectators
|
||||
targetToken.addSpectator(userID)
|
||||
|
||||
# Send spectator join packet to host
|
||||
targetToken.enqueue(serverPackets.addSpectator(userID))
|
||||
|
||||
# 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
|
||||
chat.joinChannel(token=targetToken, channel="#spect_{}".format(targetToken.userID))
|
||||
|
||||
# send fellowSpectatorJoined to all spectators
|
||||
for spec in targetToken.spectators:
|
||||
if spec is not userID:
|
||||
c = glob.tokens.getTokenFromUserID(spec)
|
||||
userToken.enqueue(serverPackets.fellowSpectatorJoined(c.userID))
|
||||
c.enqueue(serverPackets.fellowSpectatorJoined(userID))
|
||||
|
||||
# Console output
|
||||
log.info("{} are spectating {}".format(username, userHelper.getUsername(packetData["userID"])))
|
||||
except exceptions.tokenNotFoundException:
|
||||
# Stop spectating if token not found
|
||||
log.warning("Spectator start: token not found")
|
||||
|
|
|
@ -1,2 +1,37 @@
|
|||
def handle(userToken, _=None):
|
||||
userToken.stopSpectating()
|
||||
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:
|
||||
# get user token data
|
||||
userID = userToken.userID
|
||||
username = userToken.username
|
||||
|
||||
# Remove our userID from host's spectators
|
||||
target = userToken.spectating
|
||||
targetToken = glob.tokens.getTokenFromUserID(target)
|
||||
if targetToken == None:
|
||||
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:
|
||||
spec = glob.tokens.getTokenFromUserID(c)
|
||||
spec.enqueue(serverPackets.fellowSpectatorLeft(userID))
|
||||
|
||||
#targetToken.enqueue(serverPackets.fellowSpectatorLeft(userID))
|
||||
|
||||
# Console output
|
||||
log.info("{} are no longer spectating {}".format(username, target))
|
||||
except exceptions.tokenNotFoundException:
|
||||
log.warning("Spectator stop: token not found")
|
||||
finally:
|
||||
# Set our spectating user to 0
|
||||
userToken.stopSpectating()
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
from constants import clientPackets
|
||||
from objects import glob
|
||||
from helpers import chatHelper as chat
|
||||
|
||||
def handle(userToken, packetData):
|
||||
packetData = clientPackets.tournamentJoinMatchChannel(packetData)
|
||||
matchID = packetData["matchID"]
|
||||
if matchID not in glob.matches.matches or not userToken.tournament:
|
||||
return
|
||||
userToken.matchID = matchID
|
||||
chat.joinChannel(token=userToken, channel="#multi_{}".format(matchID), force=True)
|
|
@ -1,11 +0,0 @@
|
|||
from constants import clientPackets
|
||||
from objects import glob
|
||||
from helpers import chatHelper as chat
|
||||
|
||||
def handle(userToken, packetData):
|
||||
packetData = clientPackets.tournamentLeaveMatchChannel(packetData)
|
||||
matchID = packetData["matchID"]
|
||||
if matchID not in glob.matches.matches or not userToken.tournament:
|
||||
return
|
||||
chat.partChannel(token=userToken, channel="#multi_{}".format(matchID), force=True)
|
||||
userToken.matchID = 0
|
|
@ -1,10 +0,0 @@
|
|||
from constants import clientPackets
|
||||
from objects import glob
|
||||
|
||||
def handle(userToken, packetData):
|
||||
packetData = clientPackets.tournamentMatchInfoRequest(packetData)
|
||||
matchID = packetData["matchID"]
|
||||
if matchID not in glob.matches.matches or not userToken.tournament:
|
||||
return
|
||||
with glob.matches.matches[matchID] as m:
|
||||
userToken.enqueue(m.matchDataCache)
|
|
@ -1,7 +1,6 @@
|
|||
from common.log import logUtils as log
|
||||
from constants import clientPackets
|
||||
from constants import serverPackets
|
||||
|
||||
from helpers import logHelper as log
|
||||
|
||||
def handle(userToken, packetData):
|
||||
# Read userIDs list
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from common.log import logUtils as log
|
||||
from constants import clientPackets
|
||||
from constants import serverPackets
|
||||
|
||||
from helpers import logHelper as log
|
||||
|
||||
def handle(userToken, packetData):
|
||||
# Read userIDs list
|
||||
|
|
|
@ -14,10 +14,3 @@ boobs=bob
|
|||
tits=teeth
|
||||
cum=yogurt
|
||||
cunt=count
|
||||
nigger=flowers
|
||||
ngger=flowers
|
||||
niggers=flowers
|
||||
weed=grass
|
||||
AQN=meme
|
||||
theaquila=meme
|
||||
aquila=meme
|
|
@ -1,4 +0,0 @@
|
|||
find . -name "*.c" -type f -delete
|
||||
find . -name "*.o" -type f -delete
|
||||
find . -name "*.so" -type f -delete
|
||||
python3 setup.py build_ext --inplace
|
|
@ -1,46 +1,23 @@
|
|||
import json
|
||||
|
||||
import tornado.web
|
||||
import tornado.gen
|
||||
|
||||
from common.sentry import sentry
|
||||
from common.ripple import userUtils
|
||||
from common.web import requestsManager
|
||||
from helpers import requestHelper
|
||||
from constants import exceptions
|
||||
import json
|
||||
from objects import glob
|
||||
|
||||
|
||||
class handler(requestsManager.asyncRequestHandler):
|
||||
@tornado.web.asynchronous
|
||||
@tornado.gen.engine
|
||||
@sentry.captureTornado
|
||||
class handler(requestHelper.asyncRequestHandler):
|
||||
def asyncGet(self):
|
||||
statusCode = 400
|
||||
data = {"message": "unknown error"}
|
||||
try:
|
||||
# Check arguments
|
||||
if "u" not in self.request.arguments and "id" not in self.request.arguments:
|
||||
if requestHelper.checkArguments(self.request.arguments, ["u"]) == False:
|
||||
raise exceptions.invalidArgumentsException()
|
||||
|
||||
# Get online staus
|
||||
username = None
|
||||
userID = None
|
||||
if "u" in self.request.arguments:
|
||||
#username = self.get_argument("u").lower().replace(" ", "_")
|
||||
username = userUtils.safeUsername(self.get_argument("u"))
|
||||
else:
|
||||
try:
|
||||
userID = int(self.get_argument("id"))
|
||||
except:
|
||||
raise exceptions.invalidArgumentsException()
|
||||
|
||||
if username is None and userID is None:
|
||||
username = self.get_argument("u")
|
||||
if username == None:
|
||||
data["result"] = False
|
||||
else:
|
||||
if username is not None:
|
||||
data["result"] = True if glob.tokens.getTokenFromUsername(username, safe=True) is not None else False
|
||||
else:
|
||||
data["result"] = True if glob.tokens.getTokenFromUserID(userID) is not None else False
|
||||
data["result"] = True if glob.tokens.getTokenFromUsername(username) != None else False
|
||||
|
||||
# Status code and message
|
||||
statusCode = 200
|
||||
|
@ -53,5 +30,7 @@ class handler(requestsManager.asyncRequestHandler):
|
|||
data["status"] = statusCode
|
||||
|
||||
# Send response
|
||||
#self.clear()
|
||||
self.write(json.dumps(data))
|
||||
self.set_status(statusCode)
|
||||
#self.finish(json.dumps(data))
|
||||
|
|
|
@ -1,23 +1,14 @@
|
|||
from helpers import requestHelper
|
||||
import json
|
||||
|
||||
import tornado.web
|
||||
import tornado.gen
|
||||
|
||||
from common.sentry import sentry
|
||||
from common.web import requestsManager
|
||||
from objects import glob
|
||||
|
||||
|
||||
class handler(requestsManager.asyncRequestHandler):
|
||||
@tornado.web.asynchronous
|
||||
@tornado.gen.engine
|
||||
@sentry.captureTornado
|
||||
class handler(requestHelper.asyncRequestHandler):
|
||||
def asyncGet(self):
|
||||
statusCode = 400
|
||||
data = {"message": "unknown error"}
|
||||
try:
|
||||
# Get online users count
|
||||
data["result"] = int(glob.redis.get("ripple:online_users").decode("utf-8"))
|
||||
data["result"] = len(glob.tokens.tokens)
|
||||
|
||||
# Status code and message
|
||||
statusCode = 200
|
||||
|
@ -27,5 +18,7 @@ class handler(requestsManager.asyncRequestHandler):
|
|||
data["status"] = statusCode
|
||||
|
||||
# Send response
|
||||
#self.clear()
|
||||
self.write(json.dumps(data))
|
||||
self.set_status(statusCode)
|
||||
#self.finish(json.dumps(data))
|
||||
|
|
|
@ -1,23 +1,14 @@
|
|||
from helpers import requestHelper
|
||||
import json
|
||||
|
||||
import tornado.web
|
||||
import tornado.gen
|
||||
|
||||
from common.sentry import sentry
|
||||
from common.web import requestsManager
|
||||
from objects import glob
|
||||
|
||||
|
||||
class handler(requestsManager.asyncRequestHandler):
|
||||
@tornado.web.asynchronous
|
||||
@tornado.gen.engine
|
||||
@sentry.captureTornado
|
||||
class handler(requestHelper.asyncRequestHandler):
|
||||
def asyncGet(self):
|
||||
statusCode = 400
|
||||
data = {"message": "unknown error"}
|
||||
try:
|
||||
# Get online users count
|
||||
data["result"] = -1 if glob.restarting else 1
|
||||
data["result"] = -1 if glob.restarting == True else 1
|
||||
|
||||
# Status code and message
|
||||
statusCode = 200
|
||||
|
@ -27,5 +18,7 @@ class handler(requestsManager.asyncRequestHandler):
|
|||
data["status"] = statusCode
|
||||
|
||||
# Send response
|
||||
#self.clear()
|
||||
self.write(json.dumps(data))
|
||||
self.set_status(statusCode)
|
||||
#self.finish(json.dumps(data))
|
||||
|
|
|
@ -1,24 +1,16 @@
|
|||
from helpers import requestHelper
|
||||
from helpers import logHelper as log
|
||||
import json
|
||||
|
||||
import tornado.web
|
||||
import tornado.gen
|
||||
|
||||
from common.sentry import sentry
|
||||
from common.web import requestsManager
|
||||
from constants import exceptions
|
||||
from objects import glob
|
||||
from constants import exceptions
|
||||
|
||||
|
||||
class handler(requestsManager.asyncRequestHandler):
|
||||
@tornado.web.asynchronous
|
||||
@tornado.gen.engine
|
||||
@sentry.captureTornado
|
||||
class handler(requestHelper.asyncRequestHandler):
|
||||
def asyncGet(self):
|
||||
statusCode = 400
|
||||
data = {"message": "unknown error"}
|
||||
try:
|
||||
# Check arguments
|
||||
if not requestsManager.checkArguments(self.request.arguments, ["u"]):
|
||||
if requestHelper.checkArguments(self.request.arguments, ["u"]) == False:
|
||||
raise exceptions.invalidArgumentsException()
|
||||
|
||||
# Get userID and its verified cache thing
|
||||
|
|
|
@ -1,26 +1,17 @@
|
|||
import json
|
||||
|
||||
import tornado.web
|
||||
import tornado.gen
|
||||
|
||||
from common.sentry import sentry
|
||||
from common.log import logUtils as log
|
||||
from common.web import requestsManager
|
||||
from helpers import requestHelper
|
||||
from constants import exceptions
|
||||
from helpers import systemHelper
|
||||
import json
|
||||
from objects import glob
|
||||
from helpers import systemHelper
|
||||
from helpers import logHelper as log
|
||||
|
||||
|
||||
class handler(requestsManager.asyncRequestHandler):
|
||||
@tornado.web.asynchronous
|
||||
@tornado.gen.engine
|
||||
@sentry.captureTornado
|
||||
class handler(requestHelper.asyncRequestHandler):
|
||||
def asyncGet(self):
|
||||
statusCode = 400
|
||||
data = {"message": "unknown error"}
|
||||
try:
|
||||
# Check arguments
|
||||
if not requestsManager.checkArguments(self.request.arguments, ["k"]):
|
||||
if requestHelper.checkArguments(self.request.arguments, ["k"]) == False:
|
||||
raise exceptions.invalidArgumentsException()
|
||||
|
||||
# Check ci key
|
||||
|
@ -42,5 +33,7 @@ class handler(requestsManager.asyncRequestHandler):
|
|||
data["status"] = statusCode
|
||||
|
||||
# Send response
|
||||
#self.clear()
|
||||
self.write(json.dumps(data))
|
||||
self.set_status(statusCode)
|
||||
#self.finish(json.dumps(data))
|
||||
|
|
|
@ -1,25 +1,17 @@
|
|||
import json
|
||||
|
||||
import tornado.web
|
||||
import tornado.gen
|
||||
|
||||
from common.sentry import sentry
|
||||
from common.web import requestsManager
|
||||
from helpers import requestHelper
|
||||
from constants import exceptions
|
||||
from helpers import chatHelper
|
||||
import json
|
||||
from objects import glob
|
||||
from helpers import chatHelper
|
||||
from helpers import logHelper as log
|
||||
|
||||
|
||||
class handler(requestsManager.asyncRequestHandler):
|
||||
@tornado.web.asynchronous
|
||||
@tornado.gen.engine
|
||||
@sentry.captureTornado
|
||||
class handler(requestHelper.asyncRequestHandler):
|
||||
def asyncGet(self):
|
||||
statusCode = 400
|
||||
data = {"message": "unknown error"}
|
||||
try:
|
||||
# Check arguments
|
||||
if not requestsManager.checkArguments(self.request.arguments, ["k", "to", "msg"]):
|
||||
if requestHelper.checkArguments(self.request.arguments, ["k", "to", "msg"]) == False:
|
||||
raise exceptions.invalidArgumentsException()
|
||||
|
||||
# Check ci key
|
||||
|
@ -27,11 +19,8 @@ class handler(requestsManager.asyncRequestHandler):
|
|||
if key is None or key != glob.conf.config["server"]["cikey"]:
|
||||
raise exceptions.invalidArgumentsException()
|
||||
|
||||
chatHelper.sendMessage(
|
||||
glob.BOT_NAME,
|
||||
self.get_argument("to").encode().decode("ASCII", "ignore"),
|
||||
self.get_argument("msg").encode().decode("ASCII", "ignore")
|
||||
)
|
||||
log.info("API REQUEST FOR FOKABOT MESSAGE AAAAAAA")
|
||||
chatHelper.sendMessage("FokaBot", self.get_argument("to"), self.get_argument("msg"))
|
||||
|
||||
# Status code and message
|
||||
statusCode = 200
|
||||
|
@ -44,5 +33,7 @@ class handler(requestsManager.asyncRequestHandler):
|
|||
data["status"] = statusCode
|
||||
|
||||
# Send response
|
||||
#self.clear()
|
||||
self.write(json.dumps(data))
|
||||
self.set_status(statusCode)
|
||||
#self.finish(json.dumps(data))
|
|
@ -1,25 +0,0 @@
|
|||
import tornado.gen
|
||||
import tornado.web
|
||||
from common.web import requestsManager
|
||||
from objects import glob
|
||||
import time
|
||||
|
||||
class handler(requestsManager.asyncRequestHandler):
|
||||
@tornado.web.asynchronous
|
||||
@tornado.gen.engine
|
||||
def asyncGet(self):
|
||||
if not glob.debug:
|
||||
self.write("Nope")
|
||||
return
|
||||
time.sleep(0.5)
|
||||
self.write("meemmeemmeemmeemmeemmeemmeemmeemmeemmeemmeemmeemmeemmeemmeemmeemmeemmeemmeemmeemmeemmeemmeemmeemmeemmeemmeemmeem")
|
||||
self.set_status(200)
|
||||
self.add_header("cho-token", "tua madre")
|
||||
self.add_header("cho-protocol", "19")
|
||||
self.add_header("Connection", "keep-alive")
|
||||
self.add_header("Keep-Alive", "timeout=5, max=100")
|
||||
self.add_header("Content-Type", "text/html; charset=UTF-8")
|
||||
#glob.db.fetchAll("SELECT SQL_NO_CACHE * FROM beatmaps")
|
||||
#glob.db.fetchAll("SELECT SQL_NO_CACHE * FROM users")
|
||||
#glob.db.fetchAll("SELECT SQL_NO_CACHE * FROM scores")
|
||||
#self.write("ibmd")
|
264
handlers/mainHandler.py
Normal file
264
handlers/mainHandler.py
Normal file
|
@ -0,0 +1,264 @@
|
|||
import datetime
|
||||
import gzip
|
||||
from helpers import requestHelper
|
||||
from objects import glob
|
||||
from constants import exceptions
|
||||
from constants import packetIDs
|
||||
from helpers import packetHelper
|
||||
from constants import serverPackets
|
||||
from events import sendPublicMessageEvent
|
||||
from events import sendPrivateMessageEvent
|
||||
from events import channelJoinEvent
|
||||
from events import channelPartEvent
|
||||
from events import changeActionEvent
|
||||
from events import cantSpectateEvent
|
||||
from events import startSpectatingEvent
|
||||
from events import stopSpectatingEvent
|
||||
from events import spectateFramesEvent
|
||||
from events import friendAddEvent
|
||||
from events import friendRemoveEvent
|
||||
from events import logoutEvent
|
||||
from events import loginEvent
|
||||
from events import setAwayMessageEvent
|
||||
from events import joinLobbyEvent
|
||||
from events import createMatchEvent
|
||||
from events import partLobbyEvent
|
||||
from events import changeSlotEvent
|
||||
from events import joinMatchEvent
|
||||
from events import partMatchEvent
|
||||
from events import changeMatchSettingsEvent
|
||||
from events import changeMatchPasswordEvent
|
||||
from events import changeMatchModsEvent
|
||||
from events import matchReadyEvent
|
||||
from events import matchLockEvent
|
||||
from events import matchStartEvent
|
||||
from events import matchPlayerLoadEvent
|
||||
from events import matchSkipEvent
|
||||
from events import matchFramesEvent
|
||||
from events import matchCompleteEvent
|
||||
from events import matchNoBeatmapEvent
|
||||
from events import matchHasBeatmapEvent
|
||||
from events import matchTransferHostEvent
|
||||
from events import matchFailedEvent
|
||||
from events import matchInviteEvent
|
||||
from events import matchChangeTeamEvent
|
||||
from events import userStatsRequestEvent
|
||||
from events import requestStatusUpdateEvent
|
||||
from events import userPanelRequestEvent
|
||||
|
||||
# Exception tracking
|
||||
import tornado.web
|
||||
import tornado.gen
|
||||
import sys
|
||||
import traceback
|
||||
from raven.contrib.tornado import SentryMixin
|
||||
from helpers import logHelper as log
|
||||
|
||||
class handler(SentryMixin, requestHelper.asyncRequestHandler):
|
||||
@tornado.web.asynchronous
|
||||
@tornado.gen.engine
|
||||
def asyncPost(self):
|
||||
try:
|
||||
# Track time if needed
|
||||
if glob.outputRequestTime == True:
|
||||
# Start time
|
||||
st = datetime.datetime.now()
|
||||
|
||||
# Client's token string and request data
|
||||
requestTokenString = self.request.headers.get("osu-token")
|
||||
requestData = self.request.body
|
||||
|
||||
# Server's token string and request data
|
||||
responseTokenString = "ayy"
|
||||
responseData = bytes()
|
||||
|
||||
if requestTokenString == None:
|
||||
# No token, first request. Handle login.
|
||||
responseTokenString, responseData = loginEvent.handle(self)
|
||||
else:
|
||||
userToken = None # default value
|
||||
try:
|
||||
# This is not the first packet, send response based on client's request
|
||||
# Packet start position, used to read stacked packets
|
||||
pos = 0
|
||||
|
||||
# Make sure the token exists
|
||||
if requestTokenString not in glob.tokens.tokens:
|
||||
raise exceptions.tokenNotFoundException()
|
||||
|
||||
# Token exists, get its object and lock it
|
||||
userToken = glob.tokens.tokens[requestTokenString]
|
||||
userToken.lock.acquire()
|
||||
|
||||
# Keep reading packets until everything has been read
|
||||
while pos < len(requestData):
|
||||
# Get packet from stack starting from new packet
|
||||
leftData = requestData[pos:]
|
||||
|
||||
# Get packet ID, data length and data
|
||||
packetID = packetHelper.readPacketID(leftData)
|
||||
dataLength = packetHelper.readPacketLength(leftData)
|
||||
packetData = requestData[pos:(pos+dataLength+7)]
|
||||
|
||||
# Console output if needed
|
||||
if glob.outputPackets == True and packetID != 4:
|
||||
log.debug("Incoming packet ({})({}):\n\nPacket code: {}\nPacket length: {}\nSingle packet data: {}\n".format(requestTokenString, userToken.username, str(packetID), str(dataLength), str(packetData)))
|
||||
|
||||
# Event handler
|
||||
def handleEvent(ev):
|
||||
def wrapper():
|
||||
ev.handle(userToken, packetData)
|
||||
return wrapper
|
||||
|
||||
eventHandler = {
|
||||
# TODO: Rename packets and events
|
||||
# TODO: Host check for multi
|
||||
packetIDs.client_changeAction: handleEvent(changeActionEvent),
|
||||
packetIDs.client_logout: handleEvent(logoutEvent),
|
||||
packetIDs.client_friendAdd: handleEvent(friendAddEvent),
|
||||
packetIDs.client_friendRemove: handleEvent(friendRemoveEvent),
|
||||
packetIDs.client_userStatsRequest: handleEvent(userStatsRequestEvent),
|
||||
packetIDs.client_requestStatusUpdate: handleEvent(requestStatusUpdateEvent),
|
||||
packetIDs.client_userPanelRequest: handleEvent(userPanelRequestEvent),
|
||||
|
||||
packetIDs.client_channelJoin: handleEvent(channelJoinEvent),
|
||||
packetIDs.client_channelPart: handleEvent(channelPartEvent),
|
||||
packetIDs.client_sendPublicMessage: handleEvent(sendPublicMessageEvent),
|
||||
packetIDs.client_sendPrivateMessage: handleEvent(sendPrivateMessageEvent),
|
||||
packetIDs.client_setAwayMessage: handleEvent(setAwayMessageEvent),
|
||||
|
||||
packetIDs.client_startSpectating: handleEvent(startSpectatingEvent),
|
||||
packetIDs.client_stopSpectating: handleEvent(stopSpectatingEvent),
|
||||
packetIDs.client_cantSpectate: handleEvent(cantSpectateEvent),
|
||||
packetIDs.client_spectateFrames: handleEvent(spectateFramesEvent),
|
||||
|
||||
packetIDs.client_joinLobby: handleEvent(joinLobbyEvent),
|
||||
packetIDs.client_partLobby: handleEvent(partLobbyEvent),
|
||||
packetIDs.client_createMatch: handleEvent(createMatchEvent),
|
||||
packetIDs.client_joinMatch: handleEvent(joinMatchEvent),
|
||||
packetIDs.client_partMatch: handleEvent(partMatchEvent),
|
||||
packetIDs.client_matchChangeSlot: handleEvent(changeSlotEvent),
|
||||
packetIDs.client_matchChangeSettings: handleEvent(changeMatchSettingsEvent),
|
||||
packetIDs.client_matchChangePassword: handleEvent(changeMatchPasswordEvent),
|
||||
packetIDs.client_matchChangeMods: handleEvent(changeMatchModsEvent),
|
||||
packetIDs.client_matchReady: handleEvent(matchReadyEvent),
|
||||
packetIDs.client_matchNotReady: handleEvent(matchReadyEvent),
|
||||
packetIDs.client_matchLock: handleEvent(matchLockEvent),
|
||||
packetIDs.client_matchStart: handleEvent(matchStartEvent),
|
||||
packetIDs.client_matchLoadComplete: handleEvent(matchPlayerLoadEvent),
|
||||
packetIDs.client_matchSkipRequest: handleEvent(matchSkipEvent),
|
||||
packetIDs.client_matchScoreUpdate: handleEvent(matchFramesEvent),
|
||||
packetIDs.client_matchComplete: handleEvent(matchCompleteEvent),
|
||||
packetIDs.client_matchNoBeatmap: handleEvent(matchNoBeatmapEvent),
|
||||
packetIDs.client_matchHasBeatmap: handleEvent(matchHasBeatmapEvent),
|
||||
packetIDs.client_matchTransferHost: handleEvent(matchTransferHostEvent),
|
||||
packetIDs.client_matchFailed: handleEvent(matchFailedEvent),
|
||||
packetIDs.client_matchChangeTeam: handleEvent(matchChangeTeamEvent),
|
||||
packetIDs.client_invite: handleEvent(matchInviteEvent),
|
||||
}
|
||||
|
||||
# Packets processed if in restricted mode.
|
||||
# All other packets will be ignored if the user is in restricted mode
|
||||
packetsRestricted = [
|
||||
packetIDs.client_logout,
|
||||
packetIDs.client_userStatsRequest,
|
||||
packetIDs.client_requestStatusUpdate,
|
||||
packetIDs.client_userPanelRequest,
|
||||
packetIDs.client_changeAction,
|
||||
packetIDs.client_channelJoin,
|
||||
packetIDs.client_channelPart,
|
||||
]
|
||||
|
||||
# Process/ignore packet
|
||||
if packetID != 4:
|
||||
if packetID in eventHandler:
|
||||
if userToken.restricted == False or (userToken.restricted == True and packetID in packetsRestricted):
|
||||
eventHandler[packetID]()
|
||||
else:
|
||||
log.warning("Ignored packet id from {} ({}) (user is restricted)".format(requestTokenString, packetID))
|
||||
else:
|
||||
log.warning("Unknown packet id from {} ({})".format(requestTokenString, packetID))
|
||||
|
||||
# Update pos so we can read the next stacked packet
|
||||
# +7 because we add packet ID bytes, unused byte and data length bytes
|
||||
pos += dataLength+7
|
||||
|
||||
# Token queue built, send it
|
||||
responseTokenString = userToken.token
|
||||
responseData = userToken.queue
|
||||
userToken.resetQueue()
|
||||
|
||||
# Update ping time for timeout
|
||||
userToken.updatePingTime()
|
||||
except exceptions.tokenNotFoundException:
|
||||
# Token not found. Disconnect that user
|
||||
responseData = serverPackets.loginError()
|
||||
responseData += serverPackets.notification("Whoops! Something went wrong, please login again.")
|
||||
log.warning("Received packet from unknown token ({}).".format(requestTokenString))
|
||||
log.info("{} has been disconnected (invalid token)".format(requestTokenString))
|
||||
finally:
|
||||
# Unlock token
|
||||
if userToken != None:
|
||||
userToken.lock.release()
|
||||
|
||||
if glob.outputRequestTime == True:
|
||||
# End time
|
||||
et = datetime.datetime.now()
|
||||
|
||||
# Total time:
|
||||
tt = float((et.microsecond-st.microsecond)/1000)
|
||||
log.debug("Request time: {}ms".format(tt))
|
||||
|
||||
# Send server's response to client
|
||||
# We don't use token object because we might not have a token (failed login)
|
||||
if glob.gzip == True:
|
||||
# First, write the gzipped response
|
||||
self.write(gzip.compress(responseData, int(glob.conf.config["server"]["gziplevel"])))
|
||||
|
||||
# Then, add gzip headers
|
||||
self.add_header("Vary", "Accept-Encoding")
|
||||
self.add_header("Content-Encoding", "gzip")
|
||||
else:
|
||||
# First, write the response
|
||||
self.write(responseData)
|
||||
|
||||
# Add all the headers AFTER the response has been written
|
||||
self.set_status(200)
|
||||
self.add_header("cho-token", responseTokenString)
|
||||
self.add_header("cho-protocol", "19")
|
||||
#self.add_header("Keep-Alive", "timeout=5, max=100")
|
||||
#self.add_header("Connection", "keep-alive")
|
||||
self.add_header("Content-Type", "text/html; charset=UTF-8")
|
||||
except:
|
||||
log.error("Unknown error!\n```\n{}\n{}```".format(sys.exc_info(), traceback.format_exc()))
|
||||
if glob.sentry:
|
||||
yield tornado.gen.Task(self.captureException, exc_info=True)
|
||||
#finally:
|
||||
# self.finish()
|
||||
|
||||
@tornado.web.asynchronous
|
||||
@tornado.gen.engine
|
||||
def asyncGet(self):
|
||||
html = "<html><head><title>MA MAURO ESISTE?</title><style type='text/css'>body{width:30%}</style></head><body><pre>"
|
||||
html += " _ __<br>"
|
||||
html += " (_) / /<br>"
|
||||
html += " ______ __ ____ ____ / /____<br>"
|
||||
html += " / ___/ / _ \\/ _ \\/ / _ \\<br>"
|
||||
html += " / / / / /_) / /_) / / ____/<br>"
|
||||
html += "/__/ /__/ .___/ .___/__/ \\_____/<br>"
|
||||
html += " / / / /<br>"
|
||||
html += " /__/ /__/<br>"
|
||||
html += "<b>PYTHON > ALL VERSION</b><br><br>"
|
||||
html += "<marquee style='white-space:pre;'><br>"
|
||||
html += " .. o .<br>"
|
||||
html += " o.o o . o<br>"
|
||||
html += " oo...<br>"
|
||||
html += " __[]__<br>"
|
||||
html += " phwr--> _\\:D/_/o_o_o_|__ <span style=\"font-family: 'Comic Sans MS'; font-size: 8pt;\">u wot m8</span><br>"
|
||||
html += " \\\"\"\"\"\"\"\"\"\"\"\"\"\"\"/<br>"
|
||||
html += " \\ . .. .. . /<br>"
|
||||
html += "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^<br>"
|
||||
html += "</marquee><br><strike>reverse engineering a protocol impossible to reverse engineer since always</strike><br>we are actually reverse engineering bancho successfully. for the third time.<br><br><i>© Ripple team, 2016</i></pre></body></html>"
|
||||
self.write(html)
|
||||
#yield tornado.gen.Task(self.captureMessage, "test")
|
||||
#self.finish()
|
|
@ -1,266 +0,0 @@
|
|||
import datetime
|
||||
import gzip
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import tornado.gen
|
||||
import tornado.web
|
||||
from raven.contrib.tornado import SentryMixin
|
||||
|
||||
from common.log import logUtils as log
|
||||
from common.web import requestsManager
|
||||
from constants import exceptions
|
||||
from constants import packetIDs
|
||||
from constants import serverPackets
|
||||
from events import cantSpectateEvent
|
||||
from events import changeActionEvent
|
||||
from events import changeMatchModsEvent
|
||||
from events import changeMatchPasswordEvent
|
||||
from events import changeMatchSettingsEvent
|
||||
from events import changeSlotEvent
|
||||
from events import channelJoinEvent
|
||||
from events import channelPartEvent
|
||||
from events import createMatchEvent
|
||||
from events import friendAddEvent
|
||||
from events import friendRemoveEvent
|
||||
from events import joinLobbyEvent
|
||||
from events import joinMatchEvent
|
||||
from events import loginEvent
|
||||
from events import logoutEvent
|
||||
from events import matchChangeTeamEvent
|
||||
from events import matchCompleteEvent
|
||||
from events import matchFailedEvent
|
||||
from events import matchFramesEvent
|
||||
from events import matchHasBeatmapEvent
|
||||
from events import matchInviteEvent
|
||||
from events import matchLockEvent
|
||||
from events import matchNoBeatmapEvent
|
||||
from events import matchPlayerLoadEvent
|
||||
from events import matchReadyEvent
|
||||
from events import matchSkipEvent
|
||||
from events import matchStartEvent
|
||||
from events import matchTransferHostEvent
|
||||
from events import partLobbyEvent
|
||||
from events import partMatchEvent
|
||||
from events import requestStatusUpdateEvent
|
||||
from events import sendPrivateMessageEvent
|
||||
from events import sendPublicMessageEvent
|
||||
from events import setAwayMessageEvent
|
||||
from events import spectateFramesEvent
|
||||
from events import startSpectatingEvent
|
||||
from events import stopSpectatingEvent
|
||||
from events import userPanelRequestEvent
|
||||
from events import userStatsRequestEvent
|
||||
from events import tournamentMatchInfoRequestEvent
|
||||
from events import tournamentJoinMatchChannelEvent
|
||||
from events import tournamentLeaveMatchChannelEvent
|
||||
from helpers import packetHelper
|
||||
from objects import glob
|
||||
from common.sentry import sentry
|
||||
|
||||
|
||||
class handler(requestsManager.asyncRequestHandler):
|
||||
@tornado.web.asynchronous
|
||||
@tornado.gen.engine
|
||||
@sentry.captureTornado
|
||||
def asyncPost(self):
|
||||
# Track time if needed
|
||||
if glob.outputRequestTime:
|
||||
# Start time
|
||||
st = datetime.datetime.now()
|
||||
|
||||
# Client's token string and request data
|
||||
requestTokenString = self.request.headers.get("osu-token")
|
||||
requestData = self.request.body
|
||||
|
||||
# Server's token string and request data
|
||||
responseTokenString = "ayy"
|
||||
responseData = bytes()
|
||||
|
||||
if requestTokenString is None:
|
||||
# No token, first request. Handle login.
|
||||
responseTokenString, responseData = loginEvent.handle(self)
|
||||
else:
|
||||
userToken = None # default value
|
||||
try:
|
||||
# This is not the first packet, send response based on client's request
|
||||
# Packet start position, used to read stacked packets
|
||||
pos = 0
|
||||
|
||||
# Make sure the token exists
|
||||
if requestTokenString not in glob.tokens.tokens:
|
||||
raise exceptions.tokenNotFoundException()
|
||||
|
||||
# Token exists, get its object and lock it
|
||||
userToken = glob.tokens.tokens[requestTokenString]
|
||||
userToken.processingLock.acquire()
|
||||
|
||||
# Keep reading packets until everything has been read
|
||||
while pos < len(requestData):
|
||||
# Get packet from stack starting from new packet
|
||||
leftData = requestData[pos:]
|
||||
|
||||
# Get packet ID, data length and data
|
||||
packetID = packetHelper.readPacketID(leftData)
|
||||
dataLength = packetHelper.readPacketLength(leftData)
|
||||
packetData = requestData[pos:(pos+dataLength+7)]
|
||||
|
||||
# Console output if needed
|
||||
if glob.outputPackets and packetID != 4:
|
||||
log.debug("Incoming packet ({})({}):\n\nPacket code: {}\nPacket length: {}\nSingle packet data: {}\n".format(requestTokenString, userToken.username, str(packetID), str(dataLength), str(packetData)))
|
||||
|
||||
# Event handler
|
||||
def handleEvent(ev):
|
||||
def wrapper():
|
||||
ev.handle(userToken, packetData)
|
||||
return wrapper
|
||||
|
||||
eventHandler = {
|
||||
packetIDs.client_changeAction: handleEvent(changeActionEvent),
|
||||
packetIDs.client_logout: handleEvent(logoutEvent),
|
||||
packetIDs.client_friendAdd: handleEvent(friendAddEvent),
|
||||
packetIDs.client_friendRemove: handleEvent(friendRemoveEvent),
|
||||
packetIDs.client_userStatsRequest: handleEvent(userStatsRequestEvent),
|
||||
packetIDs.client_requestStatusUpdate: handleEvent(requestStatusUpdateEvent),
|
||||
packetIDs.client_userPanelRequest: handleEvent(userPanelRequestEvent),
|
||||
|
||||
packetIDs.client_channelJoin: handleEvent(channelJoinEvent),
|
||||
packetIDs.client_channelPart: handleEvent(channelPartEvent),
|
||||
packetIDs.client_sendPublicMessage: handleEvent(sendPublicMessageEvent),
|
||||
packetIDs.client_sendPrivateMessage: handleEvent(sendPrivateMessageEvent),
|
||||
packetIDs.client_setAwayMessage: handleEvent(setAwayMessageEvent),
|
||||
|
||||
packetIDs.client_startSpectating: handleEvent(startSpectatingEvent),
|
||||
packetIDs.client_stopSpectating: handleEvent(stopSpectatingEvent),
|
||||
packetIDs.client_cantSpectate: handleEvent(cantSpectateEvent),
|
||||
packetIDs.client_spectateFrames: handleEvent(spectateFramesEvent),
|
||||
|
||||
packetIDs.client_joinLobby: handleEvent(joinLobbyEvent),
|
||||
packetIDs.client_partLobby: handleEvent(partLobbyEvent),
|
||||
packetIDs.client_createMatch: handleEvent(createMatchEvent),
|
||||
packetIDs.client_joinMatch: handleEvent(joinMatchEvent),
|
||||
packetIDs.client_partMatch: handleEvent(partMatchEvent),
|
||||
packetIDs.client_matchChangeSlot: handleEvent(changeSlotEvent),
|
||||
packetIDs.client_matchChangeSettings: handleEvent(changeMatchSettingsEvent),
|
||||
packetIDs.client_matchChangePassword: handleEvent(changeMatchPasswordEvent),
|
||||
packetIDs.client_matchChangeMods: handleEvent(changeMatchModsEvent),
|
||||
packetIDs.client_matchReady: handleEvent(matchReadyEvent),
|
||||
packetIDs.client_matchNotReady: handleEvent(matchReadyEvent),
|
||||
packetIDs.client_matchLock: handleEvent(matchLockEvent),
|
||||
packetIDs.client_matchStart: handleEvent(matchStartEvent),
|
||||
packetIDs.client_matchLoadComplete: handleEvent(matchPlayerLoadEvent),
|
||||
packetIDs.client_matchSkipRequest: handleEvent(matchSkipEvent),
|
||||
packetIDs.client_matchScoreUpdate: handleEvent(matchFramesEvent),
|
||||
packetIDs.client_matchComplete: handleEvent(matchCompleteEvent),
|
||||
packetIDs.client_matchNoBeatmap: handleEvent(matchNoBeatmapEvent),
|
||||
packetIDs.client_matchHasBeatmap: handleEvent(matchHasBeatmapEvent),
|
||||
packetIDs.client_matchTransferHost: handleEvent(matchTransferHostEvent),
|
||||
packetIDs.client_matchFailed: handleEvent(matchFailedEvent),
|
||||
packetIDs.client_matchChangeTeam: handleEvent(matchChangeTeamEvent),
|
||||
packetIDs.client_invite: handleEvent(matchInviteEvent),
|
||||
|
||||
packetIDs.client_tournamentMatchInfoRequest: handleEvent(tournamentMatchInfoRequestEvent),
|
||||
packetIDs.client_tournamentJoinMatchChannel: handleEvent(tournamentJoinMatchChannelEvent),
|
||||
packetIDs.client_tournamentLeaveMatchChannel: handleEvent(tournamentLeaveMatchChannelEvent),
|
||||
}
|
||||
|
||||
# Packets processed if in restricted mode.
|
||||
# All other packets will be ignored if the user is in restricted mode
|
||||
packetsRestricted = [
|
||||
packetIDs.client_logout,
|
||||
packetIDs.client_userStatsRequest,
|
||||
packetIDs.client_requestStatusUpdate,
|
||||
packetIDs.client_userPanelRequest,
|
||||
packetIDs.client_changeAction,
|
||||
packetIDs.client_channelJoin,
|
||||
packetIDs.client_channelPart,
|
||||
]
|
||||
|
||||
# Process/ignore packet
|
||||
if packetID != 4:
|
||||
if packetID in eventHandler:
|
||||
if not userToken.restricted or (userToken.restricted and packetID in packetsRestricted):
|
||||
eventHandler[packetID]()
|
||||
else:
|
||||
log.warning("Ignored packet id from {} ({}) (user is restricted)".format(requestTokenString, packetID))
|
||||
else:
|
||||
log.warning("Unknown packet id from {} ({})".format(requestTokenString, packetID))
|
||||
|
||||
# Update pos so we can read the next stacked packet
|
||||
# +7 because we add packet ID bytes, unused byte and data length bytes
|
||||
pos += dataLength+7
|
||||
|
||||
# Token queue built, send it
|
||||
responseTokenString = userToken.token
|
||||
responseData = userToken.queue
|
||||
userToken.resetQueue()
|
||||
except exceptions.tokenNotFoundException:
|
||||
# Token not found. Disconnect that user
|
||||
responseData = serverPackets.loginError()
|
||||
responseData += serverPackets.notification("Whoops! Something went wrong, please login again.")
|
||||
log.warning("Received packet from unknown token ({}).".format(requestTokenString))
|
||||
log.info("{} has been disconnected (invalid token)".format(requestTokenString))
|
||||
finally:
|
||||
# Unlock token
|
||||
if userToken is not None:
|
||||
# Update ping time for timeout
|
||||
userToken.updatePingTime()
|
||||
# Release processing lock
|
||||
userToken.processingLock.release()
|
||||
# Delete token if kicked
|
||||
if userToken.kicked:
|
||||
glob.tokens.deleteToken(userToken)
|
||||
|
||||
if glob.outputRequestTime:
|
||||
# End time
|
||||
et = datetime.datetime.now()
|
||||
|
||||
# Total time:
|
||||
tt = float((et.microsecond-st.microsecond)/1000)
|
||||
log.debug("Request time: {}ms".format(tt))
|
||||
|
||||
# Send server's response to client
|
||||
# We don't use token object because we might not have a token (failed login)
|
||||
if glob.gzip:
|
||||
# First, write the gzipped response
|
||||
self.write(gzip.compress(responseData, int(glob.conf.config["server"]["gziplevel"])))
|
||||
|
||||
# Then, add gzip headers
|
||||
self.add_header("Vary", "Accept-Encoding")
|
||||
self.add_header("Content-Encoding", "gzip")
|
||||
else:
|
||||
# First, write the response
|
||||
self.write(responseData)
|
||||
|
||||
# Add all the headers AFTER the response has been written
|
||||
self.set_status(200)
|
||||
self.add_header("cho-token", responseTokenString)
|
||||
self.add_header("cho-protocol", "19")
|
||||
self.add_header("Connection", "keep-alive")
|
||||
self.add_header("Keep-Alive", "timeout=5, max=100")
|
||||
self.add_header("Content-Type", "text/html; charset=UTF-8")
|
||||
|
||||
@tornado.web.asynchronous
|
||||
@tornado.gen.engine
|
||||
def asyncGet(self):
|
||||
html = "<html><head><title>MA MAURO ESISTE?</title><style type='text/css'>body{width:30%;background:#222;color:#fff;}</style></head><body><pre>"
|
||||
html += " _ __<br>"
|
||||
html += " (_) / /<br>"
|
||||
html += " ______ __ ____ ____ / /____<br>"
|
||||
html += " / ___/ / _ \\/ _ \\/ / _ \\<br>"
|
||||
html += " / / / / /_) / /_) / / ____/<br>"
|
||||
html += "/__/ /__/ .___/ .___/__/ \\_____/<br>"
|
||||
html += " / / / /<br>"
|
||||
html += " /__/ /__/<br>"
|
||||
html += "<b>PYTHON > ALL VERSION</b><br><br>"
|
||||
html += "<marquee style='white-space:pre;'><br>"
|
||||
html += " .. o .<br>"
|
||||
html += " o.o o . o<br>"
|
||||
html += " oo...<br>"
|
||||
html += " __[]__<br>"
|
||||
html += " phwr--> _\\:D/_/o_o_o_|__ <span style=\"font-family: 'Comic Sans MS'; font-size: 8pt;\">u wot m8</span><br>"
|
||||
html += " \\\"\"\"\"\"\"\"\"\"\"\"\"\"\"/<br>"
|
||||
html += " \\ . .. .. . /<br>"
|
||||
html += "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^<br>"
|
||||
html += "</marquee><br><strike>reverse engineering a protocol impossible to reverse engineer since always</strike><br>we are actually reverse engineering bancho successfully. for the third time.<br><br>Running osufx branch.<br><i>© Ripple team, 2016</i></pre></body></html>"
|
||||
self.write(html)
|
52
handlers/routes.py
Normal file
52
handlers/routes.py
Normal file
|
@ -0,0 +1,52 @@
|
|||
"""
|
||||
WIP feature that will come in the future.
|
||||
Don't import
|
||||
"""
|
||||
import flask
|
||||
from objects import glob
|
||||
from constants import exceptions
|
||||
|
||||
@app.route("/api/online-users-count")
|
||||
def APIonlineUsersCount():
|
||||
return flask.jsonify({"count" : len(glob.tokens.tokens)-1})
|
||||
|
||||
@app.route("/api/user-info")
|
||||
def APIonlineUsers():
|
||||
resp = {}
|
||||
|
||||
try:
|
||||
u = flask.request.args.get('u')
|
||||
|
||||
# Username/userID
|
||||
if u.isdigit():
|
||||
u = int(u)
|
||||
else:
|
||||
u = userHelper.getID(u)
|
||||
if u == None:
|
||||
raise exceptions.userNotFoundException
|
||||
|
||||
# Make sure this user is online
|
||||
userToken = glob.tokens.getTokenFromUserID(u)
|
||||
if userToken == None:
|
||||
raise exceptions.tokenNotFoundException
|
||||
|
||||
# Build response dictionary
|
||||
resp["response"] = "1"
|
||||
resp[userToken.username] = {
|
||||
"userID" : userToken.userID,
|
||||
"actionID" : userToken.actionID,
|
||||
"actionText" : userToken.actionText,
|
||||
"actionMd5" : userToken.actionMd5,
|
||||
"actionMods": userToken.actionMods,
|
||||
"gameMode": userToken.gameMode,
|
||||
"country": countryHelper.getCountryLetters(userToken.country),
|
||||
"position": userToken.location,
|
||||
"spectating": userToken.spectating,
|
||||
"spectators": userToken.spectators
|
||||
}
|
||||
except exceptions.userNotFoundException:
|
||||
resp["response"] = "-1"
|
||||
except exceptions.tokenNotFoundException:
|
||||
resp["response"] = "-2"
|
||||
finally:
|
||||
return flask.jsonify(resp)
|
|
@ -1,103 +1,114 @@
|
|||
from common.log import logUtils as log
|
||||
from common.ripple import userUtils
|
||||
from constants import exceptions
|
||||
from constants import messageTemplates
|
||||
from constants import serverPackets
|
||||
from events import logoutEvent
|
||||
from objects import fokabot
|
||||
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 constants import messageTemplates
|
||||
|
||||
|
||||
def joinChannel(userID = 0, channel = "", token = None, toIRC = True, force=False):
|
||||
def joinChannel(userID = 0, channel = "", token = None, toIRC = True):
|
||||
"""
|
||||
Join a channel
|
||||
|
||||
:param userID: user ID of the user that joins the channel. Optional. token can be used instead.
|
||||
:param token: user token object of user that joins the channel. Optional. userID can be used instead.
|
||||
:param channel: channel name
|
||||
:param toIRC: if True, send this channel join event to IRC. Must be true if joining from bancho. Default: True
|
||||
:param force: whether to allow game clients to join #spect_ and #multi_ channels
|
||||
:return: 0 if joined or other IRC code in case of error. Needed only on IRC-side
|
||||
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 is None:
|
||||
if token == None:
|
||||
token = glob.tokens.getTokenFromUserID(userID)
|
||||
# Make sure the token exists
|
||||
if token is None:
|
||||
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()
|
||||
raise exceptions.channelUnknownException
|
||||
|
||||
# Make sure a game client is not trying to join a #multi_ or #spect_ channel manually
|
||||
# Check channel permissions
|
||||
channelObject = glob.channels.channels[channel]
|
||||
if channelObject.isSpecial and not token.irc and not force:
|
||||
raise exceptions.channelUnknownException()
|
||||
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(channelObject)
|
||||
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 and not toIRC:
|
||||
glob.ircServer.banchoJoinChannel(token.username, channel)
|
||||
if glob.irc == True and toIRC == True:
|
||||
glob.ircServer.banchoJoinChannel(username, channel)
|
||||
|
||||
# Console output
|
||||
log.info("{} joined channel {}".format(token.username, channel))
|
||||
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(token.username, channel))
|
||||
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(token.username, channel))
|
||||
return 403
|
||||
except exceptions.userAlreadyInChannelException:
|
||||
log.warning("User {} already in channel {}".format(token.username, channel))
|
||||
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, force=False):
|
||||
def partChannel(userID = 0, channel = "", token = None, toIRC = True, kick = False):
|
||||
"""
|
||||
Part a channel
|
||||
|
||||
:param userID: user ID of the user that parts the channel. Optional. token can be used instead.
|
||||
:param token: user token object of user that parts the channel. Optional. userID can be used instead.
|
||||
:param channel: channel name
|
||||
:param toIRC: if True, send this channel join event to IRC. Must be true if joining from bancho. Optional. Default: True
|
||||
:param kick: if True, channel tab will be closed on client. Used when leaving lobby. Optional. Default: False
|
||||
:param force: whether to allow game clients to part #spect_ and #multi_ channels
|
||||
:return: 0 if joined or other IRC code in case of error. Needed only on IRC-side
|
||||
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:
|
||||
# Make sure the client is not drunk and sends partChannel when closing a PM tab
|
||||
if not channel.startswith("#"):
|
||||
return
|
||||
|
||||
# Get token if not defined
|
||||
if token is None:
|
||||
if token == None:
|
||||
token = glob.tokens.getTokenFromUserID(userID)
|
||||
# Make sure the token exists
|
||||
if token is None:
|
||||
raise exceptions.userNotFoundException()
|
||||
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 is None:
|
||||
if token.spectating == 0:
|
||||
s = userID
|
||||
else:
|
||||
s = token.spectatingUserID
|
||||
s = token.spectating
|
||||
channel = "#spect_{}".format(s)
|
||||
elif channel == "#multiplayer":
|
||||
channel = "#multi_{}".format(token.matchID)
|
||||
|
@ -108,96 +119,84 @@ def partChannel(userID = 0, channel = "", token = None, toIRC = True, kick = Fal
|
|||
|
||||
# Make sure the channel exists
|
||||
if channel not in glob.channels.channels:
|
||||
raise exceptions.channelUnknownException()
|
||||
|
||||
# Make sure a game client is not trying to join a #multi_ or #spect_ channel manually
|
||||
channelObject = glob.channels.channels[channel]
|
||||
if channelObject.isSpecial and not token.irc and not force:
|
||||
raise exceptions.channelUnknownException()
|
||||
|
||||
# Make sure the user is in the channel
|
||||
if channel not in token.joinedChannels:
|
||||
raise exceptions.userNotInChannelException()
|
||||
raise exceptions.channelUnknownException
|
||||
|
||||
# Part channel (token-side and channel-side)
|
||||
token.partChannel(channelObject)
|
||||
|
||||
# Delete temporary channel if everyone left
|
||||
if "chat/{}".format(channelObject.name) in glob.streams.streams:
|
||||
if channelObject.temp and len(glob.streams.streams["chat/{}".format(channelObject.name)].clients) - 1 == 0:
|
||||
glob.channels.removeChannel(channelObject.name)
|
||||
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:
|
||||
if kick == True:
|
||||
token.enqueue(serverPackets.channelKicked(channelClient))
|
||||
|
||||
# IRC part
|
||||
if glob.irc and toIRC:
|
||||
glob.ircServer.banchoPartChannel(token.username, channel)
|
||||
if glob.irc == True and toIRC == True:
|
||||
glob.ircServer.banchoPartChannel(username, channel)
|
||||
|
||||
# Console output
|
||||
log.info("{} parted channel {} ({})".format(token.username, channel, channelClient))
|
||||
log.info("{} parted channel {} ({})".format(username, channel, channelClient))
|
||||
|
||||
# Return IRC code
|
||||
return 0
|
||||
except exceptions.channelUnknownException:
|
||||
log.warning("{} attempted to part an unknown channel ({})".format(token.username, channel))
|
||||
log.warning("{} attempted to part an unknown channel ({})".format(username, channel))
|
||||
return 403
|
||||
except exceptions.userNotInChannelException:
|
||||
log.warning("{} attempted to part {}, but he's not in that channel".format(token.username, channel))
|
||||
return 442
|
||||
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
|
||||
|
||||
:param fro: sender username. Optional. token can be used instead
|
||||
:param to: receiver channel (if starts with #) or username
|
||||
:param message: text of the message
|
||||
:param token: sender token object. Optional. fro can be used instead
|
||||
:param toIRC: if True, send the message to IRC. If False, send it to Bancho only. Default: True
|
||||
:return: 0 if joined or other IRC code in case of error. Needed only on IRC-side
|
||||
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 = ""
|
||||
tokenString = ""
|
||||
# Get token object if not passed
|
||||
if token is None:
|
||||
if token == None:
|
||||
token = glob.tokens.getTokenFromUsername(fro)
|
||||
if token is None:
|
||||
raise exceptions.userNotFoundException()
|
||||
if token == None:
|
||||
raise exceptions.userNotFoundException
|
||||
else:
|
||||
# token object alredy passed, get its string and its username (fro)
|
||||
fro = token.username
|
||||
#tokenString = token.token
|
||||
tokenString = token.token
|
||||
|
||||
# Make sure this is not a tournament client
|
||||
# if token.tournament:
|
||||
# raise exceptions.userTournamentException()
|
||||
# Set some variables
|
||||
userID = token.userID
|
||||
username = token.username
|
||||
recipients = []
|
||||
|
||||
# Make sure the user is not in restricted mode
|
||||
if token.restricted:
|
||||
raise exceptions.userRestrictedException()
|
||||
if token.restricted == True:
|
||||
raise exceptions.userRestrictedException
|
||||
|
||||
# Make sure the user is not silenced
|
||||
if token.isSilenced():
|
||||
raise exceptions.userSilencedException()
|
||||
|
||||
# Redirect !report to FokaBot
|
||||
if message.startswith("!report"):
|
||||
to = glob.BOT_NAME
|
||||
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 is None:
|
||||
s = token.userID
|
||||
if token.spectating == 0:
|
||||
s = userID
|
||||
else:
|
||||
s = token.spectatingUserID
|
||||
s = token.spectating
|
||||
to = "#spect_{}".format(s)
|
||||
elif to == "#multiplayer":
|
||||
to = "#multi_{}".format(token.matchID)
|
||||
|
@ -206,10 +205,6 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
|
|||
elif to.startswith("#multi_"):
|
||||
toClient = "#multiplayer"
|
||||
|
||||
# Make sure the message is valid
|
||||
if not message.strip():
|
||||
raise exceptions.invalidArgumentsException()
|
||||
|
||||
# Truncate message if > 2048 characters
|
||||
message = message[:2048]+"..." if len(message) > 2048 else message
|
||||
|
||||
|
@ -217,185 +212,116 @@ def sendMessage(fro = "", to = "", message = "", token = None, toIRC = True):
|
|||
message = glob.chatFilters.filterMessage(message)
|
||||
|
||||
# Build packet bytes
|
||||
packet = serverPackets.sendMessage(token.username, toClient, message)
|
||||
packet = serverPackets.sendMessage(username, toClient, message)
|
||||
|
||||
# Send the message
|
||||
isChannel = to.startswith("#")
|
||||
if isChannel:
|
||||
if isChannel == True:
|
||||
# CHANNEL
|
||||
# Make sure the channel exists
|
||||
if to not in glob.channels.channels:
|
||||
raise exceptions.channelUnknownException()
|
||||
raise exceptions.channelUnknownException
|
||||
|
||||
# Make sure the channel is not in moderated mode
|
||||
if glob.channels.channels[to].moderated and not token.admin:
|
||||
raise exceptions.channelModeratedException()
|
||||
|
||||
# Make sure we are in the channel
|
||||
if to not in token.joinedChannels:
|
||||
# I'm too lazy to put and test the correct IRC error code here...
|
||||
# but IRC is not strict at all so who cares
|
||||
raise exceptions.channelNoPermissionsException()
|
||||
if glob.channels.channels[to].moderated == True and token.admin == False:
|
||||
raise exceptions.channelModeratedException
|
||||
|
||||
# Make sure we have write permissions
|
||||
if not glob.channels.channels[to].publicWrite and not token.admin:
|
||||
raise exceptions.channelNoPermissionsException()
|
||||
|
||||
# Add message in buffer
|
||||
token.addMessageInBuffer(to, message)
|
||||
if glob.channels.channels[to].publicWrite == False and token.admin == False:
|
||||
raise exceptions.channelNoPermissionsException
|
||||
|
||||
# Everything seems fine, build recipients list and send packet
|
||||
glob.streams.broadcast("chat/{}".format(to), packet, but=[token.token])
|
||||
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 is None:
|
||||
raise exceptions.userNotFoundException()
|
||||
|
||||
# Make sure the recipient is not a tournament client
|
||||
#if recipientToken.tournament:
|
||||
# raise exceptions.userTournamentException()
|
||||
if recipientToken == None:
|
||||
raise exceptions.userNotFoundException
|
||||
|
||||
# Make sure the recipient is not restricted or we are FokaBot
|
||||
if recipientToken.restricted and fro.lower() != glob.BOT_NAME.lower():
|
||||
raise exceptions.userRestrictedException()
|
||||
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
|
||||
|
||||
# Away check
|
||||
if recipientToken.awayCheck(token.userID):
|
||||
sendMessage(to, fro, "\x01ACTION is away: {}\x01".format(recipientToken.awayMessage))
|
||||
|
||||
# Check message templates (mods/admins only)
|
||||
if message in messageTemplates.templates and token.admin:
|
||||
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 and toIRC:
|
||||
messageSplitInLines = message.encode("latin-1").decode("utf-8").split("\n")
|
||||
for line in messageSplitInLines:
|
||||
if line == messageSplitInLines[:1] and line == "":
|
||||
continue
|
||||
glob.ircServer.banchoMessage(fro, to, line)
|
||||
if glob.irc == True and toIRC == True:
|
||||
glob.ircServer.banchoMessage(fro, to, message)
|
||||
|
||||
# Spam protection (ignore FokaBot)
|
||||
if token.userID > 999:
|
||||
if userID > 999:
|
||||
token.spamProtection()
|
||||
|
||||
# Fokabot message
|
||||
if isChannel or to.lower() == glob.BOT_NAME.lower():
|
||||
fokaMessage = fokabot.fokabotResponse(token.username, to, message)
|
||||
if fokaMessage:
|
||||
sendMessage(glob.BOT_NAME, to if isChannel else fro, fokaMessage)
|
||||
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("#") and not (message.startswith("\x01ACTION is playing") and to.startswith("#spect_")):
|
||||
log.chat("{fro} @ {to}: {message}".format(fro=token.username, to=to, message=message.encode("latin-1").decode("utf-8")))
|
||||
glob.schiavo.sendChatlog("**{fro} @ {to}:** {message}".format(fro=token.username, to=to, message=message.encode("latin-1").decode("utf-8")))
|
||||
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(token.username))
|
||||
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(token.username, to))
|
||||
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(token.username, to))
|
||||
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(token.username, to))
|
||||
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(token.username, to))
|
||||
return 404
|
||||
except exceptions.userTournamentException:
|
||||
log.warning("{} tried to send a message {}, but the recipient is a tournament client".format(token.username, to))
|
||||
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
|
||||
except exceptions.invalidArgumentsException:
|
||||
log.warning("{} tried to send an invalid message to {}".format(token.username, to))
|
||||
return 404
|
||||
|
||||
|
||||
|
||||
""" IRC-Bancho Connect/Disconnect/Join/Part interfaces"""
|
||||
def fixUsernameForBancho(username):
|
||||
"""
|
||||
Convert username from IRC format (without spaces) to Bancho format (with spaces)
|
||||
|
||||
:param username: username to convert
|
||||
:return: converted username
|
||||
"""
|
||||
# If there are no spaces or underscores in the name
|
||||
# return it
|
||||
if " " not in username and "_" not in username:
|
||||
return username
|
||||
|
||||
# Exact match first
|
||||
result = glob.db.fetch("SELECT id FROM users WHERE username = %s LIMIT 1", [username])
|
||||
if result is not None:
|
||||
return username
|
||||
|
||||
# Username not found, replace _ with space
|
||||
return username.replace("_", " ")
|
||||
|
||||
def fixUsernameForIRC(username):
|
||||
"""
|
||||
Convert an username from Bancho format to IRC format (underscores instead of spaces)
|
||||
|
||||
:param username: username to convert
|
||||
:return: converted username
|
||||
"""
|
||||
return username.replace(" ", "_")
|
||||
|
||||
def IRCConnect(username):
|
||||
"""
|
||||
Handle IRC login bancho-side.
|
||||
Add token and broadcast login packet.
|
||||
|
||||
:param username: username
|
||||
:return:
|
||||
"""
|
||||
userID = userUtils.getID(username)
|
||||
if not userID:
|
||||
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.streams.broadcast("main", serverPackets.userPanel(userID))
|
||||
glob.tokens.enqueueAll(serverPackets.userPanel(userID))
|
||||
log.info("{} logged in from IRC".format(username))
|
||||
|
||||
def IRCDisconnect(username):
|
||||
"""
|
||||
Handle IRC logout bancho-side.
|
||||
Remove token and broadcast logout packet.
|
||||
|
||||
:param username: username
|
||||
:return:
|
||||
"""
|
||||
token = glob.tokens.getTokenFromUsername(username)
|
||||
if token is None:
|
||||
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):
|
||||
"""
|
||||
Handle IRC channel join bancho-side.
|
||||
|
||||
:param username: username
|
||||
:param channel: channel name
|
||||
:return: IRC return code
|
||||
"""
|
||||
userID = userUtils.getID(username)
|
||||
if not userID:
|
||||
userID = userHelper.getID(username)
|
||||
if userID == False:
|
||||
log.warning("{} doesn't exist".format(username))
|
||||
return
|
||||
# NOTE: This should have also `toIRC` = False` tho,
|
||||
|
@ -404,30 +330,8 @@ def IRCJoinChannel(username, channel):
|
|||
return joinChannel(userID, channel)
|
||||
|
||||
def IRCPartChannel(username, channel):
|
||||
"""
|
||||
Handle IRC channel part bancho-side.
|
||||
|
||||
:param username: username
|
||||
:param channel: channel name
|
||||
:return: IRC return code
|
||||
"""
|
||||
userID = userUtils.getID(username)
|
||||
if not userID:
|
||||
userID = userHelper.getID(username)
|
||||
if userID == False:
|
||||
log.warning("{} doesn't exist".format(username))
|
||||
return
|
||||
return partChannel(userID, channel)
|
||||
|
||||
def IRCAway(username, message):
|
||||
"""
|
||||
Handle IRC away command bancho-side.
|
||||
|
||||
:param username:
|
||||
:param message: away message
|
||||
:return: IRC return code
|
||||
"""
|
||||
userID = userUtils.getID(username)
|
||||
if not userID:
|
||||
log.warning("{} doesn't exist".format(username))
|
||||
return
|
||||
glob.tokens.getTokenFromUserID(userID).awayMessage = message
|
||||
return 305 if message == "" else 306
|
|
@ -2,16 +2,26 @@ import os
|
|||
import configparser
|
||||
|
||||
class config:
|
||||
# Check if config.ini exists and load/generate it
|
||||
def __init__(self, file):
|
||||
"""
|
||||
Initialize a config file object
|
||||
"""
|
||||
config.ini object
|
||||
|
||||
:param file: file name
|
||||
config -- list with ini data
|
||||
default -- if true, we have generated a default config.ini
|
||||
"""
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
fileName = "" # config filename
|
||||
default = True
|
||||
|
||||
# Check if config.ini exists and load/generate it
|
||||
def __init__(self, __file):
|
||||
"""
|
||||
self.config = configparser.ConfigParser()
|
||||
self.default = True
|
||||
self.fileName = file
|
||||
Initialize a config object
|
||||
|
||||
__file -- filename
|
||||
"""
|
||||
|
||||
self.fileName = __file
|
||||
if os.path.isfile(self.fileName):
|
||||
# config.ini found, load it
|
||||
self.config.read(self.fileName)
|
||||
|
@ -25,10 +35,11 @@ class config:
|
|||
# Check if config.ini has all needed the keys
|
||||
def checkConfig(self):
|
||||
"""
|
||||
Check is the config file has all required keys
|
||||
Check if this config has the required keys
|
||||
|
||||
:return: True if valid, False if not valid
|
||||
return -- True if valid, False if not
|
||||
"""
|
||||
|
||||
try:
|
||||
# Try to get all the required keys
|
||||
self.config.get("db","host")
|
||||
|
@ -37,54 +48,38 @@ class config:
|
|||
self.config.get("db","database")
|
||||
self.config.get("db","workers")
|
||||
|
||||
self.config.get("redis","host")
|
||||
self.config.get("redis","port")
|
||||
self.config.get("redis","database")
|
||||
self.config.get("redis","password")
|
||||
|
||||
self.config.get("server","port")
|
||||
self.config.get("server","threads")
|
||||
self.config.get("server","gzip")
|
||||
self.config.get("server","gziplevel")
|
||||
self.config.get("server","cikey")
|
||||
|
||||
self.config.get("cheesegull", "apiurl")
|
||||
self.config.get("cheesegull", "apikey")
|
||||
self.config.get("server","cloudflare")
|
||||
|
||||
self.config.get("debug","enable")
|
||||
self.config.get("debug","packets")
|
||||
self.config.get("debug","time")
|
||||
|
||||
self.config.get("sentry","enable")
|
||||
self.config.get("sentry","banchodsn")
|
||||
self.config.get("sentry","ircdsn")
|
||||
self.config.get("sentry","banchodns")
|
||||
self.config.get("sentry","ircdns")
|
||||
|
||||
self.config.get("discord","enable")
|
||||
self.config.get("discord","boturl")
|
||||
self.config.get("discord","devgroup")
|
||||
|
||||
self.config.get("datadog", "enable")
|
||||
self.config.get("datadog", "apikey")
|
||||
self.config.get("datadog", "appkey")
|
||||
|
||||
self.config.get("irc","enable")
|
||||
self.config.get("irc","port")
|
||||
self.config.get("irc","hostname")
|
||||
|
||||
self.config.get("localize","enable")
|
||||
self.config.get("localize","ipapiurl")
|
||||
|
||||
self.config.get("custom", "config")
|
||||
return True
|
||||
except configparser.Error:
|
||||
except:
|
||||
return False
|
||||
|
||||
def generateDefaultConfig(self):
|
||||
"""
|
||||
Write a default config file to disk
|
||||
|
||||
:return:
|
||||
"""
|
||||
# Generate a default config.ini
|
||||
def generateDefaultConfig(self):
|
||||
"""Open and set default keys for that config file"""
|
||||
|
||||
# Open config.ini in write mode
|
||||
f = open(self.fileName, "w")
|
||||
|
||||
|
@ -96,22 +91,12 @@ class config:
|
|||
self.config.set("db", "database", "ripple")
|
||||
self.config.set("db", "workers", "4")
|
||||
|
||||
self.config.add_section("redis")
|
||||
self.config.set("redis", "host", "localhost")
|
||||
self.config.set("redis", "port", "6379")
|
||||
self.config.set("redis", "database", "0")
|
||||
self.config.set("redis", "password", "")
|
||||
|
||||
self.config.add_section("server")
|
||||
self.config.set("server", "port", "5001")
|
||||
self.config.set("server", "threads", "16")
|
||||
self.config.set("server", "gzip", "1")
|
||||
self.config.set("server", "gziplevel", "6")
|
||||
self.config.set("server", "cikey", "changeme")
|
||||
|
||||
self.config.add_section("cheesegull")
|
||||
self.config.set("cheesegull", "apiurl", "http://cheesegu.ll/api")
|
||||
self.config.set("cheesegull", "apikey", "")
|
||||
self.config.set("server", "cloudflare", "0")
|
||||
|
||||
self.config.add_section("debug")
|
||||
self.config.set("debug", "enable", "0")
|
||||
|
@ -120,31 +105,22 @@ class config:
|
|||
|
||||
self.config.add_section("sentry")
|
||||
self.config.set("sentry", "enable", "0")
|
||||
self.config.set("sentry", "banchodsn", "")
|
||||
self.config.set("sentry", "ircdsn", "")
|
||||
self.config.set("sentry", "banchodns", "")
|
||||
self.config.set("sentry", "ircdns", "")
|
||||
|
||||
self.config.add_section("discord")
|
||||
self.config.set("discord", "enable", "0")
|
||||
self.config.set("discord", "boturl", "")
|
||||
self.config.set("discord", "devgroup", "")
|
||||
|
||||
self.config.add_section("datadog")
|
||||
self.config.set("datadog", "enable", "0")
|
||||
self.config.set("datadog", "apikey", "")
|
||||
self.config.set("datadog", "appkey", "")
|
||||
|
||||
self.config.add_section("irc")
|
||||
self.config.set("irc", "enable", "1")
|
||||
self.config.set("irc", "port", "6667")
|
||||
self.config.set("irc", "hostname", "ripple")
|
||||
|
||||
self.config.add_section("localize")
|
||||
self.config.set("localize", "enable", "1")
|
||||
self.config.set("localize", "ipapiurl", "http://ip.zxq.co")
|
||||
|
||||
self.config.add_section("custom")
|
||||
self.config.set("custom", "config", "common/config.json")
|
||||
|
||||
# Write ini to file and close
|
||||
self.config.write(f)
|
||||
f.close()
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
from common.constants import bcolors
|
||||
"""Some console related functions"""
|
||||
|
||||
from constants import bcolors
|
||||
from objects import glob
|
||||
|
||||
def printServerStartHeader(asciiArt=True):
|
||||
"""
|
||||
Print server start message
|
||||
def printServerStartHeader(asciiArt):
|
||||
"""Print server start header with optional ascii art
|
||||
|
||||
:param asciiArt: print BanchoBoat ascii art. Default: True
|
||||
:return:
|
||||
"""
|
||||
if asciiArt:
|
||||
asciiArt -- if True, will print ascii art too"""
|
||||
|
||||
if asciiArt == True:
|
||||
print("{} _ __".format(bcolors.GREEN))
|
||||
print(" (_) / /")
|
||||
print(" ______ __ ____ ____ / /____")
|
||||
|
@ -27,52 +27,45 @@ def printServerStartHeader(asciiArt=True):
|
|||
print("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^{}".format(bcolors.ENDC))
|
||||
|
||||
printColored("> Welcome to pep.py osu!bancho server v{}".format(glob.VERSION), bcolors.GREEN)
|
||||
printColored("> Common submodule v{}".format(glob.COMMON_VERSION), bcolors.GREEN)
|
||||
printColored("> Made by the Ripple team", bcolors.GREEN)
|
||||
printColored("> {}https://zxq.co/ripple/pep.py".format(bcolors.UNDERLINE), bcolors.GREEN)
|
||||
printColored("> Custom branch by the osufx team (just Sunpy)", bcolors.GREEN)
|
||||
printColored("> {}https://github.com/osufx/pep.py".format(bcolors.UNDERLINE), bcolors.GREEN)
|
||||
printColored("> Press CTRL+C to exit\n", bcolors.GREEN)
|
||||
printColored("> {}https://github.com/osuripple/ripple".format(bcolors.UNDERLINE), bcolors.GREEN)
|
||||
printColored("> Press CTRL+C to exit\n",bcolors.GREEN)
|
||||
|
||||
|
||||
def printNoNl(string):
|
||||
"""
|
||||
Print a string without \n at the end
|
||||
Print string without new line at the end
|
||||
|
||||
:param string: string to print
|
||||
:return:
|
||||
string -- string to print
|
||||
"""
|
||||
|
||||
print(string, end="")
|
||||
|
||||
|
||||
def printColored(string, color):
|
||||
"""
|
||||
Print a colored string
|
||||
Print colored string
|
||||
|
||||
:param string: string to print
|
||||
:param color: ANSI color code
|
||||
:return:
|
||||
string -- string to print
|
||||
color -- see bcolors.py
|
||||
"""
|
||||
|
||||
print("{}{}{}".format(color, string, bcolors.ENDC))
|
||||
|
||||
def printError():
|
||||
"""
|
||||
Print a red "Error"
|
||||
|
||||
:return:
|
||||
"""
|
||||
def printError():
|
||||
"""Print error text FOR LOADING"""
|
||||
|
||||
printColored("Error", bcolors.RED)
|
||||
|
||||
def printDone():
|
||||
"""
|
||||
Print a green "Done"
|
||||
|
||||
:return:
|
||||
"""
|
||||
def printDone():
|
||||
"""Print error text FOR LOADING"""
|
||||
|
||||
printColored("Done", bcolors.GREEN)
|
||||
|
||||
def printWarning():
|
||||
"""
|
||||
Print a yellow "Warning"
|
||||
|
||||
:return:
|
||||
"""
|
||||
def printWarning():
|
||||
"""Print error text FOR LOADING"""
|
||||
|
||||
printColored("Warning", bcolors.YELLOW)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# TODO: Update countries list
|
||||
"""Contains all country codes with their osu numeric code"""
|
||||
|
||||
countryCodes = {
|
||||
"LV": 132,
|
||||
"AD": 3,
|
||||
|
@ -252,13 +253,15 @@ countryCodes = {
|
|||
"AI": 7
|
||||
}
|
||||
|
||||
|
||||
def getCountryID(code):
|
||||
"""
|
||||
Get osu country ID from country letters
|
||||
Get country ID for osu client
|
||||
|
||||
:param code: country letters (eg: US)
|
||||
:return: country osu code
|
||||
code -- country name abbreviation (eg: US)
|
||||
return -- country code int
|
||||
"""
|
||||
|
||||
if code in countryCodes:
|
||||
return countryCodes[code]
|
||||
else:
|
||||
|
@ -268,9 +271,10 @@ def getCountryLetters(code):
|
|||
"""
|
||||
Get country letters from osu country ID
|
||||
|
||||
:param code: osu country ID
|
||||
:return: country letters (XX if not found)
|
||||
code -- country code int
|
||||
return -- country name (2 letters) (XX if code not found)
|
||||
"""
|
||||
|
||||
for key, value in countryCodes.items():
|
||||
if value == code:
|
||||
return key
|
||||
|
|
138
helpers/databaseHelper.py
Normal file
138
helpers/databaseHelper.py
Normal file
|
@ -0,0 +1,138 @@
|
|||
import pymysql
|
||||
from constants import bcolors
|
||||
from helpers import consoleHelper
|
||||
import threading
|
||||
from objects import glob
|
||||
|
||||
class db:
|
||||
"""A MySQL database connection"""
|
||||
|
||||
connection = None
|
||||
disconnected = False
|
||||
pingTime = 600
|
||||
|
||||
def __init__(self, __host, __username, __password, __database, __pingTime = 600):
|
||||
"""
|
||||
Connect to MySQL database
|
||||
|
||||
__host -- MySQL host name
|
||||
__username -- MySQL username
|
||||
__password -- MySQL password
|
||||
__database -- MySQL database name
|
||||
__pingTime -- MySQL database ping time (default: 600)
|
||||
"""
|
||||
|
||||
self.connection = pymysql.connect(host=__host, user=__username, password=__password, db=__database, cursorclass=pymysql.cursors.DictCursor, autocommit=True)
|
||||
self.pingTime = __pingTime
|
||||
self.pingLoop()
|
||||
|
||||
|
||||
def bindParams(self, __query, __params):
|
||||
"""
|
||||
Replace every ? with the respective **escaped** parameter in array
|
||||
|
||||
__query -- query with ?s
|
||||
__params -- array with params
|
||||
|
||||
return -- new query
|
||||
"""
|
||||
|
||||
for i in __params:
|
||||
escaped = self.connection.escape(i)
|
||||
__query = __query.replace("?", str(escaped), 1)
|
||||
|
||||
return __query
|
||||
|
||||
|
||||
def execute(self, __query, __params = None):
|
||||
"""
|
||||
Execute a SQL query
|
||||
|
||||
__query -- query, can contain ?s
|
||||
__params -- array with params. Optional
|
||||
"""
|
||||
|
||||
log.debug(query)
|
||||
with self.connection.cursor() as cursor:
|
||||
try:
|
||||
# Bind params if needed
|
||||
if __params != None:
|
||||
__query = self.bindParams(__query, __params)
|
||||
|
||||
# Execute the query
|
||||
cursor.execute(__query)
|
||||
finally:
|
||||
# Close this connection
|
||||
cursor.close()
|
||||
|
||||
|
||||
def fetch(self, __query, __params = None, __all = False):
|
||||
"""
|
||||
Fetch the first (or all) element(s) of SQL query result
|
||||
|
||||
__query -- query, can contain ?s
|
||||
__params -- array with params. Optional
|
||||
__all -- if true, will fetch all values. Same as fetchAll
|
||||
|
||||
return -- dictionary with result data or False if failed
|
||||
"""
|
||||
|
||||
log.debug(query)
|
||||
with self.connection.cursor() as cursor:
|
||||
try:
|
||||
# Bind params if needed
|
||||
if __params != None:
|
||||
__query = self.bindParams(__query, __params)
|
||||
|
||||
# Execute the query with binded params
|
||||
cursor.execute(__query)
|
||||
|
||||
# Get first result and return it
|
||||
if __all == False:
|
||||
return cursor.fetchone()
|
||||
else:
|
||||
return cursor.fetchall()
|
||||
finally:
|
||||
# Close this connection
|
||||
cursor.close()
|
||||
|
||||
|
||||
def fetchAll(self, __query, __params = None):
|
||||
"""
|
||||
Fetch the all elements of SQL query result
|
||||
|
||||
__query -- query, can contain ?s
|
||||
__params -- array with params. Optional
|
||||
|
||||
return -- dictionary with result data
|
||||
"""
|
||||
|
||||
return self.fetch(__query, __params, True)
|
||||
|
||||
def pingLoop(self):
|
||||
"""
|
||||
Pings MySQL server. We need to ping/execute a query at least once every 8 hours
|
||||
or the connection will die.
|
||||
If called once, will recall after 30 minutes and so on, forever
|
||||
CALL THIS FUNCTION ONLY ONCE!
|
||||
"""
|
||||
|
||||
# Default loop time
|
||||
time = self.pingTime
|
||||
|
||||
# Make sure the connection is alive
|
||||
try:
|
||||
# Try to ping and reconnect if not connected
|
||||
self.connection.ping()
|
||||
if self.disconnected == True:
|
||||
# If we were disconnected, set disconnected to false and print message
|
||||
self.disconnected = False
|
||||
log.error("> Reconnected to MySQL server!", bcolors.GREEN)
|
||||
except:
|
||||
# Can't ping MySQL server. Show error and call loop in 5 seconds
|
||||
log.error("[!] CRITICAL!! MySQL connection died! Make sure your MySQL server is running! Checking again in 5 seconds...", bcolors.RED)
|
||||
self.disconnected = True
|
||||
time = 5
|
||||
|
||||
# Schedule a new check (endless loop)
|
||||
threading.Timer(time, self.pingLoop).start()
|
121
helpers/databaseHelperNew.py
Normal file
121
helpers/databaseHelperNew.py
Normal file
|
@ -0,0 +1,121 @@
|
|||
import MySQLdb
|
||||
import threading
|
||||
from helpers import logHelper as log
|
||||
|
||||
class mysqlWorker:
|
||||
"""
|
||||
Instance of a pettirosso meme
|
||||
"""
|
||||
def __init__(self, wid, host, username, password, database):
|
||||
"""
|
||||
Create a pettirosso meme (mysql worker)
|
||||
|
||||
wid -- worker id
|
||||
host -- hostname
|
||||
username -- MySQL username
|
||||
password -- MySQL password
|
||||
database -- MySQL database name
|
||||
"""
|
||||
self.wid = wid
|
||||
self.connection = MySQLdb.connect(host, username, password, database)
|
||||
self.connection.autocommit(True)
|
||||
self.ready = True
|
||||
self.lock = threading.Lock()
|
||||
|
||||
class db:
|
||||
"""
|
||||
A MySQL db connection with multiple workers
|
||||
"""
|
||||
|
||||
def __init__(self, host, username, password, database, workers):
|
||||
"""
|
||||
Create MySQL workers aka pettirossi meme
|
||||
|
||||
host -- hostname
|
||||
username -- MySQL username
|
||||
password -- MySQL password
|
||||
database -- MySQL database name
|
||||
workers -- Number of workers to spawn
|
||||
"""
|
||||
#self.lock = threading.Lock()
|
||||
#self.connection = MySQLdb.connect(host, username, password, database)
|
||||
|
||||
self.workers = []
|
||||
self.lastWorker = 0
|
||||
self.workersNumber = workers
|
||||
for i in range(0,self.workersNumber):
|
||||
print(".", end="")
|
||||
self.workers.append(mysqlWorker(i, host, username, password, database))
|
||||
|
||||
def getWorker(self):
|
||||
"""
|
||||
Return a worker object (round-robin way)
|
||||
|
||||
return -- worker object
|
||||
"""
|
||||
if self.lastWorker >= self.workersNumber-1:
|
||||
self.lastWorker = 0
|
||||
else:
|
||||
self.lastWorker += 1
|
||||
#print("Using worker {}".format(self.lastWorker))
|
||||
return self.workers[self.lastWorker]
|
||||
|
||||
def execute(self, query, params = ()):
|
||||
"""
|
||||
Executes a query
|
||||
|
||||
query -- Query to execute. You can bind parameters with %s
|
||||
params -- Parameters list. First element replaces first %s and so on. Optional.
|
||||
"""
|
||||
log.debug(query)
|
||||
# Get a worker and acquire its lock
|
||||
worker = self.getWorker()
|
||||
worker.lock.acquire()
|
||||
|
||||
try:
|
||||
# Create cursor, execute query and commit
|
||||
cursor = worker.connection.cursor(MySQLdb.cursors.DictCursor)
|
||||
cursor.execute(query, params)
|
||||
return cursor.lastrowid
|
||||
finally:
|
||||
# Close the cursor and release worker's lock
|
||||
if cursor:
|
||||
cursor.close()
|
||||
worker.lock.release()
|
||||
|
||||
def fetch(self, query, params = (), all = False):
|
||||
"""
|
||||
Fetch a single value from db that matches given query
|
||||
|
||||
query -- Query to execute. You can bind parameters with %s
|
||||
params -- Parameters list. First element replaces first %s and so on. Optional.
|
||||
all -- Fetch one or all values. Used internally. Use fetchAll if you want to fetch all values.
|
||||
"""
|
||||
log.debug(query)
|
||||
# Get a worker and acquire its lock
|
||||
worker = self.getWorker()
|
||||
worker.lock.acquire()
|
||||
|
||||
try:
|
||||
# Create cursor, execute the query and fetch one/all result(s)
|
||||
cursor = worker.connection.cursor(MySQLdb.cursors.DictCursor)
|
||||
cursor.execute(query, params)
|
||||
if all == True:
|
||||
return cursor.fetchall()
|
||||
else:
|
||||
return cursor.fetchone()
|
||||
finally:
|
||||
# Close the cursor and release worker's lock
|
||||
if cursor:
|
||||
cursor.close()
|
||||
worker.lock.release()
|
||||
|
||||
def fetchAll(self, query, params = ()):
|
||||
"""
|
||||
Fetch all values from db that matche given query.
|
||||
Calls self.fetch with all = True.
|
||||
|
||||
query -- Query to execute. You can bind parameters with %s
|
||||
params -- Parameters list. First element replaces first %s and so on. Optional.
|
||||
"""
|
||||
return self.fetch(query, params, True)
|
69
helpers/discordBotHelper.py
Normal file
69
helpers/discordBotHelper.py
Normal file
|
@ -0,0 +1,69 @@
|
|||
import requests
|
||||
from objects import glob
|
||||
from helpers import generalFunctions
|
||||
from urllib.parse import urlencode
|
||||
from helpers import consoleHelper
|
||||
from constants import bcolors
|
||||
|
||||
def sendDiscordMessage(channel, message, alertDev = False, prefix = "**pep.py**"):
|
||||
"""
|
||||
Send a message to a discord server.
|
||||
This is used with ripple's schiavobot.
|
||||
|
||||
channel -- bunk, staff or general
|
||||
message -- message to send
|
||||
alertDev -- if True, hl developers group
|
||||
prefix -- string to prepend to message
|
||||
"""
|
||||
if glob.discord == True:
|
||||
for _ in range(0,20):
|
||||
try:
|
||||
finalMsg = "{prefix} {message}".format(prefix=prefix, message=message) if alertDev == False else "{prefix} {hl} - {message}".format(prefix=prefix, hl=glob.conf.config["discord"]["devgroup"], message=message)
|
||||
requests.get("{}/{}?{}".format(glob.conf.config["discord"]["boturl"], channel, urlencode({ "message": finalMsg })))
|
||||
break
|
||||
except:
|
||||
continue
|
||||
|
||||
|
||||
def sendConfidential(message, alertDev = False):
|
||||
"""
|
||||
Send a message to #bunker
|
||||
|
||||
message -- message to send
|
||||
"""
|
||||
sendDiscordMessage("bunk", message, alertDev)
|
||||
|
||||
|
||||
def sendStaff(message):
|
||||
"""
|
||||
Send a message to #staff
|
||||
|
||||
message -- message to send
|
||||
"""
|
||||
sendDiscordMessage("staff", message)
|
||||
|
||||
|
||||
def sendGeneral(message):
|
||||
"""
|
||||
Send a message to #general
|
||||
|
||||
message -- message to send
|
||||
"""
|
||||
sendDiscordMessage("general", message)
|
||||
|
||||
|
||||
def sendChatlog(message):
|
||||
"""
|
||||
Send a message to #chatlog
|
||||
|
||||
message -- message to send
|
||||
"""
|
||||
sendDiscordMessage("chatlog", message, prefix="")
|
||||
|
||||
def sendCM(message):
|
||||
"""
|
||||
Send a message to #communitymanagers
|
||||
|
||||
message -- message to send
|
||||
"""
|
||||
sendDiscordMessage("cm", message)
|
128
helpers/generalFunctions.py
Normal file
128
helpers/generalFunctions.py
Normal file
|
@ -0,0 +1,128 @@
|
|||
"""Some functions that don't fit in any other file"""
|
||||
from constants import mods
|
||||
from time import gmtime, strftime
|
||||
|
||||
def stringToBool(s):
|
||||
"""
|
||||
Convert a string (True/true/1) to bool
|
||||
|
||||
s -- string/int value
|
||||
return -- True/False
|
||||
"""
|
||||
|
||||
return (s == "True" or s== "true" or s == "1" or s == 1)
|
||||
|
||||
|
||||
def hexString(s):
|
||||
"""
|
||||
Output s' bytes in HEX
|
||||
|
||||
s -- string
|
||||
return -- string with hex value
|
||||
"""
|
||||
|
||||
return ":".join("{:02x}".format(ord(str(c))) for c in s)
|
||||
|
||||
def readableMods(__mods):
|
||||
"""
|
||||
Return a string with readable std mods.
|
||||
Used to convert a mods number for oppai
|
||||
|
||||
__mods -- mods bitwise number
|
||||
return -- readable mods string, eg HDDT
|
||||
"""
|
||||
r = ""
|
||||
if __mods == 0:
|
||||
return r
|
||||
if __mods & mods.NoFail > 0:
|
||||
r += "NF"
|
||||
if __mods & mods.Easy > 0:
|
||||
r += "EZ"
|
||||
if __mods & mods.Hidden > 0:
|
||||
r += "HD"
|
||||
if __mods & mods.HardRock > 0:
|
||||
r += "HR"
|
||||
if __mods & mods.DoubleTime > 0:
|
||||
r += "DT"
|
||||
if __mods & mods.HalfTime > 0:
|
||||
r += "HT"
|
||||
if __mods & mods.Flashlight > 0:
|
||||
r += "FL"
|
||||
if __mods & mods.SpunOut > 0:
|
||||
r += "SO"
|
||||
|
||||
return r
|
||||
|
||||
def getRank(gameMode, __mods, acc, c300, c100, c50, cmiss):
|
||||
"""
|
||||
Return a string with rank/grade for a given score.
|
||||
Used mainly for "tillerino"
|
||||
|
||||
gameMode -- mode (0 = osu!, 1 = Taiko, 2 = CtB, 3 = osu!mania)
|
||||
__mods -- mods bitwise number
|
||||
acc -- accuracy
|
||||
c300 -- 300 hit count
|
||||
c100 -- 100 hit count
|
||||
c50 -- 50 hit count
|
||||
cmiss -- miss count
|
||||
return -- rank/grade string
|
||||
"""
|
||||
total = c300 + c100 + c50 + cmiss
|
||||
hdfl = (__mods & (mods.Hidden | mods.Flashlight | mods.FadeIn)) > 0
|
||||
|
||||
ss = "sshd" if hdfl else "ss"
|
||||
s = "shd" if hdfl else "s"
|
||||
|
||||
if gameMode == 0 or gameMode == 1:
|
||||
# osu!std / taiko
|
||||
ratio300 = c300 / total
|
||||
ratio50 = c50 / total
|
||||
if ratio300 == 1:
|
||||
return ss
|
||||
if ratio300 > 0.9 and ratio50 <= 0.01 and cmiss == 0:
|
||||
return s
|
||||
if (ratio300 > 0.8 and cmiss == 0) or (ratio300 > 0.9):
|
||||
return "a"
|
||||
if (ratio300 > 0.7 and cmiss == 0) or (ratio300 > 0.8):
|
||||
return "b"
|
||||
if ratio300 > 0.6:
|
||||
return "c"
|
||||
return "d"
|
||||
elif gameMode == 2:
|
||||
# CtB
|
||||
if acc == 100:
|
||||
return ss
|
||||
if acc > 98:
|
||||
return s
|
||||
if acc > 94:
|
||||
return "a"
|
||||
if acc > 90:
|
||||
return "b"
|
||||
if acc > 85:
|
||||
return "c"
|
||||
return "d"
|
||||
elif gameMode == 3:
|
||||
# osu!mania
|
||||
if acc == 100:
|
||||
return ss
|
||||
if acc > 95:
|
||||
return s
|
||||
if acc > 90:
|
||||
return "a"
|
||||
if acc > 80:
|
||||
return "b"
|
||||
if acc > 70:
|
||||
return "c"
|
||||
return "d"
|
||||
|
||||
return "a"
|
||||
|
||||
def strContains(s, w):
|
||||
return (' ' + w + ' ') in (' ' + s + ' ')
|
||||
|
||||
def getTimestamp():
|
||||
"""
|
||||
Return current time in YYYY-MM-DD HH:MM:SS format.
|
||||
Used in logs.
|
||||
"""
|
||||
return strftime("%Y-%m-%d %H:%M:%S", gmtime())
|
|
@ -1,17 +1,17 @@
|
|||
import json
|
||||
import urllib.request
|
||||
|
||||
from common.log import logUtils as log
|
||||
import json
|
||||
from objects import glob
|
||||
|
||||
from helpers import logHelper as log
|
||||
|
||||
def getCountry(ip):
|
||||
"""
|
||||
Get country from IP address using geoip api
|
||||
Get country from IP address
|
||||
|
||||
:param ip: IP address
|
||||
:return: country code. XX if invalid.
|
||||
ip -- IP Address
|
||||
return -- Country code (2 letters)
|
||||
"""
|
||||
|
||||
try:
|
||||
# Try to get country from Pikolo Aul's Go-Sanic ip API
|
||||
result = json.loads(urllib.request.urlopen("{}/{}".format(glob.conf.config["localize"]["ipapiurl"], ip), timeout=3).read().decode())["country"]
|
||||
|
@ -20,17 +20,19 @@ def getCountry(ip):
|
|||
log.error("Error in get country")
|
||||
return "XX"
|
||||
|
||||
|
||||
def getLocation(ip):
|
||||
"""
|
||||
Get latitude and longitude from IP address using geoip api
|
||||
Get latitude and longitude from IP address
|
||||
|
||||
:param ip: IP address
|
||||
:return: (latitude, longitude)
|
||||
ip -- IP address
|
||||
return -- [latitude, longitude]
|
||||
"""
|
||||
|
||||
try:
|
||||
# Try to get position from Pikolo Aul's Go-Sanic ip API
|
||||
result = json.loads(urllib.request.urlopen("{}/{}".format(glob.conf.config["localize"]["ipapiurl"], ip), timeout=3).read().decode())["loc"].split(",")
|
||||
return float(result[0]), float(result[1])
|
||||
return [float(result[0]), float(result[1])]
|
||||
except:
|
||||
log.error("Error in get position")
|
||||
return 0, 0
|
||||
return [0,0]
|
||||
|
|
140
helpers/logHelper.py
Normal file
140
helpers/logHelper.py
Normal file
|
@ -0,0 +1,140 @@
|
|||
from constants import bcolors
|
||||
from helpers import discordBotHelper
|
||||
from helpers import generalFunctions
|
||||
from objects import glob
|
||||
from helpers import userHelper
|
||||
import time
|
||||
import os
|
||||
ENDL = "\n" if os.name == "posix" else "\r\n"
|
||||
|
||||
def logMessage(message, alertType = "INFO", messageColor = bcolors.ENDC, discord = None, alertDev = False, of = None, stdout = True):
|
||||
"""
|
||||
Logs a message to stdout/discord/file
|
||||
|
||||
message -- message to log
|
||||
alertType -- can be any string. Standard types: INFO, WARNING and ERRORS. Defalt: INFO
|
||||
messageColor -- message color (see constants.bcolors). Default = bcolots.ENDC (no color)
|
||||
discord -- discord channel (bunker/cm/staff/general). Optional. Default = None
|
||||
alertDev -- if True, devs will receive an hl on discord. Default: False
|
||||
of -- if not None but a string, log the message to that file (inside .data folder). Eg: "warnings.txt" Default: None (don't log to file)
|
||||
stdout -- if True, print the message to stdout. Default: True
|
||||
"""
|
||||
# Get type color from alertType
|
||||
if alertType == "INFO":
|
||||
typeColor = bcolors.GREEN
|
||||
elif alertType == "WARNING":
|
||||
typeColor = bcolors.YELLOW
|
||||
elif alertType == "ERROR":
|
||||
typeColor = bcolors.RED
|
||||
elif alertType == "CHAT":
|
||||
typeColor = bcolors.BLUE
|
||||
elif alertType == "DEBUG":
|
||||
typeColor = bcolors.PINK
|
||||
else:
|
||||
typeColor = bcolors.ENDC
|
||||
|
||||
# Message without colors
|
||||
finalMessage = "[{time}] {type} - {message}".format(time=generalFunctions.getTimestamp(), type=alertType, message=message)
|
||||
|
||||
# Message with colors
|
||||
finalMessageConsole = "{typeColor}[{time}] {type}{endc} - {messageColor}{message}{endc}".format(
|
||||
time=generalFunctions.getTimestamp(),
|
||||
type=alertType,
|
||||
message=message,
|
||||
|
||||
typeColor=typeColor,
|
||||
messageColor=messageColor,
|
||||
endc=bcolors.ENDC)
|
||||
|
||||
# Log to console
|
||||
if stdout == True:
|
||||
print(finalMessageConsole)
|
||||
|
||||
# Log to discord if needed
|
||||
if discord != None:
|
||||
if discord == "bunker":
|
||||
discordBotHelper.sendConfidential(message, alertDev)
|
||||
elif discord == "cm":
|
||||
discordBotHelper.sendCM(message)
|
||||
elif discord == "staff":
|
||||
discordBotHelper.sendStaff(message)
|
||||
elif discord == "general":
|
||||
discordBotHelper.sendGeneral(message)
|
||||
|
||||
# Log to file if needed
|
||||
if of != None:
|
||||
try:
|
||||
glob.fLocks.lockFile(of)
|
||||
with open(".data/{}".format(of), "a") as f:
|
||||
f.write(finalMessage+ENDL)
|
||||
finally:
|
||||
glob.fLocks.unlockFile(of)
|
||||
|
||||
def warning(message, discord = None, alertDev = False):
|
||||
"""
|
||||
Log a warning to stdout, warnings.log (always) and discord (optional)
|
||||
|
||||
message -- warning message
|
||||
discord -- if not None, send message to that discord channel through schiavo. Optional. Default = None
|
||||
alertDev -- if True, send al hl to devs on discord. Optional. Default = False.
|
||||
"""
|
||||
logMessage(message, "WARNING", bcolors.YELLOW, discord, alertDev, "warnings.txt")
|
||||
|
||||
def error(message, discord = None, alertDev = True):
|
||||
"""
|
||||
Log an error to stdout, errors.log (always) and discord (optional)
|
||||
|
||||
message -- error message
|
||||
discord -- if not None, send message to that discord channel through schiavo. Optional. Default = None
|
||||
alertDev -- if True, send al hl to devs on discord. Optional. Default = False.
|
||||
"""
|
||||
logMessage(message, "ERROR", bcolors.RED, discord, alertDev, "errors.txt")
|
||||
|
||||
def info(message, discord = None, alertDev = False):
|
||||
"""
|
||||
Log an error to stdout (and info.log)
|
||||
|
||||
message -- info message
|
||||
discord -- if not None, send message to that discord channel through schiavo. Optional. Default = None
|
||||
alertDev -- if True, send al hl to devs on discord. Optional. Default = False.
|
||||
"""
|
||||
logMessage(message, "INFO", bcolors.ENDC, discord, alertDev, "info.txt")
|
||||
|
||||
def debug(message):
|
||||
"""
|
||||
Log a debug message to stdout and debug.log if server is running in debug mode
|
||||
|
||||
message -- debug message
|
||||
"""
|
||||
if glob.debug == True:
|
||||
logMessage(message, "DEBUG", bcolors.PINK, of="debug.txt")
|
||||
|
||||
def chat(message):
|
||||
"""
|
||||
Log public messages to stdout and chatlog_public.txt
|
||||
|
||||
message -- chat message
|
||||
"""
|
||||
logMessage(message, "CHAT", bcolors.BLUE, of="chatlog_public.txt")
|
||||
|
||||
def pm(message):
|
||||
"""
|
||||
Log private messages to stdout and chatlog_private.txt
|
||||
|
||||
message -- chat message
|
||||
"""
|
||||
logMessage(message, "CHAT", bcolors.BLUE, of="chatlog_private.txt")
|
||||
|
||||
def rap(userID, message, discord=False, through="FokaBot"):
|
||||
"""
|
||||
Log a private message to Admin logs
|
||||
|
||||
userID -- userID of who made the action
|
||||
message -- message without subject (eg: "is a meme" becomes "user is a meme")
|
||||
discord -- if True, send message to discord
|
||||
through -- "through" thing string. Optional. Default: "FokaBot"
|
||||
"""
|
||||
glob.db.execute("INSERT INTO rap_logs (id, userid, text, datetime, through) VALUES (NULL, %s, %s, %s, %s)", [userID, message, int(time.time()), through])
|
||||
if discord == True:
|
||||
username = userHelper.getUsername(userID)
|
||||
logMessage("{} {}".format(username, message), discord=True)
|
272
helpers/packetHelper.py
Normal file
272
helpers/packetHelper.py
Normal file
|
@ -0,0 +1,272 @@
|
|||
import struct
|
||||
from constants import dataTypes
|
||||
|
||||
def uleb128Encode(num):
|
||||
"""
|
||||
Encode int -> uleb128
|
||||
|
||||
num -- int to encode
|
||||
return -- bytearray with encoded number
|
||||
"""
|
||||
|
||||
arr = bytearray()
|
||||
length = 0
|
||||
|
||||
if num == 0:
|
||||
return bytearray(b"\x00")
|
||||
|
||||
while num > 0:
|
||||
arr.append(num & 127)
|
||||
num = num >> 7
|
||||
if num != 0:
|
||||
arr[length] = arr[length] | 128
|
||||
length+=1
|
||||
|
||||
return arr
|
||||
|
||||
|
||||
def uleb128Decode(num):
|
||||
"""
|
||||
Decode uleb128 -> int
|
||||
|
||||
num -- encoded uleb128
|
||||
return -- list. [total, length]
|
||||
"""
|
||||
|
||||
shift = 0
|
||||
|
||||
arr = [0,0] #total, length
|
||||
|
||||
while True:
|
||||
b = num[arr[1]]
|
||||
arr[1]+=1
|
||||
arr[0] = arr[0] | (int(b & 127) << shift)
|
||||
if b & 128 == 0:
|
||||
break
|
||||
shift += 7
|
||||
|
||||
return arr
|
||||
|
||||
|
||||
def unpackData(__data, __dataType):
|
||||
"""
|
||||
Unpacks data according to dataType
|
||||
|
||||
__data -- bytes array to unpack
|
||||
__dataType -- data type. See dataTypes.py
|
||||
|
||||
return -- unpacked bytes
|
||||
"""
|
||||
|
||||
# Get right pack Type
|
||||
if __dataType == dataTypes.uInt16:
|
||||
unpackType = "<H"
|
||||
elif __dataType == dataTypes.sInt16:
|
||||
unpackType = "<h"
|
||||
elif __dataType == dataTypes.uInt32:
|
||||
unpackType = "<L"
|
||||
elif __dataType == dataTypes.sInt32:
|
||||
unpackType = "<l"
|
||||
elif __dataType == dataTypes.uInt64:
|
||||
unpackType = "<Q"
|
||||
elif __dataType == dataTypes.sInt64:
|
||||
unpackType = "<q"
|
||||
elif __dataType == dataTypes.string:
|
||||
unpackType = "<s"
|
||||
elif __dataType == dataTypes.ffloat:
|
||||
unpackType = "<f"
|
||||
else:
|
||||
unpackType = "<B"
|
||||
|
||||
# Unpack
|
||||
return struct.unpack(unpackType, bytes(__data))[0]
|
||||
|
||||
|
||||
def packData(__data, __dataType):
|
||||
"""
|
||||
Packs data according to dataType
|
||||
|
||||
data -- bytes to pack
|
||||
dataType -- data type. See dataTypes.py
|
||||
|
||||
return -- packed bytes
|
||||
"""
|
||||
|
||||
data = bytes() # data to return
|
||||
pack = True # if True, use pack. False only with strings
|
||||
|
||||
# Get right pack Type
|
||||
if __dataType == dataTypes.bbytes:
|
||||
# Bytes, do not use pack, do manually
|
||||
pack = False
|
||||
data = __data
|
||||
elif __dataType == dataTypes.intList:
|
||||
# Pack manually
|
||||
pack = False
|
||||
# Add length
|
||||
data = packData(len(__data), dataTypes.uInt16)
|
||||
# Add all elements
|
||||
for i in __data:
|
||||
data += packData(i, dataTypes.sInt32)
|
||||
elif __dataType == dataTypes.string:
|
||||
# String, do not use pack, do manually
|
||||
pack = False
|
||||
if len(__data) == 0:
|
||||
# Empty string
|
||||
data += b"\x00"
|
||||
else:
|
||||
# Non empty string
|
||||
data += b"\x0B"
|
||||
data += uleb128Encode(len(__data))
|
||||
data += str.encode(__data, "latin_1", "ignore")
|
||||
elif __dataType == dataTypes.uInt16:
|
||||
packType = "<H"
|
||||
elif __dataType == dataTypes.sInt16:
|
||||
packType = "<h"
|
||||
elif __dataType == dataTypes.uInt32:
|
||||
packType = "<L"
|
||||
elif __dataType == dataTypes.sInt32:
|
||||
packType = "<l"
|
||||
elif __dataType == dataTypes.uInt64:
|
||||
packType = "<Q"
|
||||
elif __dataType == dataTypes.sInt64:
|
||||
packType = "<q"
|
||||
elif __dataType == dataTypes.string:
|
||||
packType = "<s"
|
||||
elif __dataType == dataTypes.ffloat:
|
||||
packType = "<f"
|
||||
else:
|
||||
packType = "<B"
|
||||
|
||||
# Pack if needed
|
||||
if pack == True:
|
||||
data += struct.pack(packType, __data)
|
||||
|
||||
return data
|
||||
|
||||
# TODO: Wat dangerous
|
||||
def buildPacket(__packet, __packetData = []):
|
||||
"""
|
||||
Build a packet
|
||||
|
||||
packet -- packet id (int)
|
||||
packetData -- list [[data, dataType], [data, dataType], ...]
|
||||
|
||||
return -- packet bytes
|
||||
"""
|
||||
|
||||
# Set some variables
|
||||
packetData = bytes()
|
||||
packetLength = 0
|
||||
packetBytes = bytes()
|
||||
|
||||
# Pack packet data
|
||||
for i in __packetData:
|
||||
packetData += packData(i[0], i[1])
|
||||
|
||||
# Set packet length
|
||||
packetLength = len(packetData)
|
||||
|
||||
# Return packet as bytes
|
||||
packetBytes += struct.pack("<h", __packet) # packet id (int16)
|
||||
packetBytes += bytes(b"\x00") # unused byte
|
||||
packetBytes += struct.pack("<l", packetLength) # packet lenght (iint32)
|
||||
packetBytes += packetData # packet data
|
||||
return packetBytes
|
||||
|
||||
|
||||
def readPacketID(stream):
|
||||
"""
|
||||
Read packetID from stream (0-1 bytes)
|
||||
|
||||
stream -- data stream
|
||||
return -- packet ID (int)
|
||||
"""
|
||||
|
||||
return unpackData(stream[0:2], dataTypes.uInt16)
|
||||
|
||||
|
||||
def readPacketLength(stream):
|
||||
"""
|
||||
Read packet length from stream (3-4-5-6 bytes)
|
||||
|
||||
stream -- data stream
|
||||
return -- packet length (int)
|
||||
"""
|
||||
|
||||
return unpackData(stream[3:7], dataTypes.uInt32)
|
||||
|
||||
|
||||
def readPacketData(stream, structure = [], hasFirstBytes = True):
|
||||
"""
|
||||
Read packet data from stream according to structure
|
||||
|
||||
stream -- data stream
|
||||
structure -- [[name, dataType], [name, dataType], ...]
|
||||
hasFirstBytes -- if True, stream has packetID and length bytes.
|
||||
if False, stream has only packetData.
|
||||
Optional. Default: True
|
||||
return -- dictionary. key: name, value: read data
|
||||
"""
|
||||
|
||||
# Read packet ID (first 2 bytes)
|
||||
data = {}
|
||||
|
||||
# Skip packet ID and packet length if needed
|
||||
if hasFirstBytes == True:
|
||||
end = 7
|
||||
start = 7
|
||||
else:
|
||||
end = 0
|
||||
start = 0
|
||||
|
||||
# Read packet
|
||||
for i in structure:
|
||||
start = end
|
||||
unpack = True
|
||||
if i[1] == dataTypes.intList:
|
||||
# sInt32 list.
|
||||
# Unpack manually with for loop
|
||||
unpack = False
|
||||
|
||||
# Read length (uInt16)
|
||||
length = unpackData(stream[start:start+2], dataTypes.uInt16)
|
||||
|
||||
# Read all int inside list
|
||||
data[i[0]] = []
|
||||
for j in range(0,length):
|
||||
data[i[0]].append(unpackData(stream[start+2+(4*j):start+2+(4*(j+1))], dataTypes.sInt32))
|
||||
|
||||
# Update end
|
||||
end = start+2+(4*length)
|
||||
elif i[1] == dataTypes.string:
|
||||
# String, don't unpack
|
||||
unpack = False
|
||||
|
||||
# Check empty string
|
||||
if stream[start] == 0:
|
||||
# Empty string
|
||||
data[i[0]] = ""
|
||||
end = start+1
|
||||
else:
|
||||
# Non empty string
|
||||
# Read length and calculate end
|
||||
length = uleb128Decode(stream[start+1:])
|
||||
end = start+length[0]+length[1]+1
|
||||
|
||||
# Read bytes
|
||||
data[i[0]] = ''.join(chr(j) for j in stream[start+1+length[1]:end])
|
||||
elif i[1] == dataTypes.byte:
|
||||
end = start+1
|
||||
elif i[1] == dataTypes.uInt16 or i[1] == dataTypes.sInt16:
|
||||
end = start+2
|
||||
elif i[1] == dataTypes.uInt32 or i[1] == dataTypes.sInt32:
|
||||
end = start+4
|
||||
elif i[1] == dataTypes.uInt64 or i[1] == dataTypes.sInt64:
|
||||
end = start+8
|
||||
|
||||
# Unpack if needed
|
||||
if unpack == True:
|
||||
data[i[0]] = unpackData(stream[start:end], i[1])
|
||||
|
||||
return data
|
|
@ -1,268 +0,0 @@
|
|||
import struct
|
||||
from constants import dataTypes
|
||||
|
||||
cpdef bytearray uleb128Encode(int num):
|
||||
"""
|
||||
Encode an int to uleb128
|
||||
|
||||
:param num: int to encode
|
||||
:return: bytearray with encoded number
|
||||
"""
|
||||
cdef bytearray arr = bytearray()
|
||||
cdef int length = 0
|
||||
|
||||
if num == 0:
|
||||
return bytearray(b"\x00")
|
||||
|
||||
while num > 0:
|
||||
arr.append(num & 127)
|
||||
num >>= 7
|
||||
if num != 0:
|
||||
arr[length] |= 128
|
||||
length+=1
|
||||
|
||||
return arr
|
||||
|
||||
cpdef list uleb128Decode(bytes num):
|
||||
"""
|
||||
Decode a uleb128 to int
|
||||
|
||||
:param num: encoded uleb128 int
|
||||
:return: (total, length)
|
||||
"""
|
||||
cdef int shift = 0
|
||||
cdef list arr = [0,0] #total, length
|
||||
cdef int b
|
||||
|
||||
while True:
|
||||
b = num[arr[1]]
|
||||
arr[1]+=1
|
||||
arr[0] |= int(b & 127) << shift
|
||||
if b & 128 == 0:
|
||||
break
|
||||
shift += 7
|
||||
|
||||
return arr
|
||||
|
||||
cpdef unpackData(bytes data, int dataType):
|
||||
"""
|
||||
Unpacks a single section of a packet.
|
||||
|
||||
:param data: bytes to unpack
|
||||
:param dataType: data type
|
||||
:return: unpacked bytes
|
||||
"""
|
||||
# Get right pack Type
|
||||
if dataType == dataTypes.UINT16:
|
||||
unpackType = "<H"
|
||||
elif dataType == dataTypes.SINT16:
|
||||
unpackType = "<h"
|
||||
elif dataType == dataTypes.UINT32:
|
||||
unpackType = "<L"
|
||||
elif dataType == dataTypes.SINT32:
|
||||
unpackType = "<l"
|
||||
elif dataType == dataTypes.UINT64:
|
||||
unpackType = "<Q"
|
||||
elif dataType == dataTypes.SINT64:
|
||||
unpackType = "<q"
|
||||
elif dataType == dataTypes.STRING:
|
||||
unpackType = "<s"
|
||||
elif dataType == dataTypes.FFLOAT:
|
||||
unpackType = "<f"
|
||||
else:
|
||||
unpackType = "<B"
|
||||
|
||||
# Unpack
|
||||
return struct.unpack(unpackType, bytes(data))[0]
|
||||
|
||||
cpdef bytes packData(__data, int dataType):
|
||||
"""
|
||||
Packs a single section of a packet.
|
||||
|
||||
:param __data: data to pack
|
||||
:param dataType: data type
|
||||
:return: packed bytes
|
||||
"""
|
||||
cdef bytes data = bytes() # data to return
|
||||
cdef bint pack = True # if True, use pack. False only with strings
|
||||
cdef str packType
|
||||
|
||||
# Get right pack Type
|
||||
if dataType == dataTypes.BBYTES:
|
||||
# Bytes, do not use pack, do manually
|
||||
pack = False
|
||||
data = __data
|
||||
elif dataType == dataTypes.INT_LIST:
|
||||
# Pack manually
|
||||
pack = False
|
||||
# Add length
|
||||
data = packData(len(__data), dataTypes.UINT16)
|
||||
# Add all elements
|
||||
for i in __data:
|
||||
data += packData(i, dataTypes.SINT32)
|
||||
elif dataType == dataTypes.STRING:
|
||||
# String, do not use pack, do manually
|
||||
pack = False
|
||||
if len(__data) == 0:
|
||||
# Empty string
|
||||
data += b"\x00"
|
||||
else:
|
||||
# Non empty string
|
||||
data += b"\x0B"
|
||||
data += uleb128Encode(len(__data))
|
||||
data += str.encode(__data, "latin_1", "ignore")
|
||||
elif dataType == dataTypes.UINT16:
|
||||
packType = "<H"
|
||||
elif dataType == dataTypes.SINT16:
|
||||
packType = "<h"
|
||||
elif dataType == dataTypes.UINT32:
|
||||
packType = "<L"
|
||||
elif dataType == dataTypes.SINT32:
|
||||
packType = "<l"
|
||||
elif dataType == dataTypes.UINT64:
|
||||
packType = "<Q"
|
||||
elif dataType == dataTypes.SINT64:
|
||||
packType = "<q"
|
||||
elif dataType == dataTypes.STRING:
|
||||
packType = "<s"
|
||||
elif dataType == dataTypes.FFLOAT:
|
||||
packType = "<f"
|
||||
else:
|
||||
packType = "<B"
|
||||
|
||||
# Pack if needed
|
||||
if pack:
|
||||
data += struct.pack(packType, __data)
|
||||
|
||||
return data
|
||||
|
||||
cpdef bytes buildPacket(int __packet, list __packetData = None):
|
||||
"""
|
||||
Builds a packet
|
||||
|
||||
:param __packet: packet ID
|
||||
:param __packetData: packet structure [[data, dataType], [data, dataType], ...]
|
||||
:return: packet bytes
|
||||
"""
|
||||
# Default argument
|
||||
if __packetData is None:
|
||||
__packetData = []
|
||||
# Set some variables
|
||||
cdef bytes packetData = bytes()
|
||||
cdef int packetLength = 0
|
||||
cdef bytes packetBytes = bytes()
|
||||
|
||||
# Pack packet data
|
||||
cdef list i
|
||||
for i in __packetData:
|
||||
packetData += packData(i[0], i[1])
|
||||
|
||||
# Set packet length
|
||||
packetLength = len(packetData)
|
||||
|
||||
# Return packet as bytes
|
||||
packetBytes += struct.pack("<h", __packet) # packet id (int16)
|
||||
packetBytes += bytes(b"\x00") # unused byte
|
||||
packetBytes += struct.pack("<l", packetLength) # packet lenght (iint32)
|
||||
packetBytes += packetData # packet data
|
||||
return packetBytes
|
||||
|
||||
cpdef int readPacketID(bytes stream):
|
||||
"""
|
||||
Read packetID (first two bytes) from a packet
|
||||
|
||||
:param stream: packet bytes
|
||||
:return: packet ID
|
||||
"""
|
||||
return unpackData(stream[0:2], dataTypes.UINT16)
|
||||
|
||||
cpdef int readPacketLength(bytes stream):
|
||||
"""
|
||||
Read packet data length (3:7 bytes) from a packet
|
||||
|
||||
:param stream: packet bytes
|
||||
:return: packet data length
|
||||
"""
|
||||
return unpackData(stream[3:7], dataTypes.UINT32)
|
||||
|
||||
|
||||
cpdef readPacketData(bytes stream, list structure=None, bint hasFirstBytes = True):
|
||||
"""
|
||||
Read packet data from `stream` according to `structure`
|
||||
:param stream: packet bytes
|
||||
:param structure: packet structure: [[name, dataType], [name, dataType], ...]
|
||||
:param hasFirstBytes: if True, `stream` has packetID and length bytes.
|
||||
if False, `stream` has only packet data. Default: True
|
||||
:return: {name: unpackedValue, ...}
|
||||
"""
|
||||
# Default list argument
|
||||
if structure is None:
|
||||
structure = []
|
||||
|
||||
# Read packet ID (first 2 bytes)
|
||||
cdef dict data = {}
|
||||
|
||||
# Skip packet ID and packet length if needed
|
||||
cdef start, end
|
||||
if hasFirstBytes:
|
||||
end = 7
|
||||
start = 7
|
||||
else:
|
||||
end = 0
|
||||
start = 0
|
||||
|
||||
# Read packet
|
||||
cdef list i
|
||||
cdef bint unpack
|
||||
for i in structure:
|
||||
start = end
|
||||
unpack = True
|
||||
if i[1] == dataTypes.INT_LIST:
|
||||
# sInt32 list.
|
||||
# Unpack manually with for loop
|
||||
unpack = False
|
||||
|
||||
# Read length (uInt16)
|
||||
length = unpackData(stream[start:start+2], dataTypes.UINT16)
|
||||
|
||||
# Read all int inside list
|
||||
data[i[0]] = []
|
||||
for j in range(0,length):
|
||||
data[i[0]].append(unpackData(stream[start+2+(4*j):start+2+(4*(j+1))], dataTypes.SINT32))
|
||||
|
||||
# Update end
|
||||
end = start+2+(4*length)
|
||||
elif i[1] == dataTypes.STRING:
|
||||
# String, don't unpack
|
||||
unpack = False
|
||||
|
||||
# Check empty string
|
||||
if stream[start] == 0:
|
||||
# Empty string
|
||||
data[i[0]] = ""
|
||||
end = start+1
|
||||
else:
|
||||
# Non empty string
|
||||
# Read length and calculate end
|
||||
length = uleb128Decode(stream[start+1:])
|
||||
end = start+length[0]+length[1]+1
|
||||
|
||||
# Read bytes
|
||||
#data[i[0]] = ''.join(chr(j) for j in stream[start+1+length[1]:end])
|
||||
data[i[0]] = ""
|
||||
for j in stream[start+1+length[1]:end]:
|
||||
data[i[0]] += chr(j)
|
||||
elif i[1] == dataTypes.BYTE:
|
||||
end = start+1
|
||||
elif i[1] == dataTypes.UINT16 or i[1] == dataTypes.SINT16:
|
||||
end = start+2
|
||||
elif i[1] == dataTypes.UINT32 or i[1] == dataTypes.SINT32:
|
||||
end = start+4
|
||||
elif i[1] == dataTypes.UINT64 or i[1] == dataTypes.SINT64:
|
||||
end = start+8
|
||||
|
||||
# Unpack if needed
|
||||
if unpack:
|
||||
data[i[0]] = unpackData(stream[start:end], i[1])
|
||||
|
||||
return data
|
36
helpers/passwordHelper.py
Normal file
36
helpers/passwordHelper.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
from helpers import cryptHelper
|
||||
import base64
|
||||
import bcrypt
|
||||
|
||||
def checkOldPassword(password, salt, rightPassword):
|
||||
"""
|
||||
Check if password+salt corresponds to rightPassword
|
||||
|
||||
password -- input password
|
||||
salt -- password's salt
|
||||
rightPassword -- right password
|
||||
return -- bool
|
||||
"""
|
||||
|
||||
return (rightPassword == cryptHelper.crypt(password, "$2y$"+str(base64.b64decode(salt))))
|
||||
|
||||
def checkNewPassword(password, dbPassword):
|
||||
"""
|
||||
Check if a password (version 2) is right.
|
||||
|
||||
password -- input password
|
||||
dbPassword -- the password in the database
|
||||
return -- bool
|
||||
"""
|
||||
password = password.encode("utf8")
|
||||
dbPassword = dbPassword.encode("utf8")
|
||||
return bcrypt.hashpw(password, dbPassword) == dbPassword
|
||||
|
||||
def genBcrypt(password):
|
||||
"""
|
||||
Bcrypts a password.
|
||||
|
||||
password -- the password to hash.
|
||||
return -- bytestring
|
||||
"""
|
||||
return bcrypt.hashpw(password.encode("utf8"), bcrypt.gensalt(10, b'2a'))
|
89
helpers/requestHelper.py
Normal file
89
helpers/requestHelper.py
Normal file
|
@ -0,0 +1,89 @@
|
|||
import tornado
|
||||
import tornado.web
|
||||
import tornado.gen
|
||||
from tornado.ioloop import IOLoop
|
||||
from objects import glob
|
||||
from raven.contrib.tornado import SentryMixin
|
||||
from raven.contrib.tornado import AsyncSentryClient
|
||||
import gevent
|
||||
|
||||
class asyncRequestHandler(tornado.web.RequestHandler):
|
||||
"""
|
||||
Tornado asynchronous request handler
|
||||
create a class that extends this one (requestHelper.asyncRequestHandler)
|
||||
use asyncGet() and asyncPost() instad of get() and post().
|
||||
Done. I'm not kidding.
|
||||
"""
|
||||
@tornado.web.asynchronous
|
||||
@tornado.gen.engine
|
||||
def get(self, *args, **kwargs):
|
||||
try:
|
||||
yield tornado.gen.Task(runBackground, (self.asyncGet, tuple(args), dict(kwargs)))
|
||||
except Exception as e:
|
||||
yield tornado.gen.Task(self.captureException, exc_info=True)
|
||||
finally:
|
||||
if not self._finished:
|
||||
self.finish()
|
||||
|
||||
@tornado.web.asynchronous
|
||||
@tornado.gen.engine
|
||||
def post(self, *args, **kwargs):
|
||||
try:
|
||||
yield tornado.gen.Task(runBackground, (self.asyncPost, tuple(args), dict(kwargs)))
|
||||
except Exception as e:
|
||||
yield tornado.gen.Task(self.captureException, exc_info=True)
|
||||
finally:
|
||||
if not self._finished:
|
||||
self.finish()
|
||||
|
||||
def asyncGet(self, *args, **kwargs):
|
||||
self.send_error(405)
|
||||
self.finish()
|
||||
|
||||
def asyncPost(self, *args, **kwargs):
|
||||
self.send_error(405)
|
||||
self.finish()
|
||||
|
||||
def getRequestIP(self):
|
||||
realIP = self.request.headers.get("X-Forwarded-For") if glob.cloudflare == True else self.request.headers.get("X-Real-IP")
|
||||
if realIP != None:
|
||||
return realIP
|
||||
return self.request.remote_ip
|
||||
|
||||
|
||||
def runBackground(data, callback):
|
||||
"""
|
||||
Run a function in the background.
|
||||
Used to handle multiple requests at the same time
|
||||
"""
|
||||
func, args, kwargs = data
|
||||
def _callback(result):
|
||||
IOLoop.instance().add_callback(lambda: callback(result))
|
||||
#glob.pool.apply_async(func, args, kwargs, _callback)
|
||||
g = gevent.Greenlet(func, *args, **kwargs)
|
||||
g.link(_callback)
|
||||
g.start()
|
||||
|
||||
def checkArguments(arguments, requiredArguments):
|
||||
"""
|
||||
Check that every requiredArguments elements are in arguments
|
||||
|
||||
arguments -- full argument list, from tornado
|
||||
requiredArguments -- required arguments list es: ["u", "ha"]
|
||||
handler -- handler string name to print in exception. Optional
|
||||
return -- True if all arguments are passed, none if not
|
||||
"""
|
||||
for i in requiredArguments:
|
||||
if i not in arguments:
|
||||
return False
|
||||
return True
|
||||
|
||||
def printArguments(t):
|
||||
"""
|
||||
Print passed arguments, for debug purposes
|
||||
|
||||
t -- tornado object (self)
|
||||
"""
|
||||
print("ARGS::")
|
||||
for i in t.request.arguments:
|
||||
print ("{}={}".format(i, t.get_argument(i)))
|
|
@ -1,57 +1,41 @@
|
|||
import math
|
||||
from objects import glob
|
||||
from constants import serverPackets
|
||||
import psutil
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
import psutil
|
||||
|
||||
from common.constants import bcolors
|
||||
from common.log import logUtils as log
|
||||
from constants import serverPackets
|
||||
from helpers import consoleHelper
|
||||
from objects import glob
|
||||
|
||||
|
||||
def dispose():
|
||||
"""
|
||||
Perform some clean up. Called on shutdown.
|
||||
|
||||
:return:
|
||||
"""
|
||||
print("> Disposing server... ")
|
||||
glob.fileBuffers.flushAll()
|
||||
consoleHelper.printColored("Goodbye!", bcolors.GREEN)
|
||||
import signal
|
||||
from helpers import logHelper as log
|
||||
|
||||
def runningUnderUnix():
|
||||
"""
|
||||
Get if the server is running under UNIX or NT
|
||||
|
||||
:return: True if running under UNIX, otherwise False
|
||||
return --- True if running under UNIX, otherwise False
|
||||
"""
|
||||
|
||||
return True if os.name == "posix" else False
|
||||
|
||||
def scheduleShutdown(sendRestartTime, restart, message = "", delay=20):
|
||||
|
||||
def scheduleShutdown(sendRestartTime, restart, message = ""):
|
||||
"""
|
||||
Schedule a server shutdown/restart
|
||||
|
||||
:param sendRestartTime: time (seconds) to wait before sending server restart packets to every client
|
||||
:param restart: if True, server will restart. if False, server will shudown
|
||||
:param message: if set, send that message to every client to warn about the shutdown/restart
|
||||
:param delay: additional restart delay in seconds. Default: 20
|
||||
:return:
|
||||
sendRestartTime -- time (seconds) to wait before sending server restart packets to every client
|
||||
restart -- if True, server will restart. if False, server will shudown
|
||||
message -- if set, send that message to every client to warn about the shutdown/restart
|
||||
"""
|
||||
|
||||
# Console output
|
||||
log.info("Pep.py will {} in {} seconds!".format("restart" if restart else "shutdown", sendRestartTime+delay), "bunker")
|
||||
log.info("Pep.py will {} in {} seconds!".format("restart" if restart else "shutdown", sendRestartTime+20))
|
||||
log.info("Sending server restart packets in {} seconds...".format(sendRestartTime))
|
||||
|
||||
# Send notification if set
|
||||
if message != "":
|
||||
glob.streams.broadcast("main", serverPackets.notification(message))
|
||||
glob.tokens.enqueueAll(serverPackets.notification(message))
|
||||
|
||||
# Schedule server restart packet
|
||||
threading.Timer(sendRestartTime, glob.streams.broadcast, ["main", serverPackets.banchoRestart(delay*2*1000)]).start()
|
||||
threading.Timer(sendRestartTime, glob.tokens.enqueueAll, [serverPackets.banchoRestart(50000)]).start()
|
||||
glob.restarting = True
|
||||
|
||||
# Restart/shutdown
|
||||
|
@ -60,59 +44,44 @@ def scheduleShutdown(sendRestartTime, restart, message = "", delay=20):
|
|||
else:
|
||||
action = shutdownServer
|
||||
|
||||
# Schedule actual server shutdown/restart some seconds after server restart packet, so everyone gets it
|
||||
threading.Timer(sendRestartTime+delay, action).start()
|
||||
# Schedule actual server shutdown/restart 20 seconds after server restart packet, so everyone gets it
|
||||
threading.Timer(sendRestartTime+20, action).start()
|
||||
|
||||
|
||||
def restartServer():
|
||||
"""
|
||||
Restart pep.py
|
||||
|
||||
:return:
|
||||
"""
|
||||
"""Restart pep.py script"""
|
||||
log.info("Restarting pep.py...")
|
||||
dispose()
|
||||
os.execv(sys.executable, [sys.executable] + sys.argv)
|
||||
|
||||
def shutdownServer():
|
||||
"""
|
||||
Shutdown pep.py
|
||||
|
||||
:return:
|
||||
"""
|
||||
log.info("Shutting down pep.py...")
|
||||
dispose()
|
||||
def shutdownServer():
|
||||
"""Shutdown pep.py"""
|
||||
log.info("> Shutting down pep.py...")
|
||||
sig = signal.SIGKILL if runningUnderUnix() else signal.CTRL_C_EVENT
|
||||
os.kill(os.getpid(), sig)
|
||||
|
||||
|
||||
def getSystemInfo():
|
||||
"""
|
||||
Get a dictionary with some system/server info
|
||||
|
||||
:return: ["unix", "connectedUsers", "webServer", "cpuUsage", "totalMemory", "usedMemory", "loadAverage"]
|
||||
return -- ["unix", "connectedUsers", "webServer", "cpuUsage", "totalMemory", "usedMemory", "loadAverage"]
|
||||
"""
|
||||
data = {"unix": runningUnderUnix(), "connectedUsers": len(glob.tokens.tokens), "matches": len(glob.matches.matches)}
|
||||
|
||||
data = {}
|
||||
|
||||
# Get if server is running under unix/nt
|
||||
data["unix"] = runningUnderUnix()
|
||||
|
||||
# General stats
|
||||
delta = time.time()-glob.startTime
|
||||
days = math.floor(delta/86400)
|
||||
delta -= days*86400
|
||||
|
||||
hours = math.floor(delta/3600)
|
||||
delta -= hours*3600
|
||||
|
||||
minutes = math.floor(delta/60)
|
||||
delta -= minutes*60
|
||||
|
||||
seconds = math.floor(delta)
|
||||
|
||||
data["uptime"] = "{}d {}h {}m {}s".format(days, hours, minutes, seconds)
|
||||
data["connectedUsers"] = len(glob.tokens.tokens)
|
||||
data["matches"] = len(glob.matches.matches)
|
||||
data["cpuUsage"] = psutil.cpu_percent()
|
||||
memory = psutil.virtual_memory()
|
||||
data["totalMemory"] = "{0:.2f}".format(memory.total/1074000000)
|
||||
data["usedMemory"] = "{0:.2f}".format(memory.active/1074000000)
|
||||
data["totalMemory"] = "{0:.2f}".format(psutil.virtual_memory()[0]/1074000000)
|
||||
data["usedMemory"] = "{0:.2f}".format(psutil.virtual_memory()[3]/1074000000)
|
||||
|
||||
# Unix only stats
|
||||
if data["unix"]:
|
||||
if data["unix"] == True:
|
||||
data["loadAverage"] = os.getloadavg()
|
||||
else:
|
||||
data["loadAverage"] = (0,0,0)
|
||||
|
|
621
helpers/userHelper.py
Normal file
621
helpers/userHelper.py
Normal file
|
@ -0,0 +1,621 @@
|
|||
from helpers import passwordHelper
|
||||
from constants import gameModes
|
||||
from helpers import generalFunctions
|
||||
from objects import glob
|
||||
from helpers import logHelper as log
|
||||
import time
|
||||
from constants import privileges
|
||||
|
||||
def getID(username):
|
||||
"""
|
||||
Get username's user ID
|
||||
|
||||
db -- database connection
|
||||
username -- user
|
||||
return -- user id or False
|
||||
"""
|
||||
|
||||
# Get user ID from db
|
||||
userID = glob.db.fetch("SELECT id FROM users WHERE username = %s", [username])
|
||||
|
||||
# Make sure the query returned something
|
||||
if userID == None:
|
||||
return False
|
||||
|
||||
# Return user ID
|
||||
return userID["id"]
|
||||
|
||||
|
||||
def checkLogin(userID, password):
|
||||
"""
|
||||
Check userID's login with specified password
|
||||
|
||||
db -- database connection
|
||||
userID -- user id
|
||||
password -- plain md5 password
|
||||
return -- True or False
|
||||
"""
|
||||
|
||||
# Get password data
|
||||
passwordData = glob.db.fetch("SELECT password_md5, salt, password_version FROM users WHERE id = %s", [userID])
|
||||
|
||||
# Make sure the query returned something
|
||||
if passwordData == None:
|
||||
return False
|
||||
|
||||
|
||||
# Return valid/invalid based on the password version.
|
||||
if passwordData["password_version"] == 2:
|
||||
return passwordHelper.checkNewPassword(password, passwordData["password_md5"])
|
||||
if passwordData["password_version"] == 1:
|
||||
ok = passwordHelper.checkOldPassword(password, passwordData["salt"], passwordData["password_md5"])
|
||||
if not ok: return False
|
||||
newpass = passwordHelper.genBcrypt(password)
|
||||
glob.db.execute("UPDATE users SET password_md5=%s, salt='', password_version='2' WHERE id = %s", [newpass, userID])
|
||||
|
||||
|
||||
def exists(userID):
|
||||
"""
|
||||
Check if userID exists
|
||||
|
||||
userID -- user ID to check
|
||||
return -- bool
|
||||
"""
|
||||
|
||||
result = glob.db.fetch("SELECT id FROM users WHERE id = %s", [userID])
|
||||
if result == None:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def getSilenceEnd(userID):
|
||||
"""
|
||||
Get userID's **ABSOLUTE** silence end UNIX time
|
||||
Remember to subtract time.time() to get the actual silence time
|
||||
|
||||
userID -- userID
|
||||
return -- UNIX time
|
||||
"""
|
||||
|
||||
return glob.db.fetch("SELECT silence_end FROM users WHERE id = %s", [userID])["silence_end"]
|
||||
|
||||
|
||||
def silence(userID, seconds, silenceReason, author = 999):
|
||||
"""
|
||||
Silence someone
|
||||
|
||||
userID -- userID
|
||||
seconds -- silence length in seconds
|
||||
silenceReason -- Silence reason shown on website
|
||||
author -- userID of who silenced the user. Default: 999
|
||||
"""
|
||||
# db qurey
|
||||
silenceEndTime = int(time.time())+seconds
|
||||
glob.db.execute("UPDATE users SET silence_end = %s, silence_reason = %s WHERE id = %s", [silenceEndTime, silenceReason, userID])
|
||||
|
||||
# Loh
|
||||
targetUsername = getUsername(userID)
|
||||
# TODO: exists check im drunk rn i need to sleep (stampa piede ubriaco confirmed)
|
||||
if seconds > 0:
|
||||
log.rap(author, "has silenced {} for {} seconds for the following reason: \"{}\"".format(targetUsername, seconds, silenceReason), True)
|
||||
else:
|
||||
log.rap(author, "has removed {}'s silence".format(targetUsername), True)
|
||||
|
||||
def getRankedScore(userID, gameMode):
|
||||
"""
|
||||
Get userID's ranked score relative to gameMode
|
||||
|
||||
userID -- userID
|
||||
gameMode -- int value, see gameModes
|
||||
return -- ranked score
|
||||
"""
|
||||
|
||||
modeForDB = gameModes.getGameModeForDB(gameMode)
|
||||
return glob.db.fetch("SELECT ranked_score_"+modeForDB+" FROM users_stats WHERE id = %s", [userID])["ranked_score_"+modeForDB]
|
||||
|
||||
|
||||
def getTotalScore(userID, gameMode):
|
||||
"""
|
||||
Get userID's total score relative to gameMode
|
||||
|
||||
userID -- userID
|
||||
gameMode -- int value, see gameModes
|
||||
return -- total score
|
||||
"""
|
||||
|
||||
modeForDB = gameModes.getGameModeForDB(gameMode)
|
||||
return glob.db.fetch("SELECT total_score_"+modeForDB+" FROM users_stats WHERE id = %s", [userID])["total_score_"+modeForDB]
|
||||
|
||||
|
||||
def getAccuracy(userID, gameMode):
|
||||
"""
|
||||
Get userID's average accuracy relative to gameMode
|
||||
|
||||
userID -- userID
|
||||
gameMode -- int value, see gameModes
|
||||
return -- accuracy
|
||||
"""
|
||||
|
||||
modeForDB = gameModes.getGameModeForDB(gameMode)
|
||||
return glob.db.fetch("SELECT avg_accuracy_"+modeForDB+" FROM users_stats WHERE id = %s", [userID])["avg_accuracy_"+modeForDB]
|
||||
|
||||
|
||||
def getGameRank(userID, gameMode):
|
||||
"""
|
||||
Get userID's **in-game rank** (eg: #1337) relative to gameMode
|
||||
|
||||
userID -- userID
|
||||
gameMode -- int value, see gameModes
|
||||
return -- game rank
|
||||
"""
|
||||
|
||||
modeForDB = gameModes.getGameModeForDB(gameMode)
|
||||
result = glob.db.fetch("SELECT position FROM leaderboard_"+modeForDB+" WHERE user = %s", [userID])
|
||||
if result == None:
|
||||
return 0
|
||||
else:
|
||||
return result["position"]
|
||||
|
||||
|
||||
def getPlaycount(userID, gameMode):
|
||||
"""
|
||||
Get userID's playcount relative to gameMode
|
||||
|
||||
userID -- userID
|
||||
gameMode -- int value, see gameModes
|
||||
return -- playcount
|
||||
"""
|
||||
|
||||
modeForDB = gameModes.getGameModeForDB(gameMode)
|
||||
return glob.db.fetch("SELECT playcount_"+modeForDB+" FROM users_stats WHERE id = %s", [userID])["playcount_"+modeForDB]
|
||||
|
||||
|
||||
def getUsername(userID):
|
||||
"""
|
||||
Get userID's username
|
||||
|
||||
userID -- userID
|
||||
return -- username
|
||||
"""
|
||||
|
||||
return glob.db.fetch("SELECT username FROM users WHERE id = %s", [userID])["username"]
|
||||
|
||||
|
||||
def getFriendList(userID):
|
||||
"""
|
||||
Get userID's friendlist
|
||||
|
||||
userID -- userID
|
||||
return -- list with friends userIDs. [0] if no friends.
|
||||
"""
|
||||
|
||||
# Get friends from db
|
||||
friends = glob.db.fetchAll("SELECT user2 FROM users_relationships WHERE user1 = %s", [userID])
|
||||
|
||||
if friends == None or len(friends) == 0:
|
||||
# We have no friends, return 0 list
|
||||
return [0]
|
||||
else:
|
||||
# Get only friends
|
||||
friends = [i["user2"] for i in friends]
|
||||
|
||||
# Return friend IDs
|
||||
return friends
|
||||
|
||||
|
||||
def addFriend(userID, friendID):
|
||||
"""
|
||||
Add friendID to userID's friend list
|
||||
|
||||
userID -- user
|
||||
friendID -- new friend
|
||||
"""
|
||||
|
||||
# Make sure we aren't adding us to our friends
|
||||
if userID == friendID:
|
||||
return
|
||||
|
||||
# check user isn't already a friend of ours
|
||||
if glob.db.fetch("SELECT id FROM users_relationships WHERE user1 = %s AND user2 = %s", [userID, friendID]) != None:
|
||||
return
|
||||
|
||||
# Set new value
|
||||
glob.db.execute("INSERT INTO users_relationships (user1, user2) VALUES (%s, %s)", [userID, friendID])
|
||||
|
||||
|
||||
def removeFriend(userID, friendID):
|
||||
"""
|
||||
Remove friendID from userID's friend list
|
||||
|
||||
userID -- user
|
||||
friendID -- old friend
|
||||
"""
|
||||
|
||||
# Delete user relationship. We don't need to check if the relationship was there, because who gives a shit,
|
||||
# if they were not friends and they don't want to be anymore, be it. ¯\_(ツ)_/¯
|
||||
glob.db.execute("DELETE FROM users_relationships WHERE user1 = %s AND user2 = %s", [userID, friendID])
|
||||
|
||||
|
||||
def getCountry(userID):
|
||||
"""
|
||||
Get userID's country **(two letters)**.
|
||||
Use countryHelper.getCountryID with what that function returns
|
||||
to get osu! country ID relative to that user
|
||||
|
||||
userID -- user
|
||||
return -- country code (two letters)
|
||||
"""
|
||||
|
||||
return glob.db.fetch("SELECT country FROM users_stats WHERE id = %s", [userID])["country"]
|
||||
|
||||
def getPP(userID, gameMode):
|
||||
"""
|
||||
Get userID's PP relative to gameMode
|
||||
|
||||
userID -- user
|
||||
return -- PP
|
||||
"""
|
||||
|
||||
modeForDB = gameModes.getGameModeForDB(gameMode)
|
||||
return glob.db.fetch("SELECT pp_{} FROM users_stats WHERE id = %s".format(modeForDB), [userID])["pp_{}".format(modeForDB)]
|
||||
|
||||
def setCountry(userID, country):
|
||||
"""
|
||||
Set userID's country (two letters)
|
||||
|
||||
userID -- userID
|
||||
country -- country letters
|
||||
"""
|
||||
glob.db.execute("UPDATE users_stats SET country = %s WHERE id = %s", [country, userID])
|
||||
|
||||
def getShowCountry(userID):
|
||||
"""
|
||||
Get userID's show country status
|
||||
|
||||
userID -- userID
|
||||
return -- True if country is shown, False if it's hidden
|
||||
"""
|
||||
country = glob.db.fetch("SELECT show_country FROM users_stats WHERE id = %s", [userID])
|
||||
if country == None:
|
||||
return False
|
||||
return generalFunctions.stringToBool(country)
|
||||
|
||||
def logIP(userID, ip):
|
||||
"""
|
||||
User IP log
|
||||
USED FOR MULTIACCOUNT DETECTION
|
||||
"""
|
||||
glob.db.execute("""INSERT INTO ip_user (userid, ip, occurencies) VALUES (%s, %s, 1)
|
||||
ON DUPLICATE KEY UPDATE occurencies = occurencies + 1""", [userID, ip])
|
||||
|
||||
def saveBanchoSession(userID, ip):
|
||||
"""
|
||||
Save userid and ip of this token in bancho_sessions table.
|
||||
Used to cache logins on LETS requests
|
||||
"""
|
||||
log.debug("Saving bancho session ({}::{}) in db".format(userID, ip))
|
||||
glob.db.execute("INSERT INTO bancho_sessions (id, userid, ip) VALUES (NULL, %s, %s)", [userID, ip])
|
||||
|
||||
def deleteBanchoSessions(userID, ip):
|
||||
"""Delete this bancho session from DB"""
|
||||
log.debug("Deleting bancho session ({}::{}) from db".format(userID, ip))
|
||||
try:
|
||||
glob.db.execute("DELETE FROM bancho_sessions WHERE userid = %s AND ip = %s", [userID, ip])
|
||||
except:
|
||||
log.warning("Token for user: {} ip: {} doesn't exist".format(userID, ip))
|
||||
|
||||
def is2FAEnabled(userID):
|
||||
"""Returns True if 2FA is enable for this account"""
|
||||
result = glob.db.fetch("SELECT id FROM 2fa_telegram WHERE userid = %s LIMIT 1", [userID])
|
||||
return True if result is not None else False
|
||||
|
||||
def check2FA(userID, ip):
|
||||
"""Returns True if this IP is untrusted"""
|
||||
if is2FAEnabled(userID) == False:
|
||||
return False
|
||||
|
||||
result = glob.db.fetch("SELECT id FROM ip_user WHERE userid = %s AND ip = %s", [userID, ip])
|
||||
return True if result is None else False
|
||||
|
||||
def getUserStats(userID, gameMode):
|
||||
"""
|
||||
Get all user stats relative to gameMode with only two queries
|
||||
|
||||
userID --
|
||||
gameMode -- gameMode number
|
||||
return -- dictionary with results
|
||||
"""
|
||||
modeForDB = gameModes.getGameModeForDB(gameMode)
|
||||
|
||||
# Get stats
|
||||
stats = glob.db.fetch("""SELECT
|
||||
ranked_score_{gm} AS rankedScore,
|
||||
avg_accuracy_{gm} AS accuracy,
|
||||
playcount_{gm} AS playcount,
|
||||
total_score_{gm} AS totalScore,
|
||||
pp_{gm} AS pp
|
||||
FROM users_stats WHERE id = %s LIMIT 1""".format(gm=modeForDB), [userID])
|
||||
|
||||
# Get game rank
|
||||
result = glob.db.fetch("SELECT position FROM leaderboard_{} WHERE user = %s LIMIT 1".format(modeForDB), [userID])
|
||||
if result == None:
|
||||
stats["gameRank"] = 0
|
||||
else:
|
||||
stats["gameRank"] = result["position"]
|
||||
|
||||
# Return stats + game rank
|
||||
return stats
|
||||
|
||||
def isAllowed(userID):
|
||||
"""
|
||||
Check if userID is not banned or restricted
|
||||
|
||||
userID -- id of the user
|
||||
return -- True if not banned or restricted, otherwise false.
|
||||
"""
|
||||
result = glob.db.fetch("SELECT privileges FROM users WHERE id = %s", [userID])
|
||||
if result != None:
|
||||
return (result["privileges"] & privileges.USER_NORMAL) and (result["privileges"] & privileges.USER_PUBLIC)
|
||||
else:
|
||||
return False
|
||||
|
||||
def isRestricted(userID):
|
||||
"""
|
||||
Check if userID is restricted
|
||||
|
||||
userID -- id of the user
|
||||
return -- True if not restricted, otherwise false.
|
||||
"""
|
||||
result = glob.db.fetch("SELECT privileges FROM users WHERE id = %s", [userID])
|
||||
if result != None:
|
||||
return (result["privileges"] & privileges.USER_NORMAL) and not (result["privileges"] & privileges.USER_PUBLIC)
|
||||
else:
|
||||
return False
|
||||
|
||||
def isBanned(userID):
|
||||
"""
|
||||
Check if userID is banned
|
||||
|
||||
userID -- id of the user
|
||||
return -- True if not banned, otherwise false.
|
||||
"""
|
||||
result = glob.db.fetch("SELECT privileges FROM users WHERE id = %s", [userID])
|
||||
if result != None:
|
||||
return not (result["privileges"] & 3 > 0)
|
||||
else:
|
||||
return True
|
||||
|
||||
def ban(userID):
|
||||
"""
|
||||
Ban userID
|
||||
|
||||
userID -- id of user
|
||||
"""
|
||||
banDateTime = int(time.time())
|
||||
glob.db.execute("UPDATE users SET privileges = privileges & %s, ban_datetime = %s WHERE id = %s", [ ~(privileges.USER_NORMAL | privileges.USER_PUBLIC | privileges.USER_PENDING_VERIFICATION) , banDateTime, userID])
|
||||
|
||||
def unban(userID):
|
||||
"""
|
||||
Unban userID
|
||||
|
||||
userID -- id of user
|
||||
"""
|
||||
glob.db.execute("UPDATE users SET privileges = privileges | %s, ban_datetime = 0 WHERE id = %s", [ (privileges.USER_NORMAL | privileges.USER_PUBLIC) , userID])
|
||||
|
||||
def restrict(userID):
|
||||
"""
|
||||
Put userID in restricted mode
|
||||
|
||||
userID -- id of user
|
||||
"""
|
||||
banDateTime = int(time.time())
|
||||
glob.db.execute("UPDATE users SET privileges = privileges & %s, ban_datetime = %s WHERE id = %s", [~privileges.USER_PUBLIC, banDateTime, userID])
|
||||
|
||||
def unrestrict(userID):
|
||||
"""
|
||||
Remove restricted mode from userID.
|
||||
Same as unban().
|
||||
|
||||
userID -- id of user
|
||||
"""
|
||||
unban(userID)
|
||||
|
||||
def getPrivileges(userID):
|
||||
"""
|
||||
Return privileges for userID
|
||||
|
||||
userID -- id of user
|
||||
return -- privileges number
|
||||
"""
|
||||
result = glob.db.fetch("SELECT privileges FROM users WHERE id = %s", [userID])
|
||||
if result != None:
|
||||
return result["privileges"]
|
||||
else:
|
||||
return 0
|
||||
|
||||
def setPrivileges(userID, priv):
|
||||
"""
|
||||
Set userID's privileges in db
|
||||
|
||||
userID -- id of user
|
||||
priv -- privileges number
|
||||
"""
|
||||
glob.db.execute("UPDATE users SET privileges = %s WHERE id = %s", [priv, userID])
|
||||
|
||||
def isInPrivilegeGroup(userID, groupName):
|
||||
groupPrivileges = glob.db.fetch("SELECT privileges FROM privileges_groups WHERE name = %s", [groupName])
|
||||
if groupPrivileges == None:
|
||||
return False
|
||||
groupPrivileges = groupPrivileges["privileges"]
|
||||
userToken = glob.tokens.getTokenFromUserID(userID)
|
||||
if userToken != None:
|
||||
userPrivileges = userToken.privileges
|
||||
else:
|
||||
userPrivileges = getPrivileges(userID)
|
||||
return (userPrivileges == groupPrivileges) or (userPrivileges == (groupPrivileges | privileges.USER_DONOR))
|
||||
|
||||
|
||||
def appendNotes(userID, notes, addNl = True):
|
||||
"""
|
||||
Append "notes" to current userID's "notes for CM"
|
||||
|
||||
userID -- id of user
|
||||
notes -- text to append
|
||||
addNl -- if True, prepend \n to notes. Optional. Default: True.
|
||||
"""
|
||||
if addNl == True:
|
||||
notes = "\n"+notes
|
||||
glob.db.execute("UPDATE users SET notes=CONCAT(COALESCE(notes, ''),%s) WHERE id = %s", [notes, userID])
|
||||
|
||||
|
||||
def logHardware(userID, hashes, activation = False):
|
||||
"""
|
||||
Hardware log
|
||||
USED FOR MULTIACCOUNT DETECTION
|
||||
|
||||
Peppy's botnet (client data) structure (new line = "|", already split)
|
||||
[0] osu! version
|
||||
[1] plain mac addressed, separated by "."
|
||||
[2] mac addresses hash set
|
||||
[3] unique ID
|
||||
[4] disk ID
|
||||
|
||||
return -- True if hw is not banned, otherwise false
|
||||
"""
|
||||
# Make sure the strings are not empty
|
||||
for i in hashes[2:5]:
|
||||
if i == "":
|
||||
log.warning("Invalid hash set ({}) for user {} in HWID check".format(hashes, userID), "bunk")
|
||||
return False
|
||||
|
||||
# Run some HWID checks on that user if he is not restricted
|
||||
if isRestricted(userID) == False:
|
||||
# Get username
|
||||
username = getUsername(userID)
|
||||
|
||||
# Get the list of banned or restricted users that have logged in from this or similar HWID hash set
|
||||
banned = glob.db.fetchAll("""SELECT users.id as userid, hw_user.occurencies, users.username FROM hw_user
|
||||
LEFT JOIN users ON users.id = hw_user.userid
|
||||
WHERE hw_user.userid != %(userid)s
|
||||
AND (IF(%(mac)s!='b4ec3c4334a0249dae95c284ec5983df', hw_user.mac = %(mac)s, 1) AND hw_user.unique_id = %(uid)s AND hw_user.disk_id = %(diskid)s)
|
||||
AND (users.privileges & 3 != 3)""", {
|
||||
"userid": userID,
|
||||
"mac": hashes[2],
|
||||
"uid": hashes[3],
|
||||
"diskid": hashes[4],
|
||||
})
|
||||
|
||||
for i in banned:
|
||||
# Get the total numbers of logins
|
||||
total = glob.db.fetch("SELECT COUNT(*) AS count FROM hw_user WHERE userid = %s LIMIT 1", [userID])
|
||||
# and make sure it is valid
|
||||
if total == None:
|
||||
continue
|
||||
total = total["count"]
|
||||
|
||||
# Calculate 10% of total
|
||||
perc = (total*10)/100
|
||||
|
||||
if i["occurencies"] >= perc:
|
||||
# If the banned user has logged in more than 10% of the times from this user, restrict this user
|
||||
restrict(userID)
|
||||
appendNotes(userID, "-- Logged in from HWID ({hwid}) used more than 10% from user {banned} ({bannedUserID}), who is banned/restricted.".format(
|
||||
hwid=hashes[2:5],
|
||||
banned=i["username"],
|
||||
bannedUserID=i["userid"]
|
||||
))
|
||||
log.warning("**{user}** ({userID}) has been restricted because he has logged in from HWID _({hwid})_ used more than 10% from banned/restricted user **{banned}** ({bannedUserID}), **possible multiaccount**.".format(
|
||||
user=username,
|
||||
userID=userID,
|
||||
hwid=hashes[2:5],
|
||||
banned=i["username"],
|
||||
bannedUserID=i["userid"]
|
||||
), "cm")
|
||||
|
||||
# Update hash set occurencies
|
||||
glob.db.execute("""
|
||||
INSERT INTO hw_user (id, userid, mac, unique_id, disk_id, occurencies) VALUES (NULL, %s, %s, %s, %s, 1)
|
||||
ON DUPLICATE KEY UPDATE occurencies = occurencies + 1
|
||||
""", [userID, hashes[2], hashes[3], hashes[4]])
|
||||
|
||||
# Optionally, set this hash as 'used for activation'
|
||||
if activation == True:
|
||||
glob.db.execute("UPDATE hw_user SET activated = 1 WHERE userid = %s AND mac = %s AND unique_id = %s AND disk_id = %s", [userID, hashes[2], hashes[3], hashes[4]])
|
||||
|
||||
# Access granted, abbiamo impiegato 3 giorni
|
||||
# We grant access even in case of login from banned HWID
|
||||
# because we call restrict() above so there's no need to deny the access.
|
||||
return True
|
||||
|
||||
|
||||
def resetPendingFlag(userID, success=True):
|
||||
"""
|
||||
Remove pending flag from an user.
|
||||
|
||||
userID -- ID of the user
|
||||
success -- if True, set USER_PUBLIC and USER_NORMAL flags too
|
||||
"""
|
||||
glob.db.execute("UPDATE users SET privileges = privileges & %s WHERE id = %s LIMIT 1", [~privileges.USER_PENDING_VERIFICATION, userID])
|
||||
if success == True:
|
||||
glob.db.execute("UPDATE users SET privileges = privileges | %s WHERE id = %s LIMIT 1", [(privileges.USER_PUBLIC | privileges.USER_NORMAL), userID])
|
||||
|
||||
def verifyUser(userID, hashes):
|
||||
# Check for valid hash set
|
||||
for i in hashes[2:5]:
|
||||
if i == "":
|
||||
log.warning("Invalid hash set ({}) for user {} while verifying the account".format(str(hashes), userID), "bunk")
|
||||
return False
|
||||
|
||||
# Get username
|
||||
username = getUsername(userID)
|
||||
|
||||
# Make sure there are no other accounts activated with this exact mac/unique id/hwid
|
||||
match = glob.db.fetchAll("SELECT userid FROM hw_user WHERE (IF(%(mac)s != 'b4ec3c4334a0249dae95c284ec5983df', mac = %(mac)s, 1) AND unique_id = %(uid)s AND disk_id = %(diskid)s) AND userid != %(userid)s AND activated = 1 LIMIT 1", {
|
||||
"mac": hashes[2],
|
||||
"uid": hashes[3],
|
||||
"diskid": hashes[4],
|
||||
"userid": userID
|
||||
})
|
||||
|
||||
if match:
|
||||
# This is a multiaccount, restrict other account and ban this account
|
||||
|
||||
# Get original userID and username (lowest ID)
|
||||
originalUserID = match[0]["userid"]
|
||||
originalUsername = getUsername(originalUserID)
|
||||
|
||||
# Ban this user and append notes
|
||||
ban(userID) # this removes the USER_PENDING_VERIFICATION flag too
|
||||
appendNotes(userID, "-- {}'s multiaccount ({}), found HWID match while verifying account ({})".format(originalUsername, originalUserID, hashes[2:5]))
|
||||
appendNotes(originalUserID, "-- Has created multiaccount {} ({})".format(username, userID))
|
||||
|
||||
# Restrict the original
|
||||
restrict(originalUserID)
|
||||
|
||||
# Discord message
|
||||
log.warning("User **{originalUsername}** ({originalUserID}) has been restricted because he has created multiaccount **{username}** ({userID}). The multiaccount has been banned.".format(
|
||||
originalUsername=originalUsername,
|
||||
originalUserID=originalUserID,
|
||||
username=username,
|
||||
userID=userID
|
||||
), "cm")
|
||||
|
||||
# Disallow login
|
||||
return False
|
||||
else:
|
||||
# No matches found, set USER_PUBLIC and USER_NORMAL flags and reset USER_PENDING_VERIFICATION flag
|
||||
resetPendingFlag(userID)
|
||||
log.info("User **{}** ({}) has verified his account with hash set _{}_".format(username, userID, hashes[2:5]), "cm")
|
||||
|
||||
# Allow login
|
||||
return True
|
||||
|
||||
def hasVerifiedHardware(userID):
|
||||
"""
|
||||
userID -- id of the user
|
||||
return -- True if hwid activation data is in db, otherwise false
|
||||
"""
|
||||
data = glob.db.fetch("SELECT id FROM hw_user WHERE userid = %s AND activated = 1 LIMIT 1", [userID])
|
||||
if data != None:
|
||||
return True
|
||||
return False
|
295
irc/ircserver.py
295
irc/ircserver.py
|
@ -6,32 +6,32 @@ by Joel Rosdahl, licensed under the GNU GPL 2 License.
|
|||
Most of the reference code from miniircd was used for the low-level logic.
|
||||
The high-level code has been rewritten to make it compatible with pep.py.
|
||||
"""
|
||||
import hashlib
|
||||
import re
|
||||
import select
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
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
|
||||
import raven
|
||||
|
||||
from common.log import logUtils as log
|
||||
from common.ripple import userUtils
|
||||
from helpers import chatHelper as chat
|
||||
from objects import glob
|
||||
|
||||
|
||||
class Client:
|
||||
"""
|
||||
IRC Client object
|
||||
"""
|
||||
__linesep_regexp = re.compile(r"\r?\n")
|
||||
|
||||
|
||||
def __init__(self, server, sock):
|
||||
"""
|
||||
Initialize a Client object
|
||||
|
||||
:param server: server object
|
||||
:param sock: socket connection object
|
||||
:return:
|
||||
server -- server object
|
||||
sock -- socket connection object
|
||||
"""
|
||||
self.__timestamp = time.time()
|
||||
self.__readbuffer = ""
|
||||
|
@ -42,10 +42,8 @@ class Client:
|
|||
self.server = server
|
||||
self.socket = sock
|
||||
(self.ip, self.port) = sock.getpeername()
|
||||
self.IRCUsername = ""
|
||||
self.banchoUsername = ""
|
||||
self.username = ""
|
||||
self.supposedUsername = ""
|
||||
self.supposedUserID = 0
|
||||
self.joinedChannels = []
|
||||
|
||||
def messageChannel(self, channel, command, message, includeSelf=False):
|
||||
|
@ -59,8 +57,7 @@ class Client:
|
|||
Add a message (basic string) to client buffer.
|
||||
This is the lowest possible level.
|
||||
|
||||
:param msg: message to add
|
||||
:return:
|
||||
msg -- message to add
|
||||
"""
|
||||
self.__writebuffer += msg + "\r\n"
|
||||
|
||||
|
@ -69,7 +66,7 @@ class Client:
|
|||
"""
|
||||
Return this client's write buffer size
|
||||
|
||||
:return: write buffer size
|
||||
return -- write buffer size
|
||||
"""
|
||||
return len(self.__writebuffer)
|
||||
|
||||
|
@ -78,8 +75,7 @@ class Client:
|
|||
"""
|
||||
Add an IRC-like message to client buffer.
|
||||
|
||||
:param msg: message (without IRC stuff)
|
||||
:return:
|
||||
msg -- message (without IRC stuff)
|
||||
"""
|
||||
self.message(":{} {}".format(self.server.host, msg))
|
||||
|
||||
|
@ -88,14 +84,13 @@ class Client:
|
|||
"""
|
||||
Add an IRC-like message to client buffer with code
|
||||
|
||||
:param code: response code
|
||||
:param message: response message
|
||||
:param nickname: receiver nickname
|
||||
:param channel: optional
|
||||
:return:
|
||||
code -- response code
|
||||
message -- response message
|
||||
nickname -- receiver nickname
|
||||
channel -- optional
|
||||
"""
|
||||
if nickname == "":
|
||||
nickname = self.IRCUsername
|
||||
nickname = self.username
|
||||
if channel != "":
|
||||
channel = " "+channel
|
||||
self.reply("{code:03d} {nickname}{channel} :{message}".format(code=code, nickname=nickname, channel=channel, message=message))
|
||||
|
@ -105,8 +100,7 @@ class Client:
|
|||
"""
|
||||
Add a 403 reply (no such channel) to client buffer.
|
||||
|
||||
:param channel:
|
||||
:return:
|
||||
channel -- meh
|
||||
"""
|
||||
self.replyCode(403, "{} :No such channel".format(channel))
|
||||
|
||||
|
@ -115,8 +109,7 @@ class Client:
|
|||
"""
|
||||
Add a 461 reply (not enough parameters) to client buffer
|
||||
|
||||
:param command: name of the command that had not enough parameters
|
||||
:return:
|
||||
command -- command that had not enough parameters
|
||||
"""
|
||||
self.replyCode(403, "{} :Not enough parameters".format(command))
|
||||
|
||||
|
@ -125,9 +118,8 @@ class Client:
|
|||
"""
|
||||
Disconnects this client from the IRC server
|
||||
|
||||
:param quitmsg: IRC quit message. Default: 'Client quit'
|
||||
:param callLogout: if True, call logoutEvent on bancho
|
||||
:return:
|
||||
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))
|
||||
|
@ -138,16 +130,12 @@ class Client:
|
|||
self.server.removeClient(self, quitmsg)
|
||||
|
||||
# Bancho logout
|
||||
if callLogout and self.banchoUsername != "":
|
||||
chat.IRCDisconnect(self.IRCUsername)
|
||||
if callLogout == True:
|
||||
chat.IRCDisconnect(self.username)
|
||||
|
||||
|
||||
def readSocket(self):
|
||||
"""
|
||||
Read data coming from this client socket
|
||||
|
||||
:return:
|
||||
"""
|
||||
"""Read data coming from this client socket"""
|
||||
try:
|
||||
# Try to read incoming data from socket
|
||||
data = self.socket.recv(2 ** 10)
|
||||
|
@ -155,7 +143,7 @@ class Client:
|
|||
quitmsg = "EOT"
|
||||
except socket.error as x:
|
||||
# Error while reading data, this client will be disconnected
|
||||
data = bytes()
|
||||
data = ""
|
||||
quitmsg = x
|
||||
|
||||
if data:
|
||||
|
@ -170,11 +158,7 @@ class Client:
|
|||
|
||||
|
||||
def parseBuffer(self):
|
||||
"""
|
||||
Parse self.__readbuffer, get command, arguments and call its handler
|
||||
|
||||
:return:
|
||||
"""
|
||||
"""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]
|
||||
|
@ -211,25 +195,17 @@ class Client:
|
|||
|
||||
|
||||
def writeSocket(self):
|
||||
"""
|
||||
Write buffer to socket
|
||||
|
||||
:return:
|
||||
"""
|
||||
"""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(str(x))
|
||||
self.disconnect(x)
|
||||
|
||||
|
||||
def checkAlive(self):
|
||||
"""
|
||||
Check if this client is still connected.
|
||||
If the client is dead, disconnect it.
|
||||
|
||||
:return:
|
||||
"""
|
||||
"""Check if this client is still connected"""
|
||||
now = time.time()
|
||||
if self.__timestamp + 180 < now:
|
||||
self.disconnect("ping timeout")
|
||||
|
@ -245,19 +221,11 @@ class Client:
|
|||
|
||||
|
||||
def sendLusers(self):
|
||||
"""
|
||||
Send lusers response to this client
|
||||
|
||||
:return:
|
||||
"""
|
||||
"""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
|
||||
|
||||
:return:
|
||||
"""
|
||||
"""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")
|
||||
|
@ -282,10 +250,9 @@ class Client:
|
|||
m = hashlib.md5()
|
||||
m.update(arguments[0].encode("utf-8"))
|
||||
tokenHash = m.hexdigest()
|
||||
supposedUser = glob.db.fetch("SELECT users.username, users.id FROM users LEFT JOIN irc_tokens ON users.id = irc_tokens.userid WHERE irc_tokens.token = %s LIMIT 1", [tokenHash])
|
||||
if supposedUser:
|
||||
self.supposedUsername = chat.fixUsernameForIRC(supposedUser["username"])
|
||||
self.supposedUserID = supposedUser["id"]
|
||||
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
|
||||
|
@ -303,36 +270,29 @@ class Client:
|
|||
nick = arguments[0]
|
||||
|
||||
# Make sure this is the first time we set our nickname
|
||||
if self.IRCUsername != "":
|
||||
if self.username != "":
|
||||
self.reply("432 * %s :Erroneous nickname" % nick)
|
||||
return
|
||||
|
||||
# Make sure the IRC token was correct:
|
||||
# (self.supposedUsername is already fixed for IRC)
|
||||
if nick.lower() != self.supposedUsername.lower():
|
||||
self.reply("464 :Password incorrect")
|
||||
return
|
||||
|
||||
# Make sure that the user is not banned/restricted:
|
||||
if not userUtils.isAllowed(self.supposedUserID):
|
||||
self.reply("465 :You're banned")
|
||||
return
|
||||
|
||||
# Make sure we are not connected to Bancho
|
||||
token = glob.tokens.getTokenFromUsername(chat.fixUsernameForBancho(nick), True)
|
||||
if token is not None:
|
||||
token = glob.tokens.getTokenFromUsername(nick)
|
||||
if token != None:
|
||||
self.reply("433 * {} :Nickname is already in use".format(nick))
|
||||
return
|
||||
|
||||
# Everything seems fine, set username (nickname)
|
||||
self.IRCUsername = nick # username for IRC
|
||||
self.banchoUsername = chat.fixUsernameForBancho(self.IRCUsername) # username for bancho
|
||||
|
||||
# Disconnect other IRC clients from the same user
|
||||
# Make sure we are not already connected from IRC with that name
|
||||
for _, value in self.server.clients.items():
|
||||
if value.IRCUsername.lower() == self.IRCUsername.lower() and value != self:
|
||||
value.disconnect(quitmsg="Connected from another client")
|
||||
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
|
||||
|
@ -345,9 +305,9 @@ class Client:
|
|||
return
|
||||
|
||||
# If we now have a valid username, connect to bancho and send IRC welcome stuff
|
||||
if self.IRCUsername != "":
|
||||
if self.username != "":
|
||||
# Bancho connection
|
||||
chat.IRCConnect(self.banchoUsername)
|
||||
chat.IRCConnect(self.username)
|
||||
|
||||
# IRC reply
|
||||
self.replyCode(1, "Welcome to the Internet Relay Network")
|
||||
|
@ -358,30 +318,30 @@ class Client:
|
|||
self.sendMotd()
|
||||
self.__handleCommand = self.mainHandler
|
||||
|
||||
def quitHandler(self, _, arguments):
|
||||
def quitHandler(self, command, arguments):
|
||||
"""QUIT command handler"""
|
||||
self.disconnect(self.IRCUsername if len(arguments) < 1 else arguments[0])
|
||||
self.disconnect(self.username if len(arguments) < 1 else arguments[0])
|
||||
|
||||
def joinHandler(self, _, arguments):
|
||||
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.banchoUsername)
|
||||
if token is None:
|
||||
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'''
|
||||
return
|
||||
|
||||
# Get channels to join list
|
||||
channels = arguments[0].split(",")
|
||||
|
@ -394,13 +354,13 @@ class Client:
|
|||
continue
|
||||
|
||||
# Attempt to join the channel
|
||||
response = chat.IRCJoinChannel(self.banchoUsername, 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.IRCUsername), channel, True)
|
||||
self.messageChannel(channel, "{} JOIN".format(self.username), channel, True)
|
||||
|
||||
# Send channel description (topic)
|
||||
description = glob.channels.channels[channel].description
|
||||
|
@ -410,18 +370,16 @@ class Client:
|
|||
self.replyCode(332, description, channel=channel)
|
||||
|
||||
# Build connected users list
|
||||
if "chat/{}".format(channel) not in glob.streams.streams:
|
||||
self.reply403(channel)
|
||||
continue
|
||||
users = glob.streams.streams["chat/{}".format(channel)].clients
|
||||
users = glob.channels.channels[channel].getConnectedUsers()[:]
|
||||
usernames = []
|
||||
for user in users:
|
||||
if user not in glob.tokens.tokens:
|
||||
token = glob.tokens.getTokenFromUserID(user)
|
||||
if token == None:
|
||||
continue
|
||||
usernames.append(chat.fixUsernameForIRC(glob.tokens.tokens[user].username))
|
||||
usernames.append(token.username)
|
||||
usernames = " ".join(usernames)
|
||||
|
||||
# Send IRC users list
|
||||
# Send IRC users lis
|
||||
self.replyCode(353, usernames, channel="= {}".format(channel))
|
||||
self.replyCode(366, "End of NAMES list", channel=channel)
|
||||
elif response == 403:
|
||||
|
@ -429,15 +387,15 @@ class Client:
|
|||
self.reply403(channel)
|
||||
continue
|
||||
|
||||
def partHandler(self, _, arguments):
|
||||
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.banchoUsername)
|
||||
if token is None:
|
||||
token = glob.tokens.getTokenFromUsername(self.username)
|
||||
if token == None:
|
||||
return
|
||||
|
||||
# Get channels to part list
|
||||
|
@ -451,7 +409,7 @@ class Client:
|
|||
continue
|
||||
|
||||
# Attempt to part the channel
|
||||
response = chat.IRCPartChannel(self.banchoUsername, channel)
|
||||
response = chat.IRCPartChannel(self.username, channel)
|
||||
if response == 0:
|
||||
# No errors, remove channel from joinedChannels
|
||||
self.joinedChannels.remove(channel)
|
||||
|
@ -471,39 +429,36 @@ class Client:
|
|||
if len(arguments) == 1:
|
||||
self.replyCode(412, "No text to send")
|
||||
return
|
||||
recipientIRC = arguments[0]
|
||||
recipient = arguments[0]
|
||||
message = arguments[1]
|
||||
|
||||
# Send the message to bancho and reply
|
||||
if not recipientIRC.startswith("#"):
|
||||
recipientBancho = chat.fixUsernameForBancho(recipientIRC)
|
||||
else:
|
||||
recipientBancho = recipientIRC
|
||||
response = chat.sendMessage(self.banchoUsername, recipientBancho, message, toIRC=False)
|
||||
response = chat.sendMessage(self.username, recipient, message, toIRC=False)
|
||||
if response == 404:
|
||||
self.replyCode(404, "Cannot send to channel", channel=recipientIRC)
|
||||
self.replyCode(404, "Cannot send to channel", channel=recipient)
|
||||
return
|
||||
elif response == 403:
|
||||
self.replyCode(403, "No such channel", channel=recipientIRC)
|
||||
self.replyCode(403, "No such channel", channel=recipient)
|
||||
return
|
||||
elif response == 401:
|
||||
self.replyCode(401, "No such nick/channel", channel=recipientIRC)
|
||||
self.replyCode(401, "No such nick/channel", channel=recipient)
|
||||
return
|
||||
|
||||
# Send the message to IRC and bancho
|
||||
if recipientIRC.startswith("#"):
|
||||
if recipient.startswith("#"):
|
||||
# Public message (IRC)
|
||||
if recipientIRC not in glob.channels.channels:
|
||||
self.replyCode(401, "No such nick/channel", channel=recipientIRC)
|
||||
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 recipientIRC in value.joinedChannels and value != self:
|
||||
value.message(":{} PRIVMSG {} :{}".format(self.IRCUsername, recipientIRC, message))
|
||||
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.IRCUsername == recipientIRC:
|
||||
value.message(":{} PRIVMSG {} :{}".format(self.IRCUsername, recipientIRC, message))
|
||||
if value.username == recipient:
|
||||
value.message(":{} PRIVMSG {} :{}".format(self.username, recipient, message))
|
||||
|
||||
def motdHandler(self, command, arguments):
|
||||
"""MOTD command handler"""
|
||||
|
@ -513,7 +468,7 @@ class Client:
|
|||
"""LUSERS command handler"""
|
||||
self.sendLusers()
|
||||
|
||||
def pingHandler(self, _, arguments):
|
||||
def pingHandler(self, command, arguments):
|
||||
"""PING command handler"""
|
||||
if len(arguments) < 1:
|
||||
self.replyCode(409, "No origin specified")
|
||||
|
@ -524,17 +479,10 @@ class Client:
|
|||
"""(fake) PONG command handler"""
|
||||
pass
|
||||
|
||||
def awayHandler(self, _, arguments):
|
||||
"""AWAY command handler"""
|
||||
response = chat.IRCAway(self.banchoUsername, " ".join(arguments))
|
||||
self.replyCode(response, "You are no longer marked as being away" if response == 305 else "You have been marked as being away")
|
||||
|
||||
def mainHandler(self, command, arguments):
|
||||
"""
|
||||
Handler for post-login commands
|
||||
"""
|
||||
"""Handler for post-login commands"""
|
||||
handlers = {
|
||||
"AWAY": self.awayHandler,
|
||||
#"AWAY": away_handler,
|
||||
#"ISON": ison_handler,
|
||||
"JOIN": self.joinHandler,
|
||||
#"LIST": list_handler,
|
||||
|
@ -560,35 +508,36 @@ class Client:
|
|||
self.replyCode(421, "Unknown command ({})".format(command))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Server:
|
||||
def __init__(self, port):
|
||||
self.host = glob.conf.config["irc"]["hostname"]
|
||||
self.host = socket.getfqdn("127.0.0.1")[:63]
|
||||
self.port = port
|
||||
self.clients = {} # Socket - - > Client instance.
|
||||
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, isBanchoUsername=True):
|
||||
def forceDisconnection(self, username):
|
||||
"""
|
||||
Disconnect someone from IRC if connected
|
||||
|
||||
:param username: victim
|
||||
:param isBanchoUsername: if True, username is a bancho username, else convert it to a bancho username
|
||||
:return:
|
||||
username -- victim
|
||||
"""
|
||||
for _, value in self.clients.items():
|
||||
if (value.IRCUsername == username and not isBanchoUsername) or (value.banchoUsername == username and isBanchoUsername):
|
||||
if value.username == username:
|
||||
value.disconnect(callLogout=False)
|
||||
break # or dictionary changes size during iteration
|
||||
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
|
||||
|
||||
:param username: username of bancho user
|
||||
:param channel: joined channel name
|
||||
:return:
|
||||
username -- username of bancho user
|
||||
channel -- joined channel name
|
||||
"""
|
||||
username = chat.fixUsernameForIRC(username)
|
||||
for _, value in self.clients.items():
|
||||
if channel in value.joinedChannels:
|
||||
value.message(":{} JOIN {}".format(username, channel))
|
||||
|
@ -597,11 +546,9 @@ class Server:
|
|||
"""
|
||||
Let every IRC client connected to a specific client know that 'username' parted the channel from bancho
|
||||
|
||||
:param username: username of bancho user
|
||||
:param channel: joined channel name
|
||||
:return:
|
||||
username -- username of bancho user
|
||||
channel -- joined channel name
|
||||
"""
|
||||
username = chat.fixUsernameForIRC(username)
|
||||
for _, value in self.clients.items():
|
||||
if channel in value.joinedChannels:
|
||||
value.message(":{} PART {}".format(username, channel))
|
||||
|
@ -610,45 +557,37 @@ class Server:
|
|||
"""
|
||||
Send a message to IRC when someone sends it from bancho
|
||||
|
||||
:param fro: sender username
|
||||
:param to: receiver username
|
||||
:param message: text of the message
|
||||
:return:
|
||||
fro -- sender username
|
||||
to -- receiver username
|
||||
message -- text of the message
|
||||
"""
|
||||
fro = chat.fixUsernameForIRC(fro)
|
||||
to = chat.fixUsernameForIRC(to)
|
||||
if to.startswith("#"):
|
||||
# Public message
|
||||
for _, value in self.clients.items():
|
||||
if to in value.joinedChannels and value.IRCUsername != fro:
|
||||
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.IRCUsername == to and value.IRCUsername != fro:
|
||||
if value.username == to and value.username != fro:
|
||||
value.message(":{} PRIVMSG {} :{}".format(fro, to, message))
|
||||
|
||||
|
||||
def removeClient(self, client, _):
|
||||
def removeClient(self, client, quitmsg):
|
||||
"""
|
||||
Remove a client from connected clients
|
||||
|
||||
:param client: client object
|
||||
:return:
|
||||
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
|
||||
|
||||
:return:
|
||||
"""
|
||||
"""Start IRC server main loop"""
|
||||
# Sentry
|
||||
sentryClient = None
|
||||
if glob.sentry:
|
||||
sentryClient = raven.Client(glob.conf.config["sentry"]["ircdsn"])
|
||||
if glob.sentry == True:
|
||||
sentryClient = raven.Client(glob.conf.config["sentry"]["ircdns"])
|
||||
|
||||
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
|
@ -668,7 +607,7 @@ class Server:
|
|||
[x.socket for x in self.clients.values()
|
||||
if x.writeBufferSize() > 0],
|
||||
[],
|
||||
1)
|
||||
2)
|
||||
|
||||
# Handle incoming connections
|
||||
for x in iwtd:
|
||||
|
@ -679,7 +618,7 @@ class Server:
|
|||
try:
|
||||
self.clients[conn] = Client(self, conn)
|
||||
log.info("[IRC] Accepted connection from {}:{}".format(addr[0], addr[1]))
|
||||
except socket.error:
|
||||
except socket.error as e:
|
||||
try:
|
||||
conn.close()
|
||||
except:
|
||||
|
@ -698,15 +637,9 @@ class Server:
|
|||
lastAliveCheck = now
|
||||
except:
|
||||
log.error("[IRC] Unknown error!\n```\n{}\n{}```".format(sys.exc_info(), traceback.format_exc()))
|
||||
if glob.sentry and sentryClient is not None:
|
||||
if glob.sentry == True:
|
||||
sentryClient.captureException()
|
||||
|
||||
def main(port=6667):
|
||||
"""
|
||||
Create and start an IRC server
|
||||
|
||||
:param port: IRC port. Default: 6667
|
||||
:return:
|
||||
"""
|
||||
glob.ircServer = Server(port)
|
||||
glob.ircServer.start()
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
# TODO: Rewrite this shit
|
||||
from common import generalUtils
|
||||
from constants import serverPackets
|
||||
from objects import glob
|
||||
from common.log import logUtils as log
|
||||
|
||||
from helpers import generalFunctions
|
||||
|
||||
class banchoConfig:
|
||||
"""
|
||||
|
@ -12,56 +8,35 @@ class banchoConfig:
|
|||
|
||||
config = {"banchoMaintenance": False, "freeDirect": True, "menuIcon": "", "loginNotification": ""}
|
||||
|
||||
def __init__(self, loadFromDB = True):
|
||||
def __init__(self, __loadFromDB = True):
|
||||
"""
|
||||
Initialize a banchoConfig object (and load bancho_settings from db)
|
||||
|
||||
loadFromDB -- if True, load values from db. If False, don't load values. Optional.
|
||||
[__loadFromDB -- if True, load values from db. If False, don't load values. Default: True]
|
||||
"""
|
||||
if loadFromDB:
|
||||
|
||||
if __loadFromDB:
|
||||
try:
|
||||
self.loadSettings()
|
||||
except:
|
||||
raise
|
||||
|
||||
|
||||
def loadSettings(self):
|
||||
"""
|
||||
(re)load bancho_settings from DB and set values in config array
|
||||
"""
|
||||
self.config["banchoMaintenance"] = generalUtils.stringToBool(glob.db.fetch("SELECT value_int FROM bancho_settings WHERE name = 'bancho_maintenance'")["value_int"])
|
||||
self.config["freeDirect"] = generalUtils.stringToBool(glob.db.fetch("SELECT value_int FROM bancho_settings WHERE name = 'free_direct'")["value_int"])
|
||||
mainMenuIcon = glob.db.fetch("SELECT file_id, url FROM main_menu_icons WHERE is_current = 1 LIMIT 1")
|
||||
if mainMenuIcon is None:
|
||||
self.config["menuIcon"] = ""
|
||||
else:
|
||||
imageURL = "https://i.ppy.sh/{}.png".format(mainMenuIcon["file_id"])
|
||||
self.config["menuIcon"] = "{}|{}".format(imageURL, mainMenuIcon["url"])
|
||||
|
||||
self.config["banchoMaintenance"] = generalFunctions.stringToBool(glob.db.fetch("SELECT value_int FROM bancho_settings WHERE name = 'bancho_maintenance'")["value_int"])
|
||||
self.config["freeDirect"] = generalFunctions.stringToBool(glob.db.fetch("SELECT value_int FROM bancho_settings WHERE name = 'free_direct'")["value_int"])
|
||||
self.config["menuIcon"] = glob.db.fetch("SELECT value_string FROM bancho_settings WHERE name = 'menu_icon'")["value_string"]
|
||||
self.config["loginNotification"] = glob.db.fetch("SELECT value_string FROM bancho_settings WHERE name = 'login_notification'")["value_string"]
|
||||
|
||||
|
||||
def setMaintenance(self, maintenance):
|
||||
def setMaintenance(self, __maintenance):
|
||||
"""
|
||||
Turn on/off bancho maintenance mode. Write new value to db too
|
||||
|
||||
maintenance -- if True, turn on maintenance mode. If false, turn it off
|
||||
__maintenance -- if True, turn on maintenance mode. If false, turn it off
|
||||
"""
|
||||
self.config["banchoMaintenance"] = maintenance
|
||||
glob.db.execute("UPDATE bancho_settings SET value_int = %s WHERE name = 'bancho_maintenance'", [int(maintenance)])
|
||||
|
||||
def reload(self):
|
||||
# Reload settings from bancho_settings
|
||||
glob.banchoConf.loadSettings()
|
||||
|
||||
# Reload channels too
|
||||
glob.channels.loadChannels()
|
||||
|
||||
# And chat filters
|
||||
glob.chatFilters.loadFilters()
|
||||
|
||||
# Send new channels and new bottom icon to everyone
|
||||
glob.streams.broadcast("main", serverPackets.mainMenuIcon(glob.banchoConf.config["menuIcon"]))
|
||||
glob.streams.broadcast("main", serverPackets.channelInfoEnd())
|
||||
for key, value in glob.channels.channels.items():
|
||||
if value.publicRead and not value.hidden:
|
||||
glob.streams.broadcast("main", serverPackets.channelInfo(key))
|
||||
self.config["banchoMaintenance"] = __maintenance
|
||||
glob.db.execute("UPDATE bancho_settings SET value_int = %s WHERE name = 'bancho_maintenance'", [int(__maintenance)])
|
||||
|
|
|
@ -1,44 +1,79 @@
|
|||
import logging
|
||||
|
||||
from constants import exceptions
|
||||
from objects import glob
|
||||
|
||||
class channel:
|
||||
def __init__(self, name, description, publicRead, publicWrite, temp, hidden):
|
||||
"""
|
||||
A chat channel
|
||||
"""
|
||||
|
||||
def __init__(self, __name, __description, __publicRead, __publicWrite, temp, hidden):
|
||||
"""
|
||||
Create a new chat channel object
|
||||
|
||||
: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
|
||||
__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
|
||||
"""
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.publicRead = publicRead
|
||||
self.publicWrite = publicWrite
|
||||
|
||||
self.name = __name
|
||||
self.description = __description
|
||||
self.publicRead = __publicRead
|
||||
self.publicWrite = __publicWrite
|
||||
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)
|
||||
self.hidden = hidden
|
||||
|
||||
# Make Foka join the channel
|
||||
fokaToken = glob.tokens.getTokenFromUserID(999)
|
||||
if fokaToken is not None:
|
||||
try:
|
||||
fokaToken.joinChannel(self)
|
||||
except exceptions.userAlreadyInChannelException:
|
||||
logging.warning("FokaBot has already joined channel {}".format(self.name))
|
||||
|
||||
@property
|
||||
def isSpecial(self):
|
||||
return any(self.name.startswith(x) for x in ("#spect_", "#multi_"))
|
||||
|
||||
@property
|
||||
def clientName(self):
|
||||
# Client name (#spectator/#multiplayer)
|
||||
self.clientName = self.name
|
||||
if self.name.startswith("#spect_"):
|
||||
return "#spectator"
|
||||
self.clientName = "#spectator"
|
||||
elif self.name.startswith("#multi_"):
|
||||
return "#multiplayer"
|
||||
return self.name
|
||||
self.clientName = "#multiplayer"
|
||||
|
||||
|
||||
def userJoin(self, __userID):
|
||||
"""
|
||||
Add a user to connected users
|
||||
|
||||
__userID -- user ID that joined the channel
|
||||
"""
|
||||
|
||||
if __userID not in self.connectedUsers:
|
||||
self.connectedUsers.append(__userID)
|
||||
|
||||
|
||||
def userPart(self, __userID):
|
||||
"""
|
||||
Remove a user from connected users
|
||||
|
||||
__userID -- user ID that left the channel
|
||||
"""
|
||||
|
||||
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):
|
||||
"""
|
||||
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)
|
||||
|
|
|
@ -1,18 +1,22 @@
|
|||
from common.log import logUtils as log
|
||||
from objects import channel
|
||||
from objects import glob
|
||||
from helpers import chatHelper as chat
|
||||
|
||||
from objects import channel
|
||||
from helpers import logHelper as log
|
||||
|
||||
class channelList:
|
||||
def __init__(self):
|
||||
self.channels = {}
|
||||
"""
|
||||
Channel list
|
||||
|
||||
channels -- dictionary. key: channel name, value: channel object
|
||||
"""
|
||||
|
||||
channels = {}
|
||||
|
||||
|
||||
def loadChannels(self):
|
||||
"""
|
||||
Load chat channels from db and add them to channels list
|
||||
:return:
|
||||
Load chat channels from db and add them to channels dictionary
|
||||
"""
|
||||
|
||||
# Get channels from DB
|
||||
channels = glob.db.fetchAll("SELECT * FROM bancho_channels")
|
||||
|
||||
|
@ -23,67 +27,43 @@ class channelList:
|
|||
publicWrite = True if i["public_write"] == 1 else False
|
||||
self.addChannel(i["name"], i["description"], publicRead, publicWrite)
|
||||
|
||||
|
||||
def addChannel(self, name, description, publicRead, publicWrite, temp = False, hidden = False):
|
||||
"""
|
||||
Add a channel to channels list
|
||||
Add a channel object to channels dictionary
|
||||
|
||||
: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:
|
||||
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.
|
||||
"""
|
||||
glob.streams.add("chat/{}".format(name))
|
||||
self.channels[name] = channel.channel(name, description, publicRead, publicWrite, temp, hidden)
|
||||
log.info("Created channel {}".format(name))
|
||||
|
||||
|
||||
def addTempChannel(self, name):
|
||||
"""
|
||||
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
|
||||
|
||||
:param name: channel name
|
||||
:return: True if the channel was created, otherwise False
|
||||
name -- channel name
|
||||
return -- True if channel was created, False if failed
|
||||
"""
|
||||
if name in self.channels:
|
||||
return False
|
||||
glob.streams.add("chat/{}".format(name))
|
||||
self.channels[name] = channel.channel(name, "Chat", True, True, True, True)
|
||||
log.info("Created temp channel {}".format(name))
|
||||
|
||||
def addHiddenChannel(self, name):
|
||||
"""
|
||||
Add a hidden channel. It's like a normal channel and must be deleted manually,
|
||||
but it's not shown in channels list.
|
||||
|
||||
:param name: channel name
|
||||
:return: True if the channel was created, otherwise False
|
||||
"""
|
||||
if name in self.channels:
|
||||
return False
|
||||
glob.streams.add("chat/{}".format(name))
|
||||
self.channels[name] = channel.channel(name, "Chat", True, True, False, True)
|
||||
log.info("Created hidden channel {}".format(name))
|
||||
|
||||
def removeChannel(self, name):
|
||||
"""
|
||||
Removes a channel from channels list
|
||||
|
||||
:param name: channel name
|
||||
:return:
|
||||
name -- channel name
|
||||
"""
|
||||
if name not in self.channels:
|
||||
log.debug("{} is not in channels list".format(name))
|
||||
return
|
||||
#glob.streams.broadcast("chat/{}".format(name), serverPackets.channelKicked(name))
|
||||
stream = glob.streams.getStream("chat/{}".format(name))
|
||||
if stream is not None:
|
||||
for token in stream.clients:
|
||||
if token in glob.tokens.tokens:
|
||||
chat.partChannel(channel=name, token=glob.tokens.tokens[token], kick=True)
|
||||
glob.streams.dispose("chat/{}".format(name))
|
||||
glob.streams.remove("chat/{}".format(name))
|
||||
self.channels.pop(name)
|
||||
log.info("Removed channel {}".format(name))
|
||||
|
|
|
@ -1,20 +1,9 @@
|
|||
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 = {}
|
||||
|
||||
|
@ -27,28 +16,17 @@ class chatFilters:
|
|||
for line in data:
|
||||
# Get old/new word and save it in dictionary
|
||||
lineSplit = line.split("=")
|
||||
self.filters[lineSplit[0].lower()] = lineSplit[1].replace("\n", "")
|
||||
self.filters[lineSplit[0]] = lineSplit[1].replace("\n", "")
|
||||
|
||||
def filterMessage(self, message):
|
||||
"""
|
||||
Replace forbidden words with filtered ones
|
||||
|
||||
:param message: normal message
|
||||
:return: filtered message
|
||||
"""
|
||||
return message
|
||||
"""
|
||||
# Split words by spaces
|
||||
messageTemp = message.split(" ")
|
||||
|
||||
# Check each word
|
||||
for word in messageTemp:
|
||||
lowerWord = word.lower()
|
||||
|
||||
# If the word is filtered, replace it
|
||||
if lowerWord in self.filters:
|
||||
message = message.replace(word, self.filters[lowerWord])
|
||||
if word in self.filters:
|
||||
message = message.replace(word, self.filters[word])
|
||||
|
||||
# Return filtered message
|
||||
return message
|
||||
"""
|
||||
|
|
20
objects/fileLocks.py
Normal file
20
objects/fileLocks.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
import threading
|
||||
|
||||
class fileLocks:
|
||||
def __init__(self):
|
||||
# Dictionary containing threading.Lock s
|
||||
self.locks = {}
|
||||
|
||||
def lockFile(self, fileName):
|
||||
if fileName in self.locks:
|
||||
# Acquire existing lock
|
||||
self.locks[fileName].acquire()
|
||||
else:
|
||||
# Create new lock and acquire it
|
||||
self.locks[fileName] = threading.Lock()
|
||||
self.locks[fileName].acquire()
|
||||
|
||||
def unlockFile(self, fileName):
|
||||
if fileName in self.locks:
|
||||
# Release lock if it exists
|
||||
self.locks[fileName].release()
|
|
@ -1,54 +1,55 @@
|
|||
"""FokaBot related functions"""
|
||||
import re
|
||||
|
||||
from common import generalUtils
|
||||
from common.constants import actions
|
||||
from common.ripple import userUtils
|
||||
from constants import fokabotCommands
|
||||
from constants import serverPackets
|
||||
from helpers import userHelper
|
||||
from objects import glob
|
||||
from constants import actions
|
||||
from constants import serverPackets
|
||||
from constants import fokabotCommands
|
||||
import re
|
||||
from helpers import generalFunctions
|
||||
|
||||
# Tillerino np regex, compiled only once to increase performance
|
||||
npRegex = re.compile("^https?:\\/\\/osu\\.ppy\\.sh\\/b\\/(\\d*)")
|
||||
|
||||
def connect():
|
||||
"""
|
||||
Connect FokaBot to Bancho
|
||||
"""Add FokaBot to connected users and send userpanel/stats packet to everyone"""
|
||||
|
||||
:return:
|
||||
"""
|
||||
glob.BOT_NAME = userUtils.getUsername(999)
|
||||
token = glob.tokens.addToken(999)
|
||||
token.actionID = actions.IDLE
|
||||
glob.streams.broadcast("main", serverPackets.userPanel(999))
|
||||
glob.streams.broadcast("main", serverPackets.userStats(999))
|
||||
token.actionID = actions.idle
|
||||
glob.tokens.enqueueAll(serverPackets.userPanel(999))
|
||||
####glob.tokens.enqueueAll(serverPackets.userStats(999))
|
||||
|
||||
# NOTE: Debug thing to set all users as connected
|
||||
#users = glob.db.fetchAll("SELECT id FROM users")
|
||||
#for i in users:
|
||||
# t = glob.tokens.addToken(i["id"])
|
||||
# t.actionID = actions.idle
|
||||
|
||||
def disconnect():
|
||||
"""
|
||||
Disconnect FokaBot from Bancho
|
||||
"""Remove FokaBot from connected users"""
|
||||
|
||||
:return:
|
||||
"""
|
||||
glob.tokens.deleteToken(glob.tokens.getTokenFromUserID(999))
|
||||
|
||||
def fokabotResponse(fro, chan, message):
|
||||
"""
|
||||
Check if a message has triggered FokaBot
|
||||
Check if a message has triggered fokabot (and return its response)
|
||||
|
||||
:param fro: sender username
|
||||
:param chan: channel name (or receiver username)
|
||||
:param message: chat mesage
|
||||
:return: FokaBot's response or False if no response
|
||||
fro -- sender username (for permissions stuff with admin commands)
|
||||
chan -- channel name
|
||||
message -- message
|
||||
|
||||
return -- fokabot's response string or False
|
||||
"""
|
||||
|
||||
for i in fokabotCommands.commands:
|
||||
# Loop though all commands
|
||||
if re.compile("^{}( (.+)?)?$".format(i["trigger"])).match(message.strip()):
|
||||
#if i["trigger"] in message:
|
||||
if generalFunctions.strContains(message, i["trigger"]):
|
||||
# message has triggered a command
|
||||
|
||||
# Make sure the user has right permissions
|
||||
if i["privileges"] is not None:
|
||||
if i["privileges"] != None:
|
||||
# Rank = x
|
||||
if userUtils.getPrivileges(userUtils.getID(fro)) & i["privileges"] == 0:
|
||||
if userHelper.getPrivileges(userHelper.getID(fro)) & i["privileges"] == 0:
|
||||
return False
|
||||
|
||||
# Check argument number
|
||||
|
@ -57,7 +58,7 @@ def fokabotResponse(fro, chan, message):
|
|||
return "Wrong syntax: {} {}".format(i["trigger"], i["syntax"])
|
||||
|
||||
# Return response or execute callback
|
||||
if i["callback"] is None:
|
||||
if i["callback"] == None:
|
||||
return i["response"]
|
||||
else:
|
||||
return i["callback"](fro, chan, message[1:])
|
||||
|
|
|
@ -1,59 +1,34 @@
|
|||
"""Global objects and variables"""
|
||||
|
||||
import time
|
||||
from common.ddog import datadogClient
|
||||
from common.files import fileBuffer, fileLocks
|
||||
from objects import tokenList
|
||||
from objects import channelList
|
||||
from objects import matchList
|
||||
from objects import streamList
|
||||
from objects import tokenList
|
||||
from common.web import schiavo
|
||||
from objects import fileLocks
|
||||
|
||||
try:
|
||||
with open("version") as f:
|
||||
VERSION = f.read().strip()
|
||||
VERSION = f.read()
|
||||
if VERSION == "":
|
||||
raise Exception
|
||||
raise
|
||||
except:
|
||||
VERSION = "Unknown"
|
||||
VERSION = "¯\_(xd)_/¯"
|
||||
|
||||
DATADOG_PREFIX = "peppy"
|
||||
BOT_NAME = "FokaBot"
|
||||
application = None
|
||||
db = None
|
||||
redis = None
|
||||
conf = None
|
||||
banchoConf = None
|
||||
tokens = tokenList.tokenList()
|
||||
channels = channelList.channelList()
|
||||
matches = matchList.matchList()
|
||||
restarting = False
|
||||
fLocks = fileLocks.fileLocks()
|
||||
fileBuffers = fileBuffer.buffersList()
|
||||
schiavo = schiavo.schiavo()
|
||||
dog = datadogClient.datadogClient()
|
||||
verifiedCache = {}
|
||||
cloudflare = False
|
||||
chatFilters = None
|
||||
pool = None
|
||||
ircServer = None
|
||||
busyThreads = 0
|
||||
|
||||
debug = False
|
||||
outputRequestTime = False
|
||||
outputPackets = False
|
||||
discord = False
|
||||
gzip = False
|
||||
localize = False
|
||||
sentry = False
|
||||
irc = False
|
||||
restarting = False
|
||||
|
||||
startTime = int(time.time())
|
||||
|
||||
streams = streamList.streamList()
|
||||
|
||||
# Additional modifications
|
||||
COMMON_VERSION_REQ = "1.2.1"
|
||||
try:
|
||||
with open("common/version") as f:
|
||||
COMMON_VERSION = f.read().strip()
|
||||
except:
|
||||
COMMON_VERSION = "Unknown"
|
24
objects/logThread.py
Normal file
24
objects/logThread.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
'''
|
||||
import threading
|
||||
|
||||
class task:
|
||||
def __init__(self, function, args = (), kwargs = {}):
|
||||
self.function = function
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
class logThread:
|
||||
def __init__(self):
|
||||
self.thread = threading.Thread()
|
||||
self.queue = []
|
||||
|
||||
def enqueue(self, function, args = (), kwargs = {}):
|
||||
self.queue.append(task(function, args, kwargs))
|
||||
|
||||
def run(self):
|
||||
for i in self.queue:
|
||||
self.thread = threading.Thread(i.function, i.args, i.kwargs)
|
||||
self.thread.run()
|
||||
self.thread.join()
|
||||
self.queue = []
|
||||
'''
|
811
objects/match.py
811
objects/match.py
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user