summaryrefslogtreecommitdiff
path: root/searx/search/processors/online_currency.py
blob: c79374f51ce8b7703e723469c22292f9fcb53369 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Processor used for ``online_currency`` engines."""

import typing as t

import unicodedata
import re

import flask_babel
import babel

from searx.data import CURRENCIES
from .online import OnlineProcessor, OnlineParams

if t.TYPE_CHECKING:
    from .abstract import EngineProcessor
    from searx.search.models import SearchQuery


search_syntax = re.compile(r".*?(\d+(?:\.\d+)?) ([^.0-9]+) (?:in|to) ([^.0-9]+)", re.I)
"""Search syntax used for from/to currency (e.g. ``10 usd to eur``)"""


class CurrenciesParams(t.TypedDict):
    """Currencies request parameters."""

    amount: float
    """Currency amount to be converted"""

    to_iso4217: str
    """ISO_4217_ alpha code of the currency used as the basis for conversion.

    .. _ISO_4217: https://en.wikipedia.org/wiki/ISO_4217
    """

    from_iso4217: str
    """ISO_4217_ alpha code of the currency to be converted."""

    from_name: str
    """Name of the currency used as the basis for conversion."""

    to_name: str
    """Name of the currency of the currency to be converted."""


class OnlineCurrenciesParams(CurrenciesParams, OnlineParams):  # pylint: disable=duplicate-bases
    """Request parameters of a ``online_currency`` engine."""


class OnlineCurrencyProcessor(OnlineProcessor):
    """Processor class used by ``online_currency`` engines."""

    engine_type: str = "online_currency"

    def initialize(self, callback: t.Callable[["EngineProcessor", bool], bool]):
        CURRENCIES.init()
        super().initialize(callback)

    def get_params(self, search_query: "SearchQuery", engine_category: str) -> OnlineCurrenciesParams | None:
        """Returns a dictionary with the :ref:`request params <engine request
        online_currency>` (:py:obj:`OnlineCurrenciesParams`).  ``None`` is
        returned if the search query does not match :py:obj:`search_syntax`."""

        online_params: OnlineParams | None = super().get_params(search_query, engine_category)

        if online_params is None:
            return None
        m = search_syntax.match(search_query.query)
        if not m:
            return None

        amount_str, from_currency, to_currency = m.groups()
        try:
            amount = float(amount_str)
        except ValueError:
            return None

        # most often $ stands for USD
        if from_currency == "$":
            from_currency = "$ us"

        if to_currency == "$":
            to_currency = "$ us"

        from_iso4217 = from_currency
        if not CURRENCIES.is_iso4217(from_iso4217):
            from_iso4217 = CURRENCIES.name_to_iso4217(_normalize_name(from_currency))

        to_iso4217 = to_currency
        if not CURRENCIES.is_iso4217(to_iso4217):
            to_iso4217 = CURRENCIES.name_to_iso4217(_normalize_name(to_currency))

        if from_iso4217 is None or to_iso4217 is None:
            return None

        ui_locale = flask_babel.get_locale() or babel.Locale.parse("en")
        from_name: str = CURRENCIES.iso4217_to_name(
            from_iso4217, ui_locale.language
        )  # pyright: ignore[reportAssignmentType]
        to_name: str = CURRENCIES.iso4217_to_name(
            to_iso4217, ui_locale.language
        )  # pyright: ignore[reportAssignmentType]

        params: OnlineCurrenciesParams = {
            **online_params,
            "amount": amount,
            "from_iso4217": from_iso4217,
            "to_iso4217": to_iso4217,
            "from_name": from_name,
            "to_name": to_name,
        }

        return params


def _normalize_name(name: str):
    name = name.strip()
    name = name.lower().replace("-", " ")
    name = re.sub(" +", " ", name)
    return unicodedata.normalize("NFKD", name).lower()