Bump version to 1.1.1: Fix Telegram webhook handling and chat_id saving
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m33s

- Fix TelegramUpdate struct to handle message and edited_message properly
- Fix chat_id saving for messages without text
- Add comprehensive logging for webhook registration
- Improve error handling and diagnostics
This commit is contained in:
poignatov
2025-12-31 19:39:01 +03:00
parent 7398918bc0
commit f8aa81f963
4 changed files with 118 additions and 19 deletions

63
.env.bak Normal file
View File

@@ -0,0 +1,63 @@
# ============================================
# Единый файл конфигурации для всех проектов
# Backend и Play-Life-Web
# ============================================
# ============================================
# Database Configuration
# ============================================
DB_HOST=localhost
DB_PORT=5432
DB_USER=playeng
DB_PASSWORD=playeng
DB_NAME=playeng
# ============================================
# Backend Server Configuration
# ============================================
# Порт для backend сервера (по умолчанию: 8080)
# В production всегда используется порт 8080 внутри контейнера
PORT=8080
# ============================================
# Play Life Web Configuration
# ============================================
# Порт для frontend приложения play-life-web
WEB_PORT=3001
# ============================================
# Telegram Bot Configuration (optional)
# ============================================
# Get token from @BotFather in Telegram: https://t.me/botfather
# To get chat ID: send a message to your bot, then visit: https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getUpdates
# Look for "chat":{"id":123456789} - that number is your chat ID
TELEGRAM_BOT_TOKEN=your_telegram_bot_token_here
TELEGRAM_CHAT_ID=123456789
# Base URL для автоматической настройки webhook
# Примеры:
# - Для production с HTTPS: https://your-domain.com
# - Для локальной разработки с ngrok: https://abc123.ngrok.io
# - Для прямого доступа на нестандартном порту: http://your-server:8080
# Webhook будет настроен автоматически при старте сервера на: <TELEGRAM_WEBHOOK_BASE_URL>/webhook/telegram
# Если не указан, webhook нужно настраивать вручную
TELEGRAM_WEBHOOK_BASE_URL=https://your-domain.com
# ============================================
# Todoist Webhook Configuration (optional)
# ============================================
# Секрет для проверки подлинности webhook от Todoist
# Если задан, все запросы должны содержать заголовок X-Todoist-Webhook-Secret с этим значением
# Оставьте пустым, если не хотите использовать проверку секрета
TODOIST_WEBHOOK_SECRET=
# ============================================
# Scheduler Configuration
# ============================================
# Часовой пояс для планировщика задач (например: Europe/Moscow, America/New_York, UTC)
# Используется для:
# - Автоматической фиксации целей на неделю каждый понедельник в 6:00
# - Отправки ежедневного отчёта в 23:59
# ВАЖНО: Укажите правильный часовой пояс, иначе задачи будут срабатывать в UTC!
# Список доступных часовых поясов: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
TIMEZONE=Europe/Moscow

View File

@@ -1,2 +1,2 @@
1.1.0
1.1.1

View File

@@ -183,7 +183,8 @@ type TelegramWebhook struct {
// TelegramUpdate - структура для Telegram webhook (обычно это Update объект)
type TelegramUpdate struct {
UpdateID int `json:"update_id"`
Message TelegramMessage `json:"message"`
Message *TelegramMessage `json:"message,omitempty"`
EditedMessage *TelegramMessage `json:"edited_message,omitempty"`
}
type App struct {
@@ -2374,20 +2375,22 @@ func main() {
}
// Пытаемся настроить webhook автоматически при старте, если есть base URL и bot token в БД
// Это опционально - основная регистрация происходит при сохранении токена через UI
webhookBaseURL := getEnv("WEBHOOK_BASE_URL", "")
if webhookBaseURL != "" {
integration, err := app.getTelegramIntegration()
if err == nil && integration.BotToken != nil && *integration.BotToken != "" {
webhookURL := strings.TrimRight(webhookBaseURL, "/") + "/webhook/telegram"
log.Printf("Attempting to setup Telegram webhook at startup. WEBHOOK_BASE_URL='%s'", webhookBaseURL)
if err := setupTelegramWebhook(*integration.BotToken, webhookURL); err != nil {
log.Printf("Warning: Failed to setup Telegram webhook at startup: %v. Webhook will be configured when user saves bot token.", err)
} else {
log.Printf("Telegram webhook configured successfully at startup: %s", webhookURL)
log.Printf("SUCCESS: Telegram webhook configured successfully at startup: %s", webhookURL)
}
} else {
log.Printf("Telegram bot token not found in database. Webhook will be configured when user saves bot token.")
}
} else {
log.Printf("WEBHOOK_BASE_URL not set. Webhook will be configured when user saves bot token.")
}
// Инициализируем БД для play-life проекта
@@ -2469,6 +2472,7 @@ func getMapKeys(m map[string]interface{}) []string {
// setupTelegramWebhook настраивает webhook для Telegram бота
func setupTelegramWebhook(botToken, webhookURL string) error {
apiURL := fmt.Sprintf("https://api.telegram.org/bot%s/setWebhook", botToken)
log.Printf("Setting up Telegram webhook: apiURL=%s, webhookURL=%s", apiURL, webhookURL)
payload := map[string]string{
"url": webhookURL,
@@ -2486,12 +2490,15 @@ func setupTelegramWebhook(botToken, webhookURL string) error {
resp, err := client.Post(apiURL, "application/json", bytes.NewBuffer(jsonData))
if err != nil {
log.Printf("ERROR: Failed to send webhook setup request: %v", err)
return fmt.Errorf("failed to send webhook setup request: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
bodyBytes, _ := io.ReadAll(resp.Body)
log.Printf("Telegram API response: status=%d, body=%s", resp.StatusCode, string(bodyBytes))
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("telegram API returned status %d: %s", resp.StatusCode, string(bodyBytes))
}
@@ -4100,24 +4107,51 @@ func (a *App) telegramWebhookHandler(w http.ResponseWriter, r *http.Request) {
return
}
// Сохраняем chat_id при первом сообщении
if update.Message.Chat.ID != 0 {
chatIDStr := strconv.FormatInt(update.Message.Chat.ID, 10)
// Определяем, какое сообщение использовать (message или edited_message)
var message *TelegramMessage
if update.Message != nil {
message = update.Message
log.Printf("Telegram webhook received: update_id=%d, message type=message", update.UpdateID)
} else if update.EditedMessage != nil {
message = update.EditedMessage
log.Printf("Telegram webhook received: update_id=%d, message type=edited_message", update.UpdateID)
} else {
log.Printf("Telegram webhook received: update_id=%d, but no message or edited_message found", update.UpdateID)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"message": "No message found in update",
})
return
}
log.Printf("Telegram webhook: message present, chat_id=%d", message.Chat.ID)
// Сохраняем chat_id при первом сообщении (даже если нет текста)
if message.Chat.ID != 0 {
chatIDStr := strconv.FormatInt(message.Chat.ID, 10)
log.Printf("Processing chat_id: %s", chatIDStr)
integration, err := a.getTelegramIntegration()
if err == nil {
if err != nil {
log.Printf("Error getting telegram integration: %v", err)
} else {
// Сохраняем chat_id, если его еще нет
if integration.ChatID == nil || *integration.ChatID == "" {
log.Printf("Attempting to save chat_id: %s", chatIDStr)
if err := a.saveTelegramChatID(chatIDStr); err != nil {
log.Printf("Warning: Failed to save chat_id: %v", err)
} else {
log.Printf("Saved chat_id from first message: %s", chatIDStr)
}
log.Printf("Successfully saved chat_id from first message: %s", chatIDStr)
}
} else {
log.Printf("Chat_id already exists in database: %s", *integration.ChatID)
}
}
} else {
log.Printf("Warning: message.Chat.ID is 0, cannot save chat_id")
}
// Проверяем, что есть message
if update.Message.Text == "" {
// Проверяем, что есть текст в сообщении
if message.Text == "" {
log.Printf("Telegram webhook: no text in message")
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
@@ -4126,8 +4160,8 @@ func (a *App) telegramWebhookHandler(w http.ResponseWriter, r *http.Request) {
return
}
fullText := update.Message.Text
entities := update.Message.Entities
fullText := message.Text
entities := message.Entities
if entities == nil {
entities = []TelegramEntity{}
}
@@ -4278,16 +4312,18 @@ func (a *App) updateTelegramIntegrationHandler(w http.ResponseWriter, r *http.Re
// Настраиваем webhook автоматически при сохранении токена
webhookBaseURL := getEnv("WEBHOOK_BASE_URL", "")
log.Printf("Attempting to setup Telegram webhook. WEBHOOK_BASE_URL='%s'", webhookBaseURL)
if webhookBaseURL != "" {
webhookURL := strings.TrimRight(webhookBaseURL, "/") + "/webhook/telegram"
log.Printf("Setting up Telegram webhook: URL=%s", webhookURL)
if err := setupTelegramWebhook(req.BotToken, webhookURL); err != nil {
log.Printf("Warning: Failed to setup Telegram webhook: %v", err)
log.Printf("ERROR: Failed to setup Telegram webhook: %v", err)
// Не возвращаем ошибку, так как токен уже сохранен
} else {
log.Printf("Telegram webhook configured successfully: %s", webhookURL)
log.Printf("SUCCESS: Telegram webhook configured successfully: %s", webhookURL)
}
} else {
log.Printf("Warning: WEBHOOK_BASE_URL not set. Webhook will not be configured automatically.")
log.Printf("WARNING: WEBHOOK_BASE_URL not set. Webhook will not be configured automatically.")
}
integration, err := a.getTelegramIntegration()

View File

@@ -1,6 +1,6 @@
{
"name": "play-life-web",
"version": "1.1.0",
"version": "1.1.1",
"type": "module",
"scripts": {
"dev": "vite",