#!/usr/sbin/env python3 # Twisted/Klein imports from twisted.logger import Logger from twisted.internet import reactor from twisted.internet.task import LoopingCall, deferLater from klein import Klein # Other library imports from json import dumps, loads from json.decoder import JSONDecodeError # Project imports from settings import settings from revolut import Revolut from agora import Agora from transactions import Transactions from irc import bot def convert(data): if isinstance(data, bytes): return data.decode("ascii") if isinstance(data, dict): return dict(map(convert, data.items())) if isinstance(data, tuple): return map(convert, data) return data class WebApp(object): """ Our Klein webapp. """ app = Klein() def __init__(self, agora, irc): self.revolut = Revolut(irc) # Initialise Revolut client and pass IRC self.agora = agora self.tx = Transactions(agora, irc) # Initialise Transactions client and pass Agora and IRC self.log = Logger("webapp") @app.route("/newkey") def newkey(self, request): self.revolut.create_new_jwt() self.revolut.get_access_token() return dumps(True) @app.route("/callback", methods=["POST"]) def callback(self, request): content = request.content.read() try: parsed = loads(content) except JSONDecodeError: self.log.error("Failed to parse JSON callback: {content}", content=content) return dumps(False) self.log.info("Callback received: {parsed}", parsed=parsed["data"]["id"]) self.tx.transaction(parsed) return dumps(True) @app.route("/find//") def find(self, request, reference, amount): try: int(amount) except ValueError: return dumps({"success": False, "msg": "Amount is not an integer"}) rtrn = self.tx.find_tx(reference, amount) if rtrn == "AMOUNT_INVALID": return dumps({"success": False, "msg": "Reference found but amount invalid"}) elif not rtrn: return dumps({"success": False, "msg": "Reference not found"}) else: return dumps(convert(rtrn)) @app.route("/trades") def trades(self, request): trade_list = self.agora.get_active_trades() return dumps(trade_list) @app.route("/messages/") def messages(self, request, contact_id): message_list = self.agora.get_messages(contact_id) return dumps(message_list) @app.route("/dist") def dist_countries(self, request): rtrn = self.agora.dist_countries() return dumps(rtrn) @app.route("/ads") def ads(self, request): rtrn = self.agora.get_ads() return dumps(rtrn) @app.route("/create//") def create(self, request, countrycode, currency): rtrn = self.agora.create_ad(countrycode, currency) return dumps(rtrn) def start(handler, refresh_sec): """ Schedule to refresh the API token once the reactor starts, and create LoopingCapp to refresh it periodically. """ if settings.Revolut.SetupToken == "1": deferLater(reactor, 1, handler.setup_auth) else: deferLater(reactor, 1, handler.get_new_token) deferLater(reactor, 4, handler.setup_webhook) lc = LoopingCall(handler.get_new_token) lc.start(refresh_sec) if __name__ == "__main__": irc = bot() # Initialise IRC client agora = Agora(irc) # Initialise Agora client and pass IRC webapp = WebApp(agora, irc) # Initialise API and pass agora and IRC start(webapp.revolut, int(settings.Revolut.RefreshSec)) webapp.app.run("127.0.0.1", 8080)