Use the Python logger instead of the Twisted one
This commit is contained in:
parent
80c696ef73
commit
539d6f1fbb
|
@ -1,5 +1,4 @@
|
||||||
# Twisted/Klein imports
|
# Twisted/Klein imports
|
||||||
from twisted.logger import Logger
|
|
||||||
from twisted.internet.task import LoopingCall
|
from twisted.internet.task import LoopingCall
|
||||||
from twisted.internet.threads import deferToThread
|
from twisted.internet.threads import deferToThread
|
||||||
|
|
||||||
|
@ -16,10 +15,8 @@ from datetime import datetime
|
||||||
from settings import settings
|
from settings import settings
|
||||||
import util
|
import util
|
||||||
|
|
||||||
log = Logger("agora.global")
|
|
||||||
|
|
||||||
|
class Agora(util.Base):
|
||||||
class Agora(object):
|
|
||||||
"""
|
"""
|
||||||
AgoraDesk API handler.
|
AgoraDesk API handler.
|
||||||
"""
|
"""
|
||||||
|
@ -29,7 +26,7 @@ class Agora(object):
|
||||||
Initialise the AgoraDesk and CurrencyRates APIs.
|
Initialise the AgoraDesk and CurrencyRates APIs.
|
||||||
Initialise the last_dash storage for detecting new trades.
|
Initialise the last_dash storage for detecting new trades.
|
||||||
"""
|
"""
|
||||||
self.log = Logger("agora")
|
super().__init__()
|
||||||
self.agora = AgoraDesk(settings.Agora.Token)
|
self.agora = AgoraDesk(settings.Agora.Token)
|
||||||
self.cr = CurrencyRates() # TODO: remove this and defer to money
|
self.cr = CurrencyRates() # TODO: remove this and defer to money
|
||||||
self.cg = CoinGeckoAPI() # TODO: remove this and defer to money
|
self.cg = CoinGeckoAPI() # TODO: remove this and defer to money
|
||||||
|
@ -66,7 +63,7 @@ class Agora(object):
|
||||||
if not dash.items():
|
if not dash.items():
|
||||||
return False
|
return False
|
||||||
if "data" not in dash["response"].keys():
|
if "data" not in dash["response"].keys():
|
||||||
self.log.error("Data not in dashboard response: {content}", content=dash)
|
self.log.error(f"Data not in dashboard response: {dash}")
|
||||||
return dash_tmp
|
return dash_tmp
|
||||||
if dash["response"]["data"]["contact_count"] > 0:
|
if dash["response"]["data"]["contact_count"] > 0:
|
||||||
for contact in dash["response"]["data"]["contact_list"]:
|
for contact in dash["response"]["data"]["contact_list"]:
|
||||||
|
@ -166,7 +163,7 @@ class Agora(object):
|
||||||
if not messages["success"]:
|
if not messages["success"]:
|
||||||
return False
|
return False
|
||||||
if "data" not in messages["response"]:
|
if "data" not in messages["response"]:
|
||||||
self.log.error("Data not in messages response: {content}", content=messages["response"])
|
self.log.error(f"Data not in messages response: {messages['response']}")
|
||||||
return False
|
return False
|
||||||
open_tx = self.tx.get_ref_map().keys()
|
open_tx = self.tx.get_ref_map().keys()
|
||||||
for message in messages["response"]["data"]["message_list"]:
|
for message in messages["response"]["data"]["message_list"]:
|
||||||
|
@ -429,20 +426,15 @@ class Agora(object):
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
if "error_code" not in rtrn["response"]["error"]:
|
if "error_code" not in rtrn["response"]["error"]:
|
||||||
self.log.error("Error code not in return for ad {ad_id}: {response}", ad_id=ad_id, response=rtrn["response"])
|
self.log.error(f"Error code not in return for ad {ad_id}: {rtrn['response']}")
|
||||||
return
|
return
|
||||||
if rtrn["response"]["error"]["error_code"] == 429:
|
if rtrn["response"]["error"]["error_code"] == 429:
|
||||||
throttled += 1
|
throttled += 1
|
||||||
sleep_time = pow(throttled, float(settings.Agora.SleepExponent))
|
sleep_time = pow(throttled, float(settings.Agora.SleepExponent))
|
||||||
self.log.info(
|
self.log.info(f"Throttled {throttled} times while updating {ad_id}, sleeping for {sleep_time} seconds")
|
||||||
"Throttled {x} times while updating {id}, sleeping for {sleep} seconds",
|
|
||||||
x=throttled,
|
|
||||||
id=ad_id,
|
|
||||||
sleep=sleep_time,
|
|
||||||
)
|
|
||||||
# We're running in a thread, so this is fine
|
# We're running in a thread, so this is fine
|
||||||
sleep(sleep_time)
|
sleep(sleep_time)
|
||||||
self.log.error("Error updating ad {ad_id}: {response}", ad_id=ad_id, response=rtrn["response"])
|
self.log.error(f"Error updating ad {ad_id}: {rtrn['response']}")
|
||||||
continue
|
continue
|
||||||
iterations += 1
|
iterations += 1
|
||||||
|
|
||||||
|
@ -645,9 +637,7 @@ class Agora(object):
|
||||||
|
|
||||||
if not float(wallet_xmr) > profit_usd_in_xmr:
|
if not float(wallet_xmr) > profit_usd_in_xmr:
|
||||||
# Not enough funds to withdraw
|
# Not enough funds to withdraw
|
||||||
self.log.error(
|
self.log.error(f"Not enough funds to withdraw {profit_usd_in_xmr}, as wallet only contains {wallet_xmr}")
|
||||||
"Not enough funds to withdraw {profit}, as wallet only contains {wallet}", profit=profit_usd_in_xmr, wallet=wallet_xmr
|
|
||||||
)
|
|
||||||
self.irc.sendmsg(f"Not enough funds to withdraw {profit_usd_in_xmr}, as wallet only contains {wallet_xmr}")
|
self.irc.sendmsg(f"Not enough funds to withdraw {profit_usd_in_xmr}, as wallet only contains {wallet_xmr}")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,9 @@ from typing import Union
|
||||||
import arrow
|
import arrow
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
|
# Project imports
|
||||||
|
import util
|
||||||
|
|
||||||
__author__ = "marvin8"
|
__author__ = "marvin8"
|
||||||
__copyright__ = "(C) 2021 https://codeberg.org/MarvinsCryptoTools/agoradesk_py"
|
__copyright__ = "(C) 2021 https://codeberg.org/MarvinsCryptoTools/agoradesk_py"
|
||||||
__version__ = "0.1.0"
|
__version__ = "0.1.0"
|
||||||
|
@ -20,7 +23,7 @@ __version__ = "0.1.0"
|
||||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
||||||
logging.getLogger("requests.packages.urllib3").setLevel(logging.INFO)
|
logging.getLogger("requests.packages.urllib3").setLevel(logging.INFO)
|
||||||
logging.getLogger("urllib3.connectionpool").setLevel(logging.INFO)
|
logging.getLogger("urllib3.connectionpool").setLevel(logging.INFO)
|
||||||
logger = logging.getLogger(__name__)
|
logger = util.get_logger(__name__)
|
||||||
|
|
||||||
URI_API = "https://agoradesk.com/api/v1/"
|
URI_API = "https://agoradesk.com/api/v1/"
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# Twisted/Klein imports
|
# Twisted/Klein imports
|
||||||
from twisted.logger import Logger
|
|
||||||
from twisted.internet import reactor
|
from twisted.internet import reactor
|
||||||
from klein import Klein
|
from klein import Klein
|
||||||
|
|
||||||
|
@ -41,33 +40,20 @@ def cleanup(sig, frame):
|
||||||
signal(SIGINT, cleanup) # Handle Ctrl-C and run the cleanup routine
|
signal(SIGINT, cleanup) # Handle Ctrl-C and run the cleanup routine
|
||||||
|
|
||||||
|
|
||||||
def convert(data):
|
class WebApp(util.Base):
|
||||||
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.
|
Our Klein webapp.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
app = Klein()
|
app = Klein()
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.log = Logger("webapp")
|
|
||||||
|
|
||||||
@app.route("/callback", methods=["POST"])
|
@app.route("/callback", methods=["POST"])
|
||||||
def callback(self, request):
|
def callback(self, request):
|
||||||
content = request.content.read()
|
content = request.content.read()
|
||||||
try:
|
try:
|
||||||
parsed = loads(content)
|
parsed = loads(content)
|
||||||
except JSONDecodeError:
|
except JSONDecodeError:
|
||||||
self.log.error("Failed to parse JSON callback: {content}", content=content)
|
self.log.error(f"Failed to parse JSON callback: {content}")
|
||||||
return dumps(False)
|
return dumps(False)
|
||||||
self.log.info("Callback received: {parsed}", parsed=parsed["data"]["id"])
|
self.log.info("Callback received: {parsed}", parsed=parsed["data"]["id"])
|
||||||
# self.tx.transaction(parsed)
|
# self.tx.transaction(parsed)
|
||||||
|
@ -76,19 +62,19 @@ class WebApp(object):
|
||||||
# set up another connection to a bank
|
# set up another connection to a bank
|
||||||
@app.route("/signin", methods=["GET"])
|
@app.route("/signin", methods=["GET"])
|
||||||
def signin(self, request):
|
def signin(self, request):
|
||||||
auth_url = self.truelayer.create_auth_url()
|
auth_url = self.sinks.truelayer.create_auth_url()
|
||||||
return f'Please sign in <a href="{auth_url}" target="_blank">here.</a>'
|
return f'Please sign in <a href="{auth_url}" target="_blank">here.</a>'
|
||||||
|
|
||||||
# endpoint called after we finish setting up a connection above
|
# endpoint called after we finish setting up a connection above
|
||||||
@app.route("/callback-truelayer", methods=["POST"])
|
@app.route("/callback-truelayer", methods=["POST"])
|
||||||
def signin_callback(self, request):
|
def signin_callback(self, request):
|
||||||
code = request.args[b"code"]
|
code = request.args[b"code"]
|
||||||
self.truelayer.handle_authcode_received(code)
|
self.sinks.truelayer.handle_authcode_received(code)
|
||||||
return dumps(True)
|
return dumps(True)
|
||||||
|
|
||||||
@app.route("/accounts", methods=["GET"])
|
@app.route("/accounts", methods=["GET"])
|
||||||
def balance(self, request):
|
def balance(self, request):
|
||||||
accounts = self.truelayer.get_accounts()
|
accounts = self.sinks.truelayer.get_accounts()
|
||||||
return dumps(accounts, indent=2)
|
return dumps(accounts, indent=2)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,16 @@
|
||||||
# Twisted/Klein imports
|
|
||||||
from twisted.logger import Logger
|
|
||||||
|
|
||||||
# Other library imports
|
# Other library imports
|
||||||
from json import loads
|
from json import loads
|
||||||
|
|
||||||
# Project imports
|
# Project imports
|
||||||
from settings import settings
|
from settings import settings
|
||||||
|
import util
|
||||||
|
|
||||||
|
|
||||||
class Markets(object):
|
class Markets(util.Base):
|
||||||
""" "
|
""" "
|
||||||
Markets handler for generic market functions.
|
Markets handler for generic market functions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.log = Logger("markets")
|
|
||||||
|
|
||||||
def get_all_assets(self):
|
def get_all_assets(self):
|
||||||
assets = loads(settings.Agora.AssetList)
|
assets = loads(settings.Agora.AssetList)
|
||||||
return assets
|
return assets
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
# Twisted/Klein imports
|
|
||||||
from twisted.logger import Logger
|
|
||||||
|
|
||||||
# Other library imports
|
# Other library imports
|
||||||
from pycoingecko import CoinGeckoAPI
|
from pycoingecko import CoinGeckoAPI
|
||||||
from forex_python.converter import CurrencyRates
|
from forex_python.converter import CurrencyRates
|
||||||
|
|
||||||
# Project imports
|
# Project imports
|
||||||
from settings import settings
|
from settings import settings
|
||||||
|
import util
|
||||||
|
|
||||||
|
|
||||||
class Money(object):
|
class Money(util.Base):
|
||||||
"""
|
"""
|
||||||
Generic class for handling money-related matters that aren't Revolut or Agora.
|
Generic class for handling money-related matters that aren't Revolut or Agora.
|
||||||
"""
|
"""
|
||||||
|
@ -20,7 +18,7 @@ class Money(object):
|
||||||
Set the logger.
|
Set the logger.
|
||||||
Initialise the CoinGecko API.
|
Initialise the CoinGecko API.
|
||||||
"""
|
"""
|
||||||
self.log = Logger("money")
|
super().__init__()
|
||||||
self.cr = CurrencyRates()
|
self.cr = CurrencyRates()
|
||||||
self.cg = CoinGeckoAPI()
|
self.cg = CoinGeckoAPI()
|
||||||
|
|
||||||
|
@ -41,6 +39,7 @@ class Money(object):
|
||||||
price = float(ad[2])
|
price = float(ad[2])
|
||||||
rate = round(price / base_currency_price, 2)
|
rate = round(price / base_currency_price, 2)
|
||||||
ad.append(rate)
|
ad.append(rate)
|
||||||
|
# TODO: sort?
|
||||||
return sorted(ads, key=lambda x: x[2])
|
return sorted(ads, key=lambda x: x[2])
|
||||||
|
|
||||||
def get_rates_all(self):
|
def get_rates_all(self):
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
# Twisted/Klein imports
|
|
||||||
from twisted.logger import Logger
|
|
||||||
|
|
||||||
# Other library imports
|
# Other library imports
|
||||||
# import requests
|
# import requests
|
||||||
# from json import dumps
|
# from json import dumps
|
||||||
|
@ -10,15 +7,16 @@ from twisted.logger import Logger
|
||||||
import sinks.fidor
|
import sinks.fidor
|
||||||
import sinks.nordigen
|
import sinks.nordigen
|
||||||
import sinks.truelayer
|
import sinks.truelayer
|
||||||
|
import util
|
||||||
|
|
||||||
|
|
||||||
class Sinks(object):
|
class Sinks(util.Base):
|
||||||
"""
|
"""
|
||||||
Class to manage calls to various sinks.
|
Class to manage calls to various sinks.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.log = Logger("sinks")
|
super().__init__()
|
||||||
self.fidor = sinks.fidor.Fidor()
|
self.fidor = sinks.fidor.Fidor()
|
||||||
self.nordigen = sinks.nordigen.Nordigen()
|
self.nordigen = sinks.nordigen.Nordigen()
|
||||||
self.truelayer = sinks.truelayer.TrueLayer()
|
self.truelayer = sinks.truelayer.TrueLayer()
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
# Twisted/Klein imports
|
# Twisted/Klein imports
|
||||||
from twisted.logger import Logger
|
|
||||||
|
|
||||||
# Other library imports
|
# Other library imports
|
||||||
# import requests
|
# import requests
|
||||||
|
@ -7,16 +6,14 @@ from twisted.logger import Logger
|
||||||
|
|
||||||
# Project imports
|
# Project imports
|
||||||
# from settings import settings
|
# from settings import settings
|
||||||
|
import util
|
||||||
|
|
||||||
|
|
||||||
class Fidor(object):
|
class Fidor(util.Base):
|
||||||
"""
|
"""
|
||||||
Class to manage calls to the Fidor API.
|
Class to manage calls to the Fidor API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.log = Logger("fidor")
|
|
||||||
|
|
||||||
def authorize(self):
|
def authorize(self):
|
||||||
"""
|
"""
|
||||||
Perform initial authorization against Fidor API.
|
Perform initial authorization against Fidor API.
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
# Twisted/Klein imports
|
|
||||||
from twisted.logger import Logger
|
|
||||||
|
|
||||||
# Other library imports
|
# Other library imports
|
||||||
import requests
|
import requests
|
||||||
from json import dumps
|
from json import dumps
|
||||||
|
@ -8,15 +5,16 @@ from simplejson.errors import JSONDecodeError
|
||||||
|
|
||||||
# Project imports
|
# Project imports
|
||||||
from settings import settings
|
from settings import settings
|
||||||
|
import util
|
||||||
|
|
||||||
|
|
||||||
class Nordigen(object):
|
class Nordigen(util.Base):
|
||||||
"""
|
"""
|
||||||
Class to manage calls to Open Banking APIs through Nordigen.
|
Class to manage calls to Open Banking APIs through Nordigen.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.log = Logger("nordigen")
|
super().__init__()
|
||||||
self.token = None
|
self.token = None
|
||||||
self.get_access_token()
|
self.get_access_token()
|
||||||
|
|
||||||
|
@ -36,11 +34,11 @@ class Nordigen(object):
|
||||||
try:
|
try:
|
||||||
parsed = r.json()
|
parsed = r.json()
|
||||||
except JSONDecodeError:
|
except JSONDecodeError:
|
||||||
self.log.error("Error parsing access token response: {content}", content=r.content)
|
self.log.error(f"Error parsing access token response: {r.content}")
|
||||||
return False
|
return False
|
||||||
if "access" in parsed:
|
if "access" in parsed:
|
||||||
self.token = parsed["access"]
|
self.token = parsed["access"]
|
||||||
self.log.info("Refreshed access token - Nordigen")
|
self.log.info("Refreshed access token")
|
||||||
|
|
||||||
def get_institutions(self, country, filter_name=None):
|
def get_institutions(self, country, filter_name=None):
|
||||||
"""
|
"""
|
||||||
|
@ -58,7 +56,7 @@ class Nordigen(object):
|
||||||
try:
|
try:
|
||||||
parsed = r.json()
|
parsed = r.json()
|
||||||
except JSONDecodeError:
|
except JSONDecodeError:
|
||||||
self.log.error("Error parsing institutions response: {content}", content=r.content)
|
self.log.error(f"Error parsing institutions response: {r.content}")
|
||||||
return False
|
return False
|
||||||
new_list = []
|
new_list = []
|
||||||
if filter_name:
|
if filter_name:
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
# Twisted/Klein imports
|
# Twisted/Klein imports
|
||||||
from twisted.logger import Logger
|
|
||||||
from twisted.internet.task import LoopingCall
|
from twisted.internet.task import LoopingCall
|
||||||
|
|
||||||
# Other library imports
|
# Other library imports
|
||||||
|
@ -10,15 +9,16 @@ import urllib
|
||||||
|
|
||||||
# Project imports
|
# Project imports
|
||||||
from settings import settings
|
from settings import settings
|
||||||
|
import util
|
||||||
|
|
||||||
|
|
||||||
class TrueLayer(object):
|
class TrueLayer(util.Base):
|
||||||
"""
|
"""
|
||||||
Class to manage calls to Open Banking APIs through TrueLayer.
|
Class to manage calls to Open Banking APIs through TrueLayer.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.log = Logger("truelayer")
|
super().__init__()
|
||||||
self.token = None
|
self.token = None
|
||||||
self.lc = LoopingCall(self.get_new_token)
|
self.lc = LoopingCall(self.get_new_token)
|
||||||
self.lc.start(int(settings.TrueLayer.RefreshSec))
|
self.lc.start(int(settings.TrueLayer.RefreshSec))
|
||||||
|
@ -58,7 +58,7 @@ class TrueLayer(object):
|
||||||
settings.TrueLayer.AuthCode = authcode
|
settings.TrueLayer.AuthCode = authcode
|
||||||
settings.write()
|
settings.write()
|
||||||
self.token = parsed["access_token"]
|
self.token = parsed["access_token"]
|
||||||
self.log.info("Retrieved access/refresh tokens - TrueLayer")
|
self.log.info("Retrieved access/refresh tokens")
|
||||||
|
|
||||||
def get_new_token(self):
|
def get_new_token(self):
|
||||||
"""
|
"""
|
||||||
|
@ -81,7 +81,7 @@ class TrueLayer(object):
|
||||||
if r.status_code == 200:
|
if r.status_code == 200:
|
||||||
if "access_token" in parsed.keys():
|
if "access_token" in parsed.keys():
|
||||||
self.token = parsed["access_token"]
|
self.token = parsed["access_token"]
|
||||||
self.log.info("Refreshed access token - TrueLayer")
|
self.log.info("Refreshed access token")
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
self.log.error(f"Token refresh didn't contain access token: {parsed}", parsed=parsed)
|
self.log.error(f"Token refresh didn't contain access token: {parsed}", parsed=parsed)
|
||||||
|
@ -100,7 +100,25 @@ class TrueLayer(object):
|
||||||
try:
|
try:
|
||||||
parsed = r.json()
|
parsed = r.json()
|
||||||
except JSONDecodeError:
|
except JSONDecodeError:
|
||||||
self.log.error("Error parsing institutions response: {content}", content=r.content)
|
self.log.error("Error parsing accounts response: {content}", content=r.content)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return parsed
|
return parsed
|
||||||
|
|
||||||
|
def get_transactions(self, account_id):
|
||||||
|
"""
|
||||||
|
Get a list of transactions from an account.
|
||||||
|
:param account_id: account to fetch transactions for
|
||||||
|
:return: list of transactions
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
headers = {"Authorization": f"Bearer {self.token}"}
|
||||||
|
path = f"{settings.TrueLayer.DataBase}/accounts/{account_id}/transactions"
|
||||||
|
r = requests.get(path, headers=headers)
|
||||||
|
try:
|
||||||
|
parsed = r.json()
|
||||||
|
except JSONDecodeError:
|
||||||
|
self.log.error("Error parsing transactions response: {content}", content=r.content)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return parsed["results"]
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
# Twisted/Klein imports
|
# Twisted/Klein imports
|
||||||
from twisted.logger import Logger
|
|
||||||
from twisted.internet.task import LoopingCall
|
from twisted.internet.task import LoopingCall
|
||||||
from twisted.internet.threads import deferToThread
|
from twisted.internet.threads import deferToThread
|
||||||
|
|
||||||
|
@ -15,7 +14,7 @@ import logging
|
||||||
# Project imports
|
# Project imports
|
||||||
from settings import settings
|
from settings import settings
|
||||||
from db import r
|
from db import r
|
||||||
from util import convert
|
import util
|
||||||
|
|
||||||
# TODO: secure ES traffic properly
|
# TODO: secure ES traffic properly
|
||||||
urllib3.disable_warnings()
|
urllib3.disable_warnings()
|
||||||
|
@ -26,7 +25,7 @@ tracer = logging.getLogger("elastic_transport.transport")
|
||||||
tracer.setLevel(logging.CRITICAL)
|
tracer.setLevel(logging.CRITICAL)
|
||||||
|
|
||||||
|
|
||||||
class Transactions(object):
|
class Transactions(util.Base):
|
||||||
"""
|
"""
|
||||||
Handler class for incoming Revolut transactions.
|
Handler class for incoming Revolut transactions.
|
||||||
"""
|
"""
|
||||||
|
@ -36,7 +35,7 @@ class Transactions(object):
|
||||||
Initialise the Transaction object.
|
Initialise the Transaction object.
|
||||||
Set the logger.
|
Set the logger.
|
||||||
"""
|
"""
|
||||||
self.log = Logger("transactions")
|
super().__init__()
|
||||||
if settings.ES.Enabled == "1":
|
if settings.ES.Enabled == "1":
|
||||||
self.es = Elasticsearch(
|
self.es = Elasticsearch(
|
||||||
f"https://{settings.ES.Host}:9200",
|
f"https://{settings.ES.Host}:9200",
|
||||||
|
@ -89,10 +88,10 @@ class Transactions(object):
|
||||||
# stored_trade here is actually TX
|
# stored_trade here is actually TX
|
||||||
stored_trade = r.hgetall(f"tx.{txid}")
|
stored_trade = r.hgetall(f"tx.{txid}")
|
||||||
if not stored_trade:
|
if not stored_trade:
|
||||||
self.log.error("Could not find entry in DB for typeless transaction: {id}", id=txid)
|
self.log.error(f"Could not find entry in DB for typeless transaction: {txid}")
|
||||||
return
|
return
|
||||||
print("BEFORE CONVERT STORED TRADE", stored_trade)
|
print("BEFORE CONVERT STORED TRADE", stored_trade)
|
||||||
stored_trade = convert(stored_trade)
|
stored_trade = util.convert(stored_trade)
|
||||||
if "old_state" in inside:
|
if "old_state" in inside:
|
||||||
if "new_state" in inside:
|
if "new_state" in inside:
|
||||||
# We don't care unless we're being told a transaction is now completed
|
# We don't care unless we're being told a transaction is now completed
|
||||||
|
@ -109,7 +108,7 @@ class Transactions(object):
|
||||||
r.hmset(f"tx.{txid}", stored_trade)
|
r.hmset(f"tx.{txid}", stored_trade)
|
||||||
# Check it's all been previously validated
|
# Check it's all been previously validated
|
||||||
if "valid" not in stored_trade:
|
if "valid" not in stored_trade:
|
||||||
self.log.error("Valid not in stored trade for {txid}, aborting.", txid=txid)
|
self.log.error(f"Valid not in stored trade for {txid}, aborting.")
|
||||||
return
|
return
|
||||||
if stored_trade["valid"] == "1":
|
if stored_trade["valid"] == "1":
|
||||||
# Make it invalid immediately, as we're going to release now
|
# Make it invalid immediately, as we're going to release now
|
||||||
|
@ -124,7 +123,7 @@ class Transactions(object):
|
||||||
else:
|
else:
|
||||||
txtype = inside["type"]
|
txtype = inside["type"]
|
||||||
if txtype == "card_payment":
|
if txtype == "card_payment":
|
||||||
self.log.info("Ignoring card payment: {id}", id=txid)
|
self.log.info(f"Ignoring card payment: {txid}")
|
||||||
return
|
return
|
||||||
|
|
||||||
state = inside["state"]
|
state = inside["state"]
|
||||||
|
@ -142,7 +141,7 @@ class Transactions(object):
|
||||||
|
|
||||||
amount = leg["amount"]
|
amount = leg["amount"]
|
||||||
if amount <= 0:
|
if amount <= 0:
|
||||||
self.log.info("Ignoring transaction with negative/zero amount: {id}", id=txid)
|
self.log.info(f"Ignoring transaction with negative/zero amount: {txid}")
|
||||||
return
|
return
|
||||||
currency = leg["currency"]
|
currency = leg["currency"]
|
||||||
description = leg["description"]
|
description = leg["description"]
|
||||||
|
@ -161,7 +160,7 @@ class Transactions(object):
|
||||||
"description": description,
|
"description": description,
|
||||||
"valid": 0, # All checks passed and we can release escrow?
|
"valid": 0, # All checks passed and we can release escrow?
|
||||||
}
|
}
|
||||||
self.log.info("Transaction processed: {formatted}", formatted=dumps(to_store, indent=2))
|
self.log.info(f"Transaction processed: {dumps(to_store, indent=2)}")
|
||||||
self.irc.sendmsg(f"AUTO Incoming transaction: {amount}{currency} ({reference}) - {state} - {description}")
|
self.irc.sendmsg(f"AUTO Incoming transaction: {amount}{currency} ({reference}) - {state} - {description}")
|
||||||
# Partial reference implementation
|
# Partial reference implementation
|
||||||
# Account for silly people not removing the default string
|
# Account for silly people not removing the default string
|
||||||
|
@ -172,7 +171,7 @@ class Transactions(object):
|
||||||
# Get all parts of the given reference split that match the existing references
|
# Get all parts of the given reference split that match the existing references
|
||||||
stored_trade_reference = set(existing_refs).intersection(set(ref_split))
|
stored_trade_reference = set(existing_refs).intersection(set(ref_split))
|
||||||
if len(stored_trade_reference) > 1:
|
if len(stored_trade_reference) > 1:
|
||||||
self.log.error("Multiple references valid for TXID {txid}: {reference}", txid=txid, reference=reference)
|
self.log.error(f"Multiple references valid for TXID {txid}: {reference}")
|
||||||
self.irc.sendmsg(f"Multiple references valid for TXID {txid}: {reference}")
|
self.irc.sendmsg(f"Multiple references valid for TXID {txid}: {reference}")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -181,21 +180,16 @@ class Transactions(object):
|
||||||
|
|
||||||
# Amount/currency lookup implementation
|
# Amount/currency lookup implementation
|
||||||
if not stored_trade_reference:
|
if not stored_trade_reference:
|
||||||
self.log.info(f"No reference in DB refs for {reference}", reference=reference)
|
self.log.info(f"No reference in DB refs for {reference}")
|
||||||
self.irc.sendmsg(f"No reference in DB refs for {reference}")
|
self.irc.sendmsg(f"No reference in DB refs for {reference}")
|
||||||
# Try checking just amount and currency, as some people (usually people buying small amounts)
|
# Try checking just amount and currency, as some people (usually people buying small amounts)
|
||||||
# are unable to put in a reference properly.
|
# are unable to put in a reference properly.
|
||||||
|
|
||||||
self.log.info("Checking against amount and currency for TXID {txid}", txid=txid)
|
self.log.info(f"Checking against amount and currency for TXID {txid}")
|
||||||
self.irc.sendmsg(f"Checking against amount and currency for TXID {txid}")
|
self.irc.sendmsg(f"Checking against amount and currency for TXID {txid}")
|
||||||
stored_trade = self.find_trade(txid, currency, amount)
|
stored_trade = self.find_trade(txid, currency, amount)
|
||||||
if not stored_trade:
|
if not stored_trade:
|
||||||
self.log.info(
|
self.log.info(f"Failed to get reference by amount and currency: {txid} {currency} {amount}")
|
||||||
"Failed to get reference by amount and currency: {txid} {currency} {amount}",
|
|
||||||
txid=txid,
|
|
||||||
currency=currency,
|
|
||||||
amount=amount,
|
|
||||||
)
|
|
||||||
self.irc.sendmsg(f"Failed to get reference by amount and currency: {txid} {currency} {amount}")
|
self.irc.sendmsg(f"Failed to get reference by amount and currency: {txid} {currency} {amount}")
|
||||||
return
|
return
|
||||||
if currency == "USD":
|
if currency == "USD":
|
||||||
|
@ -215,7 +209,7 @@ class Transactions(object):
|
||||||
if not stored_trade:
|
if not stored_trade:
|
||||||
stored_trade = self.get_ref(stored_trade_reference.pop())
|
stored_trade = self.get_ref(stored_trade_reference.pop())
|
||||||
if not stored_trade:
|
if not stored_trade:
|
||||||
self.log.info("No reference in DB for {reference}", reference=reference)
|
self.log.info(f"No reference in DB for {reference}")
|
||||||
self.irc.sendmsg(f"No reference in DB for {reference}")
|
self.irc.sendmsg(f"No reference in DB for {reference}")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -224,11 +218,7 @@ class Transactions(object):
|
||||||
|
|
||||||
# Make sure it was sent in the expected currency
|
# Make sure it was sent in the expected currency
|
||||||
if not stored_trade["currency"] == currency:
|
if not stored_trade["currency"] == currency:
|
||||||
self.log.info(
|
self.log.info(f"Currency mismatch, Agora: {stored_trade['currency']} / Sink: {currency}")
|
||||||
"Currency mismatch, Agora: {currency_agora} / Sink: {currency}",
|
|
||||||
currency_agora=stored_trade["currency"],
|
|
||||||
currency=currency,
|
|
||||||
)
|
|
||||||
self.irc.sendmsg(f"Currency mismatch, Agora: {stored_trade['currency']} / Sink: {currency}")
|
self.irc.sendmsg(f"Currency mismatch, Agora: {stored_trade['currency']} / Sink: {currency}")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -238,19 +228,10 @@ class Transactions(object):
|
||||||
return
|
return
|
||||||
# If the amount does not match exactly, get the min and max values for our given acceptable margins for trades
|
# If the amount does not match exactly, get the min and max values for our given acceptable margins for trades
|
||||||
min_amount, max_amount = self.money.get_acceptable_margins(currency, stored_trade["amount"])
|
min_amount, max_amount = self.money.get_acceptable_margins(currency, stored_trade["amount"])
|
||||||
self.log.info(
|
self.log.info(f"Amount does not match exactly, trying with margins: min: {min_amount} / max: {max_amount}")
|
||||||
"Amount does not match exactly, trying with margins: min: {min_amount} / max: {max_amount}",
|
|
||||||
min_amount=min_amount,
|
|
||||||
max_amount=max_amount,
|
|
||||||
)
|
|
||||||
self.irc.sendmsg(f"Amount does not match exactly, trying with margins: min: {min_amount} / max: {max_amount}")
|
self.irc.sendmsg(f"Amount does not match exactly, trying with margins: min: {min_amount} / max: {max_amount}")
|
||||||
if not min_amount < amount < max_amount:
|
if not min_amount < amount < max_amount:
|
||||||
self.log.info(
|
self.log.info("Amount mismatch - not in margins: {stored_trade['amount']} (min: {min_amount} / max: {max_amount}")
|
||||||
"Amount mismatch - not in margins: {amount} (min: {min_amount} / max: {max_amount}",
|
|
||||||
amount=stored_trade["amount"],
|
|
||||||
min_amount=min_amount,
|
|
||||||
max_amount=max_amount,
|
|
||||||
)
|
|
||||||
self.irc.sendmsg(f"Amount mismatch - not in margins: {stored_trade['amount']} (min: {min_amount} / max: {max_amount}")
|
self.irc.sendmsg(f"Amount mismatch - not in margins: {stored_trade['amount']} (min: {min_amount} / max: {max_amount}")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -260,7 +241,7 @@ class Transactions(object):
|
||||||
# Store the trade ID so we can release it easily
|
# Store the trade ID so we can release it easily
|
||||||
to_store["trade_id"] = stored_trade["id"]
|
to_store["trade_id"] = stored_trade["id"]
|
||||||
if not state == "completed":
|
if not state == "completed":
|
||||||
self.log.info("Storing incomplete trade: {id}", id=txid)
|
self.log.info(f"Storing incomplete trade: {txid}")
|
||||||
r.hmset(f"tx.{txid}", to_store)
|
r.hmset(f"tx.{txid}", to_store)
|
||||||
# Don't procees further if state is not "completed"
|
# Don't procees further if state is not "completed"
|
||||||
return
|
return
|
||||||
|
@ -270,7 +251,7 @@ class Transactions(object):
|
||||||
self.ux.notify.notify_complete_trade(amount, currency)
|
self.ux.notify.notify_complete_trade(amount, currency)
|
||||||
|
|
||||||
def release_funds(self, trade_id, reference):
|
def release_funds(self, trade_id, reference):
|
||||||
self.log.info("All checks passed, releasing funds for {trade_id} {reference}", trade_id=trade_id, reference=reference)
|
self.log.info(f"All checks passed, releasing funds for {trade_id} {reference}")
|
||||||
self.irc.sendmsg(f"All checks passed, releasing funds for {trade_id} / {reference}")
|
self.irc.sendmsg(f"All checks passed, releasing funds for {trade_id} / {reference}")
|
||||||
rtrn = self.agora.release_funds(trade_id)
|
rtrn = self.agora.release_funds(trade_id)
|
||||||
self.agora.agora.contact_message_post(trade_id, "Thanks! Releasing now :)")
|
self.agora.agora.contact_message_post(trade_id, "Thanks! Releasing now :)")
|
||||||
|
@ -300,14 +281,14 @@ class Transactions(object):
|
||||||
"reference": reference,
|
"reference": reference,
|
||||||
"provider": provider,
|
"provider": provider,
|
||||||
}
|
}
|
||||||
self.log.info("Storing trade information: {info}", info=str(to_store))
|
self.log.info(f"Storing trade information: {str(to_store)}")
|
||||||
r.hmset(f"trade.{reference}", to_store)
|
r.hmset(f"trade.{reference}", to_store)
|
||||||
self.irc.sendmsg(f"Generated reference for {trade_id}: {reference}")
|
self.irc.sendmsg(f"Generated reference for {trade_id}: {reference}")
|
||||||
self.ux.notify.notify_new_trade(amount, currency)
|
self.ux.notify.notify_new_trade(amount, currency)
|
||||||
if settings.Agora.Send == "1":
|
if settings.Agora.Send == "1":
|
||||||
self.agora.agora.contact_message_post(trade_id, f"Hi! When sending the payment please use reference code: {reference}")
|
self.agora.agora.contact_message_post(trade_id, f"Hi! When sending the payment please use reference code: {reference}")
|
||||||
if existing_ref:
|
if existing_ref:
|
||||||
return convert(existing_ref)
|
return util.convert(existing_ref)
|
||||||
else:
|
else:
|
||||||
return reference
|
return reference
|
||||||
|
|
||||||
|
@ -332,7 +313,7 @@ class Transactions(object):
|
||||||
if stored_trade["currency"] == currency and float(stored_trade["amount"]) == float(amount):
|
if stored_trade["currency"] == currency and float(stored_trade["amount"]) == float(amount):
|
||||||
matching_refs.append(stored_trade)
|
matching_refs.append(stored_trade)
|
||||||
if len(matching_refs) != 1:
|
if len(matching_refs) != 1:
|
||||||
self.log.error("Find trade returned multiple results for TXID {txid}: {matching_refs}", txid=txid, matching_refs=matching_refs)
|
self.log.error(f"Find trade returned multiple results for TXID {txid}: {matching_refs}")
|
||||||
return False
|
return False
|
||||||
return matching_refs[0]
|
return matching_refs[0]
|
||||||
|
|
||||||
|
@ -346,7 +327,7 @@ class Transactions(object):
|
||||||
ref_keys = r.keys("trade.*.reference")
|
ref_keys = r.keys("trade.*.reference")
|
||||||
for key in ref_keys:
|
for key in ref_keys:
|
||||||
references.append(r.get(key))
|
references.append(r.get(key))
|
||||||
return convert(references)
|
return util.convert(references)
|
||||||
|
|
||||||
def get_ref_map(self):
|
def get_ref_map(self):
|
||||||
"""
|
"""
|
||||||
|
@ -357,9 +338,9 @@ class Transactions(object):
|
||||||
references = {}
|
references = {}
|
||||||
ref_keys = r.keys("trade.*.reference")
|
ref_keys = r.keys("trade.*.reference")
|
||||||
for key in ref_keys:
|
for key in ref_keys:
|
||||||
tx = convert(key).split(".")[1]
|
tx = util.convert(key).split(".")[1]
|
||||||
references[tx] = r.get(key)
|
references[tx] = r.get(key)
|
||||||
return convert(references)
|
return util.convert(references)
|
||||||
|
|
||||||
def get_ref(self, reference):
|
def get_ref(self, reference):
|
||||||
"""
|
"""
|
||||||
|
@ -370,7 +351,7 @@ class Transactions(object):
|
||||||
:rtype: dict
|
:rtype: dict
|
||||||
"""
|
"""
|
||||||
ref_data = r.hgetall(f"trade.{reference}")
|
ref_data = r.hgetall(f"trade.{reference}")
|
||||||
ref_data = convert(ref_data)
|
ref_data = util.convert(ref_data)
|
||||||
if not ref_data:
|
if not ref_data:
|
||||||
return False
|
return False
|
||||||
return ref_data
|
return ref_data
|
||||||
|
@ -394,7 +375,7 @@ class Transactions(object):
|
||||||
"""
|
"""
|
||||||
for tx, reference in self.get_ref_map().items():
|
for tx, reference in self.get_ref_map().items():
|
||||||
if reference not in references:
|
if reference not in references:
|
||||||
self.log.info("Archiving trade reference: {reference} / TX: {tx}", reference=reference, tx=tx)
|
self.log.info(f"Archiving trade reference: {reference} / TX: {tx}")
|
||||||
r.rename(f"trade.{tx}.reference", f"archive.trade.{tx}.reference")
|
r.rename(f"trade.{tx}.reference", f"archive.trade.{tx}.reference")
|
||||||
r.rename(f"trade.{reference}", f"archive.trade.{reference}")
|
r.rename(f"trade.{reference}", f"archive.trade.{reference}")
|
||||||
|
|
||||||
|
@ -408,7 +389,7 @@ class Transactions(object):
|
||||||
"""
|
"""
|
||||||
refs = self.get_refs()
|
refs = self.get_refs()
|
||||||
for reference in refs:
|
for reference in refs:
|
||||||
ref_data = convert(r.hgetall(f"trade.{reference}"))
|
ref_data = util.convert(r.hgetall(f"trade.{reference}"))
|
||||||
if not ref_data:
|
if not ref_data:
|
||||||
continue
|
continue
|
||||||
if ref_data["id"] == tx:
|
if ref_data["id"] == tx:
|
||||||
|
@ -422,7 +403,7 @@ class Transactions(object):
|
||||||
:return: trade ID
|
:return: trade ID
|
||||||
:rtype: string
|
:rtype: string
|
||||||
"""
|
"""
|
||||||
ref_data = convert(r.hgetall(f"trade.{reference}"))
|
ref_data = util.convert(r.hgetall(f"trade.{reference}"))
|
||||||
if not ref_data:
|
if not ref_data:
|
||||||
return False
|
return False
|
||||||
return ref_data["id"]
|
return ref_data["id"]
|
||||||
|
@ -448,12 +429,12 @@ class Transactions(object):
|
||||||
# Get the BTC -> USD exchange rate
|
# Get the BTC -> USD exchange rate
|
||||||
btc_usd = self.agora.cg.get_price(ids="bitcoin", vs_currencies=["USD"])
|
btc_usd = self.agora.cg.get_price(ids="bitcoin", vs_currencies=["USD"])
|
||||||
|
|
||||||
# Convert the Agora XMR total to USD
|
|
||||||
total_usd_agora_xmr = float(total_xmr_agora) * xmr_usd["monero"]["usd"]
|
|
||||||
|
|
||||||
# Convert the Agora BTC total to USD
|
# Convert the Agora BTC total to USD
|
||||||
total_usd_agora_btc = float(total_btc_agora) * btc_usd["bitcoin"]["usd"]
|
total_usd_agora_btc = float(total_btc_agora) * btc_usd["bitcoin"]["usd"]
|
||||||
|
|
||||||
|
# Convert the Agora XMR total to USD
|
||||||
|
total_usd_agora_xmr = float(total_xmr_agora) * xmr_usd["monero"]["usd"]
|
||||||
|
|
||||||
# Add it all up
|
# Add it all up
|
||||||
total_usd_agora = total_usd_agora_xmr + total_usd_agora_btc
|
total_usd_agora = total_usd_agora_xmr + total_usd_agora_btc
|
||||||
# total_usd = total_usd_agora + total_usd_revolut
|
# total_usd = total_usd_agora + total_usd_revolut
|
||||||
|
|
|
@ -1,11 +1,98 @@
|
||||||
# Twisted/Klein imports
|
|
||||||
from twisted.logger import Logger
|
|
||||||
|
|
||||||
# Other library imports
|
# Other library imports
|
||||||
from httpx import ReadTimeout, ReadError, RemoteProtocolError
|
from httpx import ReadTimeout, ReadError, RemoteProtocolError
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import logging
|
||||||
|
|
||||||
log = Logger("util.global")
|
log = logging.getLogger("util")
|
||||||
|
|
||||||
|
|
||||||
|
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
|
||||||
|
|
||||||
|
# The background is set with 40 plus the number of the color, and the foreground with 30
|
||||||
|
|
||||||
|
# These are the sequences need to get colored ouput
|
||||||
|
RESET_SEQ = "\033[0m"
|
||||||
|
COLOR_SEQ = "\033[1;%dm"
|
||||||
|
BOLD_SEQ = "\033[1m"
|
||||||
|
|
||||||
|
|
||||||
|
def formatter_message(message, use_color=True):
|
||||||
|
if use_color:
|
||||||
|
message = message.replace("$RESET", RESET_SEQ).replace("$BOLD", BOLD_SEQ)
|
||||||
|
else:
|
||||||
|
message = message.replace("$RESET", "").replace("$BOLD", "")
|
||||||
|
return message
|
||||||
|
|
||||||
|
|
||||||
|
COLORS = {"WARNING": YELLOW, "INFO": WHITE, "DEBUG": BLUE, "CRITICAL": YELLOW, "ERROR": RED}
|
||||||
|
|
||||||
|
|
||||||
|
class ColoredFormatter(logging.Formatter):
|
||||||
|
def __init__(self, msg, use_color=True):
|
||||||
|
logging.Formatter.__init__(self, msg)
|
||||||
|
self.use_color = use_color
|
||||||
|
|
||||||
|
def format(self, record):
|
||||||
|
levelname = record.levelname
|
||||||
|
if self.use_color and levelname in COLORS:
|
||||||
|
levelname_color = COLOR_SEQ % (30 + COLORS[levelname]) + levelname + RESET_SEQ
|
||||||
|
record.levelname = levelname_color
|
||||||
|
return logging.Formatter.format(self, record)
|
||||||
|
|
||||||
|
|
||||||
|
def get_logger(name):
|
||||||
|
|
||||||
|
# Define the logging format
|
||||||
|
FORMAT = "%(asctime)s %(levelname)s $BOLD%(name)13s$RESET - %(message)s"
|
||||||
|
COLOR_FORMAT = formatter_message(FORMAT, True)
|
||||||
|
color_formatter = ColoredFormatter(COLOR_FORMAT)
|
||||||
|
# formatter = logging.Formatter(
|
||||||
|
|
||||||
|
# Why is this so complicated?
|
||||||
|
ch = logging.StreamHandler()
|
||||||
|
ch.setLevel(logging.DEBUG)
|
||||||
|
# ch.setFormatter(formatter)
|
||||||
|
ch.setFormatter(color_formatter)
|
||||||
|
|
||||||
|
# Define the logger on the base class
|
||||||
|
log = logging.getLogger(name)
|
||||||
|
|
||||||
|
# Add the handler and stop it being silly and printing everything twice
|
||||||
|
log.addHandler(ch)
|
||||||
|
log.propagate = False
|
||||||
|
return log
|
||||||
|
|
||||||
|
|
||||||
|
class ColoredLogger(logging.Logger):
|
||||||
|
FORMAT = "[$BOLD%(name)-20s$RESET][%(levelname)-18s] %(message)s ($BOLD%(filename)s$RESET:%(lineno)d)"
|
||||||
|
COLOR_FORMAT = formatter_message(FORMAT, True)
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
logging.Logger.__init__(self, name, logging.DEBUG)
|
||||||
|
|
||||||
|
color_formatter = ColoredFormatter(self.COLOR_FORMAT)
|
||||||
|
|
||||||
|
console = logging.StreamHandler()
|
||||||
|
console.setFormatter(color_formatter)
|
||||||
|
|
||||||
|
self.addHandler(console)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
class Base(object):
|
||||||
|
def __init__(self):
|
||||||
|
name = self.__class__.__name__
|
||||||
|
|
||||||
|
# Set up all the logging stuff
|
||||||
|
self._setup_logger(name)
|
||||||
|
|
||||||
|
self.log.info("Class initialised")
|
||||||
|
|
||||||
|
def _setup_logger(self, name):
|
||||||
|
"""
|
||||||
|
Set up the logging handlers.
|
||||||
|
"""
|
||||||
|
self.log = get_logger(name)
|
||||||
|
|
||||||
|
|
||||||
def xmerge_attrs(init_map):
|
def xmerge_attrs(init_map):
|
||||||
|
@ -78,10 +165,10 @@ def handle_exceptions(func):
|
||||||
if "error_code" in rtrn["response"]["error"]:
|
if "error_code" in rtrn["response"]["error"]:
|
||||||
code = rtrn["response"]["error"]["error_code"]
|
code = rtrn["response"]["error"]["error_code"]
|
||||||
if not code == 136:
|
if not code == 136:
|
||||||
log.error("API error: {code}", code=code)
|
log.error(f"API error: {code}")
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
log.error("API error: {code}", code=rtrn["response"]["error"])
|
log.error(f"API error: {rtrn['response']['error']}")
|
||||||
return False
|
return False
|
||||||
return rtrn
|
return rtrn
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
# Twisted/Klein imports
|
|
||||||
from twisted.logger import Logger
|
|
||||||
|
|
||||||
# Other library imports
|
# Other library imports
|
||||||
# import requests
|
# import requests
|
||||||
# from json import dumps
|
# from json import dumps
|
||||||
|
@ -20,7 +17,7 @@ class UX(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.log = Logger("ux")
|
super().__init__()
|
||||||
|
|
||||||
self.irc = ux.irc.bot()
|
self.irc = ux.irc.bot()
|
||||||
self.notify = ux.notify.Notify()
|
self.notify = ux.notify.Notify()
|
||||||
|
|
|
@ -447,5 +447,32 @@ class IRCCommands(object):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def run(cmd, spl, length, authed, msg, agora, tx, ux):
|
def run(cmd, spl, length, authed, msg, agora, tx, ux):
|
||||||
auth_url = agora.truelayer.create_auth_url()
|
auth_url = tx.truelayer.create_auth_url()
|
||||||
msg(f"Auth URL: {auth_url}")
|
msg(f"Auth URL: {auth_url}")
|
||||||
|
|
||||||
|
class accounts(object):
|
||||||
|
name = "accounts"
|
||||||
|
authed = True
|
||||||
|
helptext = "Get a list of acccounts."
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def run(cmd, spl, length, authed, msg, agora, tx, ux):
|
||||||
|
accounts = tx.sinks.truelayer.get_accounts()
|
||||||
|
msg(dumps(accounts))
|
||||||
|
|
||||||
|
class transactions(object):
|
||||||
|
name = "transactions"
|
||||||
|
authed = True
|
||||||
|
helptext = "Get a list of transactions. Usage: transactions <account_id>"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def run(cmd, spl, length, authed, msg, agora, tx, ux):
|
||||||
|
if length == 2:
|
||||||
|
account_id = spl[1]
|
||||||
|
transactions = tx.sinks.truelayer.get_transactions(account_id)
|
||||||
|
for transaction in transactions:
|
||||||
|
timestamp = transaction["timestamp"]
|
||||||
|
amount = transaction["amount"]
|
||||||
|
currency = transaction["currency"]
|
||||||
|
recipient = transaction["counter_party_preferred_name"]
|
||||||
|
msg(f"{timestamp} {amount}{currency} {recipient}")
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
# Twisted/Klein imports
|
# Twisted/Klein imports
|
||||||
from twisted.logger import Logger
|
|
||||||
from twisted.words.protocols import irc
|
from twisted.words.protocols import irc
|
||||||
from twisted.internet import protocol, reactor, ssl
|
from twisted.internet import protocol, reactor, ssl
|
||||||
from twisted.internet.task import deferLater
|
from twisted.internet.task import deferLater
|
||||||
|
@ -7,6 +6,7 @@ from twisted.internet.task import deferLater
|
||||||
# Project imports
|
# Project imports
|
||||||
from settings import settings
|
from settings import settings
|
||||||
from ux.commands import IRCCommands
|
from ux.commands import IRCCommands
|
||||||
|
import util
|
||||||
|
|
||||||
|
|
||||||
class IRCBot(irc.IRCClient):
|
class IRCBot(irc.IRCClient):
|
||||||
|
@ -106,7 +106,7 @@ class IRCBot(irc.IRCClient):
|
||||||
Called when we have signed on to IRC.
|
Called when we have signed on to IRC.
|
||||||
Join our channel.
|
Join our channel.
|
||||||
"""
|
"""
|
||||||
self.log.info("Signed on as %s" % (self.nickname))
|
self.log.info(f"Signed on as {self.nickname}")
|
||||||
deferLater(reactor, 2, self.join, self.channel)
|
deferLater(reactor, 2, self.join, self.channel)
|
||||||
|
|
||||||
def joined(self, channel):
|
def joined(self, channel):
|
||||||
|
@ -118,7 +118,7 @@ class IRCBot(irc.IRCClient):
|
||||||
:type channel: string
|
:type channel: string
|
||||||
"""
|
"""
|
||||||
self.agora.setup_loop()
|
self.agora.setup_loop()
|
||||||
self.log.info("Joined channel %s" % (channel))
|
self.log.info(f"Joined channel {channel}")
|
||||||
|
|
||||||
def privmsg(self, user, channel, msg):
|
def privmsg(self, user, channel, msg):
|
||||||
"""
|
"""
|
||||||
|
@ -141,7 +141,7 @@ class IRCBot(irc.IRCClient):
|
||||||
|
|
||||||
ident = user.split("!")[1]
|
ident = user.split("!")[1]
|
||||||
ident = ident.split("@")[0]
|
ident = ident.split("@")[0]
|
||||||
self.log.info("(%s) %s: %s" % (channel, user, msg))
|
self.log.info(f"({channel}) {user}: {msg}")
|
||||||
if msg[0] == self.prefix:
|
if msg[0] == self.prefix:
|
||||||
if len(msg) > 1:
|
if len(msg) > 1:
|
||||||
if msg.split()[0] != "!":
|
if msg.split()[0] != "!":
|
||||||
|
@ -171,7 +171,8 @@ class IRCBot(irc.IRCClient):
|
||||||
|
|
||||||
class IRCBotFactory(protocol.ClientFactory):
|
class IRCBotFactory(protocol.ClientFactory):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.log = Logger("irc")
|
self.log = util.get_logger("IRC")
|
||||||
|
self.log.info("Class initialised")
|
||||||
|
|
||||||
def sendmsg(self, msg):
|
def sendmsg(self, msg):
|
||||||
"""
|
"""
|
||||||
|
@ -180,7 +181,7 @@ class IRCBotFactory(protocol.ClientFactory):
|
||||||
if self.client:
|
if self.client:
|
||||||
self.client.msg(self.client.channel, msg)
|
self.client.msg(self.client.channel, msg)
|
||||||
else:
|
else:
|
||||||
self.log.error("Trying to send a message without connected client: {msg}", msg=msg)
|
self.log.error(f"Trying to send a message without connected client: {msg}")
|
||||||
return
|
return
|
||||||
|
|
||||||
def buildProtocol(self, addr):
|
def buildProtocol(self, addr):
|
||||||
|
@ -189,6 +190,7 @@ class IRCBotFactory(protocol.ClientFactory):
|
||||||
Passes through the Agora instance to IRC.
|
Passes through the Agora instance to IRC.
|
||||||
:return: IRCBot Protocol instance
|
:return: IRCBot Protocol instance
|
||||||
"""
|
"""
|
||||||
|
# Pass through the logger
|
||||||
prcol = IRCBot(self.log)
|
prcol = IRCBot(self.log)
|
||||||
self.client = prcol
|
self.client = prcol
|
||||||
setattr(self.client, "agora", self.agora)
|
setattr(self.client, "agora", self.agora)
|
||||||
|
@ -205,7 +207,7 @@ class IRCBotFactory(protocol.ClientFactory):
|
||||||
:type connector: object
|
:type connector: object
|
||||||
:type reason: string
|
:type reason: string
|
||||||
"""
|
"""
|
||||||
self.log.error("Lost connection: {reason}, reconnecting", reason=reason)
|
self.log.error(f"Lost connection: {reason}, reconnecting")
|
||||||
connector.connect()
|
connector.connect()
|
||||||
|
|
||||||
def clientConnectionFailed(self, connector, reason):
|
def clientConnectionFailed(self, connector, reason):
|
||||||
|
@ -216,7 +218,7 @@ class IRCBotFactory(protocol.ClientFactory):
|
||||||
:type connector: object
|
:type connector: object
|
||||||
:type reason: string
|
:type reason: string
|
||||||
"""
|
"""
|
||||||
self.log.error("Could not connect: {reason}", reason=reason)
|
self.log.error(f"Could not connect: {reason}")
|
||||||
connector.connect()
|
connector.connect()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,16 @@
|
||||||
# Twisted/Klein imports
|
|
||||||
from twisted.logger import Logger
|
|
||||||
|
|
||||||
# Other library imports
|
# Other library imports
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
# Project imports
|
# Project imports
|
||||||
from settings import settings
|
from settings import settings
|
||||||
|
import util
|
||||||
|
|
||||||
|
|
||||||
class Notify(object):
|
class Notify(util.Base):
|
||||||
"""
|
"""
|
||||||
Class to handle more robust notifications.
|
Class to handle more robust notifications.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.log = Logger("notify")
|
|
||||||
|
|
||||||
def sendmsg(self, msg, title=None, priority=None, tags=None):
|
def sendmsg(self, msg, title=None, priority=None, tags=None):
|
||||||
headers = {"Title": "Bot"}
|
headers = {"Title": "Bot"}
|
||||||
if title:
|
if title:
|
||||||
|
|
Loading…
Reference in New Issue