-
Notifications
You must be signed in to change notification settings - Fork 0
/
dispenser.go
158 lines (124 loc) · 3.6 KB
/
dispenser.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package dispenser
import (
"sync"
"github.com/diamondburned/arikawa/v2/discord"
"github.com/diamondburned/arikawa/v2/gateway"
"github.com/diamondburned/arikawa/v2/state"
"github.com/nixhub-io/nixhub-io/templates"
"github.com/pkg/errors"
)
const BufSz = 25
type State struct {
MessagePool templates.Messages
MessageMu sync.Mutex
LastAuthor *discord.User
Typers *templates.Typing
Typing chan *gateway.TypingStartEvent
StopType chan discord.User // userID
Session *state.State
ChannelID discord.ChannelID
GuildID discord.GuildID
ClientPool map[uint64]chan<- templates.Renderer
ClientMu sync.RWMutex
Counter uint64
closers []func()
}
func (s *State) CopyPool() templates.Messages {
s.MessageMu.Lock()
defer s.MessageMu.Unlock()
return append([]templates.Message(nil), s.MessagePool...)
}
// setLastAuthor asserts and returns new.Small
func (s *State) setLastAuthor(newMessage *templates.Message) {
if s.LastAuthor != nil {
newMessage.Small = true &&
s.LastAuthor.ID == newMessage.Message.Author.ID &&
s.LastAuthor.Username == newMessage.Message.Author.Username
}
var author = newMessage.Message.Author
s.LastAuthor = &author
}
func (s *State) Close() error {
s.ClientMu.Lock()
defer s.ClientMu.Unlock()
// Delete handlers
for _, c := range s.closers {
c()
}
// Gracefully end all connections
for id, ch := range s.ClientPool {
close(ch)
delete(s.ClientPool, id)
}
// Stop the type loop
close(s.Typing)
return nil
}
func (s *State) addHandler(fn interface{}) {
s.closers = append(s.closers, s.Session.AddHandler(fn))
}
func Initialize(s *state.State, channelID discord.ChannelID) (*State, error) {
var state = State{
Session: s,
ChannelID: channelID,
ClientPool: make(map[uint64]chan<- templates.Renderer),
}
msgs, err := s.Client.Messages(channelID, BufSz)
if err != nil {
return nil, errors.Wrap(err, "Failed to get messages from "+channelID.String())
}
// The first message is the earliest.
// Discord is retarded, so we have to fetch GuildID ourselves
c, err := s.Channel(channelID)
if err != nil {
return nil, errors.Wrap(err, "Failed to get channelID "+channelID.String())
}
state.GuildID = c.GuildID
state.MessageMu.Lock()
defer state.MessageMu.Unlock()
state.MessagePool = make([]templates.Message, BufSz)
// This loop iterates messages from the latest to the earliest.
for i, j := 0, BufSz-1; i < len(msgs); i++ {
// Grab and insert missing GuildID
msg := msgs[i]
msg.GuildID = c.GuildID
state.MessagePool[j] = templates.RenderMessage(state.Session, msg)
if j--; j < 0 {
break
}
}
// We need another loop to calculate small, as it requires iterating from
// earliest to latest. We iterate from the start of MessagePool, as that's
// earliest.
for i := range state.MessagePool {
state.setLastAuthor(&state.MessagePool[i])
}
// Add hooks
state.addHandler(func(m *gateway.MessageCreateEvent) {
if m.ChannelID == channelID {
state.AddMessage(m.Message)
}
})
state.addHandler(func(m *gateway.MessageDeleteEvent) {
if m.ChannelID == channelID {
state.DeleteMessage(m.ID)
}
})
state.addHandler(func(m *gateway.MessageUpdateEvent) {
if m.ChannelID == channelID {
state.EditMessage(m.Message)
}
})
request := gateway.RequestGuildMembersData{
GuildID: []discord.GuildID{c.GuildID},
}
// Ask to fill up state
if err := s.Gateway.RequestGuildMembers(request); err != nil {
return nil, errors.Wrap(err, "Failed to request all members")
}
// Subscribe to typing events
if err := state.subscribeTyping(); err != nil {
return nil, errors.Wrap(err, "Failed to subscribe to guild")
}
return &state, nil
}