package platforms import ( "strings" "sync" "time" "fmt" "stream-bot/internal/commands" "stream-bot/internal/events" "stream-bot/internal/logger" "stream-bot/internal/userstats" "stream-bot/internal/db" "stream-bot/internal/notifications" "stream-bot/internal/webservices" ) type Platform interface { Connect() error Disconnect() SendMessage(text string) error GetName() string } type Manager struct { platforms map[string]Platform cmdProc *commands.Processor eventProc *events.Processor mu sync.RWMutex userStats *userstats.Store notifMgr *notifications.Manager webServices *webservices.Manager } func NewManager(cmdProc *commands.Processor, eventProc *events.Processor, notifMgr *notifications.Manager, webSrv *webservices.Manager) *Manager { m := &Manager{ platforms: make(map[string]Platform), cmdProc: cmdProc, eventProc: eventProc, userStats: userstats.NewStore(), notifMgr: notifMgr, webServices: webSrv, } m.platforms["twitch"] = NewTwitchPlatform(m) return m } func (m *Manager) ConnectAll() { for name, p := range m.platforms { if err := p.Connect(); err != nil { logger.Error("Failed to connect %s: %v", name, err) } } } func (m *Manager) StopAll() { for _, p := range m.platforms { p.Disconnect() } } func (m *Manager) GetPlatform(name string) Platform { m.mu.RLock() defer m.mu.RUnlock() return m.platforms[name] } func (m *Manager) OnChatMessage(platform, channel, username, message string, isMod, isBroadcaster, isVip, isSubscriber bool) { // Обновляем статистику пользователя m.userStats.Update(username, func(u *userstats.UserStats) { u.MessageCount++ u.LastActive = time.Now() u.IsMod = isMod u.IsVip = isVip u.IsSubscriber = isSubscriber }) if m.notifMgr != nil { m.notifMgr.PlayEvent("new_message") } m.webServices.SendChatMessage(webservices.ChatMessage{ Username: username, Message: message, IsMod: isMod, IsVip: isVip, IsSub: isSubscriber, Timestamp: time.Now().Unix(), }) // Обработка команды if len(message) == 0 || message[0] != '!' { // Проверка на отметку: если пользователь отмечен и это его первое сообщение за стрим (сегодня) m.checkAndSendMarkNotification(username, platform, channel) return } parts := strings.SplitN(message, " ", 2) trigger := strings.TrimPrefix(parts[0], "!") args := "" if len(parts) > 1 { args = parts[1] } resp, _, err := m.cmdProc.ProcessCommand(trigger, username, platform, isMod, isBroadcaster, args) if err != nil { logger.Error("Command error: %v", err) return } if resp != "" { if p := m.GetPlatform(platform); p != nil { p.SendMessage(resp) } } // После команды тоже проверяем отметку (можно вынести в общее место) m.checkAndSendMarkNotification(username, platform, channel) } // checkAndSendMarkNotification отправляет сообщение, если пользователь отмечен и сегодня ещё не отмечали func (m *Manager) checkAndSendMarkNotification(username, platform, channel string) { marked, lastDate, err := db.IsUserMarked(username, platform) if err != nil { logger.Error("Failed to check marked user: %v", err) return } if !marked { return } today := time.Now().Format("2006-01-02") if lastDate == today { return } // Отправляем сообщение с упоминанием пользователя, а не канала msg := fmt.Sprintf("Время кое-кого отметить! Отмечен @%s", username) if p := m.GetPlatform(platform); p != nil { p.SendMessage(msg) } db.UpdateMarkedUserDate(username, platform, time.Now()) } func (m *Manager) OnEvent(platform, eventName string, params map[string]string) { m.eventProc.ProcessEvent(platform, eventName, params) if m.webServices != nil { // Преобразуем map[string]string в map[string]interface{} data := make(map[string]interface{}) for k, v := range params { data[k] = v } // Исправленный вызов: m.webServices.SendAlertEvent(webservices.AlertEvent{ Type: eventName, Data: data, }) } } func (m *Manager) IsConnected(platform string) bool { m.mu.RLock() defer m.mu.RUnlock() if p, ok := m.platforms[platform]; ok { if tw, ok := p.(*TwitchPlatform); ok { return tw.IsConnected() } // для других платформ можно добавить аналогично } return false } func (m *Manager) GetAllUsers() []*userstats.UserStats { return m.userStats.GetAll() } func (m *Manager) UpdateUserFlags(username string, isVip, isMod, isSubscriber bool) { m.userStats.Update(username, func(u *userstats.UserStats) { if isVip { u.IsVip = isVip } if isMod { u.IsMod = isMod } if isSubscriber { u.IsSubscriber = isSubscriber } }) } func (m *Manager) UpdateUserMarked(username string, marked bool) { m.userStats.Update(username, func(u *userstats.UserStats) { u.IsMarked = marked }) } func (m *Manager) SetVip(username string, isVip bool) { m.userStats.Update(username, func(u *userstats.UserStats) { u.IsVip = isVip }) } func (m *Manager) SetMod(username string, isMod bool) { m.userStats.Update(username, func(u *userstats.UserStats) { u.IsMod = isMod }) } func (m *Manager) SetMarked(username string, isMarked bool) { m.userStats.Update(username, func(u *userstats.UserStats) { u.IsMarked = isMarked }) } func (m *Manager) GetTwitchEventSubStatus() (connected bool, subscriptions []string, err error) { tw, ok := m.platforms["twitch"].(*TwitchPlatform) if !ok { return false, nil, fmt.Errorf("twitch platform not available") } connected, subscriptions = tw.EventSubStatus() return connected, subscriptions, nil }