Initial Commit

This commit is contained in:
qewc
2018-12-09 19:58:56 +00:00
commit d03f68e4e9
34 changed files with 3512 additions and 0 deletions

0
web/__init__.py Normal file
View File

156
web/cheesegull.py Normal file
View File

@@ -0,0 +1,156 @@
import requests
import json
from constants import exceptions
from objects import glob
from common.log import logUtils as log
from objects import glob
def cheesegullRequest(handler, requestType="GET", key="", params=None, mustHave=None, wants=None):
"""
Send a request to Cheesegull
:param handler: name of the api handler (eg: `search` for `http://chesegu.ll/api/search`)
:param requestType: `GET` or `POST`. Default: `GET`
:param key: authorization key. Optional.
:param params: dictionary containing get/post form parameters. Optional.
:param mustHave: list or string containing the key(s) that must be contained in the json response. Optional.
:param wants: can be a single string, or a list of strings.
:return: returns None if the result was invalid or if the request failed.
if `wants` is a string, returns the key from the response.
if `wants` is a list of strings, return a dictionary containing the wanted keys.
"""
# Default values
if mustHave is None:
mustHave = []
if wants is None:
wants = []
if params is None:
params = {}
# Params and function
postData = None
getParams = None
if requestType.lower() == "post":
f = requests.post
postData = params
else:
f = requests.get
getParams = params
result = f("{}/{}".format(glob.conf.config["cheesegull"]["apiurl"], handler), params=getParams, data=postData, headers= {
"Authorization": key
})
log.debug(result.url)
# log.debug(str(result.text))
try:
data = json.loads(result.text)
except (json.JSONDecodeError, ValueError, requests.RequestException, KeyError, exceptions.noAPIDataError):
return None
# Params and status check
if result.status_code != 200:
return None
if mustHave is not None:
if type(mustHave) == str:
mustHave = [mustHave]
for i in mustHave:
if i not in data:
return None
# Return what we want
if type(wants) == str:
if wants in data:
return data[wants]
return None
elif len(wants) == 0:
return data
else:
res = {}
for i in data:
if i in wants:
res[i] = data[i]
return res
def getListing(rankedStatus, page, gameMode, query):
glob.dog.increment(glob.DATADOG_PREFIX + ".cheesegull_requests", tags=["cheesegull:listing"])
params = {
"query": query,
"offset": page,
"amount": 100
}
if rankedStatus is not None:
params["status"] = rankedStatus
if gameMode is not None:
params["mode"] = gameMode
return cheesegullRequest("search", params=params)
def getBeatmapSet(id):
glob.dog.increment(glob.DATADOG_PREFIX + ".cheesegull_requests", tags=["cheesegull:set"])
return cheesegullRequest("s/{}".format(id))
def getBeatmap(id):
glob.dog.increment(glob.DATADOG_PREFIX + ".cheesegull_requests", tags=["cheesegull:beatmap"])
setID = cheesegullRequest("b/{}".format(id), wants="ParentSetID")
if setID is None or setID <= 0:
return None
return getBeatmapSet(setID)
def updateBeatmap(setID):
# This has been deprecated
return
# data = cheesegullRequest("request", "POST", glob.conf.config["cheesegull"]["apikey"], params={
# "set_id": setID
# }, mustHave="Ok")
# return (True, "") if data["Ok"] else (False, data["Message"])
def toDirect(data):
if "ChildrenBeatmaps" not in data or data["ChildrenBeatmaps"] is None:
raise ValueError("`data` doesn't contain a valid cheesegull response")
s = "{SetID}.osz|{Artist}|{Title}|{Creator}|{RankedStatus}|0.00|{LastUpdate}|{SetID}|" \
"{SetID}|{HasVideoInt}|0|1337|{FileSizeNoVideo}|".format(
**data,
**{
"HasVideoInt": int(data["HasVideo"]),
"FileSizeNoVideo": "7331" if data["HasVideo"] else ""
}
)
if len(data["ChildrenBeatmaps"]) > 0:
for i in data["ChildrenBeatmaps"]:
s += "{DiffNameSanitized} ({DifficultyRating:.2f}★~{BPM}♫~AR{AR}~OD{OD}~CS{CS}~HP{HP}~{ReadableLength})" \
"@{Mode},".format(**i, **{
"DiffNameSanitized": i["DiffName"].replace("@", ""),
"ReadableLength": "{}m{}s".format(i["TotalLength"] // 60, i["TotalLength"] % 60)
})
s = s.strip(",")
s += "|"
return s
def toDirectNp(data):
return "{SetID}.osz|{Artist}|{Title}|{Creator}|{RankedStatus}|10.00|{LastUpdate}|{SetID}|" \
"{SetID}|{HasVideoInt}|0|1337|{FileSizeNoVideo}".format(
**data,
**{
"HasVideoInt": int(data["HasVideo"]),
"FileSizeNoVideo": "7331" if data["HasVideo"] else ""
}
)
def directToApiStatus(directStatus):
if directStatus is None:
return None
elif directStatus == 0 or directStatus == 7:
return [1, 2]
elif directStatus == 8:
return 4
elif directStatus == 3:
return 3
elif directStatus == 2:
return 0
elif directStatus == 5:
return -2
elif directStatus == 4:
return None
else:
return 1

96
web/requestsManager.py Normal file
View File

@@ -0,0 +1,96 @@
import sys
import traceback
import tornado
import tornado.web
import tornado.gen
from tornado.ioloop import IOLoop
from objects import glob
from common.log import logUtils as log
from raven.contrib.tornado import SentryMixin
class asyncRequestHandler(SentryMixin, tornado.web.RequestHandler):
"""
Tornado asynchronous request handler
create a class that extends this one (requestHelper.asyncRequestHandler)
use asyncGet() and asyncPost() instead 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)))
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)))
finally:
if not self._finished:
self.finish()
def asyncGet(self, *args, **kwargs):
self.send_error(405)
def asyncPost(self, *args, **kwargs):
self.send_error(405)
def getRequestIP(self):
"""
Return CF-Connecting-IP (request IP when under cloudflare, you have to configure nginx to enable that)
If that fails, return X-Forwarded-For (request IP when not under Cloudflare)
if everything else fails, return remote IP
:return: Client IP address
"""
if "CF-Connecting-IP" in self.request.headers:
return self.request.headers.get("CF-Connecting-IP")
elif "X-Forwarded-For" in self.request.headers:
return self.request.headers.get("X-Forwarded-For")
else:
return self.request.remote_ip
def runBackground(data, callback):
"""
Run a function in the background.
Used to handle multiple requests at the same time
:param data: (func, args, kwargs)
:param callback: function to call when `func` (data[0]) returns
:return:
"""
func, args, kwargs = data
def _callback(result):
IOLoop.instance().add_callback(lambda: callback(result))
glob.pool.apply_async(func, args, kwargs, _callback)
glob.dog.increment(glob.DATADOG_PREFIX + ".incoming_requests")
def checkArguments(arguments, requiredArguments):
"""
Check that every requiredArguments elements are in arguments
:param arguments: full argument list, from tornado
:param requiredArguments: required arguments list
:return: True if all arguments are passed, False if not
"""
for i in requiredArguments:
if i not in arguments:
return False
return True
def printArguments(t):
"""
Print passed arguments, for debug purposes
:param t: tornado object (self)
"""
msg = "ARGS::"
for i in t.request.arguments:
msg += "{}={}\r\n".format(i, t.get_argument(i))
log.debug(msg)

87
web/schiavo.py Normal file
View File

@@ -0,0 +1,87 @@
import requests
from urllib.parse import urlencode
class schiavo:
"""
Schiavo Bot class
"""
def __init__(self, botURL=None, prefix="", maxRetries=20):
"""
Initialize a new schiavo bot instance
:param botURL: schiavo api url.
:param prefix: text to prepend in every message, can be empty.
:param maxRetries: max retries if api request fail. 0 = don't retry.
"""
self.botURL = botURL
self.maxRetries = maxRetries
self.prefix = prefix
def sendMessage(self, channel, message, noPrefix=False):
"""
Send a generic message through schiavo api
:param channel: api channel.
:param message: message content.
:param noPrefix: if True, don't prepend prefix to message.
:return:
"""
if self.botURL is None:
return
for _ in range(0, self.maxRetries):
try:
finalMsg = "{prefix} {message}".format(prefix=self.prefix if not noPrefix else "", message=message)
requests.get("{}/{}?{}".format(self.botURL, channel, urlencode({ "message": finalMsg })))
break
except requests.RequestException:
continue
def sendConfidential(self, message, noPrefix=False):
"""
Send a message to #bunk
:param message: message content.
:param noPrefix: if True, don't prepend prefix to message.
:return:
"""
self.sendMessage("bunk", message, noPrefix)
def sendStaff(self, message, noPrefix=False):
"""
Send a message to #staff
:param message: message content.
:param noPrefix: if True, don't prepend prefix to message.
:return:
"""
self.sendMessage("staff", message, noPrefix)
def sendGeneral(self, message, noPrefix=True):
"""
Send a message to #general
:param message: message content.
:param noPrefix: if True, don't prepend prefix to message.
:return:
"""
self.sendMessage("general", message, noPrefix)
def sendChatlog(self, message, noPrefix=True):
"""
Send a message to #chatlog.
:param message: message content.
:param noPrefix: if True, don't prepend prefix to message.
:return:
"""
self.sendMessage("chatlog", message, noPrefix)
def sendCM(self, message, noPrefix=False):
"""
Send a message to #communitymanagers
:param message: message content.
:param noPrefix: if True, don't prepend prefix to message.
:return:
"""
self.sendMessage("cm", message, noPrefix)