From 26c59ffb82de9db35ff09e2c5225dec9adf40fe1 Mon Sep 17 00:00:00 2001 From: Whyverum Date: Fri, 3 Oct 2025 09:31:12 +0700 Subject: [PATCH] commit --- .dockerignore | 36 ++++++++++++++++++ .env.example | 32 ++++++++++++++++ .gitattributes | 97 +++++++++++++++++++++++++++++++++++++++++++++++ .gitignore | 74 ++++++++++++++++++++++++++++++++++++ Dockerfile | 26 +++++++++++++ LICENSE | 21 ++++++++++ README.md | Bin 0 -> 26688 bytes assets/image.jpg | Bin 0 -> 32459 bytes code/__init__.py | 4 ++ code/config.py | 84 ++++++++++++++++++++++++++++++++++++++++ code/logs.py | 30 +++++++++++++++ code/media.py | 37 ++++++++++++++++++ code/sender.py | 79 ++++++++++++++++++++++++++++++++++++++ main.py | 30 +++++++++++++++ pyproject.toml | 23 +++++++++++ 15 files changed, 573 insertions(+) create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 assets/image.jpg create mode 100644 code/__init__.py create mode 100644 code/config.py create mode 100644 code/logs.py create mode 100644 code/media.py create mode 100644 code/sender.py create mode 100644 main.py create mode 100644 pyproject.toml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..59012c9 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,36 @@ +# Исключить скрытые системные каталоги, но не всё подряд +.git/ +.gitattributes +.gitignore + +# Виртуальные окружения и Python-кэш +.venv/ +venv/ +__pycache__/ +*.py[cod] +*.pyo + +# IDE-файлы +.idea/ +.vscode/ + +# Тесты и документация +tests/ +test/ +docs/ +examples/ + +# Логи и артефакты сборки +*.log +*.logs +*.log.* +*.logs.* +Logs/ +Log/ +dist/ +build/ + +# Примеры и шаблоны +.env +env +*.session diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..99b1586 --- /dev/null +++ b/.env.example @@ -0,0 +1,32 @@ +# Общие параметры Python +PYTHONUNBUFFERED=1 + +# API для клиента Telegram +API_ID=1234567 +API_HASH=abcdef1234567890abcdef1234567890 + +# Телефонный аккаунт (если используется) +PHONE_NUMBER=+71234567890 +PASSWORD=your_password_here + +# Настройки бота +BOT_TOKEN=1234567890:ABCDefGhIJKlmNoPQRsTUVwxyZ +BOT_USERNAME=my_bot_username + +# Режим работы: user или bot +ACCOUNT_MODE=bot + +# Отправлять фото (True или False) +MSG_PHOTO=True + +# Период работы в секундах +PERIOD=3600 + +# Файл по умолчанию для отправки +DEFAULT_PHOTO=image.png + +# Текст сообщения +TEXT_MESSAGE='Приветствую, меня зовут Инокендий\n#флуд #ролевая #геншинимпакт #геншин #flood #rp #genshin' + +# Список ID групп (можно оставить пустым, если не используется) +GROUP_IDS=-1003057872759:0,-1002417346920:2,-1003019408279:0 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5a679a5 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,97 @@ +# ============================================================================= +# Git LFS: большие бинарные файлы, модели, архивы +# ============================================================================= +*.7z filter=lfs diff=lfs merge=lfs -text +*.arrow filter=lfs diff=lfs merge=lfs -text +*.bin filter=lfs diff=lfs merge=lfs -text +*.bz2 filter=lfs diff=lfs merge=lfs -text +*.ckpt filter=lfs diff=lfs merge=lfs -text +*.ftz filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.h5 filter=lfs diff=lfs merge=lfs -text +*.joblib filter=lfs diff=lfs merge=lfs -text +*.lfs.* filter=lfs diff=lfs merge=lfs -text +*.mlmodel filter=lfs diff=lfs merge=lfs -text +*.model filter=lfs diff=lfs merge=lfs -text +*.msgpack filter=lfs diff=lfs merge=lfs -text +*.npy filter=lfs diff=lfs merge=lfs -text +*.npz filter=lfs diff=lfs merge=lfs -text +*.onnx filter=lfs diff=lfs merge=lfs -text +*.ot filter=lfs diff=lfs merge=lfs -text +*.parquet filter=lfs diff=lfs merge=lfs -text +*.pb filter=lfs diff=lfs merge=lfs -text +*.pickle filter=lfs diff=lfs merge=lfs -text +*.pkl filter=lfs diff=lfs merge=lfs -text +*.pt filter=lfs diff=lfs merge=lfs -text +*.pth filter=lfs diff=lfs merge=lfs -text +*.rar filter=lfs diff=lfs merge=lfs -text +*.safetensors filter=lfs diff=lfs merge=lfs -text +saved_model/**/* filter=lfs diff=lfs merge=lfs -text +*.tar.* filter=lfs diff=lfs merge=lfs -text +*.tar filter=lfs diff=lfs merge=lfs -text +*.tflite filter=lfs diff=lfs merge=lfs -text +*.tgz filter=lfs diff=lfs merge=lfs -text +*.wasm filter=lfs diff=lfs merge=lfs -text +*.xz filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.zst filter=lfs diff=lfs merge=lfs -text +*tfevents* filter=lfs diff=lfs merge=lfs -text + +# ============================================================================= +# Автоопределение текста, окончания строк +# ============================================================================= +* text=auto eol=lf + +# ============================================================================= +# Текстовые файлы (Python, конфиги, документы) +# ============================================================================= +*.py text +*.pyi text +*.ipynb text +*.html text +*.css text +*.js text +*.json text +*.md text +*.yml text +*.yaml text +*.xml text +*.txt text +*.cfg text +*.toml text +*.ini text +*.env text + +# ============================================================================= +# Изображения +# ============================================================================= +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.bmp binary +*.webp binary +*.ico binary +*.svg text + +# ============================================================================= +# Шрифты +# ============================================================================= +*.eot binary +*.ttf binary +*.woff binary +*.woff2 binary +*.otf binary + +# ============================================================================= +# GitHub Linguist (указание языка для отображения) +# ============================================================================= +*.py linguist-language=Python +*.ipynb linguist-language=Jupyter Notebook +*.html linguist-language=HTML +*.css linguist-language=CSS +*.js linguist-language=JavaScript +*.json linguist-language=JSON +*.md linguist-language=Markdown +*.yml linguist-language=YAML +*.yaml linguist-language=YAML diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5db36f8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,74 @@ +# .gitignore: Игнорируемые файлы для Python проектов +# Подробнее: https://github.com/github/gitignore/blob/main/Python.gitignore + +### Python ### +# Виртуальные окружения и настройки +.venv +.env +env +venv/ +env/ +env.bak/ +venv.bak/ + +# Кэш интерпретатора +__pycache__/ +*.py[cod] +*$py.class + +# Пакеты и сборки +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.eg +*.egg +*.eggs + +# Poetry +poetry.lock +.pypoetry/ + +### Логи и БД ### +*.log +*.logs +*.log.* +*.logs.* +log/ +logs/ +*.sqlite +*.db +*.session + +### IDE ### +.idea/ +.vscode/ +*.swp +*.sublime-* + +### OS ### +.DS_Store +Thumbs.db + +### Тестирование ### +.coverage +htmlcov/ +.tox/ +.nox/ +.pytest_cache/ +.mypy_cache/ +test/ +tests/ +Test/ +Tests/ +count.py diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4cbd8d7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +# Используем официальный образ Python с подходящей версией +FROM python:3.12-slim + +# Устанавливаем Poetry +RUN pip install poetry + +# Устанавливаем рабочую директорию внутри контейнера +WORKDIR /app + +# Копируем файлы Poetry +COPY pyproject.toml poetry.lock* ./ + +# Настраиваем Poetry (не создавать виртуальное окружение внутри контейнера) +RUN poetry config virtualenvs.create false + +# Устанавливаем зависимости через Poetry +RUN poetry install --no-interaction --no-ansi --no-root + +# Копируем все файлы проекта внутрь контейнера +COPY . . + +# Устанавливаем переменную окружения для буферизации +ENV PYTHONUNBUFFERED=1 + +# Команда запуска — запуск скрипта main.py +CMD ["python", "main.py"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..889866f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) [2025] [Verum] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e963fd802188e89b98f08709dc1864b21eb8a6b8 GIT binary patch literal 26688 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 literal 0 HcmV?d00001 diff --git a/code/__init__.py b/code/__init__.py new file mode 100644 index 0000000..2583371 --- /dev/null +++ b/code/__init__.py @@ -0,0 +1,4 @@ +from .config import * +from .logs import * +from .media import * +from .sender import * diff --git a/code/config.py b/code/config.py new file mode 100644 index 0000000..53fd1bd --- /dev/null +++ b/code/config.py @@ -0,0 +1,84 @@ +from typing import Dict, Optional +from pydantic import field_validator +from pydantic_settings import BaseSettings, SettingsConfigDict + +__all__ = ("settings",) + + +class Settings(BaseSettings): + """Конфигурация основных режимов и параметров с валидацией""" + + model_config = SettingsConfigDict( + env_file=".env", + env_file_encoding="utf-8", + extra="ignore", + case_sensitive=False, + ) + + # Режимы и базовые параметры + PYTHONUNBUFFERED: str = "1" + API_ID: Optional[int] = None + API_HASH: Optional[str] = None + SOURCE_CHANNEL: Optional[int] = None + BOT_TOKEN: Optional[str] = None + BOT_USERNAME: Optional[str] = None + PHONE_NUMBER: Optional[str] = None + PASSWORD: Optional[str] = None + ACCOUNT_MODE: bool = True # True = аккаунт, False = бот + MSG_PHOTO: bool = True # True = фото, False = inline + PERIOD: int = 3600 + + # Файл по умолчанию для отправки + DEFAULT_PHOTO: str = "assets/image.jpg" + TEXT_MESSAGE: str = ( + "Приветствую, меня зовут Инокендий\n" + "#флуд #ролевая #геншинимпакт #геншин #flood #rp #genshin" + ) + + # Словарь групп: {chat_id: reply_to_message_id} + GROUP_IDS: Dict[int, Optional[int]] = {} + + # ================== Валидаторы ================== + @field_validator('PYTHONUNBUFFERED') + def validate_unbuffered(cls, v: str) -> str: + if v not in ('0', '1'): + raise ValueError("PYTHONUNBUFFERED должен быть '0' или '1'") + return v + + @field_validator('ACCOUNT_MODE') + def validate_account_mode(cls, v: bool) -> bool: + if not isinstance(v, bool): + raise ValueError("ACCOUNT_MODE должен быть булевым: True = аккаунт, False = бот") + return v + + @field_validator('PERIOD') + def validate_period(cls, v: int) -> int: + if v <= 0: + raise ValueError("PERIOD должен быть положительным числом") + return v + + @field_validator('API_ID') + def validate_api_id(cls, v: int) -> int: + if v is None or v <= 0: + raise ValueError("API_ID должен быть положительным числом") + return v + + @field_validator('GROUP_IDS', mode='before') + def parse_group_ids(cls, v): + """ + Конвертирует строку вида "-1003057872759:0,-1002417346920:2" + в словарь {chat_id: reply_to_message_id} + """ + if isinstance(v, str): + try: + return {int(k): int(val) for k, val in (x.split(":") for x in v.split(","))} + except Exception: + raise ValueError( + "Неправильный формат GROUP_IDS. " + "Пример: -100123:0,-100456:2" + ) + return v + + +# Экземпляр класса +settings: Settings = Settings() diff --git a/code/logs.py b/code/logs.py new file mode 100644 index 0000000..388e720 --- /dev/null +++ b/code/logs.py @@ -0,0 +1,30 @@ +from sys import stderr as console +from loguru import logger + +_all__ = ("setup_logger",) + + +def setup_logger(max_size: str = "500 MB") -> None: + """Настройка логгера для приложения""" + logger.remove() + + info_format: str = ( + "{time:YYYY-MM-DD HH:mm:ss} | " + "PRIMO-Message | " + "{extra[user]} | {message}" + ) + error_format: str = ( + "{time:YYYY-MM-DD HH:mm:ss} | " + "PRIMO-ERROR | " + "{extra[user]} | {message}" + ) + + # INFO + logger.add(console, colorize=True, format=info_format, level="INFO") + logger.add("start.log", rotation=max_size, format=info_format, level="INFO") + + # ERROR + logger.add(console, colorize=True, format=error_format, level="ERROR") + logger.add("error.log", rotation=max_size, format=error_format, level="ERROR") + + logger.bind(user="@Console").info("Программа запущена!") diff --git a/code/media.py b/code/media.py new file mode 100644 index 0000000..5838b53 --- /dev/null +++ b/code/media.py @@ -0,0 +1,37 @@ +from glob import glob +from loguru import logger +from typing import Optional + +from .config import settings + +__all__ = ("find_photo",) + + +class PhotoCache: + _cache: Optional[bytes] = None + + @classmethod + async def find_photo(cls, file: str = None) -> bytes: + """ + Загружает фото в память и возвращает его как байты. + """ + if cls._cache: + return cls._cache + + pattern: str = file or settings.DEFAULT_PHOTO + files: list[str] = glob(pattern) + if not files: + logger.bind(user="@Console").error(f"Файл {pattern} не найден.") + raise FileNotFoundError(f"Файл {pattern} не найден.") + + chosen_file: str = files[0] + logger.bind(user="@Console").info(f"Выбран файл: {chosen_file}") + + with open(chosen_file, "rb") as f: + cls._cache = f.read() + + return cls._cache + + +# Создаем функцию для обратной совместимости +find_photo = PhotoCache.find_photo diff --git a/code/sender.py b/code/sender.py new file mode 100644 index 0000000..003bf1a --- /dev/null +++ b/code/sender.py @@ -0,0 +1,79 @@ +from typing import Optional +from asyncio import sleep +from pyrogram import Client +from pyrogram.types import Message +from loguru import logger + +from .config import settings + +__all__ = ("send_inline_request", "copy_channel_message", "periodic_send",) + + +async def send_inline_request(client: Client) -> None: + """Отправка inline-запроса от имени бота.""" + for group_id in settings.GROUP_IDS.keys(): + try: + inline_results = await client.get_inline_bot_results( + settings.BOT_USERNAME, "Реклама" + ) + + if not inline_results.results: + logger.bind(user=group_id).warning( + f"Нет inline-результатов для группы {group_id}" + ) + continue + + result_id = inline_results.results[0].id + await client.send_inline_bot_result( + chat_id=group_id, + query_id=inline_results.query_id, + result_id=result_id, + ) + logger.bind(user=group_id).info(f"Inline результат отправлен в {group_id}") + + except Exception as e: + logger.bind(user=group_id).error(f"Ошибка inline: {e}") + + +async def copy_channel_message(client: Client) -> None: + """Копирование последнего сообщения с канала и отправка в группы без авторства.""" + message: Optional[Message] = None + + try: + # Получаем последнее сообщение с канала + async for msg in client.get_chat_history(settings.SOURCE_CHANNEL, limit=1): + message = msg + break # берём только первое (последнее) сообщение + + if not message: + logger.bind(user="@Console").warning("Нет сообщений для копирования") + return + + except Exception as e: + logger.bind(user="@Console").error(f"Не удалось получить сообщение с канала: {e}") + return + + for group_id, reply_id in settings.GROUP_IDS.items(): + try: + # Копируем сообщение без авторства + await client.copy_message( + chat_id=group_id, + from_chat_id=settings.SOURCE_CHANNEL, + message_id=message.id, # <-- используем id вместо message_id + reply_to_message_id=reply_id, + ) + + logger.bind(user=group_id).info(f"Сообщение скопировано в {group_id}") + except Exception as e: + logger.bind(user=group_id).error(f"Ошибка при отправке сообщения: {e}") + + +async def periodic_send(client: Client) -> None: + """Цикл отправки сообщений с заданным периодом.""" + while True: + if settings.MSG_PHOTO: + # Старый функционал фотографий заменяем на копирование сообщений + await copy_channel_message(client) + else: + await send_inline_request(client) + await sleep(settings.PERIOD) diff --git a/main.py b/main.py new file mode 100644 index 0000000..4e1f843 --- /dev/null +++ b/main.py @@ -0,0 +1,30 @@ +from asyncio import run +from pyrogram import Client + +from code import * + +async def main() -> None: + setup_logger() + + if settings.ACCOUNT_MODE: + async with Client( + name="user_session", + api_id=settings.API_ID, + api_hash=settings.API_HASH, + phone_number=settings.PHONE_NUMBER, + password=settings.PASSWORD, + ) as client: + await periodic_send(client) + + else: + async with Client( + name="bot_session", + api_id=settings.API_ID, + api_hash=settings.API_HASH, + bot_token=settings.BOT_TOKEN, + ) as client: + await periodic_send(client) + + +if __name__ == "__main__": + run(main()) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..21be85d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,23 @@ +[project] +name = "reklamabot" +version = "0.1.0" +description = "None" +authors = [ + {name = "Verum"} +] +license = {text = "MIT License"} +readme = "README.md" +requires-python = ">=3.10,<4.0" +dependencies = [ + "loguru (>=0.7.3,<0.8.0)", + "pyrogram (>=2.0.106,<3.0.0)", + "dotenv (>=0.9.9,<0.10.0)", + "python-dotenv (>=1.1.1,<2.0.0)", + "pydantic (>=2.11.9,<3.0.0)", + "pydantic-settings (>=2.11.0,<3.0.0)" +] + + +[build-system] +requires = ["poetry-core>=2.0.0,<3.0.0"] +build-backend = "poetry.core.masonry.api"