cchat-discord/internal/discord/private/private.go

128 lines
3.1 KiB
Go

package private
import (
"sort"
"sync"
"github.com/diamondburned/arikawa/discord"
"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 }
// PreviousID returns the appropriate parameters to prepend this server.
func (ps prependServer) PreviousID() (cchat.ID, bool) { return "", 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 }
func (priv Private) Servers(container cchat.ServersContainer) error {
activeIDs := priv.hub.ActiveChannelIDs()
channels := make([]*discord.Channel, 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, c)
}
// 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)
if err != nil {
return errors.Wrap(err, "failed to create server for private channel")
}
servers[i] = c
}
container.SetServers(servers)
priv.containers.Register(container)
return nil
}