1
0
Fork 0
mirror of https://github.com/diamondburned/cchat-discord.git synced 2024-12-23 04:46:43 +00:00
cchat-discord/internal/discord/private/private.go

157 lines
3.8 KiB
Go

package private
import (
"sort"
"sync"
"github.com/diamondburned/arikawa/v2/discord"
"github.com/diamondburned/arikawa/v2/gateway"
"github.com/diamondburned/cchat"
"github.com/diamondburned/cchat-discord/internal/discord/channel"
"github.com/diamondburned/cchat-discord/internal/discord/private/hub"
"github.com/diamondburned/cchat-discord/internal/discord/state"
"github.com/diamondburned/cchat/text"
"github.com/diamondburned/cchat/utils/empty"
"github.com/pkg/errors"
)
// I don't think the cchat specs said anything about sharing a cchat.Server, so
// we might need to do this. Nevertheless, it seems overkill.
type containerSet struct {
mut sync.Mutex
set map[cchat.ServersContainer]struct{}
}
func newContainerSet() *containerSet {
return &containerSet{
set: map[cchat.ServersContainer]struct{}{},
}
}
func (cset *containerSet) Register(container cchat.ServersContainer) {
cset.mut.Lock()
cset.set[container] = struct{}{}
cset.mut.Unlock()
}
// prependServer wraps around Server to always prepend this wrapped server on
// top of the servers container.
type prependServer struct{ cchat.Server }
var _ cchat.ServerUpdate = (*prependServer)(nil)
// PreviousID returns the appropriate parameters to prepend this server.
func (ps prependServer) PreviousID() (cchat.ID, bool) {
// Return the private container's ID so this server goes right after it.
return "!!!private-container!!!", false
}
func (cset *containerSet) AddChannel(s *state.Instance, ch *discord.Channel) {
c, err := channel.New(s, *ch)
if err != nil {
return
}
replace := prependServer{Server: c}
cset.mut.Lock()
for container := range cset.set {
container.UpdateServer(replace)
}
cset.mut.Unlock()
}
type Private struct {
empty.Server
state *state.Instance
hub *hub.Server
containers *containerSet
}
func New(s *state.Instance) (cchat.Server, error) {
containers := newContainerSet()
hubServer, err := hub.New(s, containers)
if err != nil {
return nil, errors.Wrap(err, "failed to make hub server")
}
return Private{
state: s,
hub: hubServer,
containers: containers,
}, nil
}
func (priv Private) ID() cchat.ID {
// Not even a number, so no chance of colliding with snowflakes.
return "!!!private-container!!!"
}
func (priv Private) Name() text.Rich {
return text.Plain("Private Channels")
}
func (priv Private) AsLister() cchat.Lister { return priv }
type activeChannel struct {
*discord.Channel
*gateway.ReadState // used for sorting
}
func (active activeChannel) LastMessageID() discord.MessageID {
if active.ReadState == nil {
return active.Channel.LastMessageID
}
if active.ReadState.LastMessageID > active.Channel.LastMessageID {
return active.ReadState.LastMessageID
}
if active.Channel.LastMessageID.IsValid() {
return active.Channel.LastMessageID
}
// Whatever.
return discord.MessageID(active.Channel.ID)
}
func (priv Private) Servers(container cchat.ServersContainer) error {
activeIDs := priv.hub.ActiveChannelIDs()
channels := make([]activeChannel, 0, len(activeIDs))
for _, id := range activeIDs {
c, err := priv.state.Channel(id)
if err != nil {
return errors.Wrap(err, "failed to get private channel")
}
channels = append(channels, activeChannel{
Channel: c,
ReadState: priv.state.ReadState.FindLast(id),
})
}
// Sort so that channels with the largest last message ID (and therefore the
// latest message) will be on top.
sort.Slice(channels, func(i, j int) bool {
return channels[i].LastMessageID() > channels[j].LastMessageID()
})
servers := make([]cchat.Server, len(channels)+1)
servers[0] = priv.hub
for i, ch := range channels {
c, err := channel.New(priv.state, *ch.Channel)
if err != nil {
return errors.Wrap(err, "failed to create server for private channel")
}
servers[i+1] = c
}
container.SetServers(servers)
priv.containers.Register(container)
return nil
}