Libraries refactor and add some sinks #4
|
@ -7,42 +7,16 @@ from twisted.internet.threads import deferToThread
|
||||||
from json import loads
|
from json import loads
|
||||||
from forex_python.converter import CurrencyRates
|
from forex_python.converter import CurrencyRates
|
||||||
from agoradesk_py import AgoraDesk
|
from agoradesk_py import AgoraDesk
|
||||||
from httpx import ReadTimeout, ReadError, RemoteProtocolError
|
from pycoingecko import CoinGeckoAPI # TODO: remove this import and defer to money
|
||||||
from pycoingecko import CoinGeckoAPI
|
|
||||||
from datetime import datetime
|
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from pyotp import TOTP
|
from pyotp import TOTP
|
||||||
|
|
||||||
# Project imports
|
# Project imports
|
||||||
from settings import settings
|
from settings import settings
|
||||||
|
import util
|
||||||
|
|
||||||
log = Logger("agora.global")
|
log = Logger("agora.global")
|
||||||
|
|
||||||
# TODO: move to utils
|
|
||||||
|
|
||||||
|
|
||||||
def handle_exceptions(func):
|
|
||||||
def inner_function(*args, **kwargs):
|
|
||||||
try:
|
|
||||||
rtrn = func(*args, **kwargs)
|
|
||||||
except (ReadTimeout, ReadError, RemoteProtocolError):
|
|
||||||
return False
|
|
||||||
if isinstance(rtrn, dict):
|
|
||||||
if "success" in rtrn:
|
|
||||||
if "message" in rtrn:
|
|
||||||
if not rtrn["success"] and rtrn["message"] == "API ERROR":
|
|
||||||
if "error_code" in rtrn["response"]["error"]:
|
|
||||||
code = rtrn["response"]["error"]["error_code"]
|
|
||||||
if not code == 136:
|
|
||||||
log.error("API error: {code}", code=code)
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
log.error("API error: {code}", code=rtrn["response"]["error"])
|
|
||||||
return False
|
|
||||||
return rtrn
|
|
||||||
|
|
||||||
return inner_function
|
|
||||||
|
|
||||||
|
|
||||||
class Agora(object):
|
class Agora(object):
|
||||||
"""
|
"""
|
||||||
|
@ -56,8 +30,8 @@ class Agora(object):
|
||||||
"""
|
"""
|
||||||
self.log = Logger("agora")
|
self.log = Logger("agora")
|
||||||
self.agora = AgoraDesk(settings.Agora.Token)
|
self.agora = AgoraDesk(settings.Agora.Token)
|
||||||
self.cr = CurrencyRates()
|
self.cr = CurrencyRates() # TODO: remove this and defer to money
|
||||||
self.cg = CoinGeckoAPI()
|
self.cg = CoinGeckoAPI() # TODO: remove this and defer to money
|
||||||
|
|
||||||
# Cache for detecting new trades
|
# Cache for detecting new trades
|
||||||
self.last_dash = set()
|
self.last_dash = set()
|
||||||
|
@ -78,7 +52,7 @@ class Agora(object):
|
||||||
self.lc_cheat = LoopingCall(self.run_cheat_in_thread)
|
self.lc_cheat = LoopingCall(self.run_cheat_in_thread)
|
||||||
self.lc_cheat.start(int(settings.Agora.CheatSec))
|
self.lc_cheat.start(int(settings.Agora.CheatSec))
|
||||||
|
|
||||||
@handle_exceptions
|
@util.handle_exceptions
|
||||||
def wrap_dashboard(self):
|
def wrap_dashboard(self):
|
||||||
dash = self.agora.dashboard_seller()
|
dash = self.agora.dashboard_seller()
|
||||||
if dash is None:
|
if dash is None:
|
||||||
|
@ -179,7 +153,7 @@ class Agora(object):
|
||||||
current_trades.append(reference)
|
current_trades.append(reference)
|
||||||
self.tx.cleanup(current_trades)
|
self.tx.cleanup(current_trades)
|
||||||
|
|
||||||
@handle_exceptions
|
@util.handle_exceptions
|
||||||
def get_recent_messages(self, send_irc=True):
|
def get_recent_messages(self, send_irc=True):
|
||||||
"""
|
"""
|
||||||
Get recent messages.
|
Get recent messages.
|
||||||
|
@ -226,7 +200,7 @@ class Agora(object):
|
||||||
|
|
||||||
return messages_tmp
|
return messages_tmp
|
||||||
|
|
||||||
@handle_exceptions
|
@util.handle_exceptions
|
||||||
def enum_ad_ids(self, page=0):
|
def enum_ad_ids(self, page=0):
|
||||||
ads = self.agora._api_call(api_method="ads", query_values={"page": page})
|
ads = self.agora._api_call(api_method="ads", query_values={"page": page})
|
||||||
if ads is False:
|
if ads is False:
|
||||||
|
@ -248,7 +222,7 @@ class Agora(object):
|
||||||
ads_total.append(ad)
|
ads_total.append(ad)
|
||||||
return ads_total
|
return ads_total
|
||||||
|
|
||||||
@handle_exceptions
|
@util.handle_exceptions
|
||||||
def enum_ads(self, requested_asset=None, page=0):
|
def enum_ads(self, requested_asset=None, page=0):
|
||||||
query_values = {"page": page}
|
query_values = {"page": page}
|
||||||
if requested_asset:
|
if requested_asset:
|
||||||
|
@ -278,22 +252,7 @@ class Agora(object):
|
||||||
ads_total.append([ad[0], ad[1], ad[2], ad[3], ad[4]])
|
ads_total.append([ad[0], ad[1], ad[2], ad[3], ad[4]])
|
||||||
return ads_total
|
return ads_total
|
||||||
|
|
||||||
# TODO: move to utils library
|
@util.handle_exceptions
|
||||||
def last_online_recent(self, date):
|
|
||||||
"""
|
|
||||||
Check if the last online date was recent.
|
|
||||||
:param date: date last online
|
|
||||||
:type date: string
|
|
||||||
:return: bool indicating whether the date was recent enough
|
|
||||||
:rtype: bool
|
|
||||||
"""
|
|
||||||
date_parsed = datetime.strptime(date, "%Y-%m-%dT%H:%M:%S.%fZ")
|
|
||||||
now = datetime.now()
|
|
||||||
sec_ago_date = (now - date_parsed).total_seconds()
|
|
||||||
# self.log.debug("Seconds ago date for {date} ^ {now}: {x}", date=date, now=str(now), x=sec_ago_date)
|
|
||||||
return sec_ago_date < 172800
|
|
||||||
|
|
||||||
@handle_exceptions
|
|
||||||
def enum_public_ads(self, asset, currency, providers=None, page=0):
|
def enum_public_ads(self, asset, currency, providers=None, page=0):
|
||||||
to_return = []
|
to_return = []
|
||||||
|
|
||||||
|
@ -326,7 +285,7 @@ class Agora(object):
|
||||||
continue
|
continue
|
||||||
date_last_seen = ad["data"]["profile"]["last_online"]
|
date_last_seen = ad["data"]["profile"]["last_online"]
|
||||||
# Check if this person was seen recently
|
# Check if this person was seen recently
|
||||||
if not self.last_online_recent(date_last_seen):
|
if not util.last_online_recent(date_last_seen):
|
||||||
continue
|
continue
|
||||||
ad_id = ad["data"]["ad_id"]
|
ad_id = ad["data"]["ad_id"]
|
||||||
username = ad["data"]["profile"]["username"]
|
username = ad["data"]["profile"]["username"]
|
||||||
|
@ -391,7 +350,7 @@ class Agora(object):
|
||||||
else:
|
else:
|
||||||
deferToThread(self.update_prices, assets)
|
deferToThread(self.update_prices, assets)
|
||||||
|
|
||||||
@handle_exceptions
|
@util.handle_exceptions
|
||||||
def update_prices(self, assets=None):
|
def update_prices(self, assets=None):
|
||||||
# Get all public ads for the given assets
|
# Get all public ads for the given assets
|
||||||
public_ads = self.get_all_public_ads(assets)
|
public_ads = self.get_all_public_ads(assets)
|
||||||
|
@ -403,7 +362,7 @@ class Agora(object):
|
||||||
self.slow_ad_update(to_update)
|
self.slow_ad_update(to_update)
|
||||||
|
|
||||||
# TODO: make generic and move to markets
|
# TODO: make generic and move to markets
|
||||||
@handle_exceptions
|
@util.handle_exceptions
|
||||||
def get_all_public_ads(self, assets=None, currencies=None, providers=None):
|
def get_all_public_ads(self, assets=None, currencies=None, providers=None):
|
||||||
"""
|
"""
|
||||||
Get all public ads for our listed currencies.
|
Get all public ads for our listed currencies.
|
||||||
|
@ -487,7 +446,7 @@ class Agora(object):
|
||||||
continue
|
continue
|
||||||
iterations += 1
|
iterations += 1
|
||||||
|
|
||||||
@handle_exceptions
|
@util.handle_exceptions
|
||||||
def nuke_ads(self):
|
def nuke_ads(self):
|
||||||
"""
|
"""
|
||||||
Delete all of our adverts.
|
Delete all of our adverts.
|
||||||
|
@ -534,7 +493,7 @@ class Agora(object):
|
||||||
max_local = max_usd * rates[currency]
|
max_local = max_usd * rates[currency]
|
||||||
return (min_local, max_local)
|
return (min_local, max_local)
|
||||||
|
|
||||||
@handle_exceptions
|
@util.handle_exceptions
|
||||||
def create_ad(self, asset, countrycode, currency, provider, edit=False, ad_id=None):
|
def create_ad(self, asset, countrycode, currency, provider, edit=False, ad_id=None):
|
||||||
"""
|
"""
|
||||||
Post an ad with the given asset in a country with a given currency.
|
Post an ad with the given asset in a country with a given currency.
|
||||||
|
@ -654,7 +613,7 @@ class Agora(object):
|
||||||
return False
|
return False
|
||||||
yield (rtrn, ad_id)
|
yield (rtrn, ad_id)
|
||||||
|
|
||||||
@handle_exceptions
|
@util.handle_exceptions
|
||||||
def strip_duplicate_ads(self):
|
def strip_duplicate_ads(self):
|
||||||
"""
|
"""
|
||||||
Remove duplicate ads.
|
Remove duplicate ads.
|
||||||
|
@ -676,7 +635,7 @@ class Agora(object):
|
||||||
actioned.append(rtrn["success"])
|
actioned.append(rtrn["success"])
|
||||||
return all(actioned)
|
return all(actioned)
|
||||||
|
|
||||||
@handle_exceptions
|
@util.handle_exceptions
|
||||||
def release_funds(self, contact_id):
|
def release_funds(self, contact_id):
|
||||||
"""
|
"""
|
||||||
Release funds for a contact_id.
|
Release funds for a contact_id.
|
||||||
|
@ -695,7 +654,7 @@ class Agora(object):
|
||||||
return rtrn
|
return rtrn
|
||||||
|
|
||||||
# TODO: write test before re-enabling adding total_trades
|
# TODO: write test before re-enabling adding total_trades
|
||||||
@handle_exceptions
|
@util.handle_exceptions
|
||||||
def withdraw_funds(self):
|
def withdraw_funds(self):
|
||||||
"""
|
"""
|
||||||
Withdraw excess funds to our XMR wallets.
|
Withdraw excess funds to our XMR wallets.
|
||||||
|
|
|
@ -6,6 +6,7 @@ from copy import deepcopy
|
||||||
from tests.common import fake_public_ads, cg_prices, expected_to_update
|
from tests.common import fake_public_ads, cg_prices, expected_to_update
|
||||||
from agora import Agora
|
from agora import Agora
|
||||||
from markets import Markets
|
from markets import Markets
|
||||||
|
import util
|
||||||
|
|
||||||
|
|
||||||
class TestAgora(TestCase):
|
class TestAgora(TestCase):
|
||||||
|
@ -120,6 +121,9 @@ class TestAgora(TestCase):
|
||||||
self.agora.last_online_recent = MagicMock()
|
self.agora.last_online_recent = MagicMock()
|
||||||
self.agora.last_online_recent.return_value = True
|
self.agora.last_online_recent.return_value = True
|
||||||
|
|
||||||
|
util.last_online_recent = MagicMock()
|
||||||
|
util.last_online_recent.return_value = True
|
||||||
|
|
||||||
# Override get_price
|
# Override get_price
|
||||||
self.agora.cg.get_price = MagicMock()
|
self.agora.cg.get_price = MagicMock()
|
||||||
self.agora.cg.get_price.return_value = cg_prices
|
self.agora.cg.get_price.return_value = cg_prices
|
||||||
|
@ -132,8 +136,8 @@ class TestAgora(TestCase):
|
||||||
def test_enum_public_ads(self):
|
def test_enum_public_ads(self):
|
||||||
# Override enum_public_ads
|
# Override enum_public_ads
|
||||||
self.agora.agora._api_call = self.mock_enum_public_ads_api_call
|
self.agora.agora._api_call = self.mock_enum_public_ads_api_call
|
||||||
self.agora.last_online_recent = MagicMock()
|
util.last_online_recent = MagicMock()
|
||||||
self.agora.last_online_recent.return_value = True
|
util.last_online_recent.return_value = True
|
||||||
|
|
||||||
enum_ads_return = self.agora.enum_public_ads("XMR", "USD", self.all_providers)
|
enum_ads_return = self.agora.enum_public_ads("XMR", "USD", self.all_providers)
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,13 @@
|
||||||
|
# Twisted/Klein imports
|
||||||
|
from twisted.logger import Logger
|
||||||
|
|
||||||
|
# Other library imports
|
||||||
|
from httpx import ReadTimeout, ReadError, RemoteProtocolError
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
log = Logger("util.global")
|
||||||
|
|
||||||
|
|
||||||
def convert(data):
|
def convert(data):
|
||||||
"""
|
"""
|
||||||
Recursively convert a dictionary.
|
Recursively convert a dictionary.
|
||||||
|
@ -11,3 +21,41 @@ def convert(data):
|
||||||
if isinstance(data, list):
|
if isinstance(data, list):
|
||||||
return list(map(convert, data))
|
return list(map(convert, data))
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def last_online_recent(date):
|
||||||
|
"""
|
||||||
|
Check if the last online date was recent.
|
||||||
|
:param date: date last online
|
||||||
|
:type date: string
|
||||||
|
:return: bool indicating whether the date was recent enough
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
date_parsed = datetime.strptime(date, "%Y-%m-%dT%H:%M:%S.%fZ")
|
||||||
|
now = datetime.now()
|
||||||
|
sec_ago_date = (now - date_parsed).total_seconds()
|
||||||
|
# self.log.debug("Seconds ago date for {date} ^ {now}: {x}", date=date, now=str(now), x=sec_ago_date)
|
||||||
|
return sec_ago_date < 172800
|
||||||
|
|
||||||
|
|
||||||
|
def handle_exceptions(func):
|
||||||
|
def inner_function(*args, **kwargs):
|
||||||
|
try:
|
||||||
|
rtrn = func(*args, **kwargs)
|
||||||
|
except (ReadTimeout, ReadError, RemoteProtocolError):
|
||||||
|
return False
|
||||||
|
if isinstance(rtrn, dict):
|
||||||
|
if "success" in rtrn:
|
||||||
|
if "message" in rtrn:
|
||||||
|
if not rtrn["success"] and rtrn["message"] == "API ERROR":
|
||||||
|
if "error_code" in rtrn["response"]["error"]:
|
||||||
|
code = rtrn["response"]["error"]["error_code"]
|
||||||
|
if not code == 136:
|
||||||
|
log.error("API error: {code}", code=code)
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
log.error("API error: {code}", code=rtrn["response"]["error"])
|
||||||
|
return False
|
||||||
|
return rtrn
|
||||||
|
|
||||||
|
return inner_function
|
||||||
|
|
Loading…
Reference in New Issue