From a3d25b22dee5ffdfa8decad74a6ddd3dab54d988 Mon Sep 17 00:00:00 2001 From: Rushil Umaretiya Date: Sun, 20 Feb 2022 12:02:21 -0500 Subject: [PATCH] fix: convert to lf --- .env.sample | 4 +- .gitignore | 304 ++++++++++++++++---------------- LICENSE | 42 ++--- bot.py | 486 ++++++++++++++++++++++++++-------------------------- 4 files changed, 418 insertions(+), 418 deletions(-) diff --git a/.env.sample b/.env.sample index 8fbf061..aa1ed4c 100644 --- a/.env.sample +++ b/.env.sample @@ -1,3 +1,3 @@ -DISCORD_TOKEN='' -THRESHOLD=0.1 +DISCORD_TOKEN='' +THRESHOLD=0.1 MENTION_ROLE=alert \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4f04c81..c4b46a6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,153 +1,153 @@ -# disc bot -.env -discord.log - -#led / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class -secrets.py -static_files -.idea/ -.vscode/ -/static - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# bruh windows -*:Zone.Identifier - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -#VSCODE +# disc bot +.env +discord.log + +#led / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class +secrets.py +static_files +.idea/ +.vscode/ +/static + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# bruh windows +*:Zone.Identifier + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +#VSCODE .vscode/* \ No newline at end of file diff --git a/LICENSE b/LICENSE index 6576078..2671579 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2022 Rushil Umaretiya - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +MIT License + +Copyright (c) 2022 Rushil Umaretiya + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/bot.py b/bot.py index 1dbbd7c..9a2a58b 100644 --- a/bot.py +++ b/bot.py @@ -1,243 +1,243 @@ -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) - +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) +