You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

287 lines
9.6 KiB
Python

import functools
from json import JSONDecodeError, dumps, loads
from klein import Klein
from twisted.web.server import Request
import main
from modules import userinfo
from utils.logging.log import warn
def login_required(func):
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
if isinstance(args[0], Request):
request = args[0]
apikey = request.getHeader("ApiKey")
token = request.getHeader("Token")
if not apikey:
return "No API key provided"
if not token:
return "No token provided"
if apikey not in main.tokens:
return "No such API key"
config_token = main.tokens[apikey]
if not token == config_token["hello"]:
return "Invalid token"
counter = config_token["counter"]
request.setHeader("Counter", counter)
return func(self, *args, **kwargs)
return wrapper
class API(object):
"""
Our API webapp.
"""
app = Klein()
@app.route("/", methods=["GET"])
@login_required
def hello(self, request):
return "Hello"
@app.route("/who/<query>/", methods=["POST"])
@login_required
def who(self, request, query):
try:
data = loads(request.content.read())
except JSONDecodeError:
return "Invalid JSON"
if "query" not in data:
return "No query provided"
result = userinfo.getWho(data["query"])
# Expand the generator
return dumps({k: [x for x in v] for k, v in result.items()})
@app.route("/chans/", methods=["POST"])
@login_required
def chans(self, request):
try:
data = loads(request.content.read())
except JSONDecodeError:
return "Invalid JSON"
if "net" not in data:
return "No net provided"
if "query" not in data:
return "No query provided"
if not data["query"]:
warn(f"No query provided: for chans {data}")
return dumps({})
result = userinfo.getChansSingle(data["net"], data["query"])
if not result:
return dumps({})
return dumps({"chans": [x for x in result]})
@app.route("/users/", methods=["POST"])
@login_required
def users(self, request):
try:
data = loads(request.content.read())
except JSONDecodeError:
return "Invalid JSON"
if "net" not in data:
return "No net provided"
if "query" not in data:
return "No query provided"
if not data["query"]:
warn(f"No query provided for users: {data}")
return dumps({})
result = userinfo.getUsersSingle(data["net"], data["query"])
if not result:
return dumps({})
return dumps({"users": [x for x in result]})
@app.route("/online/", methods=["POST"])
@login_required
def online(self, request):
try:
data = loads(request.content.read())
except JSONDecodeError:
return "Invalid JSON"
if "net" not in data:
return "No net provided"
if "query" not in data:
return "No users provided"
if not data["query"]:
warn(f"No users provided: for online {data}")
return dumps({})
net = data["net"]
usermap = {}
for user in data["query"]:
channels = userinfo.getChansSingle(net, [user])
if channels:
usermap[user] = True
else:
usermap[user] = False
return dumps(usermap)
@app.route("/num_users/", methods=["POST"])
@login_required
def num_users(self, request):
try:
data = loads(request.content.read())
except JSONDecodeError:
return "Invalid JSON"
if "net" not in data:
return "No net provided"
if "query" not in data:
return "No users provided"
if not data["query"]:
warn(f"No chans provided: for num_users {data}")
return dumps({})
net = data["net"]
results = userinfo.getUserNum(net, data["query"])
return dumps(results)
@app.route("/num_chans/", methods=["POST"])
@login_required
def num_chans(self, request):
try:
data = loads(request.content.read())
except JSONDecodeError:
return "Invalid JSON"
if "net" not in data:
return "No net provided"
if "query" not in data:
return "No users provided"
if not data["query"]:
warn(f"No users provided: for num_chans {data}")
return dumps({})
net = data["net"]
results = userinfo.getChanNum(net, data["query"])
return dumps(results)
@app.route("/irc/stats/", methods=["POST"])
@login_required
def irc_stats(self, request):
stats = {}
numChannels = 0
numWhoEntries = 0
for i in main.IRCPool.keys():
numChannels += len(main.IRCPool[i].channels)
numWhoEntries += userinfo.getNumTotalWhoEntries()
numRelays = 0
for net in main.network.keys():
numRelays += len(main.network[net].relays)
stats["servers_total_total"] = numRelays
stats["servers_total_unique"] = len(main.network.keys())
stats["servers_online_total"] = len(main.IRCPool.keys())
stats["servers_online_unique"] = len(main.liveNets())
stats["channels"] = numChannels
stats["records"] = numWhoEntries
stats["eventrate"] = main.lastMinuteSample
return dumps(stats)
@app.route("/irc/networks/", methods=["POST"])
@login_required
def irc_networks(self, request):
networks = {}
for net in main.network.keys():
networks[net] = {
"relays": len(main.network[net].relays),
"channels": userinfo.getTotalChanNum(net),
"records": userinfo.getNumWhoEntries(net),
}
return dumps(networks)
@app.route("/irc/network/<net>/", methods=["POST"])
@login_required
def irc_network(self, request, net):
if net not in main.network.keys():
return dumps(False)
inst = main.network[net]
network = {}
network["net"] = inst.net
network["auth"] = inst.auth
network["host"] = inst.host
network["last"] = inst.last
network["port"] = inst.port
network["security"] = inst.security
network["relays"] = len(inst.relays)
network["channels"] = userinfo.getTotalChanNum(net)
network["records"] = userinfo.getNumWhoEntries(net)
return dumps(network)
@app.route("/irc/network/<net>/edit/", methods=["POST"])
@login_required
def irc_network_edit(self, request, net):
try:
data = loads(request.content.read())
except JSONDecodeError:
return "Invalid JSON"
if net not in main.network.keys():
return dumps(False)
inst = main.network[net]
for item in data:
if item == "auth":
auth = data[item][0]
if auth not in ["sasl", "ns", "none"]:
return dumps({"success": False, "reason": "invalid auth."})
elif item == "host":
host = data[item][0]
elif item == "last":
last = data[item][0]
if not last.isdigit():
return dumps({"success": False, "reason": "invalid last: not a number."})
elif item == "port":
port = data[item][0]
if not port.isdigit():
return dumps({"success": False, "reason": "invalid port: not a number."})
port = int(port)
elif item == "security":
security = data[item][0]
if security not in ["ssl", "plain"]:
return dumps({"success": False, "reason": "invalid security."})
inst.auth = auth
inst.host = host
inst.last = last
inst.port = port
inst.security = security
main.saveConf("network")
return dumps({"success": True})
@app.route("/irc/network/<net>/relays/", methods=["POST"])
@login_required
def irc_network_relays(self, request, net):
if net not in main.network.keys():
return dumps(False)
relays_inst = main.network[net].relays
relays = []
for num in relays_inst.keys():
to_append = relays_inst[num]
name = f"{net}{num}"
if name in main.IRCPool.keys():
to_append["chans"] = len(main.IRCPool[name].channels)
to_append["nick"] = main.IRCPool[name].nickname
else:
to_append["chans"] = 0
to_append["nick"] = None
relays.append(to_append)
return dumps({"relays": relays})
@app.route("/irc/network/<net>/channels/", methods=["POST"])
@login_required
def irc_network_channels(self, request, net):
if net not in main.network.keys():
return dumps(False)
relays_inst = main.network[net].relays
channels = {}
for num in relays_inst.keys():
name = f"{net}{num}"
if name in main.IRCPool.keys():
net_chans = main.IRCPool[name].channels
channels_annotated = userinfo.getUserNum(net, net_chans)
for channel in net_chans:
channels[channel] = channels_annotated[channel]
return dumps({"channels": channels})