Skip to content

Commit

Permalink
Merge pull request #41 from kozalosev/release/2.3.0
Browse files Browse the repository at this point in the history
Release/2.3.0
  • Loading branch information
Leonid Kozarin authored Aug 24, 2023
2 parents dbf1410 + 8efdd5a commit 93d9534
Show file tree
Hide file tree
Showing 20 changed files with 648 additions and 61 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
app/data
venv*
63 changes: 63 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Publish to GitHub Registry
on:
push:
tags:
- 'v*'
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
Publish-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event."
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!"
- run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}."

- name: Check out repository code
uses: actions/checkout@v3
- run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."

- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
# output 0.1.2
type=semver,pattern={{version}}
# output 0.1
type=semver,pattern={{major}}.{{minor}}
# disabled if major zero
type=semver,pattern={{major}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.') }}
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

- run: echo "🍏 This job's status is ${{ job.status }}."

Create-release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v3
- uses: ncipollo/release-action@v1
with:
generateReleaseNotes: true
token: ${{ secrets.GH_KEY_2_RELEASE }}
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
FROM python:3.11-alpine

RUN apk update && \
apk add git gcc musl-dev
apk add git gcc g++ musl-dev

WORKDIR /home/textUtilsBot

COPY requirements.txt .
RUN pip install -r requirements.txt
COPY requirements.txt requirements-extra.txt ./
RUN pip install -r requirements.txt -r requirements-extra.txt

# www-data
USER 33
Expand Down
4 changes: 2 additions & 2 deletions app/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,12 @@ def transform_query(transformer: TextProcessor, **kwargs):
add_article(localized_transformer_name, processed_str, description, parse_mode,
transformer.snake_case_name, **kwargs)

exclusive_processors = text_processors.match_exclusive_processors(request.query)
exclusive_processors = text_processors.match_exclusive_processors(request.query, lang_code)
if exclusive_processors:
for processor in exclusive_processors:
transform_query(processor)
else:
processors = text_processors.match_simple_processors(request.query)
processors = text_processors.match_simple_processors(request.query, lang_code)
reversible_processors = [x for x in processors if x.is_reversible]
non_reversible_processors = [x for x in processors if not x.is_reversible]

Expand Down
12 changes: 8 additions & 4 deletions app/localizations.ini
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@ hint_base64_encoder = Encrypted text
help_base64_encoder = Converts some text to a hexadecimal string. For example, the word `hello` will be transformed into `aGVsbG8=`.
hint_base64_decoder = Unencrypted text
help_url_encoder = Encodes URL addresses containing non-Latin characters exactly like Chrome does, and vice versa. The address must start with `http://` or `https://`.
hint_calculator = Calculator
help_calculator = Evaluates expressions like `{{ 2*2 + 2 $ > RUB }}` into something like `448.77 RUB`
hint_calculator = Embedded calculator for texts
help_calculator = Evaluates expressions like `Total = {{ 2*2 + 2 $ > RUB }}` into something like `Total = 448.77 RUB`
hint_single_expression_calculator = Calculator
help_single_expression_calculator = Evaluates expressions like `2*2 + 2 $ > RUB` into something like `448.77 RUB`
[ru]
help_message = Привет! Я могу тебе помочь с различными преобразованием текста. Просто введи моё имя в строке ввода сообщения через собачку, а дальше пиши любой текст. Работает в любом чате! Но не злоупотребляй длинными сообщениями, так как я не смогу их обработать!
Expand Down Expand Up @@ -79,5 +81,7 @@ hint_base64_encoder = Шифровка
help_base64_encoder = Кодирует текст в base64-строку. Например, слово `привет` первратится в `0L/RgNC40LLQtdGC`.
hint_base64_decoder = Дешифровка
help_url_encoder = Кодирует URL-адрес с нелатинскими символами так же, как это делает Chrome. Возможно обратное декодирование. Адрес должен начинаться с `http://` или `https://`.
hint_calculator = Калькулятор
help_calculator = Вычисляет выражения типа `{{ 2*2 + 2 $ > RUB }}` во что-то вроде `448.77 RUB`
hint_calculator = Встроенный калькулятор для текстов
help_calculator = Вычисляет выражения типа `сумма = {{ 2*2 + 2 $ > RUB }}` во что-то вроде `сумма = 448.77 RUB`
hint_single_expression_calculator = Калькулятор
help_single_expression_calculator = Вычисляет выражения типа `2*2 + 2 $ > RUB` во что-то вроде `448.77 RUB`
8 changes: 4 additions & 4 deletions app/strconv/binhex64.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def process(self, query: str, lang_code: str = "") -> str:

class BinaryDecoder(Decoder):
@classmethod
def can_process(cls, query: str) -> bool:
def can_process(cls, query: str, lang_code: str = "") -> bool:
return all(char in ('0', '1', ' ') for char in query) and bin_to_str(query)

def process(self, query: str, lang_code: str = "") -> str:
Expand All @@ -34,10 +34,10 @@ def process(self, query: str, lang_code: str = "") -> str:

class HexadecimalDecoder(Decoder):
@classmethod
def can_process(cls, query: str) -> bool:
def can_process(cls, query: str, lang_code: str = "") -> bool:
# Since this processor is able to handle the same queries the BinaryDecoder can, we need to ensure that
# we won't swallow binary strings here.
if BinaryDecoder.can_process(query):
if BinaryDecoder.can_process(query, lang_code):
return False
return all(char in string.hexdigits + ' ' for char in query) and hex_to_str(query)

Expand All @@ -52,7 +52,7 @@ def process(self, query: str, lang_code: str = "") -> str:

class Base64Decoder(Decoder):
@classmethod
def can_process(cls, query: str) -> bool:
def can_process(cls, query: str, lang_code: str = "") -> bool:
return all(char in string.ascii_letters + string.digits + '+/=' for char in query) and base64_to_str(query)

def process(self, query: str, lang_code: str = "") -> str:
Expand Down
19 changes: 10 additions & 9 deletions app/strconv/calc.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
from txtproc.abc import TextProcessor
from . import currates

_subst_re = re.compile(r"\{\{(?P<expr>[0-9+\-*/%^., ]+?) *?"
r"((?P<from_curr>[A-Z]{3,}|[$€₽£¥]) *?"
r"(to|>) *?"
r"(?P<to_curr>[A-Z]{3,}|[$€₽£¥]))?? *?}}")
_subst_re = re.compile(r"\{\{(?P<expr>[0-9+\-*/%^., ()]+?) *?"
r"((?P<from_curr>[A-Za-zа-я.]{3,}|[$€₽£¥]) *?"
r"(to|>|в)? *?"
r"(?P<to_curr>[A-Za-zа-я.]{3,}|[$€₽£¥])?)?? *?}}")
_logger = logging.getLogger(__file__)
_MAX_RECURSION = 100

Expand All @@ -30,7 +30,7 @@ class Calculator(TextProcessor):
_logger = logging.getLogger(__name__)

@classmethod
def can_process(cls, query: str) -> bool:
def can_process(cls, query: str, lang_code: str = "") -> bool:
return bool(_subst_re.search(query))

def process(self, query: str, lang_code: str = "") -> str:
Expand All @@ -42,6 +42,9 @@ def process(self, query: str, lang_code: str = "") -> str:
except currates.UnsupportedCurrency as err:
self._logger.warning(f"The following currency was requested but is not present in our data: {err}")
return "" # the bot will ignore this result
except currates.UnknownLanguageCode as err:
self._logger.warning(f"The following language code was received but is not present in our data: {err}")
return "" # the bot will ignore this result

def next_expr(self, query: str, lang_code: str, sb: StringIO, pos: int = 0, guard: int = 0) -> str:
match = _subst_re.search(query, pos=pos)
Expand All @@ -51,14 +54,12 @@ def next_expr(self, query: str, lang_code: str, sb: StringIO, pos: int = 0, guar
sb.write(query[pos:match.start()])
expr = match.group("expr").strip().replace(",", ".")
val = _eval_func(expr)
# TODO: remove when #37 is fixed (this log will not be useful if the numexpr implementation is used)
if not isinstance(val, (int, float)):
self._logger.warning(f"'{val}' is not a number for some reason. The query was '{query}', expr: '{expr}'")
# end TODO
from_curr = match.group("from_curr")
to_curr = match.group("to_curr")
if from_curr and to_curr:
val = currates.convert(from_curr, to_curr, val, lang_code)
if from_curr:
val, to_curr = currates.convert(from_curr, to_curr, val, lang_code)
val = self._format_number(val)
if len(to_curr) == 1:
sb.write(f"{to_curr}{val}")
Expand Down
36 changes: 36 additions & 0 deletions app/strconv/calcsimp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""Simplified variant of `.calc.Calculator` working with only one expression."""

import re

from txtproc.abc import TextProcessor
from . import currates
from .calc import Calculator

_subst_re = re.compile(r"^(?P<expr>[0-9+\-*/%^., ()]+?)? *?"
r"((?P<from_curr>[A-Za-zа-я]{3,}|[$€₽£¥]) *?"
r"(to|>|в)? *?"
r"(?P<to_curr>[A-Za-zа-я]{3,}|[$€₽£¥])?)??$")


class SingleExpressionCalculator(TextProcessor):
_calc = Calculator()

@classmethod
def can_process(cls, query: str, lang_code: str = "") -> bool:
if len(query) == 0:
return False
match = _subst_re.search(query)
if not match:
return False
from_curr = match.group("from_curr")
to_curr = match.group("to_curr")
if from_curr is None:
return True
return currates.currency_exists(from_curr, lang_code) and \
(to_curr is None or currates.currency_exists(to_curr, lang_code))

def process(self, query: str, lang_code: str = "") -> str:
expr = _subst_re.search(query).group("expr")
if not expr:
query = "1 " + query
return self._calc.process("{{" + query + "}}", lang_code)
66 changes: 43 additions & 23 deletions app/strconv/currates/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,19 @@
import random
import asyncio
from functools import reduce
from typing import List, Iterable
from typing import List, Iterable, Optional

from .localcurr import LOCALE_TO_CURRENCY
from .currdsl import Currency
from .types import *
from .exceptions import *

try:
from data.currates_conf import CURRENCIES_MAPPING
except ModuleNotFoundError:
# for tests
from examples.currates_conf import CURRENCIES_MAPPING

__all__ = ['update_rates', 'update_rates_async_loop', 'update_volatile_rates_async_loop', 'convert']

__db = dbm.open('app/data/currates.db', flag='c')
Expand Down Expand Up @@ -105,20 +113,40 @@ async def update_volatile_rates_async_loop(src: Iterable[DataSource], period_in_
await asyncio.sleep(run_in_time.total_seconds())


def convert(from_curr: str, to_curr: str, val: float, lang_code: str) -> float:
def convert(from_curr: str, to_curr: Optional[str], val: float, lang_code: str) -> (float, str):
"""
Converts a value from one currency into another
:param from_curr: source currency
:param to_curr: destination currency
:param to_curr: destination currency (if None, will be inferred from a 'lang_code')
:param val: numeric value
:param lang_code: used for conversion '¥' into either 'yen' or 'yuan'
:return: converted numeric value
:return: converted numeric value and currency name in the right declension
:raises UnsupportedCurrency: if one or both of the currencies isn't present in our data
:raises UnknownLanguageCode: if 'to_curr' is None and cannot be inferred from a 'lang_code'
"""
from_curr = _ensure_not_symbol(from_curr, lang_code)
to_curr = _ensure_not_symbol(to_curr, lang_code)
return _get_coefficient_for(from_curr, to_curr) * val
if not to_curr:
try:
to_curr = LOCALE_TO_CURRENCY[lang_code.upper()]
except KeyError:
raise UnknownLanguageCode(lang_code)
from_curr = _ensure_not_symbol_or_word(from_curr, lang_code)
to_curr = _ensure_not_symbol_or_word(to_curr, lang_code)
result = _get_coefficient_for(from_curr.code, to_curr.code) * val
return result, to_curr.resolve_declension(result)


def currency_exists(curr: Optional[str], lang_code: str) -> bool:
""":returns: True if a specified currency is present in the database."""
if len(__db) == 0:
_logger.warning("Currencies disappeared somewhere...")
update_rates(__src_cache)

try:
curr = _ensure_not_symbol_or_word(curr or "", lang_code)
except UnsupportedCurrency | UnknownLanguageCode:
return False
return curr and curr.code.upper() in __db


def _fetch_rates(src: DataSource) -> ExchangeRates:
Expand All @@ -144,23 +172,15 @@ def _get_coefficient_for(from_curr: str, to_curr: str) -> float:
return to_usd_rate / from_usd_rate


def _ensure_not_symbol(curr: str, lang_code: str) -> str:
def _ensure_not_symbol_or_word(curr: str, lang_code: str) -> Currency:
if curr == "¥":
if 'ja' in lang_code:
return "JPY"
elif 'zh' in lang_code:
return "CNY"
else:
raise UnsupportedCurrency(f"{curr}:{lang_code}")
match curr:
case "$": return "USD"
case "€": return "EUR"
case "₽" | "RUR": return "RUB"
case "£": return "GBP"
case "₿": return "BTC"
case "₹" | "₨" | "Rs" | "Rp": return "INR"
case "₪": return "ILS"
return curr
match lang_code:
case 'ja': curr = 'JPY'
case 'zh': curr = 'CNY'
case _: raise UnsupportedCurrency(f"{curr}:{lang_code}")
mappings = (m.clone() for m in CURRENCIES_MAPPING)
matched_curr = next((m for m in mappings if m.matches(curr)), None)
return matched_curr or Currency.from_code(curr)


def _mock_database(temp_file_path: str):
Expand Down
Loading

0 comments on commit 93d9534

Please sign in to comment.