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
|
||||
from twisted.logger import Logger
|
||||
from twisted.internet.task import LoopingCall
|
||||
from twisted.internet.threads import deferToThread
|
||||
|
||||
|
@ -16,10 +15,8 @@ from datetime import datetime
|
|||
from settings import settings
|
||||
import util
|
||||
|
||||
log = Logger("agora.global")
|
||||
|
||||
|
||||
class Agora(object):
|
||||
class Agora(util.Base):
|
||||
"""
|
||||
AgoraDesk API handler.
|
||||
"""
|
||||
|
@ -29,7 +26,7 @@ class Agora(object):
|
|||
Initialise the AgoraDesk and CurrencyRates APIs.
|
||||
Initialise the last_dash storage for detecting new trades.
|
||||
"""
|
||||
self.log = Logger("agora")
|
||||
super().__init__()
|
||||
self.agora = AgoraDesk(settings.Agora.Token)
|
||||
self.cr = CurrencyRates() # 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():
|
||||
return False
|
||||
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
|
||||
if dash["response"]["data"]["contact_count"] > 0:
|
||||
for contact in dash["response"]["data"]["contact_list"]:
|
||||
|
@ -166,7 +163,7 @@ class Agora(object):
|
|||
if not messages["success"]:
|
||||
return False
|
||||
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
|
||||
open_tx = self.tx.get_ref_map().keys()
|
||||
for message in messages["response"]["data"]["message_list"]:
|
||||
|
@ -429,20 +426,15 @@ class Agora(object):
|
|||
continue
|
||||
else:
|
||||
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
|
||||
if rtrn["response"]["error"]["error_code"] == 429:
|
||||
throttled += 1
|
||||
sleep_time = pow(throttled, float(settings.Agora.SleepExponent))
|
||||
self.log.info(
|
||||
"Throttled {x} times while updating {id}, sleeping for {sleep} seconds",
|
||||
x=throttled,
|
||||
id=ad_id,
|
||||
sleep=sleep_time,
|
||||
)
|
||||
self.log.info(f"Throttled {throttled} times while updating {ad_id}, sleeping for {sleep_time} seconds")
|
||||
# We're running in a thread, so this is fine
|
||||
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
|
||||
iterations += 1
|
||||
|
||||
|
@ -645,9 +637,7 @@ class Agora(object):
|
|||
|
||||
if not float(wallet_xmr) > profit_usd_in_xmr:
|
||||
# Not enough funds to withdraw
|
||||
self.log.error(
|
||||
"Not enough funds to withdraw {profit}, as wallet only contains {wallet}", profit=profit_usd_in_xmr, wallet=wallet_xmr
|
||||
)
|
||||
self.log.error(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
|
||||
|
||||
|
|
|
@ -12,6 +12,9 @@ from typing import Union
|
|||
import arrow
|
||||
import httpx
|
||||
|
||||
# Project imports
|
||||
import util
|
||||
|
||||
__author__ = "marvin8"
|
||||
__copyright__ = "(C) 2021 https://codeberg.org/MarvinsCryptoTools/agoradesk_py"
|
||||
__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.getLogger("requests.packages.urllib3").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/"
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
# Twisted/Klein imports
|
||||
from twisted.logger import Logger
|
||||
from twisted.internet import reactor
|
||||
from klein import Klein
|
||||
|
||||
|
@ -41,33 +40,20 @@ def cleanup(sig, frame):
|
|||
signal(SIGINT, cleanup) # Handle Ctrl-C and run the cleanup routine
|
||||
|
||||
|
||||
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):
|
||||
class WebApp(util.Base):
|
||||
"""
|
||||
Our Klein webapp.
|
||||
"""
|
||||
|
||||
app = Klein()
|
||||
|
||||
def __init__(self):
|
||||
self.log = Logger("webapp")
|
||||
|
||||
@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)
|
||||
self.log.error(f"Failed to parse JSON callback: {content}")
|
||||
return dumps(False)
|
||||
self.log.info("Callback received: {parsed}", parsed=parsed["data"]["id"])
|
||||
# self.tx.transaction(parsed)
|
||||
|
@ -76,19 +62,19 @@ class WebApp(object):
|
|||
# set up another connection to a bank
|
||||
@app.route("/signin", methods=["GET"])
|
||||
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>'
|
||||
|
||||
# endpoint called after we finish setting up a connection above
|
||||
@app.route("/callback-truelayer", methods=["POST"])
|
||||
def signin_callback(self, request):
|
||||
code = request.args[b"code"]
|
||||
self.truelayer.handle_authcode_received(code)
|
||||
self.sinks.truelayer.handle_authcode_received(code)
|
||||
return dumps(True)
|
||||
|
||||
@app.route("/accounts", methods=["GET"])
|
||||
def balance(self, request):
|
||||
accounts = self.truelayer.get_accounts()
|
||||
accounts = self.sinks.truelayer.get_accounts()
|
||||
return dumps(accounts, indent=2)
|
||||
|
||||
|
||||
|
|
|
@ -1,21 +1,16 @@
|
|||
# Twisted/Klein imports
|
||||
from twisted.logger import Logger
|
||||
|
||||
# Other library imports
|
||||
from json import loads
|
||||
|
||||
# Project imports
|
||||
from settings import settings
|
||||
import util
|
||||
|
||||
|
||||
class Markets(object):
|
||||
class Markets(util.Base):
|
||||
""" "
|
||||
Markets handler for generic market functions.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.log = Logger("markets")
|
||||
|
||||
def get_all_assets(self):
|
||||
assets = loads(settings.Agora.AssetList)
|
||||
return assets
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
# Twisted/Klein imports
|
||||
from twisted.logger import Logger
|
||||
|
||||
# Other library imports
|
||||
from pycoingecko import CoinGeckoAPI
|
||||
from forex_python.converter import CurrencyRates
|
||||
|
||||
# Project imports
|
||||
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.
|
||||
"""
|
||||
|
@ -20,7 +18,7 @@ class Money(object):
|
|||
Set the logger.
|
||||
Initialise the CoinGecko API.
|
||||
"""
|
||||
self.log = Logger("money")
|
||||
super().__init__()
|
||||
self.cr = CurrencyRates()
|
||||
self.cg = CoinGeckoAPI()
|
||||
|
||||
|
@ -41,6 +39,7 @@ class Money(object):
|
|||
price = float(ad[2])
|
||||
rate = round(price / base_currency_price, 2)
|
||||
ad.append(rate)
|
||||
# TODO: sort?
|
||||
return sorted(ads, key=lambda x: x[2])
|
||||
|
||||
def get_rates_all(self):
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
# Twisted/Klein imports
|
||||
from twisted.logger import Logger
|
||||
|
||||
# Other library imports
|
||||
# import requests
|
||||
# from json import dumps
|
||||
|
@ -10,15 +7,16 @@ from twisted.logger import Logger
|
|||
import sinks.fidor
|
||||
import sinks.nordigen
|
||||
import sinks.truelayer
|
||||
import util
|
||||
|
||||
|
||||
class Sinks(object):
|
||||
class Sinks(util.Base):
|
||||
"""
|
||||
Class to manage calls to various sinks.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.log = Logger("sinks")
|
||||
super().__init__()
|
||||
self.fidor = sinks.fidor.Fidor()
|
||||
self.nordigen = sinks.nordigen.Nordigen()
|
||||
self.truelayer = sinks.truelayer.TrueLayer()
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
# Twisted/Klein imports
|
||||
from twisted.logger import Logger
|
||||
|
||||
# Other library imports
|
||||
# import requests
|
||||
|
@ -7,16 +6,14 @@ from twisted.logger import Logger
|
|||
|
||||
# Project imports
|
||||
# from settings import settings
|
||||
import util
|
||||
|
||||
|
||||
class Fidor(object):
|
||||
class Fidor(util.Base):
|
||||
"""
|
||||
Class to manage calls to the Fidor API.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.log = Logger("fidor")
|
||||
|
||||
def authorize(self):
|
||||
"""
|
||||
Perform initial authorization against Fidor API.
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
# Twisted/Klein imports
|
||||
from twisted.logger import Logger
|
||||
|
||||
# Other library imports
|
||||
import requests
|
||||
from json import dumps
|
||||
|
@ -8,15 +5,16 @@ from simplejson.errors import JSONDecodeError
|
|||
|
||||
# Project imports
|
||||
from settings import settings
|
||||
import util
|
||||
|
||||
|
||||
class Nordigen(object):
|
||||
class Nordigen(util.Base):
|
||||
"""
|
||||
Class to manage calls to Open Banking APIs through Nordigen.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.log = Logger("nordigen")
|
||||
super().__init__()
|
||||
self.token = None
|
||||
self.get_access_token()
|
||||
|
||||
|
@ -36,11 +34,11 @@ class Nordigen(object):
|
|||
try:
|
||||
parsed = r.json()
|
||||
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
|
||||
if "access" in parsed:
|
||||
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):
|
||||
"""
|
||||
|
@ -58,7 +56,7 @@ class Nordigen(object):
|
|||
try:
|
||||
parsed = r.json()
|
||||
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
|
||||
new_list = []
|
||||
if filter_name:
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
# Twisted/Klein imports
|
||||
from twisted.logger import Logger
|
||||
from twisted.internet.task import LoopingCall
|
||||
|
||||
# Other library imports
|
||||
|
@ -10,15 +9,16 @@ import urllib
|
|||
|
||||
# Project imports
|
||||
from settings import settings
|
||||
import util
|
||||
|
||||
|
||||
class TrueLayer(object):
|
||||
class TrueLayer(util.Base):
|
||||
"""
|
||||
Class to manage calls to Open Banking APIs through TrueLayer.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.log = Logger("truelayer")
|
||||
super().__init__()
|
||||
self.token = None
|
||||
self.lc = LoopingCall(self.get_new_token)
|
||||
self.lc.start(int(settings.TrueLayer.RefreshSec))
|
||||
|
@ -58,7 +58,7 @@ class TrueLayer(object):
|
|||
settings.TrueLayer.AuthCode = authcode
|
||||
settings.write()
|
||||
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):
|
||||
"""
|
||||
|
@ -81,7 +81,7 @@ class TrueLayer(object):
|
|||
if r.status_code == 200:
|
||||
if "access_token" in parsed.keys():
|
||||
self.token = parsed["access_token"]
|
||||
self.log.info("Refreshed access token - TrueLayer")
|
||||
self.log.info("Refreshed access token")
|
||||
return True
|
||||
else:
|
||||
self.log.error(f"Token refresh didn't contain access token: {parsed}", parsed=parsed)
|
||||
|
@ -100,7 +100,25 @@ class TrueLayer(object):
|
|||
try:
|
||||
parsed = r.json()
|
||||
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 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
|
||||
from twisted.logger import Logger
|
||||
from twisted.internet.task import LoopingCall
|
||||
from twisted.internet.threads import deferToThread
|
||||
|
||||
|
@ -15,7 +14,7 @@ import logging
|
|||
# Project imports
|
||||
from settings import settings
|
||||
from db import r
|
||||
from util import convert
|
||||
import util
|
||||
|
||||
# TODO: secure ES traffic properly
|
||||
urllib3.disable_warnings()
|
||||
|
@ -26,7 +25,7 @@ tracer = logging.getLogger("elastic_transport.transport")
|
|||
tracer.setLevel(logging.CRITICAL)
|
||||
|
||||
|
||||
class Transactions(object):
|
||||
class Transactions(util.Base):
|
||||
"""
|
||||
Handler class for incoming Revolut transactions.
|
||||
"""
|
||||
|
@ -36,7 +35,7 @@ class Transactions(object):
|
|||
Initialise the Transaction object.
|
||||
Set the logger.
|
||||
"""
|
||||
self.log = Logger("transactions")
|
||||
super().__init__()
|
||||
if settings.ES.Enabled == "1":
|
||||
self.es = Elasticsearch(
|
||||
f"https://{settings.ES.Host}:9200",
|
||||
|
@ -89,10 +88,10 @@ class Transactions(object):
|
|||
# stored_trade here is actually TX
|
||||
stored_trade = r.hgetall(f"tx.{txid}")
|
||||
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
|
||||
print("BEFORE CONVERT STORED TRADE", stored_trade)
|
||||
stored_trade = convert(stored_trade)
|
||||
stored_trade = util.convert(stored_trade)
|
||||
if "old_state" in inside:
|
||||
if "new_state" in inside:
|
||||
# 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)
|
||||
# Check it's all been previously validated
|
||||
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
|
||||
if stored_trade["valid"] == "1":
|
||||
# Make it invalid immediately, as we're going to release now
|
||||
|
@ -124,7 +123,7 @@ class Transactions(object):
|
|||
else:
|
||||
txtype = inside["type"]
|
||||
if txtype == "card_payment":
|
||||
self.log.info("Ignoring card payment: {id}", id=txid)
|
||||
self.log.info(f"Ignoring card payment: {txid}")
|
||||
return
|
||||
|
||||
state = inside["state"]
|
||||
|
@ -142,7 +141,7 @@ class Transactions(object):
|
|||
|
||||
amount = leg["amount"]
|
||||
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
|
||||
currency = leg["currency"]
|
||||
description = leg["description"]
|
||||
|
@ -161,7 +160,7 @@ class Transactions(object):
|
|||
"description": description,
|
||||
"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}")
|
||||
# Partial reference implementation
|
||||
# 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
|
||||
stored_trade_reference = set(existing_refs).intersection(set(ref_split))
|
||||
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}")
|
||||
return
|
||||
|
||||
|
@ -181,21 +180,16 @@ class Transactions(object):
|
|||
|
||||
# Amount/currency lookup implementation
|
||||
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}")
|
||||
# Try checking just amount and currency, as some people (usually people buying small amounts)
|
||||
# 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}")
|
||||
stored_trade = self.find_trade(txid, currency, amount)
|
||||
if not stored_trade:
|
||||
self.log.info(
|
||||
"Failed to get reference by amount and currency: {txid} {currency} {amount}",
|
||||
txid=txid,
|
||||
currency=currency,
|
||||
amount=amount,
|
||||
)
|
||||
self.log.info(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
|
||||
if currency == "USD":
|
||||
|
@ -215,7 +209,7 @@ class Transactions(object):
|
|||
if not stored_trade:
|
||||
stored_trade = self.get_ref(stored_trade_reference.pop())
|
||||
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}")
|
||||
return
|
||||
|
||||
|
@ -224,11 +218,7 @@ class Transactions(object):
|
|||
|
||||
# Make sure it was sent in the expected currency
|
||||
if not stored_trade["currency"] == currency:
|
||||
self.log.info(
|
||||
"Currency mismatch, Agora: {currency_agora} / Sink: {currency}",
|
||||
currency_agora=stored_trade["currency"],
|
||||
currency=currency,
|
||||
)
|
||||
self.log.info(f"Currency mismatch, Agora: {stored_trade['currency']} / Sink: {currency}")
|
||||
self.irc.sendmsg(f"Currency mismatch, Agora: {stored_trade['currency']} / Sink: {currency}")
|
||||
return
|
||||
|
||||
|
@ -238,19 +228,10 @@ class Transactions(object):
|
|||
return
|
||||
# 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"])
|
||||
self.log.info(
|
||||
"Amount does not match exactly, trying with margins: min: {min_amount} / max: {max_amount}",
|
||||
min_amount=min_amount,
|
||||
max_amount=max_amount,
|
||||
)
|
||||
self.log.info(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:
|
||||
self.log.info(
|
||||
"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.log.info("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
|
||||
|
||||
|
@ -260,7 +241,7 @@ class Transactions(object):
|
|||
# Store the trade ID so we can release it easily
|
||||
to_store["trade_id"] = stored_trade["id"]
|
||||
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)
|
||||
# Don't procees further if state is not "completed"
|
||||
return
|
||||
|
@ -270,7 +251,7 @@ class Transactions(object):
|
|||
self.ux.notify.notify_complete_trade(amount, currency)
|
||||
|
||||
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}")
|
||||
rtrn = self.agora.release_funds(trade_id)
|
||||
self.agora.agora.contact_message_post(trade_id, "Thanks! Releasing now :)")
|
||||
|
@ -300,14 +281,14 @@ class Transactions(object):
|
|||
"reference": reference,
|
||||
"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)
|
||||
self.irc.sendmsg(f"Generated reference for {trade_id}: {reference}")
|
||||
self.ux.notify.notify_new_trade(amount, currency)
|
||||
if settings.Agora.Send == "1":
|
||||
self.agora.agora.contact_message_post(trade_id, f"Hi! When sending the payment please use reference code: {reference}")
|
||||
if existing_ref:
|
||||
return convert(existing_ref)
|
||||
return util.convert(existing_ref)
|
||||
else:
|
||||
return reference
|
||||
|
||||
|
@ -332,7 +313,7 @@ class Transactions(object):
|
|||
if stored_trade["currency"] == currency and float(stored_trade["amount"]) == float(amount):
|
||||
matching_refs.append(stored_trade)
|
||||
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 matching_refs[0]
|
||||
|
||||
|
@ -346,7 +327,7 @@ class Transactions(object):
|
|||
ref_keys = r.keys("trade.*.reference")
|
||||
for key in ref_keys:
|
||||
references.append(r.get(key))
|
||||
return convert(references)
|
||||
return util.convert(references)
|
||||
|
||||
def get_ref_map(self):
|
||||
"""
|
||||
|
@ -357,9 +338,9 @@ class Transactions(object):
|
|||
references = {}
|
||||
ref_keys = r.keys("trade.*.reference")
|
||||
for key in ref_keys:
|
||||
tx = convert(key).split(".")[1]
|
||||
tx = util.convert(key).split(".")[1]
|
||||
references[tx] = r.get(key)
|
||||
return convert(references)
|
||||
return util.convert(references)
|
||||
|
||||
def get_ref(self, reference):
|
||||
"""
|
||||
|
@ -370,7 +351,7 @@ class Transactions(object):
|
|||
:rtype: dict
|
||||
"""
|
||||
ref_data = r.hgetall(f"trade.{reference}")
|
||||
ref_data = convert(ref_data)
|
||||
ref_data = util.convert(ref_data)
|
||||
if not ref_data:
|
||||
return False
|
||||
return ref_data
|
||||
|
@ -394,7 +375,7 @@ class Transactions(object):
|
|||
"""
|
||||
for tx, reference in self.get_ref_map().items():
|
||||
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.{reference}", f"archive.trade.{reference}")
|
||||
|
||||
|
@ -408,7 +389,7 @@ class Transactions(object):
|
|||
"""
|
||||
refs = self.get_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:
|
||||
continue
|
||||
if ref_data["id"] == tx:
|
||||
|
@ -422,7 +403,7 @@ class Transactions(object):
|
|||
:return: trade ID
|
||||
:rtype: string
|
||||
"""
|
||||
ref_data = convert(r.hgetall(f"trade.{reference}"))
|
||||
ref_data = util.convert(r.hgetall(f"trade.{reference}"))
|
||||
if not ref_data:
|
||||
return False
|
||||
return ref_data["id"]
|
||||
|
@ -448,12 +429,12 @@ class Transactions(object):
|
|||
# Get the BTC -> USD exchange rate
|
||||
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
|
||||
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
|
||||
total_usd_agora = total_usd_agora_xmr + total_usd_agora_btc
|
||||
# total_usd = total_usd_agora + total_usd_revolut
|
||||
|
|
|
@ -1,11 +1,98 @@
|
|||
# Twisted/Klein imports
|
||||
from twisted.logger import Logger
|
||||
|
||||
# Other library imports
|
||||
from httpx import ReadTimeout, ReadError, RemoteProtocolError
|
||||
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):
|
||||
|
@ -78,10 +165,10 @@ def handle_exceptions(func):
|
|||
if "error_code" in rtrn["response"]["error"]:
|
||||
code = rtrn["response"]["error"]["error_code"]
|
||||
if not code == 136:
|
||||
log.error("API error: {code}", code=code)
|
||||
log.error(f"API error: {code}")
|
||||
return False
|
||||
else:
|
||||
log.error("API error: {code}", code=rtrn["response"]["error"])
|
||||
log.error(f"API error: {rtrn['response']['error']}")
|
||||
return False
|
||||
return rtrn
|
||||
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
# Twisted/Klein imports
|
||||
from twisted.logger import Logger
|
||||
|
||||
# Other library imports
|
||||
# import requests
|
||||
# from json import dumps
|
||||
|
@ -20,7 +17,7 @@ class UX(object):
|
|||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.log = Logger("ux")
|
||||
super().__init__()
|
||||
|
||||
self.irc = ux.irc.bot()
|
||||
self.notify = ux.notify.Notify()
|
||||
|
|
|
@ -447,5 +447,32 @@ class IRCCommands(object):
|
|||
|
||||
@staticmethod
|
||||
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}")
|
||||
|
||||
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
|
||||
from twisted.logger import Logger
|
||||
from twisted.words.protocols import irc
|
||||
from twisted.internet import protocol, reactor, ssl
|
||||
from twisted.internet.task import deferLater
|
||||
|
@ -7,6 +6,7 @@ from twisted.internet.task import deferLater
|
|||
# Project imports
|
||||
from settings import settings
|
||||
from ux.commands import IRCCommands
|
||||
import util
|
||||
|
||||
|
||||
class IRCBot(irc.IRCClient):
|
||||
|
@ -106,7 +106,7 @@ class IRCBot(irc.IRCClient):
|
|||
Called when we have signed on to IRC.
|
||||
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)
|
||||
|
||||
def joined(self, channel):
|
||||
|
@ -118,7 +118,7 @@ class IRCBot(irc.IRCClient):
|
|||
:type channel: string
|
||||
"""
|
||||
self.agora.setup_loop()
|
||||
self.log.info("Joined channel %s" % (channel))
|
||||
self.log.info(f"Joined channel {channel}")
|
||||
|
||||
def privmsg(self, user, channel, msg):
|
||||
"""
|
||||
|
@ -141,7 +141,7 @@ class IRCBot(irc.IRCClient):
|
|||
|
||||
ident = user.split("!")[1]
|
||||
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 len(msg) > 1:
|
||||
if msg.split()[0] != "!":
|
||||
|
@ -171,7 +171,8 @@ class IRCBot(irc.IRCClient):
|
|||
|
||||
class IRCBotFactory(protocol.ClientFactory):
|
||||
def __init__(self):
|
||||
self.log = Logger("irc")
|
||||
self.log = util.get_logger("IRC")
|
||||
self.log.info("Class initialised")
|
||||
|
||||
def sendmsg(self, msg):
|
||||
"""
|
||||
|
@ -180,7 +181,7 @@ class IRCBotFactory(protocol.ClientFactory):
|
|||
if self.client:
|
||||
self.client.msg(self.client.channel, msg)
|
||||
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
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
|
@ -189,6 +190,7 @@ class IRCBotFactory(protocol.ClientFactory):
|
|||
Passes through the Agora instance to IRC.
|
||||
:return: IRCBot Protocol instance
|
||||
"""
|
||||
# Pass through the logger
|
||||
prcol = IRCBot(self.log)
|
||||
self.client = prcol
|
||||
setattr(self.client, "agora", self.agora)
|
||||
|
@ -205,7 +207,7 @@ class IRCBotFactory(protocol.ClientFactory):
|
|||
:type connector: object
|
||||
:type reason: string
|
||||
"""
|
||||
self.log.error("Lost connection: {reason}, reconnecting", reason=reason)
|
||||
self.log.error(f"Lost connection: {reason}, reconnecting")
|
||||
connector.connect()
|
||||
|
||||
def clientConnectionFailed(self, connector, reason):
|
||||
|
@ -216,7 +218,7 @@ class IRCBotFactory(protocol.ClientFactory):
|
|||
:type connector: object
|
||||
:type reason: string
|
||||
"""
|
||||
self.log.error("Could not connect: {reason}", reason=reason)
|
||||
self.log.error(f"Could not connect: {reason}")
|
||||
connector.connect()
|
||||
|
||||
|
||||
|
|
|
@ -1,21 +1,16 @@
|
|||
# Twisted/Klein imports
|
||||
from twisted.logger import Logger
|
||||
|
||||
# Other library imports
|
||||
import requests
|
||||
|
||||
# Project imports
|
||||
from settings import settings
|
||||
import util
|
||||
|
||||
|
||||
class Notify(object):
|
||||
class Notify(util.Base):
|
||||
"""
|
||||
Class to handle more robust notifications.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.log = Logger("notify")
|
||||
|
||||
def sendmsg(self, msg, title=None, priority=None, tags=None):
|
||||
headers = {"Title": "Bot"}
|
||||
if title:
|
||||
|
|
Loading…
Reference in New Issue