package webservices import ( "encoding/json" "fmt" "html/template" "net/http" "stream-bot/internal/db" "stream-bot/internal/logger" ) type ChatService struct { *baseService config *db.ChatWebConfig server *http.Server } func NewChatService(port int, config *db.ChatWebConfig) *ChatService { return &ChatService{ baseService: newBaseService(port), config: config, } } func (s *ChatService) Start() error { mux := http.NewServeMux() mux.HandleFunc("/", s.handleIndex) mux.HandleFunc("/events", s.handleEvents) s.server = &http.Server{Addr: fmt.Sprintf(":%d", s.port), Handler: mux} s.running = true go func() { if err := s.server.ListenAndServe(); err != nil && err != http.ErrServerClosed { logger.Error("Chat service error on port %d: %v", s.port, err) } }() logger.Info("Chat service started on port %d", s.port) return nil } func (s *ChatService) Stop() error { s.running = false if s.server != nil { return s.server.Close() } return nil } func (s *ChatService) ReloadConfig(config interface{}) error { newCfg, ok := config.(*db.ChatWebConfig) if !ok { return fmt.Errorf("invalid config type, expected *db.ChatWebConfig") } s.config = newCfg return nil } func (s *ChatService) GetPort() int { return s.port } func (s *ChatService) GetType() string { return "chat" } func (s *ChatService) IsRunning() bool { return s.running } // SendToClients отправляет сообщение всем подключённым SSE-клиентам func (s *ChatService) SendToClients(msg ChatMessage) { s.broadcast(msg) } func (s *ChatService) handleIndex(w http.ResponseWriter, r *http.Request) { tmpl := ` Chat Overlay
` t := template.Must(template.New("chat").Parse(tmpl)) t.Execute(w, s.config) } func (s *ChatService) handleEvents(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/event-stream") w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Connection", "keep-alive") w.Header().Set("Access-Control-Allow-Origin", "*") flusher, ok := w.(http.Flusher) if !ok { http.Error(w, "Streaming unsupported", http.StatusInternalServerError) return } clientChan := make(chan interface{}, 100) s.clientsMu.Lock() s.clients[clientChan] = true s.clientsMu.Unlock() defer func() { s.clientsMu.Lock() delete(s.clients, clientChan) s.clientsMu.Unlock() close(clientChan) }() for { select { case data := <-clientChan: jsonData, _ := json.Marshal(data) fmt.Fprintf(w, "data: %s\n\n", jsonData) flusher.Flush() case <-r.Context().Done(): return } } }