Initial Commit
This commit is contained in:
0
web/__init__.py
Normal file
0
web/__init__.py
Normal file
156
web/cheesegull.py
Normal file
156
web/cheesegull.py
Normal 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
96
web/requestsManager.py
Normal 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
87
web/schiavo.py
Normal 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)
|
Reference in New Issue
Block a user