отправка сообщения через бота
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2025-09-29 22:43:20 +03:00
parent 824a7978ac
commit af755115c8
4 changed files with 366 additions and 0 deletions
+132
View File
@@ -0,0 +1,132 @@
# Отправка сообщений через Telegram бота
## Описание
В проект добавлена возможность отправки сообщений пользователям Telegram через бота по их ID. Функциональность включает:
1. **API endpoint** для отправки сообщений из других скриптов
2. **Интерфейс в веб-приложении** для отправки сообщений через браузер
3. **Обработку ошибок** и логирование всех операций
## Использование через веб-интерфейс
В заголовке страницы "Управление постами VK и событиями" добавлены два поля:
- **ID пользователя** - числовой ID пользователя в Telegram
- **Сообщение** - текст сообщения для отправки
- **Кнопка "Отправить"** - отправляет сообщение через бота
### Особенности интерфейса:
- Поля проверяются на заполненность перед отправкой
- ID пользователя проверяется на корректность (должен быть числом)
- Поддержка отправки по клавише Enter
- Индикатор загрузки во время отправки
- Автоматическая очистка полей после успешной отправки
## Использование через API
### Endpoint
```
POST /send_message
```
### Параметры запроса
```json
{
"user_id": 123456789,
"message": "Текст сообщения"
}
```
### Ответы
**Успешная отправка (200):**
```json
{
"success": true,
"message": "Сообщение отправлено успешно"
}
```
**Ошибка (400):**
```json
{
"success": false,
"error": "Описание ошибки"
}
```
### Пример использования из Python
```python
import requests
def send_telegram_message(user_id, message):
url = "http://localhost:8443/send_message"
data = {
"user_id": user_id,
"message": message
}
response = requests.post(url, json=data)
result = response.json()
if result["success"]:
print("Сообщение отправлено успешно!")
else:
print(f"Ошибка: {result['error']}")
```
### Пример использования из командной строки
```bash
# Используя curl
curl -X POST http://localhost:8443/send_message \
-H "Content-Type: application/json" \
-d '{"user_id": 123456789, "message": "Привет!"}'
# Используя примерный скрипт
python send_telegram_example.py 123456789 "Привет! Это тестовое сообщение."
```
## Обработка ошибок
Система обрабатывает следующие типы ошибок:
1. **403 Forbidden** - Пользователь заблокировал бота или не начал с ним диалог
2. **400 Bad Request** - Неверный ID пользователя
3. **429 Too Many Requests** - Превышен лимит отправки сообщений
4. **Пустое сообщение** - Не указан текст сообщения
5. **Неверный формат ID** - user_id не является числом
Все ошибки логируются в файл логов с указанием типа ошибки и ID пользователя.
## Логирование
Все операции отправки сообщений записываются в лог с указанием:
- Времени операции
- ID пользователя
- Статуса отправки (успех/ошибка)
- Краткого описания ошибки (при наличии)
Пример записи в логе:
```
[2024-01-15 10:30:45] [TG_bot] ИНФО: Сообщение отправлено: Привет! Это тестовое сообщение... (Пользователь user_123456789)
```
## Требования
- Telegram бот должен быть запущен и подключен через webhook
- Пользователь должен начать диалог с ботом (отправить команду /start)
- Пользователь не должен заблокировать бота
## Безопасность
- API endpoint доступен только для POST запросов
- Проверка корректности входных данных
- Логирование всех попыток отправки для аудита
- Обработка ошибок без раскрытия внутренней информации системы
+70
View File
@@ -0,0 +1,70 @@
#!/usr/bin/env python3
"""
Пример скрипта для отправки сообщений через Telegram бота
Использование: python send_telegram_example.py <user_id> <message>
"""
import sys
import requests
import json
def send_message(user_id, message_text, bot_url="http://localhost:8443"):
"""
Отправляет сообщение пользователю через Telegram бота
Args:
user_id (int): ID пользователя в Telegram
message_text (str): Текст сообщения
bot_url (str): URL бота (по умолчанию localhost:8443)
Returns:
tuple: (success: bool, message: str)
"""
try:
url = f"{bot_url}/send_message"
data = {
"user_id": user_id,
"message": message_text
}
response = requests.post(url, json=data, timeout=10)
result = response.json()
if response.status_code == 200 and result.get("success"):
return True, result.get("message", "Сообщение отправлено успешно")
else:
return False, result.get("error", "Неизвестная ошибка")
except requests.exceptions.RequestException as e:
return False, f"Ошибка сети: {e}"
except json.JSONDecodeError as e:
return False, f"Ошибка парсинга ответа: {e}"
except Exception as e:
return False, f"Неожиданная ошибка: {e}"
def main():
if len(sys.argv) != 3:
print("Использование: python send_telegram_example.py <user_id> <message>")
print("Пример: python send_telegram_example.py 123456789 'Привет! Это тестовое сообщение.'")
sys.exit(1)
try:
user_id = int(sys.argv[1])
except ValueError:
print("Ошибка: user_id должен быть числом")
sys.exit(1)
message_text = sys.argv[2]
print(f"Отправка сообщения пользователю {user_id}: {message_text}")
success, result_message = send_message(user_id, message_text)
if success:
print(f"{result_message}")
else:
print(f"{result_message}")
sys.exit(1)
if __name__ == "__main__":
main()
+83
View File
@@ -388,6 +388,18 @@
<nav class="navbar navbar-expand-lg navbar-dark bg-dark"> <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid"> <div class="container-fluid">
<a class="navbar-brand" href="#">Управление постами VK и событиями</a> <a class="navbar-brand" href="#">Управление постами VK и событиями</a>
<!-- Поля для отправки сообщений через бота -->
<div class="navbar-nav me-auto">
<div class="d-flex align-items-center">
<input type="text" class="form-control form-control-sm me-2" id="telegram-user-id" placeholder="ID пользователя" style="width: 150px;">
<input type="text" class="form-control form-control-sm me-2" id="telegram-message" placeholder="Сообщение" style="width: 200px;">
<button class="btn btn-outline-light btn-sm" id="send-telegram-btn">
<i class="fas fa-paper-plane me-1"></i> Отправить
</button>
</div>
</div>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
@@ -2103,6 +2115,77 @@
window.hideProcessingMessage = hideProcessingMessage; window.hideProcessingMessage = hideProcessingMessage;
window.unlinkPostFromEvent = unlinkPostFromEvent; window.unlinkPostFromEvent = unlinkPostFromEvent;
window.unlinkEventFromPost = unlinkEventFromPost; window.unlinkEventFromPost = unlinkEventFromPost;
// Функция для отправки сообщения через Telegram бота
async function sendTelegramMessage() {
const userId = document.getElementById('telegram-user-id').value.trim();
const message = document.getElementById('telegram-message').value.trim();
// Проверяем, что оба поля заполнены
if (!userId || !message) {
alert('Пожалуйста, заполните оба поля: ID пользователя и сообщение');
return;
}
// Проверяем, что user_id является числом
if (isNaN(userId)) {
alert('ID пользователя должен быть числом');
return;
}
// Показываем индикатор загрузки
const sendBtn = document.getElementById('send-telegram-btn');
const originalText = sendBtn.innerHTML;
sendBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i> Отправка...';
sendBtn.disabled = true;
try {
const response = await fetch('/send_message', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
user_id: parseInt(userId),
message: message
})
});
const result = await response.json();
if (result.success) {
alert('Сообщение отправлено успешно!');
// Очищаем поля
document.getElementById('telegram-user-id').value = '';
document.getElementById('telegram-message').value = '';
} else {
alert('Ошибка отправки сообщения: ' + result.error);
}
} catch (error) {
console.error('Ошибка при отправке сообщения:', error);
alert('Произошла ошибка при отправке сообщения. Проверьте консоль для подробностей.');
} finally {
// Восстанавливаем кнопку
sendBtn.innerHTML = originalText;
sendBtn.disabled = false;
}
}
// Добавляем обработчик события для кнопки отправки
document.getElementById('send-telegram-btn').addEventListener('click', sendTelegramMessage);
// Добавляем обработчик для отправки по Enter в полях
document.getElementById('telegram-user-id').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
document.getElementById('telegram-message').focus();
}
});
document.getElementById('telegram-message').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
sendTelegramMessage();
}
});
</script> </script>
<script> <script>
// Column resizer setup // Column resizer setup
+81
View File
@@ -733,6 +733,49 @@ def send_post_message(chat_id, text, image_url=None, reply_markup=None):
logger.error(f"Ошибка отправки сообщения: {e}") logger.error(f"Ошибка отправки сообщения: {e}")
return False return False
def send_direct_message(user_id, message_text):
"""Отправляет сообщение пользователю по ID с обработкой ошибок"""
try:
# Проверяем, что сообщение не пустое
if not message_text or not message_text.strip():
error_msg = "Пустое сообщение"
logger.warning(error_msg)
log_event("error", f"user_{user_id}", message=error_msg)
return False, error_msg
# Отправляем сообщение
bot.send_message(
user_id,
message_text,
parse_mode='HTML'
)
# Логируем успешную отправку
log_event("info", f"user_{user_id}", message=f"Сообщение отправлено: {message_text[:50]}...")
logger.info(f"Сообщение успешно отправлено пользователю {user_id}")
return True, "Сообщение отправлено успешно"
except telebot.apihelper.ApiTelegramException as e:
error_msg = f"Ошибка Telegram API: {e}"
logger.error(error_msg)
log_event("error", f"user_{user_id}", message=error_msg)
# Определяем тип ошибки для более информативного ответа
if e.error_code == 403:
return False, "Пользователь заблокировал бота или не начал с ним диалог"
elif e.error_code == 400:
return False, "Неверный ID пользователя"
elif e.error_code == 429:
return False, "Превышен лимит отправки сообщений"
else:
return False, f"Ошибка Telegram: {e.description}"
except Exception as e:
error_msg = f"Неожиданная ошибка при отправке сообщения: {e}"
logger.error(error_msg)
log_event("error", f"user_{user_id}", message=error_msg)
return False, f"Неожиданная ошибка: {str(e)}"
def send_management_message(chat_id, post_id, user_id, user_name): def send_management_message(chat_id, post_id, user_id, user_name):
"""Отправляет сообщение для управления подпиской на посты""" """Отправляет сообщение для управления подпиской на посты"""
try: try:
@@ -1234,6 +1277,44 @@ def webhook():
log_event("error", "system", message=error_msg) log_event("error", "system", message=error_msg)
return "Internal Server Error", 500 return "Internal Server Error", 500
@app.route('/send_message', methods=['POST'])
def send_message_api():
"""API endpoint для отправки сообщений через бота"""
try:
data = request.get_json()
if not data:
return {"success": False, "error": "Отсутствуют данные"}, 400
user_id = data.get('user_id')
message_text = data.get('message')
if not user_id:
return {"success": False, "error": "Не указан user_id"}, 400
if not message_text:
return {"success": False, "error": "Не указан текст сообщения"}, 400
# Проверяем, что user_id является числом
try:
user_id = int(user_id)
except (ValueError, TypeError):
return {"success": False, "error": "user_id должен быть числом"}, 400
# Отправляем сообщение
success, result_message = send_direct_message(user_id, message_text)
if success:
return {"success": True, "message": result_message}, 200
else:
return {"success": False, "error": result_message}, 400
except Exception as e:
error_msg = f"Ошибка в API отправки сообщений: {e}"
logger.error(error_msg)
log_event("error", "system", message=error_msg)
return {"success": False, "error": "Внутренняя ошибка сервера"}, 500
if __name__ == '__main__': if __name__ == '__main__':
# Логируем запуск бота # Логируем запуск бота
log_event("system", "system", message="Бот запущен") log_event("system", "system", message="Бот запущен")