From 81aa3a9fad73d63b71b1e671ced36c1b9ec68d9d Mon Sep 17 00:00:00 2001 From: Caleb Fultz Date: Thu, 10 Oct 2024 17:09:05 +0200 Subject: [PATCH] add bot.py --- bot.py | 191 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 bot.py diff --git a/bot.py b/bot.py new file mode 100644 index 0000000..b16a34d --- /dev/null +++ b/bot.py @@ -0,0 +1,191 @@ +import discord +from discord.ext import commands +import requests +import random +import re +from pyedhrec import EDHRec + +# Dictionary of keywords and their definitions +keywords = { + "ward": "Whenever this creature becomes the target of a spell or ability an opponent controls, counter that spell or ability unless its controller pays {COST}.", + "hexproof": "This permanent can't be the target of spells or abilities your opponents control.", + "flying": "This creature can't be blocked except by creatures with flying and/or reach.", + "first strike": "If this creature is blocking or being blocked by a creature without first strike or double strike, it deals combat damage first.", + "deathtouch": "Any amount of damage this deals to a creature is enough to destroy it.", + "trample": "This creature can deal excess combat damage to the player or planeswalker it's attacking.", + "haste": "This creature can attack and {T} as soon as it comes under your control.", + "vigilance": "Attacking doesn't cause this creature to tap.", + "reach": "This creature can block creatures with flying.", + "lifelink": "If this creature deals damage, you gain that much life.", + "menace": "This creature can't be blocked except by two or more creatures.", + "indestructible": "Damage and effects that say 'destroy' don't destroy this.", + "double strike": "This creature deals both first-strike and regular combat damage.", + "flash": "You may cast this spell any time you could cast an instant.", + "prowess": "Whenever you cast a noncreature spell, this creature gets +1/+1 until end of turn.", + "defender": "This creature can't attack.", + "landwalk": "This creature can't be blocked as long as defending player controls a [LAND TYPE] (e.g., Islandwalk).", + "scry": "Look at the top X cards of your library, then put any number of them on the bottom of your library and the rest on top in any order.", + "affinity": "This spell costs {1} less to cast for each [THING] you control (e.g., Affinity for artifacts).", + "convoke": "You may tap any number of creatures you control as you cast this spell. Each creature tapped this way pays for {1} or one mana of that creature's color.", + "equip": "({COST}: Attach to target creature you control. Equip only as a sorcery.)", + "flashback": "You may cast this card from your graveyard for its flashback cost. Then exile it.", + "morph": "(You may cast this card face down as a 2/2 creature for {3}. Turn it face up any time for its morph cost.)", + "regenerate": "({COST}: The next time this creature would be destroyed this turn, it isn't. Instead tap it, remove all damage from it, and remove it from combat.)", + # ... and keep adding more if you like! +} + +intents = discord.Intents.default() # Select the intents you need +intents.message_content = True # This is needed to read message content + +bot = commands.Bot(command_prefix='!', intents=intents) +edhrec = EDHRec() + +@bot.command(name='define') +async def define_keyword(ctx, keyword): + """Defines a Magic: The Gathering keyword ability.""" + keyword = keyword.lower() + if keyword in keywords: + definition = keywords[keyword] + await ctx.send(f"**{keyword.capitalize()}**: {definition}") + else: + await ctx.send(f"I don't know the keyword '{keyword}'.") + +@bot.command(name='search') +async def search_commander(ctx, *, commander_name): + """Searches for decks with the specified commander (using EDHRec API).""" + try: + # Get commander data from EDHRec + commander_data = edhrec.get_commander_data(commander_name) + + # Access the Moxfield URI + commander_moxfield_uri = commander_data['container']['json_dict']['card']['moxfield_uri'] + + # Check if 'similar' key exists before accessing it + if 'similar' in commander_data['container']['json_dict']: + similar_commanders = commander_data['container']['json_dict']['similar'] + if similar_commanders: + random_commander = random.choice(similar_commanders) + random_moxfield_uri = random_commander['moxfield_uri'] + else: + random_moxfield_uri = None + else: + random_moxfield_uri = None + + # Send the Moxfield URIs to the channel + await ctx.send(f"Moxfield decks for {commander_name}: {commander_moxfield_uri}") + if random_moxfield_uri: + await ctx.send(f"Random similar commander decks on Moxfield: {random_moxfield_uri}") + + except Exception as e: # Catch potential pyedhrec or other exceptions + await ctx.send(f"An error occurred while fetching data from EDHRec: {e}") + +@bot.command(name='rec') +async def get_recommendations(ctx, *, commander_name): + """Gets top card recommendations with high synergy for a commander from EDHRec.""" + try: + # Get commander data from EDHRec + commander_data = edhrec.get_commander_data(commander_name) + + recommendations = [] + for cardlist in commander_data['container']['json_dict']['cardlists']: + # Sort cards by synergy in descending order + sorted_cards = sorted(cardlist['cardviews'], key=lambda card: card['synergy'], reverse=True) + + count = 0 + for card in sorted_cards: + if card['synergy'] >= 0.65 and count < 5: + recommendations.append(card['name']) + count += 1 + + if recommendations: + await ctx.send(f"Top recommendations with high synergy for {commander_name} on EDHRec:\n- " + "\n- ".join(recommendations)) + else: + await ctx.send(f"No recommendations with high synergy found for {commander_name} on EDHRec.") + + except Exception as e: + await ctx.send(f"An error occurred while fetching recommendations from EDHRec: {e}") + +@bot.command(name='combos') +async def get_combos(ctx, *, commander_name): + """Gets a random combo for a commander from EDHRec.""" + try: + # Get combos data from EDHRec + combos_data = edhrec.get_card_combos(commander_name) + + # Extract the combo list and total count + combo_list = combos_data['container']['json_dict']['cardlists'] + total_combos = len(combo_list) + + # Select 1 random combo + if combo_list: + random_combo = random.choice(combo_list) + combo_name = random_combo['header'] + combo_link = "https://edhrec.com" + random_combo['href'] # Construct full combo link + + # Construct and send the response message (corrected line) + response_message = ( + f"**{combos_data['header']} ({total_combos} total)**\n" + f"Random combo: {combo_name}\n{combo_link}\n" + f"See all combos for {commander_name} on EDHRec: https://edhrec.com{list(combos_data['container']['breadcrumb'][1].keys())[0]}" # Convert to list + ) + await ctx.send(response_message) + + except requests.exceptions.HTTPError as e: + if e.response.status_code == 404: + # Suggest checking the spelling if 404 Not Found + await ctx.send(f"Could not find combos for '{commander_name}'. Please double-check the spelling.") + else: + # Generic error message for other HTTP errors + await ctx.send(f"An error occurred while fetching combos from EDHRec: {e}") + + except Exception as e: + await ctx.send(f"An error occurred while fetching combos from EDHRec: {e}") + + +@bot.command(name='details') +async def get_card_details(ctx, *, card_name): + """Gets card details for a card from EDHRec.""" + try: + details = edhrec.get_card_details(card_name) + card_link = edhrec.get_card_link(card_name) + + await ctx.send(f"Card details for {card_name} on EDHRec: {card_link}") + await ctx.send(f"```\n{details['oracle_text']}\n```") # Using code block for formatting + + except Exception as e: + await ctx.send(f"An error occurred while fetching card details from EDHRec: {e}") + +@bot.command(name='rules') +async def get_card_rulings(ctx, *, card_name): + """Fetches rulings for a specific Magic: The Gathering card from Scryfall.""" + try: + # Use Scryfall API to search for the card and get rulings URI + response = requests.get(f"https://api.scryfall.com/cards/named?fuzzy={card_name}") + response.raise_for_status() + card_data = response.json() + rulings_uri = card_data['rulings_uri'] + + # Fetch rulings from the rulings URI + response = requests.get(rulings_uri) + response.raise_for_status() + rulings_data = response.json() + + # Extract published date and comment for each ruling + rulings_info = [] + for ruling in rulings_data['data']: + published_at = ruling['published_at'] + comment = ruling['comment'] + rulings_info.append(f"**{published_at}**: {comment}") + + # Send the rulings information to the channel + if rulings_info: + await ctx.send(f"Rulings for {card_data['name']}:\n" + "\n".join(rulings_info)) + else: + await ctx.send(f"No rulings found for {card_data['name']}.") + + except requests.exceptions.RequestException as e: + # If card not found or API error, send error message + await ctx.send(f"Could not find card '{card_name}' or an error occurred.") + +# Replace 'YOUR_BOT_TOKEN' with your actual bot token +bot.run('YOUR_BOT_TOKEN') \ No newline at end of file