Advertisement
Guest User

monero dicing

a guest
Nov 25th, 2024
63
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.41 KB | None | 0 0
  1. import aiohttp
  2. import requests
  3. import sqlite3
  4. import random
  5. import json
  6. import time
  7. import asyncio
  8. import logging
  9. from dataclasses import dataclass
  10. from typing import Optional
  11. from typing import Union
  12. from pyrogram import Client, filters
  13. from pyrogram.types import (
  14. CallbackQuery,
  15. InlineKeyboardButton,
  16. InlineKeyboardMarkup,
  17. Message,
  18. )
  19.  
  20. from database import cur, save
  21. from utils import log_games
  22.  
  23. MONEROPAY_ENDPOINT = "http://127.0.0.1:5051"
  24. DB_PATH = 'user_addresses.db'
  25. user_states = {}
  26.  
  27.  
  28. conn = sqlite3.connect(DB_PATH, check_same_thread=False)
  29.  
  30.  
  31. with conn:
  32. conn.execute("""
  33. CREATE TABLE IF NOT EXISTS user_addresses (
  34. user_id INTEGER PRIMARY KEY,
  35. payout_address TEXT NOT NULL
  36. )
  37. """)
  38.  
  39. async def log_to_admin(c: Client, user_id: int, message: str):
  40. admin_user_id = 5852338735 # Admin's user ID
  41. try:
  42. # Send the log message to the admin
  43. await c.send_message(admin_user_id, message)
  44. except Exception as e:
  45. print(f"Failed to send log message: {e}")
  46.  
  47.  
  48.  
  49. # Callback handler for inline button clicks (use stored address)
  50. @Client.on_callback_query(filters.regex(r'^use_address\|'))
  51. async def use_stored_address(c: Client, m: CallbackQuery):
  52. data = m.data.split('|')
  53. bet_amount = float(data[1])
  54. dice_number = int(data[2])
  55. user_id = m.from_user.id
  56.  
  57. # Retrieve stored address
  58. stored_address = get_user_address(user_id)
  59. if not stored_address:
  60. await m.message.reply_text("⚠️ No registered address found. Please enter a new address.")
  61. return await enter_new_address(c, m)
  62.  
  63. # Proceed with payment flow
  64. await generate_payment_address_and_handle_flow(m, c, user_id, bet_amount, dice_number, stored_address)
  65.  
  66.  
  67.  
  68.  
  69.  
  70.  
  71.  
  72. # Callback handler for inline button clicks (insert new address)
  73. @Client.on_callback_query(filters.regex(r'^new_address\|'))
  74. async def enter_new_address(c: Client, m: CallbackQuery, bet_amount: float = None, dice_number: int = None):
  75. # If bet_amount and dice_number are not passed, extract them from the callback data
  76. if not bet_amount or not dice_number:
  77. data = m.data.split('|')
  78. bet_amount = float(data[1])
  79. dice_number = int(data[2])
  80.  
  81. # Set the user's state to "waiting_for_address"
  82. user_states[m.from_user.id] = {'state': 'waiting_for_address', 'cancelled': False}
  83.  
  84. # Create a cancel button
  85. cancel_button = InlineKeyboardButton("Cancel", callback_data="cancel_address")
  86. keyboard = InlineKeyboardMarkup([[cancel_button]])
  87.  
  88. # Prompt for new address with a cancel button
  89. await m.message.reply_text(
  90. "Please enter the payout address:",
  91. reply_markup=keyboard
  92. )
  93.  
  94. # Wait for the user's reply
  95. user_reply = await c.wait_for_message(
  96. chat_id=m.message.chat.id,
  97. filters=filters.text & filters.user(m.from_user.id)
  98. )
  99.  
  100. # Check if the user canceled the operation
  101. if user_states[m.from_user.id]['cancelled']:
  102. await m.message.reply_text("Operação cancelada.")
  103. # Reset the user's state after cancellation
  104. user_states.pop(m.from_user.id, None)
  105. return # Stop further processing
  106.  
  107. # If the user provided an address, process it
  108. payout_address = user_reply.text.strip()
  109.  
  110. # Validate the address
  111. if not is_valid_monero_address(payout_address):
  112. await m.message.reply_text("<b>⚠️ No registered address found. Please enter a new address.</b>")
  113. # Reset user state to stop further requests
  114. user_states.pop(m.from_user.id, None)
  115. return await enter_new_address(c, m, bet_amount, dice_number)
  116.  
  117. # Save the address in the database
  118. store_user_address(m.from_user.id, payout_address)
  119.  
  120. # Proceed with the payment flow after address is saved
  121. await generate_payment_address_and_handle_flow(m, c, m.from_user.id, bet_amount, dice_number, payout_address)
  122.  
  123.  
  124.  
  125.  
  126.  
  127.  
  128.  
  129.  
  130.  
  131.  
  132.  
  133. @Client.on_callback_query(filters.regex(r'^cancel_address$'))
  134. async def cancel_address(c: Client, m: CallbackQuery):
  135. # If the cancel button was pressed, send cancellation message and reset the state
  136. await m.message.reply_text("Operação cancelada.")
  137.  
  138. # Remove the user state to stop the address request
  139. user_states.pop(m.from_user.id, None)
  140.  
  141.  
  142. def get_user_address(user_id):
  143. """Retrieve the stored payout address for a given user."""
  144. cursor = conn.cursor()
  145. cursor.execute("SELECT payout_address FROM user_addresses WHERE user_id = ?", (user_id,))
  146. row = cursor.fetchone()
  147. return row[0] if row else None
  148.  
  149.  
  150.  
  151. def store_user_address(user_id, payout_address):
  152. """Store or update the payout address for a given user."""
  153. with conn:
  154. conn.execute("""
  155. INSERT INTO user_addresses (user_id, payout_address)
  156. VALUES (?, ?)
  157. ON CONFLICT(user_id)
  158. DO UPDATE SET payout_address = excluded.payout_address;
  159. """, (user_id, payout_address))
  160.  
  161.  
  162.  
  163.  
  164. def create_payment_address(amount, description, callback_url):
  165. response = requests.post(
  166. f"{MONEROPAY_ENDPOINT}/receive",
  167. headers={"Content-Type": "application/json"},
  168. json={
  169. "amount": amount,
  170. "description": description,
  171. "callback_url": callback_url
  172. }
  173. )
  174. return response.json()
  175.  
  176.  
  177. def is_valid_monero_address(address: str) -> bool:
  178. # Basic validation for Monero address
  179. if len(address) in [95, 106]: # Monero address lengths (short/standard)
  180. return address.startswith("4") or address.startswith("8")
  181. return False
  182.  
  183.  
  184.  
  185. @Client.on_callback_query(filters.regex(r'^dice'))
  186. async def dice(c: Client, m: CallbackQuery):
  187. user_id = m.from_user.id
  188. await handle_bet_flow(m, c, user_id)
  189.  
  190. async def handle_bet_flow(m, c, user_id):
  191. # Step 1: Ask for the Bet Amount
  192. bet_msg = await m.message.reply_text(
  193. """<b>💰 Enter the bet amount:</b>\n<i>⚠ If you guess the dice number correctly, you'll win 5x the bet amount!</i>\n"""
  194. """<b>⚠️ Minimum bet: 0.001 XMR</i>\n"""
  195. """<b>⚠️ Maximum bet: 0.1 XMR</i>"""
  196. )
  197. # Wait for the user to reply with a bet value
  198. user_reply: Message = await c.wait_for_message(
  199. chat_id=m.message.chat.id,
  200. filters=filters.text & filters.user(user_id)
  201. )
  202.  
  203. # Validate Bet Amount
  204. try:
  205. bet_amount = float(user_reply.text)
  206. if bet_amount < 0.001:
  207. raise ValueError("The minimum bet is 0,001 XMR")
  208.  
  209. if bet_amount > 0.1:
  210. raise ValueError("The maximum bet is 0,1 XMR")
  211. except ValueError:
  212. await bet_msg.delete()
  213. return await m.message.reply_text("<b>⚠️ Invalid value. Enter amounts between 0.001 XMR and 0.1 XMR</b>")
  214.  
  215. dice_msg = await m.message.reply_text(
  216. """<b>🎲 Choose a number to bet on the dice (1-6):</b>\n<i>Choose wisely!</i>"""
  217. )
  218.  
  219. # Wait for the user to choose a number between 1 and 6
  220. user_reply = await c.wait_for_message(
  221. chat_id=m.message.chat.id,
  222. filters=filters.text & filters.user(user_id)
  223. )
  224.  
  225. # Validate chosen dice number
  226. try:
  227. dice_number = int(user_reply.text)
  228. if dice_number < 1 or dice_number > 6:
  229. raise ValueError("Choose a number between 1 e 6.")
  230. except ValueError:
  231. await dice_msg.delete()
  232. return await m.message.reply_text("<b>⚠️ Invalid number. Enter a number between 1 and 6.</b>")
  233.  
  234. # Step 3: Ask if the user wants to use the stored address or enter a new one
  235. stored_address = get_user_address(user_id)
  236. if stored_address:
  237. # Address exists, proceed with the bet
  238. await m.message.reply_text(
  239. f"You have a registered address: {stored_address}\n"
  240. "Would you like to use this address or provide a new one?",
  241. reply_markup=InlineKeyboardMarkup([
  242. [
  243. InlineKeyboardButton(
  244. "Use registered address",
  245. callback_data=f"use_address|{bet_amount}|{dice_number}"
  246. )
  247. ],
  248. [
  249. InlineKeyboardButton(
  250. "Enter new address",
  251. callback_data=f"new_address|{bet_amount}|{dice_number}"
  252. )
  253. ]
  254. ])
  255. )
  256. else:
  257. # No address found, prompt user to enter a new address
  258. await enter_new_address(c, m, bet_amount, dice_number)
  259.  
  260.  
  261.  
  262.  
  263.  
  264. # Step 3: Generate Payment Address using MoneroPay API
  265.  
  266. async def generate_payment_address_and_handle_flow(m, c, user_id, bet_amount, dice_number, payout_address):
  267. # Step 3: Generate Payment Address using MoneroPay API
  268. create_address_response = requests.post(
  269. f'{MONEROPAY_ENDPOINT}/receive',
  270. json={
  271. 'amount': int(bet_amount * 1e12), # Convert XMR to piconero
  272. 'description': f'Dice Bet by {user_id}',
  273. 'callback_url': 'https://dontpad.com/monerologs'
  274. }
  275. )
  276.  
  277. print(f"Status Code: {create_address_response.status_code}")
  278. print(f"Response Text: {create_address_response.text}")
  279.  
  280. if create_address_response.status_code != 200:
  281. await m.message.reply_text('<b>⚠️ Error generating the payment address. Please try again.</b>')
  282. return
  283.  
  284. create_address_data = create_address_response.json()
  285. payment_address = create_address_data.get('address')
  286.  
  287. 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}"
  288. await log_to_admin(c, user_id, log_message)
  289.  
  290. formatted_bet = f"{bet_amount:.8f}".rstrip('0').rstrip('.')
  291.  
  292. # Step 4: Inform the user about the generated payment address
  293. await m.message.reply_text(
  294. f"<b>✅ Payment address generated:</b>\n<code>{payment_address}</code>\n\n"
  295. f"<i>Please send <code>{formatted_bet}</code> XMR to the address above. The game will start after 1 confirmation.</i>\n"
  296. f"<i>If the payment is not made within 15 minutes, the bet will be canceled.</i>"
  297. )
  298.  
  299. # Step 5: Handle the payment flow
  300. await handle_user_payment_flow(m, c, user_id, payment_address, payout_address, create_address_data, bet_amount, dice_number)
  301.  
  302.  
  303. # Define the function to handle user payment flow
  304. async def handle_user_payment_flow(m, c, user_id, payment_address, payout_address, create_address_data, bet_amount, dice_number):
  305. # Now, the payout address is passed into the function, so you can use it directly.
  306.  
  307. print(f"User entered Payout Address: {payout_address}")
  308.  
  309. # Set timeout (15 minutes)
  310. timeout = 900 # 900 seconds = 15 minutes
  311. start_time = time.time()
  312.  
  313. # Polling the status with the receive endpoint
  314. while True:
  315. # Check if 15 minutes have passed
  316. if time.time() - start_time > timeout:
  317. await log_to_admin(c, m.message.chat.id, "Timeout reached, no payment received within the allowed time from <code>{user_id}</code>.")
  318. reply_markup = InlineKeyboardMarkup(
  319. [[InlineKeyboardButton("Iniciar novamente", callback_data="start")]]
  320. )
  321.  
  322. await m.message.reply_text(
  323. "The time to confirm the payment has expired. Please start the process again.",
  324. reply_markup=reply_markup
  325. )
  326. return
  327.  
  328. try:
  329. # Log the status check attempt to ensure it's being executed
  330. print(f"Checking payment status for address: {payment_address}...")
  331.  
  332. # Make a request to check the transactions to the specified payout address
  333. payment_status_response = requests.get(
  334. f'{MONEROPAY_ENDPOINT}/receive/{payment_address}' # Use the payout address to check payments
  335. )
  336.  
  337. # Log the raw response for debugging
  338. print(f"Response Status Code: {payment_status_response.status_code}")
  339. print(f"Response Body: {payment_status_response.text}")
  340.  
  341. # Attempt to parse the JSON response
  342. payment_status = payment_status_response.json()
  343.  
  344. # Check if any transactions have been received for the address
  345. transactions = payment_status.get('transactions', [])
  346. if transactions:
  347. tx = transactions[0] # Assuming we're looking at the first transaction
  348. confirmations = tx.get('confirmations', 0) # Get number of confirmations
  349. if confirmations >= 0: # Allow 0 confirmations
  350. # Once the payment is received (0 confirmations or more), notify the user
  351. log_message = f"<code>{user_id}</code> has paid for his bet of <code>{bet_amount}</code> XMR for <code>{payment_address}</code>"
  352. await log_to_admin(c, user_id, log_message)
  353. await m.message.reply_text(f"Payment confirmed with {confirmations} confirmations! TXID: {tx['tx_hash']}. Please wait for the next step.")
  354. sort = await c.send_dice(chat_id=m.message.chat.id, emoji='🎲')
  355. await asyncio.sleep(5)
  356.  
  357. formatted_prize = f"{bet_amount * 5:.8f}" # Format the prize to 8 decimal places for XMR
  358.  
  359. await dice_logic(c, sort, bet_amount, payout_address, m, formatted_prize, dice_number)
  360. return
  361. else:
  362. # Log status and continue checking
  363. print(f"Payment not yet confirmed, waiting for more confirmations...")
  364. await asyncio.sleep(10)
  365. else:
  366. # No transaction yet, waiting for payment
  367. print("Payment not yet received. Waiting for transaction...")
  368. await asyncio.sleep(5) # Wait for 5 seconds and check again
  369. except json.JSONDecodeError as e:
  370. # Handle JSON errors gracefully
  371. print(f"JSONDecodeError: {e}")
  372. print(f"Raw response: {payment_status_response.text}")
  373. await m.message.reply_text("An error occurred while checking the payment status. Please try again later.")
  374. return
  375. except Exception as e:
  376. # General exception handling
  377. print(f"Error: {e}")
  378. await m.message.reply_text("An unexpected error occurred. Please try again later.")
  379. return
  380.  
  381.  
  382.  
  383. # Step 4: Roll the Dice
  384. sort = await c.send_dice(chat_id=m.message.chat.id, emoji='🎲')
  385. await asyncio.sleep(10)
  386.  
  387. formatted_prize = f"{bet_amount * 5:.8f}" # Format the prize to 8 decimal places for XMR
  388.  
  389. await dice_logic(c, sort, bet_amount, payout_address, m, formatted_prize, dice_number)
  390.  
  391.  
  392. async def send_prize_to_address(prize, payout_address):
  393. headers = {'Content-Type': 'application/json'}
  394.  
  395. amount_piconero = int(prize * 1e12)
  396.  
  397. payload = {
  398. "destinations": [
  399. {
  400. "amount": amount_piconero,
  401. "address": payout_address
  402. }
  403. ]
  404. }
  405.  
  406.  
  407. # Format the prize for display
  408. formatted_prize = f"{prize:.8f}" # Format the prize to 8 decimal places for XMR
  409.  
  410. print(f"Sending {formatted_prize} XMR to address: {payout_address}...") # Print for debugging
  411.  
  412. # Send the POST request to transfer the prize
  413. response = requests.post(f"{MONEROPAY_ENDPOINT}/transfer", json=payload, headers=headers)
  414.  
  415. if response.status_code == 200:
  416. data = response.json()
  417. tx_hash = data.get("tx_hash_list", [None])[0]
  418. if tx_hash:
  419. print(f"Transfer initiated successfully. Transaction hash: {tx_hash}")
  420. return True, tx_hash # Return success and transaction hash
  421. else:
  422. print("Transaction hash not found in the response.")
  423. return False, "Transaction hash not found"
  424. else:
  425. print(f"Error: {response.text}")
  426. return False, f"Error: {response.text}"
  427.  
  428. async def check_transfer_status(tx_hash):
  429. response = requests.get(f"{MONEROPAY_ENDPOINT}/transfer/{tx_hash}")
  430.  
  431. if response.status_code == 200:
  432. data = response.json()
  433. state = data.get("state")
  434. confirmations = data.get("confirmations", 0)
  435.  
  436. if state == "completed": # Treat completed state as confirmed
  437. return True, "Transaction completed successfully"
  438. else:
  439. return False, f"Transaction state: {state}"
  440. else:
  441. return False, f"Error checking status: {response.text}"
  442.  
  443.  
  444.  
  445. async def dice_logic(c, sort, bet_amount, payout_address, m, formatted_prize, dice_number):
  446. # Step 5: Check the Dice Result
  447. if sort.dice.value == dice_number:
  448. # Calculate the prize
  449. prize = bet_amount * 5
  450. msg = f"🎉 You rolled {dice_number} and won {formatted_prize} XMR!"
  451.  
  452. # Log the win
  453. 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"
  454. await log_to_admin(c, user_id, win_log)
  455.  
  456. # Send the prize to the payout address
  457. success, tx_hash = await send_prize_to_address(prize, payout_address)
  458.  
  459. if success:
  460. msg += f"\n💸 Sent! Transaction Hash: {tx_hash}"
  461. # Log the successful transaction
  462. tx_log = f"Transaction successful for User {m.message.chat.id}: TX Hash: {tx_hash}, Prize: {prize} XMR"
  463. await log_to_admin(c, m.message.chat.id, tx_log)
  464. else:
  465. msg += "\n⚠️ Error transferring the prize. Please try again later."
  466. # Log the failed transaction
  467. fail_log = f"Transaction failed for User {m.message.chat.id}: Failed to send prize {prize} XMR"
  468. await log_to_admin(c, m.message.chat.id, fail_log)
  469. else:
  470. prize = 0
  471. msg = f"😞 You lost the bet! The dice rolled {sort.dice.value}."
  472.  
  473. # Log the loss
  474. loss_log = f"User {m.message.chat.id} lost the bet: Rolled {sort.dice.value}, Bet: {bet_amount}"
  475. await log_to_admin(c, m.message.chat.id, loss_log)
  476.  
  477. # Define the inline keyboard with the button
  478. reply_markup = InlineKeyboardMarkup(
  479. [[InlineKeyboardButton("Play again", callback_data="dice")]] # "dice" is the callback to trigger another bet
  480. )
  481.  
  482. # Send the result message with the reply_markup (the button)
  483. await m.message.reply_text(msg, reply_markup=reply_markup)
  484.  
  485.  
  486.  
  487.  
  488. @Client.on_callback_query(filters.regex("^info$"))
  489. async def info(c: Client, m: CallbackQuery):
  490. # Acknowledge the callback query so the button disappears or the loading stops
  491. await m.answer()
  492.  
  493. await m.message.edit_text(
  494. f"<b>Information about Monero Casino</b>\n\n"
  495. 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"
  496. f"<i>Why is it fair?</i>\n"
  497. 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"
  498. f"<b>Good luck!</b> 🍀\n"
  499. f"<i>Support: @ninefag</i>\n",
  500. reply_markup=InlineKeyboardMarkup([
  501. [InlineKeyboardButton("🎲 Play Monero Dice", callback_data="dice")]
  502. ])
  503. )
  504.  
  505.  
  506.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement