Add Telegram Bot settings to example.env
This commit is contained in:
@@ -10,3 +10,7 @@ IMAP_PORT=993
|
|||||||
SMTP_HOST=smtp.gmail.com
|
SMTP_HOST=smtp.gmail.com
|
||||||
SMTP_PORT=587
|
SMTP_PORT=587
|
||||||
|
|
||||||
|
# Telegram Bot settings
|
||||||
|
TELEGRAM_BOT_TOKEN=your_bot_token_here
|
||||||
|
TELEGRAM_CHAT_ID=your_chat_id_here
|
||||||
|
|
||||||
|
|||||||
0
src/mail_order_bot/abcp_api/telegram/__init__.py
Normal file
0
src/mail_order_bot/abcp_api/telegram/__init__.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
"""
|
||||||
|
Обрабатывает письмо
|
||||||
|
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from mail_order_bot.task_processor.abstract_task import AbstractTask
|
||||||
|
from mail_order_bot.email_client.utils import EmailUtils
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class EmailParcer(AbstractTask):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def do(self) -> None:
|
||||||
|
# Определить клиента
|
||||||
|
email = self.context.data.get("email", None)
|
||||||
|
if email is not None:
|
||||||
|
email_body = EmailUtils.extract_body(email)
|
||||||
|
self.context.data["email_body"] = email_body
|
||||||
|
|
||||||
|
# todo при переводе на основной ящик переделать на другую функцию
|
||||||
|
email_from = EmailUtils.extract_first_sender(email_body)
|
||||||
|
self.context.data["email_from"] = email_from
|
||||||
|
|
||||||
|
email_subj = EmailUtils.extract_header(email, "subj")
|
||||||
|
self.context.data["email_subj"] = email_subj
|
||||||
|
|
||||||
|
client = EmailUtils.extract_domain(email_from)
|
||||||
|
self.context.data["client"] = client
|
||||||
|
|
||||||
|
attachments = EmailUtils.extract_attachments(email)
|
||||||
|
self.context.data["attachments"] = attachments
|
||||||
|
logger.info(f"Извлечено вложений: {len(attachments)} ")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import logging
|
||||||
|
import pandas as pd
|
||||||
|
from io import BytesIO
|
||||||
|
# from mail_order_bot.task_processor.handlers.order_position import OrderPosition
|
||||||
|
from mail_order_bot.task_processor.abstract_task import AbstractTask
|
||||||
|
|
||||||
|
|
||||||
|
from mail_order_bot.order.auto_part_position import PositionStatus
|
||||||
|
from mail_order_bot.parsers.excel_parcer import ExcelFileParcer
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateExcelFile(AbstractTask):
|
||||||
|
"""
|
||||||
|
Хендлер для каждого вложения считывает эксель файл и сохраняет его контекст
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.excel_config = self.config.get("excel", {})
|
||||||
|
|
||||||
|
def do(self) -> None:
|
||||||
|
# todo сделать проверку на наличие файла и его тип
|
||||||
|
|
||||||
|
attachments = self.context.data.get("attachments", [])
|
||||||
|
for attachment in attachments:
|
||||||
|
excel_file = attachment.get("excel")
|
||||||
|
order = attachment.get("order")
|
||||||
|
config = self.context.data.get("excel", {})
|
||||||
|
updatable_fields = config.get("updatable_fields", {})
|
||||||
|
|
||||||
|
for position in order.positions:
|
||||||
|
if position.status == PositionStatus.READY:
|
||||||
|
sku = position.sku
|
||||||
|
manufacturer = position.manufacturer
|
||||||
|
|
||||||
|
for key, value in updatable_fields.items():
|
||||||
|
|
||||||
|
if key == "ordered_quantity":
|
||||||
|
column = value
|
||||||
|
value = position.ordered_quantity
|
||||||
|
excel_file.set_value(sku, manufacturer, column, value)
|
||||||
|
|
||||||
|
if key == "ordered_price":
|
||||||
|
column = value
|
||||||
|
value = position.ordered_price
|
||||||
|
excel_file.set_value(sku, manufacturer, column, value)
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
4
src/mail_order_bot/telegram/__init__.py
Normal file
4
src/mail_order_bot/telegram/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
from mail_order_bot.telegram.client import TelegramClient
|
||||||
|
|
||||||
|
__all__ = ['TelegramClient']
|
||||||
|
|
||||||
176
src/mail_order_bot/telegram/client.py
Normal file
176
src/mail_order_bot/telegram/client.py
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import requests
|
||||||
|
from typing import Optional
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class TelegramClient:
|
||||||
|
"""
|
||||||
|
Класс для отправки сообщений через Telegram Bot API.
|
||||||
|
|
||||||
|
Поддерживает отправку:
|
||||||
|
- Текстовых сообщений
|
||||||
|
- Сообщений с вложением (Excel файл в формате BytesIO)
|
||||||
|
"""
|
||||||
|
|
||||||
|
BASE_URL = "https://api.telegram.org/bot"
|
||||||
|
|
||||||
|
def __init__(self, bot_token: Optional[str] = None, chat_id: Optional[str] = None):
|
||||||
|
"""
|
||||||
|
Инициализация TelegramClient.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
bot_token: Токен бота Telegram. Если не указан, берется из TELEGRAM_BOT_TOKEN
|
||||||
|
chat_id: ID чата для отправки сообщений. Если не указан, берется из TELEGRAM_CHAT_ID
|
||||||
|
"""
|
||||||
|
self.bot_token = bot_token or os.getenv('TELEGRAM_BOT_TOKEN')
|
||||||
|
self.chat_id = chat_id or os.getenv('TELEGRAM_CHAT_ID')
|
||||||
|
|
||||||
|
if not self.bot_token:
|
||||||
|
raise ValueError("Telegram bot token is required. Set TELEGRAM_BOT_TOKEN environment variable or pass bot_token parameter.")
|
||||||
|
|
||||||
|
if not self.chat_id:
|
||||||
|
raise ValueError("Telegram chat ID is required. Set TELEGRAM_CHAT_ID environment variable or pass chat_id parameter.")
|
||||||
|
|
||||||
|
self.api_url = f"{self.BASE_URL}{self.bot_token}"
|
||||||
|
|
||||||
|
def send_message(self, text: str, parse_mode: Optional[str] = None) -> dict:
|
||||||
|
"""
|
||||||
|
Отправляет текстовое сообщение в Telegram.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text: Текст сообщения для отправки
|
||||||
|
parse_mode: Режим парсинга (HTML, Markdown, MarkdownV2). По умолчанию None
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Результат запроса с полями success (bool) и data/error
|
||||||
|
"""
|
||||||
|
url = f"{self.api_url}/sendMessage"
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"chat_id": self.chat_id,
|
||||||
|
"text": text
|
||||||
|
}
|
||||||
|
|
||||||
|
if parse_mode:
|
||||||
|
payload["parse_mode"] = parse_mode
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(url, json=payload)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
result = response.json()
|
||||||
|
if result.get("ok"):
|
||||||
|
logger.debug(f"Сообщение успешно отправлено в Telegram")
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"data": result.get("result")
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
error_description = result.get("description", "Unknown error")
|
||||||
|
logger.warning(f"Ошибка отправки сообщения в Telegram: {error_description}")
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"error": error_description
|
||||||
|
}
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
logger.error(f"Ошибка при отправке сообщения в Telegram: {e}")
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"error": str(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
def send_document(
|
||||||
|
self,
|
||||||
|
document: BytesIO,
|
||||||
|
filename: str = "document.xlsx",
|
||||||
|
caption: Optional[str] = None,
|
||||||
|
parse_mode: Optional[str] = None
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
Отправляет документ (Excel файл) в Telegram.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
document: BytesIO объект с содержимым файла
|
||||||
|
filename: Имя файла для отправки (по умолчанию "document.xlsx")
|
||||||
|
caption: Подпись к документу (опционально)
|
||||||
|
parse_mode: Режим парсинга для подписи (HTML, Markdown, MarkdownV2). По умолчанию None
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Результат запроса с полями success (bool) и data/error
|
||||||
|
"""
|
||||||
|
url = f"{self.api_url}/sendDocument"
|
||||||
|
|
||||||
|
# Убедимся, что указатель файла находится в начале
|
||||||
|
document.seek(0)
|
||||||
|
|
||||||
|
files = {
|
||||||
|
"document": (filename, document, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||||
|
}
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"chat_id": self.chat_id
|
||||||
|
}
|
||||||
|
|
||||||
|
if caption:
|
||||||
|
data["caption"] = caption
|
||||||
|
|
||||||
|
if parse_mode:
|
||||||
|
data["parse_mode"] = parse_mode
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(url, files=files, data=data)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
result = response.json()
|
||||||
|
if result.get("ok"):
|
||||||
|
logger.debug(f"Документ успешно отправлен в Telegram")
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"data": result.get("result")
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
error_description = result.get("description", "Unknown error")
|
||||||
|
logger.warning(f"Ошибка отправки документа в Telegram: {error_description}")
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"error": error_description
|
||||||
|
}
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
logger.error(f"Ошибка при отправке документа в Telegram: {e}")
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"error": str(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
def send_message_with_document(
|
||||||
|
self,
|
||||||
|
text: str,
|
||||||
|
document: BytesIO,
|
||||||
|
filename: str = "document.xlsx",
|
||||||
|
parse_mode: Optional[str] = None
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
Отправляет сообщение с документом. Текст используется как подпись к документу.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text: Текст сообщения (будет использован как подпись к документу)
|
||||||
|
document: BytesIO объект с содержимым файла
|
||||||
|
filename: Имя файла для отправки (по умолчанию "document.xlsx")
|
||||||
|
parse_mode: Режим парсинга для подписи (HTML, Markdown, MarkdownV2). По умолчанию None
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Результат запроса с полями success (bool) и data/error
|
||||||
|
"""
|
||||||
|
return self.send_document(
|
||||||
|
document=document,
|
||||||
|
filename=filename,
|
||||||
|
caption=text,
|
||||||
|
parse_mode=parse_mode
|
||||||
|
)
|
||||||
|
|
||||||
Reference in New Issue
Block a user