aboutsummaryrefslogtreecommitdiff
path: root/internal/chat/chat.go
diff options
context:
space:
mode:
author2025-12-30 19:33:39 +0300
committer2025-12-30 19:33:39 +0300
commitd317e8f6df0e0e16445db606da1d683a6b35f531 (patch)
tree4b80de04e17a137cff2dc309508b5f841f48c994 /internal/chat/chat.go
downloadqchat-d317e8f6df0e0e16445db606da1d683a6b35f531.tar.gz
qchat-d317e8f6df0e0e16445db606da1d683a6b35f531.tar.bz2
qchat-d317e8f6df0e0e16445db606da1d683a6b35f531.tar.xz
qchat-d317e8f6df0e0e16445db606da1d683a6b35f531.zip
начальный коммит
Diffstat (limited to 'internal/chat/chat.go')
-rw-r--r--internal/chat/chat.go178
1 files changed, 178 insertions, 0 deletions
diff --git a/internal/chat/chat.go b/internal/chat/chat.go
new file mode 100644
index 0000000..4a639ba
--- /dev/null
+++ b/internal/chat/chat.go
@@ -0,0 +1,178 @@
+package chat
+
+import (
+ "context"
+ "fmt"
+ "strings"
+ "sync"
+ "time"
+
+ "go.neonxp.ru/qchat/internal/config"
+)
+
+var (
+ helpMessage = "Available commands:\n" +
+ "/join [chan] - change current channel to [chan]\n" +
+ "/chans - list all chans\n" +
+ "/users - list all online users\n" +
+ "ctrl+c - leave chat"
+)
+
+type Chat struct {
+ cfg *config.Config
+ users map[*User]struct{}
+ channels map[*Channel]struct{}
+ wg sync.WaitGroup
+ mu sync.RWMutex
+}
+
+func New(cfg *config.Config) *Chat {
+ return &Chat{
+ cfg: cfg,
+ users: make(map[*User]struct{}, 32),
+ channels: make(map[*Channel]struct{}, 32),
+ wg: sync.WaitGroup{},
+ mu: sync.RWMutex{},
+ }
+}
+
+func (c *Chat) Run(ctx context.Context) error {
+ for _, channel := range c.cfg.Channels {
+ c.NewChannel(ctx, channel.Name)
+ }
+
+ c.wg.Wait()
+
+ return nil
+}
+
+func (c *Chat) NewUser(username, identify string) *User {
+ u := &User{
+ Username: username,
+ Identify: identify,
+ Chans: map[string]*Channel{},
+ CurrentChan: nil,
+ Events: make(chan any, 32),
+ mu: sync.RWMutex{},
+ }
+
+ ch := c.GetChannel("main")
+ if ch != nil {
+ u.JoinChan(ch)
+ }
+
+ c.users[u] = struct{}{}
+
+ u.Events <- SystemMessage{
+ Message: fmt.Sprintf("Connected to %s chat server...", c.cfg.Server.Name),
+ }
+
+ return u
+}
+
+func (c *Chat) RemoveUser(u *User) {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ for ch := range c.channels {
+ ch.Leave(u)
+ }
+
+ close(u.Events)
+
+ delete(c.users, u)
+}
+
+func (c *Chat) NewChannel(ctx context.Context, name string) *Channel {
+ ch := &Channel{
+ Name: name,
+ Users: make(map[*User]struct{}, 32),
+ Events: make(chan any),
+ mu: sync.RWMutex{},
+ }
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ c.channels[ch] = struct{}{}
+ c.wg.Go(func() {
+ ch.Listen(ctx)
+ })
+
+ return ch
+}
+
+func (c *Chat) GetChannel(name string) *Channel {
+ for ch := range c.channels {
+ if ch.Name == name {
+ return ch
+ }
+ }
+
+ return nil
+}
+
+func (c *Chat) Message(user *User, message string) {
+ if user.CurrentChan == nil {
+ return
+ }
+
+ user.CurrentChan.Events <- Message{
+ User: user,
+ Message: message,
+ Time: time.Now(),
+ }
+}
+
+func (c *Chat) SelfMessage(user *User, message string) {
+ if user.CurrentChan == nil {
+ return
+ }
+
+ user.CurrentChan.Events <- SelfMessage{
+ User: user,
+ Message: message,
+ }
+}
+func (c *Chat) SystemMessage(user *User, message string) {
+ user.Events <- SystemMessage{
+ Message: message,
+ }
+}
+
+func (c *Chat) Input(ctx context.Context, user *User, input string) {
+ cmd := strings.ToLower(input)
+ switch {
+ case strings.HasPrefix(cmd, "/me "):
+ c.SelfMessage(user, strings.TrimPrefix(input, "/me "))
+ case cmd == "/help":
+ c.SystemMessage(user, helpMessage)
+ case cmd == "/list":
+ list := make([]string, 0, len(c.channels))
+ for ch := range c.channels {
+ list = append(list, ch.Name)
+ }
+ c.SystemMessage(user, "Chans:\n"+strings.Join(list, "\n"))
+ case cmd == "/users":
+ list := make([]string, 0, len(c.users))
+ for u := range c.users {
+ list = append(list, u.NUsername())
+ }
+ c.SystemMessage(user, "Users:\n"+strings.Join(list, "\n"))
+ case strings.HasPrefix(cmd, "/join "):
+ newChanName := strings.TrimPrefix(input, "/join ")
+ var newChan *Channel
+ for ch := range c.channels {
+ if ch.Name == newChanName {
+ newChan = ch
+ break
+ }
+ }
+ if newChan == nil {
+ newChan = c.NewChannel(ctx, newChanName)
+ }
+ user.CurrentChan.Leave(user)
+ user.CurrentChan = newChan
+ newChan.Join(user)
+ default:
+ c.Message(user, input)
+ }
+}