taslim19
fix: use HTML link and parse_mode for kang sticker pack message
49650a3
"""
Extol Wallet & Payment Integration for DragMusic Bot
Commands:
/addapi <api_key> (private chat only)
/addextol <address>
/removeextol
/payextol <amount>
/checkextol
/extolbal
/sendextol <to_address> <amount>
"""
import os
import httpx
from pyrogram import filters
from DragMusic import app
from DragMusic.utils.database import mongodb
from pyrogram.types import Message
from datetime import datetime
from pyrogram.enums import ParseMode
API_BASE_URL = "https://marketapi.animerealms.org"
api_keys_collection = mongodb.iac_api_keys
addresses_collection = mongodb.iac_extol_addresses
payments_collection = mongodb.iac_payments
# Async helpers for MongoDB
async def set_user_api_key(user_id: int, api_key: str):
await api_keys_collection.update_one(
{"user_id": user_id},
{"$set": {"api_key": api_key}},
upsert=True
)
async def get_user_api_key(user_id: int):
doc = await api_keys_collection.find_one({"user_id": user_id})
return doc["api_key"] if doc and "api_key" in doc else None
async def set_user_address(user_id: int, address: str):
await addresses_collection.update_one(
{"user_id": user_id},
{"$set": {"address": address}},
upsert=True
)
async def get_user_address(user_id: int):
doc = await addresses_collection.find_one({"user_id": user_id})
return doc["address"] if doc and "address" in doc else None
async def remove_user_address(user_id: int):
await addresses_collection.delete_one({"user_id": user_id})
async def set_last_payment_id(user_id: int, payment_id: str):
await payments_collection.update_one(
{"user_id": user_id},
{"$set": {"last_payment_id": payment_id}},
upsert=True
)
async def get_last_payment_id(user_id: int):
doc = await payments_collection.find_one({"user_id": user_id})
return doc["last_payment_id"] if doc and "last_payment_id" in doc else None
async def save_payment(user_id: int, payment_id: str, address: str, amount: float, payment_url: str):
await payments_collection.insert_one({
"user_id": user_id,
"payment_id": payment_id,
"address": address,
"amount": amount,
"payment_url": payment_url,
"created_at": datetime.utcnow()
})
async def get_user_payments(user_id: int, limit: int = 10):
cursor = payments_collection.find({"user_id": user_id}).sort("created_at", -1).limit(limit)
return await cursor.to_list(length=limit)
# /addapi <api_key> (private chat only)
@app.on_message(filters.command("addapi") & filters.private)
async def set_api_key_handler(client, message: Message):
args = message.text.split()
if len(args) != 2:
await message.reply_text("Usage: /addapi <your_api_key>")
return
await set_user_api_key(message.from_user.id, args[1])
await message.reply_text("βœ… Extol API Key set successfully.")
# Helper to check API key
async def check_api_key(message: Message):
user_id = message.from_user.id
api_key = await get_user_api_key(user_id)
if not api_key:
await message.reply_text("[IAC Marketplace] API key not set. Use /addapi <your_api_key> in bot DM to set it.")
return None
return api_key
# /addextol <address>
@app.on_message(filters.command("addextol"))
async def add_extol_handler(client, message: Message):
args = message.text.split()
if len(args) != 2 or not args[1].startswith("EXT"):
await message.reply_text("❌ Provide a valid Extol address starting with EXT.\n Usage: /addextol <address>")
return
await set_user_address(message.from_user.id, args[1])
await message.reply_text(f"βœ… Extol address saved: `{args[1]}`.")
# /removeextol
@app.on_message(filters.command("removeextol"))
async def remove_extol_handler(client, message: Message):
await remove_user_address(message.from_user.id)
await message.reply_text("βœ… Extol address removed.")
# /payextol <amount>
@app.on_message(filters.command("payextol"))
async def pay_extol_handler(client, message: Message):
args = message.text.split()
if len(args) != 2 or not args[1].isdigit():
await message.reply_text("❌ Enter a valid numeric amount.\n Usage: /payextol <amount>")
return
address = await get_user_address(message.from_user.id)
if not address:
await message.reply_text("❌ Use /addextol <address> to set your address first.")
return
msg = await message.reply_text("πŸ”— Generating payment link...")
data = {"address": address, "amount": int(args[1])}
resp = await extol_request(message.from_user.id, "/api/create-payment", method="POST", data=data)
if not resp.get("ok"):
return await msg.edit(f"❌ Error: `{resp.get('error')}`")
payment_id = resp["payment_id"]
await set_last_payment_id(message.from_user.id, payment_id)
# Save payment to history for /checkextol
await save_payment(message.from_user.id, payment_id, address, int(args[1]), resp["payment_url"])
await msg.edit(f"βœ… <a href='{resp['payment_url']}'>Click here to pay</a>", disable_web_page_preview=True, parse_mode=ParseMode.HTML)
# /checkextol
@app.on_message(filters.command("checkextol"))
async def check_extol_handler(client, message: Message):
payments = await get_user_payments(message.from_user.id, limit=10)
if not payments:
await message.reply_text("❌ No recent payments found.")
return
msg = await message.reply_text("πŸ” Checking last 10 payment statuses...")
status_lines = []
for p in payments:
payment_id = p["payment_id"]
amount = p["amount"]
pay_url = p["payment_url"]
resp = await extol_request(message.from_user.id, "/api/payment-status", params={"payment_id": payment_id})
if not resp.get("ok"):
status_str = f"❌ Error: {resp.get('error') or 'Unknown'}"
else:
status = resp["status"]
if status == "paid":
status_str = f"βœ… <b>Paid</b><br>From: <code>{resp.get('from_address', '-')}</code>, At: <code>{resp.get('paid_at', '-')}</code>"
elif status == "timeout":
status_str = f"βŒ› <b>Timeout</b><br>Expired: <code>{resp.get('expired_at', '-')}</code>"
else:
status_str = "⏳ <b>Pending</b>"
status_lines.append(f"Amount: <code>{amount}</code><br>"
f'<a href="{pay_url}">Click here to pay</a><br>'
f"ID: <code>{payment_id}</code><br>{status_str}")
await msg.edit("\n\n".join(status_lines), disable_web_page_preview=True, parse_mode=ParseMode.HTML)
# /extolbal
@app.on_message(filters.command("extolbal"))
async def extol_balance_handler(client, message: Message):
msg = await message.reply_text("πŸ” Fetching Extol balance...")
resp = await extol_request(message.from_user.id, "/api/balance")
if not resp.get("ok"):
return await msg.edit(f"❌ Error: `{resp.get('error')}`")
balance = resp.get("balance", 0)
address = resp.get("address", "Unknown")
await msg.edit(f"πŸ’° Balance: {balance} EXT\n🏷️ Address: {address}")
# /sendextol <to_address> <amount>
@app.on_message(filters.command("sendextol"))
async def send_extol_handler(client, message: Message):
args = message.text.split()
if len(args) != 3 or not args[2].isdigit() or not args[1].startswith("EXT"):
await message.reply_text("Usage: /sendextol <to_address> <amount>")
return
to_address, amt = args[1], int(args[2])
if amt <= 0:
await message.reply_text("❌ Amount must be positive.")
return
msg = await message.reply_text("πŸ”„ Sending Extol...")
resp = await extol_request(
message.from_user.id,
"/api/withdraw",
method="GET",
params={"amount": amt, "address": to_address},
)
if not resp.get("ok"):
error_msg = resp.get('error') or 'Unknown error (no message from API)'
code = resp.get('code')
if code is not None:
error_msg = f"(code {code}) {error_msg}"
return await msg.edit(f"❌ Error: {error_msg}")
link = resp.get("link", "No explorer link.")
await msg.edit(f"βœ… Sent {amt} EXT to {to_address} \n<a href='{link}'>View Transaction</a>", disable_web_page_preview=True, parse_mode=ParseMode.HTML)
# --- HTTPX helper ---
async def extol_request(user_id, path, method="GET", data=None, params=None):
api_key = await get_user_api_key(user_id)
if not api_key:
return {"ok": False, "error": "API key not set. Use /addapi <key> in DM."}
async with httpx.AsyncClient() as client:
headers = {"api-key": api_key}
try:
if method == "GET":
r = await client.get(f"{API_BASE_URL}{path}", headers=headers, params=params, timeout=10)
else:
r = await client.post(f"{API_BASE_URL}{path}", headers=headers, json=data, timeout=10)
return r.json()
except Exception as e:
return {"ok": False, "error": str(e)}