import aiosqlite import discord import logging import requests import random from datetime import datetime from discord.ext import commands, tasks import os 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) # Logging logger = logging.getLogger('discord') logger.setLevel(logging.DEBUG) handler = logging.FileHandler(filename='discord.log', encoding='utf-8', mode='w') handler.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s:%(name)s: %(message)s')) logger.addHandler(handler) # Database schema = [ '''CREATE TABLE IF NOT EXISTS collections ( id integer PRIMARY KEY, symbol text, image text, name text, description text, last_price integer )''', '''CREATE TABLE IF NOT EXISTS guilds ( id integer PRIMARY KEY, guild_id integer )''', '''CREATE TABLE IF NOT EXISTS lists ( collection_id integer, guild_id integer )''' ] async def create_db(): for query in schema: await db.execute (query) await db.commit() print('Database ready') @bot.event async def on_ready(): global db 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): await db.execute('INSERT INTO guilds (guild_id) VALUES (?)', (guild.id,)) await db.commit() print(f"Joined guild {guild.name}") @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): 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) embed.set_image(url=str(random.choice(NFT_LINKS))) 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: 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_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)