async def handle(self, message: types.Message, user: UserRepository, _: dict, state: FSMContext, logger: LoggerAdapter): account_id = message.text await state.finish() if (validate_uuid4(account_id)): user_account = await user.get_accounts(message.from_user.id) exist_account = next( (acc for acc in user_account if str(acc.account_id) == account_id), None) if (not exist_account): async with EmcdClient(account_id, logger) as client: account = await client.get_info() if (account): await user.add_account(message.from_user.id, account_id, account.username) coins_api = account.get_coins() for coin in await user.get_coins(message.from_user.id): c = coins_api[coin.coin_id] await user.add_account_coin( message.from_user.id, account_id, coin.coin_id, c, coin.is_enabled) await message.answer(_['account_added'].format( account_name=account.username)) return else: await message.answer(_['account_id_already_registered'].format( account_name=exist_account.username)) return keyboard_markup = types.InlineKeyboardMarkup(row_width=1) keyboard_markup.add( types.InlineKeyboardButton( _['again_button'], callback_data=menu_cb.new(id='_', type="account", action='new'), )) await message.answer(_['account_id_invalid'], reply_markup=keyboard_markup)
async def payouts_info_callback_handler( query: types.CallbackQuery, callback_data: typing.Dict[str, str], user: UserRepository, _: LangHolder, logger: logging.Logger, ): account_id = callback_data["id"] coind_id = callback_data['type'] page = int(callback_data['page']) coin_name = Coin(coind_id).name.lower() keyboard_markup = types.InlineKeyboardMarkup(row_width=2) account = next( (acc for acc in await user.get_accounts(query.from_user.id) if str(acc.account_id) == account_id), None, ) if (account is None): await query.answer() return await reply_to_account_not_found(query.message, _) payouts = None async with EmcdClient(account_id, logger) as client: payouts = await client.get_payouts(coind_id) if (payouts is None): await query.answer() logger.warning('payouts is none') return message_text = _['payouts'] buttons = [] if (page > 1): buttons.append( types.InlineKeyboardButton( _["prev_button"], callback_data=payouts_cb.new( id=account_id, page=page - 1, type=coind_id, ), ), ) if (payouts.payouts): buttons.append( types.InlineKeyboardButton( f"{page}/{ceil(len(payouts.payouts) / PER_PAGE_PAYOUTS)}", callback_data="do_nothing"), ) if (payouts): for payout in payouts.payouts[(page - 1) * PER_PAGE_PAYOUTS:page * PER_PAGE_PAYOUTS]: message_text += '\n' + _['payouts_template'].format( datetime=payout.gmt_time, amount=payout.amount, transaction_link= (f'<a href="https://blockchair.com/{coin_name}/transaction/{payout.txid}">{payout.txid[8:]}</a>' ) if payout.txid else _['no_link']) if (len(payouts.payouts) > page * PER_PAGE_PAYOUTS): buttons.append( types.InlineKeyboardButton( _["next_button"], callback_data=payouts_cb.new( id=account_id, page=page + 1, type=coind_id, ), ), ) keyboard_markup.row(*buttons) action_type = SELECT_COIN_CB coins = [ coin for coin in await user.get_account_coins( query.from_user.id, account_id) if coin.is_active ] if (len(coins) == 1 ): #in case if enabled only one coin we treat them as default action_type = coins[0].coin_id keyboard_markup.row( types.InlineKeyboardButton( _["income_stat_button"], callback_data=income_cb.new( id=account_id, page=1, type=coind_id, ), ), types.InlineKeyboardButton( _['back_to_account_button'], callback_data=menu_cb.new( id=account.account_id, type="account", action='open'), ) if len(coins) == 1 else types.InlineKeyboardButton( _["back_to_payouts"], callback_data=finance_cb.new( id=account_id, type=action_type, action=SELECT_COIN_CB, page= page, #id=account_id, type=coin.coin_id, action=, page=page, ), ), ) await query.message.edit_text( message_text, reply_markup=keyboard_markup, disable_web_page_preview=True, ) await query.answer()
async def worker_info_callback_handler( query: types.CallbackQuery, callback_data: typing.Dict[str, str], user: UserRepository, _: dict, logger: logging.Logger, ): account_id = callback_data["id"] coind_id = callback_data['type'] page = int(callback_data['page']) status_id = int(callback_data['status_id']) keyboard_markup = types.InlineKeyboardMarkup(row_width=2) workers = None async with EmcdClient(account_id, logger) as client: workers = await client.get_workers(coind_id) if (workers is None): await query.answer() logger.warning('workers is none') return message_text = "" buttons = [] if (page > 1): buttons.append( types.InlineKeyboardButton( _["prev_button"], callback_data=worker_cb.new( id=account_id, page=page - 1, type=coind_id, status_id=status_id, ), ), ) workers_normalized = workers.get_all_workers_by_status(0, status_id) if (workers_normalized): buttons.append( types.InlineKeyboardButton( f"{page}/{ceil(len(workers_normalized) / PER_PAGE_WORKERS)}", callback_data="do_nothing" ), ) locales = { 'hashrate': _['hashrate'], 'current': _['current'], '1_hour': _['1_hour'], '24_hour': _['24_hour'], } if (workers): for worker in workers_normalized[(page - 1) * PER_PAGE_WORKERS: page * PER_PAGE_WORKERS]: message_text += '\n' + format_worker_info(worker, locales) if (len(workers_normalized) > page * PER_PAGE_WORKERS): buttons.append( types.InlineKeyboardButton( _["next_button"], callback_data=worker_cb.new( id=account_id, page=page + 1, type=coind_id, status_id=status_id, ), ), ) keyboard_markup.row(*buttons) coins = [coin for coin in await user.get_account_coins(query.from_user.id, account_id) if coin.is_active] filter_text = _['show_dead_workers'] new_status_id = WORKER_STATUS_CAROUSEL[status_id] if (new_status_id == -1): filter_text = _['show_dead_workers'] elif (new_status_id == 0): filter_text = _['show_inactive_workers'] elif (new_status_id == 1): filter_text = _['show_active_workers'] elif (new_status_id == 3): filter_text = _['show_all_workers'] if (len(coins) == 1): #in case if enabled only one coin we treat them as default keyboard_markup.row( types.InlineKeyboardButton( filter_text, callback_data=worker_cb.new( id=account_id, page=page, type=coind_id, status_id=new_status_id, ), ), types.InlineKeyboardButton( _["cabinet"], callback_data=menu_cb.new( id=account_id, type="account", action="open", ), ), ) else: keyboard_markup.row( types.InlineKeyboardButton( filter_text, callback_data=worker_cb.new( id=account_id, page=page, type=coind_id, status_id=new_status_id, ), ), types.InlineKeyboardButton( _["back_to_workers"], callback_data=worker_cb.new( id=account_id, page=page, type=SELECT_COIN_CB, status_id=status_id, ), ), ) workers_all = workers.get_all_workers(0) await query.message.edit_text( _['worker_info_descr'].format( hashrate=format_rate(workers.total_hashrate.hashrate), hashrate1h=format_rate(workers.total_hashrate.hashrate1_h), hashrate24h=format_rate(workers.total_hashrate.hashrate24_h), total=str(len(workers_all)) + (_['show_workers_filter_pointer'] if status_id == 3 else ''), dead=str(len([i for i in workers_all if i.status_id == -1])) + (_['show_workers_filter_pointer'] if status_id == -1 else ''), active=str(len([i for i in workers_all if i.status_id == 1])) + (_['show_workers_filter_pointer'] if status_id == 1 else ''), inactive=str(len([i for i in workers_all if i.status_id == 0])) + (_['show_workers_filter_pointer'] if status_id == 0 else ''), description=message_text, ), reply_markup=keyboard_markup, ) await query.answer()
async def job(self, con: Connection, item: AccountCoinNotificationWorker, notifier: TelegramNotifier, locales: Dict): now = datetime.now() message_text = '' user_repo = UserRepository(con) notification_repo = NotificationRepository(con) async with EmcdClient(item.account_id, logger) as client: api_account = await client.get_workers(item.coin_id) if (api_account is None): logger.info(f'{item.account_id}|{item.coin_id} - is none') return user_account = next( (acc for acc in await user_repo.get_accounts(item.user_id) if acc.account_id == item.account_id), None) logger.info( f'{item.account_id}|{item.coin_id} - Scraped worker info') workers = api_account.get_all_workers(item.id) await user_repo.update_account_coin(item.id, item.user_id, item.account_id, item.coin_id, item.address, api_account, item.is_active, now) logger.info( f'{item.account_id}|{item.coin_id} - Total workers count {len(workers)}' ) if (workers): user_db = await user_repo.get_user(item.user_id) user_locale = Lang(user_db.lang_id) translation = locales[user_locale.name] change_status_descr = [] blacklisted_workers = [ w.worker_id for w in await user_repo.get_blacklisted_workers(item.user_id) ] previous_worker_state = await user_repo.get_previous_worker_state_for_account( item.id) for worker in workers: previous_state = next((w for w in previous_worker_state if w.worker_id == worker.worker), None) if (previous_state): if (previous_state.status_id != worker.status_id): logger.info( f'{item.account_id}|{item.coin_id} - worker changed status {worker.worker}' ) if (worker.worker not in blacklisted_workers): change_status_descr.append( WorkerChangeStatusDataModel( previous_state.status_id, worker.status_id, previous_state.worker_id)) else: logger.info( f'{item.account_id}|{item.coin_id} - {worker.worker} blacklisted' ) if (worker.status_id == -1): logger.info( f'{item.account_id}|{item.coin_id} - {worker.worker} dead, need to delete from blacklist' ) await user_repo.toggle_worker_blacklist( item.user_id, worker.worker) logger.info( f'{item.account_id}|{item.coin_id} - Sending notification {item.id}' ) if (change_status_descr): descr = [ st.to_description( translation['worker_changed_status_descr'], translation) for st in change_status_descr ] message_text = translation[ 'worker_changed_status_body'].format( account_name=user_account.username, description='\n'.join([i for i in descr])) await notification_repo.add(message_text, NotifyType.Worker, [NotifyChannel.Telegram], user_db.id) logger.info( f'{item.account_id}|{item.coin_id} - Clean up worker history {item.id}' ) await user_repo.cleanup_worker_history_for_account(item.id) logger.info( f'{item.account_id}|{item.coin_id} - Storing to database') await user_repo.store_coin_account_worker_history(workers, now) logger.info(f'{item.account_id}|{item.coin_id} - Stored')
async def statistic_info_callback_handler( query: types.CallbackQuery, callback_data: typing.Dict[str, str], user: UserRepository, _: LangHolder, logger: logging.Logger, ): account_id = callback_data["id"] coin_id = callback_data['type'] keyboard_markup = types.InlineKeyboardMarkup(row_width=2) account = next( (acc for acc in await user.get_accounts(query.from_user.id) if str(acc.account_id) == account_id), None, ) if (account is None): await query.answer() return await reply_to_account_not_found(query.message, _) account_coin = next( (i for i in await user.get_account_coins(query.from_user.id, account_id) if i.coin_id == coin_id), None, ) buttons = [] coins = [ coin for coin in await user.get_account_coins( query.from_user.id, account_id) if coin.is_active ] if (len(coins) == 1 ): #in case if enabled only one coin we treat them as default buttons.append( types.InlineKeyboardButton( _["cabinet"], callback_data=menu_cb.new(id=account_id, type="account", action="open"), ), ) else: buttons.append( types.InlineKeyboardButton( _["back_to_statistic"], callback_data=statistic_cb.new( id=account_id, type=SELECT_COIN_CB, ), ), ) keyboard_markup.row(*buttons) account_api = None currency_list = None user_currency = await user.get_user_currency(query.from_user.id) async with EmcdClient(account_id, logger) as client: account_api = await client.get_info() if (account_api is None): await query.answer() logger.warning('account_api is none') return is_fallback = False async with CoinCapClient() as client: c_id = coin_id if (c_id == 'bchn'): c_id = 'bch' currency_list = await client.get_info_for_exchange( c_id, user_currency.currency_code) if (currency_list is None or len(currency_list.data) == 0): currency_list = await client.get_info_for_exchange( c_id, FALLBACK_CURRENCY) is_fallback = True if (currency_list is None): await query.answer('Please try later, 3rd party service is busy') logger.warning('currency_list is none') return curr = sorted(currency_list.data, key=take_update_timestamp)[0] coin_info = account_api.get_coins()[coin_id] message_text = "" if (is_fallback): message_text = _['currency_not_found'].format( currency_code=user_currency.currency_code, fallback_currency_code=FALLBACK_CURRENCY, ) + '\n' message_text += _['statistic_descr'].format( account_name=account.username, address=account_coin.address, current_balance=format(coin_info.balance, '.8f'), current_balance_dol=format_currency(round( coin_info.balance * curr.price_usd, 4), '', locale="en_US"), current_balance_sec=format_currency(round( coin_info.balance * curr.price_quote, 4), '', locale="en_US"), current_balance_sec_symbol=curr.quote_symbol, total_paid=format(coin_info.total_paid, '.8f'), total_paid_dol=format_currency(round( coin_info.total_paid * curr.price_usd, 4), '', locale="en_US"), total_paid_sec=format_currency(round( coin_info.total_paid * curr.price_quote, 4), '', locale="en_US"), total_paid_sec_symbol=curr.quote_symbol, currency_dol=format_currency(round(curr.price_usd, 2), '', locale="en_US"), currency_sec=format_currency(round(curr.price_quote, 2), '', locale="en_US"), currency_sec_symbol=curr.quote_symbol, ) await query.message.edit_text( message_text, reply_markup=keyboard_markup, ) await query.answer()
async def job(self, con: Connection, item: AccountCoinNotificationPayout, notifier: TelegramNotifier, locales: Dict): user_repo = UserRepository(con) notification_repo = NotificationRepository(con) async with EmcdClient(item.account_id, logger) as client: user_account = next( (acc for acc in await user_repo.get_accounts(item.user_id) if acc.account_id == item.account_id), None) payouts = await client.get_payouts(item.coin_id) if (payouts is None): logger.info('payouts is none') return user_db = await user_repo.get_user(item.user_id) actual_payouts = [ p for p in payouts.payouts if p.timestamp > PAYOUTS_CHECK_START_DATETIME and p.timestamp > item.notification_update_datetime.timestamp() and p.timestamp > item.account_created_datetime.timestamp() and p.txid is not None and p.txid != '' ] if (actual_payouts): user_locale = Lang(user_db.lang_id) translation = locales[user_locale.name] for payout in actual_payouts: is_payout_notified = await user_repo.is_payout_notified( item.id, payout.timestamp, ) if (is_payout_notified == False): coin = Coin(item.coin_id) latest_account_data = await client.get_info() if (latest_account_data is None): logger.info('account is none') return latest_coin_data = latest_account_data.get_coins()[ item.coin_id] msg_text = translation['new_payout_received'].format( account=user_account.username, link= f'<a href="https://blockchair.com/{coin.name.lower()}/transaction/{payout.txid}">{payout.txid[8:]}</a>', amount=payout.amount, current_balance=format(latest_coin_data.balance, '.8f'), ) await notification_repo.add(msg_text, NotifyType.Payout, [NotifyChannel.Telegram], user_db.id) await user_repo.mark_payout_as_notified( item.id, payout.timestamp, )
async def income_info_callback_handler( query: types.CallbackQuery, callback_data: typing.Dict[str, str], user: UserRepository, _: LangHolder, logger: logging.Logger, ): account_id = callback_data["id"] coind_id = callback_data['type'] page = int(callback_data['page']) keyboard_markup = types.InlineKeyboardMarkup(row_width=2) account = next( (acc for acc in await user.get_accounts(query.from_user.id) if str(acc.account_id) == account_id), None, ) if (account is None): await query.answer() return await reply_to_account_not_found(query.message, _) incomes = None async with EmcdClient(account_id, logger) as client: incomes = await client.get_rewards(coind_id) if (incomes is None): await query.answer() logger.warning('incomes is none') return message_text = _['income'] buttons = [] if (page > 1): buttons.append( types.InlineKeyboardButton( _["prev_button"], callback_data=income_cb.new( id=account_id, page=page - 1, type=coind_id, ), ), ) if (incomes.income): buttons.append( types.InlineKeyboardButton( f"{page}/{ceil(len(incomes.income) / PER_PAGE_INCOME)}", callback_data="do_nothing"), ) if (incomes): for income in incomes.income[(page - 1) * PER_PAGE_INCOME:page * PER_PAGE_INCOME]: message_text += '\n' + _['income_template'].format( datetime=income.gmt_time, amount=format(income.income, '.8f'), ) if (len(incomes.income) > page * PER_PAGE_INCOME): buttons.append( types.InlineKeyboardButton( _["next_button"], callback_data=income_cb.new( id=account_id, page=page + 1, type=coind_id, ), ), ) keyboard_markup.row(*buttons) action_type = SELECT_COIN_CB coins = [ coin for coin in await user.get_account_coins( query.from_user.id, account_id) if coin.is_active ] if (len(coins) == 1 ): #in case if enabled only one coin we treat them as default action_type = coins[0].coin_id keyboard_markup.row( types.InlineKeyboardButton( _["payouts_stat_button"], callback_data=payouts_cb.new( id=account_id, page=1, type=coind_id, ), ), types.InlineKeyboardButton( _['back_to_account_button'], callback_data=menu_cb.new( id=account.account_id, type="account", action='open'), ) if len(coins) == 1 else types.InlineKeyboardButton( _["back_to_income"], callback_data=finance_cb.new( id=account_id, type=action_type, action=SELECT_COIN_CB, page= page, #id=account_id, type=coin.coin_id, action=, page=page, ), ), ) await query.message.edit_text( message_text, reply_markup=keyboard_markup, ) await query.answer()
async def black_list_info_callback_handler( query: types.CallbackQuery, callback_data: typing.Dict[str, str], user: UserRepository, _: dict, logger: logging.Logger, state: FSMContext, ): account_id = callback_data["id"] coin_id = callback_data['type'] action = callback_data['action'] page = int(callback_data['page']) if (action != '_'): async with state.proxy() as data: worker_id = data.get('workers_unique', {}).get(action, None) if (worker_id): is_blacklisted = await user.toggle_worker_blacklist( query.from_user.id, worker_id) await query.answer(_['worker_blacklisted' if is_blacklisted else 'worker_not_blacklisted'].format( worker=worker_id, )) else: logger.warning( f'worker_id was null {data.get("workers_unique")}') await query.answer( 'If you see this message please contact with support', show_alert=True) keyboard_markup = types.InlineKeyboardMarkup(row_width=2) blacklisted_workers = [ w.worker_id for w in await user.get_blacklisted_workers(query.from_user.id) ] workers = None async with EmcdClient(account_id, logger) as client: workers = await client.get_workers(coin_id) if (workers is None): await query.answer() logger.warning('workers is none') return buttons = [] buttons_workers = [] if (page > 1): buttons.append( types.InlineKeyboardButton( _["prev_button"], callback_data=worker_black_cb.new(id=account_id, page=page - 1, type=coin_id, action="_"), ), ) workers_normalized = workers.get_all_workers(0, ) if (workers_normalized): buttons.append( types.InlineKeyboardButton( f"{page}/{ceil(len(workers_normalized) / PER_PAGE_BLACK_LIST)}", callback_data="do_nothing"), ) async with state.proxy() as data: data['workers_unique'] = generate_unique_workers_dict( workers_normalized) for worker in workers_normalized[(page - 1) * PER_PAGE_BLACK_LIST:page * PER_PAGE_BLACK_LIST]: buttons_workers.append( types.InlineKeyboardButton( worker.worker + (_["blacklist_pointer"] if worker.worker in blacklisted_workers else ""), callback_data=worker_black_cb.new( id=account_id, page=page, type=coin_id, action=worker.hash_name()), ), ) if (len(workers_normalized) > page * PER_PAGE_BLACK_LIST): buttons.append( types.InlineKeyboardButton( _["next_button"], callback_data=worker_black_cb.new(id=account_id, page=page + 1, type=coin_id, action="_"), ), ) for w_row in grouper(2, buttons_workers): keyboard_markup.row(*w_row) keyboard_markup.row(*buttons) coins = [ coin for coin in await user.get_account_coins( query.from_user.id, account_id) if coin.is_active ] if (len(coins) == 1 ): #in case if enabled only one coin we treat them as default keyboard_markup.row( types.InlineKeyboardButton( _["cabinet"], callback_data=menu_cb.new( id=account_id, type="account", action="open", ), ), ) else: keyboard_markup.row( types.InlineKeyboardButton( _["back_to_workers_blacklist"], callback_data=worker_black_cb.new( id=account_id, page=page, type=SELECT_COIN_CB, action="_", ), ), ) await query.message.edit_text( _['worker_blacklist_descr'].format( b_count=len(blacklisted_workers), pointer=_["blacklist_pointer"], ), reply_markup=keyboard_markup, ) await query.answer()