Reformat project

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

@ -1,13 +1,21 @@
repos:
- repo: https://github.com/ambv/black
rev: 22.3.0
- repo: https://github.com/psf/black
rev: 22.6.0
hooks:
- id: black
args:
- --line-length=120
- repo: https://gitlab.com/pycqa/flake8
exclude: ^core/migrations
- repo: https://github.com/PyCQA/isort
rev: 5.10.1
hooks:
- id: isort
- repo: https://github.com/PyCQA/flake8
rev: 4.0.1
hooks:
- id: flake8
args:
- "--max-line-length=120"
- id: flake8
args: [--max-line-length=88]
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
# Twisted/Klein imports
from twisted.internet import reactor
from klein import Klein
from twisted.internet.protocol import Factory
# Other library imports
from json import dumps
from signal import signal, SIGINT
from signal import SIGINT, signal
from sys import argv
# Project imports
from settings import settings
import util
import lib.antifraud
import lib.logstash
import lib.markets
import lib.money
import lib.transactions
# New style classes
import sinks
import sources
import util
import ux
import lib.antifraud
import lib.transactions
import lib.markets
import lib.money
import lib.logstash
from klein import Klein
# Project imports
from settings import settings
from twisted.internet import reactor
from twisted.internet.protocol import Factory
init_map = None
Factory.noisy = False

@ -1,9 +1,8 @@
# Other library imports
import util
from redis import StrictRedis
# Project imports
from settings import settings
import util
log = util.get_logger("DB")
@ -129,7 +128,9 @@ def cleanup(subclass, references):
for tx, reference in get_ref_map().items():
if reference not in references:
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)
log.info(logmessage)
r.rename(f"trade.{tx}.reference", f"archive.trade.{tx}.reference")

@ -3,18 +3,13 @@
# 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
from typing import Any, Dict, List, Optional, Union
import arrow
import treq
from twisted.internet.defer import inlineCallbacks
# Project imports
import util
from twisted.internet.defer import inlineCallbacks
__author__ = "marvin8"
__copyright__ = "(C) 2021 https://codeberg.org/MarvinsCryptoTools/agoradesk_py"
@ -88,7 +83,8 @@ class AgoraDesk:
headers = {
"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,
}
@ -253,7 +249,9 @@ class AgoraDesk:
# ===========================
# 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.
https://agoradesk.com/api-docs/v1#operation/setUserFeedback
@ -279,7 +277,9 @@ class AgoraDesk:
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
def contact_cancel(
@ -299,7 +299,9 @@ class AgoraDesk:
# post/contact_escrow/{trade_id} • Enable escrow
# 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.
https://agoradesk.com/api-docs/v1#operation/getTradeMessages
@ -354,7 +356,9 @@ class AgoraDesk:
# Todo: Add image upload functionality
# 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.
https://agoradesk.com/api-docs/v1#operation/sendChatMessage
@ -504,7 +508,9 @@ class AgoraDesk:
if track_max_amount:
params["track_max_amount"] = 1 if track_max_amount else 0
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:
params["verified_email_required"] = 1 if verified_email_required else 0
if online_provider:
@ -680,7 +686,8 @@ class AgoraDesk:
params = self._generic_search_parameters(amount, page)
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,
)
@ -823,7 +830,8 @@ class AgoraDesk:
params = self._generic_search_parameters(amount, page)
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,
)
@ -938,7 +946,9 @@ class AgoraDesk:
# 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.
https://agoradesk.com/api-docs/v1#operation/getXmrTicker and
@ -1028,7 +1038,9 @@ class AgoraDesk:
if 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(
self,

@ -1,6 +1,6 @@
# Project imports
import util
import db
import util
class AntiFraud(util.Base):
@ -46,7 +46,9 @@ class AntiFraud(util.Base):
return True
if platform_buyer in senders:
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
def check_tx_sender(self, tx, reference):
@ -65,7 +67,9 @@ class AntiFraud(util.Base):
bank_sender = stored_tx["sender"]
platform_buyer = stored_trade["buyer"]
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:
return True
return False

@ -1,29 +1,22 @@
"""See https://agoradesk.com/api-docs/v1."""
# pylint: disable=too-many-lines
# 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 hmac as hmac_lib
import requests
import json
import logging
import sys
import time
from typing import Any, Dict, List, Optional, Union
from urllib.parse import urlparse
import arrow
import requests
import treq
# Project imports
import util
from twisted.internet.defer import inlineCallbacks
from urllib.parse import urlparse
__author__ = "marvin8"
__copyright__ = "(C) 2021 https://codeberg.org/MarvinsCryptoTools/agoradesk_py"
__version__ = "0.1.0"
@ -86,17 +79,25 @@ class LocalBitcoins:
message += params_encoded.encode("ascii")
else:
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
def encode_params(self, http_method, api_call_url, query_values):
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
# GET method
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
return (api_request, params_encoded)
@ -132,7 +133,9 @@ class LocalBitcoins:
url = url[len(SERVER) :] # noqa
# 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")
signature = self.sign_payload(nonce, url, params_encoded)
@ -288,7 +291,9 @@ class LocalBitcoins:
# ===========================
# 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.
https://localbitcoins.com/api-docs/#feedback
@ -314,7 +319,9 @@ class LocalBitcoins:
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
def contact_cancel(
@ -334,7 +341,9 @@ class LocalBitcoins:
# post/contact_escrow/{trade_id} • Enable escrow
# 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.
https://localbitcoins.com/api-docs/#contact-message
@ -389,7 +398,9 @@ class LocalBitcoins:
# Todo: Add image upload functionality
# 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.
https://localbitcoins.com/api-docs/#contact-post
@ -726,7 +737,8 @@ class LocalBitcoins:
params = self._generic_search_parameters(amount, page)
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,
)

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

@ -1,10 +1,10 @@
# Other library imports
from json import loads
import db
import util
# Project imports
from settings import settings
import util
import db
class Markets(util.Base):
@ -60,7 +60,9 @@ class Markets(util.Base):
self.log.info(f"Sending bank details/reference for {platform}/{trade_id}")
if send_setting == "1":
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:
self.log.error(f"Payment info invalid: {formatted_account_info}")
return
@ -110,12 +112,21 @@ class Markets(util.Base):
currencies = self.get_all_currencies(platform)
providers = self.get_all_providers(platform)
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
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
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:
# Filter currency
try:
@ -123,22 +134,32 @@ class Markets(util.Base):
except KeyError:
# self.log.error("Error getting public ads for currency {currency}", currency=currency)
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
continue
# Filter asset
public_ads_filtered = [ad for ad in public_ads_currency if ad[4] == asset]
# 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]
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
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)
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":
new_formula = f"btc_in_usd*{new_margin}*USD_in_{currency}"
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]
# self.log.debug("Ads without us: {x}", x=ads_without_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)
# Check that this list without us is not empty
if ads_without_us:
@ -247,7 +270,9 @@ class Markets(util.Base):
currencies = self.sinks.currencies
account_info = self.sinks.account_info
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 = {}
for currency in supported_currencies:
for bank, accounts in account_info.items():
@ -255,11 +280,16 @@ class Markets(util.Base):
if account["currency"] == currency:
currency_account_info_map[currency] = account["account_number"]
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)
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:
return False
return currency_account_info_map[currency]
@ -278,7 +308,10 @@ class Markets(util.Base):
currencies = self.sinks.currencies
if not 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]
@ -329,7 +362,9 @@ class Markets(util.Base):
"""
platforms = ("agora", "lbtc")
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):
"""

@ -1,19 +1,18 @@
# 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
from opensearchpy import OpenSearch
from datetime import datetime
# Project imports
from settings import settings
import urllib3
import util
from forex_python.converter import CurrencyRates
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
urllib3.disable_warnings()
@ -74,7 +73,15 @@ class Money(util.Base):
total_remaining = self.get_total_remaining()
total_with_trades = self.get_total_with_trades()
# 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
def setup_loops(self):
@ -382,7 +389,9 @@ class Money(util.Base):
if not total_usd:
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
cast_es = {
"remaining_usd": remaining,
@ -416,13 +425,17 @@ class Money(util.Base):
asset = "BTC"
if asset == "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:
return False
crypto_usd = float(history["market_data"]["current_price"]["usd"])
elif asset == "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"])
# Convert crypto to fiat
amount = float(amount_crypto) * crypto_usd
@ -476,7 +489,9 @@ class Money(util.Base):
if not total_usd:
return False
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
cast_es = {

@ -1,16 +1,14 @@
# Twisted/Klein imports
from twisted.internet.defer import inlineCallbacks
# Other library imports
from json import dumps
from random import choices
from string import ascii_uppercase
# Project imports
from settings import settings
import db
import util
# Project imports
from settings import settings
from twisted.internet.defer import inlineCallbacks
class Transactions(util.Base):
@ -95,7 +93,9 @@ class Transactions(util.Base):
if len(stored_trade_reference) > 1:
self.log.error(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
if len(stored_trade_reference) == 0:
return None
@ -105,10 +105,16 @@ class Transactions(util.Base):
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"]
if float(amount_usd) > float(settings.Agora.AcceptableAltLookupUSD):
self.log.info("Not checking against amount and currency as amount exceeds MAX")
self.irc.sendmsg("Not checking against amount and currency as amount exceeds MAX")
self.log.info(
"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
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 True
@ -124,9 +130,15 @@ class Transactions(util.Base):
return False
stored_trade = self.find_trade(txid, currency, amount)
if not stored_trade:
self.log.info(f"Failed to get reference by amount and currency: {txid} {currency} {amount}")
self.irc.sendmsg(f"Failed to get reference by amount and currency: {txid} {currency} {amount}")
self.ux.notify.notify_tx_lookup_failed(currency, amount, reference, "ALT_LOOKUP_FAILED")
self.log.info(
f"Failed to get reference by amount and currency: {txid} {currency} {amount}"
)
self.irc.sendmsg(
f"Failed to get reference by amount and currency: {txid} {currency} {amount}"
)
self.ux.notify.notify_tx_lookup_failed(
currency, amount, reference, "ALT_LOOKUP_FAILED"
)
return None
stored_trade["amount"] = float(stored_trade["amount"]) # convert to float
return stored_trade
@ -136,15 +148,21 @@ class Transactions(util.Base):
if not stored_trade:
self.log.info(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
stored_trade["amount"] = float(stored_trade["amount"]) # convert to float
return stored_trade
def currency_check(self, currency, amount, reference, stored_trade):
if not stored_trade["currency"] == currency:
self.log.info(f"Currency mismatch, Agora: {stored_trade['currency']} / Sink: {currency}")
self.irc.sendmsg(f"Currency mismatch, Agora: {stored_trade['currency']} / Sink: {currency}")
self.log.info(
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(
currency,
amount,
@ -157,9 +175,15 @@ class Transactions(util.Base):
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
min_amount, max_amount = self.money.get_acceptable_margins(platform, currency, stored_trade["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}")
min_amount, max_amount = self.money.get_acceptable_margins(
platform, currency, stored_trade["amount"]
)
self.log.info(
f"Amount does not match exactly, trying with margins: min: {min_amount} / max: {max_amount}"
)
self.irc.sendmsg(
f"Amount does not match exactly, trying with margins: min: {min_amount} / max: {max_amount}"
)
if not min_amount < amount < max_amount:
self.log.info(
"Amount mismatch - not in margins: {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)
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
return
@ -220,14 +248,18 @@ class Transactions(util.Base):
# Normal implementation for when we have a 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:
# return
# Amount/currency lookup implementation for when we have no reference
else:
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:
return
if stored_trade:
@ -249,13 +281,17 @@ class Transactions(util.Base):
if looked_up_without_reference:
return
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
platform = stored_trade["subclass"]
platform_buyer = stored_trade["buyer"]
# 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}")
# trade_released = self.release_map_trade(reference, txid)
# if trade_released:
@ -325,7 +361,9 @@ class Transactions(util.Base):
return True
elif is_updated is False:
# 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
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
for ref in refs:
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)
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 matching_refs[0]

@ -2,12 +2,12 @@
# import requests
# from json import dumps
# Project imports
from settings import settings
import sinks.nordigen
import sinks.truelayer
import util
from db import r
# Project imports
from settings import settings
class Sinks(util.Base):
@ -54,7 +54,9 @@ class Sinks(util.Base):
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
r.rename(new_key_name, old_key_name)
@ -79,12 +81,18 @@ class Sinks(util.Base):
fields = ["sort_code", "number", "iban"]
for field in fields:
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]
if len(account["account_number"]) == 1:
account_infos[bank].remove(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():
self.account_info[bank] = []
for account in accounts:

@ -1,28 +1,20 @@
# Twisted/Klein imports
from twisted.internet.task import LoopingCall
from twisted.internet.defer import inlineCallbacks
from hashlib import sha256
from json import dumps, loads
# Other library imports
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 util
from lib.serde.nordigen import (AccessToken, AccountBalancesRoot,
AccountDetails, Agreement, Institutions,
RequisitionResponse, Requisitions, TXRoot)
from serde import ValidationError
# Project imports
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):
@ -296,7 +288,9 @@ class Nordigen(util.Base):
ownernames = self.get_ownernames()
if account_id in ownernames:
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:
self.log.error(f"No owner name in parsed, cannot use: {account_id}")
return False
@ -387,7 +381,9 @@ class Nordigen(util.Base):
else:
# 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
# Rename timestamp

@ -1,18 +1,17 @@
# Twisted/Klein imports
from twisted.internet.task import LoopingCall
import urllib
from json import dumps, loads
from time import time
# Other library imports
import requests
from simplejson.errors import JSONDecodeError
from time import time
from json import dumps, loads
import util
from lib.serde.truelayer import AccountBalancesRoot
import urllib
from serde import ValidationError
# Project imports
from settings import settings
import util
from simplejson.errors import JSONDecodeError
from twisted.internet.task import LoopingCall
class TrueLayer(util.Base):
@ -130,7 +129,9 @@ class TrueLayer(util.Base):
else:
self.log.error("Received an authcode we didn't ask for")
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):
refresh_tokens = loads(settings.TrueLayer.RefreshKeys)
@ -174,7 +175,10 @@ class TrueLayer(util.Base):
if "access_token" in parsed.keys():
self.tokens[bank] = parsed["access_token"]
# 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!
self.__authed__()
self.authed = True
@ -197,7 +201,9 @@ class TrueLayer(util.Base):
try:
parsed = r.json()
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 parsed

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

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

@ -1,20 +1,18 @@
# Twisted/Klein imports
from twisted.internet.task import LoopingCall
from twisted.internet.defer import inlineCallbacks
from datetime import datetime
# Other library imports
from json import loads
from datetime import datetime
from time import sleep # TODO: async
# Project imports
from settings import settings
import db
import util
from lib.agoradesk_py import AgoraDesk
from lib.localbitcoins_py import LocalBitcoins
import db
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):
@ -28,7 +26,9 @@ class Local(util.Base):
self.api = AgoraDesk(settings.Agora.Token)
self.sets = settings.Agora
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
else:
self.log.error("Platform not defined.")
@ -49,7 +49,9 @@ class Local(util.Base):
provider_map = {"NATIONAL_BANK": "national-bank-transfer"}
if reverse:
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:
return False
else:
@ -221,13 +223,17 @@ class Local(util.Base):
for user, message in messages_tmp[reference][::-1]:
if reference in self.last_messages:
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
self.last_messages[reference].append([user, message])
else:
self.last_messages[reference] = [[user, message]]
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
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:
providers = self.markets.get_all_providers(self.platform)
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
# 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 currency in currencies:
cg_asset_name = crypto_map[asset]
@ -493,7 +503,9 @@ class Local(util.Base):
continue
else:
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
if rtrn["response"]["error"]["error_code"] == 429:
throttled += 1
@ -551,14 +563,20 @@ class Local(util.Base):
"""
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)
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":
bank_name = payment_details["bank"]
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":
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
: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()
(
supported_currencies,
@ -683,7 +703,10 @@ class Local(util.Base):
for i in range(_size):
k = i + 1
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])
actioned = []

@ -1,10 +1,9 @@
# Other library imports
import sources.local
import util
from pyotp import TOTP
# Project imports
from settings import settings
import util
import sources.local
class LBTC(sources.local.Local):
@ -39,7 +38,9 @@ class LBTC(sources.local.Local):
:rtype: dict
"""
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
payload = {
"tradeId": contact_id,
@ -89,8 +90,12 @@ class LBTC(sources.local.Local):
if not float(wallet_xmr) > profit_usd_in_xmr:
# 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.irc.sendmsg(f"Not enough funds to withdraw {profit_usd_in_xmr}, as wallet only contains {wallet_xmr}")
self.log.error(
f"Not enough funds to withdraw {profit_usd_in_xmr}, as wallet only contains {wallet_xmr}"
)
self.irc.sendmsg(
f"Not enough funds to withdraw {profit_usd_in_xmr}, as wallet only contains {wallet_xmr}"
)
self.ux.notify.notify_need_topup(profit_usd_in_xmr)
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.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.money
import util
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):
@ -20,7 +19,9 @@ class TestAgora(TestCase):
with open("tests/data/agora_ads.json", "r") as f:
for line in f.readlines():
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)
@ -181,10 +182,14 @@ class TestAgora(TestCase):
util.last_online_recent = MagicMock()
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
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)
self.assertEqual(len(enum_ads_return_ids), len(enum_ads_return_ids_dedup))
@ -243,7 +248,9 @@ class TestAgora(TestCase):
ad.append(margin)
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)
def test_lookup_rates_not_usd(self):
@ -271,5 +278,7 @@ class TestAgora(TestCase):
ad.append(margin)
expected_return.append(ad)
# 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)

@ -1,18 +1,18 @@
import logging
from copy import deepcopy
from json import loads
from unittest import TestCase
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.money
import util
import settings
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):
@ -21,7 +21,9 @@ class TestLBTC(TestCase):
with open("tests/data/lbtc_ads.json", "r") as f:
for line in f.readlines():
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)
@ -126,10 +128,14 @@ class TestLBTC(TestCase):
util.last_online_recent = MagicMock()
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
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)
self.assertEqual(len(enum_ads_return_ids), len(enum_ads_return_ids_dedup))
@ -187,7 +193,9 @@ class TestLBTC(TestCase):
ad.append(margin)
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)
def test_lookup_rates_not_usd(self):
@ -215,5 +223,7 @@ class TestLBTC(TestCase):
ad.append(margin)
expected_return.append(ad)
# 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)

@ -1,10 +1,11 @@
import logging
from unittest import TestCase
from unittest.mock import MagicMock
from tests.common import fake_public_ads, expected_to_update
import lib.markets
from sources.agora import Agora
import settings
import logging
from sources.agora import Agora
from tests.common import expected_to_update, fake_public_ads
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"]
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)
def test_format_ad(self):
@ -116,7 +119,9 @@ $PAYMENT$
"sort_code": "02-03-04",
"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)
expected = """* Set **Country of recipient's bank** to **"United Kingdom"**
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",
"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.
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**
* Account number: **0023-0045**
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)

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

@ -1,11 +1,11 @@
import logging
from copy import deepcopy
from unittest import TestCase
from unittest.mock import MagicMock
from copy import deepcopy
import logging
import lib.transactions
import lib.money
import lib.antifraud
import lib.money
import lib.transactions
class TestTransactions(TestCase):
@ -57,7 +57,9 @@ class TestTransactions(TestCase):
self.money = lib.money.Money()
self.money.get_rates_all = MagicMock()
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 = {
1: {

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

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

@ -1,9 +1,9 @@
# Other library imports
from json import dumps, loads
import db
# Project imports
from settings import settings
import db
class GenericCommands(object):
@ -27,7 +27,19 @@ class GenericCommands(object):
class create(object):
@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.
"""
@ -47,7 +59,9 @@ class GenericCommands(object):
payment_details=account_info[currency],
)
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:
msg(dumps(posted["response"]))
elif length == 5:
@ -70,7 +84,9 @@ class GenericCommands(object):
payment_details=account_info[currency],
)
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:
msg(dumps(posted["response"]))
@ -106,13 +122,17 @@ class GenericCommands(object):
return
for x in caller.dist_countries(filter_asset=asset):
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:
msg(dumps(x["response"]))
elif length == 1:
for x in caller.dist_countries():
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:
msg(dumps(x["response"]))
@ -188,7 +208,9 @@ class GenericCommands(object):
return
providers = spl[3].split(",")
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)
class cheat(object):
@ -268,7 +290,9 @@ class IRCCommands(object):
@staticmethod
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):
authed = True
@ -277,7 +301,9 @@ class IRCCommands(object):
@staticmethod
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):
name = "acreate"
@ -328,7 +354,9 @@ class IRCCommands(object):
@staticmethod
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):
authed = True
@ -337,7 +365,9 @@ class IRCCommands(object):
@staticmethod
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):
authed = True
@ -346,7 +376,18 @@ class IRCCommands(object):
@staticmethod
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):
authed = True
@ -356,7 +397,16 @@ class IRCCommands(object):
@staticmethod
def run(cmd, spl, length, authed, msg, agora, tx, ux):
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):
@ -366,7 +416,9 @@ class IRCCommands(object):
@staticmethod
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):
authed = True
@ -375,7 +427,9 @@ class IRCCommands(object):
@staticmethod
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):
authed = True
@ -384,7 +438,9 @@ class IRCCommands(object):
@staticmethod
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):
authed = True
@ -393,7 +449,9 @@ class IRCCommands(object):
@staticmethod
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):
name = "total"
@ -440,7 +498,9 @@ class IRCCommands(object):
@staticmethod
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):
authed = True
@ -449,7 +509,9 @@ class IRCCommands(object):
@staticmethod
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):
name = "refs"
@ -496,7 +558,9 @@ class IRCCommands(object):
@staticmethod
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):
authed = True
@ -505,7 +569,9 @@ class IRCCommands(object):
@staticmethod
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):
name = "map"
@ -535,7 +601,9 @@ class IRCCommands(object):
@staticmethod
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):
authed = True
@ -544,7 +612,9 @@ class IRCCommands(object):
@staticmethod
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):
authed = True
@ -553,7 +623,9 @@ class IRCCommands(object):
@staticmethod
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):
authed = True
@ -562,7 +634,9 @@ class IRCCommands(object):
@staticmethod
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):
authed = True
@ -571,7 +645,18 @@ class IRCCommands(object):
@staticmethod
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):
authed = True
@ -581,7 +666,16 @@ class IRCCommands(object):
@staticmethod
def run(cmd, spl, length, authed, msg, agora, tx, ux):
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):
@ -591,7 +685,18 @@ class IRCCommands(object):
@staticmethod
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):
authed = True
@ -601,7 +706,16 @@ class IRCCommands(object):
@staticmethod
def run(cmd, spl, length, authed, msg, agora, tx, ux):
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):
@ -611,7 +725,9 @@ class IRCCommands(object):
@staticmethod
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):
authed = True
@ -620,7 +736,9 @@ class IRCCommands(object):
@staticmethod
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):
authed = True
@ -638,7 +756,9 @@ class IRCCommands(object):
@staticmethod
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):
name = "xmr"
@ -647,7 +767,9 @@ class IRCCommands(object):
@staticmethod
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_usd = xmr_prices["monero"]["usd"]
price_gbp = xmr_prices["monero"]["gbp"]
@ -660,7 +782,9 @@ class IRCCommands(object):
@staticmethod
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_usd = xmr_prices["bitcoin"]["usd"]
price_gbp = xmr_prices["bitcoin"]["gbp"]
@ -673,7 +797,9 @@ class IRCCommands(object):
@staticmethod
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):
authed = True
@ -682,7 +808,9 @@ class IRCCommands(object):
@staticmethod
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):
name = "r"
@ -807,7 +935,9 @@ class IRCCommands(object):
account = spl[1]
accounts = tx.sinks.truelayer.get_accounts(account)
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):
name = "naccounts"
@ -870,7 +1000,9 @@ class IRCCommands(object):
if length == 2:
account_id = spl[1]
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):
name = "nreqs"
@ -964,7 +1096,9 @@ class IRCCommands(object):
accounts_active.append(account)
accounts_all = tx.sinks.truelayer.get_accounts(bank)
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)}")
@ -982,7 +1116,9 @@ class IRCCommands(object):
class authlink(object):
name = "authlink"
authed = True
helptext = "Create a URL for identity verification. Usage: authlink <identifier>"
helptext = (
"Create a URL for identity verification. Usage: authlink <identifier>"
)
@staticmethod
def run(cmd, spl, length, authed, msg, agora, tx, ux):
@ -994,7 +1130,9 @@ class IRCCommands(object):
class checkauth(object):
name = "checkauth"
authed = True
helptext = "Check the authentication for an identifier. Usage: checkauth <identifier>"
helptext = (
"Check the authentication for an identifier. Usage: checkauth <identifier>"
)
@staticmethod
def run(cmd, spl, length, authed, msg, agora, tx, ux):
@ -1013,8 +1151,12 @@ class IRCCommands(object):
if length == 3:
platform = spl[1]
currency = spl[2]
account_info = tx.markets.get_matching_account_details(platform, currency)
bank_details = tx.markets.format_payment_details(currency, account_info, real=True)
account_info = tx.markets.get_matching_account_details(
platform, currency
)
bank_details = tx.markets.format_payment_details(
currency, account_info, real=True
)
if not bank_details:
msg("Could not get bank details.")
return
@ -1023,7 +1165,9 @@ class IRCCommands(object):
class id(object):
name = "id"
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
def run(cmd, spl, length, authed, msg, agora, tx, ux):

@ -1,11 +1,10 @@
# Twisted/Klein imports
from twisted.words.protocols import irc
from twisted.internet import protocol, reactor, ssl
import util
# Project imports
from settings import settings
from twisted.internet import protocol, reactor, ssl
from twisted.words.protocols import irc
from ux.commands import IRCCommands
import util
class IRCBot(irc.IRCClient):
@ -18,7 +17,9 @@ class IRCBot(irc.IRCClient):
self.log = log
self.cmd = IRCCommands()
# 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.password = settings.IRC.Pass
self.realname = self.nickname

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

@ -1,13 +1,13 @@
# Other library imports
import requests
import hashlib
import hmac
import time
import json
import time
import requests
import util
# Project imports
from settings import settings
import util
class Verify(util.Base):
@ -35,7 +35,9 @@ class Verify(util.Base):
"""
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.
"""
@ -46,7 +48,11 @@ class Verify(util.Base):
if type(content) == str:
content = content.encode("utf-8")
# 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
@ -68,7 +74,9 @@ class Verify(util.Base):
if "reviewAnswer" in content_json["reviewResult"]:
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
def handle_callback(self, request):
@ -137,7 +145,10 @@ class Verify(util.Base):
"""
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))
s = requests.Session()
response = s.send(resp)
@ -173,10 +184,19 @@ class Verify(util.Base):
"""
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"}
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()
response = s.send(resp)
@ -196,9 +216,16 @@ class Verify(util.Base):
body = b"" if prepared_request.body is None else prepared_request.body
if type(body) == str:
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
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-Access-Ts"] = str(now)
prepared_request.headers["X-App-Access-Sig"] = signature.hexdigest()

Loading…
Cancel
Save