package chat import ( "context" "log/slog" "sync" "time" ) type Channel struct { Name string Users map[*User]struct{} Events chan any Log Logs mu sync.RWMutex } func (c *Channel) Listen(ctx context.Context) { for { select { case <-ctx.Done(): return case ev := <-c.Events: c.processEvent(ev) c.processLog(ev) } } } func (c *Channel) processEvent(ev any) { slog.Info("channel event", slog.String("channel", c.Name), slog.Any("event", ev)) for u := range c.Users { if ev, ok := ev.(Message); ok { if ev.User == u { // Do not return user message to him continue } } select { case u.Events <- ev: default: } } } func (c *Channel) processLog(ev any) { switch ev := ev.(type) { case UserJoined: c.Log.Append(LogEntry{ Username: ev.User.NUsername(), Time: ev.Time, Type: TypeJoined, }) case UserLeft: c.Log.Append(LogEntry{ Username: ev.User.NUsername(), Time: ev.Time, Type: TypeLeft, }) case Message: c.Log.Append(LogEntry{ Username: ev.User.NUsername(), Time: ev.Time, Message: ev.Message, Type: TypeMessage, }) case SelfMessage: c.Log.Append(LogEntry{ Username: ev.User.NUsername(), Time: time.Now(), Message: "/me " + ev.Message, Type: TypeMessage, }) } } func (c *Channel) Join(u *User) { c.mu.Lock() defer c.mu.Unlock() if _, ok := c.Users[u]; ok { return } c.Events <- UserJoined{ User: u, Time: time.Now(), Chan: c, } c.Users[u] = struct{}{} // Отправляем при подключении последние 20 сообщений u.Events <- UserLogs{ Logs: c.Log.Get(), } } func (c *Channel) Leave(u *User) { c.mu.Lock() defer c.mu.Unlock() if _, ok := c.Users[u]; !ok { return } c.Events <- UserLeft{ User: u, Time: time.Now(), Chan: c, } delete(c.Users, u) }