You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

269 lines
9.7 KiB
Python

import wownero
import config
import logging
import db
import six
from functools import wraps
from decimal import Decimal
def log_event(f):
@wraps(f)
def decorated_function(*args, **kwargs):
msg = args[0].message
logging.info(f'"{f.__name__}" invoked from {msg.from_user["id"]} ({msg.from_user["first_name"]}) - Full command: "{msg.text}"')
return f(*args, **kwargs)
return decorated_function
def wallet_rpc_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
wallet = wownero.Wallet()
if not wallet.connected:
logging.error(f'Wallet RPC interface is not available: {args[0].message}')
args[0].message.reply_text('Wallet RPC interface is not available right now. Try again later.')
return False
return f(*args, **kwargs)
return decorated_function
def registration_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
wallet = wownero.Wallet()
if not db.User.filter(telegram_id=args[0].message.from_user['id']):
args[0].message.reply_text('You are not yet registered. Issue the /register command.')
return False
return f(*args, **kwargs)
return decorated_function
def help(update, context):
commands = list()
for i in all_commands:
pk = all_commands[i]
if not pk.get('admin', False):
commands.append('{example} - {help}'.format(
example=pk['example'],
help=pk['help']
))
update.message.reply_text('Here are the available commands for this bot:\n\n' + '\n\n'.join(commands))
@wallet_rpc_required
@log_event
def register(update, context):
uid = update.message.from_user['id']
un = update.message.from_user['first_name']
if db.User.filter(telegram_id=uid):
if db.User.filter(telegram_id=uid, telegram_user=un):
update.message.reply_text('You are already registered. Use /help to see available bot commands.')
else:
update.message.reply_text('Your ID exists in the database already but your `first_name` attribute has changed. Updating.')
try:
u = db.User.get(telegram_id=uid)
u.telegram_user = un
u.save()
update.message.reply_text(f'You have been registered again as Telegram ID {uid} but with username {un}.')
except Exception as e:
logging.error(f'Unable to update user in DB: {e}. Debug: {update.message}')
update.message.reply_text('Unable to update your existing account. Ask for help.')
return False
else:
try:
wallet = wownero.Wallet()
account_index = wallet.new_account(label=un)
except Exception as e:
logging.error(f'Unable to create a new account in wallet RPC: {e}. Debug: {update.message}')
update.message.reply_text('Unable to create a new account for you. Ask for help.')
return False
try:
u = db.User(
telegram_id=uid,
telegram_user=un,
account_index=account_index,
)
u.save()
reply_text = [
f'You have been registered as Telegram ID {uid} and username {un} and can now send and receive tips.',
'Ask for /help to see all available bot commands. Maybe start with /deposit to get your deposit address.'
]
update.message.reply_text(' '.join(reply_text))
except Exception as e:
logging.error(f'Unable to register user in DB: {e}. Debug: {update.message}')
update.message.reply_text('Unable to create a new account for you. Ask for help.')
return False
@wallet_rpc_required
@registration_required
@log_event
def tip(update, context):
if len(context.args) < 2:
update.message.reply_text('Not enough arguments passed.')
return False
elif len(context.args) == 2:
message = ""
elif len(context.args) > 2:
message = context.args[2:]
# validate target user
if context.args[0].startswith('@'):
target_un = context.args[0][1:]
else:
target_un = context.args[0]
if target_un == update.message.from_user['first_name']:
update.message.reply_text('You cannot tip yourself!')
return False
if not db.User.filter(telegram_user=target_un):
reply_text = [
'That user has not registered and cannot receive tips yet.',
'If they would like to receive a tip, have them /register with the bot.'
]
update.message.reply_text(' '.join(reply_text))
return False
# validate amount
try:
amount = Decimal(context.args[1])
except:
update.message.reply_text(f'Bad Wownero amount specified; "{context.args[1]}" is not a valid number.')
return False
if amount < 1:
update.message.reply_text('Bad Wownero amount specified. Provide only positive integers or decimals greater than or equal to 1.')
return False
tipper = db.User.get(telegram_id=update.message.from_user['id'])
tipper_balances = wownero.Wallet().balances(account=tipper.account_index)
if amount >= tipper_balances[1]:
update.message.reply_text(f'You do not have sufficient funds to send {amount} WOW. Check your /balance')
return False
# get target user details
receiver = db.User.get(telegram_user=target_un)
address = wownero.Wallet().addresses(account=receiver.account_index)[0]
# transfer funds to user
try:
tx = wownero.Wallet().transfer(dest_address=address, amount=wownero.as_wownero(amount), priority=2, account=tipper.account_index)
if 'tx_hash' in tx:
update.message.reply_text(f'Tipped @{target_un} {wownero.from_atomic(tx["amount"])} WOW! Here is the TX ID: {tx["tx_hash"]}')
else:
update.message.reply_text('Failed to send a tip. Ask for help.')
except Exception as e:
logging.error(f'Unable to send transfer: {e}. Debug: {update.message}')
update.message.reply_text('Failed to send a tip. Ask for help.')
@wallet_rpc_required
@registration_required
@log_event
def send(update, context):
if len(context.args) < 2:
update.message.reply_text('Not enough arguments passed.')
return False
# validate address
if len(context.args[0]) in [97, 107]:
address = context.args[0]
else:
update.message.reply_text('This does not look like a valid Wownero address. Try again.')
return False
# validate amount
try:
amount = wownero.as_wownero(context.args[1])
except:
update.message.reply_text(f'Bad Wownero amount specified; "{context.args[1]}" is not a valid number.')
return False
if amount < 1:
update.message.reply_text('Bad Wownero amount specified. Provide only positive integers or decimals greater than or equal to 1.')
return False
sender = db.User.get(telegram_id=update.message.from_user['id'])
sender_balances = wownero.Wallet().balances(account=sender.account_index)
if amount > sender_balances[1]:
update.message.reply_text(f'You do not have sufficient funds to send {amount} WOW. Check your /balance')
return False
# transfer funds to given address
try:
tx = wownero.Wallet().transfer(dest_address=address, amount=wownero.as_wownero(amount), priority=2, account=sender.account_index)
if 'tx_hash' in tx:
update.message.reply_text(f'Sent {wownero.from_atomic(tx["amount"])} WOW! Here is the TX ID: {tx["tx_hash"]}')
else:
update.message.reply_text('Failed to send Wownero. Ask for help.')
except Exception as e:
logging.error(f'Unable to send transfer: {e}. Debug: {update.message}')
update.message.reply_text('Failed to send Wownero. Ask for help.')
@wallet_rpc_required
@registration_required
@log_event
def balance(update, context):
u = db.User.get(telegram_id=update.message.from_user['id'])
balances = wownero.Wallet().balances(account=u.account_index)
update.message.reply_text(f'Available balance for {u.telegram_user}: {float(balances[1])} WOW ({float(balances[0])} WOW locked)')
@wallet_rpc_required
@registration_required
@log_event
def deposit(update, context):
u = db.User.get(telegram_id=update.message.from_user['id'])
address = wownero.Wallet().addresses(account=u.account_index)[0]
update.message.reply_text(f'Deposit address for {u.telegram_user}: {address}')
@wallet_rpc_required
@log_event
def debug(update, context):
if is_tg_admin(update.message.from_user['id']):
pass
else:
update.message.reply_text('you cant do that.')
def is_tg_admin(chat_id):
if chat_id == config.TG_ADMIN_ID:
return True
else:
return False
all_commands = {
'tip': {
'func': tip,
'example': '/tip <username> <amount> <message>',
'help': 'Tip a user in Wownero'
},
'send': {
'func': send,
'example': '/send <address> <amount>',
'help': 'Send Wownero to a specified Wownero address'
},
'balance': {
'func': balance,
'example': '/balance',
'help': 'Show your current balance'
},
'register': {
'func': register,
'example': '/register',
'help': 'Register your Telegram user ID to this bot to begin sending and receiving tips',
},
'deposit': {
'func': deposit,
'example': '/deposit',
'help': 'Show your Wownero wallet address for transferring funds to'
},
'help': {
'func': help,
'example': '/help',
'help': 'Show available commands for the bot',
},
'debug': {
'func': debug,
'admin': True
}
}