]> git.r.bdr.sh - rbdr/mobius/blob - hotline/chat.go
Add support for trackers that require a password
[rbdr/mobius] / hotline / chat.go
1 package hotline
2
3 import (
4 "crypto/rand"
5 "github.com/stretchr/testify/mock"
6 "slices"
7 "sync"
8 )
9
10 type PrivateChat struct {
11 Subject string
12 ClientConn map[[2]byte]*ClientConn
13 }
14
15 type ChatID [4]byte
16
17 type ChatManager interface {
18 New(cc *ClientConn) ChatID
19 GetSubject(id ChatID) string
20 Join(id ChatID, cc *ClientConn)
21 Leave(id ChatID, clientID [2]byte)
22 SetSubject(id ChatID, subject string)
23 Members(id ChatID) []*ClientConn
24 }
25
26 type MemChatManager struct {
27 chats map[ChatID]*PrivateChat
28
29 mu sync.Mutex
30 }
31
32 func NewMemChatManager() *MemChatManager {
33 return &MemChatManager{
34 chats: make(map[ChatID]*PrivateChat),
35 }
36 }
37
38 func (cm *MemChatManager) New(cc *ClientConn) ChatID {
39 cm.mu.Lock()
40 defer cm.mu.Unlock()
41
42 var randID [4]byte
43 _, _ = rand.Read(randID[:])
44
45 cm.chats[randID] = &PrivateChat{ClientConn: make(map[[2]byte]*ClientConn)}
46
47 cm.chats[randID].ClientConn[cc.ID] = cc
48
49 return randID
50 }
51
52 func (cm *MemChatManager) Join(id ChatID, cc *ClientConn) {
53 cm.mu.Lock()
54 defer cm.mu.Unlock()
55
56 chat := cm.chats[id]
57 chat.ClientConn[cc.ID] = cc
58 }
59
60 func (cm *MemChatManager) Leave(id ChatID, clientID [2]byte) {
61 cm.mu.Lock()
62 defer cm.mu.Unlock()
63
64 privChat, ok := cm.chats[id]
65 if !ok {
66 return
67 }
68
69 delete(privChat.ClientConn, clientID)
70 }
71
72 func (cm *MemChatManager) GetSubject(id ChatID) string {
73 cm.mu.Lock()
74 defer cm.mu.Unlock()
75
76 return cm.chats[id].Subject
77 }
78
79 func (cm *MemChatManager) Members(id ChatID) []*ClientConn {
80 cm.mu.Lock()
81 defer cm.mu.Unlock()
82
83 chat := cm.chats[id]
84
85 var members []*ClientConn
86 for _, cc := range chat.ClientConn {
87 members = append(members, cc)
88 }
89
90 slices.SortFunc(members, clientConnSortFunc)
91
92 return members
93 }
94
95 func (cm *MemChatManager) SetSubject(id ChatID, subject string) {
96 cm.mu.Lock()
97 defer cm.mu.Unlock()
98
99 chat := cm.chats[id]
100
101 chat.Subject = subject
102 }
103
104 type MockChatManager struct {
105 mock.Mock
106 }
107
108 func (m *MockChatManager) New(cc *ClientConn) ChatID {
109 args := m.Called(cc)
110
111 return args.Get(0).(ChatID)
112 }
113
114 func (m *MockChatManager) GetSubject(id ChatID) string {
115 args := m.Called(id)
116
117 return args.String(0)
118 }
119
120 func (m *MockChatManager) Join(id ChatID, cc *ClientConn) {
121 m.Called(id, cc)
122 }
123
124 func (m *MockChatManager) Leave(id ChatID, clientID [2]byte) {
125 m.Called(id, clientID)
126 }
127
128 func (m *MockChatManager) SetSubject(id ChatID, subject string) {
129 m.Called(id, subject)
130
131 }
132
133 func (m *MockChatManager) Members(id ChatID) []*ClientConn {
134 args := m.Called(id)
135
136 return args.Get(0).([]*ClientConn)
137 }