Reformat project

master
Mark Veidemanis 2 years ago
parent 933642def6
commit ea81748c0a
Signed by: m
GPG Key ID: 5ACFCEED46C0904F

@ -1,13 +1,21 @@
repos: repos:
- repo: https://github.com/ambv/black - repo: https://github.com/psf/black
rev: 22.3.0 rev: 22.6.0
hooks: hooks:
- id: black - id: black
args: exclude: ^core/migrations
- --line-length=120 - repo: https://github.com/PyCQA/isort
- repo: https://gitlab.com/pycqa/flake8 rev: 5.10.1
hooks:
- id: isort
- repo: https://github.com/PyCQA/flake8
rev: 4.0.1 rev: 4.0.1
hooks: hooks:
- id: flake8 - id: flake8
args: args: [--max-line-length=88]
- "--max-line-length=120" exclude: ^core/migrations
- repo: https://github.com/thibaudcolas/curlylint
rev: v0.13.1
hooks:
- id: curlylint
files: \.(html|sls)$

@ -1,27 +1,25 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# Twisted/Klein imports # Twisted/Klein imports
from twisted.internet import reactor
from klein import Klein
from twisted.internet.protocol import Factory
# Other library imports # Other library imports
from json import dumps from json import dumps
from signal import signal, SIGINT from signal import SIGINT, signal
from sys import argv from sys import argv
# Project imports import lib.antifraud
from settings import settings import lib.logstash
import util import lib.markets
import lib.money
import lib.transactions
# New style classes # New style classes
import sinks import sinks
import sources import sources
import util
import ux import ux
import lib.antifraud from klein import Klein
import lib.transactions # Project imports
import lib.markets from settings import settings
import lib.money from twisted.internet import reactor
import lib.logstash from twisted.internet.protocol import Factory
init_map = None init_map = None
Factory.noisy = False Factory.noisy = False

@ -1,9 +1,8 @@
# Other library imports # Other library imports
import util
from redis import StrictRedis from redis import StrictRedis
# Project imports # Project imports
from settings import settings from settings import settings
import util
log = util.get_logger("DB") log = util.get_logger("DB")
@ -129,7 +128,9 @@ def cleanup(subclass, references):
for tx, reference in get_ref_map().items(): for tx, reference in get_ref_map().items():
if reference not in references: if reference not in references:
if get_subclass(reference) == subclass: if get_subclass(reference) == subclass:
logmessage = f"[{reference}] ({subclass}): Archiving trade reference. TX: {tx}" logmessage = (
f"[{reference}] ({subclass}): Archiving trade reference. TX: {tx}"
)
messages.append(logmessage) messages.append(logmessage)
log.info(logmessage) log.info(logmessage)
r.rename(f"trade.{tx}.reference", f"archive.trade.{tx}.reference") r.rename(f"trade.{tx}.reference", f"archive.trade.{tx}.reference")

@ -3,18 +3,13 @@
# Large API. Lots of lines can't be avoided. # Large API. Lots of lines can't be avoided.
import json import json
import logging import logging
from typing import Any from typing import Any, Dict, List, Optional, Union
from typing import Dict
from typing import List
from typing import Optional
from typing import Union
import arrow import arrow
import treq import treq
from twisted.internet.defer import inlineCallbacks
# Project imports # Project imports
import util import util
from twisted.internet.defer import inlineCallbacks
__author__ = "marvin8" __author__ = "marvin8"
__copyright__ = "(C) 2021 https://codeberg.org/MarvinsCryptoTools/agoradesk_py" __copyright__ = "(C) 2021 https://codeberg.org/MarvinsCryptoTools/agoradesk_py"
@ -88,7 +83,8 @@ class AgoraDesk:
headers = { headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"User-Agent": f"agoradesk_py/{__version__} " f"https://codeberg.org/MarvinsCryptoTools/agoradesk_py", "User-Agent": f"agoradesk_py/{__version__} "
f"https://codeberg.org/MarvinsCryptoTools/agoradesk_py",
"Authorization": self.api_key, "Authorization": self.api_key,
} }
@ -253,7 +249,9 @@ class AgoraDesk:
# =========================== # ===========================
# post/feedback/{username} • Give feedback to a user # post/feedback/{username} • Give feedback to a user
def feedback(self, username: str, feedback: str, msg: Optional[str]) -> Dict[str, Any]: def feedback(
self, username: str, feedback: str, msg: Optional[str]
) -> Dict[str, Any]:
"""See Agoradesk API. """See Agoradesk API.
https://agoradesk.com/api-docs/v1#operation/setUserFeedback https://agoradesk.com/api-docs/v1#operation/setUserFeedback
@ -279,7 +277,9 @@ class AgoraDesk:
https://agoradesk.com/api-docs/v1#operation/markPaid https://agoradesk.com/api-docs/v1#operation/markPaid
""" """
return self._api_call(api_method=f"contact_mark_as_paid/{trade_id}", http_method="POST") return self._api_call(
api_method=f"contact_mark_as_paid/{trade_id}", http_method="POST"
)
# post/contact_cancel/{trade_id} • Cancel the trade # post/contact_cancel/{trade_id} • Cancel the trade
def contact_cancel( def contact_cancel(
@ -299,7 +299,9 @@ class AgoraDesk:
# post/contact_escrow/{trade_id} • Enable escrow # post/contact_escrow/{trade_id} • Enable escrow
# get/contact_messages/{trade_id} • Get trade messages # get/contact_messages/{trade_id} • Get trade messages
def contact_messages(self, trade_id: str, after: Optional[arrow.Arrow] = None) -> Dict[str, Any]: def contact_messages(
self, trade_id: str, after: Optional[arrow.Arrow] = None
) -> Dict[str, Any]:
"""See Agoradesk API. """See Agoradesk API.
https://agoradesk.com/api-docs/v1#operation/getTradeMessages https://agoradesk.com/api-docs/v1#operation/getTradeMessages
@ -354,7 +356,9 @@ class AgoraDesk:
# Todo: Add image upload functionality # Todo: Add image upload functionality
# post/contact_message_post/{trade_id} • Send a chat message/attachment # post/contact_message_post/{trade_id} • Send a chat message/attachment
def contact_message_post(self, trade_id: str, msg: Optional[str] = None) -> Dict[str, Any]: def contact_message_post(
self, trade_id: str, msg: Optional[str] = None
) -> Dict[str, Any]:
"""See Agoradesk API. """See Agoradesk API.
https://agoradesk.com/api-docs/v1#operation/sendChatMessage https://agoradesk.com/api-docs/v1#operation/sendChatMessage
@ -504,7 +508,9 @@ class AgoraDesk:
if track_max_amount: if track_max_amount:
params["track_max_amount"] = 1 if track_max_amount else 0 params["track_max_amount"] = 1 if track_max_amount else 0
if require_trusted_by_advertiser: if require_trusted_by_advertiser:
params["require_trusted_by_advertiser"] = 1 if require_trusted_by_advertiser else 0 params["require_trusted_by_advertiser"] = (
1 if require_trusted_by_advertiser else 0
)
if verified_email_required: if verified_email_required:
params["verified_email_required"] = 1 if verified_email_required else 0 params["verified_email_required"] = 1 if verified_email_required else 0
if online_provider: if online_provider:
@ -680,7 +686,8 @@ class AgoraDesk:
params = self._generic_search_parameters(amount, page) params = self._generic_search_parameters(amount, page)
return self._api_call( return self._api_call(
api_method=f"{direction}-{main_currency}-online/" f"{exchange_currency}{add_to_api_method}", api_method=f"{direction}-{main_currency}-online/"
f"{exchange_currency}{add_to_api_method}",
query_values=params, query_values=params,
) )
@ -823,7 +830,8 @@ class AgoraDesk:
params = self._generic_search_parameters(amount, page) params = self._generic_search_parameters(amount, page)
return self._api_call( return self._api_call(
api_method=f"{direction}-{main_currency}-with-cash/" f"{exchange_currency}/{country_code}/{lat}/{lon}", api_method=f"{direction}-{main_currency}-with-cash/"
f"{exchange_currency}/{country_code}/{lat}/{lon}",
query_values=params, query_values=params,
) )
@ -938,7 +946,9 @@ class AgoraDesk:
# Statistics related API Methods # Statistics related API Methods
# ============================== # ==============================
def moneroaverage(self, currency: Optional[str] = "ticker-all-currencies") -> Dict[str, Any]: def moneroaverage(
self, currency: Optional[str] = "ticker-all-currencies"
) -> Dict[str, Any]:
"""See Agoradesk API. """See Agoradesk API.
https://agoradesk.com/api-docs/v1#operation/getXmrTicker and https://agoradesk.com/api-docs/v1#operation/getXmrTicker and
@ -1028,7 +1038,9 @@ class AgoraDesk:
if otp: if otp:
params["otp"] = otp params["otp"] = otp
return self._api_call(api_method="wallet-send", http_method="POST", query_values=params) return self._api_call(
api_method="wallet-send", http_method="POST", query_values=params
)
def wallet_send_xmr( def wallet_send_xmr(
self, self,

@ -1,6 +1,6 @@
# Project imports # Project imports
import util
import db import db
import util
class AntiFraud(util.Base): class AntiFraud(util.Base):
@ -46,7 +46,9 @@ class AntiFraud(util.Base):
return True return True
if platform_buyer in senders: if platform_buyer in senders:
return True return True
self.ux.notify.notify_sender_name_mismatch(reference, platform_buyer, bank_sender) self.ux.notify.notify_sender_name_mismatch(
reference, platform_buyer, bank_sender
)
return False return False
def check_tx_sender(self, tx, reference): def check_tx_sender(self, tx, reference):
@ -65,7 +67,9 @@ class AntiFraud(util.Base):
bank_sender = stored_tx["sender"] bank_sender = stored_tx["sender"]
platform_buyer = stored_trade["buyer"] platform_buyer = stored_trade["buyer"]
platform = stored_trade["subclass"] platform = stored_trade["subclass"]
is_allowed = self.check_valid_sender(reference, platform, bank_sender, platform_buyer) is_allowed = self.check_valid_sender(
reference, platform, bank_sender, platform_buyer
)
if is_allowed is True: if is_allowed is True:
return True return True
return False return False

@ -1,29 +1,22 @@
"""See https://agoradesk.com/api-docs/v1.""" """See https://agoradesk.com/api-docs/v1."""
# pylint: disable=too-many-lines # pylint: disable=too-many-lines
# Large API. Lots of lines can't be avoided. # Large API. Lots of lines can't be avoided.
import json
import logging
from typing import Any
from typing import Dict
from typing import List
from typing import Optional
from typing import Union
import arrow
# Project imports
import util
import hashlib import hashlib
import hmac as hmac_lib import hmac as hmac_lib
import requests import json
import logging
import sys import sys
import time import time
from typing import Any, Dict, List, Optional, Union
from urllib.parse import urlparse
import arrow
import requests
import treq import treq
# Project imports
import util
from twisted.internet.defer import inlineCallbacks from twisted.internet.defer import inlineCallbacks
from urllib.parse import urlparse
__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"
@ -86,17 +79,25 @@ class LocalBitcoins:
message += params_encoded.encode("ascii") message += params_encoded.encode("ascii")
else: else:
message += params_encoded message += params_encoded
signature = hmac_lib.new(self.hmac_secret, msg=message, digestmod=hashlib.sha256).hexdigest().upper() signature = (
hmac_lib.new(self.hmac_secret, msg=message, digestmod=hashlib.sha256)
.hexdigest()
.upper()
)
return signature return signature
def encode_params(self, http_method, api_call_url, query_values): def encode_params(self, http_method, api_call_url, query_values):
if http_method == "POST": if http_method == "POST":
api_request = requests.Request("POST", api_call_url, data=query_values).prepare() api_request = requests.Request(
"POST", api_call_url, data=query_values
).prepare()
params_encoded = api_request.body params_encoded = api_request.body
# GET method # GET method
else: else:
api_request = requests.Request("GET", api_call_url, params=query_values).prepare() api_request = requests.Request(
"GET", api_call_url, params=query_values
).prepare()
params_encoded = urlparse(api_request.url).query params_encoded = urlparse(api_request.url).query
return (api_request, params_encoded) return (api_request, params_encoded)
@ -132,7 +133,9 @@ class LocalBitcoins:
url = url[len(SERVER) :] # noqa url = url[len(SERVER) :] # noqa
# HMAC crypto stuff # HMAC crypto stuff
api_request, params_encoded = self.encode_params(http_method, api_call_url, query_values) api_request, params_encoded = self.encode_params(
http_method, api_call_url, query_values
)
nonce = str(int(time.time() * 1000)).encode("ascii") nonce = str(int(time.time() * 1000)).encode("ascii")
signature = self.sign_payload(nonce, url, params_encoded) signature = self.sign_payload(nonce, url, params_encoded)
@ -288,7 +291,9 @@ class LocalBitcoins:
# =========================== # ===========================
# post/feedback/{username} • Give feedback to a user # post/feedback/{username} • Give feedback to a user
def feedback(self, username: str, feedback: str, msg: Optional[str]) -> Dict[str, Any]: def feedback(
self, username: str, feedback: str, msg: Optional[str]
) -> Dict[str, Any]:
"""See LocalBitcoins API. """See LocalBitcoins API.
https://localbitcoins.com/api-docs/#feedback https://localbitcoins.com/api-docs/#feedback
@ -314,7 +319,9 @@ class LocalBitcoins:
https://localbitcoins.com/api-docs/#contact-paid https://localbitcoins.com/api-docs/#contact-paid
""" """
return self._api_call(api_method=f"contact_mark_as_paid/{trade_id}/", http_method="POST") return self._api_call(
api_method=f"contact_mark_as_paid/{trade_id}/", http_method="POST"
)
# post/contact_cancel/{trade_id} • Cancel the trade # post/contact_cancel/{trade_id} • Cancel the trade
def contact_cancel( def contact_cancel(
@ -334,7 +341,9 @@ class LocalBitcoins:
# post/contact_escrow/{trade_id} • Enable escrow # post/contact_escrow/{trade_id} • Enable escrow
# get/contact_messages/{trade_id} • Get trade messages # get/contact_messages/{trade_id} • Get trade messages
def contact_messages(self, trade_id: str, after: Optional[arrow.Arrow] = None) -> Dict[str, Any]: def contact_messages(
self, trade_id: str, after: Optional[arrow.Arrow] = None
) -> Dict[str, Any]:
"""See LocalBitcoins API. """See LocalBitcoins API.
https://localbitcoins.com/api-docs/#contact-message https://localbitcoins.com/api-docs/#contact-message
@ -389,7 +398,9 @@ class LocalBitcoins:
# Todo: Add image upload functionality # Todo: Add image upload functionality
# post/contact_message_post/{trade_id} • Send a chat message/attachment # post/contact_message_post/{trade_id} • Send a chat message/attachment
def contact_message_post(self, trade_id: str, msg: Optional[str] = None) -> Dict[str, Any]: def contact_message_post(
self, trade_id: str, msg: Optional[str] = None
) -> Dict[str, Any]:
"""See LocalBitcoins API. """See LocalBitcoins API.
https://localbitcoins.com/api-docs/#contact-post https://localbitcoins.com/api-docs/#contact-post
@ -726,7 +737,8 @@ class LocalBitcoins:
params = self._generic_search_parameters(amount, page) params = self._generic_search_parameters(amount, page)
return self._api_call( return self._api_call(
api_method=f"{direction}-{main_currency}-online/" f"{exchange_currency}{add_to_api_method}/.json", api_method=f"{direction}-{main_currency}-online/"
f"{exchange_currency}{add_to_api_method}/.json",
query_values=params, query_values=params,
) )

@ -1,8 +1,8 @@
# Other library imports # Other library imports
from json import dumps
import logstash
import logging import logging
from json import dumps
import logstash
# Project imports # Project imports
from settings import settings from settings import settings

@ -1,10 +1,10 @@
# Other library imports # Other library imports
from json import loads from json import loads
import db
import util
# Project imports # Project imports
from settings import settings from settings import settings
import util
import db
class Markets(util.Base): class Markets(util.Base):
@ -60,7 +60,9 @@ class Markets(util.Base):
self.log.info(f"Sending bank details/reference for {platform}/{trade_id}") self.log.info(f"Sending bank details/reference for {platform}/{trade_id}")
if send_setting == "1": if send_setting == "1":
account_info = self.get_matching_account_details(platform, currency) account_info = self.get_matching_account_details(platform, currency)
formatted_account_info = self.format_payment_details(currency, account_info, real=True) formatted_account_info = self.format_payment_details(
currency, account_info, real=True
)
if not formatted_account_info: if not formatted_account_info:
self.log.error(f"Payment info invalid: {formatted_account_info}") self.log.error(f"Payment info invalid: {formatted_account_info}")
return return
@ -110,12 +112,21 @@ class Markets(util.Base):
currencies = self.get_all_currencies(platform) currencies = self.get_all_currencies(platform)
providers = self.get_all_providers(platform) providers = self.get_all_providers(platform)
if platform == "lbtc": if platform == "lbtc":
providers = [self.sources.lbtc.map_provider(x, reverse=True) for x in providers] providers = [
self.sources.lbtc.map_provider(x, reverse=True) for x in providers
]
sinks_currencies = self.sinks.currencies sinks_currencies = self.sinks.currencies
supported_currencies = [currency for currency in currencies if currency in sinks_currencies] supported_currencies = [
currency for currency in currencies if currency in sinks_currencies
]
currencies = supported_currencies currencies = supported_currencies
brute = [(asset, currency, provider) for asset in assets for currency in currencies for provider in providers] brute = [
(asset, currency, provider)
for asset in assets
for currency in currencies
for provider in providers
]
for asset, currency, provider in brute: for asset, currency, provider in brute:
# Filter currency # Filter currency
try: try:
@ -123,22 +134,32 @@ class Markets(util.Base):
except KeyError: except KeyError:
# self.log.error("Error getting public ads for currency {currency}", currency=currency) # self.log.error("Error getting public ads for currency {currency}", currency=currency)
if currency == "GBP": if currency == "GBP":
self.log.error("Error getting public ads for currency GBP, aborting") self.log.error(
"Error getting public ads for currency GBP, aborting"
)
break break
continue continue
# Filter asset # Filter asset
public_ads_filtered = [ad for ad in public_ads_currency if ad[4] == asset] public_ads_filtered = [ad for ad in public_ads_currency if ad[4] == asset]
# Filter provider # Filter provider
public_ads_filtered = [ad for ad in public_ads_filtered if ad[3] == provider] public_ads_filtered = [
ad for ad in public_ads_filtered if ad[3] == provider
]
our_ads = [ad for ad in public_ads_filtered if ad[1] == username] our_ads = [ad for ad in public_ads_filtered if ad[1] == username]
if not our_ads: if not our_ads:
self.log.warning(f"No ads found in {platform} public listing for {asset} {currency} {provider}") self.log.warning(
f"No ads found in {platform} public listing for {asset} {currency} {provider}"
)
continue continue
new_margin = self.autoprice(username, min_margin, max_margin, public_ads_filtered, currency) new_margin = self.autoprice(
username, min_margin, max_margin, public_ads_filtered, currency
)
# self.log.info("New rate for {currency}: {rate}", currency=currency, rate=new_margin) # self.log.info("New rate for {currency}: {rate}", currency=currency, rate=new_margin)
if platform == "agora": if platform == "agora":
new_formula = f"coingecko{asset.lower()}usd*usd{currency.lower()}*{new_margin}" new_formula = (
f"coingecko{asset.lower()}usd*usd{currency.lower()}*{new_margin}"
)
elif platform == "lbtc": elif platform == "lbtc":
new_formula = f"btc_in_usd*{new_margin}*USD_in_{currency}" new_formula = f"btc_in_usd*{new_margin}*USD_in_{currency}"
for ad in our_ads: for ad in our_ads:
@ -172,7 +193,9 @@ class Markets(util.Base):
ads_without_us = [ad for ad in ads if not ad[1] == username] ads_without_us = [ad for ad in ads if not ad[1] == username]
# self.log.debug("Ads without us: {x}", x=ads_without_us) # self.log.debug("Ads without us: {x}", x=ads_without_us)
# Find ads above our min that are not us # Find ads above our min that are not us
ads_above_our_min_not_us = [ad for ad in ads_without_us if ad[6] > float(min_margin)] ads_above_our_min_not_us = [
ad for ad in ads_without_us if ad[6] > float(min_margin)
]
# self.log.debug("Ads above our min not us: {x}", x=ads_above_our_min_not_us) # self.log.debug("Ads above our min not us: {x}", x=ads_above_our_min_not_us)
# Check that this list without us is not empty # Check that this list without us is not empty
if ads_without_us: if ads_without_us:
@ -247,7 +270,9 @@ class Markets(util.Base):
currencies = self.sinks.currencies currencies = self.sinks.currencies
account_info = self.sinks.account_info account_info = self.sinks.account_info
all_currencies = self.get_all_currencies(platform) all_currencies = self.get_all_currencies(platform)
supported_currencies = [currency for currency in currencies if currency in all_currencies] supported_currencies = [
currency for currency in currencies if currency in all_currencies
]
currency_account_info_map = {} currency_account_info_map = {}
for currency in supported_currencies: for currency in supported_currencies:
for bank, accounts in account_info.items(): for bank, accounts in account_info.items():
@ -255,11 +280,16 @@ class Markets(util.Base):
if account["currency"] == currency: if account["currency"] == currency:
currency_account_info_map[currency] = account["account_number"] currency_account_info_map[currency] = account["account_number"]
currency_account_info_map[currency]["bank"] = bank.split("_")[0] currency_account_info_map[currency]["bank"] = bank.split("_")[0]
currency_account_info_map[currency]["recipient"] = account["recipient"] currency_account_info_map[currency]["recipient"] = account[
"recipient"
]
return (supported_currencies, currency_account_info_map) return (supported_currencies, currency_account_info_map)
def get_matching_account_details(self, platform, currency): def get_matching_account_details(self, platform, currency):
supported_currencies, currency_account_info_map = self.get_valid_account_details(platform) (
supported_currencies,
currency_account_info_map,
) = self.get_valid_account_details(platform)
if currency not in supported_currencies: if currency not in supported_currencies:
return False return False
return currency_account_info_map[currency] return currency_account_info_map[currency]
@ -278,7 +308,10 @@ class Markets(util.Base):
currencies = self.sinks.currencies currencies = self.sinks.currencies
if not account_info: if not account_info:
account_info = self.sinks.account_info account_info = self.sinks.account_info
supported_currencies, currency_account_info_map = self.get_valid_account_details(platform) (
supported_currencies,
currency_account_info_map,
) = self.get_valid_account_details(platform)
# not_supported = [currency for currency in all_currencies if currency not in supported_currencies] # not_supported = [currency for currency in all_currencies if currency not in supported_currencies]
@ -329,7 +362,9 @@ class Markets(util.Base):
""" """
platforms = ("agora", "lbtc") platforms = ("agora", "lbtc")
for platform in platforms: for platform in platforms:
self._distribute_account_details(platform, currencies=currencies, account_info=account_info) self._distribute_account_details(
platform, currencies=currencies, account_info=account_info
)
def format_ad(self, asset, currency, payment_details_text): def format_ad(self, asset, currency, payment_details_text):
""" """

@ -1,19 +1,18 @@
# Twisted imports # Twisted imports
from twisted.internet.task import LoopingCall
from twisted.internet.defer import inlineCallbacks
# Other library imports
from pycoingecko import CoinGeckoAPI
from forex_python.converter import CurrencyRates
import urllib3
import logging import logging
from opensearchpy import OpenSearch
from datetime import datetime from datetime import datetime
# Project imports import urllib3
from settings import settings
import util import util
from forex_python.converter import CurrencyRates
from lib.logstash import send_logstash from lib.logstash import send_logstash
from opensearchpy import OpenSearch
# Other library imports
from pycoingecko import CoinGeckoAPI
# Project imports
from settings import settings
from twisted.internet.defer import inlineCallbacks
from twisted.internet.task import LoopingCall
# TODO: secure ES traffic properly # TODO: secure ES traffic properly
urllib3.disable_warnings() urllib3.disable_warnings()
@ -74,7 +73,15 @@ class Money(util.Base):
total_remaining = self.get_total_remaining() total_remaining = self.get_total_remaining()
total_with_trades = self.get_total_with_trades() total_with_trades = self.get_total_with_trades()
# This will make them all run concurrently, hopefully not hitting rate limits # This will make them all run concurrently, hopefully not hitting rate limits
for x in (total, remaining, profit, profit_with_trades, open_trades, total_remaining, total_with_trades): for x in (
total,
remaining,
profit,
profit_with_trades,
open_trades,
total_remaining,
total_with_trades,
):
yield x yield x
def setup_loops(self): def setup_loops(self):
@ -382,7 +389,9 @@ class Money(util.Base):
if not total_usd: if not total_usd:
return False return False
withdraw_threshold = float(settings.Money.BaseUSD) + float(settings.Money.WithdrawLimit) withdraw_threshold = float(settings.Money.BaseUSD) + float(
settings.Money.WithdrawLimit
)
remaining = withdraw_threshold - total_usd remaining = withdraw_threshold - total_usd
cast_es = { cast_es = {
"remaining_usd": remaining, "remaining_usd": remaining,
@ -416,13 +425,17 @@ class Money(util.Base):
asset = "BTC" asset = "BTC"
if asset == "XMR": if asset == "XMR":
amount_crypto = contact["data"]["amount_xmr"] amount_crypto = contact["data"]["amount_xmr"]
history = self.cg.get_coin_history_by_id(id="monero", date=date_formatted) history = self.cg.get_coin_history_by_id(
id="monero", date=date_formatted
)
if "market_data" not in history: if "market_data" not in history:
return False return False
crypto_usd = float(history["market_data"]["current_price"]["usd"]) crypto_usd = float(history["market_data"]["current_price"]["usd"])
elif asset == "BTC": elif asset == "BTC":
amount_crypto = contact["data"]["amount_btc"] amount_crypto = contact["data"]["amount_btc"]
history = self.cg.get_coin_history_by_id(id="bitcoin", date=date_formatted) history = self.cg.get_coin_history_by_id(
id="bitcoin", date=date_formatted
)
crypto_usd = float(history["market_data"]["current_price"]["usd"]) crypto_usd = float(history["market_data"]["current_price"]["usd"])
# Convert crypto to fiat # Convert crypto to fiat
amount = float(amount_crypto) * crypto_usd amount = float(amount_crypto) * crypto_usd
@ -476,7 +489,9 @@ class Money(util.Base):
if not total_usd: if not total_usd:
return False return False
total_usd += total_trades_usd total_usd += total_trades_usd
withdraw_threshold = float(settings.Money.BaseUSD) + float(settings.Money.WithdrawLimit) withdraw_threshold = float(settings.Money.BaseUSD) + float(
settings.Money.WithdrawLimit
)
remaining = withdraw_threshold - total_usd remaining = withdraw_threshold - total_usd
cast_es = { cast_es = {

@ -1,16 +1,14 @@
# Twisted/Klein imports # Twisted/Klein imports
from twisted.internet.defer import inlineCallbacks
# Other library imports # Other library imports
from json import dumps from json import dumps
from random import choices from random import choices
from string import ascii_uppercase from string import ascii_uppercase
# Project imports
from settings import settings
import db import db
import util import util
# Project imports
from settings import settings
from twisted.internet.defer import inlineCallbacks
class Transactions(util.Base): class Transactions(util.Base):
@ -95,7 +93,9 @@ class Transactions(util.Base):
if len(stored_trade_reference) > 1: if len(stored_trade_reference) > 1:
self.log.error(f"Multiple references valid for TXID {txid}: {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}")
self.ux.notify.notify_tx_lookup_failed(currency, amount, reference, "MULTIPLE_REFS_MATCH") self.ux.notify.notify_tx_lookup_failed(
currency, amount, reference, "MULTIPLE_REFS_MATCH"
)
return False return False
if len(stored_trade_reference) == 0: if len(stored_trade_reference) == 0:
return None return None
@ -105,10 +105,16 @@ class Transactions(util.Base):
amount_usd = self.money.to_usd(amount, currency) amount_usd = self.money.to_usd(amount, currency)
# Amount is reliable here as it is checked by find_trade, so no need for stored_trade["amount"] # Amount is reliable here as it is checked by find_trade, so no need for stored_trade["amount"]
if float(amount_usd) > float(settings.Agora.AcceptableAltLookupUSD): if float(amount_usd) > float(settings.Agora.AcceptableAltLookupUSD):
self.log.info("Not checking against amount and currency as amount exceeds MAX") self.log.info(
self.irc.sendmsg("Not checking against amount and currency as amount exceeds MAX") "Not checking against amount and currency as amount exceeds MAX"
)
self.irc.sendmsg(
"Not checking against amount and currency as amount exceeds MAX"
)
# Close here if the amount exceeds the allowable limit for no reference # Close here if the amount exceeds the allowable limit for no reference
self.ux.notify.notify_tx_lookup_failed(currency, amount, reference, "EXCEEDS_MAX") self.ux.notify.notify_tx_lookup_failed(
currency, amount, reference, "EXCEEDS_MAX"
)
return False return False
return True return True
@ -124,9 +130,15 @@ class Transactions(util.Base):
return False return False
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(f"Failed to get reference by amount and currency: {txid} {currency} {amount}") self.log.info(
self.irc.sendmsg(f"Failed to get reference by amount and currency: {txid} {currency} {amount}") f"Failed to get reference by amount and currency: {txid} {currency} {amount}"
self.ux.notify.notify_tx_lookup_failed(currency, amount, reference, "ALT_LOOKUP_FAILED") )
self.irc.sendmsg(
f"Failed to get reference by amount and currency: {txid} {currency} {amount}"
)
self.ux.notify.notify_tx_lookup_failed(
currency, amount, reference, "ALT_LOOKUP_FAILED"
)
return None return None
stored_trade["amount"] = float(stored_trade["amount"]) # convert to float stored_trade["amount"] = float(stored_trade["amount"]) # convert to float
return stored_trade return stored_trade
@ -136,15 +148,21 @@ class Transactions(util.Base):
if not stored_trade: if not stored_trade:
self.log.info(f"No reference in DB for {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}")
self.ux.notify.notify_tx_lookup_failed(currency, amount, reference, "NOREF", stored_trade_reference) self.ux.notify.notify_tx_lookup_failed(
currency, amount, reference, "NOREF", stored_trade_reference
)
return False return False
stored_trade["amount"] = float(stored_trade["amount"]) # convert to float stored_trade["amount"] = float(stored_trade["amount"]) # convert to float
return stored_trade return stored_trade
def currency_check(self, currency, amount, reference, stored_trade): def currency_check(self, currency, amount, reference, stored_trade):
if not stored_trade["currency"] == currency: if not stored_trade["currency"] == currency:
self.log.info(f"Currency mismatch, Agora: {stored_trade['currency']} / Sink: {currency}") self.log.info(
self.irc.sendmsg(f"Currency mismatch, Agora: {stored_trade['currency']} / Sink: {currency}") f"Currency mismatch, Agora: {stored_trade['currency']} / Sink: {currency}"
)
self.irc.sendmsg(
f"Currency mismatch, Agora: {stored_trade['currency']} / Sink: {currency}"
)
self.ux.notify.notify_tx_lookup_failed( self.ux.notify.notify_tx_lookup_failed(
currency, currency,
amount, amount,
@ -157,9 +175,15 @@ class Transactions(util.Base):
def alt_amount_check(self, platform, amount, currency, reference, stored_trade): def alt_amount_check(self, platform, amount, currency, reference, stored_trade):
# 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(platform, currency, stored_trade["amount"]) min_amount, max_amount = self.money.get_acceptable_margins(
self.log.info(f"Amount does not match exactly, trying with margins: min: {min_amount} / max: {max_amount}") platform, currency, stored_trade["amount"]
self.irc.sendmsg(f"Amount does not match exactly, trying with margins: min: {min_amount} / max: {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: 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: {stored_trade['amount']} (min: {min_amount} / max: {max_amount}"
@ -209,9 +233,13 @@ class Transactions(util.Base):
db.r.hmset(f"tx.{txid}", to_store) db.r.hmset(f"tx.{txid}", to_store)
self.log.info(f"Transaction processed: {dumps(to_store, indent=2)}") self.log.info(f"Transaction processed: {dumps(to_store, indent=2)}")
self.irc.sendmsg(f"AUTO Incoming transaction on {subclass}: {txid} {amount}{currency} ({reference})") self.irc.sendmsg(
f"AUTO Incoming transaction on {subclass}: {txid} {amount}{currency} ({reference})"
)
stored_trade_reference = self.reference_partial_check(reference, txid, currency, amount) stored_trade_reference = self.reference_partial_check(
reference, txid, currency, amount
)
if stored_trade_reference is False: # can be None though if stored_trade_reference is False: # can be None though
return return
@ -220,14 +248,18 @@ class Transactions(util.Base):
# Normal implementation for when we have a reference # Normal implementation for when we have a reference
if stored_trade_reference: if stored_trade_reference:
stored_trade = self.normal_lookup(stored_trade_reference, reference, currency, amount) stored_trade = self.normal_lookup(
stored_trade_reference, reference, currency, amount
)
# if not stored_trade: # if not stored_trade:
# return # return
# Amount/currency lookup implementation for when we have no reference # Amount/currency lookup implementation for when we have no reference
else: else:
if not stored_trade: # check we don't overwrite the lookup above if not stored_trade: # check we don't overwrite the lookup above
stored_trade = self.amount_currency_lookup(amount, currency, txid, reference) stored_trade = self.amount_currency_lookup(
amount, currency, txid, reference
)
if stored_trade is False: if stored_trade is False:
return return
if stored_trade: if stored_trade:
@ -249,13 +281,17 @@ class Transactions(util.Base):
if looked_up_without_reference: if looked_up_without_reference:
return return
platform = stored_trade["subclass"] platform = stored_trade["subclass"]
if not self.alt_amount_check(platform, amount, currency, reference, stored_trade): if not self.alt_amount_check(
platform, amount, currency, reference, stored_trade
):
return return
platform = stored_trade["subclass"] platform = stored_trade["subclass"]
platform_buyer = stored_trade["buyer"] platform_buyer = stored_trade["buyer"]
# Check sender - we don't do anything with this yet # Check sender - we don't do anything with this yet
sender_valid = self.antifraud.check_valid_sender(reference, platform, sender, platform_buyer) sender_valid = self.antifraud.check_valid_sender(
reference, platform, sender, platform_buyer
)
self.log.info(f"Trade {reference} buyer {platform_buyer} valid: {sender_valid}") self.log.info(f"Trade {reference} buyer {platform_buyer} valid: {sender_valid}")
# trade_released = self.release_map_trade(reference, txid) # trade_released = self.release_map_trade(reference, txid)
# if trade_released: # if trade_released:
@ -325,7 +361,9 @@ class Transactions(util.Base):
return True return True
elif is_updated is False: elif is_updated is False:
# Already mapped # Already mapped
self.log.error(f"Trade {reference} already has a TX mapped, cannot map {tx}.") self.log.error(
f"Trade {reference} already has a TX mapped, cannot map {tx}."
)
return False return False
def new_trade( def new_trade(
@ -396,9 +434,13 @@ class Transactions(util.Base):
# TODO: use get_ref_map in this function instead of calling get_ref multiple times # TODO: use get_ref_map in this function instead of calling get_ref multiple times
for ref in refs: for ref in refs:
stored_trade = db.get_ref(ref) stored_trade = db.get_ref(ref)
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(f"Find trade returned multiple results for TXID {txid}: {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]

@ -2,12 +2,12 @@
# import requests # import requests
# from json import dumps # from json import dumps
# Project imports
from settings import settings
import sinks.nordigen import sinks.nordigen
import sinks.truelayer import sinks.truelayer
import util import util
from db import r from db import r
# Project imports
from settings import settings
class Sinks(util.Base): class Sinks(util.Base):
@ -54,7 +54,9 @@ class Sinks(util.Base):
difference = util.convert(difference) difference = util.convert(difference)
new_transactions = [x for x in transactions if x["transaction_id"] in difference] new_transactions = [
x for x in transactions if x["transaction_id"] in difference
]
# Rename the new key to the old key so we can run the diff again # Rename the new key to the old key so we can run the diff again
r.rename(new_key_name, old_key_name) r.rename(new_key_name, old_key_name)
@ -79,12 +81,18 @@ class Sinks(util.Base):
fields = ["sort_code", "number", "iban"] fields = ["sort_code", "number", "iban"]
for field in fields: for field in fields:
if field in account: if field in account:
account_infos[bank][index]["account_number"][field] = account[field] account_infos[bank][index]["account_number"][
field
] = account[field]
del account_infos[bank][index][field] del account_infos[bank][index][field]
if len(account["account_number"]) == 1: if len(account["account_number"]) == 1:
account_infos[bank].remove(account) account_infos[bank].remove(account)
self.log.warning(f"Potentially useless bank account: {account}") self.log.warning(f"Potentially useless bank account: {account}")
currencies = [account["currency"] for bank, accounts in account_infos.items() for account in accounts] currencies = [
account["currency"]
for bank, accounts in account_infos.items()
for account in accounts
]
for bank, accounts in account_infos.items(): for bank, accounts in account_infos.items():
self.account_info[bank] = [] self.account_info[bank] = []
for account in accounts: for account in accounts:

@ -1,28 +1,20 @@
# Twisted/Klein imports # Twisted/Klein imports
from twisted.internet.task import LoopingCall from hashlib import sha256
from twisted.internet.defer import inlineCallbacks from json import dumps, loads
# Other library imports # Other library imports
import requests import requests
from simplejson.errors import JSONDecodeError
from json import dumps, loads
from lib.serde.nordigen import (
TXRoot,
AccessToken,
Institutions,
Agreement,
Requisitions,
AccountDetails,
AccountBalancesRoot,
RequisitionResponse,
)
from serde import ValidationError
from hashlib import sha256
import treq import treq
import util
from lib.serde.nordigen import (AccessToken, AccountBalancesRoot,
AccountDetails, Agreement, Institutions,
RequisitionResponse, Requisitions, TXRoot)
from serde import ValidationError
# Project imports # Project imports
from settings import settings from settings import settings
import util from simplejson.errors import JSONDecodeError
from twisted.internet.defer import inlineCallbacks
from twisted.internet.task import LoopingCall
class Nordigen(util.Base): class Nordigen(util.Base):
@ -296,7 +288,9 @@ class Nordigen(util.Base):
ownernames = self.get_ownernames() ownernames = self.get_ownernames()
if account_id in ownernames: if account_id in ownernames:
parsed["ownerName"] = ownernames[account_id] parsed["ownerName"] = ownernames[account_id]
self.log.info(f"Found supplementary owner name for {account_id}: {ownernames[account_id]}") self.log.info(
f"Found supplementary owner name for {account_id}: {ownernames[account_id]}"
)
else: else:
self.log.error(f"No owner name in parsed, cannot use: {account_id}") self.log.error(f"No owner name in parsed, cannot use: {account_id}")
return False return False
@ -387,7 +381,9 @@ class Nordigen(util.Base):
else: else:
# No transaction ID. This is a problem for our implementation # No transaction ID. This is a problem for our implementation
tx_hash = sha256(dumps(transaction, sort_keys=True).encode("utf8")).hexdigest() tx_hash = sha256(
dumps(transaction, sort_keys=True).encode("utf8")
).hexdigest()
transaction["transaction_id"] = tx_hash transaction["transaction_id"] = tx_hash
# Rename timestamp # Rename timestamp

@ -1,18 +1,17 @@
# Twisted/Klein imports # Twisted/Klein imports
from twisted.internet.task import LoopingCall import urllib
from json import dumps, loads
from time import time
# Other library imports # Other library imports
import requests import requests
from simplejson.errors import JSONDecodeError import util
from time import time
from json import dumps, loads
from lib.serde.truelayer import AccountBalancesRoot from lib.serde.truelayer import AccountBalancesRoot
import urllib
from serde import ValidationError from serde import ValidationError
# Project imports # Project imports
from settings import settings from settings import settings
import util from simplejson.errors import JSONDecodeError
from twisted.internet.task import LoopingCall
class TrueLayer(util.Base): class TrueLayer(util.Base):
@ -130,7 +129,9 @@ class TrueLayer(util.Base):
else: else:
self.log.error("Received an authcode we didn't ask for") self.log.error("Received an authcode we didn't ask for")
return return
self.log.info(f"Retrieved access/refresh tokens for {self.current_authcode_bank}") self.log.info(
f"Retrieved access/refresh tokens for {self.current_authcode_bank}"
)
def get_new_tokens_all(self): def get_new_tokens_all(self):
refresh_tokens = loads(settings.TrueLayer.RefreshKeys) refresh_tokens = loads(settings.TrueLayer.RefreshKeys)
@ -174,7 +175,10 @@ class TrueLayer(util.Base):
if "access_token" in parsed.keys(): if "access_token" in parsed.keys():
self.tokens[bank] = parsed["access_token"] self.tokens[bank] = parsed["access_token"]
# self.log.info(f"Refreshed access token for {bank}") # self.log.info(f"Refreshed access token for {bank}")
if len(self.refresh_tokens.keys()) == len(self.tokens.keys()) and not self.authed: if (
len(self.refresh_tokens.keys()) == len(self.tokens.keys())
and not self.authed
):
# We are now fully authenticated and ready to start loops! # We are now fully authenticated and ready to start loops!
self.__authed__() self.__authed__()
self.authed = True self.authed = True
@ -197,7 +201,9 @@ class TrueLayer(util.Base):
try: try:
parsed = r.json() parsed = r.json()
except JSONDecodeError: except JSONDecodeError:
self.log.error("Error parsing accounts response: {content}", content=r.content) self.log.error(
"Error parsing accounts response: {content}", content=r.content
)
return False return False
return parsed return parsed

@ -1,9 +1,8 @@
# Project imports # Project imports
# from settings import settings # from settings import settings
import util
import sources.agora import sources.agora
import sources.localbitcoins import sources.localbitcoins
import util
class Sources(util.Base): class Sources(util.Base):

@ -1,12 +1,10 @@
# Twisted/Klein imports # Twisted/Klein imports
from twisted.internet.defer import inlineCallbacks import sources.local
# Other library imports # Other library imports
from pyotp import TOTP from pyotp import TOTP
# Project imports # Project imports
from settings import settings from settings import settings
import sources.local from twisted.internet.defer import inlineCallbacks
class Agora(sources.local.Local): class Agora(sources.local.Local):
@ -42,7 +40,9 @@ class Agora(sources.local.Local):
""" """
print("CALLING RELEASE FUNDS", contact_id) print("CALLING RELEASE FUNDS", contact_id)
if self.sets.Dummy == "1": if self.sets.Dummy == "1":
self.log.error(f"Running in dummy mode, not releasing funds for {contact_id}") self.log.error(
f"Running in dummy mode, not releasing funds for {contact_id}"
)
return return
payload = {"tradeId": contact_id, "password": self.sets.Pass} payload = {"tradeId": contact_id, "password": self.sets.Pass}
rtrn = yield self.api._api_call( rtrn = yield self.api._api_call(
@ -90,8 +90,12 @@ class Agora(sources.local.Local):
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(f"Not enough funds to withdraw {profit_usd_in_xmr}, as wallet only contains {wallet_xmr}") self.log.error(
self.irc.sendmsg(f"Not enough funds to withdraw {profit_usd_in_xmr}, as wallet only contains {wallet_xmr}") 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}"
)
self.ux.notify.notify_need_topup(profit_usd_in_xmr) self.ux.notify.notify_need_topup(profit_usd_in_xmr)
return return

@ -1,20 +1,18 @@
# Twisted/Klein imports # Twisted/Klein imports
from twisted.internet.task import LoopingCall from datetime import datetime
from twisted.internet.defer import inlineCallbacks
# Other library imports # Other library imports
from json import loads from json import loads
from datetime import datetime
from time import sleep # TODO: async from time import sleep # TODO: async
import db
# Project imports
from settings import settings
import util import util
from lib.agoradesk_py import AgoraDesk from lib.agoradesk_py import AgoraDesk
from lib.localbitcoins_py import LocalBitcoins from lib.localbitcoins_py import LocalBitcoins
import db
from lib.logstash import send_logstash from lib.logstash import send_logstash
# Project imports
from settings import settings
from twisted.internet.defer import inlineCallbacks
from twisted.internet.task import LoopingCall
class Local(util.Base): class Local(util.Base):
@ -28,7 +26,9 @@ class Local(util.Base):
self.api = AgoraDesk(settings.Agora.Token) self.api = AgoraDesk(settings.Agora.Token)
self.sets = settings.Agora self.sets = settings.Agora
elif self.platform == "lbtc": elif self.platform == "lbtc":
self.api = LocalBitcoins(settings.LocalBitcoins.Token, settings.LocalBitcoins.Secret) self.api = LocalBitcoins(
settings.LocalBitcoins.Token, settings.LocalBitcoins.Secret
)
self.sets = settings.LocalBitcoins self.sets = settings.LocalBitcoins
else: else:
self.log.error("Platform not defined.") self.log.error("Platform not defined.")
@ -49,7 +49,9 @@ class Local(util.Base):
provider_map = {"NATIONAL_BANK": "national-bank-transfer"} provider_map = {"NATIONAL_BANK": "national-bank-transfer"}
if reverse: if reverse:
try: try:
return next(key for key, value in provider_map.items() if value == provider) return next(
key for key, value in provider_map.items() if value == provider
)
except StopIteration: except StopIteration:
return False return False
else: else:
@ -221,13 +223,17 @@ class Local(util.Base):
for user, message in messages_tmp[reference][::-1]: for user, message in messages_tmp[reference][::-1]:
if reference in self.last_messages: if reference in self.last_messages:
if not [user, message] in self.last_messages[reference]: if not [user, message] in self.last_messages[reference]:
self.irc.sendmsg(f"[{reference}] ({self.platform}) <{user}> {message}") self.irc.sendmsg(
f"[{reference}] ({self.platform}) <{user}> {message}"
)
# Append sent messages to last_messages so we don't send them again # Append sent messages to last_messages so we don't send them again
self.last_messages[reference].append([user, message]) self.last_messages[reference].append([user, message])
else: else:
self.last_messages[reference] = [[user, message]] self.last_messages[reference] = [[user, message]]
for x in messages_tmp[reference]: for x in messages_tmp[reference]:
self.irc.sendmsg(f"[{reference}] ({self.platform}) <{user}> {message}") self.irc.sendmsg(
f"[{reference}] ({self.platform}) <{user}> {message}"
)
# Purge old trades from cache # Purge old trades from cache
for ref in list(self.last_messages): # We're removing from the list on the fly for ref in list(self.last_messages): # We're removing from the list on the fly
@ -419,10 +425,14 @@ class Local(util.Base):
if not providers: if not providers:
providers = self.markets.get_all_providers(self.platform) providers = self.markets.get_all_providers(self.platform)
sinks_currencies = self.sinks.currencies sinks_currencies = self.sinks.currencies
supported_currencies = [currency for currency in currencies if currency in sinks_currencies] supported_currencies = [
currency for currency in currencies if currency in sinks_currencies
]
currencies = supported_currencies currencies = supported_currencies
# We want to get the ads for each of these currencies and return the result # We want to get the ads for each of these currencies and return the result
rates = self.money.cg.get_price(ids=["monero", "bitcoin"], vs_currencies=currencies) rates = self.money.cg.get_price(
ids=["monero", "bitcoin"], vs_currencies=currencies
)
for asset in assets: for asset in assets:
for currency in currencies: for currency in currencies:
cg_asset_name = crypto_map[asset] cg_asset_name = crypto_map[asset]
@ -493,7 +503,9 @@ class Local(util.Base):
continue continue
else: else:
if "error_code" not in rtrn["response"]["error"]: if "error_code" not in rtrn["response"]["error"]:
self.log.error(f"Error code not in return for ad {ad_id}: {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
@ -551,14 +563,20 @@ class Local(util.Base):
""" """
if payment_details: if payment_details:
payment_details_text = self.markets.format_payment_details(currency, payment_details) payment_details_text = self.markets.format_payment_details(
currency, payment_details
)
ad_text = self.markets.format_ad(asset, currency, payment_details_text) ad_text = self.markets.format_ad(asset, currency, payment_details_text)
min_amount, max_amount = self.money.get_minmax(self.platform, asset, currency) min_amount, max_amount = self.money.get_minmax(
self.platform, asset, currency
)
if self.platform == "lbtc": if self.platform == "lbtc":
bank_name = payment_details["bank"] bank_name = payment_details["bank"]
if self.platform == "agora": if self.platform == "agora":
price_formula = f"coingecko{asset.lower()}usd*usd{currency.lower()}*{self.sets.Margin}" price_formula = (
f"coingecko{asset.lower()}usd*usd{currency.lower()}*{self.sets.Margin}"
)
elif self.platform == "lbtc": elif self.platform == "lbtc":
price_formula = f"btc_in_usd*{self.sets.Margin}*USD_in_{currency}" price_formula = f"btc_in_usd*{self.sets.Margin}*USD_in_{currency}"
@ -605,7 +623,9 @@ class Local(util.Base):
:return: False or dict with response :return: False or dict with response
:rtype: bool or dict :rtype: bool or dict
""" """
dist_list = list(self.markets.create_distribution_list(self.platform, filter_asset)) dist_list = list(
self.markets.create_distribution_list(self.platform, filter_asset)
)
our_ads = yield self.enum_ads() our_ads = yield self.enum_ads()
( (
supported_currencies, supported_currencies,
@ -683,7 +703,10 @@ class Local(util.Base):
for i in range(_size): for i in range(_size):
k = i + 1 k = i + 1
for j in range(k, _size): for j in range(k, _size):
if existing_ads[i] == existing_ads[j] and existing_ads[i] not in repeated: if (
existing_ads[i] == existing_ads[j]
and existing_ads[i] not in repeated
):
repeated.append(existing_ads[i]) repeated.append(existing_ads[i])
actioned = [] actioned = []

@ -1,10 +1,9 @@
# Other library imports # Other library imports
import sources.local
import util
from pyotp import TOTP from pyotp import TOTP
# Project imports # Project imports
from settings import settings from settings import settings
import util
import sources.local
class LBTC(sources.local.Local): class LBTC(sources.local.Local):
@ -39,7 +38,9 @@ class LBTC(sources.local.Local):
:rtype: dict :rtype: dict
""" """
if self.sets.Dummy == "1": if self.sets.Dummy == "1":
self.log.error(f"Running in dummy mode, not releasing funds for {contact_id}") self.log.error(
f"Running in dummy mode, not releasing funds for {contact_id}"
)
return return
payload = { payload = {
"tradeId": contact_id, "tradeId": contact_id,
@ -89,8 +90,12 @@ class LBTC(sources.local.Local):
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(f"Not enough funds to withdraw {profit_usd_in_xmr}, as wallet only contains {wallet_xmr}") self.log.error(
self.irc.sendmsg(f"Not enough funds to withdraw {profit_usd_in_xmr}, as wallet only contains {wallet_xmr}") 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}"
)
self.ux.notify.notify_need_topup(profit_usd_in_xmr) self.ux.notify.notify_need_topup(profit_usd_in_xmr)
return return

File diff suppressed because it is too large Load Diff

@ -1,17 +1,16 @@
import logging
from copy import deepcopy
from json import loads
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from twisted.internet.defer import inlineCallbacks
from json import loads
from copy import deepcopy
import logging
from tests.common import fake_public_ads, cg_prices, expected_to_update
import sources.agora
import lib.markets import lib.markets
import lib.money import lib.money
import util
import settings import settings
import sources.agora
import util
from tests.common import cg_prices, expected_to_update, fake_public_ads
from twisted.internet.defer import inlineCallbacks
class TestAgora(TestCase): class TestAgora(TestCase):
@ -20,7 +19,9 @@ class TestAgora(TestCase):
with open("tests/data/agora_ads.json", "r") as f: with open("tests/data/agora_ads.json", "r") as f:
for line in f.readlines(): for line in f.readlines():
parsed = loads(line) parsed = loads(line)
self.test_return_data[(parsed[2], parsed[1], str(parsed[0]))] = parsed[3] self.test_return_data[(parsed[2], parsed[1], str(parsed[0]))] = parsed[
3
]
super().__init__(*args, *kwargs) super().__init__(*args, *kwargs)
@ -181,10 +182,14 @@ class TestAgora(TestCase):
util.last_online_recent = MagicMock() util.last_online_recent = MagicMock()
util.last_online_recent.return_value = True util.last_online_recent.return_value = True
enum_ads_return = yield self.agora.enum_public_ads("XMR", "USD", self.all_providers) enum_ads_return = yield self.agora.enum_public_ads(
"XMR", "USD", self.all_providers
)
# Ensure there are no duplicates # Ensure there are no duplicates
enum_ads_return_ids = [(x[0], x[1], x[2], x[3], x[4], x[5]) for x in enum_ads_return] enum_ads_return_ids = [
(x[0], x[1], x[2], x[3], x[4], x[5]) for x in enum_ads_return
]
enum_ads_return_ids_dedup = set(enum_ads_return_ids) enum_ads_return_ids_dedup = set(enum_ads_return_ids)
self.assertEqual(len(enum_ads_return_ids), len(enum_ads_return_ids_dedup)) self.assertEqual(len(enum_ads_return_ids), len(enum_ads_return_ids_dedup))
@ -243,7 +248,9 @@ class TestAgora(TestCase):
ad.append(margin) ad.append(margin)
expected_return.append(ad) expected_return.append(ad)
lookup_rates_return = self.agora.money.lookup_rates("agora", enum_ads_return) # TODO: do this properly lookup_rates_return = self.agora.money.lookup_rates(
"agora", enum_ads_return
) # TODO: do this properly
self.assertCountEqual(lookup_rates_return, expected_return) self.assertCountEqual(lookup_rates_return, expected_return)
def test_lookup_rates_not_usd(self): def test_lookup_rates_not_usd(self):
@ -271,5 +278,7 @@ class TestAgora(TestCase):
ad.append(margin) ad.append(margin)
expected_return.append(ad) expected_return.append(ad)
# Test specifying rates= # Test specifying rates=
lookup_rates_return = self.agora.money.lookup_rates("agora", enum_ads_return, rates=cg_prices) lookup_rates_return = self.agora.money.lookup_rates(
"agora", enum_ads_return, rates=cg_prices
)
self.assertCountEqual(lookup_rates_return, expected_return) self.assertCountEqual(lookup_rates_return, expected_return)

@ -1,18 +1,18 @@
import logging
from copy import deepcopy
from json import loads
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from twisted.internet.defer import inlineCallbacks
from json import loads
from copy import deepcopy
import logging
from tests.common import fake_public_ads_lbtc, cg_prices, expected_to_update_lbtc
import sources.localbitcoins
import lib.markets import lib.markets
import lib.money import lib.money
import util
import settings import settings
import sources import sources
import sources.localbitcoins
import util
from tests.common import (cg_prices, expected_to_update_lbtc,
fake_public_ads_lbtc)
from twisted.internet.defer import inlineCallbacks
class TestLBTC(TestCase): class TestLBTC(TestCase):
@ -21,7 +21,9 @@ class TestLBTC(TestCase):
with open("tests/data/lbtc_ads.json", "r") as f: with open("tests/data/lbtc_ads.json", "r") as f:
for line in f.readlines(): for line in f.readlines():
parsed = loads(line) parsed = loads(line)
self.test_return_data[(parsed[2], parsed[1], str(parsed[0]))] = parsed[3] self.test_return_data[(parsed[2], parsed[1], str(parsed[0]))] = parsed[
3
]
super().__init__(*args, *kwargs) super().__init__(*args, *kwargs)
@ -126,10 +128,14 @@ class TestLBTC(TestCase):
util.last_online_recent = MagicMock() util.last_online_recent = MagicMock()
util.last_online_recent.return_value = True util.last_online_recent.return_value = True
enum_ads_return = yield self.lbtc.enum_public_ads("BTC", "GBP", self.all_providers) enum_ads_return = yield self.lbtc.enum_public_ads(
"BTC", "GBP", self.all_providers
)
# Ensure there are no duplicates # Ensure there are no duplicates
enum_ads_return_ids = [(x[0], x[1], x[2], x[3], x[4], x[5]) for x in enum_ads_return] enum_ads_return_ids = [
(x[0], x[1], x[2], x[3], x[4], x[5]) for x in enum_ads_return
]
enum_ads_return_ids_dedup = set(enum_ads_return_ids) enum_ads_return_ids_dedup = set(enum_ads_return_ids)
self.assertEqual(len(enum_ads_return_ids), len(enum_ads_return_ids_dedup)) self.assertEqual(len(enum_ads_return_ids), len(enum_ads_return_ids_dedup))
@ -187,7 +193,9 @@ class TestLBTC(TestCase):
ad.append(margin) ad.append(margin)
expected_return.append(ad) expected_return.append(ad)
lookup_rates_return = self.lbtc.money.lookup_rates("lbtc", enum_ads_return) # TODO: do this properly lookup_rates_return = self.lbtc.money.lookup_rates(
"lbtc", enum_ads_return
) # TODO: do this properly
self.assertCountEqual(lookup_rates_return, expected_return) self.assertCountEqual(lookup_rates_return, expected_return)
def test_lookup_rates_not_usd(self): def test_lookup_rates_not_usd(self):
@ -215,5 +223,7 @@ class TestLBTC(TestCase):
ad.append(margin) ad.append(margin)
expected_return.append(ad) expected_return.append(ad)
# Test specifying rates= # Test specifying rates=
lookup_rates_return = self.lbtc.money.lookup_rates("lbtc", enum_ads_return, rates=cg_prices) lookup_rates_return = self.lbtc.money.lookup_rates(
"lbtc", enum_ads_return, rates=cg_prices
)
self.assertCountEqual(lookup_rates_return, expected_return) self.assertCountEqual(lookup_rates_return, expected_return)

@ -1,10 +1,11 @@
import logging
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock from unittest.mock import MagicMock
from tests.common import fake_public_ads, expected_to_update
import lib.markets import lib.markets
from sources.agora import Agora
import settings import settings
import logging from sources.agora import Agora
from tests.common import expected_to_update, fake_public_ads
class TestMarkets(TestCase): class TestMarkets(TestCase):
@ -105,7 +106,9 @@ class TestMarkets(TestCase):
expected_btc_to_update = [x for x in expected_to_update if x[2] == "BTC"] expected_btc_to_update = [x for x in expected_to_update if x[2] == "BTC"]
self.assertCountEqual(res_btc, expected_btc_to_update) self.assertCountEqual(res_btc, expected_btc_to_update)
res_both = self.markets.get_new_ad_equations("agora", fake_public_ads, ["XMR", "BTC"]) res_both = self.markets.get_new_ad_equations(
"agora", fake_public_ads, ["XMR", "BTC"]
)
self.assertCountEqual(res_both, expected_to_update) self.assertCountEqual(res_both, expected_to_update)
def test_format_ad(self): def test_format_ad(self):
@ -116,7 +119,9 @@ $PAYMENT$
"sort_code": "02-03-04", "sort_code": "02-03-04",
"account_number": "0023-0045", "account_number": "0023-0045",
} }
payment_details_text = self.markets.format_payment_details("GBP", payment_details) payment_details_text = self.markets.format_payment_details(
"GBP", payment_details
)
ad_text = self.markets.format_ad("XMR", "GBP", payment_details_text) ad_text = self.markets.format_ad("XMR", "GBP", payment_details_text)
expected = """* Set **Country of recipient's bank** to **"United Kingdom"** expected = """* Set **Country of recipient's bank** to **"United Kingdom"**
Payment details will be released after verification has passed. Payment details will be released after verification has passed.
@ -130,7 +135,9 @@ If you've already completed verification, they will be sent immediately.
"sort_code": "02-03-04", "sort_code": "02-03-04",
"account_number": "0023-0045", "account_number": "0023-0045",
} }
payment_details_text = self.markets.format_payment_details("GBP", payment_details) payment_details_text = self.markets.format_payment_details(
"GBP", payment_details
)
expected = """Payment details will be released after verification has passed. expected = """Payment details will be released after verification has passed.
If you've already completed verification, they will be sent immediately.""" If you've already completed verification, they will be sent immediately."""
@ -139,5 +146,7 @@ If you've already completed verification, they will be sent immediately."""
expected_real = """* Sort code: **02-03-04** expected_real = """* Sort code: **02-03-04**
* Account number: **0023-0045** * Account number: **0023-0045**
Please send in GBP.""" Please send in GBP."""
payment_details_text_real = self.markets.format_payment_details("GBP", payment_details, real=True) payment_details_text_real = self.markets.format_payment_details(
"GBP", payment_details, real=True
)
self.assertEqual(payment_details_text_real, expected_real) self.assertEqual(payment_details_text_real, expected_real)

@ -1,6 +1,7 @@
import logging
from unittest import TestCase from unittest import TestCase
import lib.money import lib.money
import logging
class TestMoney(TestCase): class TestMoney(TestCase):

@ -1,11 +1,11 @@
import logging
from copy import deepcopy
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock from unittest.mock import MagicMock
from copy import deepcopy
import logging
import lib.transactions
import lib.money
import lib.antifraud import lib.antifraud
import lib.money
import lib.transactions
class TestTransactions(TestCase): class TestTransactions(TestCase):
@ -57,7 +57,9 @@ class TestTransactions(TestCase):
self.money = lib.money.Money() self.money = lib.money.Money()
self.money.get_rates_all = MagicMock() self.money.get_rates_all = MagicMock()
self.money.get_rates_all.return_value = {"GBP": 0.8} self.money.get_rates_all.return_value = {"GBP": 0.8}
self.transactions.money.get_acceptable_margins = self.money.get_acceptable_margins self.transactions.money.get_acceptable_margins = (
self.money.get_acceptable_margins
)
self.trades = { self.trades = {
1: { 1: {

@ -1,8 +1,8 @@
# Other library imports # Other library imports
from httpx import ReadTimeout, ReadError, RemoteProtocolError
from datetime import datetime, timezone
import logging import logging
from datetime import datetime, timezone
from httpx import ReadError, ReadTimeout, RemoteProtocolError
# Project imports # Project imports
from settings import settings from settings import settings
@ -40,7 +40,9 @@ class ColoredFormatter(logging.Formatter):
def format(self, record): def format(self, record):
levelname = record.levelname levelname = record.levelname
if self.use_color and levelname in COLORS: if self.use_color and levelname in COLORS:
levelname_color = COLOR_SEQ % (30 + COLORS[levelname]) + levelname + RESET_SEQ levelname_color = (
COLOR_SEQ % (30 + COLORS[levelname]) + levelname + RESET_SEQ
)
record.levelname = levelname_color record.levelname = levelname_color
return logging.Formatter.format(self, record) return logging.Formatter.format(self, record)

@ -5,9 +5,8 @@
# Project imports # Project imports
# from settings import settings # from settings import settings
import util import util
import ux.irc
import ux.commands import ux.commands
import ux.irc
import ux.notify import ux.notify
import ux.verify import ux.verify

@ -1,9 +1,9 @@
# Other library imports # Other library imports
from json import dumps, loads from json import dumps, loads
import db
# Project imports # Project imports
from settings import settings from settings import settings
import db
class GenericCommands(object): class GenericCommands(object):
@ -27,7 +27,19 @@ class GenericCommands(object):
class create(object): class create(object):
@staticmethod @staticmethod
def run(cmd, spl, length, authed, msg, agora, tx, ux, asset_list, provider_list, caller): def run(
cmd,
spl,
length,
authed,
msg,
agora,
tx,
ux,
asset_list,
provider_list,
caller,
):
""" """
Post an ad on AgoraDesk/LBTC with the given country and currency code. Post an ad on AgoraDesk/LBTC with the given country and currency code.
""" """
@ -47,7 +59,9 @@ class GenericCommands(object):
payment_details=account_info[currency], payment_details=account_info[currency],
) )
if posted["success"]: if posted["success"]:
msg(f"{posted['response']['data']['message']}: {posted['response']['data']['ad_id']}") msg(
f"{posted['response']['data']['message']}: {posted['response']['data']['ad_id']}"
)
else: else:
msg(dumps(posted["response"])) msg(dumps(posted["response"]))
elif length == 5: elif length == 5:
@ -70,7 +84,9 @@ class GenericCommands(object):
payment_details=account_info[currency], payment_details=account_info[currency],
) )
if posted["success"]: if posted["success"]:
msg(f"{posted['response']['data']['message']}: {posted['response']['data']['ad_id']}") msg(
f"{posted['response']['data']['message']}: {posted['response']['data']['ad_id']}"
)
else: else:
msg(dumps(posted["response"])) msg(dumps(posted["response"]))
@ -106,13 +122,17 @@ class GenericCommands(object):
return return
for x in caller.dist_countries(filter_asset=asset): for x in caller.dist_countries(filter_asset=asset):
if x["success"]: if x["success"]:
msg(f"{x['response']['data']['message']}: {x['response']['data']['ad_id']}") msg(
f"{x['response']['data']['message']}: {x['response']['data']['ad_id']}"
)
else: else:
msg(dumps(x["response"])) msg(dumps(x["response"]))
elif length == 1: elif length == 1:
for x in caller.dist_countries(): for x in caller.dist_countries():
if x["success"]: if x["success"]:
msg(f"{x['response']['data']['message']}: {x['response']['data']['ad_id']}") msg(
f"{x['response']['data']['message']}: {x['response']['data']['ad_id']}"
)
else: else:
msg(dumps(x["response"])) msg(dumps(x["response"]))
@ -188,7 +208,9 @@ class GenericCommands(object):
return return
providers = spl[3].split(",") providers = spl[3].split(",")
currency = spl[2] currency = spl[2]
c = caller.get_all_public_ads(assets=[asset], currencies=[currency], providers=providers) c = caller.get_all_public_ads(
assets=[asset], currencies=[currency], providers=providers
)
c.addCallback(GenericCommands.pubads.got_pubads, currency, msg) c.addCallback(GenericCommands.pubads.got_pubads, currency, msg)
class cheat(object): class cheat(object):
@ -268,7 +290,9 @@ 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):
GenericCommands.trades.run(cmd, spl, length, authed, msg, agora, tx, ux, agora) GenericCommands.trades.run(
cmd, spl, length, authed, msg, agora, tx, ux, agora
)
class ltrades(object): class ltrades(object):
authed = True authed = True
@ -277,7 +301,9 @@ 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):
GenericCommands.trades.run(cmd, spl, length, authed, msg, agora, tx, ux, tx.lbtc) GenericCommands.trades.run(
cmd, spl, length, authed, msg, agora, tx, ux, tx.lbtc
)
class acreate(object): class acreate(object):
name = "acreate" name = "acreate"
@ -328,7 +354,9 @@ 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):
GenericCommands.messages.run(cmd, spl, length, authed, msg, agora, tx, ux, agora) GenericCommands.messages.run(
cmd, spl, length, authed, msg, agora, tx, ux, agora
)
class lmessages(object): class lmessages(object):
authed = True authed = True
@ -337,7 +365,9 @@ 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):
GenericCommands.messages.run(cmd, spl, length, authed, msg, agora, tx, ux, tx.lbtc) GenericCommands.messages.run(
cmd, spl, length, authed, msg, agora, tx, ux, tx.lbtc
)
class adist(object): class adist(object):
authed = True authed = True
@ -346,7 +376,18 @@ 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):
GenericCommands.dist.run(cmd, spl, length, authed, msg, agora, tx, ux, settings.Agora.AssetList, agora) GenericCommands.dist.run(
cmd,
spl,
length,
authed,
msg,
agora,
tx,
ux,
settings.Agora.AssetList,
agora,
)
class ldist(object): class ldist(object):
authed = True authed = True
@ -356,7 +397,16 @@ 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):
GenericCommands.dist.run( GenericCommands.dist.run(
cmd, spl, length, authed, msg, agora, tx, ux, settings.LocalBitcoins.AssetList, tx.lbtc cmd,
spl,
length,
authed,
msg,
agora,
tx,
ux,
settings.LocalBitcoins.AssetList,
tx.lbtc,
) )
class aredist(object): class aredist(object):
@ -366,7 +416,9 @@ 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):
GenericCommands.redist.run(cmd, spl, length, authed, msg, agora, tx, ux, agora) GenericCommands.redist.run(
cmd, spl, length, authed, msg, agora, tx, ux, agora
)
class lredist(object): class lredist(object):
authed = True authed = True
@ -375,7 +427,9 @@ 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):
GenericCommands.redist.run(cmd, spl, length, authed, msg, agora, tx, ux, tx.lbtc) GenericCommands.redist.run(
cmd, spl, length, authed, msg, agora, tx, ux, tx.lbtc
)
class astripdupes(object): class astripdupes(object):
authed = True authed = True
@ -384,7 +438,9 @@ 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):
GenericCommands.stripdupes.run(cmd, spl, length, authed, msg, agora, tx, ux, agora) GenericCommands.stripdupes.run(
cmd, spl, length, authed, msg, agora, tx, ux, agora
)
class lstripdupes(object): class lstripdupes(object):
authed = True authed = True
@ -393,7 +449,9 @@ 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):
GenericCommands.stripdupes.run(cmd, spl, length, authed, msg, agora, tx, ux, tx.lbtc) GenericCommands.stripdupes.run(
cmd, spl, length, authed, msg, agora, tx, ux, tx.lbtc
)
class total(object): class total(object):
name = "total" name = "total"
@ -440,7 +498,9 @@ 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):
GenericCommands.message.run(cmd, spl, length, authed, msg, agora, tx, ux, agora.agora) GenericCommands.message.run(
cmd, spl, length, authed, msg, agora, tx, ux, agora.agora
)
class lmessage(object): class lmessage(object):
authed = True authed = True
@ -449,7 +509,9 @@ 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):
GenericCommands.message.run(cmd, spl, length, authed, msg, agora, tx, ux, tx.lbtc.lbtc) GenericCommands.message.run(
cmd, spl, length, authed, msg, agora, tx, ux, tx.lbtc.lbtc
)
class refs(object): class refs(object):
name = "refs" name = "refs"
@ -496,7 +558,9 @@ 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):
GenericCommands.release.run(cmd, spl, length, authed, msg, agora, tx, ux, agora) GenericCommands.release.run(
cmd, spl, length, authed, msg, agora, tx, ux, agora
)
class lrelease(object): class lrelease(object):
authed = True authed = True
@ -505,7 +569,9 @@ 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):
GenericCommands.release.run(cmd, spl, length, authed, msg, agora, tx, ux, tx.lbtc) GenericCommands.release.run(
cmd, spl, length, authed, msg, agora, tx, ux, tx.lbtc
)
class map(object): class map(object):
name = "map" name = "map"
@ -535,7 +601,9 @@ 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):
GenericCommands.nuke.run(cmd, spl, length, authed, msg, agora, tx, ux, agora) GenericCommands.nuke.run(
cmd, spl, length, authed, msg, agora, tx, ux, agora
)
class lnuke(object): class lnuke(object):
authed = True authed = True
@ -544,7 +612,9 @@ 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):
GenericCommands.nuke.run(cmd, spl, length, authed, msg, agora, tx, ux, tx.lbtc) GenericCommands.nuke.run(
cmd, spl, length, authed, msg, agora, tx, ux, tx.lbtc
)
class awallet(object): class awallet(object):
authed = True authed = True
@ -553,7 +623,9 @@ 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):
GenericCommands.wallet.run(cmd, spl, length, authed, msg, agora, tx, ux, True, agora.api) GenericCommands.wallet.run(
cmd, spl, length, authed, msg, agora, tx, ux, True, agora.api
)
class lwallet(object): class lwallet(object):
authed = True authed = True
@ -562,7 +634,9 @@ 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):
GenericCommands.wallet.run(cmd, spl, length, authed, msg, agora, tx, ux, False, tx.lbtc.api) GenericCommands.wallet.run(
cmd, spl, length, authed, msg, agora, tx, ux, False, tx.lbtc.api
)
class apubads(object): class apubads(object):
authed = True authed = True
@ -571,7 +645,18 @@ 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):
GenericCommands.pubads.run(cmd, spl, length, authed, msg, agora, tx, ux, settings.Agora.AssetList, agora) GenericCommands.pubads.run(
cmd,
spl,
length,
authed,
msg,
agora,
tx,
ux,
settings.Agora.AssetList,
agora,
)
class lpubads(object): class lpubads(object):
authed = True authed = True
@ -581,7 +666,16 @@ 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):
GenericCommands.pubads.run( GenericCommands.pubads.run(
cmd, spl, length, authed, msg, agora, tx, ux, settings.LocalBitcoins.AssetList, tx.lbtc cmd,
spl,
length,
authed,
msg,
agora,
tx,
ux,
settings.LocalBitcoins.AssetList,
tx.lbtc,
) )
class acheat(object): class acheat(object):
@ -591,7 +685,18 @@ 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):
GenericCommands.cheat.run(cmd, spl, length, authed, msg, agora, tx, ux, settings.Agora.AssetList, agora) GenericCommands.cheat.run(
cmd,
spl,
length,
authed,
msg,
agora,
tx,
ux,
settings.Agora.AssetList,
agora,
)
class lcheat(object): class lcheat(object):
authed = True authed = True
@ -601,7 +706,16 @@ 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):
GenericCommands.cheat.run( GenericCommands.cheat.run(
cmd, spl, length, authed, msg, agora, tx, ux, settings.LocalBitcoins.AssetList, tx.lbtc cmd,
spl,
length,
authed,
msg,
agora,
tx,
ux,
settings.LocalBitcoins.AssetList,
tx.lbtc,
) )
class acheatnext(object): class acheatnext(object):
@ -611,7 +725,9 @@ 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):
GenericCommands.cheatnext.run(cmd, spl, length, authed, msg, agora, tx, ux, agora) GenericCommands.cheatnext.run(
cmd, spl, length, authed, msg, agora, tx, ux, agora
)
class lcheatnext(object): class lcheatnext(object):
authed = True authed = True
@ -620,7 +736,9 @@ 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):
GenericCommands.cheatnext.run(cmd, spl, length, authed, msg, agora, tx, ux, tx.lbtc) GenericCommands.cheatnext.run(
cmd, spl, length, authed, msg, agora, tx, ux, tx.lbtc
)
class aads(object): class aads(object):
authed = True authed = True
@ -638,7 +756,9 @@ 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):
GenericCommands.ads.run(cmd, spl, length, authed, msg, agora, tx, ux, tx.lbtc) GenericCommands.ads.run(
cmd, spl, length, authed, msg, agora, tx, ux, tx.lbtc
)
class xmr(object): class xmr(object):
name = "xmr" name = "xmr"
@ -647,7 +767,9 @@ 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):
xmr_prices = agora.money.cg.get_price(ids="monero", vs_currencies=["sek", "usd", "gbp"]) xmr_prices = agora.money.cg.get_price(
ids="monero", vs_currencies=["sek", "usd", "gbp"]
)
price_sek = xmr_prices["monero"]["sek"] price_sek = xmr_prices["monero"]["sek"]
price_usd = xmr_prices["monero"]["usd"] price_usd = xmr_prices["monero"]["usd"]
price_gbp = xmr_prices["monero"]["gbp"] price_gbp = xmr_prices["monero"]["gbp"]
@ -660,7 +782,9 @@ 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):
xmr_prices = agora.money.cg.get_price(ids="bitcoin", vs_currencies=["sek", "usd", "gbp"]) xmr_prices = agora.money.cg.get_price(
ids="bitcoin", vs_currencies=["sek", "usd", "gbp"]
)
price_sek = xmr_prices["bitcoin"]["sek"] price_sek = xmr_prices["bitcoin"]["sek"]
price_usd = xmr_prices["bitcoin"]["usd"] price_usd = xmr_prices["bitcoin"]["usd"]
price_gbp = xmr_prices["bitcoin"]["gbp"] price_gbp = xmr_prices["bitcoin"]["gbp"]
@ -673,7 +797,9 @@ 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):
GenericCommands.withdraw.run(cmd, spl, length, authed, msg, agora, tx, ux, agora) GenericCommands.withdraw.run(
cmd, spl, length, authed, msg, agora, tx, ux, agora
)
class lwithdraw(object): class lwithdraw(object):
authed = True authed = True
@ -682,7 +808,9 @@ 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):
GenericCommands.withdraw.run(cmd, spl, length, authed, msg, agora, tx, ux, tx.lbtc) GenericCommands.withdraw.run(
cmd, spl, length, authed, msg, agora, tx, ux, tx.lbtc
)
class remaining(object): class remaining(object):
name = "r" name = "r"
@ -807,7 +935,9 @@ class IRCCommands(object):
account = spl[1] account = spl[1]
accounts = tx.sinks.truelayer.get_accounts(account) accounts = tx.sinks.truelayer.get_accounts(account)
for account in accounts["results"]: for account in accounts["results"]:
msg(f"{account['account_id']} {account['display_name']} {account['currency']}") msg(
f"{account['account_id']} {account['display_name']} {account['currency']}"
)
class naccounts(object): class naccounts(object):
name = "naccounts" name = "naccounts"
@ -870,7 +1000,9 @@ class IRCCommands(object):
if length == 2: if length == 2:
account_id = spl[1] account_id = spl[1]
transactions = tx.sinks.nordigen.get_transactions(account_id) transactions = tx.sinks.nordigen.get_transactions(account_id)
transactions.addCallback(IRCCommands.ntransactions.got_transactions, msg) transactions.addCallback(
IRCCommands.ntransactions.got_transactions, msg
)
class nreqs(object): class nreqs(object):
name = "nreqs" name = "nreqs"
@ -964,7 +1096,9 @@ class IRCCommands(object):
accounts_active.append(account) accounts_active.append(account)
accounts_all = tx.sinks.truelayer.get_accounts(bank) accounts_all = tx.sinks.truelayer.get_accounts(bank)
accounts_unmapped = [ accounts_unmapped = [
x["account_id"] for x in accounts_all["results"] if x["account_id"] not in accounts_active x["account_id"]
for x in accounts_all["results"]
if x["account_id"] not in accounts_active
] ]
msg(f"Unmapped accounts: {', '.join(accounts_unmapped)}") msg(f"Unmapped accounts: {', '.join(accounts_unmapped)}")
@ -982,7 +1116,9 @@ class IRCCommands(object):
class authlink(object): class authlink(object):
name = "authlink" name = "authlink"
authed = True authed = True
helptext = "Create a URL for identity verification. Usage: authlink <identifier>" helptext = (
"Create a URL for identity verification. Usage: authlink <identifier>"
)
@staticmethod @staticmethod
def run(cmd, spl, length, authed, msg, agora, tx, ux): def run(cmd, spl, length, authed, msg, agora, tx, ux):
@ -994,7 +1130,9 @@ class IRCCommands(object):
class checkauth(object): class checkauth(object):
name = "checkauth" name = "checkauth"
authed = True authed = True
helptext = "Check the authentication for an identifier. Usage: checkauth <identifier>" helptext = (
"Check the authentication for an identifier. Usage: checkauth <identifier>"
)
@staticmethod @staticmethod
def run(cmd, spl, length, authed, msg, agora, tx, ux): def run(cmd, spl, length, authed, msg, agora, tx, ux):
@ -1013,8 +1151,12 @@ class IRCCommands(object):
if length == 3: if length == 3:
platform = spl[1] platform = spl[1]
currency = spl[2] currency = spl[2]
account_info = tx.markets.get_matching_account_details(platform, currency) account_info = tx.markets.get_matching_account_details(
bank_details = tx.markets.format_payment_details(currency, account_info, real=True) platform, currency
)
bank_details = tx.markets.format_payment_details(
currency, account_info, real=True
)
if not bank_details: if not bank_details:
msg("Could not get bank details.") msg("Could not get bank details.")
return return
@ -1023,7 +1165,9 @@ class IRCCommands(object):
class id(object): class id(object):
name = "id" name = "id"
authed = True authed = True
helptext = "Get the identity information of a user. Usage: id <platform> <username>" helptext = (
"Get the identity information of a user. Usage: id <platform> <username>"
)
@staticmethod @staticmethod
def run(cmd, spl, length, authed, msg, agora, tx, ux): def run(cmd, spl, length, authed, msg, agora, tx, ux):

@ -1,11 +1,10 @@
# Twisted/Klein imports # Twisted/Klein imports
from twisted.words.protocols import irc import util
from twisted.internet import protocol, reactor, ssl
# Project imports # Project imports
from settings import settings from settings import settings
from twisted.internet import protocol, reactor, ssl
from twisted.words.protocols import irc
from ux.commands import IRCCommands from ux.commands import IRCCommands
import util
class IRCBot(irc.IRCClient): class IRCBot(irc.IRCClient):
@ -18,7 +17,9 @@ class IRCBot(irc.IRCClient):
self.log = log self.log = log
self.cmd = IRCCommands() self.cmd = IRCCommands()
# Parse the commands into "commandname": "commandclass" # Parse the commands into "commandname": "commandclass"
self.cmdhash = {getattr(self.cmd, x).name: x for x in dir(self.cmd) if not x.startswith("_")} self.cmdhash = {
getattr(self.cmd, x).name: x for x in dir(self.cmd) if not x.startswith("_")
}
self.nickname = settings.IRC.Nick self.nickname = settings.IRC.Nick
self.password = settings.IRC.Pass self.password = settings.IRC.Pass
self.realname = self.nickname self.realname = self.nickname

@ -1,9 +1,8 @@
# Other library imports # Other library imports
import requests import requests
import util
# Project imports # Project imports
from settings import settings from settings import settings
import util
class Notify(util.Base): class Notify(util.Base):

@ -1,13 +1,13 @@
# Other library imports # Other library imports
import requests
import hashlib import hashlib
import hmac import hmac
import time
import json import json
import time
import requests
import util
# Project imports # Project imports
from settings import settings from settings import settings
import util
class Verify(util.Base): class Verify(util.Base):
@ -35,7 +35,9 @@ class Verify(util.Base):
""" """
self.antifraud.user_verification_successful(external_user_id) self.antifraud.user_verification_successful(external_user_id)
def update_verification_status(self, external_user_id, review_status, review_answer=None): def update_verification_status(
self, external_user_id, review_status, review_answer=None
):
""" """
Update the authentication status of a external user ID. Update the authentication status of a external user ID.
""" """
@ -46,7 +48,11 @@ class Verify(util.Base):
if type(content) == str: if type(content) == str:
content = content.encode("utf-8") content = content.encode("utf-8")
# hmac needs bytes # hmac needs bytes
signature = hmac.new(settings.Verify.WebHookSecret.encode("utf-8"), content, digestmod=hashlib.sha1).hexdigest() signature = hmac.new(
settings.Verify.WebHookSecret.encode("utf-8"),
content,
digestmod=hashlib.sha1,
).hexdigest()
return signature == payload_digest return signature == payload_digest
@ -68,7 +74,9 @@ class Verify(util.Base):
if "reviewAnswer" in content_json["reviewResult"]: if "reviewAnswer" in content_json["reviewResult"]:
review_answer = content_json["reviewResult"]["reviewAnswer"] review_answer = content_json["reviewResult"]["reviewAnswer"]
self.update_verification_status(external_user_id, review_status, review_answer=review_answer) self.update_verification_status(
external_user_id, review_status, review_answer=review_answer
)
return True return True
def handle_callback(self, request): def handle_callback(self, request):
@ -137,7 +145,10 @@ class Verify(util.Base):
""" """
Get the status of an applicant by the external user ID. Get the status of an applicant by the external user ID.
""" """
url = settings.Verify.Base + f"/resources/applicants/-;externalUserId={external_user_id}/one" url = (
settings.Verify.Base
+ f"/resources/applicants/-;externalUserId={external_user_id}/one"
)
resp = self.sign_request(requests.Request("GET", url)) resp = self.sign_request(requests.Request("GET", url))
s = requests.Session() s = requests.Session()
response = s.send(resp) response = s.send(resp)
@ -173,10 +184,19 @@ class Verify(util.Base):
""" """
Get an access token for an external user ID. Get an access token for an external user ID.
""" """
params = {"userId": external_user_id, "ttlInSecs": "600", "levelName": level_name} params = {
"userId": external_user_id,
"ttlInSecs": "600",
"levelName": level_name,
}
headers = {"Content-Type": "application/json", "Content-Encoding": "utf-8"} headers = {"Content-Type": "application/json", "Content-Encoding": "utf-8"}
resp = self.sign_request( resp = self.sign_request(
requests.Request("POST", f"{settings.Verify.Base}/resources/accessTokens", params=params, headers=headers) requests.Request(
"POST",
f"{settings.Verify.Base}/resources/accessTokens",
params=params,
headers=headers,
)
) )
s = requests.Session() s = requests.Session()
response = s.send(resp) response = s.send(resp)
@ -196,9 +216,16 @@ class Verify(util.Base):
body = b"" if prepared_request.body is None else prepared_request.body body = b"" if prepared_request.body is None else prepared_request.body
if type(body) == str: if type(body) == str:
body = body.encode("utf-8") body = body.encode("utf-8")
data_to_sign = str(now).encode("utf-8") + method.encode("utf-8") + path_url.encode("utf-8") + body data_to_sign = (
str(now).encode("utf-8")
+ method.encode("utf-8")
+ path_url.encode("utf-8")
+ body
)
# hmac needs bytes # hmac needs bytes
signature = hmac.new(settings.Verify.Key.encode("utf-8"), data_to_sign, digestmod=hashlib.sha256) signature = hmac.new(
settings.Verify.Key.encode("utf-8"), data_to_sign, digestmod=hashlib.sha256
)
prepared_request.headers["X-App-Token"] = settings.Verify.Token prepared_request.headers["X-App-Token"] = settings.Verify.Token
prepared_request.headers["X-App-Access-Ts"] = str(now) prepared_request.headers["X-App-Access-Ts"] = str(now)
prepared_request.headers["X-App-Access-Sig"] = signature.hexdigest() prepared_request.headers["X-App-Access-Sig"] = signature.hexdigest()

Loading…
Cancel
Save