From afc9ad884a244ce218833b4fc96949c51184782d Mon Sep 17 00:00:00 2001 From: MuslemRahimi Date: Thu, 10 Apr 2025 21:01:19 +0200 Subject: [PATCH] add discord bot --- app/cron_discord.py | 119 ---------------------------------- app/cron_discord_bot.py | 137 ++++++++++++++++++++++++++++++++++++++++ app/primary_cron_job.py | 6 ++ 3 files changed, 143 insertions(+), 119 deletions(-) delete mode 100644 app/cron_discord.py create mode 100644 app/cron_discord_bot.py diff --git a/app/cron_discord.py b/app/cron_discord.py deleted file mode 100644 index 3c355ef..0000000 --- a/app/cron_discord.py +++ /dev/null @@ -1,119 +0,0 @@ -import discord -import re -import sqlite3 -from contextlib import contextmanager - -STOCK_DB = 'stocks' -ETF_DB = 'etf' -CRYPTO_DB = 'crypto' - - -@contextmanager -def db_connection(db_name): - conn = sqlite3.connect(f'{db_name}.db') - cursor = conn.cursor() - cursor.execute("PRAGMA journal_mode = wal") - try: - yield cursor - finally: - conn.commit() - cursor.close() - conn.close() - -#------Start Stocks DB------------# -with db_connection(STOCK_DB) as cursor: - cursor.execute("SELECT DISTINCT symbol FROM stocks") - symbols = [row[0] for row in cursor.fetchall()] - - cursor.execute("SELECT symbol FROM stocks") - raw_data = cursor.fetchall() - stock_list_data = [{ - 'symbol': row[0], - 'type': 'stocks', - } for row in raw_data] -#------End Stocks DB------------# - -#------Start ETF DB------------# -with db_connection(ETF_DB) as cursor: - cursor.execute("SELECT DISTINCT symbol FROM etfs") - etf_symbols = [row[0] for row in cursor.fetchall()] - - cursor.execute("SELECT symbol FROM etfs") - raw_data = cursor.fetchall() - etf_list_data = [{ - 'symbol': row[0], - 'type': 'etf', - } for row in raw_data] -#------End ETF DB------------# - -#------Start Crypto DB------------# -with db_connection(CRYPTO_DB) as cursor: - cursor.execute("SELECT DISTINCT symbol FROM cryptos") - crypto_symbols = [row[0] for row in cursor.fetchall()] - - cursor.execute("SELECT symbol FROM cryptos") - raw_data = cursor.fetchall() - crypto_list_data = [{ - 'symbol': row[0], - 'type': 'crypto', - } for row in raw_data] -#------End Crypto DB------------# - -#------Init Searchbar Data------------# -searchbar_data = stock_list_data + etf_list_data + crypto_list_data - -# Replace with your bot token -TOKEN = 'token' - -# Initialize the bot -intents = discord.Intents.default() -intents.message_content = True -client = discord.Client(intents=intents) - -# Pattern to match $stockname format (e.g., $AAPL, $TSLA) -ticker_pattern = re.compile(r"\$(\w+)") - - -@client.event -async def on_ready(): - print(f'Logged in as {client.user}!') - - -@client.event -async def on_message(message): - # Ignore messages from the bot itself - if message.author == client.user: - return - - # Copy of original message content to modify - modified_content = message.content - - # Find all tickers in the message - tickers = ticker_pattern.findall(message.content) - - # If no tickers found, exit - if not tickers: - return - - for ticker in tickers: - # Find the corresponding symbol in the searchbar_data - matched = next((item for item in searchbar_data if item['symbol'].upper() == ticker.upper()), None) - - if matched: - symbol_type = matched['type'] - # Construct the URL based on the symbol type (stock, etf, crypto) - if symbol_type == 'stocks': - stock_url = f"https://stocknear.com/stocks/{ticker.upper()}" - elif symbol_type == 'etf': - stock_url = f"https://stocknear.com/etf/{ticker.upper()}" - elif symbol_type == 'crypto': - stock_url = f"https://stocknear.com/crypto/{ticker.upper()}" - - # Replace the ticker in the content with a markdown hyperlink - modified_content = modified_content.replace(f"${ticker}", f"[${ticker}]({stock_url})") - - # Edit the original message with the modified content - await message.edit(content=modified_content) - -# Run the bot -client.run(TOKEN) diff --git a/app/cron_discord_bot.py b/app/cron_discord_bot.py new file mode 100644 index 0000000..fa48833 --- /dev/null +++ b/app/cron_discord_bot.py @@ -0,0 +1,137 @@ +import requests +import time +import orjson +from datetime import datetime, timedelta, timezone +import os +from dotenv import load_dotenv + + +load_dotenv() +today = datetime.utcnow().date() +now = datetime.now(timezone.utc) +N_minutes_ago = now - timedelta(minutes=30) + +WEBHOOK_URL = os.getenv("DISCORD_DARK_POOL_WEBHOOK") + +def save_json(data): + directory = "json/discord" + try: + os.makedirs(directory, exist_ok=True) + with open(directory+"/dark_pool.json", 'wb') as file: + file.write(orjson.dumps(data)) + except Exception as e: + print(f"An error occurred while saving data: {e}") + + +def format_number(num, decimal=False): + """Abbreviate large numbers with B/M suffix and format appropriately""" + # Handle scientific notation formats like 5E6 + if isinstance(num, str) and ('e' in num.lower() or 'E' in num.lower()): + try: + num = float(num) + except ValueError: + return num # Return as is if conversion fails + + # Convert strings to numbers if needed + if isinstance(num, str): + try: + num = float(num) + if num.is_integer(): + num = int(num) + except ValueError: + return num # Return as is if conversion fails + + # Format based on size + if num >= 1_000_000_000: # Billions + formatted = num / 1_000_000_000 + # Only show decimal places if needed + return f"{formatted:.2f}B".rstrip('0').rstrip('.') + 'B' + elif num >= 1_000_000: # Millions + formatted = num / 1_000_000 + # Only show decimal places if needed + return f"{formatted:.2f}".rstrip('0').rstrip('.') + 'M' + elif decimal and isinstance(num, float) and not num.is_integer(): + return f"{num:,.2f}" + else: + return f"{num:,.0f}" # Format smaller numbers with commas + +def dark_pool_flow(): + + try: + with open(f"json/discord/dark_pool.json", "r") as file: + seen_list = orjson.loads(file.read()) + seen_list = [item for item in seen_list if datetime.fromisoformat(item['date']).date() == today] + except: + seen_list = [] + + with open(f"json/dark-pool/feed/data.json", "r") as file: + res_list = orjson.loads(file.read()) + res_list = [item for item in res_list if float(item['premium']) >= 30E6 and datetime.fromisoformat(item['date']).date() == today] + + + if res_list: + filtered_recent = [ + item for item in res_list + if (dt := datetime.fromisoformat(item['date'])) >= N_minutes_ago + ] + # If there are any recent orders, find the one with the highest premium + if filtered_recent: + best_order = max(filtered_recent, key=lambda x: x['premium']) + result = {k: best_order[k] for k in ['date', 'trackingID', 'price', 'size', 'premium', 'ticker']} + else: + result = None # Or handle however you like (e.g., empty dict) + + + if seen_list: + seen_ids = {item['trackingID'] for item in seen_list} + else: + seen_ids = {} + + if result != None and result['trackingID'] not in seen_ids: + symbol = result['ticker'] + quantity = format_number(result['size']) + price = result['price'] + amount = format_number(result['premium']) + + + message_timestamp = int((datetime.now() - timedelta(minutes=0)).timestamp()) + + embed = { + "color": 0xC475FD, # Green color from original embed + "thumbnail": {"url": "https://stocknear.com/pwa-64x64.png"}, + "title": "Dark Pool Order", + "fields": [ + {"name": "Symbol", "value": symbol, "inline": True}, + {"name": "", "value": "", "inline": True}, + {"name": "Quantity", "value": str(quantity), "inline": True}, + {"name": "Price", "value": str(price), "inline": True}, + {"name": "", "value": "\u200B \u200B", "inline": True}, + {"name": "Amount", "value": "$"+amount, "inline": True}, + {"name": "", "value": "", "inline": False}, + {"name": f"Data by Stocknear - - Delayed by 15 min.", "value": "", "inline": False} + ], + "footer": {"text": ""} + } + + payload = { + "content": "", + "embeds": [embed] + } + + response = requests.post(WEBHOOK_URL, json=payload) + + if response.status_code in (200, 204): + seen_list.append({'date': result['date'], 'trackingID': result['trackingID']}) + with open("json/discord/dark_pool.json","wb") as file: + file.write(orjson.dumps(seen_list)) + print("Embed sent successfully!") + + else: + print(f"Failed to send embed. Status code: {response.status_code}") + print("Response content:", response.text) + + else: + print("Dark pool already sent!") + +if __name__ == "__main__": + dark_pool_flow() \ No newline at end of file diff --git a/app/primary_cron_job.py b/app/primary_cron_job.py index 6ea4c81..c82b38b 100755 --- a/app/primary_cron_job.py +++ b/app/primary_cron_job.py @@ -288,6 +288,11 @@ def run_list(): run_command(["python3", "cron_list.py"]) run_command(["python3", "cron_heatmap.py"]) +def run_discord_bot(): + week = datetime.today().weekday() + if week <= 4: + run_command(["python3", "cron_discord_bot.py"]) + def run_financial_statements(): week = datetime.today().weekday() @@ -422,6 +427,7 @@ schedule.every(5).minutes.do(run_threaded, run_push_notifications).tag('push_not schedule.every(5).minutes.do(run_threaded, run_market_flow).tag('market_flow_job') schedule.every(5).minutes.do(run_threaded, run_list).tag('stock_list_job') +schedule.every(5).minutes.do(run_threaded, run_discord_bot).tag('discord_bot')