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