191 lines
9.2 KiB
Python
191 lines
9.2 KiB
Python
|
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')
|