From 0e2874a82d64f0c6d3973a78e096995382bbe8a2 Mon Sep 17 00:00:00 2001
From: Rushil Umaretiya <rushilwiz@gmail.com>
Date: Sat, 19 Feb 2022 18:17:39 -0500
Subject: [PATCH] feat: finished bot

---
 .env.sample |   4 +-
 bot.py      | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 172 insertions(+), 7 deletions(-)

diff --git a/.env.sample b/.env.sample
index 4697ead..8fbf061 100644
--- a/.env.sample
+++ b/.env.sample
@@ -1 +1,3 @@
-DISCORD_TOKEN=''
\ No newline at end of file
+DISCORD_TOKEN=''
+THRESHOLD=0.1
+MENTION_ROLE=alert
\ No newline at end of file
diff --git a/bot.py b/bot.py
index 88c79f2..8f6f8ff 100644
--- a/bot.py
+++ b/bot.py
@@ -2,8 +2,10 @@ import aiosqlite
 import discord
 import logging
 import requests
+import random
+from datetime import datetime
 
-from discord.ext import commands
+from discord.ext import commands, tasks
 
 import os
 from dotenv import load_dotenv
@@ -11,8 +13,27 @@ from dotenv import load_dotenv
 load_dotenv()
 
 TOKEN = os.getenv('DISCORD_TOKEN')
+MENTION_ROLE = str(os.getenv('MENTION_ROLE'))
+THRESHOLD = float(os.getenv('THRESHOLD'))
 API_ENDPOINT = 'http://api-mainnet.magiceden.dev/v2/'
 
+NFT_LINKS = [
+    "https://monkelabs.nyc3.digitaloceanspaces.com/public/assets/LabMonke_Brown.gif",
+    "https://monkelabs.nyc3.digitaloceanspaces.com/public/assets/LabMonke_Orangutan.gif",
+    "https://monkelabs.nyc3.digitaloceanspaces.com/public/assets/LabMonke_Green.gif",
+    "https://monkelabs.nyc3.digitaloceanspaces.com/public/assets/LabMonke_Blue.gif",
+    "https://monkelabs.nyc3.digitaloceanspaces.com/public/assets/LabMonke_Gorilla.gif"
+    "https://monkelabs.nyc3.digitaloceanspaces.com/public/assets/LabMonke_Arctic.gif",
+    "https://smr-volume-1.s3.amazonaws.com/assets/4713.png",
+    "https://smr-volume-1.s3.amazonaws.com/assets/1739.png",
+    "https://smr-volume-1.s3.amazonaws.com/assets/5275.png",
+    "https://smr-volume-1.s3.amazonaws.com/assets/5796.png",
+    "https://www.arweave.net/shJMjP9y9mwFwmaTet0Jcok-dG1TEr8t7E362zeS6uM?ext=png"
+    "https://arweave.net/teqvJFiNG-_i7OHDwfeb5ISsGzN_SkU3jNQ7uzelJLo?ext=png",
+    "https://arweave.net/giVsTWye2PILj1wZe3uP3VNRwvVbbLz-9-FJ94ToNv0?ext=png",
+    "https://www.arweave.net/GciyPdeivlARIjnpQtFnFMVIVZTVUGEFt28ui-Yem9k?ext=png"
+]
+
 # Bot
 intents = discord.Intents.all()
 bot = commands.Bot(command_prefix='$', intents=intents)
@@ -31,6 +52,7 @@ schema = [
     symbol text,
     image text,
     name text,
+    description text,
     last_price integer
 )''',
 
@@ -57,6 +79,10 @@ async def on_ready():
     db = await aiosqlite.connect('db.sqlite3')
     await create_db()
     print('We have logged in as {0.user}'.format(bot))
+    update_prices.start()
+    
+    await bot.change_presence(activity=discord.Game(name="The NFT Market"))
+    
 
 @bot.event
 async def on_guild_join(guild):
@@ -64,17 +90,154 @@ async def on_guild_join(guild):
     await db.commit()
     print(f"Joined guild {guild.name}")
 
-@bot.command()
+@bot.event
+async def on_guild_remove(guild):
+    await db.execute('DELETE FROM guilds (guild_id) VALUES (?)', (guild.id,))
+    await db.execute('DELETE FROM lists (guild_id) VALUES (?)', (guild.id,))
+    await db.commit()
+    print(f"Left guild {guild.name}")
+
+@bot.command(help='Lists all of your collections')
 async def list(ctx):
-    list_string = 'Here is a list of all your tracked collections:\n'
+    async with await db.execute("SELECT id from guilds WHERE guild_id=69 LIMIT 1") as cursor:
+        print(cursor.rowcount)
+
+    embed = discord.Embed(title='Here is a list of all your tracked collections', color=0x6106D0)
 
     async with await db.execute("SELECT id from guilds WHERE guild_id=? LIMIT 1", (ctx.guild.id,)) as cursor:
         async for row in cursor:
             guild_id = row[0]
 
+    collections = []
+
     async with await db.execute("SELECT collection_id from lists WHERE guild_id=?", (guild_id,)) as cursor:
         async for collection in cursor:
-            list_string += f'{collection.name}\n'
-    # await ctx.send(list_string)
+            collections.append(collection[0])
+    print(collections)
+
+    for collection_id in collections:
+        async with await db.execute("SELECT symbol,name,last_price from collections WHERE id=? LIMIT 1", (collection_id,)) as cursor:
+            async for collection in cursor:
+                embed.add_field(name=collection[1], value=f'`{collection[0]}` - {format_price(collection[2])} ◎', inline=False)
+    
+
+    await ctx.send(embed=embed)
+
+@bot.command(brief='Add a collection to your list', help='Add a collection to your list, pass a symbol: magiceden.io/marketplace/SYMBOL', usage='SYMBOL')
+async def add(ctx, symbol):
+
+    data = requests.get(API_ENDPOINT + 'collections/' + symbol)
+    if data.status_code != 200:
+        await ctx.send(f'Could not find collection with symbol `{symbol}`')
+    else:
+        price_data = requests.get(API_ENDPOINT + 'collections/' + symbol + '/stats').json()
+        price = int(str(price_data['floorPrice'])[:-7])
+        
+        made = False
+        async with await db.execute("SELECT id from collections WHERE symbol=?", (symbol,)) as cursor:
+            async for row in cursor:
+                made = True
+                
+        if not made:
+            await db.execute('INSERT INTO collections (symbol,image,name,last_price) VALUES (?,?,?,?)', (symbol,data.json()['image'],data.json()['name'],price))
+            await db.commit()
+
+        async with await db.execute("SELECT id FROM collections WHERE symbol=? LIMIT 1", (symbol,)) as cursor:
+            async for row in cursor:
+                collection_id = row[0]
+            
+        async with await db.execute("SELECT id from guilds WHERE guild_id=? LIMIT 1", (ctx.guild.id,)) as cursor:
+            async for row in cursor:
+                guild_id = row[0]
+
+        await db.execute('REPLACE INTO lists (guild_id, collection_id) VALUES (?,?)', (guild_id, collection_id))
+        await db.commit()
+
+        await ctx.send(f'Added `{symbol}`({format_price(price)} ◎) to your list')
+
+@bot.command(brief='Remove a collection from your list', help='Remove a collection from your list, pass a symbol: magiceden.io/marketplace/SYMBOL', usage='SYMBOL')
+async def remove(ctx, symbol):
+    collection = None
+
+    async with await db.execute("SELECT id from collections WHERE symbol=?", (symbol,)) as cursor:
+        async for row in cursor:
+            collection = row[0]
+    
+    if collection is None:
+        await ctx.send(f'Could not find collection with symbol `{symbol}`')
+    else:
+        await db.execute('DELETE FROM lists WHERE collection_id=?', (collection,))
+        await db.commit()
+
+        await ctx.send(f'Removed `{symbol}` from your list')
+
+@tasks.loop(seconds=10, minutes=0, hours=0, count=None)
+async def update_prices():
+    collections = []
+    async with await db.execute("SELECT id,symbol,last_price,name from collections") as cursor:
+        async for row in cursor:
+            collections.append(row)
+
+    prices = []
+    changes = []
+
+    for collection in collections:
+        data = requests.get(API_ENDPOINT + 'collections/' + collection[1] + '/stats').json()
+        price = int(str(data['floorPrice'])[:-7])
+        prices.append((collection, price))
+        if float(abs(price - collection[2])) > THRESHOLD*100:
+            changes.append((collection, price))
+
+    if len(changes) > 0:
+        print("We have a change!")
+        embed = discord.Embed(title='Price changes', color=0x6106D0)
+
+        embed.set_image(url=str(random.choice(NFT_LINKS)))
+        embed.set_footer(text=f'Sent at {datetime.now().strftime("%d/%m/%Y %H:%M:%S")}', icon_url="https://imagedelivery.net/E-VnZk4fwouzlzwX_qz4fg/532afb9b-8805-424d-8f85-da5c3e0f8600/public")
+
+
+        g_ids = []
+
+        for change in changes:
+            if change[1] - change[0][2] > 0:
+                embed.add_field(name=f'{change[0][3]} (`{change[0][1]}`)', value=f'{format_price(change[0][2])} ◎ :arrow_right: {format_price(change[1])} ◎\n:chart_with_upwards_trend:Change: {format_price(change[1] - change[0][2])}', inline=False)
+            else:
+                embed.add_field(name=f'{change[0][3]} (`{change[0][1]}`)', value=f'{format_price(change[0][2])} ◎ :arrow_right: {format_price(change[1])} ◎\n:chart_with_downwards_trend:Change: {format_price(change[1] - change[0][2])}', inline=False)
+            collection_id = change[0][0]
+            async with await db.execute("SELECT guild_id from lists WHERE collection_id=?", (collection_id,)) as cursor:
+                async for row in cursor:
+                    g_ids.append(row[0])
+        
+        g_ids = set(g_ids)
+        guilds = []
+        for g_id in g_ids:
+            async with await db.execute("SELECT guild_id from guilds WHERE id=?", (g_id,)) as cursor:
+                async for row in cursor:
+                    guilds.append(row[0])
+
+        print("Sending to:", guilds)
+
+        for g_id in guilds:
+            guild = bot.get_guild(g_id)
+            channel_id = None
+            for c in guild.channels:
+                if c.name == 'fp-changes':
+                    channel_id = c.id
+            channel = bot.get_channel(channel_id)
+            if channel_id is not None:
+                await channel.send(embed=embed)
+                mention_role = discord.utils.get(guild.roles, name=MENTION_ROLE)
+                if mention_role is not None:
+                    await channel.send(f'{mention_role.mention} `({", ".join([changes[i][0][1] for i in range(len(changes))])})`')
+
+
+    for price in prices:
+        await db.execute('UPDATE collections SET last_price=? WHERE id=?', (price[1], price[0][0]))
+    await db.commit()
+
+def format_price(price):
+    if price == None: return 'NULL'
+    return f'{price/100}'
+
+bot.run(TOKEN)
 
-bot.run(TOKEN)
\ No newline at end of file