Пример #1
0
async def photo_message(message: types.Message):
    photo_path = 'photos/' + str(message.message_id) + '.jpg'
    await message.photo[-1].download(photo_path)
    await message.reply('Wait a sec...')
    embedding = get_face_descriptor(photo_path)
    if embedding is None:
        await bot.delete_message(message.chat.id, message.message_id + 1)
        await message.reply(
            "I can't find a face in this photo, try to send another")
        # Перемещаем фотографии без лица в отдельную папку
        os.rename(photo_path, f'photos/withoutface/{message.message_id}.jpg')
        return
    ids, _ = index.knnQuery(embedding, k=3)
    urls = [
        get_url_of_face_with_id(ids[0]),
        get_url_of_face_with_id(ids[1]),
        get_url_of_face_with_id(ids[2])
    ]
    url_names = []
    for url in urls:
        url_names.append(get_name_for_url(url))
    await bot.delete_message(message.chat.id, message.message_id + 1)
    await message.reply('Most similar people\'s:\n\n'+ link(url_names[0],urls[0]) + '\n\n' \
                        + link(url_names[1],urls[1]) + '\n\n' + link(url_names[2],urls[2]), \
                        parse_mode = ParseMode.MARKDOWN)
    await message.reply('If you wanna try again, just send me a photo')
Пример #2
0
async def full_card_number_sent(call: types.CallbackQuery, offer: EscrowOffer):
    """Confirm that full card number is sent and ask for first and last 4 digits."""
    await offer.update_document(
        {"$set": {
            "pending_input_from": call.from_user.id
        }})
    await call.answer()
    if call.from_user.id == offer.init["id"]:
        counter = offer.counter
        await tg.send_message(
            counter["id"],
            i18n("ask_address {currency}").format(currency=offer.buy))
        await tg.send_message(
            call.message.chat.id,
            i18n("exchange_continued {user}").format(
                user=markdown.link(counter["mention"],
                                   User(id=counter["id"]).url)),
            parse_mode=ParseMode.MARKDOWN,
        )
        await offer.update_document(
            {"$set": {
                "pending_input_from": counter["id"]
            }})
        counter_state = FSMContext(dp.storage, counter["id"], counter["id"])
        await counter_state.set_state(states.Escrow.receive_address.state)
        await states.Escrow.receive_card_number.set()
    else:
        await tg.send_message(
            call.message.chat.id,
            i18n("send_first_and_last_4_digits_of_card_number {currency}").
            format(currency=offer.sell if offer.type == "buy" else offer.buy),
        )
        await states.Escrow.receive_card_number.set()
Пример #3
0
async def start_cmd_handler(message: types.Message):
    support_chat_url = link("сюда", "https://t.me/miptonelove")
    welcome_message = f"Приветик\!\nПросто выбери семестр и набери номер задачи\.\nЕсли вам есть что сказать \- пишите {support_chat_url}\."
    message_to_pin = await bot.send_message(
        message.from_user.id,
        welcome_message,
        reply_markup=change_sem_keyboard(),
        parse_mode="MarkdownV2",
    )
    await bot.pin_chat_message(chat_id=message_to_pin.chat.id,
                               message_id=message_to_pin.message_id)
Пример #4
0
async def cmd_about(message: types.Message):
    # In this function markdown utils are userd for formatting message text
    return SendMessage(message.chat.id,
                       text(bold('Hi! I\'m just a simple telegram bot.'),
                            '',
                            text(
                                'With',
                                link(text,
                                     'https://github.com/aiogram/aiogram')),
                            sep='\n'),
                       parse_mode=ParseMode.MARKDOWN)
Пример #5
0
async def full_card_number_message(message: types.Message, offer: EscrowOffer):
    """React to sent message while sending full card number to fiat sender."""
    if message.from_user.id == offer.init["id"]:
        user = offer.counter
    else:
        user = offer.init
    mention = markdown.link(user["mention"], User(id=user["id"]).url)
    await tg.send_message(
        message.chat.id,
        i18n("wrong_full_card_number_receiver {user}").format(user=mention),
        parse_mode=ParseMode.MARKDOWN,
    )
Пример #6
0
def get_user_link(user_id: Union[str, int], text: Optional[str] = None) -> str:
    """
    Get markdown link on a user
    Used in the admin's block commands

    :param user_id:   user's id
    :param text:      text of the link

    :returns:         link
    """
    if not text:
        text = user_id
    return link(str(text), f"tg://user?id={user_id}")
Пример #7
0
def _status_to_md_row(status_record):
    status_emoji = replace_emoji(status_record['status'])
    name = escape_md(status_record['name'])
    timestamp = escape_md(status_record['timestamp'])
    url = status_record['details_url']
    if url:
        timestamp = link(timestamp, url)
    else:
        timestamp = f'`{timestamp or "n/a"}`'
    if 'error' in status_record:
        details = f'{name}\n{escape_md(status_record["error"])}'
    else:
        details = rf'{name} \({timestamp}\)'
    return f'{status_emoji}  —  {details}'
Пример #8
0
async def cmd_about(message: types.Message):
    # In this function markdown utils are userd for formatting message text
    return SendMessage(message.chat.id,
                       text(bold('Hi! I\'m just a simple telegram bot.'),
                            '',
                            text('I\'m powered by',
                                 bold('Python',
                                      Version(*sys.version_info[:]))),
                            text(
                                'With',
                                link(text('aiogram', aiogram.VERSION),
                                     'https://github.com/aiogram/aiogram')),
                            sep='\n'),
                       parse_mode=ParseMode.MARKDOWN)
Пример #9
0
async def cmd_about(message: types.Message):
    # In this function used markdown utils for formatting message text
    return SendMessage(message.chat.id,
                       text(
                           bold('Hi! I\'m simple telegram bot.'),
                           '',
                           text('I\'m worked on',
                                bold('Python', Version(*sys.version_info[:]))),
                           text(
                               'With',
                               link(text('aiogram', aiogram.VERSION),
                                    'https://bitbucket.org/illemius/aiogram')),
                           sep='\n'),
                       parse_mode=ParseMode.MARKDOWN)
Пример #10
0
async def validate_offer(call: types.CallbackQuery, offer: EscrowOffer):
    """Ask support for manual verification of exchange."""
    if offer.type == "buy":
        sender = offer.counter
        receiver = offer.init
        currency = offer.sell
    elif offer.type == "sell":
        sender = offer.init
        receiver = offer.counter
        currency = offer.buy

    escrow_instance = get_escrow_instance(offer.escrow)
    answer = "{0}\n{1} sender: {2}{3}\n{1} receiver: {4}{5}\nBank: {6}\nMemo: {7}"
    answer = answer.format(
        markdown.link("Unconfirmed escrow.",
                      escrow_instance.trx_url(offer.trx_id)),
        currency,
        markdown.link(sender["mention"],
                      User(id=sender["id"]).url),
        " ({})".format(sender["name"]) if "name" in sender else "",
        markdown.link(receiver["mention"],
                      User(id=receiver["id"]).url),
        " ({})".format(receiver["name"]) if "name" in receiver else "",
        offer.bank,
        markdown.code(offer.memo),
    )
    await tg.send_message(config.SUPPORT_CHAT_ID,
                          answer,
                          parse_mode=ParseMode.MARKDOWN)
    await offer.delete_document()
    await call.answer()
    await tg.send_message(
        call.message.chat.id,
        i18n("request_validation_promise"),
        reply_markup=start_keyboard(),
    )
Пример #11
0
async def send_new_post(callback_query: types.CallbackQuery):
    await bot.answer_callback_query(callback_query.id)
    data = json_methods.read(DB_PATH)
    message_text = data['user_id'][str(
        callback_query.from_user.id)]['head'] + '\n\n' + data['user_id'][str(
            callback_query.from_user.id)]['body']
    await bot.send_photo(config.CHANEL_ID,
                         data['user_id'][str(
                             callback_query.from_user.id)]['photo_id'],
                         caption=message_text)
    message_text = text('Готово, ваш пост был отправлен в ' +
                        link('канал', config.CHANEL_URL) +
                        'Хотите создать еще один пост?')
    await bot.send_message(callback_query.from_user.id,
                           message_text,
                           reply_markup=keyboards.creation_agreement_kb,
                           parse_mode=ParseMode.MARKDOWN)
Пример #12
0
async def complete_offer(call: types.CallbackQuery, offer: EscrowOffer):
    """Release escrow asset and finish exchange."""
    if offer.type == "buy":
        recipient_user = offer.counter
        sender_user = offer.init
        amount = offer.sum_buy.to_decimal()  # type: ignore
    elif offer.type == "sell":
        recipient_user = offer.init
        sender_user = offer.counter
        amount = offer.sum_sell.to_decimal()  # type: ignore

    sum_fee_up = offer.sum_fee_up.to_decimal()  # type: ignore
    sum_fee_down = offer.sum_fee_down.to_decimal()  # type: ignore

    await call.answer(i18n("escrow_completing"))
    escrow_instance = get_escrow_instance(offer.escrow)
    trx_url = await escrow_instance.transfer(
        recipient_user["receive_address"],
        sum_fee_down,
        offer.escrow,
        memo=create_memo(offer, transfer=True),
    )

    if sender_user["send_address"] != recipient_user["receive_address"]:
        add_cashback(offer.escrow, amount, sum_fee_up, sum_fee_down,
                     sender_user, recipient_user)

    answer = i18n("escrow_completed", locale=sender_user["locale"])
    recipient_answer = i18n("escrow_completed",
                            locale=recipient_user["locale"])
    recipient_answer += " " + markdown.link(
        i18n("escrow_sent {amount} {currency}",
             locale=recipient_user["locale"]).format(amount=amount,
                                                     currency=offer.escrow),
        trx_url,
    )
    await offer.delete_document()
    await tg.send_message(
        recipient_user["id"],
        recipient_answer,
        reply_markup=start_keyboard(),
        parse_mode=ParseMode.MARKDOWN,
    )
    await tg.send_message(sender_user["id"],
                          answer,
                          reply_markup=start_keyboard())
Пример #13
0
async def claim_transfer_custom_address(message: types.Message,
                                        state: FSMContext):
    """Transfer cashback to custom address."""
    data = await state.get_data()
    await tg.send_message(message.chat.id, i18n("claim_transfer_wait"))
    try:
        trx_url = await transfer_cashback(message.from_user.id,
                                          data["currency"], message.text)
    except TransferError:
        await tg.send_message(message.chat.id, i18n("cashback_transfer_error"))
    else:
        await tg.send_message(
            message.chat.id,
            markdown.link(i18n("cashback_transferred"), trx_url),
            parse_mode=ParseMode.MARKDOWN,
            reply_markup=start_keyboard(),
        )
Пример #14
0
async def errors_handler(update: types.Update, exception: Exception):
    """Handle exceptions when calling handlers.

    Send error notification to special chat and warn user about the error.
    """
    if isinstance(exception, MessageNotModified):
        return True

    log.error("Error handling request {}".format(update.update_id),
              exc_info=True)

    chat_id = None
    if update.message:
        update_type = "message"
        from_user = update.message.from_user
        chat_id = update.message.chat.id
    if update.callback_query:
        update_type = "callback query"
        from_user = update.callback_query.from_user
        chat_id = update.callback_query.message.chat.id

    if chat_id is not None:
        try:
            exceptions_chat_id = config.EXCEPTIONS_CHAT_ID
        except AttributeError:
            pass
        else:
            await tg.send_message(
                exceptions_chat_id,
                "Error handling {} {} from {} ({}) in chat {}\n{}".format(
                    update_type,
                    update.update_id,
                    markdown.link(from_user.mention, from_user.url),
                    from_user.id,
                    chat_id,
                    markdown.code(traceback.format_exc(limit=-3)),
                ),
                parse_mode=types.ParseMode.MARKDOWN,
            )
        await tg.send_message(
            chat_id,
            i18n("unexpected_error"),
            reply_markup=start_keyboard(),
        )

    return True
Пример #15
0
async def claim_transfer(call: types.CallbackQuery):
    """Transfer cashback to suggested address."""
    _, currency, address = call.data.split()
    await call.answer(i18n("claim_transfer_wait"), show_alert=True)
    try:
        trx_url = await transfer_cashback(call.from_user.id, currency, address)
    except TransferError:
        await tg.send_message(
            call.message.chat.id,
            i18n("cashback_transfer_error"),
            reply_markup=start_keyboard(),
        )
    else:
        await tg.send_message(
            call.message.chat.id,
            markdown.link(i18n("cashback_transferred"), trx_url),
            reply_markup=start_keyboard(),
            parse_mode=ParseMode.MARKDOWN,
        )
Пример #16
0
async def send_message_to_support(message: types.Message):
    """Format message and send it to support.

    Envelope emoji at the beginning is the mark of support ticket.
    """
    if message.from_user.username:
        username = "******" + message.from_user.username
    else:
        username = markdown.link(message.from_user.full_name,
                                 message.from_user.url)

    await tg.send_message(
        config.SUPPORT_CHAT_ID,
        emojize(":envelope:") +
        f" #chat\\_{message.chat.id} {message.message_id}\n{username}:\n" +
        markdown.escape_md(message.text),
        parse_mode=types.ParseMode.MARKDOWN,
    )
    await tg.send_message(
        message.chat.id,
        i18n("support_response_promise"),
        reply_markup=start_keyboard(),
    )
Пример #17
0
async def set_init_send_address(address: str, message: types.Message,
                                offer: EscrowOffer):
    """Set ``address`` as sender's address of initiator.

    Send offer to counteragent.
    """
    locale = offer.counter["locale"]
    buy_keyboard = InlineKeyboardMarkup()
    buy_keyboard.row(
        InlineKeyboardButton(i18n("show_order", locale=locale),
                             callback_data=f"get_order {offer.order}"))
    buy_keyboard.add(
        InlineKeyboardButton(i18n("accept", locale=locale),
                             callback_data=f"accept {offer._id}"),
        InlineKeyboardButton(i18n("decline", locale=locale),
                             callback_data=f"decline {offer._id}"),
    )
    mention = markdown.link(offer.init["mention"],
                            User(id=offer.init["id"]).url)
    answer = i18n(
        "escrow_offer_notification {user} {sell_amount} {sell_currency} "
        "for {buy_amount} {buy_currency}",
        locale=locale,
    ).format(
        user=mention,
        sell_amount=offer.sum_sell,
        sell_currency=offer.sell,
        buy_amount=offer.sum_buy,
        buy_currency=offer.buy,
    )
    if offer.bank:
        answer += " " + i18n("using {bank}",
                             locale=locale).format(bank=offer.bank)
    answer += "."
    update_dict = {"init.send_address": address}
    if offer.type == "sell":
        insured = await get_insurance(offer)
        update_dict["insured"] = Decimal128(insured)
        if offer[f"sum_{offer.type}"].to_decimal() > insured:
            answer += "\n" + i18n(
                "exceeded_insurance {amount} {currency}").format(
                    amount=insured, currency=offer.escrow)
    await offer.update_document({
        "$set": update_dict,
        "$unset": {
            "pending_input_from": True
        }
    })
    await tg.send_message(
        offer.counter["id"],
        answer,
        reply_markup=buy_keyboard,
        parse_mode=ParseMode.MARKDOWN,
    )
    sell_keyboard = InlineKeyboardMarkup()
    sell_keyboard.add(
        InlineKeyboardButton(i18n("cancel"),
                             callback_data=f"escrow_cancel {offer._id}"))
    await tg.send_message(message.from_user.id,
                          i18n("offer_sent"),
                          reply_markup=sell_keyboard)
    await dp.current_state().finish()
Пример #18
0
async def ask_credentials(
    call: types.CallbackQuery,
    offer: EscrowOffer,
):
    """Update offer with ``update_dict`` and start asking transfer information.

    Ask to choose bank if user is initiator and there is a fiat
    currency. Otherwise ask receive address.
    """
    await call.answer()
    is_user_init = call.from_user.id == offer.init["id"]
    has_fiat_currency = "RUB" in {offer.buy, offer.sell}
    if has_fiat_currency:
        if is_user_init:
            keyboard = InlineKeyboardMarkup()
            for bank in SUPPORTED_BANKS:
                keyboard.row(
                    InlineKeyboardButton(
                        bank, callback_data=f"bank {offer._id} {bank}"))
            await tg.send_message(call.message.chat.id,
                                  i18n("choose_bank"),
                                  reply_markup=keyboard)
            await states.Escrow.bank.set()
        else:
            if offer.type == "buy":
                request_user = offer.init
                answer_user = offer.counter
                currency = offer.sell
            else:
                request_user = offer.counter
                answer_user = offer.init
                currency = offer.buy
            keyboard = InlineKeyboardMarkup()
            keyboard.add(
                InlineKeyboardButton(i18n("sent"),
                                     callback_data=f"card_sent {offer._id}"))
            mention = markdown.link(answer_user["mention"],
                                    User(id=answer_user["id"]).url)
            await tg.send_message(
                request_user["id"],
                i18n(
                    "request_full_card_number {currency} {user}",
                    locale=request_user["locale"],
                ).format(currency=currency, user=mention),
                reply_markup=keyboard,
                parse_mode=ParseMode.MARKDOWN,
            )
            state = FSMContext(dp.storage, request_user["id"],
                               request_user["id"])
            await state.set_state(states.Escrow.full_card.state)
            answer = i18n(
                "asked_full_card_number {user}",
                locale=answer_user["locale"]).format(
                    user=markdown.link(request_user["mention"],
                                       User(id=request_user["id"]).url))
            await tg.send_message(
                answer_user["id"],
                answer,
                parse_mode=ParseMode.MARKDOWN,
            )
        return

    await tg.send_message(
        call.message.chat.id,
        i18n("ask_address {currency}").format(
            currency=offer.sell if is_user_init else offer.buy),
    )
    await offer.update_document(
        {"$set": {
            "pending_input_from": call.from_user.id
        }})
    await states.Escrow.receive_address.set()
Пример #19
0
    async def _refund_callback(
        self,
        reasons: typing.FrozenSet[str],
        offer_id: ObjectId,
        op: typing.Mapping[str, typing.Any],
        from_address: str,
        amount: Decimal,
        asset: str,
        block_num: int,
    ) -> None:
        """Refund transaction after confirmation because of mistakes in it.

        :param reasons: Frozen set of mistakes in transaction.
            The only allowed elements are ``asset``, ``amount`` and ``memo``.
        :param offer_id: ``_id`` of escrow offer.
        :param op: Operation object to confirm.
        :param from_address: Address which sent assets.
        :param amount: Amount of transferred asset.
        :param asset: Transferred asset.
        """
        offer = await database.escrow.find_one({"_id": offer_id})
        if not offer:
            return

        user = offer["init"] if offer["type"] == "buy" else offer["counter"]
        answer = i18n("transfer_mistakes", locale=user["locale"])
        points = []
        for reason in reasons:
            if reason == "asset":
                memo_point = i18n("wrong_asset", locale="en")
                message_point = i18n("wrong_asset", locale=user["locale"])
            elif reason == "amount":
                memo_point = i18n("wrong_amount", locale="en")
                message_point = i18n("wrong_amount", locale=user["locale"])
            elif reason == "memo":
                memo_point = i18n("wrong_memo", locale="en")
                message_point = i18n("wrong_memo", locale=user["locale"])
            else:
                continue
            points.append(memo_point)
            answer += f"\n• {message_point}"

        answer += "\n\n" + i18n("refund_promise", locale=user["locale"])
        await tg.send_message(user["id"],
                              answer,
                              parse_mode=ParseMode.MARKDOWN)
        is_confirmed = await create_task(self.is_block_confirmed(
            block_num, op))
        await database.escrow.update_one(
            {"_id": offer["_id"]}, {"$set": {
                "transaction_time": time()
            }})
        if is_confirmed:
            trx_url = await self.transfer(
                from_address,
                amount,
                asset,
                memo="reason of refund: " + ", ".join(points),
            )
            answer = markdown.link(
                i18n("transaction_refunded", locale=user["locale"]), trx_url)
        else:
            answer = i18n("transaction_not_confirmed", locale=user["locale"])
        answer += " " + i18n("try_again", locale=user["locale"])
        await tg.send_message(user["id"],
                              answer,
                              parse_mode=ParseMode.MARKDOWN)
Пример #20
0
    async def _confirmation_callback(
        self,
        offer_id: ObjectId,
        op: typing.Mapping[str, typing.Any],
        trx_id: str,
        block_num: int,
    ) -> bool:
        """Confirm found block with transaction.

        Notify escrow asset sender and check if block is confirmed.
        If it is, continue exchange. If it is not, send warning and
        update ``transaction_time`` of escrow offer.

        :param offer_id: ``_id`` of escrow offer.
        :param op: Operation object to confirm.
        :param trx_id: ID of transaction with desired operation.
        :param block_num: Number of block to confirm.
        :return: True if transaction was confirmed and False otherwise.
        """
        offer = await database.escrow.find_one({"_id": offer_id})
        if not offer:
            return False

        if offer["type"] == "buy":
            new_currency = "sell"
            escrow_user = offer["init"]
            other_user = offer["counter"]
        elif offer["type"] == "sell":
            new_currency = "buy"
            escrow_user = offer["counter"]
            other_user = offer["init"]

        answer = i18n(
            "transaction_passed {currency}",
            locale=escrow_user["locale"]).format(currency=offer[new_currency])
        await tg.send_message(escrow_user["id"], answer)
        is_confirmed = await create_task(self.is_block_confirmed(
            block_num, op))
        if is_confirmed:
            await database.escrow.update_one(
                {"_id": offer["_id"]},
                {"$set": {
                    "trx_id": trx_id,
                    "unsent": True
                }})
            keyboard = InlineKeyboardMarkup()
            keyboard.add(
                InlineKeyboardButton(
                    i18n("sent", locale=other_user["locale"]),
                    callback_data="tokens_sent {}".format(offer["_id"]),
                ))
            answer = markdown.link(
                i18n("transaction_confirmed", locale=other_user["locale"]),
                self.trx_url(trx_id),
            )
            answer += "\n" + i18n("send {amount} {currency} {address}",
                                  locale=other_user["locale"]).format(
                                      amount=offer[f"sum_{new_currency}"],
                                      currency=offer[new_currency],
                                      address=markdown.escape_md(
                                          escrow_user["receive_address"]),
                                  )
            answer += "."
            await tg.send_message(
                other_user["id"],
                answer,
                reply_markup=keyboard,
                parse_mode=ParseMode.MARKDOWN,
            )
            return True

        await database.escrow.update_one(
            {"_id": offer["_id"]}, {"$set": {
                "transaction_time": time()
            }})
        answer = i18n("transaction_not_confirmed",
                      locale=escrow_user["locale"])
        answer += " " + i18n("try_again", locale=escrow_user["locale"])
        await tg.send_message(escrow_user["id"], answer)
        return False
Пример #21
0
def nicer(card: Card):
    return link(card.name, card.link)
Пример #22
0
from aiogram.utils.markdown import link, text

start = 'Привет, я бот, созданный для психологической помощи. Выбери действие.'

call = text(
    link('Запись в службу психологической помощи\n',
         'http://pozapisi.ru/day/psy/'),
    'Мы располагаемся по адресу: Санкт-Петербург, Менделеевская линия, д. 5 (здание Института истории и Института философии), во внутреннем дворе.\n\n',
    'Если Вы записались на консультацию к специалистам Кафедры медицинской психологии, то Ваша консультация будет проходить по адресу Наб.Макарова, д.6, каб. 109в.\n\n',
    'Телефоны: 363-65-01, 328-94-17\n', 'E-mail: [email protected]')
Пример #23
0
async def show_order(
    order: typing.Mapping[str, typing.Any],
    chat_id: int,
    user_id: int,
    message_id: typing.Optional[int] = None,
    location_message_id: typing.Optional[int] = None,
    show_id: bool = False,
    invert: typing.Optional[bool] = None,
    edit: bool = False,
    locale: typing.Optional[str] = None,
):
    """Send detailed order.

    :param order: Order document.
    :param chat_id: Telegram ID of chat to send message to.
    :param user_id: Telegram user ID of message receiver.
    :param message_id: Telegram ID of message to edit.
    :param location_message_id: Telegram ID of message with location object.
        It is deleted when **Hide** inline button is pressed.
    :param show_id: Add ID of order to the top.
    :param invert: Invert price.
    :param edit: Enter edit mode.
    :param locale: Locale of message receiver.
    """
    if locale is None:
        locale = i18n.ctx_locale.get()

    new_edit_msg = None
    if invert is None:
        user = await database.users.find_one({"id": user_id})
        invert = user.get("invert_order", False)
    else:
        user = await database.users.find_one_and_update(
            {"id": user_id}, {"$set": {
                "invert_order": invert
            }})
        if "edit" in user:
            if edit:
                if user["edit"]["field"] == "price":
                    new_edit_msg = i18n(
                        "new_price {of_currency} {per_currency}",
                        locale=locale)
                    if invert:
                        new_edit_msg = new_edit_msg.format(
                            of_currency=order["buy"],
                            per_currency=order["sell"])
                    else:
                        new_edit_msg = new_edit_msg.format(
                            of_currency=order["sell"],
                            per_currency=order["buy"])
            elif user["edit"]["order_message_id"] == message_id:
                await tg.delete_message(user["chat"],
                                        user["edit"]["message_id"])
                await database.users.update_one(
                    {"_id": user["_id"]},
                    {"$unset": {
                        "edit": True,
                        STATE_KEY: True
                    }})

    if location_message_id is None:
        if order.get("lat") is not None and order.get("lon") is not None:
            location_message = await tg.send_location(chat_id, order["lat"],
                                                      order["lon"])
            location_message_id = location_message.message_id
        else:
            location_message_id = -1

    header = ""
    if show_id:
        header += "ID: {}\n".format(markdown.code(order["_id"]))

    if order.get("archived"):
        header += markdown.bold(i18n("archived", locale=locale)) + "\n"

    creator = await database.users.find_one({"id": order["user_id"]})
    header += "{} ({}) ".format(
        markdown.link(creator["mention"],
                      types.User(id=creator["id"]).url),
        markdown.code(creator["id"]),
    )
    if invert:
        act = i18n("sells {sell_currency} {buy_currency}", locale=locale)
    else:
        act = i18n("buys {buy_currency} {sell_currency}", locale=locale)
    header += act.format(buy_currency=order["buy"],
                         sell_currency=order["sell"]) + "\n"

    lines = [header]
    field_names = {
        "sum_buy": i18n("buy_amount", locale=locale),
        "sum_sell": i18n("sell_amount", locale=locale),
        "price": i18n("price", locale=locale),
        "payment_system": i18n("payment_system", locale=locale),
        "duration": i18n("duration", locale=locale),
        "comments": i18n("comments", locale=locale),
    }
    lines_format: typing.Dict[str, typing.Optional[str]] = {}
    for name in field_names:
        lines_format[name] = None

    if "sum_buy" in order:
        lines_format["sum_buy"] = "{} {}".format(order["sum_buy"],
                                                 order["buy"])
    if "sum_sell" in order:
        lines_format["sum_sell"] = "{} {}".format(order["sum_sell"],
                                                  order["sell"])
    if "price_sell" in order:
        if invert:
            lines_format["price"] = "{} {}/{}".format(order["price_buy"],
                                                      order["buy"],
                                                      order["sell"])
        else:
            lines_format["price"] = "{} {}/{}".format(order["price_sell"],
                                                      order["sell"],
                                                      order["buy"])
    if "payment_system" in order:
        lines_format["payment_system"] = order["payment_system"]
    if "duration" in order:
        lines_format["duration"] = "{} - {}".format(
            datetime.utcfromtimestamp(
                order["start_time"]).strftime("%d.%m.%Y"),
            datetime.utcfromtimestamp(
                order["expiration_time"]).strftime("%d.%m.%Y"),
        )
    if "comments" in order:
        lines_format["comments"] = "«{}»".format(order["comments"])

    keyboard = types.InlineKeyboardMarkup(row_width=6)

    keyboard.row(
        types.InlineKeyboardButton(
            i18n("invert", locale=locale),
            callback_data="{} {} {} {}".format(
                "revert" if invert else "invert",
                order["_id"],
                location_message_id,
                int(edit),
            ),
        ))

    if edit and creator["id"] == user_id:
        buttons = []
        for i, (field, value) in enumerate(lines_format.items()):
            if value is not None:
                lines.append(f"{i + 1}. {field_names[field]} {value}")
            else:
                lines.append(f"{i + 1}. {field_names[field]} -")
            buttons.append(
                types.InlineKeyboardButton(
                    f"{i + 1}",
                    callback_data="edit {} {} {} 0".format(
                        order["_id"], field, location_message_id),
                ))

        keyboard.add(*buttons)
        keyboard.row(
            types.InlineKeyboardButton(
                i18n("finish", locale=locale),
                callback_data="{} {} {} 0".format(
                    "invert" if invert else "revert", order["_id"],
                    location_message_id),
            ))

    else:
        for field, value in lines_format.items():
            if value is not None:
                lines.append(field_names[field] + " " + value)

        keyboard.row(
            types.InlineKeyboardButton(
                i18n("similar", locale=locale),
                callback_data="similar {}".format(order["_id"]),
            ),
            types.InlineKeyboardButton(
                i18n("match", locale=locale),
                callback_data="match {}".format(order["_id"]),
            ),
        )

        if creator["id"] == user_id:
            keyboard.row(
                types.InlineKeyboardButton(
                    i18n("edit", locale=locale),
                    callback_data="{} {} {} 1".format(
                        "invert" if invert else "revert",
                        order["_id"],
                        location_message_id,
                    ),
                ),
                types.InlineKeyboardButton(
                    i18n("delete", locale=locale),
                    callback_data="delete {} {}".format(
                        order["_id"], location_message_id),
                ),
            )
            keyboard.row(
                types.InlineKeyboardButton(
                    i18n("unarchive", locale=locale) if order.get("archived")
                    else i18n("archive", locale=locale),
                    callback_data="archive {} {}".format(
                        order["_id"], location_message_id),
                ),
                types.InlineKeyboardButton(
                    i18n("change_duration", locale=locale),
                    callback_data="edit {} duration {} 1".format(
                        order["_id"], location_message_id),
                ),
            )
        elif "price_sell" in order:
            if (get_escrow_instance(order["buy"]) is not None
                    or get_escrow_instance(order["sell"]) is not None):
                keyboard.row(
                    types.InlineKeyboardButton(
                        i18n("escrow", locale=locale),
                        callback_data="escrow {} sum_buy 0".format(
                            order["_id"]),
                    ))

        keyboard.row(
            types.InlineKeyboardButton(
                i18n("hide", locale=locale),
                callback_data="hide {}".format(location_message_id),
            ))

    answer = "\n".join(lines)

    if message_id is not None:
        await tg.edit_message_text(
            answer,
            chat_id,
            message_id,
            reply_markup=keyboard,
            parse_mode=types.ParseMode.MARKDOWN,
            disable_web_page_preview=True,
        )
        if new_edit_msg is not None:
            keyboard = types.InlineKeyboardMarkup()
            keyboard.row(
                types.InlineKeyboardButton(i18n("unset", locale=locale),
                                           callback_data="unset"))
            await tg.edit_message_text(
                new_edit_msg,
                chat_id,
                user["edit"]["message_id"],
                reply_markup=keyboard,
            )
    else:
        await tg.send_message(
            chat_id,
            answer,
            reply_markup=keyboard,
            parse_mode=types.ParseMode.MARKDOWN,
            disable_web_page_preview=True,
        )