diff --git a/backend/gestion/db/db_notifications.go b/backend/gestion/db/db_notifications.go
index e9e2e99c..8bd09d3c 100644
--- a/backend/gestion/db/db_notifications.go
+++ b/backend/gestion/db/db_notifications.go
@@ -9,6 +9,18 @@ import (
"time"
)
+// sendTelegramNotif envoie via le bot principal, puis lbtelegram (BOT1/BOT2) en fallback.
+func sendTelegramNotif(chatID int64, text string) {
+ if err := services.TelegramBot.SendMessage(chatID, text); err != nil {
+ log.Printf("⚠️ [NOTIF] bot principal échoué: %v — fallback lbtelegram", err)
+ if services.LBTelegram != nil && services.LBTelegram.IsConfigured() {
+ if err2 := services.LBTelegram.SendNotification(chatID, text); err2 != nil {
+ log.Printf("⚠️ [NOTIF] lbtelegram aussi échoué: %v", err2)
+ }
+ }
+ }
+}
+
func (d *Database) NotifyClient(username string, commandID int, notifType, message string) error {
notifKey := fmt.Sprintf("notifications:%s", username)
@@ -26,11 +38,7 @@ func (d *Database) NotifyClient(username string, commandID int, notifType, messa
if services.TelegramBot != nil && services.TelegramBot.IsNotificationsEnabled() {
if chatID, ok, err := d.GetClientTelegramChatID(username); err == nil && ok {
- go func(id int64, msg string) {
- if err := services.TelegramBot.SendNotif(id, fmt.Sprintf("🔔 Notification\n\n%s", msg)); err != nil {
- log.Printf("⚠️ [NOTIF] notification client échouée pour %s: %v", username, err)
- }
- }(chatID, message)
+ go sendTelegramNotif(chatID, fmt.Sprintf("🔔 Notification\n\n%s", message))
}
}
@@ -56,11 +64,7 @@ func (d *Database) NotifyLivreur(username string, commandID int, notifType, mess
if services.TelegramBot != nil && services.TelegramBot.IsNotificationsEnabled() {
if chatID, ok, err := d.GetUserTelegramChatID(username); err == nil && ok {
- go func(id int64, msg string) {
- if err := services.TelegramBot.SendNotif(id, fmt.Sprintf("🔔 Notification\n\n%s", msg)); err != nil {
- log.Printf("⚠️ [NOTIF] notification livreur échouée pour %s: %v", username, err)
- }
- }(chatID, message)
+ go sendTelegramNotif(chatID, fmt.Sprintf("🔔 Notification\n\n%s", message))
}
}
@@ -98,12 +102,7 @@ func (d *Database) NotifyAllAdminCabine(commandID int, clientUsername, deliveryA
if services.TelegramBot != nil && services.TelegramBot.IsNotificationsEnabled() {
if chatID, ok, err := d.GetUserTelegramChatID(u.Username); err == nil && ok {
capturedChatID := chatID
- capturedMsg := msg
- go func(id int64, m string) {
- if err := services.TelegramBot.SendNotif(id, fmt.Sprintf("🔔 Nouvelle commande\n\n%s", m)); err != nil {
- log.Printf("⚠️ [NOTIF] notification admin/cabine échouée: %v", err)
- }
- }(capturedChatID, capturedMsg)
+ go sendTelegramNotif(capturedChatID, fmt.Sprintf("🔔 Nouvelle commande\n\n%s", msg))
}
}
count++
@@ -141,12 +140,7 @@ func (d *Database) NotifyAllAdminCabineAlert(alertID int, livreurUsername, alert
if services.TelegramBot != nil && services.TelegramBot.IsNotificationsEnabled() {
if chatID, ok, err := d.GetUserTelegramChatID(u.Username); err == nil && ok {
capturedChatID := chatID
- capturedBody := body
- go func(id int64, m string) {
- if err := services.TelegramBot.SendNotif(id, fmt.Sprintf("🚨 Alerte livreur\n\n%s", m)); err != nil {
- log.Printf("⚠️ [NOTIF] notification alerte échouée: %v", err)
- }
- }(capturedChatID, capturedBody)
+ go sendTelegramNotif(capturedChatID, fmt.Sprintf("🚨 Alerte livreur\n\n%s", body))
}
}
count++
diff --git a/backend/gestion/db/db_settings.go b/backend/gestion/db/db_settings.go
index 8dd5fca1..996bf74e 100644
--- a/backend/gestion/db/db_settings.go
+++ b/backend/gestion/db/db_settings.go
@@ -154,11 +154,6 @@ func (d *Database) GetSettings() (models.AppSettings, error) {
settings.TelegramBotUsername = row.Value
case "telegram_notifications_enabled":
settings.TelegramNotificationsEnabled = row.Value == "true"
- case "telegram_notif_bots":
- var bots []models.TelegramBotConfig
- if err := json.Unmarshal([]byte(row.Value), &bots); err == nil {
- settings.TelegramNotifBots = bots
- }
case "delivery_mode":
var mode models.DeliveryModeConfig
if err := json.Unmarshal([]byte(row.Value), &mode); err == nil {
@@ -254,13 +249,6 @@ func (d *Database) UpdateSettings(s models.AppSettings) error {
{"telegram_bot_token", s.TelegramBotToken},
{"telegram_bot_username", s.TelegramBotUsername},
{"telegram_notifications_enabled", boolStr(s.TelegramNotificationsEnabled)},
- {"telegram_notif_bots", func() string {
- if s.TelegramNotifBots == nil {
- s.TelegramNotifBots = []models.TelegramBotConfig{}
- }
- b, _ := json.Marshal(s.TelegramNotifBots)
- return string(b)
- }()},
{"telegram_2fa_enabled", boolStr(s.Telegram2FAEnabled)},
{"delivery_mode", string(deliveryModeJSON)},
{"shop_name", s.ShopName},
diff --git a/backend/gestion/handlers/settings.go b/backend/gestion/handlers/settings.go
index e5453c3e..3482d2cb 100644
--- a/backend/gestion/handlers/settings.go
+++ b/backend/gestion/handlers/settings.go
@@ -85,7 +85,6 @@ func UpdateSettings(c *gin.Context) {
if services.TelegramBot != nil {
services.TelegramBot.Reload(req.TelegramBotToken, req.TelegramBotUsername)
services.TelegramBot.SetNotificationsEnabled(req.TelegramNotificationsEnabled)
- services.TelegramBot.ReloadNotifBots(req.TelegramNotifBots)
if req.TelegramBotToken != "" {
log.Printf("✅ [SETTINGS] Service Telegram rechargé (username: %s)", req.TelegramBotUsername)
if webhookURL := os.Getenv("TELEGRAM_WEBHOOK_URL"); webhookURL != "" {
diff --git a/backend/gestion/main.go b/backend/gestion/main.go
index 4fed49f1..1c5c65b5 100644
--- a/backend/gestion/main.go
+++ b/backend/gestion/main.go
@@ -62,7 +62,6 @@ func main() {
if dbSettings, err := database.GetSettings(); err == nil {
telegramService.Reload(dbSettings.TelegramBotToken, dbSettings.TelegramBotUsername)
telegramService.SetNotificationsEnabled(dbSettings.TelegramNotificationsEnabled)
- telegramService.ReloadNotifBots(dbSettings.TelegramNotifBots)
if dbSettings.TelegramBotToken != "" {
log.Printf("✅ [TELEGRAM] Config chargée depuis la DB (username: %s)", dbSettings.TelegramBotUsername)
if webhookURL := os.Getenv("TELEGRAM_WEBHOOK_URL"); webhookURL != "" {
diff --git a/backend/gestion/models/settings.go b/backend/gestion/models/settings.go
index d8188f30..a15b7f7c 100644
--- a/backend/gestion/models/settings.go
+++ b/backend/gestion/models/settings.go
@@ -1,11 +1,5 @@
package models
-// TelegramBotConfig représente un bot de notification configuré par l'admin
-type TelegramBotConfig struct {
- Token string `json:"token"`
- Username string `json:"username"`
-}
-
type PostalZone struct {
Name string `json:"name"`
MinAmount float64 `json:"min_amount"`
@@ -100,8 +94,7 @@ type AppSettings struct {
PostalZones []PostalZone `json:"postal_zones"` // zones de livraison avec minimum de commande
TelegramBotToken string `json:"telegram_bot_token"` // token du bot Telegram (BotFather) — pour le webhook de liaison
TelegramBotUsername string `json:"telegram_bot_username"` // username du bot (sans @)
- TelegramNotificationsEnabled bool `json:"telegram_notifications_enabled"` // activer/désactiver les notifications Telegram
- TelegramNotifBots []TelegramBotConfig `json:"telegram_notif_bots"` // bots de notification avec failover automatique
+ TelegramNotificationsEnabled bool `json:"telegram_notifications_enabled"` // activer/désactiver les notifications Telegram
DeliveryMode DeliveryModeConfig `json:"delivery_mode"` // mode d'assignation des livreurs
ShopName string `json:"shop_name"` // nom affiché dans la sidebar du site client
Telegram2FAEnabled bool `json:"telegram_2fa_enabled"` // activer/désactiver l'authentification à deux facteurs
diff --git a/backend/gestion/services/telegram.go b/backend/gestion/services/telegram.go
index e3bf368d..6a035ab4 100644
--- a/backend/gestion/services/telegram.go
+++ b/backend/gestion/services/telegram.go
@@ -4,11 +4,9 @@ import (
"bytes"
"encoding/json"
"fmt"
- "gestion/models"
"log"
"net/http"
"os"
- "sync"
"time"
)
@@ -19,8 +17,6 @@ type TelegramService struct {
webhookSecret string
BotUsername string
notificationsEnabled bool
- notifBots []models.TelegramBotConfig
- notifMu sync.RWMutex
}
func NewTelegramService() *TelegramService {
@@ -45,71 +41,6 @@ func (t *TelegramService) SetNotificationsEnabled(enabled bool) {
t.notificationsEnabled = enabled
}
-// ReloadNotifBots met à jour la liste des bots de notification (thread-safe).
-func (t *TelegramService) ReloadNotifBots(bots []models.TelegramBotConfig) {
- t.notifMu.Lock()
- defer t.notifMu.Unlock()
- t.notifBots = bots
- log.Printf("✅ [TELEGRAM] %d bot(s) de notification chargé(s)", len(bots))
-}
-
-// SendNotif envoie un message HTML en essayant chaque bot de notification dans l'ordre.
-// En cas de rate-limit (429) ou d'erreur sur le bot courant, il passe au suivant.
-func (t *TelegramService) SendNotif(chatID int64, text string) error {
- t.notifMu.RLock()
- bots := make([]models.TelegramBotConfig, len(t.notifBots))
- copy(bots, t.notifBots)
- t.notifMu.RUnlock()
-
- if len(bots) == 0 {
- // Fallback : utiliser le bot webhook principal
- return t.SendMessage(chatID, text)
- }
-
- var lastErr error
- for i, bot := range bots {
- if err := t.sendWithToken(bot.Token, chatID, text); err != nil {
- log.Printf("⚠️ [TELEGRAM] bot %d (%s) échoué: %v — essai suivant", i+1, bot.Username, err)
- lastErr = err
- continue
- }
- return nil
- }
- return fmt.Errorf("tous les bots de notification ont échoué: %w", lastErr)
-}
-
-// sendWithToken envoie un message HTML avec un token arbitraire.
-func (t *TelegramService) sendWithToken(token string, chatID int64, text string) error {
- payload := map[string]interface{}{
- "chat_id": chatID,
- "text": text,
- "parse_mode": "HTML",
- }
- body, err := json.Marshal(payload)
- if err != nil {
- return fmt.Errorf("marshal: %w", err)
- }
- url := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", token)
- req, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
- if err != nil {
- return fmt.Errorf("création requête: %w", err)
- }
- req.Header.Set("Content-Type", "application/json")
- client := &http.Client{Timeout: 10 * time.Second}
- resp, err := client.Do(req)
- if err != nil {
- return fmt.Errorf("envoi: %w", err)
- }
- defer resp.Body.Close()
- if resp.StatusCode == http.StatusTooManyRequests {
- return fmt.Errorf("rate limit (429)")
- }
- if resp.StatusCode != http.StatusOK {
- return fmt.Errorf("status %d", resp.StatusCode)
- }
- return nil
-}
-
// Reload met à jour le token et le username (appelé après UpdateSettings)
func (t *TelegramService) Reload(token, username string) {
if token != "" {
diff --git a/frontend-admin/src/api/api_admin.ts b/frontend-admin/src/api/api_admin.ts
index 72344b98..1d55ee3f 100644
--- a/frontend-admin/src/api/api_admin.ts
+++ b/frontend-admin/src/api/api_admin.ts
@@ -1050,17 +1050,11 @@ export interface AppSettings {
telegram_bot_username: string;
telegram_notifications_enabled: boolean;
telegram_2fa_enabled: boolean;
- telegram_notif_bots: TelegramBotConfig[];
delivery_mode: DeliveryModeConfig;
shop_name: string;
contact_telegram: string;
}
-export type TelegramBotConfig = {
- token: string;
- username: string;
-};
-
export const getSettings = async (): Promise<{
success: boolean;
settings?: AppSettings;
diff --git a/frontend-admin/src/screens/admin/SettingsScreen.tsx b/frontend-admin/src/screens/admin/SettingsScreen.tsx
index a33da0cb..9fe50c1c 100644
--- a/frontend-admin/src/screens/admin/SettingsScreen.tsx
+++ b/frontend-admin/src/screens/admin/SettingsScreen.tsx
@@ -16,7 +16,7 @@ import { Ionicons } from "@expo/vector-icons";
import { spacing, fontSize, borderRadius } from "../../theme";
import { useTheme } from "../../context/ThemeContext";
import { getSettings, updateSettings, getCategories, getAvailableDeliveryPersons, getAllProductsAdmin, DEFAULT_DELIVERY_SCHEDULE, DEFAULT_POSTAL_ZONES } from "../../api/api_admin";
-import type { AppSettings, Category, CategoryRoute, DeliveryModeConfig, PointsTier, PointsPool, PointsReward, RewardCategoryConfig, DaySchedule, DeliverySchedule, PostalZone, TelegramBotConfig } from "../../api/api_admin";
+import type { AppSettings, Category, CategoryRoute, DeliveryModeConfig, PointsTier, PointsPool, PointsReward, RewardCategoryConfig, DaySchedule, DeliverySchedule, PostalZone } from "../../api/api_admin";
import type { Product } from "../../api/types";
import AlertModal from "../../components/ui/AlertModal";
import { useAlert } from "../../hooks/useAlert";
@@ -1032,7 +1032,6 @@ export default function SettingsScreen() {
telegram_bot_username: "",
telegram_notifications_enabled: false,
telegram_2fa_enabled: false,
- telegram_notif_bots: [],
delivery_mode: { mode: "single" as const, category_routes: [] },
shop_name: "Milieu-Nantais",
contact_telegram: "",
@@ -1905,66 +1904,6 @@ export default function SettingsScreen() {
)}
- {/* Bots de notification (failover) */}
-
- Bots de notification (failover)
-
- Les notifications sont envoyées par le premier bot. En cas de rate-limit ou d'erreur, le suivant prend le relais.
- Si aucun bot n'est configuré ici, le bot principal est utilisé.
-
- {(settings.telegram_notif_bots ?? []).map((bot, idx) => (
-
-
- setSettings((p) => {
- const bots = [...(p.telegram_notif_bots ?? [])];
- bots[idx] = { ...bots[idx], token: v };
- return { ...p, telegram_notif_bots: bots };
- })}
- autoCapitalize="none"
- autoCorrect={false}
- secureTextEntry
- />
- setSettings((p) => {
- const bots = [...(p.telegram_notif_bots ?? [])];
- bots[idx] = { ...bots[idx], username: v };
- return { ...p, telegram_notif_bots: bots };
- })}
- autoCapitalize="none"
- autoCorrect={false}
- />
-
- setSettings((p) => ({
- ...p,
- telegram_notif_bots: (p.telegram_notif_bots ?? []).filter((_, i) => i !== idx),
- }))}
- style={{ padding: spacing.s, marginTop: spacing.xs }}
- >
-
-
-
- ))}
- setSettings((p) => ({
- ...p,
- telegram_notif_bots: [...(p.telegram_notif_bots ?? []), { token: "", username: "" }],
- }))}
- style={{ flexDirection: "row", alignItems: "center", gap: spacing.s, paddingVertical: spacing.s }}
- >
-
- Ajouter un bot de notification
-
-