diff --git a/tipbot.py b/tipbot.py index 656dcb2..e64c805 100644 --- a/tipbot.py +++ b/tipbot.py @@ -1,7 +1,6 @@ """ - Developed by @vsnation(t.me/vsnation) - Email: vsnation.v@gmail.com - If you'll need the support use the contacts ^(above)! +Created by @vsnation(t.me/vsnation) vsnation.v@gmail.com +Updated for use with Firo by Likho Jiba (likho) and Joshua Babb (sneurlax@gmail.com). """ import json import logging @@ -15,18 +14,23 @@ import matplotlib.pyplot as plt import datetime import time -from pymongo import MongoClient +from pymongo import MongoClient, ReturnDocument from telegram import Bot, InlineKeyboardMarkup, InlineKeyboardButton import uuid +from decimal import Decimal, getcontext from api.firo_wallet_api import FiroWalletAPI plt.style.use('seaborn-whitegrid') -logger = logging.getLogger() -logger.setLevel(logging.ERROR) +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') +handler = logging.StreamHandler() +handler.setFormatter(formatter) +logger.addHandler(handler) -AV_FEE = 0.002 +AV_FEE = Decimal('0.002') +getcontext().prec = 8 # Set precision for Decimal. with open('services.json') as conf_file: conf = json.load(conf_file) @@ -36,7 +40,7 @@ dictionary = conf['dictionary'] LOG_CHANNEL = conf['log_ch'] -SATS_IN_BTC = 1e8 +SATS_IN_BTC = Decimal('1e8') wallet_api = FiroWalletAPI(httpprovider) @@ -46,16 +50,18 @@ bold_high = ImageFont.truetype(font="fonts/ProximaNova-Bold.ttf", size=int(26 * point_to_pixels)) WELCOME_MESSAGE = """ -Welcome to the Firo telegram tip bot! +Welcome to the Firo telegram tip bot! """ +def to_decimal(value): + return Decimal(str(value)) class TipBot: def __init__(self, wallet_api): - # INIT + # INIT. self.bot = Bot(bot_token) self.wallet_api = wallet_api - # firo Butler Initialization + # Firo Butler Initialization. client = MongoClient(connectionString) db = client.get_default_database() self.col_captcha = db['captcha'] @@ -84,11 +90,11 @@ def __init__(self, wallet_api): while True: try: self._is_user_in_db = None - # get chat updates + # Get chat updates. new_messages = self.wait_new_message() self.processing_messages(new_messages) except Exception as exc: - print(exc) + logger.error(exc, exc_info=True) def pending_tasks(self): while True: @@ -104,26 +110,22 @@ def processing_messages(self, new_messages): else self.new_message.callback_query.message self.text, self._is_video = self.get_action(self.new_message) self.message_text = str(self.text).lower() - # init user data + # Init user data. self.first_name = self.new_message.effective_user.first_name self.username = self.new_message.effective_user.username self.user_id = int(self.new_message.effective_user.id) self.firo_address, self.balance_in_firo, self.locked_in_firo, self.is_withdraw = self.get_user_data() - self.balance_in_groth = self.balance_in_firo * SATS_IN_BTC if self.balance_in_firo is not None else 0 + self.balance_in_groth = self.balance_in_firo * SATS_IN_BTC if self.balance_in_firo is not None else Decimal('0') try: self._is_verified = self.col_users.find_one({"_id": self.user_id})['IsVerified'] self._is_user_in_db = self._is_verified except Exception as exc: - print(exc) + logger.error(exc, exc_info=True) self._is_verified = True self._is_user_in_db = False - # - print(self.username) - print(self.user_id) - print(self.first_name) - print(self.message_text, '\n') + self.group_id = self.message.chat.id self.group_username = self.get_group_username() @@ -133,13 +135,11 @@ def processing_messages(self, new_messages): else: args = None - # Check if user changed his username + # Check if user changed his username. self.check_username_on_change() self.action_processing(str(split[0]).lower(), args) - # self.check_group_msg() except Exception as exc: - print(exc) - traceback.print_exc() + logger.error(exc, exc_info=True) def send_to_logs(self, text): try: @@ -149,11 +149,11 @@ def send_to_logs(self, text): parse_mode='HTML' ) except Exception as exc: - print(exc) + logger.error(exc, exc_info=True) def get_group_username(self): """ - Get group username + Get group username """ try: return str(self.message.chat.username) @@ -162,7 +162,7 @@ def get_group_username(self): def get_user_username(self): """ - Get User username + Get User username """ try: return str(self.message.from_user.username) @@ -196,7 +196,7 @@ def get_action(message): def action_processing(self, cmd, args): """ - Check each user actions + Check each user actions """ # ***** Tip bot section begin ***** if cmd.startswith("/tip") or cmd.startswith("/atip"): @@ -228,7 +228,7 @@ def action_processing(self, cmd, args): parse_mode='HTML' ) except Exception as exc: - print(exc) + logger.error(exc, exc_info=True) self.incorrect_parametrs_image() self.send_message( self.user_id, @@ -263,7 +263,7 @@ def action_processing(self, cmd, args): else: self.incorrect_parametrs_image() except Exception as exc: - print(exc) + logger.error(exc, exc_info=True) self.incorrect_parametrs_image() @@ -278,11 +278,10 @@ def action_processing(self, cmd, args): envelope_id = cmd.split("|")[1] self.catch_envelope(envelope_id) except Exception as exc: - print(exc) + logger.error(exc, exc_info=True) self.incorrect_parametrs_image() - elif cmd.startswith("/balance"): if not self._is_user_in_db: self.send_message(self.group_id, @@ -307,7 +306,7 @@ def action_processing(self, cmd, args): else: self.incorrect_parametrs_image() except Exception as exc: - print(exc) + logger.error(exc, exc_info=True) traceback.print_exc() elif cmd.startswith("/deposit"): @@ -338,7 +337,7 @@ def action_processing(self, cmd, args): def check_username_on_change(self): """ - Check username on change in the bot + Check username on change in the bot """ _is_username_in_db = self.col_users.find_one( {"username": self.username}) is not None \ @@ -376,163 +375,138 @@ def get_wallet_balance(self): try: r = self.wallet_api.listsparkmints() result = sum([_x['amount'] for _x in r['result'] if not _x['isUsed']]) - print("Current Balance", result / 1e8) + logger.info(f"Current Balance: {result / SATS_IN_BTC} FIRO") except Exception as exc: - print(exc) + logger.error(exc, exc_info=True) def update_balance(self): """ - Update user's balance using transactions history + Update user's balance using transactions history """ - print("Handle TXs") - # First get unused mints for the wallet, check if mint is confirmed in the tx list - unused_mints = [] - mints = wallet_api.listsparkmints() - - for mnt in mints['result']: - if not mnt['isUsed']: - unused_mints.append(mnt) + logger.info("Handle TXs") + unused_mints = [mnt for mnt in wallet_api.listsparkmints()['result'] if not mnt['isUsed']] response = self.wallet_api.get_txs_list() for _tx in response['result']: - for unused_mnt in unused_mints: - try: - if unused_mnt['txid'] == _tx['txid']: - """ - Check withdraw txs - """ - sparkcoin_addr = wallet_api.get_spark_coin_address(unused_mnt['txid']) - _user_receiver = self.col_users.find_one( - {"Address": sparkcoin_addr[0]['address']} - ) - - _is_tx_exist_deposit = self.col_txs.find_one( - {"txId": _tx['txid'], "type": "deposit"} - ) is not None - if _user_receiver is not None and \ - not _is_tx_exist_deposit and \ - _tx['confirmations'] >= 2 and _tx['category'] == 'receive': - - value_in_coins = float(_tx['amount']) - new_balance = _user_receiver['Balance'] + value_in_coins - _id = str(uuid.uuid4()) - self.col_txs.insert_one({ - '_id': _id, - 'txId': _tx['txid'], - **_tx, - 'type': "deposit", - 'timestamp': datetime.datetime.now() - }) - self.col_users.update_one( - _user_receiver, - { - "$set": - { - "Balance": float("{0:.8f}".format(float(new_balance))) - } - } - ) - self.create_receive_tips_image( - _user_receiver['_id'], - "{0:.8f}".format(value_in_coins), - "Deposit") - - print("*Deposit Success*\n" - "Balance of address %s has recharged on *%s* firos." % ( - sparkcoin_addr[0]['address'], value_in_coins - )) - continue - - _is_tx_exist_withdraw = self.col_txs.find_one( - {"txId": _tx['txid'], "type": "withdraw"} - ) is not None - - pending_sender = self.col_senders.find_one( - {"txId": _tx['txid'], "status": "pending"} - ) - if not pending_sender: - continue - _user_sender = self.col_users.find_one({"_id": pending_sender['user_id']}) - if _user_sender is not None and not _is_tx_exist_withdraw and _tx['category'] == "spend": - - value_in_coins = float((abs(_tx['amount']))) - - if _tx['confirmations'] >= 2: - _id = str(uuid.uuid4()) - self.col_txs.insert_one({ - '_id': _id, - "txId": _tx['txid'], - **_tx, - 'type': "withdraw", - 'timestamp': datetime.datetime.now() - }) - new_locked = float(_user_sender['Locked']) - value_in_coins - if new_locked >= 0: - self.col_users.update_one( - { - "_id": _user_sender['_id'] - }, - { - "$set": - { - "Locked": float("{0:.8f}".format(new_locked)), - "IsWithdraw": False - } - } - ) - else: - new_balance = float(_user_sender['Balance']) - value_in_coins - self.col_users.update_one( - { - "_id": _user_sender['_id'] - }, - { - "$set": - { - "Balance": float("{0:.8f}".format(new_balance)), - "IsWithdraw": False - } - } - ) - - self.create_send_tips_image(_user_sender['_id'], - "{0:.8f}".format(float(abs(_tx['amount']))), - "%s..." % _user_sender['Address'][0][:8]) - - self.col_senders.update_one( - {"txId": _tx['txid'], "status": "pending", "user_id": _user_sender['_id']}, - {"$set": {"status": "completed"}} - ) - print("*Withdrawal Success*\n" - "Balance of address %s has recharged on *%s* firos." % ( - _user_sender['Address'], value_in_coins - )) - continue - - except Exception as exc: - print(exc) - traceback.print_exc() + if self.col_txs.find_one({"txId": _tx['txid']}) is None: # Avoid processing duplicate transactions. + for unused_mnt in unused_mints: + try: + if unused_mnt['txid'] == _tx['txid']: + with client.start_session() as session: + with session.start_transaction(): + try: + sparkcoin_addr = wallet_api.get_spark_coin_address(unused_mnt['txid']) + _user_receiver = self.col_users.find_one( + {"Address": sparkcoin_addr[0]['address']}, + session=session + ) + + if _user_receiver and \ + not self.col_txs.find_one({"txId": _tx['txid'], "type": "deposit"}, session=session) and \ + _tx['confirmations'] >= 2 and _tx['category'] == 'receive': + + value_in_coins = to_decimal(_tx['amount']) + new_balance = to_decimal(_user_receiver['Balance']) + value_in_coins + _id = str(uuid.uuid4()) + self.col_txs.insert_one({ + '_id': _id, + 'txId': _tx['txid'], + **_tx, + 'type': "deposit", + 'timestamp': datetime.datetime.now() + }, session=session) + self.col_users.update_one( + {"_id": _user_receiver['_id']}, + {"$set": {"Balance": new_balance}}, + session=session + ) + self.create_receive_tips_image( + _user_receiver['_id'], + "{0:.8f}".format(value_in_coins), + "Deposit" + ) + logger.info(f"*Deposit Success*\nBalance of address {sparkcoin_addr[0]['address']} " + f"has recharged on *{value_in_coins}* FIRO.") + continue + + pending_sender = self.col_senders.find_one( + {"txId": _tx['txid'], "status": "pending"}, + session=session + ) + if pending_sender and not self.col_txs.find_one({"txId": _tx['txid'], "type": "withdraw"}, session=session) and _tx['category'] == "spend": + _user_sender = self.col_users.find_one({"_id": pending_sender['user_id']}, session=session) + if _user_sender: + + value_in_coins = to_decimal(abs(_tx['amount'])) + if _tx['confirmations'] >= 2: + _id = str(uuid.uuid4()) + self.col_txs.insert_one({ + '_id': _id, + "txId": _tx['txid'], + **_tx, + 'type': "withdraw", + 'timestamp': datetime.datetime.now() + }, session=session) + + new_locked = to_decimal(_user_sender['Locked']) - value_in_coins + if new_locked >= 0: + self.col_users.update_one( + {"_id": _user_sender['_id']}, + {"$set": { + "Locked": new_locked, + "IsWithdraw": False + }}, + session=session + ) + else: + new_balance = to_decimal(_user_sender['Balance']) - value_in_coins + self.col_users.update_one( + {"_id": _user_sender['_id']}, + {"$set": { + "Balance": new_balance, + "IsWithdraw": False + }}, + session=session + ) + + self.create_send_tips_image(_user_sender['_id'], + "{0:.8f}".format(value_in_coins), + f"{_user_sender['Address'][0][:8]}...") + + self.col_senders.update_one( + {"txId": _tx['txid'], "status": "pending", "user_id": _user_sender['_id']}, + {"$set": {"status": "completed"}}, + session=session + ) + logger.info(f"*Withdrawal Success*\nBalance of address {sparkcoin_addr[0]['address']} " + f"has been deducted by *{value_in_coins}* FIRO.") + continue + except Exception as e: + logger.error(f"Error updating balance: {str(e)}", exc_info=True) + session.abort_transaction() + + except Exception as exc: + logger.error(exc, exc_info=True) def get_user_data(self): """ - Get user data + Get user data """ try: _user = self.col_users.find_one({"_id": self.user_id}) self.update_address_and_balance(_user) return _user['Address'], _user['Balance'], _user['Locked'], _user['IsWithdraw'] except Exception as exc: - print(exc) - traceback.print_exc() + logger.error(exc, exc_info=True) return None, None, None, None def update_address_and_balance(self, _user): mints = wallet_api.listsparkmints() if len(mints) > 0: - # Check if User has a Lelantus address + # Check if User has a Lelantus address. valid = wallet_api.validate_address(_user['Address'][0])['result'] is_valid_firo = 'isvalid' - # User still has Lelantus address, Update address and balance + # User still has Lelantus address, Update address and balance. if is_valid_firo in valid: spark_address = wallet_api.create_user_wallet() self.col_users.update_one( @@ -547,99 +521,66 @@ def update_address_and_balance(self, _user): def withdraw_coins(self, address, amount, comment=""): """ - Withdraw coins to address with params: - address - amount + Withdraw coins to address with params: + address + amount """ try: - - try: - amount = float(amount) - except Exception as exc: - self.send_message(self.user_id, - dictionary['incorrect_amount'], - parse_mode='HTML') - print(exc) - traceback.print_exc() - return - - _is_address_valid = False + amount = to_decimal(amount) + total_amount = amount + AV_FEE validate = self.wallet_api.validate_address(address)['result'] is_valid_spark = 'isvalidSpark' is_valid_firo = 'isvalid' - if is_valid_spark in validate or is_valid_firo in validate: - _is_address_valid = True - - if not _is_address_valid: - self.send_message( - self.user_id, - "You specified incorrect address", - parse_mode='HTML' - ) + if is_valid_spark not in validate and is_valid_firo not in validate: + self.send_message(self.user_id, "You specified an incorrect address", parse_mode='HTML') return - if float(self.balance_in_firo) >= float("{0:.8f}".format(amount)) and float(self.balance_in_firo) >= AV_FEE: - - _user = self.col_users.find_one({"_id": self.user_id}) - - new_balance = float("{0:.8f}".format(float(self.balance_in_firo - amount))) - new_locked = float("{0:.8f}".format(float(self.locked_in_firo + amount - AV_FEE))) - response = self.wallet_api.spendspark( - address, - float(amount), - comment - ) - print(response, "withdraw") - if response.get('error'): - self.send_message( - self.user_id, "Not enough inputs. Try to repeat a bit later!" + # Atomic operation to lock the user's balance and update it. + with client.start_session() as session: + with session.start_transaction(): + user = self.col_users.find_one_and_update( + {"_id": self.user_id, "Balance": {"$gte": total_amount}}, + {"$inc": {"Balance": -total_amount, "Locked": total_amount}}, + return_document=ReturnDocument.AFTER, + session=session ) - self.send_to_logs(f"Unavailable Withdraw\n{str(response)}") - return - - self.col_senders.insert_one( - {"txId": response['result'], "status": "pending", "user_id": self.user_id} - ) - self.col_users.update_one( - { - "_id": self.user_id - }, - { - "$set": - { - "Balance": new_balance, - "Locked": new_locked, - } - } - ) - self.withdraw_image(self.user_id, - "{0:.8f}".format(float(amount)), - address, - msg=f"Your txId {response['result']}") - - else: - self.insufficient_balance_image() + if not user: + self.insufficient_balance_image() + return + + # Proceed with the withdrawal. + response = self.wallet_api.spendspark(address, amount, comment) + if response.get('error'): + # Rollback if spend failed. + self.col_users.update_one( + {"_id": self.user_id}, + {"$inc": {"Balance": amount, "Locked": -total_amount}}, + session=session + ) + self.send_message(self.user_id, "Not enough inputs. Try again later!") + self.send_to_logs(f"Unavailable Withdraw\n{str(response)}") + return + + # Insert the transaction into the senders collection. + self.col_senders.insert_one( + {"txId": response['result'], "status": "pending", "user_id": self.user_id}, + session=session + ) + self.withdraw_image(self.user_id, "{0:.8f}".format(amount), address, msg=f"Your txId {response['result']}") except Exception as exc: - print(exc) - traceback.print_exc() + logger.error(exc, exc_info=True) def tip_user(self, username, amount, comment, _type=None): """ - Tip user with params: - username - amount + Tip user with params: + username + amount """ try: - try: - amount = float(amount) - if amount < 0.00000001: - raise Exception - except Exception as exc: - self.incorrect_parametrs_image() - print(exc) - traceback.print_exc() - return + amount = to_decimal(amount) + if amount < Decimal('0.00000001'): + raise ValueError("Amount too small") username = username.replace('@', '') @@ -655,23 +596,17 @@ def tip_user(self, username, amount, comment, _type=None): self.send_tip(_user['_id'], amount, _type, comment) except Exception as exc: - print(exc) - traceback.print_exc() + logger.error(exc, exc_info=True) + self.incorrect_parametrs_image() def tip_in_the_chat(self, amount, comment="", _type=None): """ - Send a tip to user in the chat + Send a tip to user in the chat """ try: - try: - amount = float(amount) - if amount < 0.00000001: - raise Exception - except Exception as exc: - self.incorrect_parametrs_image() - print(exc) - traceback.print_exc() - return + amount = to_decimal(amount) + if amount < Decimal('0.00000001'): + raise ValueError("Amount too small") self.send_tip( self.message.reply_to_message.from_user.id, @@ -681,111 +616,60 @@ def tip_in_the_chat(self, amount, comment="", _type=None): ) except Exception as exc: - print(exc) - traceback.print_exc() + logger.error(exc, exc_info=True) + self.incorrect_parametrs_image() def send_tip(self, user_id, amount, _type, comment): """ - Send tip to user with params - user_id - user identificator - addrees - user address - amount - amount of a tip + Send tip to user with params + user_id - user identifier + amount - amount of a tip """ try: if self.user_id == user_id: - self.send_message( - self.user_id, - "You can't send tips to yourself!", - parse_mode='HTML' - ) + self.send_message(self.user_id, "You can't send tips to yourself!", parse_mode='HTML') return - _user_receiver = self.col_users.find_one({"_id": user_id}) - - if _user_receiver is None or _user_receiver['IsVerified'] is False: - self.send_message(self.user_id, - dictionary['username_error'], - parse_mode='HTML') - return - - if _type == 'anonymous': - sender_name = str(_type).title() - # sender_user_id = 0000000 - else: - sender_name = self.first_name - # sender_user_id = self.user_id - - if self.balance_in_firo >= amount > 0: - try: - - self.create_send_tips_image( - self.user_id, - "{0:.8f}".format(float(amount)), - _user_receiver['first_name'], - comment + with client.start_session() as session: + with session.start_transaction(): + _user_receiver = self.col_users.find_one({"_id": user_id}, session=session) + if not _user_receiver or _user_receiver['IsVerified'] is False: + self.send_message(self.user_id, dictionary['username_error'], parse_mode='HTML') + return + + user_sender = self.col_users.find_one_and_update( + {"_id": self.user_id, "Balance": {"$gte": amount}}, + {"$inc": {"Balance": -amount}}, + return_document=ReturnDocument.AFTER, + session=session ) - self.create_receive_tips_image( - _user_receiver['_id'], - "{0:.8f}".format(float(amount)), - sender_name, - comment - ) + if not user_sender: + self.insufficient_balance_image() + return + # Update receiver's balance. self.col_users.update_one( - { - "_id": self.user_id - }, - { - "$set": - { - "Balance": float( - "{0:.8f}".format(float(float(self.balance_in_firo) - float(amount)))) - } - } - ) - self.col_users.update_one( - { - "_id": _user_receiver['_id'] - }, - { - "$set": - { - "Balance": float( - "{0:.8f}".format(float(float(_user_receiver['Balance']) + float(amount)))) - } - } + {"_id": user_id}, + {"$inc": {"Balance": amount}}, + session=session ) - if _type == 'anonymous': - self.col_tip_logs.insert( - { - "type": "atip", - "from_user_id": self.user_id, - "to_user_id": _user_receiver['_id'], - "amount": amount - } - ) - - else: - self.col_tip_logs.insert( - { - "type": "tip", - "from_user_id": self.user_id, - "to_user_id": _user_receiver['_id'], - "amount": amount - } - ) + # Log the tip. + tip_log = { + "type": "atip" if _type == "anonymous" else "tip", + "from_user_id": self.user_id, + "to_user_id": user_id, + "amount": amount + } + self.col_tip_logs.insert_one(tip_log, session=session) - except Exception as exc: - print(exc) - traceback.print_exc() + # Create images. + self.create_send_tips_image(self.user_id, "{0:.8f}".format(amount), _user_receiver['first_name'], comment) + self.create_receive_tips_image(user_id, "{0:.8f}".format(amount), str(_type).title() if _type else self.first_name, comment) - else: - self.insufficient_balance_image() except Exception as exc: - print(exc) - traceback.print_exc() + logger.error(exc, exc_info=True) def create_receive_tips_image(self, user_id, amount, first_name, comment=""): try: @@ -822,15 +706,7 @@ def create_receive_tips_image(self, user_id, amount, first_name, comment=""): except Exception as exc: - try: - print(exc) - if 'blocked' in str(exc): - self.send_message(self.group_id, - "User needs to unblock the bot in order to check their balance!" % user_id, - parse_mode='HTML') - traceback.print_exc() - except Exception as exc: - print(exc) + logger.error(exc, exc_info=True) def create_send_tips_image(self, user_id, amount, first_name, comment=""): try: @@ -858,16 +734,7 @@ def create_send_tips_image(self, user_id, amount, first_name, comment=""): ) except Exception as exc: - try: - print(exc) - if 'blocked' in str(exc): - self.send_message(self.group_id, - "User needs to unblock the bot in order to check their balance!" % user_id, - parse_mode='HTML') - traceback.print_exc() - except Exception as exc: - print(exc) - traceback.print_exc() + logger.error(exc, exc_info=True) def withdraw_image(self, user_id, amount, address, msg=None): try: @@ -891,8 +758,7 @@ def withdraw_image(self, user_id, amount, address, msg=None): caption=f'{msg}' ) except Exception as exc: - print(exc) - traceback.print_exc() + logger.error(exc, exc_info=True) def create_wallet_image(self, public_address): try: @@ -913,8 +779,7 @@ def create_wallet_image(self, public_address): timeout=200 ) except Exception as exc: - print(exc) - traceback.print_exc() + logger.error(exc, exc_info=True) def withdraw_failed_image(self, user_id): try: @@ -934,8 +799,7 @@ def withdraw_failed_image(self, user_id): parse_mode='HTML' ) except Exception as exc: - print(exc) - traceback.print_exc() + logger.error(exc, exc_info=True) def insufficient_balance_image(self): try: @@ -958,10 +822,9 @@ def insufficient_balance_image(self): parse_mode='HTML' ) except Exception as exc: - print(exc) + logger.error(exc, exc_info=True) except Exception as exc: - print(exc) - traceback.print_exc() + logger.error(exc, exc_info=True) def red_envelope_catched(self, amount): try: @@ -983,10 +846,9 @@ def red_envelope_catched(self, amount): open(image_name, 'rb') ) except Exception as exc: - print(exc) + logger.error(exc, exc_info=True) except Exception as exc: - print(exc) - traceback.print_exc() + logger.error(exc, exc_info=True) def red_envelope_created(self, first_name, envelope_id): im = Image.open("images/red_envelope_created.png") @@ -1013,7 +875,7 @@ def red_envelope_created(self, first_name, envelope_id): ) return response['message_id'] except Exception as exc: - print(exc) + logger.error(exc, exc_info=True) return 0 def red_envelope_ended(self): @@ -1033,7 +895,7 @@ def red_envelope_ended(self): open(image_name, 'rb'), ) except Exception as exc: - print(exc) + logger.error(exc, exc_info=True) def incorrect_parametrs_image(self): try: @@ -1055,14 +917,13 @@ def incorrect_parametrs_image(self): parse_mode='HTML' ) except Exception as exc: - print(exc) - traceback.print_exc() + logger.error(exc, exc_info=True) def create_red_envelope(self, amount): try: - amount = float(amount) + amount = to_decimal(amount) - if amount < 0.001: + if amount < Decimal('0.001'): self.incorrect_parametrs_image() return @@ -1076,7 +937,7 @@ def create_red_envelope(self, amount): { "$set": { - "Balance": float("{0:.8f}".format(float(self.balance_in_firo) - amount)) + "Balance": to_decimal(self.balance_in_firo) - amount } } ) @@ -1101,103 +962,104 @@ def create_red_envelope(self, amount): self.insufficient_balance_image() except Exception as exc: + logger.error(exc, exc_info=True) self.incorrect_parametrs_image() - print(exc) def catch_envelope(self, envelope_id): try: - envelope = self.col_envelopes.find_one({"_id": envelope_id}) - _is_envelope_exist = envelope is not None - _is_ended = envelope['remains'] == 0 - _is_user_catched = str(self.user_id) in str(envelope['takers']) - - if _is_user_catched: - self.answer_call_back(text="❗️You have already caught Firo from this envelope❗️", - query_id=self.new_message.callback_query.id) - return - - if _is_ended: - self.answer_call_back(text="❗RED ENVELOPE ENDED❗️", - query_id=self.new_message.callback_query.id) - self.red_envelope_ended() - self.delete_tg_message(self.group_id, self.message.message_id) - return - - if _is_envelope_exist: - minimal_amount = 0.001 - if envelope['remains'] <= minimal_amount: - catch_amount = envelope['remains'] - else: - if len(envelope['takers']) < 5: - catch_amount = float( - "{0:.8f}".format(float(random.uniform(minimal_amount, envelope['remains'] / 2)))) - else: - catch_amount = float( - "{0:.8f}".format(float(random.uniform(minimal_amount, envelope['remains'])))) - - new_remains = float("{0:.8f}".format(envelope['remains'] - catch_amount)) - if new_remains < 0: - new_remains = 0 - catch_amount = envelope['remains'] - - self.col_envelopes.update_one( - { - "_id": envelope_id, - }, - { - "$push": { - "takers": [self.user_id, catch_amount] - }, - "$set": { - "remains": new_remains - } - } - ) - self.col_users.update_one( - { - "_id": self.user_id - }, - { - "$set": + with client.start_session() as session: + with session.start_transaction(): + envelope = self.col_envelopes.find_one({"_id": envelope_id}, session=session) + _is_envelope_exist = envelope is not None + _is_ended = envelope['remains'] == 0 + _is_user_catched = str(self.user_id) in str(envelope['takers']) + + if _is_user_catched: + self.answer_call_back(text="❗️You have already caught Firo from this envelope❗️", + query_id=self.new_message.callback_query.id) + return + + if _is_ended: + self.answer_call_back(text="❗RED ENVELOPE ENDED❗️", + query_id=self.new_message.callback_query.id) + self.red_envelope_ended() + self.delete_tg_message(self.group_id, self.message.message_id) + return + + if _is_envelope_exist: + minimal_amount = Decimal('0.001') + if envelope['remains'] <= minimal_amount: + catch_amount = envelope['remains'] + else: + if len(envelope['takers']) < 5: + catch_amount = to_decimal(random.uniform(minimal_amount, envelope['remains'] / 2)) + else: + catch_amount = to_decimal(random.uniform(minimal_amount, envelope['remains'])) + + new_remains = envelope['remains'] - catch_amount + if new_remains < 0: + new_remains = Decimal('0') + catch_amount = envelope['remains'] + + self.col_envelopes.update_one( { - "Balance": float("{0:.8f}".format(float(self.balance_in_firo) + catch_amount)) - } - } - ) - try: - if envelope['group_username'] != "None": - msg_text = '%s caught %s Firo from a RED ENVELOPE' % ( - self.user_id, - self.first_name, - "{0:.8f}".format(catch_amount), - envelope['group_username'], - envelope['msg_id'] + "_id": envelope_id, + }, + { + "$push": { + "takers": [self.user_id, catch_amount] + }, + "$set": { + "remains": new_remains + } + }, + session=session ) - else: - msg_text = '%s caught %s Firo from a RED ENVELOPE' % ( - self.user_id, - self.first_name, - "{0:.8f}".format(catch_amount), + self.col_users.update_one( + { + "_id": self.user_id + }, + { + "$inc": { + "Balance": catch_amount + } + }, + session=session ) - self.send_message( - envelope['group_id'], - text=msg_text, - disable_web_page_preview=True, - parse_mode='HTML' - ) - except Exception: - traceback.print_exc() + try: + if envelope['group_username'] != "None": + msg_text = '%s caught %s Firo from a RED ENVELOPE' % ( + self.user_id, + self.first_name, + "{0:.8f}".format(catch_amount), + envelope['group_username'], + envelope['msg_id'] + ) + else: + msg_text = '%s caught %s Firo from a RED ENVELOPE' % ( + self.user_id, + self.first_name, + "{0:.8f}".format(catch_amount), + ) + self.send_message( + envelope['group_id'], + text=msg_text, + disable_web_page_preview=True, + parse_mode='HTML' + ) + except Exception: + logger.error(exc, exc_info=True) - self.answer_call_back(text="✅YOU CAUGHT %s Firo from ENVELOPE✅️" % catch_amount, - query_id=self.new_message.callback_query.id) - self.red_envelope_catched("{0:.8f}".format(catch_amount)) + self.answer_call_back(text="✅YOU CAUGHT %s Firo from ENVELOPE✅️" % catch_amount, + query_id=self.new_message.callback_query.id) + self.red_envelope_catched("{0:.8f}".format(catch_amount)) - else: - self.insufficient_balance_image() + else: + self.insufficient_balance_image() except Exception as exc: + logger.error(exc, exc_info=True) self.incorrect_parametrs_image() - print(exc) def delete_tg_message(self, user_id, message_id): try: @@ -1213,7 +1075,7 @@ def answer_call_back(self, text, query_id): show_alert=True ) except Exception as exc: - print(exc) + logger.error(exc, exc_info=True) def auth_user(self): try: @@ -1235,8 +1097,8 @@ def auth_user(self): { "IsVerified": True, "Address": public_address, - "Balance": 0, - "Locked": 0, + "Balance": Decimal('0'), + "Locked": Decimal('0'), "IsWithdraw": False } }, upsert=True @@ -1258,8 +1120,8 @@ def auth_user(self): "IsVerified": True, "JoinDate": datetime.datetime.now(), "Address": public_address, - "Balance": 0, - "Locked": 0, + "Balance": Decimal('0'), + "Locked": Decimal('0'), "IsWithdraw": False, } }, upsert=True @@ -1290,8 +1152,7 @@ def auth_user(self): parse_mode='html', ) except Exception as exc: - print(exc) - traceback.print_exc() + logger.error(exc, exc_info=True) def create_qr_code(self): try: @@ -1305,7 +1166,7 @@ def create_qr_code(self): parse_mode='HTML' ) except Exception as exc: - print(exc) + logger.error(exc, exc_info=True) def cleanhtml(self, string_html): cleanr = re.compile('<.*?>') @@ -1323,7 +1184,7 @@ def send_message(self, user_id, text, parse_mode=None, disable_web_page_preview= ) return response except Exception as exc: - print(exc) + logger.error(exc, exc_info=True) def main(): @@ -1331,8 +1192,7 @@ def main(): TipBot(wallet_api) except Exception as e: - print(e) - traceback.print_exc() + logger.error(e, exc_info=True) if __name__ == '__main__':