Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import aiohttp
- import requests
- import sqlite3
- import random
- import json
- import time
- import asyncio
- import logging
- from dataclasses import dataclass
- from typing import Optional
- from typing import Union
- from pyrogram import Client, filters
- from pyrogram.types import (
- CallbackQuery,
- InlineKeyboardButton,
- InlineKeyboardMarkup,
- Message,
- )
- from database import cur, save
- from utils import log_games
- MONEROPAY_ENDPOINT = "http://127.0.0.1:5051"
- DB_PATH = 'user_addresses.db'
- user_states = {}
- conn = sqlite3.connect(DB_PATH, check_same_thread=False)
- with conn:
- conn.execute("""
- CREATE TABLE IF NOT EXISTS user_addresses (
- user_id INTEGER PRIMARY KEY,
- payout_address TEXT NOT NULL
- )
- """)
- async def log_to_admin(c: Client, user_id: int, message: str):
- admin_user_id = 5852338735 # Admin's user ID
- try:
- # Send the log message to the admin
- await c.send_message(admin_user_id, message)
- except Exception as e:
- print(f"Failed to send log message: {e}")
- # Callback handler for inline button clicks (use stored address)
- @Client.on_callback_query(filters.regex(r'^use_address\|'))
- async def use_stored_address(c: Client, m: CallbackQuery):
- data = m.data.split('|')
- bet_amount = float(data[1])
- dice_number = int(data[2])
- user_id = m.from_user.id
- # Retrieve stored address
- stored_address = get_user_address(user_id)
- if not stored_address:
- await m.message.reply_text("⚠️ No registered address found. Please enter a new address.")
- return await enter_new_address(c, m)
- # Proceed with payment flow
- await generate_payment_address_and_handle_flow(m, c, user_id, bet_amount, dice_number, stored_address)
- # Callback handler for inline button clicks (insert new address)
- @Client.on_callback_query(filters.regex(r'^new_address\|'))
- async def enter_new_address(c: Client, m: CallbackQuery, bet_amount: float = None, dice_number: int = None):
- # If bet_amount and dice_number are not passed, extract them from the callback data
- if not bet_amount or not dice_number:
- data = m.data.split('|')
- bet_amount = float(data[1])
- dice_number = int(data[2])
- # Set the user's state to "waiting_for_address"
- user_states[m.from_user.id] = {'state': 'waiting_for_address', 'cancelled': False}
- # Create a cancel button
- cancel_button = InlineKeyboardButton("Cancel", callback_data="cancel_address")
- keyboard = InlineKeyboardMarkup([[cancel_button]])
- # Prompt for new address with a cancel button
- await m.message.reply_text(
- "Please enter the payout address:",
- reply_markup=keyboard
- )
- # Wait for the user's reply
- user_reply = await c.wait_for_message(
- chat_id=m.message.chat.id,
- filters=filters.text & filters.user(m.from_user.id)
- )
- # Check if the user canceled the operation
- if user_states[m.from_user.id]['cancelled']:
- await m.message.reply_text("Operação cancelada.")
- # Reset the user's state after cancellation
- user_states.pop(m.from_user.id, None)
- return # Stop further processing
- # If the user provided an address, process it
- payout_address = user_reply.text.strip()
- # Validate the address
- if not is_valid_monero_address(payout_address):
- await m.message.reply_text("<b>⚠️ No registered address found. Please enter a new address.</b>")
- # Reset user state to stop further requests
- user_states.pop(m.from_user.id, None)
- return await enter_new_address(c, m, bet_amount, dice_number)
- # Save the address in the database
- store_user_address(m.from_user.id, payout_address)
- # Proceed with the payment flow after address is saved
- await generate_payment_address_and_handle_flow(m, c, m.from_user.id, bet_amount, dice_number, payout_address)
- @Client.on_callback_query(filters.regex(r'^cancel_address$'))
- async def cancel_address(c: Client, m: CallbackQuery):
- # If the cancel button was pressed, send cancellation message and reset the state
- await m.message.reply_text("Operação cancelada.")
- # Remove the user state to stop the address request
- user_states.pop(m.from_user.id, None)
- def get_user_address(user_id):
- """Retrieve the stored payout address for a given user."""
- cursor = conn.cursor()
- cursor.execute("SELECT payout_address FROM user_addresses WHERE user_id = ?", (user_id,))
- row = cursor.fetchone()
- return row[0] if row else None
- def store_user_address(user_id, payout_address):
- """Store or update the payout address for a given user."""
- with conn:
- conn.execute("""
- INSERT INTO user_addresses (user_id, payout_address)
- VALUES (?, ?)
- ON CONFLICT(user_id)
- DO UPDATE SET payout_address = excluded.payout_address;
- """, (user_id, payout_address))
- def create_payment_address(amount, description, callback_url):
- response = requests.post(
- f"{MONEROPAY_ENDPOINT}/receive",
- headers={"Content-Type": "application/json"},
- json={
- "amount": amount,
- "description": description,
- "callback_url": callback_url
- }
- )
- return response.json()
- def is_valid_monero_address(address: str) -> bool:
- # Basic validation for Monero address
- if len(address) in [95, 106]: # Monero address lengths (short/standard)
- return address.startswith("4") or address.startswith("8")
- return False
- @Client.on_callback_query(filters.regex(r'^dice'))
- async def dice(c: Client, m: CallbackQuery):
- user_id = m.from_user.id
- await handle_bet_flow(m, c, user_id)
- async def handle_bet_flow(m, c, user_id):
- # Step 1: Ask for the Bet Amount
- bet_msg = await m.message.reply_text(
- """<b>💰 Enter the bet amount:</b>\n<i>⚠ If you guess the dice number correctly, you'll win 5x the bet amount!</i>\n"""
- """<b>⚠️ Minimum bet: 0.001 XMR</i>\n"""
- """<b>⚠️ Maximum bet: 0.1 XMR</i>"""
- )
- # Wait for the user to reply with a bet value
- user_reply: Message = await c.wait_for_message(
- chat_id=m.message.chat.id,
- filters=filters.text & filters.user(user_id)
- )
- # Validate Bet Amount
- try:
- bet_amount = float(user_reply.text)
- if bet_amount < 0.001:
- raise ValueError("The minimum bet is 0,001 XMR")
- if bet_amount > 0.1:
- raise ValueError("The maximum bet is 0,1 XMR")
- except ValueError:
- await bet_msg.delete()
- return await m.message.reply_text("<b>⚠️ Invalid value. Enter amounts between 0.001 XMR and 0.1 XMR</b>")
- dice_msg = await m.message.reply_text(
- """<b>🎲 Choose a number to bet on the dice (1-6):</b>\n<i>Choose wisely!</i>"""
- )
- # Wait for the user to choose a number between 1 and 6
- user_reply = await c.wait_for_message(
- chat_id=m.message.chat.id,
- filters=filters.text & filters.user(user_id)
- )
- # Validate chosen dice number
- try:
- dice_number = int(user_reply.text)
- if dice_number < 1 or dice_number > 6:
- raise ValueError("Choose a number between 1 e 6.")
- except ValueError:
- await dice_msg.delete()
- return await m.message.reply_text("<b>⚠️ Invalid number. Enter a number between 1 and 6.</b>")
- # Step 3: Ask if the user wants to use the stored address or enter a new one
- stored_address = get_user_address(user_id)
- if stored_address:
- # Address exists, proceed with the bet
- await m.message.reply_text(
- f"You have a registered address: {stored_address}\n"
- "Would you like to use this address or provide a new one?",
- reply_markup=InlineKeyboardMarkup([
- [
- InlineKeyboardButton(
- "Use registered address",
- callback_data=f"use_address|{bet_amount}|{dice_number}"
- )
- ],
- [
- InlineKeyboardButton(
- "Enter new address",
- callback_data=f"new_address|{bet_amount}|{dice_number}"
- )
- ]
- ])
- )
- else:
- # No address found, prompt user to enter a new address
- await enter_new_address(c, m, bet_amount, dice_number)
- # Step 3: Generate Payment Address using MoneroPay API
- async def generate_payment_address_and_handle_flow(m, c, user_id, bet_amount, dice_number, payout_address):
- # Step 3: Generate Payment Address using MoneroPay API
- create_address_response = requests.post(
- f'{MONEROPAY_ENDPOINT}/receive',
- json={
- 'amount': int(bet_amount * 1e12), # Convert XMR to piconero
- 'description': f'Dice Bet by {user_id}',
- 'callback_url': 'https://dontpad.com/monerologs'
- }
- )
- print(f"Status Code: {create_address_response.status_code}")
- print(f"Response Text: {create_address_response.text}")
- if create_address_response.status_code != 200:
- await m.message.reply_text('<b>⚠️ Error generating the payment address. Please try again.</b>')
- return
- create_address_data = create_address_response.json()
- payment_address = create_address_data.get('address')
- log_message = f"<code>{user_id}</code> generated a payment address <code>{payment_address}</code> for <code>{bet_amount}</code> XMR with his payout address {payout_address}, he is betting on number {dice_number}"
- await log_to_admin(c, user_id, log_message)
- formatted_bet = f"{bet_amount:.8f}".rstrip('0').rstrip('.')
- # Step 4: Inform the user about the generated payment address
- await m.message.reply_text(
- f"<b>✅ Payment address generated:</b>\n<code>{payment_address}</code>\n\n"
- f"<i>Please send <code>{formatted_bet}</code> XMR to the address above. The game will start after 1 confirmation.</i>\n"
- f"<i>If the payment is not made within 15 minutes, the bet will be canceled.</i>"
- )
- # Step 5: Handle the payment flow
- await handle_user_payment_flow(m, c, user_id, payment_address, payout_address, create_address_data, bet_amount, dice_number)
- # Define the function to handle user payment flow
- async def handle_user_payment_flow(m, c, user_id, payment_address, payout_address, create_address_data, bet_amount, dice_number):
- # Now, the payout address is passed into the function, so you can use it directly.
- print(f"User entered Payout Address: {payout_address}")
- # Set timeout (15 minutes)
- timeout = 900 # 900 seconds = 15 minutes
- start_time = time.time()
- # Polling the status with the receive endpoint
- while True:
- # Check if 15 minutes have passed
- if time.time() - start_time > timeout:
- await log_to_admin(c, m.message.chat.id, "Timeout reached, no payment received within the allowed time from <code>{user_id}</code>.")
- reply_markup = InlineKeyboardMarkup(
- [[InlineKeyboardButton("Iniciar novamente", callback_data="start")]]
- )
- await m.message.reply_text(
- "The time to confirm the payment has expired. Please start the process again.",
- reply_markup=reply_markup
- )
- return
- try:
- # Log the status check attempt to ensure it's being executed
- print(f"Checking payment status for address: {payment_address}...")
- # Make a request to check the transactions to the specified payout address
- payment_status_response = requests.get(
- f'{MONEROPAY_ENDPOINT}/receive/{payment_address}' # Use the payout address to check payments
- )
- # Log the raw response for debugging
- print(f"Response Status Code: {payment_status_response.status_code}")
- print(f"Response Body: {payment_status_response.text}")
- # Attempt to parse the JSON response
- payment_status = payment_status_response.json()
- # Check if any transactions have been received for the address
- transactions = payment_status.get('transactions', [])
- if transactions:
- tx = transactions[0] # Assuming we're looking at the first transaction
- confirmations = tx.get('confirmations', 0) # Get number of confirmations
- if confirmations >= 0: # Allow 0 confirmations
- # Once the payment is received (0 confirmations or more), notify the user
- log_message = f"<code>{user_id}</code> has paid for his bet of <code>{bet_amount}</code> XMR for <code>{payment_address}</code>"
- await log_to_admin(c, user_id, log_message)
- await m.message.reply_text(f"Payment confirmed with {confirmations} confirmations! TXID: {tx['tx_hash']}. Please wait for the next step.")
- sort = await c.send_dice(chat_id=m.message.chat.id, emoji='🎲')
- await asyncio.sleep(5)
- formatted_prize = f"{bet_amount * 5:.8f}" # Format the prize to 8 decimal places for XMR
- await dice_logic(c, sort, bet_amount, payout_address, m, formatted_prize, dice_number)
- return
- else:
- # Log status and continue checking
- print(f"Payment not yet confirmed, waiting for more confirmations...")
- await asyncio.sleep(10)
- else:
- # No transaction yet, waiting for payment
- print("Payment not yet received. Waiting for transaction...")
- await asyncio.sleep(5) # Wait for 5 seconds and check again
- except json.JSONDecodeError as e:
- # Handle JSON errors gracefully
- print(f"JSONDecodeError: {e}")
- print(f"Raw response: {payment_status_response.text}")
- await m.message.reply_text("An error occurred while checking the payment status. Please try again later.")
- return
- except Exception as e:
- # General exception handling
- print(f"Error: {e}")
- await m.message.reply_text("An unexpected error occurred. Please try again later.")
- return
- # Step 4: Roll the Dice
- sort = await c.send_dice(chat_id=m.message.chat.id, emoji='🎲')
- await asyncio.sleep(10)
- formatted_prize = f"{bet_amount * 5:.8f}" # Format the prize to 8 decimal places for XMR
- await dice_logic(c, sort, bet_amount, payout_address, m, formatted_prize, dice_number)
- async def send_prize_to_address(prize, payout_address):
- headers = {'Content-Type': 'application/json'}
- amount_piconero = int(prize * 1e12)
- payload = {
- "destinations": [
- {
- "amount": amount_piconero,
- "address": payout_address
- }
- ]
- }
- # Format the prize for display
- formatted_prize = f"{prize:.8f}" # Format the prize to 8 decimal places for XMR
- print(f"Sending {formatted_prize} XMR to address: {payout_address}...") # Print for debugging
- # Send the POST request to transfer the prize
- response = requests.post(f"{MONEROPAY_ENDPOINT}/transfer", json=payload, headers=headers)
- if response.status_code == 200:
- data = response.json()
- tx_hash = data.get("tx_hash_list", [None])[0]
- if tx_hash:
- print(f"Transfer initiated successfully. Transaction hash: {tx_hash}")
- return True, tx_hash # Return success and transaction hash
- else:
- print("Transaction hash not found in the response.")
- return False, "Transaction hash not found"
- else:
- print(f"Error: {response.text}")
- return False, f"Error: {response.text}"
- async def check_transfer_status(tx_hash):
- response = requests.get(f"{MONEROPAY_ENDPOINT}/transfer/{tx_hash}")
- if response.status_code == 200:
- data = response.json()
- state = data.get("state")
- confirmations = data.get("confirmations", 0)
- if state == "completed": # Treat completed state as confirmed
- return True, "Transaction completed successfully"
- else:
- return False, f"Transaction state: {state}"
- else:
- return False, f"Error checking status: {response.text}"
- async def dice_logic(c, sort, bet_amount, payout_address, m, formatted_prize, dice_number):
- # Step 5: Check the Dice Result
- if sort.dice.value == dice_number:
- # Calculate the prize
- prize = bet_amount * 5
- msg = f"🎉 You rolled {dice_number} and won {formatted_prize} XMR!"
- # Log the win
- win_log = f"User <code>{user_id}</code> won the bet: Rolled {sort.dice.value}, Bet: <code>{bet_amount}</code>, Prize: <code>{prize}</code> XMR"
- await log_to_admin(c, user_id, win_log)
- # Send the prize to the payout address
- success, tx_hash = await send_prize_to_address(prize, payout_address)
- if success:
- msg += f"\n💸 Sent! Transaction Hash: {tx_hash}"
- # Log the successful transaction
- tx_log = f"Transaction successful for User {m.message.chat.id}: TX Hash: {tx_hash}, Prize: {prize} XMR"
- await log_to_admin(c, m.message.chat.id, tx_log)
- else:
- msg += "\n⚠️ Error transferring the prize. Please try again later."
- # Log the failed transaction
- fail_log = f"Transaction failed for User {m.message.chat.id}: Failed to send prize {prize} XMR"
- await log_to_admin(c, m.message.chat.id, fail_log)
- else:
- prize = 0
- msg = f"😞 You lost the bet! The dice rolled {sort.dice.value}."
- # Log the loss
- loss_log = f"User {m.message.chat.id} lost the bet: Rolled {sort.dice.value}, Bet: {bet_amount}"
- await log_to_admin(c, m.message.chat.id, loss_log)
- # Define the inline keyboard with the button
- reply_markup = InlineKeyboardMarkup(
- [[InlineKeyboardButton("Play again", callback_data="dice")]] # "dice" is the callback to trigger another bet
- )
- # Send the result message with the reply_markup (the button)
- await m.message.reply_text(msg, reply_markup=reply_markup)
- @Client.on_callback_query(filters.regex("^info$"))
- async def info(c: Client, m: CallbackQuery):
- # Acknowledge the callback query so the button disappears or the loading stops
- await m.answer()
- await m.message.edit_text(
- f"<b>Information about Monero Casino</b>\n\n"
- f"At the moment, we only have <b>Monero Dice</b> as a game, but we plan to add more games to the bot soon.\n\n"
- f"<i>Why is it fair?</i>\n"
- f"Our game is <b>provably fair</b> and <b>random</b> because it uses Telegram's real-time dice, which cannot be tampered with. With each roll, the result is generated directly on Telegram's servers (like every animated dice emoji on Telegram), ensuring that no one can interfere with the game's outcome.\n\n"
- f"<b>Good luck!</b> 🍀\n"
- f"<i>Support: @ninefag</i>\n",
- reply_markup=InlineKeyboardMarkup([
- [InlineKeyboardButton("🎲 Play Monero Dice", callback_data="dice")]
- ])
- )
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement