add proxy for tg and fix auth

This commit is contained in:
Yuriy Yuriev
2026-05-14 19:18:44 +07:00
parent 328f510151
commit bdf3fef7ec
7 changed files with 79 additions and 90 deletions
+3 -1
View File
@@ -5,9 +5,11 @@ import logging
from datetime import datetime, timedelta
from typing import Dict, Optional
from config.settings import settings
logger = logging.getLogger(__name__)
ADMIN_PASSWORD = "NjduqrKozu#G"
ADMIN_PASSWORD = settings.ADMIN_PASSWORD
class AuthManager:
+12 -20
View File
@@ -2,8 +2,8 @@
Хранилище учётных данных пользователей.
"""
import json
import asyncio
import json
import logging
from pathlib import Path
from typing import Optional, Dict
@@ -20,57 +20,49 @@ class AuthStorage:
self._lock = asyncio.Lock()
async def register(self, user_id: int, password_hash: str, salt: str) -> bool:
"""Регистрирует нового пользователя. Возвращает True если успешно."""
async with self._lock:
data = self._load()
data = await asyncio.to_thread(self._load_sync)
if str(user_id) in data:
return False
data[str(user_id)] = {
"password_hash": password_hash,
"salt": salt,
}
self._save(data)
data[str(user_id)] = {"password_hash": password_hash, "salt": salt}
await asyncio.to_thread(self._save_sync, data)
logger.info(f"User {user_id} registered")
return True
async def get_user(self, user_id: int) -> Optional[Dict]:
"""Получает данные пользователя по ID."""
async with self._lock:
data = self._load()
data = await asyncio.to_thread(self._load_sync)
return data.get(str(user_id))
async def change_password(self, user_id: int, new_hash: str, new_salt: str) -> bool:
"""Сменить пароль пользователя."""
async with self._lock:
data = self._load()
data = await asyncio.to_thread(self._load_sync)
user_str = str(user_id)
if user_str not in data:
return False
data[user_str]["password_hash"] = new_hash
data[user_str]["salt"] = new_salt
self._save(data)
await asyncio.to_thread(self._save_sync, data)
logger.info(f"User {user_id} changed password")
return True
async def delete_user(self, user_id: int) -> bool:
"""Удалить учётную запись пользователя."""
async with self._lock:
data = self._load()
data = await asyncio.to_thread(self._load_sync)
user_str = str(user_id)
if user_str not in data:
return False
del data[user_str]
self._save(data)
await asyncio.to_thread(self._save_sync, data)
logger.info(f"User {user_id} deleted")
return True
async def user_exists(self, user_id: int) -> bool:
"""Проверяет, существует ли пользователь."""
async with self._lock:
data = self._load()
data = await asyncio.to_thread(self._load_sync)
return str(user_id) in data
def _load(self) -> dict:
def _load_sync(self) -> dict:
if not self.file_path.exists():
return {}
try:
@@ -80,7 +72,7 @@ class AuthStorage:
logger.warning(f"Failed to load auth storage: {e}")
return {}
def _save(self, data: dict):
def _save_sync(self, data: dict) -> None:
try:
with open(self.file_path, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
+2
View File
@@ -8,6 +8,8 @@ class Settings(BaseSettings):
# Bot
BOT_TOKEN: str = ""
TELEGRAM_PROXY: str = "" # socks5://user:pass@host:port
ADMIN_PASSWORD: str = ""
# Telethon/MTProto settings
API_ID: int = 12345
+3 -1
View File
@@ -7,6 +7,7 @@ import logging
from types import SimpleNamespace
from aiogram import Bot, Dispatcher
from aiogram.types import BotCommand
from aiogram.client.session.aiohttp import AiohttpSession
from config.settings import settings
from core.logger import setup_logger
from managers.proxy_manager import ProxyManager
@@ -71,7 +72,8 @@ class BotApplication:
raise ValueError("BOT_TOKEN not set!")
self.bot = Bot(token=settings.BOT_TOKEN)
session = AiohttpSession(proxy=settings.TELEGRAM_PROXY) if settings.TELEGRAM_PROXY else AiohttpSession()
self.bot = Bot(token=settings.BOT_TOKEN, session=session)
self.dispatcher = Dispatcher()
self.interface.register(self.dispatcher)
+46 -56
View File
@@ -2,12 +2,12 @@
Сохранение и загрузка задач между перезапусками.
"""
import json
import asyncio
import json
import logging
from pathlib import Path
from datetime import datetime
from typing import Dict, Optional
from typing import Dict
from managers.task_manager import TaskParams
@@ -23,7 +23,6 @@ class TaskStorage:
self._lock = asyncio.Lock()
def _params_to_dict(self, params: TaskParams) -> dict:
"""Конвертирует TaskParams в словарь."""
return {
"url": params.url,
"task_type": params.task_type,
@@ -49,7 +48,6 @@ class TaskStorage:
}
def _dict_to_params(self, data: dict) -> TaskParams:
"""Конвертирует словарь в TaskParams."""
params = TaskParams(
url=data.get("url", ""),
task_type=data.get("task_type", "visit"),
@@ -72,44 +70,39 @@ class TaskStorage:
links_found=data.get("links_found", 0),
chat_id=data.get("chat_id"),
)
if data.get("started_at"):
try:
params.started_at = datetime.fromisoformat(data["started_at"])
except (ValueError, TypeError) as e:
logger.warning(f"Failed to parse started_at: {e}")
return params
def _save_sync(self, data: dict) -> None:
with open(self.file_path, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
def _load_sync(self) -> dict:
if not self.file_path.exists():
return {}
with open(self.file_path, "r", encoding="utf-8") as f:
return json.load(f)
async def save_tasks(self, tasks: Dict[str, TaskParams]) -> None:
"""Сохраняет все задачи в файл."""
async with self._lock:
try:
data = {}
for task_id, params in tasks.items():
data[task_id] = self._params_to_dict(params)
with open(self.file_path, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
data = {tid: self._params_to_dict(p) for tid, p in tasks.items()}
await asyncio.to_thread(self._save_sync, data)
logger.info(f"💾 Saved {len(data)} tasks to {self.file_path}")
except Exception as e:
logger.error(f"Save error: {e}")
async def load_tasks(self) -> Dict[str, TaskParams]:
"""Загружает задачи из файла."""
if not self.file_path.exists():
logger.info("No saved tasks found")
return {}
try:
with open(self.file_path, 'r', encoding='utf-8') as f:
data = json.load(f)
tasks = {}
for task_id, params_data in data.items():
tasks[task_id] = self._dict_to_params(params_data)
data = await asyncio.to_thread(self._load_sync)
if not data:
logger.info("No saved tasks found")
return {}
tasks = {tid: self._dict_to_params(d) for tid, d in data.items()}
logger.info(f"📂 Loaded {len(tasks)} tasks from {self.file_path}")
return tasks
except Exception as e:
@@ -117,13 +110,11 @@ class TaskStorage:
return {}
async def save_task(self, task_id: str, params: TaskParams) -> None:
"""Сохраняет одну задачу (добавляет к существующим)."""
tasks = await self.load_tasks()
tasks[task_id] = params
await self.save_tasks(tasks)
async def delete_task(self, task_id: str) -> None:
"""Удаляет задачу из файла."""
tasks = await self.load_tasks()
if task_id in tasks:
del tasks[task_id]
@@ -138,40 +129,39 @@ class ChatStorage:
self.file_path.parent.mkdir(parents=True, exist_ok=True)
self._lock = asyncio.Lock()
async def get_chat_streamers(self, chat_id: int) -> list:
"""Получает список стримеров для чата."""
data = await self._load()
return data.get(str(chat_id), [])
async def add_streamer(self, chat_id: int, channel: str) -> None:
"""Добавляет стримера в чат."""
data = await self._load()
key = str(chat_id)
if key not in data:
data[key] = []
if channel not in data[key]:
data[key].append(channel)
await self._save(data)
async def remove_streamer(self, chat_id: int, channel: str) -> None:
"""Удаляет стримера из чата."""
data = await self._load()
key = str(chat_id)
if key in data and channel in data[key]:
data[key].remove(channel)
await self._save(data)
async def _load(self) -> dict:
def _load_sync(self) -> dict:
if not self.file_path.exists():
return {}
try:
with open(self.file_path, 'r', encoding='utf-8') as f:
with open(self.file_path, "r", encoding="utf-8") as f:
return json.load(f)
except (json.JSONDecodeError, IOError) as e:
logger.warning(f"Failed to load chats: {e}")
return {}
async def _save(self, data: dict) -> None:
def _save_sync(self, data: dict) -> None:
with open(self.file_path, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
async def get_chat_streamers(self, chat_id: int) -> list:
async with self._lock:
with open(self.file_path, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
data = await asyncio.to_thread(self._load_sync)
return data.get(str(chat_id), [])
async def add_streamer(self, chat_id: int, channel: str) -> None:
async with self._lock:
data = await asyncio.to_thread(self._load_sync)
key = str(chat_id)
if key not in data:
data[key] = []
if channel not in data[key]:
data[key].append(channel)
await asyncio.to_thread(self._save_sync, data)
async def remove_streamer(self, chat_id: int, channel: str) -> None:
async with self._lock:
data = await asyncio.to_thread(self._load_sync)
key = str(chat_id)
if key in data and channel in data[key]:
data[key].remove(channel)
await asyncio.to_thread(self._save_sync, data)
View File
+2 -1
View File
@@ -1,5 +1,6 @@
"""Telegram message utilities."""
import asyncio
import logging
import os
from typing import Optional
@@ -69,7 +70,7 @@ async def send_visit_result(
)
screenshot_path = result.get('screenshot_path')
if screenshot_path and os.path.exists(screenshot_path):
if screenshot_path and await asyncio.to_thread(os.path.exists, screenshot_path):
photo = FSInputFile(screenshot_path)
await bot.send_photo(chat_id=chat_id, photo=photo, caption=text)
else: