commit
This commit is contained in:
36
.dockerignore
Normal file
36
.dockerignore
Normal file
@@ -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
|
||||||
32
.env.example
Normal file
32
.env.example
Normal file
@@ -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
|
||||||
97
.gitattributes
vendored
Normal file
97
.gitattributes
vendored
Normal file
@@ -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
|
||||||
74
.gitignore
vendored
Normal file
74
.gitignore
vendored
Normal file
@@ -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
|
||||||
26
Dockerfile
Normal file
26
Dockerfile
Normal file
@@ -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"]
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -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.
|
||||||
BIN
assets/image.jpg
Normal file
BIN
assets/image.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
4
code/__init__.py
Normal file
4
code/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
from .config import *
|
||||||
|
from .logs import *
|
||||||
|
from .media import *
|
||||||
|
from .sender import *
|
||||||
84
code/config.py
Normal file
84
code/config.py
Normal file
@@ -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()
|
||||||
30
code/logs.py
Normal file
30
code/logs.py
Normal file
@@ -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 = (
|
||||||
|
"<green>{time:YYYY-MM-DD HH:mm:ss}</green> | "
|
||||||
|
"<blue>PRIMO-Message</blue> | "
|
||||||
|
"<cyan>{extra[user]}</cyan> | <level>{message}</level>"
|
||||||
|
)
|
||||||
|
error_format: str = (
|
||||||
|
"<red>{time:YYYY-MM-DD HH:mm:ss}</red> | "
|
||||||
|
"<bold>PRIMO-ERROR</bold> | "
|
||||||
|
"{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("Программа запущена!")
|
||||||
37
code/media.py
Normal file
37
code/media.py
Normal file
@@ -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
|
||||||
79
code/sender.py
Normal file
79
code/sender.py
Normal file
@@ -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)
|
||||||
30
main.py
Normal file
30
main.py
Normal file
@@ -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())
|
||||||
23
pyproject.toml
Normal file
23
pyproject.toml
Normal file
@@ -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"
|
||||||
Reference in New Issue
Block a user