Add the notify module

This commit is contained in:
Mark Veidemanis 2022-01-27 19:12:15 +00:00
parent b91431b3ba
commit 365fc71f64
Signed by: m
GPG Key ID: 5ACFCEED46C0904F
6 changed files with 134 additions and 34 deletions

View File

@ -65,6 +65,9 @@ class Agora(object):
def set_tx(self, tx):
self.tx = tx
def set_notify(self, notify):
self.notify = notify
def setup_loop(self):
"""
Set up the LoopingCall to get all active trades and messages.
@ -470,12 +473,13 @@ class Agora(object):
# Don't waste API rate limits on setting the same margin as before
if new_margin != our_margin:
# rtrn = self.agora.ad_equation(ad_id, new_formula)
to_update.append([ad_id, new_formula, asset, False])
to_update.append([ad_id, new_formula, asset, currency, False])
# if not rtrn["success"]:
# self.log.error("Error updating ad {ad_id}: {response}", ad_id=ad_id, response=rtrn["response"])
self.log.info("Rate for {currency}: {margin}", currency=currency, margin=new_margin)
else:
self.log.info("Not changed rate for {currency}, keeping old margin of {margin}", currency=currency, margin=our_margin)
print("TO UPDATE", to_update)
self.slow_ad_update(to_update)
def slow_ad_update(self, ads):
@ -487,15 +491,18 @@ class Agora(object):
iterations = 0
throttled = 0
assets = set()
while not all([x[2] for x in ads]) or iterations == 1000:
currencies = set()
while not all([x[4] for x in ads]) or iterations == 1000:
for ad_index in range(len(ads)):
ad_id, new_formula, asset, actioned = ads[ad_index]
ad_id, new_formula, asset, currency, actioned = ads[ad_index]
print("SLOW ITER", ad_id, new_formula, asset, currency, actioned)
assets.add(asset)
currencies.add(currency)
self.log.error("ASSET {a}", a=asset)
if not actioned:
rtrn = self.agora.ad_equation(ad_id, new_formula)
if rtrn["success"]:
ads[ad_index][2] = True
ads[ad_index][4] = True
throttled = 0
self.log.info("Successfully updated ad: {id}", id=ad_id)
continue
@ -518,8 +525,13 @@ class Agora(object):
self.log.info("Slow ad update finished, no ads to update")
self.irc.sendmsg("Slow ad update finished, no ads to update")
else:
self.log.info("Slow ad update completed with {iterations} iterations: [{assets}]", iterations=iterations, assets=assets)
self.irc.sendmsg(f"Slow ad update completed with {iterations} iterations: [{assets}]")
self.log.info(
"Slow ad update completed with {iterations} iterations: [{assets}] | [{currencies}]",
iterations=iterations,
assets=", ".join(assets),
currencies=", ".join(currencies),
)
self.irc.sendmsg(f"Slow ad update completed with {iterations} iterations: [{', '.join(assets)}] | [{', '.join(currencies)}]")
def autoprice(self, ads, currency):
"""
@ -858,3 +870,11 @@ class Agora(object):
rtrn2 = self.agora.wallet_send_xmr(**send_cast)
self.irc.sendmsg(f"Withdrawal: {rtrn1['success']} | {rtrn2['success']}")
self.notify.notify_withdrawal(profit_usd / 2)
def to_usd(self, amount, currency):
if currency == "USD":
return float(amount)
else:
rates = self.get_rates_all()
return float(amount) / rates[currency]

View File

@ -15,6 +15,7 @@ from revolut import Revolut
from agora import Agora
from transactions import Transactions
from irc import bot
from notify import Notify
def convert(data):
@ -54,10 +55,20 @@ class WebApp(object):
if __name__ == "__main__":
# Define Notify
notify = Notify()
# Define IRC and Agora
irc = bot()
agora = Agora()
# Pass Notify to IRC and Agora
irc.set_notify(notify)
agora.set_notify(notify)
# Pass Agora to Notify
notify.set_agora(agora)
# Pass IRC to Agora and Agora to IRC
# This is to prevent recursive dependencies
agora.set_irc(irc)
@ -73,6 +84,9 @@ if __name__ == "__main__":
# Define Transactions
tx = Transactions()
# Pass Notify to Transactions
tx.set_notify(notify)
# Pass Agora and IRC to Transactions and Transactions to IRC
tx.set_agora(agora)
tx.set_irc(irc)

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):
def run(cmd, spl, length, authed, msg, agora, revolut, 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>"
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx):
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
"""
Post an ad on AgoraDesk with the given country and currency code.
"""
@ -52,7 +52,7 @@ class IRCCommands(object):
helptext = "Get messages. Usage: messages [<reference>]"
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx):
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
"""
Get all messages for all open trades or a given trade.
"""
@ -86,7 +86,7 @@ class IRCCommands(object):
helptext = "Distribute all our chosen currency and country ad pairs."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx):
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
# Distribute out our ad to all countries in the config
for x in agora.dist_countries():
if x["success"]:
@ -100,7 +100,7 @@ class IRCCommands(object):
# helptext = "Use a bruteforce algorithm to create all possible currency and country pairs."
#
# @staticmethod
# def run(cmd, spl, length, authed, msg, agora, revolut, tx):
# def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
# for x in agora.dist_bruteforce():
# if x["success"]:
# msg(f"{x['response']['data']['message']}: {x['response']['data']['ad_id']}")
@ -113,7 +113,7 @@ class IRCCommands(object):
# helptext = "Resume a run of brute by getting all our adverts then filling the blanks."
#
# @staticmethod
# def run(cmd, spl, length, authed, msg, agora, revolut, tx):
# def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
# for x in agora.bruteforce_fill_blanks():
# if x["success"]:
# msg(f"{x['response']['data']['message']}: {x['response']['data']['ad_id']}")
@ -126,7 +126,7 @@ class IRCCommands(object):
helptext = "Remove all duplicate adverts."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx):
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
rtrn = agora.strip_duplicate_ads()
msg(dumps(rtrn))
@ -136,7 +136,7 @@ class IRCCommands(object):
helptext = "Find a transaction. Usage: find <currency> <amount>"
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx):
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
"""
Find a transaction received by Revolut with the given reference and amount.
"""
@ -158,7 +158,7 @@ class IRCCommands(object):
helptext = "Get all account information from Revolut."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx):
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
accounts = revolut.accounts()
accounts_posted = 0
if accounts is None:
@ -180,7 +180,7 @@ class IRCCommands(object):
helptext = "Get total account balance from Revolut in USD."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx):
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.")
@ -192,7 +192,7 @@ class IRCCommands(object):
helptext = "Get total account balance from Revolut and Agora."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx):
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
totals_all = tx.get_total()
totals = totals_all[0]
wallets = totals_all[1]
@ -208,13 +208,22 @@ class IRCCommands(object):
def run(cmd, spl, length, authed, msg):
msg("Pong!")
class summon(object):
name = "summon"
authed = True
helptext = "Summon all operators."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
notify.sendmsg("You have been summoned!")
class release_url(object):
name = "release_url"
authed = True
helptext = "Get release URL for all open trades."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx):
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
trades = agora.dashboard_release_urls()
if not trades:
msg("No trades.")
@ -227,7 +236,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):
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
if length > 2:
full_msg = " ".join(spl[2:])
reference = tx.ref_to_tx(spl[1])
@ -243,7 +252,7 @@ class IRCCommands(object):
helptext = "List all references"
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx):
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
msg(f"References: {', '.join(tx.get_refs())}")
class ref(object):
@ -252,7 +261,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):
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
if length == 2:
ref_data = tx.get_ref(spl[1])
if not ref_data:
@ -266,7 +275,7 @@ class IRCCommands(object):
helptext = "Delete a reference. Usage: del <reference>"
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx):
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
if length == 2:
ref_data = tx.get_ref(spl[1])
if not ref_data:
@ -281,7 +290,7 @@ class IRCCommands(object):
helptext = "Release funds for a trade. Usage: release <reference>"
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx):
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
if length == 2:
tx = tx.ref_to_tx(spl[1])
if not tx:
@ -298,7 +307,7 @@ class IRCCommands(object):
helptext = "Delete all our adverts."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx):
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
rtrn = agora.nuke_ads()
msg(dumps(rtrn))
@ -308,7 +317,7 @@ class IRCCommands(object):
helptext = "Get Agora wallet balances."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx):
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
rtrn_xmr = agora.agora.wallet_balance_xmr()
if not rtrn_xmr["success"]:
msg("Error getting XMR wallet details.")
@ -328,7 +337,7 @@ class IRCCommands(object):
helptext = "View public adverts. Usage: pubads <XMR/BTC> <currency>"
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx):
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
if length == 3:
asset = spl[1]
if asset not in loads(settings.Agora.AssetList):
@ -345,7 +354,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):
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
if length == 1:
agora._update_prices()
msg("Running cheat in thread.")
@ -369,7 +378,7 @@ class IRCCommands(object):
helptext = "Run the next currency for cheat."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx):
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
if length == 1:
asset = agora._update_prices(None, None)
msg(f"Running next asset for cheat in thread: {asset}")
@ -380,7 +389,7 @@ class IRCCommands(object):
helptext = "Get all our ad regions"
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx):
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
ads = agora.enum_ads()
for ad in ads:
msg(f"({ad[0]}) {ad[1]} {ad[2]} {ad[3]}")
@ -391,7 +400,7 @@ class IRCCommands(object):
helptext = "Get current XMR price."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx):
def run(cmd, spl, length, authed, msg, agora, revolut, 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"]
@ -404,7 +413,7 @@ class IRCCommands(object):
helptext = "Get current BTC price."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx):
def run(cmd, spl, length, authed, msg, agora, revolut, 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"]
@ -417,7 +426,7 @@ class IRCCommands(object):
helptext = "Take profit."
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx):
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
agora.withdraw_funds()
class shuffle(object):
@ -426,7 +435,7 @@ class IRCCommands(object):
helptext = "Convert all currencies in Revolut to supplied one. Usage: shuffle <currency>"
@staticmethod
def run(cmd, spl, length, authed, msg, agora, revolut, tx):
def run(cmd, spl, length, authed, msg, agora, revolut, tx, notify):
if length == 2:
currency = spl[1]
rtrn = revolut.shuffle(currency)

View File

@ -47,6 +47,9 @@ class IRCBot(irc.IRCClient):
def set_tx(self, tx):
self.tx = tx
def set_notify(self, notify):
self.notify = notify
def parse(self, user, host, channel, msg):
"""
Simple handler for IRC commands.
@ -97,7 +100,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)
obj.run(cmd, spl, length, authed, msgl, self.agora, self.revolut, self.tx, self.notify)
else:
# Handle authentication here instead of in the command module for security
self.msg(channel, "Access denied.")
@ -189,6 +192,9 @@ class IRCBotFactory(protocol.ClientFactory):
def set_tx(self, tx):
self.tx = tx
def set_notify(self, notify):
self.notify = notify
def sendmsg(self, msg):
"""
Passthrough function to send a message to the channel.
@ -210,6 +216,7 @@ class IRCBotFactory(protocol.ClientFactory):
self.client.set_agora(self.agora)
self.client.set_revolut(self.revolut)
self.client.set_tx(self.tx)
self.client.set_notify(self.notify)
return prcol
def clientConnectionLost(self, connector, reason):

45
handler/notify.py Normal file
View File

@ -0,0 +1,45 @@
# Twisted/Klein imports
from twisted.logger import Logger
# Other library imports
import requests
# Project imports
from settings import settings
class Notify(object):
"""
Class to handle more robust notifications.
"""
def __init__(self):
self.log = Logger("notify")
def set_agora(self, agora):
self.agora = agora
def sendmsg(self, msg, title=None, priority=None, tags=None):
headers = {"Title": "Bot"}
if title:
headers["Title"] = title
if priority:
headers["Priority"] = priority
if tags:
headers["Tags"] = tags
requests.post(
f"{settings.Notify.Host}/{settings.Notify.Topic}",
data=msg,
headers=headers,
)
def notify_new_trade(self, amount, currency):
amount_usd = self.agora.to_usd(amount, currency)
self.sendmsg(f"Total: {amount_usd}", title="New trade", tags="trades")
def notify_complete_trade(self, amount, currency):
amount_usd = self.agora.to_usd(amount, currency)
self.sendmsg(f"Total: {amount_usd}", title="Trade complete", tags="trades,profit")
def notify_withdrawal(self, amount_usd):
self.sendmsg(f"Total: {amount_usd}", title="Withdrawal", tags="profit")

View File

@ -33,6 +33,9 @@ class Transactions(object):
def set_revolut(self, revolut):
self.revolut = revolut
def set_notify(self, notify):
self.notify = notify
def transaction(self, data):
"""
Store details of transaction and post notifications to IRC.
@ -232,6 +235,7 @@ class Transactions(object):
r.hmset(f"tx.{txid}", to_store)
self.release_funds(stored_trade["id"], stored_trade["reference"])
self.notify.notify_complete_trade(amount, currency)
def release_funds(self, trade_id, reference):
self.log.info("All checks passed, releasing funds for {trade_id} {reference}", trade_id=trade_id, reference=reference)
@ -266,6 +270,7 @@ class Transactions(object):
self.log.info("Storing trade information: {info}", info=str(to_store))
r.hmset(f"trade.{reference}", to_store)
self.irc.sendmsg(f"Generated reference for {trade_id}: {reference}")
self.notify.notify_new_trade(amount, currency)
if settings.Agora.Send == "1":
self.agora.agora.contact_message_post(trade_id, f"Hi! When sending the payment please use reference code: {reference}")
if existing_ref: