This commit is contained in:
@@ -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 запросов
|
||||||
|
- Проверка корректности входных данных
|
||||||
|
- Логирование всех попыток отправки для аудита
|
||||||
|
- Обработка ошибок без раскрытия внутренней информации системы
|
||||||
@@ -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()
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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="Бот запущен")
|
||||||
|
|||||||
Reference in New Issue
Block a user