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//", 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//", 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//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//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})