summaryrefslogtreecommitdiff
path: root/server/channel/channel.go
diff options
context:
space:
mode:
authorcitrons <citrons@mondecitronne.com>2025-01-26 01:56:53 -0600
committercitrons <citrons@mondecitronne.com>2025-01-26 01:56:53 -0600
commit10b8a79389e7073f6bd65695c3d05c77b825bc33 (patch)
treeb7e6dbd84b5b2c960ab8aafc1f99c3950d679e44 /server/channel/channel.go
initial commit
Diffstat (limited to 'server/channel/channel.go')
-rw-r--r--server/channel/channel.go148
1 files changed, 148 insertions, 0 deletions
diff --git a/server/channel/channel.go b/server/channel/channel.go
new file mode 100644
index 0000000..50b43b6
--- /dev/null
+++ b/server/channel/channel.go
@@ -0,0 +1,148 @@
+package channel
+
+import (
+ "citrons.xyz/talk/proto"
+ "citrons.xyz/talk/server/object"
+ "citrons.xyz/talk/server/session"
+ "citrons.xyz/talk/server/user"
+)
+
+type ChannelStore struct {
+ world *object.World
+ byName map[string]*Channel
+}
+
+type Channel struct {
+ store *ChannelStore
+ id string
+ name string
+ members map[string]Membership
+ messages []proto.Object
+ byId map[string]int
+ defaultMembership Membership
+ Stream session.Stream
+}
+
+func NewStore(world *object.World) *ChannelStore {
+ return &ChannelStore {world, make(map[string]*Channel)}
+}
+
+func (cs *ChannelStore) CreateChannel(name string) (*Channel, *proto.Fail) {
+ if cs.byName[name] != nil {
+ return nil, &proto.Fail {
+ "name-taken", "", map[string]string {"": name},
+ }
+ }
+ var c Channel
+ c.store = cs
+ c.name = name
+ c.members = make(map[string]Membership)
+ c.byId = make(map[string]int)
+ c.defaultMembership = DefaultMembership
+
+ cs.byName[name] = &c
+ c.id = cs.world.NewObject(&c)
+ return &c, nil
+}
+
+func (cs *ChannelStore) ByName(name string) *Channel {
+ return cs.byName[name]
+}
+
+func (c *Channel) Name() string {
+ return c.name
+}
+
+func (c *Channel) Id() string {
+ return c.id
+}
+
+func (c *Channel) Rename(name string) *proto.Fail {
+ if c.store.byName[name] != nil {
+ return &proto.Fail {
+ "name-taken", "", map[string]string {"": name},
+ }
+ }
+ c.store.byName[c.name] = nil
+ c.store.byName[name] = c
+ c.name = name
+ return nil
+}
+
+func (c *Channel) Put(m proto.Object) proto.Object {
+ m.Id = proto.GenId()
+ m.Fields["t"] = proto.Timestamp()
+ c.byId[m.Id] = len(c.messages)
+ c.messages = append(c.messages, m)
+ for s, _ := range c.Stream.Subscribers() {
+ if m.Fields["f"] == s.UserId {
+ continue
+ }
+ if c.members[s.UserId].See {
+ s.Event(proto.NewCmd("p", c.id, m))
+ }
+ }
+ return m
+}
+
+func (c *Channel) prune() {
+ for m, _ := range c.members {
+ switch c.store.world.GetObject(m).(type) {
+ case *user.User:
+ default:
+ delete(c.members, m)
+ }
+ }
+}
+
+func (c *Channel) Join(u *user.User) *proto.Fail {
+ if c.members[u.Id()].Yes {
+ return nil
+ }
+ c.members[u.Id()] = c.defaultMembership
+ u.Channels[c.id] = true
+ c.Put(proto.Object{"join", "", map[string]string {"": u.Id()}})
+ return nil
+}
+
+func (c *Channel) Leave(u *user.User) *proto.Fail {
+ if !c.members[u.Id()].Yes {
+ return nil
+ }
+ delete(c.members, u.Id())
+ delete(u.Channels, c.id)
+ c.Put(proto.Object{"leave", "", map[string]string {"": u.Id()}})
+ return nil
+}
+
+func (c *Channel) Members() map[string]Membership {
+ c.prune()
+ return c.members
+}
+
+func (c *Channel) SetMembership(u *user.User, m Membership) {
+ if c.members[u.Id()].Yes {
+ c.members[u.Id()] = m
+ }
+}
+
+func (c *Channel) Delete() {
+ c.Stream.Event(proto.NewCmd("delete", c.id))
+ c.Stream.UnsubscribeAll()
+
+ for m, _ := range c.members {
+ switch u := c.store.world.GetObject(m).(type) {
+ case *user.User:
+ u.Channels[c.id] = false
+ default:
+ }
+ }
+ delete(c.store.byName, c.name)
+ c.store.world.RemoveObject(c.id)
+}
+
+func (c *Channel) GetInfo() proto.Object {
+ return proto.Object {
+ "channel", c.id, map[string]string {"": c.name},
+ }
+}