Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
aa280de
Refactor logging configuration and update log file path; enhance .git…
UznetDev Feb 27, 2025
09b8cec
Update .gitignore to include log files and directories
UznetDev Feb 27, 2025
8427b20
Refactor database interaction methods to use 'insert_' prefix for use…
UznetDev Feb 27, 2025
006c06d
Fix logging output format in main.py
UznetDev Feb 27, 2025
f2ee7ad
Update .gitignore to exclude logs directory
UznetDev Feb 27, 2025
6940d95
Refactor user ID references from 'cid' to 'user_ud' in admin and user…
UznetDev Feb 27, 2025
9c8f405
Rename 'mid' to 'message_id' for clarity in main_panel function
UznetDev Feb 27, 2025
c6191c7
Refactor user ID references from 'user_ud' to 'user_id' for consisten…
UznetDev Feb 27, 2025
6634f79
Refactor database schema to replace 'admin_user_id' with 'initiator_u…
UznetDev Feb 27, 2025
4d2ed49
Refactor insert functions in Database class to remove unused date par…
UznetDev Feb 27, 2025
690de8c
Add settings table methods: insert, update, and select functionality
UznetDev Feb 27, 2025
600fe1e
Add updater_user_id field to users, channels, and admins tables for t…
UznetDev Feb 27, 2025
5c0dc8b
Fix SQL syntax in ban table creation and ensure proper formatting for…
UznetDev Feb 27, 2025
ed5fb48
Add select methods for settings, banned users, all users, admins, and…
UznetDev Feb 27, 2025
cff6bad
Refactor Database class: remove deprecated ban table creation, update…
UznetDev Feb 27, 2025
f2b4c0c
Set default values for boolean fields in the Database class
UznetDev Feb 28, 2025
b1ca8e5
Add update_user_status method and refactor ban table references in Da…
UznetDev Feb 28, 2025
45c1c56
Refactor channel handling in Database class: rename parameters for cl…
UznetDev Feb 28, 2025
1204a75
Refactor admin management: update parameter names for clarity, enhanc…
UznetDev Feb 28, 2025
db6d937
Refactor users table: change status field to ENUM, add ban_time colum…
UznetDev Feb 28, 2025
d1b8b35
Refactor user handling: rename variables for clarity, fix SQL syntax,…
UznetDev Feb 28, 2025
f8d871c
Refactor admin handling: rename user_ud to user_id for consistency an…
UznetDev Feb 28, 2025
3491aeb
Refactor super_admin and check_usr handlers: rename user_ud to user_i…
UznetDev Feb 28, 2025
804c82f
Refactor user identification: rename user_ud to user_id for consisten…
UznetDev Feb 28, 2025
afce058
Refactor user identification: rename user_ud to user_id for consisten…
UznetDev Feb 28, 2025
7566a22
Refactor user identification: rename user_ud to user_id for consisten…
UznetDev Feb 28, 2025
a7b30f5
Refactor database queries and admin handling: update SQL queries to u…
UznetDev Feb 28, 2025
2ba4114
Refactor user identification in user_check: rename user_ud to user_id…
UznetDev Feb 28, 2025
a7141ab
Refactor middleware imports and improve ban handling: update Throttli…
UznetDev Feb 28, 2025
3eebb38
Enhance ban handling and user management: add ban time display in ban…
UznetDev Feb 28, 2025
32b7004
Refactor ThrottlingMiddleware: enhance member check logic and streaml…
UznetDev Mar 1, 2025
4526085
Refactor language code handling: standardize variable naming for lang…
UznetDev Mar 1, 2025
a91838b
Refactor date handling: update variable names from 'created_time' to …
UznetDev Mar 1, 2025
b799307
Update .gitignore: remove redundant log directory entries for cleaner…
UznetDev Mar 1, 2025
fd8e201
Add function to format seconds and update database cursor for buffere…
UznetDev Mar 1, 2025
f8d99f0
Refactor database handling: add features for managing texts, translat…
UznetDev May 11, 2025
d070d19
Implement feature management with Redis caching and user prompts; add…
UznetDev May 12, 2025
77eaca0
Refactor Makefile to set default goal to 'main' and enhance setup com…
UznetDev May 13, 2025
4aaf537
feat: Implement inline keyboard buttons and callbacks for user intera…
UznetDev May 13, 2025
938d65a
refactor: Update import paths and enhance setup commands; add pytest …
UznetDev May 13, 2025
4e34368
feat: Add main entry point and logging setup; refactor Makefile and u…
UznetDev May 22, 2025
afceca5
refactor: Remove unused test fixtures and commented code from conftes…
UznetDev May 22, 2025
7025e40
feat: Add empty __init__.py files for admin and user handlers to init…
UznetDev May 22, 2025
8e564b0
refactor: Clean up imports and enhance feature management in handlers…
UznetDev May 22, 2025
c035557
Refactor admin filters and handlers; improve logging and feature mana…
UznetDev May 22, 2025
53bbf43
refactor: Organize imports and enhance main admin panel handler funct…
UznetDev May 22, 2025
96d0728
refactor: Update logging mechanism in close handler and reorganize im…
UznetDev May 22, 2025
a068769
refactor: Enhance Database class with improved logging and SettingsMa…
UznetDev May 22, 2025
d10fbc3
Add tests for feature manager functionality
UznetDev May 22, 2025
4fd4033
refactor: Remove unused methods from SelectAdmin class and streamline…
UznetDev May 22, 2025
f48f832
refactor: Replace SelectAdmin with AdminFilter in admin-related handl…
UznetDev May 22, 2025
5dc2c8d
refactor: Remove unused admin-related methods and streamline database…
UznetDev May 22, 2025
c6225bd
refactor: Remove unused mysql.connector import from admin rights mana…
UznetDev May 22, 2025
83e4f8c
refactor: Move feature-related database methods to FeatureManager and…
UznetDev May 22, 2025
80de546
refactor: Add __call__ method to AdminsManager for improved usability
UznetDev May 22, 2025
f51193d
refactor: Simplify feature management by consolidating update logic a…
UznetDev May 22, 2025
d076ab0
refactor: Consolidate settings management logic and enhance test cove…
UznetDev May 22, 2025
41fc6f7
refactor: Enhance Translator class with logging and database methods,…
UznetDev May 22, 2025
0899644
refactor: Add __call__ method to Translator, FeatureManager, and Sett…
UznetDev May 22, 2025
946f848
refactor: Update Translator initialization to include Redis client an…
UznetDev May 23, 2025
4d131b6
refactor: Enhance AdminsManager and related filters with improved adm…
UznetDev May 23, 2025
22b8075
refactor: Remove unnecessary comments and streamline admin checks in …
UznetDev Jun 3, 2025
c9b9274
refactor: Enhance AdminsManager with improved feature management and …
UznetDev Jun 3, 2025
4eb368d
Refactor admin rights management system
UznetDev Jun 29, 2025
c0db277
refactor: Enhance admin statistics features and streamline related code
UznetDev Jul 6, 2025
0128cce
refactor: Update admin feature checks and enhance admin settings func…
UznetDev Jul 6, 2025
9cc0978
refactor: Clean up AdminsManager and FeatureManager, streamline admin…
UznetDev Jul 6, 2025
e55249f
refactor: Implement mandatory membership feature for channel management
UznetDev Jul 6, 2025
a18262a
refactor: Remove unused channel table creation and streamline channel…
UznetDev Jul 13, 2025
f82815b
refactor: Enhance ManageMandatoryMembership class with Redis synchron…
UznetDev Jul 13, 2025
7033600
.
UznetDev Sep 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: CI

on: [push, pull_request]

jobs:
test:
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8.4
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: app_db
ports: [3306]
options: --health-cmd="mysqladmin ping -h localhost -proot" --health-interval=5s --health-timeout=5s --health-retries=5
redis:
image: redis:7-alpine
ports: [6379]
options: >-
--health-cmd="redis-cli ping" --health-interval=5s
--health-timeout=5s --health-retries=5

steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: { python-version: '3.12' }
- run: pip install -r requirements.txt
- run: pytest -q --cov=.
12 changes: 10 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
/.idea/
/__pycache__/
/env/
/db/
/logs/
/build/
/.command*
/.env
/_build*
/.vscode*
*my_test
*my_test
__pycache__
__pycache__/*
*.so
*.json
*.log
*.xls
.bins
my_test.py
41 changes: 41 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
.DEFAULT_GOAL := main
SHELL := bash

VENV := env

ifeq (, $(shell command -v python3 2> /dev/null))
PY_SYS := python
else
PY_SYS := python3
endif

ifeq ($(OS),Windows_NT)
VENV_PY := $(VENV)/Scripts/python.exe
else
VENV_PY := $(VENV)/bin/python
endif

.PHONY: main setup requirements venv

main: setup
@echo "🚀 Running main.py…"
$(VENV_PY) -m bot

setup: requirements
@echo "⚙️ Running setup.py…"
$(VENV_PY) setup.py

requirements: venv
@echo "📦 Installing/upgrading requirements…"
$(VENV_PY) -m pip install --upgrade pip
$(VENV_PY) -m pip install --upgrade mysql-connector-python

venv:
@if [ ! -f "$(VENV_PY)" ]; then \
echo "🔧 Creating virtual environment in $(VENV)"; \
$(PY_SYS) -m venv $(VENV); \
$(VENV_PY) -m pip install --upgrade pip; \
$(VENV_PY) -m pip install -r requirements.txt; \
else \
echo "✅ Virtual environment already exists."; \
fi
1 change: 1 addition & 0 deletions bot/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import main
32 changes: 32 additions & 0 deletions bot/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

import os
import sys
import asyncio
import logging
from .main import main
from bot.data.config import log_file_name
from bot.loader import root_logger, log_file_name, formatter


if __name__ == "__main__":

if not os.path.exists('logs'):
os.mkdir('logs')

if not os.path.exists(log_file_name):
with open(log_file_name, 'w') as f:
pass

file_handler = logging.FileHandler(log_file_name)
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(formatter)
root_logger.addHandler(file_handler)

stream_handler = logging.StreamHandler(sys.stdout)
stream_handler.setLevel(logging.INFO)
stream_handler.setFormatter(formatter)
root_logger.addHandler(stream_handler)



asyncio.run(main())
157 changes: 157 additions & 0 deletions bot/api/translator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import logging
from typing import Optional

from redis import Redis, RedisError
from deep_translator import GoogleTranslator

from bot.db.database import Database
from bot.function.function import to_hash
from bot.core.feature_manager import FeatureManager


class Translator:
_REDIS_HASH_TEXTS = "texts"
_REDIS_HASH_TRANSLATIONS = "translations"

def __init__(
self,
db: Database,
FM: FeatureManager,
root_logger: logging.Logger,
redis_client: Redis,
default_dest: str = "en",
default_src: str = "en",
):
self.db: Database = db
self.redis: Redis = redis_client
self.FM: FeatureManager = FM
self.log = root_logger

self.default_dest = default_dest
self.default_src = default_src

self._create_table_texts()
self._create_table_translations()

def __call__(
self,
text: str,
dest: Optional[str] = None,
src: Optional[str] = None,
) -> str:
return self.translate(text, dest=dest, src=src)

def translate(
self,
text: str,
dest: Optional[str] = None,
src: Optional[str] = None,
) -> str:
if not self.FM.feature("translator") or not text:
return text

dest = (dest or self.default_dest).lower()
src = (src or self.default_src).lower()
if dest == src:
return text

h = to_hash(text)
field_t = f"{h}:{dest}"
try:
cached = self.redis.hget(self._REDIS_HASH_TRANSLATIONS, field_t)
if cached:
return cached.decode() if isinstance(cached, bytes) else cached
except RedisError as err:
self.log.warning(f"Redis unavailable (translations): {err}")

text_id = self._select_text_id(h)
if text_id:
mysql_translation = self._select_translation(text_id, dest)
if mysql_translation:
self._cache_translation_redis(field_t, mysql_translation)
return mysql_translation
try:
translated = GoogleTranslator(source=src, target=dest).translate(text)
except Exception as err:
self.log.error(f"GoogleTranslator error: {err}")
return text
if not text_id:
text_id = self._insert_text(h, text)
self._insert_translation(text_id, dest, translated)
self._cache_translation_redis(field_t, translated)
return translated

def _cache_translation_redis(self, field: str, value: str) -> None:
try:
self.redis.hset(self._REDIS_HASH_TRANSLATIONS, field, value)
except RedisError as err:
self.log.warning(f"Redis cache write failed: {err}")

def _insert_text(self, hash_value: str, raw_text: str) -> int:
sql = "INSERT INTO texts (hash_value, raw_text) VALUES (%s, %s)"
self._exec(sql, (hash_value, raw_text))
return self.db.cursor.lastrowid

def _insert_translation(self, text_id: int, dest_lang: str, translated: str) -> None:
sql = (
"INSERT INTO translations (text_id, dest_lang, translated_content) "
"VALUES (%s, %s, %s)"
)
self._exec(sql, (text_id, dest_lang, translated))

def _select_text_id(self, hash_value: str) -> Optional[int]:
sql = "SELECT id FROM texts WHERE hash_value = %s LIMIT 1"
row = self._fetchone(sql, (hash_value,))
return row["id"] if row else None

def _select_translation(self, text_id: int, dest_lang: str) -> Optional[str]:
sql = (
"SELECT translated_content FROM translations "
"WHERE text_id = %s AND dest_lang = %s LIMIT 1"
)
row = self._fetchone(sql, (text_id, dest_lang))
return row["translated_content"] if row else None

def _exec(self, sql: str, params: tuple) -> None:
try:
self.db.cursor.execute(sql, params)
self.db.connection.commit()
except Exception as err:
self.log.error(err)

def _fetchone(self, sql: str, params: tuple):
try:
self.db.cursor.execute(sql, params)
return self.db.cursor.fetchone()
except Exception as err:
self.log.error(err)

def _create_table_texts(self):
sql = """
CREATE TABLE IF NOT EXISTS texts (
id INT AUTO_INCREMENT PRIMARY KEY,
hash_value BIGINT UNSIGNED,
raw_text TEXT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
deleted_at TIMESTAMP NULL DEFAULT NULL,
INDEX(hash_value)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
"""
self._exec(sql, ())

def _create_table_translations(self):
sql = """
CREATE TABLE IF NOT EXISTS translations (
id INT AUTO_INCREMENT PRIMARY KEY,
text_id INT NOT NULL,
dest_lang CHAR(5) NOT NULL,
translated_content TEXT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
deleted_at TIMESTAMP NULL DEFAULT NULL,
FOREIGN KEY (text_id) REFERENCES texts(id)
ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
"""
self._exec(sql, ())
Loading
Loading