예제 #1
0
async def price_ask(
    call: types.CallbackQuery, order: Mapping[str, Any], price_currency: str
):
    """Edit currency of price in message to ``price_currency`` field value."""
    if price_currency == "sell":
        answer = i18n("ask_buy_price {of_currency} {per_currency}").format(
            of_currency=order["sell"], per_currency=order["buy"]
        )
        callback_command = "buy"
    else:
        answer = (
            i18n("ask_sell_price {of_currency} {per_currency}").format(
                of_currency=order["buy"], per_currency=order["sell"]
            ),
        )
        callback_command = "sell"

    buttons = await inline_control_buttons()
    callback_data = f"price {callback_command}"
    buttons.insert(
        0, [InlineKeyboardButton(i18n("invert"), callback_data=callback_data)],
    )
    await tg.edit_message_text(
        answer,
        call.message.chat.id,
        call.message.message_id,
        reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons),
    )
예제 #2
0
async def choose_sell(message: types.Message, state: FSMContext):
    """Set currency user wants to sell and ask for price."""
    if message.text.startswith(emojize(":fast_reverse_button:")):
        await OrderCreation.buy.set()
        await tg.send_message(
            message.chat.id,
            i18n("ask_buy_currency"),
            reply_markup=whitelist.currency_keyboard("buy"),
        )
        return
    elif message.text.startswith(emojize(":x:")):
        await cancel_order_creation(message.from_user.id, message.chat.id)
        return

    match = await match_currency("sell", message)
    if not match:
        return

    order = await database.creation.find_one_and_update(
        {"user_id": message.from_user.id, "buy": {"$ne": match}},
        {"$set": {"sell": match, "price_currency": "sell"}},
        return_document=ReturnDocument.AFTER,
    )
    if not order:
        await tg.send_message(
            message.chat.id,
            i18n("same_currency_error"),
            reply_markup=whitelist.currency_keyboard("sell"),
        )
        return

    await set_price_state(message, order)
예제 #3
0
async def change_state(call: types.CallbackQuery, state: FSMContext):
    """React to back/skip button query."""
    args = call.data.split()
    query_state_name = args[1]
    direction = args[2]

    state_name = await state.get_state()
    if state_name != query_state_name:
        return await call.answer(i18n("wrong_button"))

    if state_name in OrderCreation.all_states_names:
        if direction == "back":
            new_state = await OrderCreation.previous()
        elif direction == "skip":
            new_state = await OrderCreation.next()
        handler = state_handlers.get(new_state)
        if handler:
            error = await handler(call)
            if not error:
                return await call.answer()
        await state.set_state(state_name)

    if direction == "back":
        answer = i18n("back_error")
    elif direction == "skip":
        answer = i18n("skip_error")
    return await call.answer(answer)
예제 #4
0
async def delete_button(call: types.CallbackQuery, order: OrderType):
    """React to "Delete" button by asking user to confirm deletion."""
    args = call.data.split()
    location_message_id = int(args[2])
    show_id = call.message.text.startswith("ID")

    keyboard = types.InlineKeyboardMarkup()
    keyboard.row(
        types.InlineKeyboardButton(
            i18n("totally_sure"),
            callback_data="confirm_delete {} {}".format(
                order["_id"], location_message_id
            ),
        )
    )
    keyboard.row(
        types.InlineKeyboardButton(
            i18n("no"),
            callback_data="revert {} {} 0 {}".format(
                order["_id"], location_message_id, int(show_id)
            ),
        )
    )

    await tg.edit_message_text(
        i18n("delete_order_confirmation"),
        call.message.chat.id,
        call.message.message_id,
        reply_markup=keyboard,
    )
예제 #5
0
async def set_name(message: types.Message, offer: EscrowOffer):
    """Set fiat sender's name on card and ask for first and last 4 digits."""
    name = message.text.split()
    if len(name) != 3:
        await tg.send_message(
            message.chat.id,
            i18n("wrong_word_count {word_count}").format(word_count=3),
        )
        return
    name[2] = name[2][0] + "."  # Leaving the first letter of surname with dot

    if offer.type == "buy":
        user_field = "counter"
        currency = offer.sell
    else:
        user_field = "init"
        currency = offer.buy

    await offer.update_document(
        {"$set": {
            f"{user_field}.name": " ".join(name).upper()
        }})
    await tg.send_message(
        message.chat.id,
        i18n("send_first_and_last_4_digits_of_card_number {currency}").format(
            currency=currency),
    )
    await states.Escrow.send_card_number.set()
예제 #6
0
async def claim_currency(call: types.CallbackQuery):
    """Set cashback currency and suggest last escrow address."""
    currency = call.data.split()[1]
    cursor = (database.cashback.find({
        "id": call.from_user.id,
        "currency": currency,
        "address": {
            "$ne": None
        }
    }).sort("time", pymongo.DESCENDING).limit(1))
    last_cashback = await cursor.to_list(length=1)
    if last_cashback:
        address = last_cashback[0]["address"]
        keyboard = InlineKeyboardMarkup(row_width=1)
        keyboard.add(
            InlineKeyboardButton(
                i18n("confirm_cashback_address"),
                callback_data=f"claim_transfer {currency} {address}",
            ),
            InlineKeyboardButton(
                i18n("custom_cashback_address"),
                callback_data=f"custom_cashback_address {currency}",
            ),
        )
        await call.answer()
        await tg.edit_message_text(
            i18n("use_cashback_address {address}").format(
                address=markdown.code(address)),
            call.message.chat.id,
            call.message.message_id,
            reply_markup=keyboard,
            parse_mode=ParseMode.MARKDOWN,
        )
    else:
        return await custom_cashback_address(call)
예제 #7
0
async def whitelisting_request(call: types.CallbackQuery):
    """Send whitelisting request to support or increment requests count."""
    currency = call.data.split()[1]
    request = await database.whitelisting_requests.find_one_and_update(
        {"_id": currency},
        {"$addToSet": {"users": call.from_user.id}},
        upsert=True,
        return_document=ReturnDocument.BEFORE,
    )

    double_request = False
    if request:
        if call.from_user.id in request["users"]:
            double_request = True
        else:
            support_text = emojize(":label: #whitelisting_request {} - {}.").format(
                currency, len(request["users"]) + 1
            )
            if len(request["users"]) == 1:
                message = await tg.send_message(config.SUPPORT_CHAT_ID, support_text)
                await database.whitelisting_requests.update_one(
                    {"_id": request["_id"]},
                    {"$set": {"message_id": message.message_id}},
                )
            else:
                await tg.edit_message_text(
                    support_text, config.SUPPORT_CHAT_ID, request["message_id"],
                )

    await call.answer()
    await tg.send_message(
        call.message.chat.id,
        i18n("double_request") if double_request else i18n("request_sent"),
    )
예제 #8
0
async def choose_duration(message: types.Message, state: FSMContext):
    """Set duration and ask for comments."""
    try:
        duration = int(message.text)
        if duration <= 0:
            raise ValueError
    except ValueError:
        await tg.send_message(message.chat.id, i18n("send_natural_number"))
        return

    if duration > config.ORDER_DURATION_LIMIT:
        await tg.send_message(
            message.chat.id,
            i18n("exceeded_duration_limit {limit}").format(
                limit=config.ORDER_DURATION_LIMIT),
        )
        return

    await database.creation.update_one({"user_id": message.from_user.id},
                                       {"$set": {
                                           "duration": duration
                                       }})

    await OrderCreation.comments.set()
    await tg.send_message(
        message.chat.id,
        i18n("ask_comments"),
        reply_markup=InlineKeyboardMarkup(
            inline_keyboard=await inline_control_buttons()),
    )
예제 #9
0
async def choose_bank(call: types.CallbackQuery, offer: EscrowOffer):
    """Set chosen bank and continue.

    Because bank is chosen by initiator, ask for receive address if
    they receive escrow asset.
    """
    bank = call.data.split()[2]
    if bank not in SUPPORTED_BANKS:
        await call.answer(i18n("bank_not_supported"))
        return

    update_dict = {"bank": bank}
    await call.answer()
    update_dict["pending_input_from"] = call.from_user.id
    await offer.update_document({"$set": update_dict})
    if offer.sell == "RUB":
        await tg.send_message(
            call.message.chat.id,
            i18n("send_first_and_last_4_digits_of_card_number {currency}").
            format(currency=offer.sell),
        )
        await states.Escrow.receive_card_number.set()
    else:
        await tg.send_message(
            call.message.chat.id,
            i18n("ask_address {currency}").format(currency=offer.sell),
        )
        await states.Escrow.receive_address.set()
예제 #10
0
async def get_referral_link(message: types.Message):
    """Send user's referral link and generate if it doesn't exist."""
    user = database_user.get()
    code = user.get("referral_code")
    if code is None:
        while True:
            cryptogen = SystemRandom()
            code = "".join(cryptogen.choice(ascii_lowercase) for _ in range(7))
            try:
                await database.users.update_one(
                    {"_id": user["_id"]}, {"$set": {
                        "referral_code": code
                    }})
            except DuplicateKeyError:
                continue
            else:
                break
    me = await tg.me
    answer = i18n("referral_share {link}").format(
        link=f"https://t.me/{me.username}?start={code}")
    if message.from_user.username:
        answer += "\n" + i18n("referral_share_alias {link}").format(
            link=
            f"https://t.me/{me.username}?start=_{message.from_user.username}")
    await tg.send_message(
        message.chat.id,
        answer,
        disable_web_page_preview=True,
        reply_markup=start_keyboard(),
    )
예제 #11
0
async def choose_buy_gateway(message: types.Message, state: FSMContext):
    """Set gateway of buy currency and ask for sell currency."""
    if message.text.startswith(emojize(":fast_reverse_button:")):
        await OrderCreation.previous()
        await tg.send_message(
            message.chat.id,
            i18n("ask_buy_currency"),
            reply_markup=whitelist.currency_keyboard("buy"),
        )
        return
    elif message.text.startswith(emojize(":x:")):
        await cancel_order_creation(message.from_user.id, message.chat.id)
        return
    elif not message.text.startswith(emojize(":fast_forward:")):
        gateway_result = await get_currency_with_gateway("buy", message)
        if not gateway_result:
            return
        order, currency = gateway_result
        await database.creation.update_one({"_id": order["_id"]},
                                           {"$set": {
                                               "buy": currency
                                           }})

    await OrderCreation.sell.set()
    await tg.send_message(
        message.chat.id,
        i18n("ask_sell_currency"),
        reply_markup=whitelist.currency_keyboard("sell"),
    )
예제 #12
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()
예제 #13
0
async def archive_button(call: types.CallbackQuery, order: OrderType):
    """React to "Archive" or "Unarchive" button by flipping archived flag."""
    args = call.data.split()

    archived = order.get("archived")
    if archived:
        update_dict = {"$unset": {"archived": True}, "$set": {"notify": True}}
    else:
        update_dict = {"$set": {"archived": True, "notify": False}}
    order = await database.orders.find_one_and_update(
        {"_id": ObjectId(args[1]), "user_id": call.from_user.id},
        update_dict,
        return_document=pymongo.ReturnDocument.AFTER,
    )
    if not order:
        await call.answer(
            i18n("unarchive_order_error") if archived else i18n("archive_order_error")
        )
        return

    await call.answer()
    await show_order(
        order,
        call.message.chat.id,
        call.from_user.id,
        message_id=call.message.message_id,
        location_message_id=int(args[2]),
        show_id=call.message.text.startswith("ID"),
    )
예제 #14
0
async def handle_start_command(message: types.Message, state: FSMContext):
    """Handle /start.

    Ask for language if user is new or show menu.
    """
    user = {"id": message.from_user.id, "chat": message.chat.id}
    result = await database.users.update_one(user, {"$setOnInsert": user}, upsert=True)

    if not result.matched_count:
        keyboard = InlineKeyboardMarkup()
        for language in i18n.available_locales:
            keyboard.row(
                InlineKeyboardButton(
                    Locale(language).display_name,
                    callback_data="locale {}".format(language),
                )
            )
        await tg.send_message(
            message.chat.id, i18n("choose_language"), reply_markup=keyboard
        )
        return

    await state.finish()
    await tg.send_message(
        message.chat.id, i18n("help_message"), reply_markup=start_keyboard()
    )
예제 #15
0
async def handle_create(message: types.Message, state: FSMContext):
    """Start order creation by asking user for currency they want to buy."""
    current_time = time()
    user_orders = await database.orders.count_documents(
        {
            "user_id": message.from_user.id,
            "start_time": {"$gt": current_time - config.ORDERS_LIMIT_HOURS * 3600},
        }
    )
    if user_orders >= config.ORDERS_LIMIT_COUNT:
        await tg.send_message(
            message.chat.id,
            i18n("exceeded_order_creation_time_limit {orders} {hours}").format(
                orders=config.ORDERS_LIMIT_COUNT, hours=config.ORDERS_LIMIT_HOURS
            ),
        )
        return

    creation = {"user_id": message.from_user.id}
    await database.creation.find_one_and_replace(creation, creation, upsert=True)
    await states.OrderCreation.first()

    await tg.send_message(
        message.chat.id,
        i18n("ask_buy_currency"),
        reply_markup=whitelist.currency_keyboard("buy"),
    )
예제 #16
0
async def sum_handler(call: types.CallbackQuery):
    """Ask for sum currency."""
    order = await database.creation.find_one_and_update(
        {"user_id": call.from_user.id}, {"$unset": {
            "price_currency": True
        }})

    if not order:
        await call.answer(i18n("no_creation"))
        return True

    keyboard = InlineKeyboardMarkup()
    keyboard.add(
        InlineKeyboardButton(order["buy"], callback_data="sum buy"),
        InlineKeyboardButton(order["sell"], callback_data="sum sell"),
    )
    for row in await inline_control_buttons():
        keyboard.row(*row)

    await tg.edit_message_text(
        i18n("ask_sum_currency"),
        call.message.chat.id,
        call.message.message_id,
        reply_markup=keyboard,
    )
예제 #17
0
async def search_by_creator(message: types.Message, state: FSMContext):
    """Search orders by creator.

    Creator is indicated with username (with or without @) or user ID
    after **/creator** or **/c** in message text.

    In contrast to usernames and user IDs, names aren't unique and
    therefore not supported.
    """
    query: typing.Dict[str, typing.Any] = {
        "$or": [{
            "archived": {
                "$exists": False
            }
        }, {
            "archived": False
        }],
        "expiration_time": {
            "$gt": time()
        },
    }
    source = message.text.split()
    try:
        creator = source[1]
        if creator.isdigit():
            query["user_id"] = int(creator)
        else:
            mention_regexp = f"^{creator}$" if creator[
                0] == "@" else f"^@{creator}$"
            user = await database.users.find_one({
                "mention":
                re.compile(mention_regexp, re.IGNORECASE),
                "has_username":
                True,
            })
            if user:
                query["user_id"] = user["id"]
            else:
                await tg.send_message(message.chat.id, i18n("user_not_found"))
                return
    except IndexError:
        await tg.send_message(
            message.chat.id,
            i18n("no_user_argument"),
        )
        return

    cursor = database.orders.find(query).sort("start_time", DESCENDING)
    quantity = await database.orders.count_documents(query)
    await state.finish()
    await orders_list(cursor,
                      message.chat.id,
                      0,
                      quantity,
                      "orders",
                      user_id=message.from_user.id)
예제 #18
0
async def choose_sell_gateway(message: types.Message, state: FSMContext):
    """Set gateway of sell currency and ask for price."""
    if message.text.startswith(emojize(":fast_reverse_button:")):
        await OrderCreation.previous()
        await tg.send_message(
            message.chat.id,
            i18n("ask_sell_currency"),
            reply_markup=whitelist.currency_keyboard("sell"),
        )
        return
    elif message.text.startswith(emojize(":x:")):
        await cancel_order_creation(message.from_user.id, message.chat.id)
        return

    same_gateway = False
    if not message.text.startswith(emojize(":fast_forward:")):
        gateway_result = await get_currency_with_gateway("sell", message)
        if not gateway_result:
            return
        order, currency = gateway_result
        if currency == order["buy"]:
            same_gateway = True
        else:
            await database.creation.update_one(
                {"_id": order["_id"]},
                {"$set": {
                    "sell": currency,
                    "price_currency": "sell"
                }},
            )
    else:
        order = await database.creation.find_one_and_update(
            {
                "user_id": message.from_user.id,
                "$expr": {
                    "$ne": ["$buy", "$sell"]
                }
            },
            {"$set": {
                "price_currency": "sell"
            }},
            return_document=ReturnDocument.AFTER,
        )
        if not order:
            order = await database.creation.find_one(
                {"user_id": message.from_user.id})
            same_gateway = True

    if same_gateway:
        await tg.send_message(
            message.chat.id,
            i18n("same_gateway_error"),
            reply_markup=whitelist.gateway_keyboard(order["sell"], "sell"),
        )
    else:
        await set_price_state(message, order)
예제 #19
0
async def help_command(message: types.Message):
    """Handle request to support."""
    await states.asking_support.set()
    await tg.send_message(
        message.chat.id,
        i18n("request_question"),
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[[
            InlineKeyboardButton(i18n("cancel"), callback_data="unhelp")
        ]]),
    )
예제 #20
0
async def decline_offer(call: types.CallbackQuery, offer: EscrowOffer):
    """React to counteragent declining offer."""
    offer.react_time = time()
    await offer.delete_document()
    await tg.send_message(
        offer.init["id"],
        i18n("escrow_offer_declined", locale=offer.init["locale"]),
    )
    await call.answer()
    await tg.send_message(call.message.chat.id, i18n("offer_declined"))
예제 #21
0
def gateway_keyboard(currency: str, currency_type: str) -> ReplyKeyboardMarkup:
    """Get keyboard with gateways of ``currency`` from whitelist."""
    keyboard = ReplyKeyboardMarkup(row_width=5,
                                   one_time_keyboard=currency_type == "sell")
    keyboard.add(*[KeyboardButton(g) for g in CRYPTOCURRENCY[currency]])
    keyboard.row(
        KeyboardButton(emojize(":fast_reverse_button: ") + i18n("back")),
        KeyboardButton(emojize(":fast_forward: ") + i18n("without_gateway")),
        KeyboardButton(emojize(":x: ") + i18n("cancel")),
    )
    return keyboard
예제 #22
0
async def toggle_escrow(message: types.Message):
    """Toggle escrow availability.

    This command makes creation of new escrow offers unavailable if
    escrow is enabled, and makes it available if it's disabled.
    """
    config.ESCROW_ENABLED = not config.ESCROW_ENABLED
    if config.ESCROW_ENABLED:
        await tg.send_message(message.chat.id, i18n("escrow_enabled"))
    else:
        await tg.send_message(message.chat.id, i18n("escrow_disabled"))
예제 #23
0
async def set_price_state(message: types.Message, order: Mapping[str, Any]):
    """Ask for price."""
    await OrderCreation.price.set()
    buttons = await inline_control_buttons(back=False)
    buttons.insert(
        0, [InlineKeyboardButton(i18n("invert"), callback_data="price buy")])
    await tg.send_message(
        message.chat.id,
        i18n("ask_buy_price {of_currency} {per_currency}").format(
            of_currency=order["sell"], per_currency=order["buy"]),
        reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons),
    )
예제 #24
0
async def set_escrow_sum(message: types.Message, offer: EscrowOffer):
    """Set sum and ask for fee payment agreement."""
    try:
        offer_sum = money(message.text)
    except MoneyValueError as exception:
        await tg.send_message(message.chat.id, str(exception))
        return

    order = await database.orders.find_one({"_id": offer.order})
    order_sum = order.get(offer.sum_currency)
    if order_sum and offer_sum > order_sum.to_decimal():
        await tg.send_message(message.chat.id, i18n("exceeded_order_sum"))
        return

    update_dict = {offer.sum_currency: Decimal128(offer_sum)}
    new_currency = "sell" if offer.sum_currency == "sum_buy" else "buy"
    update_dict[f"sum_{new_currency}"] = Decimal128(
        normalize(offer_sum * order[f"price_{new_currency}"].to_decimal()))
    escrow_sum = update_dict[f"sum_{offer.type}"]
    escrow_fee = Decimal(config.ESCROW_FEE_PERCENTS) / Decimal("100")
    update_dict["sum_fee_up"] = Decimal128(
        normalize(escrow_sum.to_decimal() * (Decimal("1") + escrow_fee)))
    update_dict["sum_fee_down"] = Decimal128(
        normalize(escrow_sum.to_decimal() * (Decimal("1") - escrow_fee)))
    offer = replace(offer, **update_dict)  # type: ignore

    if offer.sum_currency == offer.type:
        insured = await get_insurance(offer)
        update_dict["insured"] = Decimal128(insured)
        if offer_sum > insured:
            keyboard = InlineKeyboardMarkup()
            keyboard.add(
                InlineKeyboardButton(
                    i18n("continue"),
                    callback_data=f"accept_insurance {offer._id}"),
                InlineKeyboardButton(i18n("cancel"),
                                     callback_data=f"init_cancel {offer._id}"),
            )
            answer = i18n("exceeded_insurance {amount} {currency}").format(
                amount=insured, currency=offer.escrow)
            answer += "\n" + i18n("exceeded_insurance_options")
            await tg.send_message(message.chat.id,
                                  answer,
                                  reply_markup=keyboard)
    else:
        await ask_fee(message.from_user.id, message.chat.id, offer)

    await offer.update_document({
        "$set": update_dict,
        "$unset": {
            "sum_currency": True
        }
    })
예제 #25
0
async def final_offer_confirmation(call: types.CallbackQuery,
                                   offer: EscrowOffer):
    """Ask not escrow asset receiver to confirm transfer."""
    if not offer.unsent:
        await call.answer(i18n("transfer_already_confirmed"))
        return
    await offer.update_document({"$unset": {"unsent": True}})

    if offer.type == "buy":
        confirm_user = offer.init
        other_user = offer.counter
        currency = offer.sell
    elif offer.type == "sell":
        confirm_user = offer.counter
        other_user = offer.init
        currency = offer.buy

    keyboard = InlineKeyboardMarkup()
    keyboard.add(
        InlineKeyboardButton(
            i18n("yes", locale=confirm_user["locale"]),
            callback_data=f"escrow_complete {offer._id}",
        ))
    reply = await tg.send_message(
        confirm_user["id"],
        i18n("receiving_confirmation {currency} {user}",
             locale=confirm_user["locale"]).format(
                 currency=currency, user=other_user["send_address"]),
        reply_markup=keyboard,
    )
    keyboard.add(
        InlineKeyboardButton(
            i18n("no", locale=confirm_user["locale"]),
            callback_data=f"escrow_validate {offer._id}",
        ))
    await call_later(
        60 * 10,
        edit_keyboard,
        offer._id,
        confirm_user["id"],
        reply.message_id,
        keyboard,
    )
    await call.answer()
    await tg.send_message(
        other_user["id"],
        i18n(
            "complete_escrow_promise",
            locale=other_user["locale"],
        ),
        reply_markup=start_keyboard(),
    )
예제 #26
0
def start_keyboard() -> types.ReplyKeyboardMarkup:
    """Create reply keyboard with main menu."""
    keyboard = types.ReplyKeyboardMarkup(resize_keyboard=True, row_width=2)
    keyboard.add(
        types.KeyboardButton(
            emojize(":heavy_plus_sign: ") + i18n("create_order")),
        types.KeyboardButton(
            emojize(":bust_in_silhouette: ") + i18n("my_orders")),
        types.KeyboardButton(emojize(":closed_book: ") + i18n("order_book")),
        types.KeyboardButton(emojize(":abcd: ") + i18n("language")),
        types.KeyboardButton(emojize(":question: ") + i18n("support")),
    )
    return keyboard
예제 #27
0
async def cancel_order_creation(user_id: int, chat_id: int):
    """Cancel order creation."""
    await dp.get_current().current_state().finish()

    order = await database.creation.delete_one({"user_id": user_id})
    if not order.deleted_count:
        await tg.send_message(
            chat_id, i18n("no_creation"), reply_markup=start_keyboard(),
        )
        return True

    await tg.send_message(
        chat_id, i18n("order_cancelled"), reply_markup=start_keyboard()
    )
예제 #28
0
def currency_keyboard(currency_type: str) -> ReplyKeyboardMarkup:
    """Get keyboard with currencies from whitelists."""
    keyboard = ReplyKeyboardMarkup(row_width=5,
                                   one_time_keyboard=currency_type == "sell")
    keyboard.row(*[KeyboardButton(c) for c in FIAT])
    keyboard.add(*[KeyboardButton(c) for c in CRYPTOCURRENCY])
    cancel_button = KeyboardButton(emojize(":x: ") + i18n("cancel"))
    if currency_type == "sell":
        keyboard.row(
            KeyboardButton(emojize(":fast_reverse_button: ") + i18n("back")),
            cancel_button,
        )
    else:
        keyboard.row(cancel_button)
    return keyboard
예제 #29
0
async def set_receive_card_number(message: types.Message, offer: EscrowOffer):
    """Create address from first and last 4 digits of card number and ask send address.

    First and last 4 digits of card number are sent by fiat receiver,
    so their send address is escrow asset address.
    """
    card_number = await get_card_number(message.text, message.chat.id)
    if not card_number:
        return

    if message.from_user.id == offer.init["id"]:
        user_field = "init"
    else:
        user_field = "counter"

    await offer.update_document({
        "$set": {
            f"{user_field}.receive_address": ("*" * 8).join(card_number)
        }
    })
    await tg.send_message(
        message.chat.id,
        i18n("ask_address {currency}").format(currency=offer.escrow),
    )
    await states.Escrow.send_address.set()
예제 #30
0
async def get_card_number(
        text: str, chat_id: int) -> typing.Optional[typing.Tuple[str, str]]:
    """Parse first and last 4 digits from card number in ``text``.

    If parsing is unsuccessful, send warning to ``chat_id`` and return
    None. Otherwise return tuple of first and last 4 digits of card number.
    """
    if len(text) < 8:
        await tg.send_message(chat_id, i18n("send_at_least_8_digits"))
        return None
    first = text[:4]
    last = text[-4:]
    if not first.isdigit() or not last.isdigit():
        await tg.send_message(chat_id, i18n("digits_parsing_error"))
        return None
    return (first, last)