예제 #1
0
    def test_no_separators(self, m):
        self.assertEqual(
            RegexParser("usdrub", 1, "en", "USD", False).parse(),
            PriceRequest(
                amount=None,
                currency="USD",
                to_currency="RUB",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.UNKNOWN,
            ),
        )

        self.assertEqual(
            RegexParser("scburst", 1, "en", "USD", False).parse(),
            PriceRequest(
                amount=None,
                currency="SC",
                to_currency="BURST",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.UNKNOWN,
            ),
        )

        self.assertEqual(
            RegexParser("burstsc", 1, "en", "USD", False).parse(),
            PriceRequest(
                amount=None,
                currency="BURST",
                to_currency="SC",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.UNKNOWN,
            ),
        )

        self.assertEqual(
            RegexParser("scsc", 1, "en", "USD", False).parse(),
            PriceRequest(
                amount=None,
                currency="SC",
                to_currency="SC",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.UNKNOWN,
            ),
        )

        self.assertEqual(
            RegexParser("burstburst", 1, "en", "USD", False).parse(),
            PriceRequest(
                amount=None,
                currency="BURST",
                to_currency="BURST",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.UNKNOWN,
            ),
        )
예제 #2
0
    def test_ok_digits_l2r(self, m):
        # pair, space separator
        self.assertEqual(
            RegexParser("100.20 usd rub", 1, "en", "USD", True).parse(),
            PriceRequest(
                amount=Decimal("100.20"),
                currency="USD",
                to_currency="RUB",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.LEFT2RIGHT,
            ),
        )

        # single from, space separator
        self.assertEqual(
            RegexParser("100.20 rub", 1, "en", "USD", True).parse(),
            PriceRequest(
                amount=Decimal("100.20"),
                currency="RUB",
                to_currency="USD",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.LEFT2RIGHT,
            ),
        )

        # single from, no space separator
        self.assertEqual(
            RegexParser("100.20rub", 1, "en", "USD", True).parse(),
            PriceRequest(
                amount=Decimal("100.20"),
                currency="RUB",
                to_currency="USD",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.LEFT2RIGHT,
            ),
        )

        # single to, no space separator
        self.assertEqual(
            RegexParser("100.20rub", 1, "en", "USD", False).parse(),
            PriceRequest(
                amount=Decimal("100.20"),
                currency="USD",
                to_currency="RUB",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.LEFT2RIGHT,
            ),
        )
    def parse(self) -> PriceRequest:
        text = self.text

        obj = AMOUNT_PATTERN_COMPILED.match(text)
        if not obj:
            raise WrongFormatException

        amount = obj[0]

        if amount:
            amount = parse_amount(amount, self.locale)

        last_request = (Session.query(ChatRequests).filter_by(
            chat_id=self.chat_id).order_by(
                ChatRequests.modified_at.desc()).first())

        if not last_request:
            raise WrongFormatException

        locale = Locale(self.locale)

        if locale.character_order == "right-to-left":
            direction_writing = DirectionWriting.RIGHT2LEFT
        else:
            direction_writing = DirectionWriting.LEFT2RIGHT

        return PriceRequest(
            amount=amount,
            currency=last_request.from_currency.code,
            to_currency=last_request.to_currency.code,
            parser_name=self.name,
            direction_writing=direction_writing,
        )
예제 #4
0
    def test_ok_pair_no_digits(self, m):
        # pair low case, no digits, space separator
        self.assertEqual(
            RegexParser("usd rub", 1, "en", "USD", False).parse(),
            PriceRequest(
                amount=None,
                currency="USD",
                to_currency="RUB",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.UNKNOWN,
            ),
        )
        # pair upper case, no digits, space separator
        self.assertEqual(
            RegexParser("USD EUR", 1, "en", "USD", False).parse(),
            PriceRequest(
                amount=None,
                currency="USD",
                to_currency="EUR",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.UNKNOWN,
            ),
        )

        # pair min and max len, no digits, space separator
        self.assertEqual(
            RegexParser("sc burst", 1, "en", "USD", False).parse(),
            PriceRequest(
                amount=None,
                currency="SC",
                to_currency="BURST",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.UNKNOWN,
            ),
        )

        # pair min and max len, no digits, space separator
        self.assertEqual(
            RegexParser("burst sc", 1, "en", "USD", False).parse(),
            PriceRequest(
                amount=None,
                currency="BURST",
                to_currency="SC",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.UNKNOWN,
            ),
        )
예제 #5
0
    def test_ok_separators(self, m):
        self.assertEqual(
            RegexParser("usd to rub", 1, "en", "USD", False).parse(),
            PriceRequest(
                amount=None,
                currency="USD",
                to_currency="RUB",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.UNKNOWN,
            ),
        )

        self.assertEqual(
            RegexParser("usd=rub", 1, "en", "USD", False).parse(),
            PriceRequest(
                amount=None,
                currency="USD",
                to_currency="RUB",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.UNKNOWN,
            ),
        )

        self.assertEqual(
            RegexParser("100.20 usd in rub", 1, "en", "USD", True).parse(),
            PriceRequest(
                amount=Decimal("100.20"),
                currency="USD",
                to_currency="RUB",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.LEFT2RIGHT,
            ),
        )

        self.assertEqual(
            RegexParser("rub = usd 100.20", 1, "en", "USD", True).parse(),
            PriceRequest(
                amount=Decimal("100.20"),
                currency="USD",
                to_currency="RUB",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.RIGHT2LEFT,
            ),
        )
예제 #6
0
    def test_ok_single_no_digits(self, m):
        # single to, no digits
        self.assertEqual(
            RegexParser("rub", 1, "en", "USD", False).parse(),
            PriceRequest(
                amount=None,
                currency="USD",
                to_currency="RUB",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.UNKNOWN,
            ),
        )

        # single from, no digits
        self.assertEqual(
            RegexParser("rub", 1, "en", "USD", True).parse(),
            PriceRequest(
                amount=None,
                currency="RUB",
                to_currency="USD",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.UNKNOWN,
            ),
        )
    def test_locale(self):
        prr = PriceRequestResult(
            price_request=PriceRequest(
                amount=None,
                currency="USD",
                to_currency="EUR",
                parser_name="test-parser",
                direction_writing=DirectionWriting.UNKNOWN,
            ),
            exchanges=["test-exchanger"],
            rate=Decimal("1.5"),
            rate_open=Decimal("1.3"),
            low24h=Decimal("1.2"),
            high24h=Decimal("1.6"),
            last_trade_at=datetime.now(),
        )

        fpr = FormatPriceRequestResult(prr, "en")

        self.assertTrue(fpr.is_rate_diff_available())
        self.assertFalse(fpr.is_convert_mode())
        self.assertEqual(
            fpr.get(),
            "*USD EUR* 1.5 ⬆️\n+0.2 (+15.38%)\n*Low*: 1.2 *High*: 1.6\n_17 March, 22:14 UTC_\ntest-exchanger 📡",
        )

        fpr = FormatPriceRequestResult(prr, "de")

        self.assertTrue(fpr.is_rate_diff_available())
        self.assertFalse(fpr.is_convert_mode())
        self.assertEqual(
            fpr.get(),
            "*USD EUR* 1,5 ⬆️\n+0,2 (+15,38%)\n*Low*: 1,2 *High*: 1,6\n_17 March, 22:14 UTC_\ntest-exchanger 📡",
        )

        fpr = FormatPriceRequestResult(prr, "zh-hans-sg")

        self.assertTrue(fpr.is_rate_diff_available())
        self.assertFalse(fpr.is_convert_mode())
        self.assertEqual(
            fpr.get(),
            "*USD EUR* 1.5 ⬆️\n+0.2 (+15.38%)\n*Low*: 1.2 *High*: 1.6\n_17 March, 22:14 UTC_\ntest-exchanger 📡",
        )
    def test_price_mode_no_diff(self):
        prr = PriceRequestResult(
            price_request=PriceRequest(
                amount=None,
                currency="USD",
                to_currency="EUR",
                parser_name="test-parser",
                direction_writing=DirectionWriting.UNKNOWN,
            ),
            exchanges=["test-exchanger"],
            rate=Decimal("1.5"),
            last_trade_at=datetime.now(),
        )
        fpr = NotifyFormatPriceRequestResult(prr, "en")

        self.assertFalse(fpr.is_rate_diff_available())
        self.assertFalse(fpr.is_convert_mode())
        self.assertEqual(
            fpr.get(),
            "*USD EUR* 1.5 🔔\n_17 March, 22:14 UTC_\ntest-exchanger 📡")
    def test_price_mode_no_diff(self):
        prr = PriceRequestResult(
            price_request=PriceRequest(
                amount=None,
                currency="USD",
                to_currency="EUR",
                parser_name="test-parser",
                direction_writing=DirectionWriting.UNKNOWN,
            ),
            exchanges=["test-exchanger"],
            rate=Decimal("1.5"),
            rate_open=None,
            low24h=Decimal("1.2"),
            high24h=Decimal("1.6"),
            last_trade_at=datetime.now(),
        )
        fpr = InlineFormatPriceRequestResult(prr, "en")

        self.assertFalse(fpr.is_rate_diff_available())
        self.assertFalse(fpr.is_convert_mode())
        self.assertEqual(fpr.get(), "USD EUR 1.5")
    def test_convert_r2l(self):
        prr = PriceRequestResult(
            price_request=PriceRequest(
                amount=Decimal("0.5"),
                currency="USD",
                to_currency="EUR",
                parser_name="test-parser",
                direction_writing=DirectionWriting.RIGHT2LEFT,
            ),
            exchanges=["test-exchanger"],
            rate=Decimal("3"),
            rate_open=Decimal("1"),
            low24h=Decimal("1"),
            high24h=Decimal("1"),
            last_trade_at=datetime.now(),
        )

        fpr = InlineFormatPriceRequestResult(prr, "en")

        self.assertTrue(fpr.is_rate_diff_available())
        self.assertTrue(fpr.is_convert_mode())
        self.assertEqual(fpr.get(), "1.5 EUR = 0.5 USD")
    def test_convert_mode_1996(self):
        prr = PriceRequestResult(
            price_request=PriceRequest(
                amount=Decimal("0.5"),
                currency="USD",
                to_currency="USD",
                parser_name="test-parser",
                direction_writing=DirectionWriting.UNKNOWN,
            ),
            exchanges=["test-exchanger"],
            rate=Decimal("3"),
            rate_open=Decimal("1"),
            low24h=Decimal("1"),
            high24h=Decimal("1"),
            last_trade_at=datetime.now(),
        )

        fpr = FormatPriceRequestResult(prr, "en")

        self.assertTrue(fpr.is_rate_diff_available())
        self.assertTrue(fpr.is_convert_mode())
        self.assertEqual(
            fpr.get(),
            "0.5 *USD* = 1.5 *USD*\n_17 March 1996_\ntest-exchanger 📡")
예제 #12
0
    def test_money_formats(self, m):
        self.assertEqual(
            RegexParser("100.20 usd rub", 1, "en", "USD", True).parse(),
            PriceRequest(
                amount=Decimal("100.20"),
                currency="USD",
                to_currency="RUB",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.LEFT2RIGHT,
            ),
        )

        self.assertEqual(
            RegexParser("100.20 usd rub", 1, "de", "USD", True).parse(),
            PriceRequest(
                amount=Decimal("10020"),
                currency="USD",
                to_currency="RUB",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.LEFT2RIGHT,
            ),
        )

        self.assertEqual(
            RegexParser("100,20 usd rub", 1, "en", "USD", True).parse(),
            PriceRequest(
                amount=Decimal("10020"),
                currency="USD",
                to_currency="RUB",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.LEFT2RIGHT,
            ),
        )

        self.assertEqual(
            RegexParser("100,20 usd rub", 1, "de", "USD", True).parse(),
            PriceRequest(
                amount=Decimal("100.20"),
                currency="USD",
                to_currency="RUB",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.LEFT2RIGHT,
            ),
        )

        self.assertEqual(
            RegexParser("100 20 usd rub", 1, "de", "USD", True).parse(),
            PriceRequest(
                amount=Decimal("10020"),
                currency="USD",
                to_currency="RUB",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.LEFT2RIGHT,
            ),
        )

        self.assertEqual(
            RegexParser("100 20,12 usd rub", 1, "de", "USD", True).parse(),
            PriceRequest(
                amount=Decimal("10020.12"),
                currency="USD",
                to_currency="RUB",
                parser_name="RegexParser",
                direction_writing=DirectionWriting.LEFT2RIGHT,
            ),
        )
예제 #13
0
def inline_query_callback(update: Update, context: CallbackContext,
                          chat_info: dict):
    query = update.inline_query.query

    if not query:
        logging.info("inline_request empty query")

        last_requests = get_last_request(update.effective_user.id)

        results = []

        for r in last_requests:
            if not r.from_currency.is_active or not r.to_currency.is_active:
                continue

            try:
                price_request_result = convert(
                    PriceRequest(
                        amount=None,
                        currency=r.from_currency.code,
                        to_currency=r.to_currency.code,
                        parser_name="InlineQuery",
                    ))
            except NoRatesException:
                continue

            title = InlineFormatPriceRequestResult(price_request_result,
                                                   chat_info["locale"]).get()
            text_to = FormatPriceRequestResult(price_request_result,
                                               chat_info["locale"]).get()

            ident = (
                f"{r.from_currency.code}{r.to_currency.code}"
                f"{price_request_result.rate}{price_request_result.last_trade_at}"
            )

            results.append(
                InlineQueryResultArticle(
                    id=ident,
                    title=title,
                    input_message_content=InputTextMessageContent(
                        text_to,
                        disable_web_page_preview=True,
                        parse_mode=ParseMode.MARKDOWN,
                    ),
                ))
        write_request_log.delay(
            chat_id=update.effective_user.id,
            msg="",
            created_at=datetime.now(),
            tag="Inline All",
        )
    else:
        tag = ""
        try:
            price_request = start_parse(
                query,
                chat_info["chat_id"],
                chat_info["locale"],
                chat_info["default_currency"],
                chat_info["default_currency_position"],
            )

            logging.info(f"inline_request: {query} -> {price_request}")

            tag = price_request.parser_name

            price_request_result = convert(price_request)

            logging.info(f"inline_request: {price_request_result}")

            title = InlineFormatPriceRequestResult(price_request_result,
                                                   chat_info["locale"]).get()
            text_to = FormatPriceRequestResult(price_request_result,
                                               chat_info["locale"]).get()

            ident = (
                f"{price_request.currency}|{price_request.to_currency}|"
                f"{price_request_result.rate}|{price_request_result.last_trade_at}"
            )

            results = [
                InlineQueryResultArticle(
                    id=ident,
                    title=title,
                    input_message_content=InputTextMessageContent(
                        text_to,
                        disable_web_page_preview=True,
                        parse_mode=ParseMode.MARKDOWN,
                    ),
                )
            ]

        except (ValidationException, ConverterException):
            logging.info(f"inline_request unrecognized: {query}")
            results = []

        finally:
            if len(query) <= settings.MAX_LEN_MSG_REQUESTS_LOG:
                write_request_log.delay(
                    chat_id=update.effective_user.id,
                    msg=query,
                    created_at=datetime.now(),
                    tag=f"Inline {tag}" if tag else "Inline",
                )

    update.inline_query.answer(results)
예제 #14
0
    def parse(self) -> PriceRequest:
        text = self.text

        obj = REQUEST_PATTERN_COMPILED.match(text)
        if not obj:
            raise WrongFormatException

        groups = obj.groups()

        if groups[PRICE_REQUEST_LEFT_AMOUNT] and groups[
                PRICE_REQUEST_RIGHT_AMOUNT]:
            raise WrongFormatException

        if groups[PRICE_REQUEST_LEFT_AMOUNT]:
            direction_writing = DirectionWriting.LEFT2RIGHT

        elif groups[PRICE_REQUEST_RIGHT_AMOUNT]:
            direction_writing = DirectionWriting.RIGHT2LEFT

        else:
            direction_writing = DirectionWriting.UNKNOWN

        amount = groups[PRICE_REQUEST_LEFT_AMOUNT] or groups[
            PRICE_REQUEST_RIGHT_AMOUNT]

        if amount:
            amount = parse_amount(amount, self.locale)

        text = groups[PRICE_REQUEST_CURRENCIES]
        text = text.upper()

        currencies = self.split_currencies(text)

        if len(currencies) == 2:
            currency, to_currency = currencies[0], currencies[1]
            if not self.is_currency_recognized(
                    currency) or not self.is_currency_recognized(to_currency):
                raise UnknownCurrencyException

        elif len(currencies) == 1 and self.is_currency_recognized(
                currencies[0]):
            if self.default_currency_position:
                currency, to_currency = currencies[0], self.default_currency
            else:
                currency, to_currency = self.default_currency, currencies[0]

            if direction_writing == DirectionWriting.RIGHT2LEFT:
                currency, to_currency = to_currency, currency

        else:
            currencies = currencies[0]
            for x in range(2, len(currencies) - 1):
                currency, to_currency = currencies[:x], currencies[x:]
                if self.is_currency_recognized(
                        currency) and self.is_currency_recognized(to_currency):
                    break
            else:
                raise WrongFormatException

        if direction_writing == DirectionWriting.RIGHT2LEFT:
            currency, to_currency = to_currency, currency

        return PriceRequest(
            amount=amount,
            currency=currency,
            to_currency=to_currency,
            parser_name=self.name,
            direction_writing=direction_writing,
        )
    def test_rounding(self):
        prr = PriceRequestResult(
            price_request=PriceRequest(
                amount=None,
                currency="USD",
                to_currency="EUR",
                parser_name="test-parser",
                direction_writing=DirectionWriting.UNKNOWN,
            ),
            exchanges=["test-exchanger"],
            rate=Decimal("0.5"),
            rate_open=Decimal("1"),
            low24h=Decimal("0.05"),
            high24h=Decimal("0.005"),
            last_trade_at=datetime.now(),
        )
        fpr = FormatPriceRequestResult(prr, "en")

        self.assertTrue(fpr.is_rate_diff_available())
        self.assertFalse(fpr.is_convert_mode())
        self.assertEqual(
            fpr.get(),
            "*USD EUR* 0.5 🔻\n-0.5 (-50.0%)\n*Low*: 0.05 *High*: 0.0050\n_17 March, 22:14 UTC_\ntest-exchanger 📡",
        )

        prr = PriceRequestResult(
            price_request=PriceRequest(
                amount=None,
                currency="USD",
                to_currency="EUR",
                parser_name="test-parser",
                direction_writing=DirectionWriting.UNKNOWN,
            ),
            exchanges=["test-exchanger"],
            rate=Decimal("123456789012.123456789012"),
            rate_open=Decimal("1"),
            low24h=Decimal("0.000000000012"),
            high24h=Decimal("1"),
            last_trade_at=datetime.now(),
        )
        fpr = FormatPriceRequestResult(prr, "en")

        self.assertTrue(fpr.is_rate_diff_available())
        self.assertFalse(fpr.is_convert_mode())
        self.assertEqual(
            fpr.get(),
            """*USD EUR* 123,456,789,012.1235 ⬆️
+123,456,789,011.1235 (+12,345,678,901,112.35%)
*Low*: 0.000000000012 *High*: 1.0
_17 March, 22:14 UTC_
test-exchanger 📡""",
        )

        prr = PriceRequestResult(
            price_request=PriceRequest(
                amount=None,
                currency="USD",
                to_currency="EUR",
                parser_name="test-parser",
                direction_writing=DirectionWriting.UNKNOWN,
            ),
            exchanges=["test-exchanger"],
            rate=Decimal("123456789012.123456789012"),
            rate_open=Decimal("123456789000.000012345"),
            low24h=Decimal("1234567890.000"),
            high24h=Decimal("198765432112.000000000012"),
            last_trade_at=datetime.now(),
        )
        fpr = FormatPriceRequestResult(prr, "en")

        self.assertTrue(fpr.is_rate_diff_available())
        self.assertFalse(fpr.is_convert_mode())
        self.assertEqual(
            fpr.get(),
            """*USD EUR* 123,456,789,012.1235 ⬆️
+12.1234 (+0.0000000098%)
*Low*: 1,234,567,890.0 *High*: 198,765,432,112.0
_17 March, 22:14 UTC_
test-exchanger 📡""",
        )
예제 #16
0
def notification_checker() -> None:
    db_session = Session()
    pairs = (db_session.query(
        Notification.from_currency_id,
        Notification.to_currency_id).filter_by(is_active=true()).group_by(
            Notification.from_currency_id, Notification.to_currency_id).all())

    for pair in pairs:
        from_currency = db_session.query(Currency).get(pair[0])
        to_currency = db_session.query(Currency).get(pair[1])

        if not from_currency.is_active or not to_currency.is_active:
            logging.info(
                "Disable notifications because currency %s %s is not active anymore",
                from_currency.code,
                to_currency.code,
            )
            notification_auto_disable(pair)
            continue

        pr = PriceRequest(
            amount=None,
            currency=from_currency.code,
            to_currency=to_currency.code,
            parser_name="Notification",
        )

        prr = convert(pr)

        notifications = (db_session.query(Notification).filter_by(
            is_active=true()).filter_by(from_currency_id=pair[0],
                                        to_currency_id=pair[1]).all())

        for n in notifications:
            if is_triggered(n.trigger_clause, n.trigger_value, n.last_rate,
                            prr.rate):
                # replace rate_open from daily to rate when was created or last triggered notification
                # TODO: PriceRequestResult -> dataclass, inside diff and etc
                nprr = PriceRequestResult(
                    price_request=prr.price_request,
                    exchanges=prr.exchanges,
                    rate=prr.rate,
                    rate_open=n.last_rate,
                    last_trade_at=prr.last_trade_at,
                    low24h=prr.low24h,
                    high24h=prr.high24h,
                )

                text_to = NotifyFormatPriceRequestResult(nprr,
                                                         n.chat.locale).get()

                if n.trigger_clause in [
                        NotifyTriggerClauseEnum.less,
                        NotifyTriggerClauseEnum.more,
                ]:
                    n.is_active = False
                    _ = get_translations(n.chat.locale)
                    text_to += "\n"
                    text_to += _("_One-time reminder. Set up a new reminder._")

                send_notification.delay(n.chat_id, text_to)

                n.last_rate = prr.rate

                transaction.commit()