import discord import asyncio import requests import json import io import qrcode from discord import File, Embed from discord.ext import commands, tasks with open("config.json", "r") as f: config = json.load(f) TOKEN = config["discord_token"] GUILD_ID = int(config["guild_id"]) ROLE_ID = int(config["role_id"]) LNBITS_URL = config["lnbits_url"] LNBITS_API_KEY = config["lnbits_api_key"] PRICE = config["price"] CHECK_INTERVAL = config["check_interval"] MAX_CHECKS = config["max_checks"] intents = discord.Intents.default() intents.members = True intents.message_content = True bot = commands.Bot(command_prefix="!", intents=intents) pending_invoices = {} @bot.event async def on_ready(): """Called when the bot successfully logs in.""" print(f"✅ Logged in as {bot.user}") print("Bot is in the following guilds:") for g in bot.guilds: print(f" - {g.name} (ID: {g.id})") synced_cmds = await bot.tree.sync() print("✅ Synced global commands:", synced_cmds) print(f"🔄 Checking invoices every {CHECK_INTERVAL} seconds...") check_invoices.start() @bot.tree.command(name="support", description="Pay a Lightning invoice to get your role!") async def support_command(interaction: discord.Interaction): user_id = interaction.user.id invoice_data = { "out": False, "amount": PRICE, "memo": "Lightning Payment" } headers = { "X-Api-Key": LNBITS_API_KEY, "Content-Type": "application/json" } resp = requests.post(f"{LNBITS_URL}/api/v1/payments", json=invoice_data, headers=headers) if resp.status_code == 201: invoice_json = resp.json() payment_request = invoice_json["payment_request"] payment_hash = invoice_json["payment_hash"] pending_invoices[payment_hash] = (user_id, 0) qr_buffer = io.BytesIO() qrcode.make(payment_request).save(qr_buffer, format="PNG") qr_buffer.seek(0) qr_file = File(fp=qr_buffer, filename="invoice_qr.png") embed = Embed( title="Payment Invoice", description="Please pay the following Lightning invoice to complete your purchase." ) embed.add_field(name="Invoice", value=f"```{payment_request}```", inline=False) embed.add_field(name="Amount", value=f"{PRICE} sats", inline=True) embed.add_field(name="Requesting User", value=f"{interaction.user.display_name}", inline=True) embed.set_image(url="attachment://invoice_qr.png") await interaction.user.send( content=f"{interaction.user.display_name}, please pay **{PRICE} sats** using the Lightning Network.", embed=embed, file=qr_file ) await interaction.response.send_message("✅ I've sent you a payment invoice via DM!", ephemeral=True) else: await interaction.response.send_message("❌ Failed to generate invoice. Try again later.", ephemeral=True) print(f"❌ LNbits Error: {resp.text}") @tasks.loop(seconds=CHECK_INTERVAL) async def check_invoices(): if not pending_invoices: return guild = discord.utils.get(bot.guilds, id=GUILD_ID) if guild is None: print(f"⚠️ Bot not in server with ID {GUILD_ID}") return headers = { "X-Api-Key": LNBITS_API_KEY, "Content-Type": "application/json" } invoices_to_remove = [] for payment_hash, (user_id, attempts) in pending_invoices.items(): if attempts >= MAX_CHECKS: invoices_to_remove.append(payment_hash) continue status_resp = requests.get(f"{LNBITS_URL}/api/v1/payments/{payment_hash}", headers=headers) if status_resp.status_code == 200: payment_status = status_resp.json() if payment_status.get("paid", False): member = guild.get_member(user_id) if member: role = guild.get_role(ROLE_ID) if role: await member.add_roles(role) await member.send("✅ **Thank you! Your role has been assigned.**") print(f"🎉 Role assigned to {member.name}") invoices_to_remove.append(payment_hash) else: print(f"❌ Role ID {ROLE_ID} not found!") else: print(f"❌ Member {user_id} not found!") pending_invoices[payment_hash] = (user_id, attempts + 1) for done_hash in invoices_to_remove: del pending_invoices[done_hash] bot.run(TOKEN)