LTC Payment Discord Bot

This Python bot manages Litecoin (LTC) payments (with blockchain verification) for a Discord server, featuring SQLite database integration for user balances and transactions, key generation for Product 1 and Product 2 Products, and Product 1/Product 2 loader building capabilities with custom build commands.

Setup Tutorial

Follow these steps to set up your LTC Payment Discord Bot:

  1. Create a Discord Bot:
  2. Invite the Bot:
    • In OAuth2 > URL Generator, select "bot" scope and appropriate permissions.
    • Use the generated URL to invite the bot to your server.
  3. Set Up Environment:
    • Install Python 3.8+.
    • Install dependencies:
      pip install discord.py sqlite3 requests aiohttp
    • Create bot.db (auto-initialized by bot).
  4. Configure Bot:
    • Update constants (YOUR_GUILD_ID, WHITELISTED_ADMIN, TOKEN, LTC_ADDRESS).
    • Set paths for Product 2.exe and build files.
  5. Run the Bot:
    • Save as bot.py and run:
      python bot.py

Notes:

  • Requires Visual Studio for $build command.
  • Ensure bot has proper permissions.
  • Test with $help_bot.

1. Imports and Configuration

Modules and initial bot configuration with redacted sensitive data.


import discord
from discord.ext import commands
import sqlite3
import time
import random
import requests
import json
import asyncio
import os
import logging
from datetime import datetime, timedelta
import aiohttp
import urllib.parse

# Bot Configuration
YOUR_GUILD_ID = [redacted]
WHITELISTED_ADMIN = [redacted]
TOKEN = "[redacted]"
LTC_ADDRESS = '[redacted]'
Product 2_PATH = r"C:\\Users\\Administrator\\Desktop\\Product 2.exe"
pending_deposits = {}
VALID_TXID_LENGTH = 64

# API Endpoints
COINBASE_API_URL = '[redacted]'
BLOCKCYPHER_API_URL = '[redacted]'

# Discord Bot Setup
intents = discord.Intents.default()
intents.members = True
intents.message_content = True
client = commands.Bot(command_prefix="$", intents=intents)
rgb_color = (0, 0, 0)
hex_color = f'#{rgb_color[0]:02x}{rgb_color[1]:02x}{rgb_color[2]:02x}'

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('bot.log'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)
            

2. Database and Utility Functions

SQLite database setup and helper functions.


def is_reseller(member):
    return discord.utils.get(member.roles, name="reseller") is not None

def init_db():
    with sqlite3.connect('bot.db') as conn:
        c = conn.cursor()
        c.execute('''
        CREATE TABLE IF NOT EXISTS users (
            user_id TEXT PRIMARY KEY,
            balance REAL DEFAULT 0,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )
        ''')
        c.execute('''
        CREATE TABLE IF NOT EXISTS transactions (
            txid TEXT PRIMARY KEY,
            user_id TEXT,
            amount_ltc REAL,
            amount_usd REAL,
            status TEXT,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            FOREIGN KEY (user_id) REFERENCES users (user_id)
        )
        ''')
        conn.commit()

def get_user_balance(user_id):
    with sqlite3.connect('bot.db') as conn:
        c = conn.cursor()
        c.execute('SELECT balance FROM users WHERE user_id = ?', (str(user_id),))
        result = c.fetchone()
        return result[0] if result else 0.0

def update_balance(user_id, amount):
    try:
        with sqlite3.connect('bot.db') as conn:
            c = conn.cursor()
            c.execute('SELECT balance FROM users WHERE user_id = ?', (str(user_id),))
            result = c.fetchone()
            if result is not None:
                current_balance = result[0]
                new_balance = current_balance + amount
                c.execute('UPDATE users SET balance = ? WHERE user_id = ?', 
                         (new_balance, str(user_id)))
            else:
                c.execute('INSERT INTO users (user_id, balance) VALUES (?, ?)', 
                         (str(user_id), amount))
            conn.commit()
            logger.info(f"Updated balance for user {user_id}: {amount:+.2f}")
            return True
    except Exception as e:
        logger.error(f"Error updating balance for user {user_id}: {e}")
        return False

def add_transaction(txid, user_id, amount_ltc, amount_usd, status):
    with sqlite3.connect('bot.db') as conn:
        c = conn.cursor()
        c.execute('''
        INSERT INTO transactions (txid, user_id, amount_ltc, amount_usd, status)
        VALUES (?, ?, ?, ?, ?)
        ''', (txid, str(user_id), amount_ltc, amount_usd, status))
        conn.commit()

def is_transaction_used(txid):
    with sqlite3.connect('bot.db') as conn:
        c = conn.cursor()
        c.execute('SELECT txid FROM transactions WHERE txid = ?', (txid,))
        return c.fetchone() is not None

async def generate_qr_code(address, amount=None):
    qr_content = f"litecoin:{address}"
    if amount:
        qr_content += f"?amount={amount}"
    encoded_content = urllib.parse.quote(qr_content)
    return f"[redacted]?size=200x200&data={encoded_content}"

async def get_ltc_price():
    try:
        async with aiohttp.ClientSession() as session:
            async with session.get('[redacted]') as response:
                if response.status == 200:
                    data = await response.json()
                    return float(data['data']['rates']['USD'])
                else:
                    logger.error(f"Failed to get LTC price. Status: {response.status}")
                    return None
    except Exception as e:
        logger.error(f"Error getting LTC price: {e}")
        return None

async def add_member_roles(guild_id, member_id):
    try:
        guild = client.get_guild(guild_id)
        if guild:
            member = await guild.fetch_member(member_id)
            if member:
                roles_to_add = ["pro","pro v2", "customer"]
                for role_name in roles_to_add:
                    role = discord.utils.get(guild.roles, name=role_name)
                    if role and role not in member.roles:
                        await member.add_roles(role)
                        logger.info(f"Added {role_name} role to {member.name}")
    except Exception as e:
        logger.error(f"Error adding roles: {e}")

def format_ltc(amount):
    """Format LTC amount to max 8 decimal places without trailing zeros"""
    return f"{amount:.8f}".rstrip('0').rstrip('.')
            

3. Bot Events and Payment Verification

Core event handlers and LTC payment verification logic.


@client.event
async def on_ready():
    init_db()
    logger.info(f"Bot is ready as {client.user.name}")
    client.loop.create_task(cleanup_old_transactions())
    client.loop.create_task(cleanup_pending_deposits())
    await client.change_presence(
        status=discord.Status.online,
        activity=discord.Game(name="$help_bot")
    )

@client.command(name='checkbalance')
async def check_balance_admin(ctx, user_id: str):
    if ctx.author.id != WHITELISTED_ADMIN:
        return
    try:
        with sqlite3.connect('bot.db') as conn:
            c = conn.cursor()
            c.execute('''
            SELECT balance, created_at 
            FROM users 
            WHERE user_id = ?
            ''', (user_id,))
            result = c.fetchone()
            if result:
                balance, created_at = result
                embed = discord.Embed(
                    title="Balance Check",
                    description=f"User ID: {user_id}\nBalance: ${balance:.2f}\nAccount Created: {created_at}",
                    color=int(hex_color[1:], 16)
                )
                await ctx.reply(embed=embed)
            else:
                await ctx.reply("User not found in database.")
    except Exception as e:
        logger.error(f"Error checking balance: {e}")
        await ctx.reply("Error checking balance.")

async def verify_ltc_payment(txid, user_id):
    logger.info(f"Starting verification for TXID {txid}")
    if len(txid) != VALID_TXID_LENGTH:
        logger.warning(f"Invalid TXID length: {len(txid)}")
        return False, 0, 0
    if is_transaction_used(txid):
        logger.warning(f"Transaction {txid} already used")
        return False, 0, 0
    for attempt in range(10):
        try:
            async with aiohttp.ClientSession() as session:
                logger.info(f"Fetching transaction data from BlockCypher for {txid} (Attempt {attempt + 1}/10)")
                async with session.get('[redacted]' + txid) as response:
                    logger.info(f"BlockCypher response status: {response.status}")
                    if response.status == 200:
                        data = await response.json()
                        logger.info(f"Transaction data received: {data}")
                        if data.get('confirmations', 0) >= 1:
                            received_amount = sum(
                                output['value'] for output in data['outputs']
                                if output.get('addresses', [None])[0] == '[redacted]'
                            )
                            logger.info(f"Received amount in satoshis: {received_amount}")
                            received_ltc = received_amount / 1e8
                            received_ltc_formatted = format_ltc(received_ltc)
                            logger.info(f"Converted amount: {received_ltc_formatted} LTC")
                            ltc_price = await get_ltc_price()
                            if ltc_price is None:
                                logger.error("Failed to get LTC price")
                                if attempt == 9:
                                    return False, 0, 0
                                continue
                            logger.info(f"Current LTC price: ${ltc_price}")
                            usd_value = round(received_ltc * ltc_price, 2)
                            logger.info(f"USD value: ${usd_value}")
                            try:
                                guild = client.get_guild(YOUR_GUILD_ID)
                                if guild:
                                    member = await guild.fetch_member(int(user_id))
                                    if member:
                                        is_user_reseller = is_reseller(member)
                                        min_deposit = 100 if is_user_reseller else 0
                                        if is_user_reseller and usd_value < min_deposit:
                                            logger.warning(f"Reseller deposit below minimum: ${usd_value:.2f}")
                                            return False, 0, 0
                            except Exception as e:
                                logger.warning(f"Could not verify member status: {e}")
                            add_transaction(txid, user_id, float(received_ltc_formatted), usd_value, 'completed')
                            logger.info(f"Transaction recorded successfully")
                            return True, float(received_ltc_formatted), usd_value
                        logger.info(f"Transaction {txid} not confirmed yet (Attempt {attempt + 1}/10)")
                        if attempt < 9:
                            await asyncio.sleep(60)
                            continue
                        return False, 0, 0
                    elif response.status == 404:
                        logger.info(f"Transaction not found yet (Attempt {attempt + 1}/10)")
                        if attempt < 9:
                            await asyncio.sleep(60)
                            continue
                        return False, 0, 0
                    else:
                        logger.error(f"Failed to verify transaction. Status: {response.status}")
                        if attempt < 9:
                            await asyncio.sleep(60)
                            continue
                        return False, 0, 0
        except Exception as e:
            logger.error(f"Error verifying transaction: {e}")
            if attempt < 9:
                await asyncio.sleep(60)
                continue
            return False, 0, 0

async def is_reseller_safe(user_id):
    try:
        guild = client.get_guild(YOUR_GUILD_ID)
        if guild:
            member = await guild.fetch_member(int(user_id))
            if member:
                return discord.utils.get(member.roles, name="reseller") is not None
    except Exception as e:
        logger.warning(f"Could not verify reseller status: {e}")
    return False

async def generate_qr_code_with_amount(address, ltc_price, is_reseller=False):
    if is_reseller:
        ltc_amount = 100 / ltc_price
        qr_content = f"litecoin:{address}?amount={format_ltc(ltc_amount)}"
    else:
        qr_content = f"litecoin:{address}"
    encoded_content = urllib.parse.quote(qr_content)
    return f"[redacted]?size=200x200&data={encoded_content}"
            

4. Deposit and Message Handling

Deposit command and message event handler for payment processing.


@client.command(name='deposit')
@commands.guild_only()
async def deposit(ctx):
    is_user_reseller = await is_reseller_safe(ctx.author.id)
    ltc_price = await get_ltc_price()
    if ltc_price is None:
        await ctx.author.send("Error getting LTC price. Please try again later.")
        return
    min_deposit = 100 if is_user_reseller else 0
    if is_user_reseller:
        await ctx.reply(f"As a reseller, minimum deposit is ${min_deposit:.2f}. Check your DMs for deposit information!")
    else:
        await ctx.reply("Check your DMs for deposit information!")
    qr_code_url = await generate_qr_code_with_amount('[redacted]', ltc_price, is_user_reseller)
    description = "Send any amount of LTC to top up your balance.\n"
    if is_user_reseller:
        min_ltc = min_deposit / ltc_price
        description = f"Minimum deposit: ${min_deposit:.2f} (approximately {format_ltc(min_ltc)} LTC)\n"
    description += "Send your transaction ID (txid) in this DM after making the payment"
    embed = discord.Embed(
        title="LTC Deposit Information",
        description=description,
        color=int(hex_color[1:], 16)
    )
    embed.add_field(name="LTC Address", value=f"`[redacted]`")
    if is_user_reseller:
        min_ltc = min_deposit / ltc_price
        embed.add_field(name="Recommended Amount", value=f"`{format_ltc(min_ltc)} LTC`", inline=False)
    embed.set_thumbnail(url="[redacted]")
    embed.set_image(url=qr_code_url)
    await ctx.author.send(embed=embed)
    pending_deposits[ctx.author.id] = True

@client.event
async def on_message(message):
    if message.author == client.user:
        return
    if isinstance(message.channel, discord.DMChannel):
        user_id = message.author.id
        if user_id in pending_deposits:
            if message.content.startswith('0x') or len(message.content) == 64:
                txid = message.content.strip()
                status_message = await message.channel.send("Starting transaction verification... This will take up to 10 minutes.")
                try:
                    success, ltc_amount, usd_value = await verify_ltc_payment(txid, user_id)
                    is_user_reseller = await is_reseller_safe(user_id)
                    min_deposit = 100 if is_user_reseller else 0
                    if success:
                        update_balance(user_id, usd_value)
                        current_balance = get_user_balance(user_id)
                        embed = discord.Embed(
                            title="Deposit Successful",
                            description=f"Successfully deposited {format_ltc(ltc_amount)} LTC (${usd_value:.2f})\n"
                                       f"New balance: ${current_balance:.2f}",
                            color=int(hex_color[1:], 16)
                        )
                        embed.set_thumbnail(url="[redacted]")
                        await message.channel.send(embed=embed)
                        try:
                            await add_member_roles(YOUR_GUILD_ID, user_id)
                        except Exception as e:
                            logger.warning(f"Could not add roles to user {user_id}: {e}")
                    else:
                        if is_user_reseller and usd_value < min_deposit:
                            await message.channel.send(f"Deposit failed: Amount ${usd_value:.2f} is below the reseller minimum of ${min_deposit:.2f}")
                        else:
                            await message.channel.send("Payment verification failed. Please ensure your transaction is confirmed and try again with $deposit")
                except Exception as e:
                    logger.error(f"Error processing payment: {e}")
                    await message.channel.send("An error occurred while processing your payment. Please try again or contact support.")
                del pending_deposits[user_id]
            else:
                await message.channel.send("Invalid transaction ID format. Please send a valid transaction ID or use $deposit to start over.")
    await client.process_commands(message)
            

5. Transaction and Balance Commands

Commands for viewing transactions and checking balances.


@client.command(name='transactions')
async def view_transactions(ctx, user_id: str):
    if ctx.author.id != WHITELISTED_ADMIN:
        return
    with sqlite3.connect('bot.db') as conn:
        c = conn.cursor()
        c.execute('''
        SELECT created_at, amount_ltc, amount_usd, txid, status
        FROM transactions
        WHERE user_id = ?
        ORDER BY created_at DESC
        LIMIT 10
        ''', (user_id,))
        transactions = c.fetchall()
    if not transactions:
        await ctx.reply("No transactions found for this user.")
        return
    embed = discord.Embed(
        title=f"Transaction History for {user_id}",
        color=int(hex_color[1:], 16)
    )
    for tx in transactions:
        created_at, amount_ltc, amount_usd, txid, status = tx
        embed.add_field(
            name=f"Transaction {created_at}",
            value=f"LTC: {format_ltc(amount_ltc)}\nUSD: ${amount_usd:.2f}\nStatus: {status}\nTXID: `{txid}`",
            inline=False
        )
    embed.set_thumbnail(url="[redacted]")
    await ctx.reply(embed=embed)

async def cleanup_pending_deposits():
    while True:
        try:
            current_time = datetime.utcnow()
            for user_id in list(pending_deposits.keys()):
                del pending_deposits[user_id]
        except Exception as e:
            logger.error(f"Error cleaning up pending deposits: {e}")
        await asyncio.sleep(3600)

@client.command(name='balance')
async def check_balance(ctx):
    balance = get_user_balance(ctx.author.id)
    embed = discord.Embed(
        title="Balance",
        description=f"Your current balance: ${balance:.2f}",
        color=int(hex_color[1:], 16)
    )
    embed.set_thumbnail(url="[redacted]")
    await ctx.reply(embed=embed)
            

6. Key Generation Commands

Commands for generating Product 1 and Product 2 keys.


@client.command(name='help_bot')
async def help(ctx):
    embed = discord.Embed(
        title="Help Menu",
        description="Available commands:",
        color=int(hex_color[1:], 16)
    )
    embed.add_field(name="$deposit", value="Get LTC deposit information", inline=False)
    embed.add_field(name="$balance", value="Check your current balance", inline=False)
    embed.add_field(name="$generate_key_month ", value="Generate a month key for Product 1 ($50)", inline=False)
    embed.add_field(name="$generate_key_week", value="Generate a week key for Product 1 ($25)", inline=False)
    embed.add_field(name="$generate_key_Product 2_month ", value="Generate a month key for Product 2 ($30)", inline=False)
    embed.add_field(name="$generate_key_Product 2_week ", value="Generate a week key for Product 2 ($15)", inline=False)
    embed.add_field(name="$generate_key_Product 2_day ", value="Generate a month key for Product 2 ($5)", inline=False)
    embed.add_field(name="$build", value="Build personal Product 1 loader", inline=False)
    embed.add_field(name="?build", value="Build personal Product 2 loader", inline=False)
    embed.add_field(name="$prices", value="View Product prices", inline=False)
    embed.set_thumbnail(url="[redacted]")
    await ctx.reply(embed=embed)

@client.command(name='generate_key_month')
@commands.guild_only()
async def generate_key_month(ctx, amount: int = 1):
    is_user_reseller = is_reseller(ctx.author)
    price = 25.0*amount if is_user_reseller else 50.0*amount
    balance = get_user_balance(ctx.author.id)
    if balance < price:
        await ctx.reply(f"Insufficient balance. You need ${price:.2f}, but have ${balance:.2f}")
        return
    api_url = "[redacted]"
    try:
        keys = []
        response = requests.get(api_url)
        if response.status_code == 200:
            for _ in range(amount):
                keys.append(response.text.strip())
        else:
            await ctx.reply("Error generating keys. Please try again later.")
            return
        update_balance(ctx.author.id, -price)
        embed = discord.Embed(
            title="Keys Generated",
            description=f"Successfully generated {amount} monthly {'keys' if amount > 1 else 'key'}.\nCheck your DMs for the {'keys' if amount > 1 else 'key'}.",
            color=int(hex_color[1:], 16)
        )
        embed.set_thumbnail(url="[redacted]")
        await ctx.reply(embed=embed)
        keys_message = "\n".join([f"Monthly key {i+1}: `{key}`" for i, key in enumerate(keys)])
        await ctx.author.send(f"{keys_message}\nNew balance: ${get_user_balance(ctx.author.id):.2f}")
    except Exception as e:
        logger.error(f"Error generating key: {e}")
        await ctx.reply("Error generating keys. Please try again later.")

@client.command(name='generate_key_week')
@commands.guild_only()
async def generate_key_week(ctx):
    is_user_reseller = is_reseller(ctx.author)
    price = 25.0 if is_user_reseller else 25.0
    num_keys = 2 if is_user_reseller else 1
    balance = get_user_balance(ctx.author.id)
    if balance < price:
        await ctx.reply(f"Insufficient balance. You need ${price:.2f}, but have ${balance:.2f}")
        return
    api_url = "[redacted]"
    try:
        keys = []
        for _ in range(num_keys):
            response = requests.get(api_url)
            if response.status_code == 200:
                keys.append(response.text.strip())
            else:
                await ctx.reply("Error generating keys. Please try again later.")
                return
        update_balance(ctx.author.id, -price)
        embed = discord.Embed(
            title="Keys Generated",
            description=f"Successfully generated {'2 weekly keys' if num_keys == 2 else 'weekly key'}.\nCheck your DMs for the {'keys' if num_keys == 2 else 'key'}.",
            color=int(hex_color[1:], 16)
        )
        embed.set_thumbnail(url="[redacted]")
        await ctx.reply(embed=embed)
        keys_message = "\n".join([f"Weekly key {i+1}: `{key}`" for i, key in enumerate(keys)])
        await ctx.author.send(f"{keys_message}\nNew balance: ${get_user_balance(ctx.author.id):.2f}")
    except Exception as e:
        logger.error(f"Error generating key: {e}")
        await ctx.reply("Error generating keys. Please try again later.")

@client.command(name='generate_key_Product 2_day')
@commands.guild_only()
async def generate_key_Product 2_day(ctx, amount: int = 1):
    is_user_reseller = is_reseller(ctx.author)
    price = 2.5*amount if is_user_reseller else 5.0*amount
    balance = get_user_balance(ctx.author.id)
    if balance < price:
        await ctx.reply(f"Insufficient balance. You need ${price:.2f}, but have ${balance:.2f}")
        return
    api_url = "[redacted]"
    try:
        keys = []
        response = requests.get(api_url)
        if response.status_code == 200:
            for _ in range(amount):
                keys.append(response.text.strip())
        else:
            await ctx.reply("Error generating keys. Please try again later.")
            return
        update_balance(ctx.author.id, -price)
        embed = discord.Embed(
            title="Keys Generated",
            description=f"Successfully generated {amount} daily {'keys' if amount > 1 else 'key'}.\nCheck your DMs for the {'keys' if amount > 1 else 'key'}.",
            color=int(hex_color[1:], 16)
        )
        embed.set_thumbnail(url="[redacted]")
        await ctx.reply(embed=embed)
        keys_message = "\n".join([f"Daily key {i+1}: `{key}`" for i, key in enumerate(keys)])
        await ctx.author.send(f"{keys_message}\nNew balance: ${get_user_balance(ctx.author.id):.2f}")
        await ctx.author.send(file=discord.File(Product 2_PATH))
    except Exception as e:
        logger.error(f"Error generating key: {e}")
        await ctx.reply("Error generating keys. Please try again later.")

@client.command(name='generate_key_Product 2_week')
@commands.guild_only()
async def generate_key_Product 2_week(ctx, amount: int = 1):
    is_user_reseller = is_reseller(ctx.author)
    price = 7.5*amount if is_user_reseller else 15.0*amount
    balance = get_user_balance(ctx.author.id)
    if balance < price:
        await ctx.reply(f"Insufficient balance. You need ${price:.2f}, but have ${balance:.2f}")
        return
    api_url = "[redacted]"
    try:
        keys = []
        response = requests.get(api_url)
        if response.status_code == 200:
            for _ in range(amount):
                keys.append(response.text.strip())
        else:
            await ctx.reply("Error generating keys. Please try again later.")
            return
        update_balance(ctx.author.id, -price)
        embed = discord.Embed(
            title="Keys Generated",
            description=f"Successfully generated {amount} weekly {'keys' if amount > 1 else 'key'}.\nCheck your DMs for the {'keys' if amount > 1 else 'key'}.",
            color=int(hex_color[1:], 16)
        )
        embed.set_thumbnail(url="[redacted]")
        await ctx.reply(embed=embed)
        keys_message = "\n".join([f"Weekly key {i+1}: `{key}`" for i, key in enumerate(keys)])
        await ctx.author.send(f"{keys_message}\nNew balance: ${get_user_balance(ctx.author.id):.2f}")
        await ctx.author.send(file=discord.File(Product 2_PATH))
    except Exception as e:
        logger.error(f"Error generating key: {e}")
        await ctx.reply("Error generating keys. Please try again later.")

@client.command(name='generate_key_Product 2_month')
@commands.guild_only()
async def generate_key_Product 2_month(ctx, amount: int = 1):
    is_user_reseller = is_reseller(ctx.author)
    price = 15*amount if is_user_reseller else 30.0*amount
    balance = get_user_balance(ctx.author.id)
    if balance < price:
        await ctx.reply(f"Insufficient balance. You need ${price:.2f}, but have ${balance:.2f}")
        return
    api_url = "[redacted]"
    try:
        keys = []
        response = requests.get(api_url)
        if response.status_code == 200:
            for _ in range(amount):
                keys.append(response.text.strip())
        else:
            await ctx.reply("Error generating keys. Please try again later.")
            return
        update_balance(ctx.author.id, -price)
        embed = discord.Embed(
            title="Keys Generated",
            description=f"Successfully generated {amount} monthly {'keys' if amount > 1 else 'key'}.\nCheck your DMs for the {'keys' if amount > 1 else 'key'}.",
            color=int(hex_color[1:], 16)
        )
        embed.set_thumbnail(url="[redacted]")
        await ctx.reply(embed=embed)
        keys_message = "\n".join([f"Monthly key {i+1}: `{key}`" for i, key in enumerate(keys)])
        await ctx.author.send(f"{keys_message}\nNew balance: ${get_user_balance(ctx.author.id):.2f}")
        await ctx.author.send(file=discord.File(Product 2_PATH))
    except Exception as e:
        logger.error(f"Error generating key: {e}")
        await ctx.reply("Error generating keys. Please try again later.")
            

7. Additional Commands and Admin Tools

Pricing, admin commands, and system utilities.


@client.command(name='prices')
async def prices(ctx):
    is_user_reseller = is_reseller(ctx.author)
    embed = discord.Embed(
        title="Product Prices",
        description="Current prices for our Products:",
        color=int(hex_color[1:], 16)
    )
    if is_user_reseller:
        embed.add_field(name="Monthly Key Product 1", value="$25.00", inline=False)
        embed.add_field(name="Weekly Keys Product 1", value="$25.00 (2 keys)", inline=False)
        embed.add_field(name="Monthly Key Product 2", value="$15.00", inline=False)
        embed.add_field(name="Weekly Key Product 2", value="$7.5.00", inline=False)
        embed.add_field(name="Daily Key Product 2", value="$2.50", inline=False)
        embed.add_field(name="Minimum Deposit", value="$100.00", inline=False)
    else:
        embed.add_field(name="Monthly Key Product 1", value="$50.00", inline=False)
        embed.add_field(name="Weekly Key Product 1", value="$25.00", inline=False)
        embed.add_field(name="Monthly Key Product 2", value="$30.00", inline=False)
        embed.add_field(name="Weekly Key Product 2", value="$15.00", inline=False)
        embed.add_field(name="Daily Key Product 2", value="$5.00", inline=False)
    embed.set_thumbnail(url="[redacted]")
    await ctx.reply(embed=embed)

@client.command(name='admin')
async def admin_menu(ctx):
    if ctx.author.id != WHITELISTED_ADMIN:
        return
    embed = discord.Embed(
        title="Admin Menu",
        description="Available admin commands:",
        color=int(hex_color[1:], 16)
    )
    embed.add_field(name="$transactions ", value="View user's transactions", inline=False)
    embed.add_field(name="$setbalance  ", value="Set user's balance", inline=False)
    embed.set_thumbnail(url="[redacted]")
    await ctx.reply(embed=embed)

@client.command(name='setbalance')
async def set_balance(ctx, user_id: str, amount: float):
    if ctx.author.id != WHITELISTED_ADMIN:
        return
    try:
        with sqlite3.connect('bot.db') as conn:
            c = conn.cursor()
            c.execute('''
            INSERT INTO users (user_id, balance) 
            VALUES (?, ?)
            ON CONFLICT(user_id) 
            DO UPDATE SET balance = ?
            ''', (user_id, amount, amount))
            conn.commit()
        embed = discord.Embed(
            title="Balance Updated",
            description=f"Set balance for user {user_id} to ${amount:.2f}",
            color=int(hex_color[1:], 16)
        )
        embed.set_thumbnail(url="[redacted]")
        await ctx.reply(embed=embed)
    except Exception as e:
        logger.error(f"Error setting balance: {e}")
        await ctx.reply("Error updating balance.")

@client.command(name='reset')
async def reset(ctx, key=None):
    pro_role = discord.utils.get(ctx.author.roles, name="pro")
    if pro_role:
        if key is not None:
            await ctx.message.delete()
            await ctx.send("Resetting...")
            api_url = f"[redacted]&user={key}"
            response = requests.get(api_url)
            if response.status_code == 200:
                await ctx.send(f"Successfully reset key for {ctx.author.mention}.")
            else:
                await ctx.send("Failed to reset key. Please try again later.")
        else:
            await ctx.reply("Please provide the key to reset. Usage: `$reset `")
    else:
        await ctx.reply("You do not have access to this command.")

@client.command(name='adminhelp')
async def admin_help(ctx):
    if ctx.author.id != WHITELISTED_ADMIN:
        return
    embed = discord.Embed(
        title="Admin Help",
        description="Detailed admin command information:",
        color=int(hex_color[1:], 16)
    )
    commands = {
        "$transactions ": "View last 10 transactions for a user",
        "$setbalance  ": "Set a user's balance to specific amount",
        "$adminhelp": "Show this help message",
        "$stats": "Show system statistics"
    }
    for cmd, desc in commands.items():
        embed.add_field(name=cmd, value=desc, inline=False)
    embed.set_thumbnail(url="[redacted]")
    await ctx.reply(embed=embed)

@client.command(name='stats')
async def show_stats(ctx):
    if ctx.author.id != WHITELISTED_ADMIN:
        return
    try:
        with sqlite3.connect('bot.db') as conn:
            c = conn.cursor()
            c.execute('SELECT COUNT(*) FROM users')
            total_users = c.fetchone()[0]
            c.execute('SELECT COUNT(*) FROM transactions')
            total_transactions = c.fetchone()[0]
            c.execute('SELECT SUM(amount_usd) FROM transactions WHERE status = "completed"')
            total_volume = c.fetchone()[0] or 0
            c.execute('''
            SELECT SUM(amount_usd) 
            FROM transactions 
            WHERE status = "completed" 
            AND date(created_at) = date("now")
            ''')
            today_volume = c.fetchone()[0] or 0
        embed = discord.Embed(
            title="System Statistics",
            color=int(hex_color[1:], 16)
        )
        stats = {
            "Total Users": total_users,
            "Total Transactions": total_transactions,
            "Total Volume": f"${total_volume:.2f}",
            "Today's Volume": f"${today_volume:.2f}"
        }
        for name, value in stats.items():
            embed.add_field(name=name, value=value, inline=True)
        embed.set_thumbnail(url="[redacted]")
        await ctx.reply(embed=embed)
    except Exception as e:
        logger.error(f"Error getting stats: {e}")
        await ctx.reply("Error fetching statistics.")

async def cleanup_old_transactions():
    while True:
        try:
            with sqlite3.connect('bot.db') as conn:
                c = conn.cursor()
                c.execute('''
                DELETE FROM transactions 
                WHERE status != "completed" 
                AND datetime(created_at) < datetime("now", "-24 hours")
                ''')
                conn.commit()
        except Exception as e:
            logger.error(f"Error cleaning up transactions: {e}")
        await asyncio.sleep(3600)
            

8. Error Handling and Member Join

Error handling and welcome message for new members.


@client.event
async def on_command_error(ctx, error):
    if isinstance(error, commands.errors.CommandNotFound):
        return
    error_message = "An error occurred while processing your command."
    if isinstance(error, commands.errors.MissingRequiredArgument):
        error_message = f"Missing required argument: {error.param.name}"
    elif isinstance(error, commands.errors.BadArgument):
        error_message = f"Invalid argument provided: {str(error)}"
    embed = discord.Embed(
        title="Error",
        description=error_message,
        color=discord.Color.red()
    )
    await ctx.reply(embed=embed)
    logger.error(f"Command error in {ctx.command}: {error}")

@client.event
async def on_member_join(member):
    try:
        loader_channel = client.get_channel([redacted])
        embed = discord.Embed(
            title="Welcome to MizuTools!",
            description=f"Thank you for joining our server! Please use all commands in {loader_channel.mention}",
            color=int(hex_color[1:], 16)
        )
        info = [
            "💰 Use `$deposit` to deposit money to buy keys",
            "🔑 After depositing, use `$generate_key_month` to buy a key (1 month = $50)",
            "💳 Need other payment methods? DM lucas!",
            "🔧 Use `$build` for personal Product 1 loader",
            "💻 Use `?build` for personal Product 2 loader",

            "\n**Additional Info:**",
            "• All Product 2 uses WebGUI",
            "• Access the interface via the link shown in loader after login"
        ]
        embed.add_field(
            name="Getting Started",
            value="\n".join(info),
            inline=False
        )
        embed.set_thumbnail(url="[redacted]")
        await member.send(embed=embed)
        logger.info(f"Sent welcome DM to new member {member.id}")
    except Exception as e:
        logger.error(f"Error sending welcome DM to {member.id}: {e}")
            

9. Build Command

Product 1 loader building with cooldown and queue system.


import subprocess
import shutil
import datetime

MAIN_CPP_PATH = '[redacted]'
SOLUTION_FILE_PATH = '[redacted]'
BUILD_DIRECTORY = '[redacted]'
VCVARSALL_PATH = '[redacted]'
VM_PROTECT_COMMAND = '[redacted]'
OUTPUT_FILE_PATH = '[redacted]'
PROTECTED_FILE_PATH = '[redacted]'
COOLDOWN_FILE = '[redacted]'
BASE_OUTPUT_DIR = '[redacted]'

queue = []

try:
    if os.path.exists(COOLDOWN_FILE) and os.path.getsize(COOLDOWN_FILE) > 0:
        with open(COOLDOWN_FILE, 'r') as file:
            cooldown_data = json.load(file)
    else:
        cooldown_data = {}
except json.JSONDecodeError:
    cooldown_data = {}

def save_cooldown_data():
    with open(COOLDOWN_FILE, 'w') as file:
        json.dump(cooldown_data, file)

def update_main_cpp(file_path, username):
    with open(file_path, 'r') as file:
        content = file.read()
    current_time = datetime.datetime.now()
    build_time = f"Built for {username} at: {current_time.strftime('%Y-%m-%d %H:%M:%S')} GMT +8"
    content = content.replace('BUILD_TIME_PLACEHOLDER', build_time)
    with open(file_path, 'w') as file:
        file.write(content)
    return build_time

def revert_main_cpp(file_path, build_time):
    try:
        with open(file_path, 'r') as file:
            content = file.read()
        content = content.replace(build_time, 'BUILD_TIME_PLACEHOLDER')
        with open(file_path, 'w') as file:
            file.write(content)
    except Exception as e:
        logger.error(f"Error reverting main.cpp: {e}")
        raise

async def process_queue():
    while queue:
        ctx = queue[0]
        user_id = str(ctx.author.id)
        username = str(ctx.author)
        current_time = datetime.datetime.now()
        if user_id == "[redacted]":
            pass
        else:
            if user_id in cooldown_data:
                last_execution_time = datetime.datetime.strptime(cooldown_data[user_id], "%Y-%m-%dT%H:%M:%S.%f")
                time_since_last_execution = current_time - last_execution_time
                if time_since_last_execution < datetime.timedelta(weeks=1):
                    time_remaining = datetime.timedelta(weeks=1) - time_since_last_execution
                    await ctx.send(f'You can make a new loader in {time_remaining}.')
                    queue.pop(0)
                    continue
        try:
            await ctx.send(f"Building loader for {username}...")
            build_time = update_main_cpp(MAIN_CPP_PATH, username)
            os.makedirs(BUILD_DIRECTORY, exist_ok=True)
            os.chdir(BUILD_DIRECTORY)
            vcvars_command = f'call "[redacted]" x64 && msbuild "[redacted]" /p:Configuration=Release'
            process = await asyncio.create_subprocess_shell(vcvars_command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
            stdout, stderr = await process.communicate()
            if process.returncode == 0:
                vm_protect_command = f'[redacted] "[redacted]" -pf "[redacted]"'
                vm_protect_process = await asyncio.create_subprocess_shell(vm_protect_command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
                vm_protect_stdout, vm_protect_stderr = await vm_protect_process.communicate()
                if os.path.exists(OUTPUT_FILE_PATH):
                    user_dir = os.path.join(BASE_OUTPUT_DIR, username)
                    os.makedirs(user_dir, exist_ok=True)
                    user_file_path = os.path.join(user_dir, 'angel.exe')
                    shutil.copy2(OUTPUT_FILE_PATH, user_file_path)
                    await ctx.author.send(file=discord.File(user_file_path))
                    await ctx.send(f'Compilation for {username} successful! Check DM!')
                    revert_main_cpp(MAIN_CPP_PATH, build_time)
                    cooldown_data[user_id] = current_time.isoformat()
                    save_cooldown_data()
                else:
                    await ctx.send(f'An error occurred during VMProtect processing: {vm_protect_stderr.decode()}')
            else:
                revert_main_cpp(MAIN_CPP_PATH, build_time)
                await ctx.send(f'Build failed: {stderr.decode()}')
        except Exception as e:
            logger.error(f"Error processing build: {e}")
            await ctx.send(f'An error occurred: {str(e)}')
        queue.pop(0)
        await asyncio.sleep(1)

@client.command(name='build')
@commands.guild_only()
async def build_solution(ctx):
    if (ctx.channel.id == [redacted]) or (ctx.channel.id == [redacted]):
        pass
    else:
        await ctx.send('You cannot use this command in this channel!')
        return
    pro_role = discord.utils.get(ctx.author.roles, name="pro")
    if not pro_role:
        await ctx.reply("You need the 'pro' role to use this command.")
        return
    user_id = str(ctx.author.id)
    username = str(ctx.author)
    if user_id in [str(user.author.id) for user in queue]:
        await ctx.send(f'{username} is already in the queue. Please wait for your turn.')
    else:
        queue.append(ctx)
        await ctx.send(f'{username} has been added to the queue. Please wait for your turn.')
    if len(queue) == 1:
        await process_queue()

if __name__ == "__main__":
    client.run(TOKEN)