Пример #1
0
    async def handle(self, message: types.Message, user: UserRepository,
                     _: dict):
        user_accounts = await user.get_accounts(message.from_user.id)

        keyboard_markup = types.InlineKeyboardMarkup(row_width=1)

        message_text = _['cabinet_msg']
        if (len(user_accounts) == 0):
            message_text += "\n\n" + _['add_account']
        else:
            for i, account in enumerate(user_accounts):
                keyboard_markup.add(
                    types.InlineKeyboardButton(
                        f'#{i + 1} - {account.username}',
                        callback_data=menu_cb.new(id=account.account_id,
                                                  type="account",
                                                  action='open'),
                    ))

        keyboard_markup.add(
            types.InlineKeyboardButton(
                _['add_account_btn'],
                callback_data=menu_cb.new(id='_', type="account",
                                          action='new'),
            ))

        await message.answer(message_text.format(
            account_count=len(user_accounts), ),
                             reply_markup=keyboard_markup)
Пример #2
0
async def cabinet_callback_handler(
    query: types.CallbackQuery,
    callback_data: typing.Dict[str, str],
    user: UserRepository,
    _: dict,
):
    user_accounts = await user.get_accounts(query.from_user.id)

    keyboard_markup = types.InlineKeyboardMarkup(row_width=1)

    message_text = _["cabinet_msg"]
    if len(user_accounts) == 0:
        message_text += "\n\n" + _["add_account"]
    else:
        for i, account in enumerate(user_accounts):
            keyboard_markup.add(
                types.InlineKeyboardButton(
                    f"#{i + 1} - {account.username}",
                    callback_data=menu_cb.new(id=account.account_id,
                                              type="account",
                                              action="open"),
                ))

    keyboard_markup.add(
        types.InlineKeyboardButton(
            _["add_account_btn"],
            callback_data=menu_cb.new(id="_", type="account", action="new"),
        ))

    await query.message.edit_text(
        message_text.format(account_count=len(user_accounts), ),
        reply_markup=keyboard_markup,
    )
    await query.answer()
Пример #3
0
async def delete_account_confirmation_callback_handler(
    query: types.CallbackQuery,
    callback_data: typing.Dict[str, str],
    user: UserRepository,
    _: dict,
):
    account_id = callback_data["id"]
    keyboard_markup = types.InlineKeyboardMarkup(row_width=2)
    
    keyboard_markup.add(
        types.InlineKeyboardButton(
            _["back_to_account_list_button"],
            callback_data=menu_cb.new(
                id="_", type="menu", action="main_menu"
            ),
        ),
    )

    await user.delete_account_notification_settings_account(account_id, query.from_user.id)
    await user.delete_user_account_coin(account_id, query.from_user.id)
    await user.delete_user_account(account_id, query.from_user.id)
    
    await query.message.edit_text(
        _['account_deleted_descr'],
        reply_markup=keyboard_markup,
    )
    await query.answer()
Пример #4
0
def keyboard_when_account_deleted(_: LangHolder) -> types.InlineKeyboardMarkup:
    keyboard_markup = types.InlineKeyboardMarkup(row_width=2)

    keyboard_markup.add(
        types.InlineKeyboardButton(
            _["back_to_account_list_button"],
            callback_data=menu_cb.new(id="_", type="menu", action="main_menu"),
        ), )

    return keyboard_markup
Пример #5
0
async def finance_callback_handler(
    query: types.CallbackQuery,
    callback_data: typing.Dict[str, str],
    user: UserRepository,
    _: LangHolder,
):
    account_id = callback_data["id"]
    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):
        return await reply_to_account_not_found(query.message, _)

    btn_list = []

    for coin in await user.get_account_coins(query.from_user.id, account_id):
        if coin.is_active:
            btn_list.append(
                types.InlineKeyboardButton(
                    f"{Coin(coin.coin_id).name}",
                    callback_data=finance_cb.new(
                        id=account_id,
                        type=coin.coin_id,
                        action="payouts",
                        page=page,
                    ),
                ), )

    for i in grouper(2, btn_list):
        keyboard_markup.add(*i)

    keyboard_markup.add(
        types.InlineKeyboardButton(
            _['back_to_account_button'],
            callback_data=menu_cb.new(id=account.account_id,
                                      type="account",
                                      action='open'),
        ), )

    await query.message.edit_text(
        _["finance_choose_coin"],
        reply_markup=keyboard_markup,
    )
    await query.answer()
Пример #6
0
    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)
Пример #7
0
    async def handle(self, message: types.Message, user: UserRepository,
                     _: dict):
        inline_keyboard_markup = types.InlineKeyboardMarkup(row_width=2)

        inline_keyboard_markup.row(
            types.InlineKeyboardButton(
                _["delete_account"],
                callback_data=delete_account_cb.new(id="_", action="choose"),
            ),
            types.InlineKeyboardButton(
                _['language'],
                callback_data=lang_cb.new(id="_", ),
            ),
        )

        inline_keyboard_markup.row(
            types.InlineKeyboardButton(
                _["notifcation_button"],
                callback_data=notification_cb.new(action="_", ),
            ),
            types.InlineKeyboardButton(
                _['change_coins_button'],
                callback_data=menu_cb.new(id="_",
                                          type="account",
                                          action="c_coins"),
            ),
        )

        inline_keyboard_markup.row(
            types.InlineKeyboardButton(
                _["notifcation_payout_button"],
                callback_data=notification_payout_cb.new(action="_", ),
            ),
            types.InlineKeyboardButton(
                _["curr_list_button"],
                callback_data=currency_cb.new(
                    action="open",
                    id="_",
                ),
            ),
        )

        await message.answer(_['setting_descr'],
                             reply_markup=inline_keyboard_markup)
Пример #8
0
async def account_cabinet_callback_handler(
    query: types.CallbackQuery,
    callback_data: typing.Dict[str, str],
    user: UserRepository,
    _: LangHolder,
):
    # id=account.account_id, type="account", action='open'
    account_id = callback_data["id"]

    keyboard_markup = types.InlineKeyboardMarkup(row_width=2)
    action_type = SELECT_COIN_CB

    account = next(
        (acc for acc in await user.get_accounts(query.from_user.id)
         if str(acc.account_id) == str(account_id)),
        None,
    )

    if (account is None):
        return await reply_to_account_not_found(query.message, _)

    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.add(
        types.InlineKeyboardButton(
            _["workers_stat_button"],
            callback_data=worker_cb.new(
                id=account_id,
                page=1,
                type=action_type,
                status_id=3,
            ),
        ),
        types.InlineKeyboardButton(
            _["workers_black_list_button"],
            callback_data=worker_black_cb.new(
                id=account_id,
                page=1,
                type=action_type,
                action="_",
            ),
        ),
    )

    keyboard_markup.add(
        types.InlineKeyboardButton(
            _["statistic_button"],
            callback_data=statistic_cb.new(
                id=account_id,
                type=action_type,
            ),
        ),
        types.InlineKeyboardButton(
            _["finance_button"],
            callback_data=finance_cb.new(
                id=account_id,
                type=action_type,
                action="_" if action_type == SELECT_COIN_CB else "payouts",
                page=1,  #id=account_id, type=coin.coin_id, action=, page=page,
            ),
        ),
    )

    keyboard_markup.add(
        types.InlineKeyboardButton(
            _["back_to_account_list_button"],
            callback_data=menu_cb.new(id="_", type="menu", action="main_menu"),
        ), )

    await query.message.edit_text(
        _["account_cabinet"].format(account_name=account.username),
        reply_markup=keyboard_markup,
    )
    await query.answer()
Пример #9
0
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()
Пример #10
0
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()
Пример #11
0
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()
Пример #12
0
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()
Пример #13
0
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()