Reformat project
This commit is contained in:
parent
933642def6
commit
ea81748c0a
|
@ -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"
|
||||
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
|
||||
import treq
|
||||
from twisted.internet.defer import inlineCallbacks
|
||||
|
||||
|
||||
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
|
||||
|
||||
__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
|
||||
|
||||
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
|
||||
import util
|
||||
from lib.logstash import send_logstash
|
||||
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 @@
|
|||
from unittest import TestCase
|
||||
import lib.money
|
||||
import logging
|
||||
from unittest import TestCase
|
||||
|
||||
import lib.money
|
||||
|
||||
|
||||
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…
Reference in New Issue