Remove calls to Revolut

This commit is contained in:
Mark Veidemanis 2022-03-04 22:15:53 +00:00
parent b53fbfc905
commit 2266300064
Signed by: m
GPG Key ID: 5ACFCEED46C0904F
4 changed files with 33 additions and 342 deletions

View File

@ -12,7 +12,6 @@ from signal import signal, SIGINT
# Project imports
from settings import settings
import util
from revolut import Revolut
from agora import Agora
from transactions import Transactions
from irc import bot
@ -99,7 +98,6 @@ if __name__ == "__main__":
"irc": bot(),
"agora": Agora(),
"markets": Markets(),
"revolut": Revolut(),
"nordigen": Nordigen(),
"truelayer": TrueLayer(),
"fidor": Fidor(),
@ -110,21 +108,6 @@ if __name__ == "__main__":
# Merge all classes into each other
util.xmerge_attrs(init_map)
# Setup the authcode -> refresh token and refresh_token -> auth_token stuff
# util.setup_call_loops(
# token_setting=settings.Revolut.SetupToken,
# function_init=init_map["revolut"].setup_auth,
# function_continuous=init_map["revolut"].get_new_token,
# delay=int(settings.Revolut.RefreshSec),
# function_post_start=init_map["revolut"].setup_webhook,
# )
# util.setup_call_loops(
# token_setting=settings.TrueLayer.SetupToken,
# function_init=init_map["truelayer"].setup_auth,
# function_continuous=init_map["truelayer"].get_new_token,
# delay=int(settings.TrueLayer.RefreshSec),
# )
# Set up the loops to put data in ES
init_map["tx"].setup_loops()

View File

@ -12,7 +12,7 @@ class IRCCommands(object):
helptext = "Get all open trades."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
"""
Get details of open trades and post on IRC.
"""
@ -32,7 +32,7 @@ class IRCCommands(object):
helptext = "Create an ad. Usage: create <XMR/BTC> <country> <currency> [<provider>]"
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
"""
Post an ad on AgoraDesk with the given country and currency code.
"""
@ -64,7 +64,7 @@ class IRCCommands(object):
helptext = "Get messages. Usage: messages [<reference>]"
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
"""
Get all messages for all open trades or a given trade.
"""
@ -98,7 +98,7 @@ class IRCCommands(object):
helptext = "Distribute all our chosen currency and country ad pairs. Usage: dist [<XMR/BTC>]"
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
# Distribute out our ad to all countries in the config
if length == 2:
asset = spl[1]
@ -123,7 +123,7 @@ class IRCCommands(object):
helptext = "Update all ads with details."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
for x in agora.redist_countries():
if x[0]["success"]:
msg(f"{x[0]['response']['data']['message']}: {x[1]}")
@ -136,51 +136,17 @@ class IRCCommands(object):
helptext = "Remove all duplicate adverts."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
rtrn = agora.strip_duplicate_ads()
msg(dumps(rtrn))
class accounts(object):
name = "accounts"
authed = True
helptext = "Get all account information from Revolut."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
accounts = revolut.accounts()
accounts_posted = 0
if accounts is None:
msg("Error getting accounts.")
for account in accounts:
if account["balance"] > 0:
if "name" in account:
name = account["name"]
else:
name = "not_set"
msg(f"{name} {account['currency']}: {account['balance']}")
accounts_posted += 1
if accounts_posted == 0:
msg("No accounts with balances.")
class balance(object):
name = "balance"
authed = True
helptext = "Get total account balance from Revolut in USD."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
total_usd = revolut.get_total_usd()
if total_usd is False:
msg("Error getting total balance.")
msg(f"Total: {round(total_usd, 2)}USD")
class total(object):
name = "total"
authed = True
helptext = "Get total account balance from Revolut and Agora."
helptext = "Get total account balance from Sinks and Agora."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
totals_all = tx.get_total()
totals = totals_all[0]
wallets = totals_all[1]
@ -202,7 +168,7 @@ class IRCCommands(object):
helptext = "Summon all operators."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
notify.sendmsg("You have been summoned!")
class message(object):
@ -211,7 +177,7 @@ class IRCCommands(object):
helptext = "Send a message on a trade. Usage: msg <reference> <message...>"
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
if length > 2:
full_msg = " ".join(spl[2:])
reference = tx.ref_to_tx(spl[1])
@ -227,7 +193,7 @@ class IRCCommands(object):
helptext = "List all references"
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
msg(f"References: {', '.join(tx.get_refs())}")
class ref(object):
@ -236,7 +202,7 @@ class IRCCommands(object):
helptext = "Get more information about a reference. Usage: ref <reference>"
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
if length == 2:
ref_data = tx.get_ref(spl[1])
if not ref_data:
@ -250,7 +216,7 @@ class IRCCommands(object):
helptext = "Delete a reference. Usage: del <reference>"
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
if length == 2:
ref_data = tx.get_ref(spl[1])
if not ref_data:
@ -265,7 +231,7 @@ class IRCCommands(object):
helptext = "Release funds for a trade. Usage: release <reference>"
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
if length == 2:
tx = tx.ref_to_tx(spl[1])
if not tx:
@ -282,7 +248,7 @@ class IRCCommands(object):
helptext = "Delete all our adverts."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
rtrn = agora.nuke_ads()
msg(dumps(rtrn))
@ -292,7 +258,7 @@ class IRCCommands(object):
helptext = "Get Agora wallet balances."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
rtrn_xmr = agora.agora.wallet_balance_xmr()
if not rtrn_xmr["success"]:
msg("Error getting XMR wallet details.")
@ -312,7 +278,7 @@ class IRCCommands(object):
helptext = "View public adverts. Usage: pubads <XMR/BTC> <currency> [<provider,...>]"
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
if length == 3:
asset = spl[1]
if asset not in loads(settings.Agora.AssetList):
@ -345,7 +311,7 @@ class IRCCommands(object):
helptext = "Cheat the markets by manipulating our prices to exploit people. Usage: cheat [<XMR/BTC>]"
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
if length == 1:
agora.run_cheat_in_thread()
msg("Running cheat in thread.")
@ -363,7 +329,7 @@ class IRCCommands(object):
helptext = "Run the next currency for cheat."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
if length == 1:
asset = agora.run_cheat_in_thread()
msg(f"Running next asset for cheat in thread: {asset}")
@ -374,7 +340,7 @@ class IRCCommands(object):
helptext = "Get all our ad regions"
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
ads = agora.enum_ads()
for ad in ads:
msg(f"({ad[0]}) {ad[1]} {ad[2]} {ad[3]} {ad[4]}")
@ -385,7 +351,7 @@ class IRCCommands(object):
helptext = "Get current XMR price."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
xmr_prices = agora.cg.get_price(ids="monero", vs_currencies=["sek", "usd", "gbp"])
price_sek = xmr_prices["monero"]["sek"]
price_usd = xmr_prices["monero"]["usd"]
@ -398,7 +364,7 @@ class IRCCommands(object):
helptext = "Get current BTC price."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
xmr_prices = agora.cg.get_price(ids="bitcoin", vs_currencies=["sek", "usd", "gbp"])
price_sek = xmr_prices["bitcoin"]["sek"]
price_usd = xmr_prices["bitcoin"]["usd"]
@ -411,28 +377,16 @@ class IRCCommands(object):
helptext = "Take profit."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
agora.withdraw_funds()
class shuffle(object):
name = "shuffle"
authed = True
helptext = "Convert all currencies in Revolut to supplied one. Usage: shuffle <currency>"
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
if length == 2:
currency = spl[1]
rtrn = revolut.shuffle(currency)
msg(dumps(rtrn))
class remaining(object):
name = "r"
authed = True
helptext = "Show how much is left before we are able to withdraw funds."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
remaining = tx.get_remaining()
msg(f"Remaining: {remaining}USD")
@ -442,7 +396,7 @@ class IRCCommands(object):
helptext = "Show how much is left before we are able to withdraw funds (including open trades)."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
remaining = tx.get_total_remaining()
msg(f"Total remaining: {remaining}USD")
@ -452,7 +406,7 @@ class IRCCommands(object):
helptext = "Get total value of all open trades in USD."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
total = tx.get_open_trades_usd()
msg(f"Total trades: {total}USD")
@ -462,7 +416,7 @@ class IRCCommands(object):
helptext = "Get total value of everything, including open trades."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
total = tx.get_total_with_trades()
msg(f"${total}")
@ -472,7 +426,7 @@ class IRCCommands(object):
helptext = "Get total profit."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
total = tx.money.get_profit()
msg(f"Profit: {total}USD")
@ -482,7 +436,7 @@ class IRCCommands(object):
helptext = "Get total profit with open trades."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
total = tx.money.get_profit(True)
msg(f"Profit: {total}USD")
@ -492,6 +446,6 @@ class IRCCommands(object):
helptext = "Generate a TrueLayer signin URL."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
def run(cmd, spl, length, authed, msg, agora, tx, notify):
auth_url = agora.truelayer.create_auth_url()
msg(f"Auth URL: {auth_url}")

View File

@ -88,7 +88,7 @@ class IRCBot(irc.IRCClient):
# Check if the command required authentication
if obj.authed:
if host in self.admins:
obj.run(cmd, spl, length, authed, msgl, self.agora, self.revolut, self.tx, self.notify)
obj.run(cmd, spl, length, authed, msgl, self.agora, self.tx, self.notify)
else:
# Handle authentication here instead of in the command module for security
self.msg(channel, "Access denied.")
@ -192,7 +192,8 @@ class IRCBotFactory(protocol.ClientFactory):
prcol = IRCBot(self.log)
self.client = prcol
setattr(self.client, "agora", self.agora)
setattr(self.client, "revolut", self.revolut)
setattr(self.client, "truelayer", self.truelayer)
setattr(self.client, "nordigen", self.nordigen)
setattr(self.client, "tx", self.tx)
setattr(self.client, "notify", self.notify)
return prcol

View File

@ -1,247 +0,0 @@
# Twisted/Klein imports
from twisted.logger import Logger
# Other library imports
from json import dumps
from simplejson.errors import JSONDecodeError
import requests
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
import jwt
from random import choices
from string import ascii_uppercase
# Project imports
from settings import settings
class Revolut(object):
"""
Class to handle Revolut API calls.
"""
def __init__(self):
"""
Initialise the Revolut object.
Set the logger and token.
"""
self.log = Logger("revolut")
self.token = None
def setup_auth(self):
"""
Function to create a new Java Web Token and use it to get a refresh/access token.
"""
self.create_new_jwt()
self.get_access_token()
def create_new_jwt(self):
"""
Create a new Java Web Token.
"""
payload = {
"iss": settings.Revolut.Domain,
"sub": settings.Revolut.ClientID,
"aud": "https://revolut.com",
"exp": int(settings.Revolut.Expiry),
}
with open(settings.Revolut.PrivateKey, "rb") as f:
pem_bytes = f.read()
# payload = {jwt_header, jwt_body}
private_key = serialization.load_pem_private_key(pem_bytes, password=None, backend=default_backend())
encoded = jwt.encode(payload, private_key, algorithm="RS256")
settings.Revolut.JWT = encoded
settings.write()
def get_access_token(self):
"""
Get an access token with our JWT.
:return: True or False
:rtype: bool
"""
headers = {"Content-Type": "application/x-www-form-urlencoded"}
data = {
"grant_type": "authorization_code",
"code": settings.Revolut.AuthCode,
"client_id": settings.Revolut.ClientID,
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": settings.Revolut.JWT,
}
r = requests.post(f"{settings.Revolut.Base}/auth/token", data=data, headers=headers)
try:
parsed = r.json()
except JSONDecodeError:
self.log.error("Error parsing access token response: {content}", content=r.content)
return False
if r.status_code == 200:
try:
settings.Revolut.RefreshToken = parsed["refresh_token"]
settings.Revolut.SetupToken = "0"
settings.write()
self.log.info("Refreshed refresh token - Revolut")
self.token = parsed["access_token"]
self.log.info("Refreshed access token - Revolut")
except KeyError:
self.log.error(f"Token authorization didn't contain refresh or access token: {parsed}", parsed=parsed)
return False
else:
self.log.error(f"Cannot refresh token: {parsed}", parsed=parsed)
return False
def get_new_token(self, fail=False):
"""
Get a new access token with the refresh token.
:param fail: whether to exit() if this fails
:type fail: bool
:return: True or False
:rtype: bool
"""
headers = {"Content-Type": "application/x-www-form-urlencoded"}
data = {
"grant_type": "refresh_token",
"refresh_token": settings.Revolut.RefreshToken,
"client_id": settings.Revolut.ClientID,
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": settings.Revolut.JWT,
}
r = requests.post(f"{settings.Revolut.Base}/auth/token", data=data, headers=headers)
try:
parsed = r.json()
except JSONDecodeError:
if fail:
exit()
return False
if r.status_code == 200:
if "access_token" in parsed.keys():
self.token = parsed["access_token"]
self.log.info("Refreshed access token - Revolut")
return True
else:
self.log.error(f"Token refresh didn't contain access token: {parsed}", parsed=parsed)
if fail:
exit()
return False
else:
self.log.error(f"Cannot refresh token: {parsed}", parsed=parsed)
if fail:
exit()
return False
def setup_webhook(self):
"""
Check the webhook we have set up in Revolut.
Set up configured webhook if not already set up.
:return: True or False
:rtype: bool
"""
webhook = self.get_webhook()
if "url" in webhook.keys():
if webhook["url"] == settings.Revolut.WebhookURL:
self.log.info("Webhook exists - skipping setup: {url}", url=webhook["url"])
return True # Webhook already exists
self.log.info("Setting up webhook: {url}", url=settings.Revolut.WebhookURL)
headers = {"Authorization": f"Bearer {self.token}"}
data = {"url": settings.Revolut.WebhookURL}
r = requests.post(f"{settings.Revolut.Base}/webhook", data=dumps(data), headers=headers)
if r.status_code == 204:
self.log.info("Set up webhook: {url}", url=settings.Revolut.WebhookURL)
return True
else:
parsed = r.json()
self.log.info("Failed setting up webhook: {parsed}", parsed=parsed)
return False
def get_webhook(self):
"""
Get the webhook address active in Revolut.
:return: dict of hook with key url or False
:rtype: dict or bool
"""
headers = {"Authorization": f"Bearer {self.token}"}
r = requests.get(f"{settings.Revolut.Base}/webhook", headers=headers)
if r.status_code == 200:
parsed = r.json()
return parsed
elif r.status_code == 404:
return {}
else:
self.log.error("Cannot get webhook: {content}", r.content)
return False
def accounts(self):
"""
Get the details and balances of all Revolut accounts.
:return: account details
:rtype: dict
"""
headers = {"Authorization": f"Bearer {self.token}"}
r = requests.get(f"{settings.Revolut.Base}/accounts", headers=headers)
if r.status_code == 200:
return r.json()
else:
self.log.error("Error getting accounts: {content}", content=r.content)
return False
def get_total_usd(self):
rates = self.money.get_rates_all()
accounts = self.accounts()
if not accounts:
return False
total_usd = 0
for account in accounts:
if account["currency"] == "USD":
total_usd += account["balance"]
else:
total_usd += account["balance"] / rates[account["currency"]]
return total_usd
def convert(self, from_account_id, from_currency, to_account_id, to_currency, sell_amount):
"""
Convert currency.
:param sell_currency: currency to sell
:param buy_currency: currency to buy
:param sell_amount: amount of currency to sell
"""
reference = "".join(choices(ascii_uppercase, k=5))
headers = {"Authorization": f"Bearer {self.token}"}
data = {
"from": {
"account_id": from_account_id,
"currency": from_currency,
"amount": sell_amount,
},
"to": {
"account_id": to_account_id,
"currency": to_currency,
},
"request_id": reference,
}
r = requests.post(f"{settings.Revolut.Base}/exchange", headers=headers, data=dumps(data))
if r.status_code == 200:
return r.json()
else:
self.log.error("Error converting balance: {content}", content=r.content)
return False
def shuffle(self, currency):
"""
Exchange money in all accounts to the given currency.
:param currency: the currency to convert all our funds to
"""
accounts = self.accounts()
# Find given currency account
for account in accounts:
if account["currency"] == currency:
if account["state"] == "active" and account["public"] is True:
dest_account = account
# Remove this account
accounts.remove(dest_account)
break
for account in accounts:
if account["balance"] > 0:
self.convert(account["id"], account["currency"], dest_account["id"], dest_account["currency"], account["balance"])
return True