diff options
Diffstat (limited to 'server')
| -rw-r--r-- | server/channel/channel.go | 57 | ||||
| -rw-r--r-- | server/server/command.go | 94 |
2 files changed, 115 insertions, 36 deletions
diff --git a/server/channel/channel.go b/server/channel/channel.go index 449375b..056b942 100644 --- a/server/channel/channel.go +++ b/server/channel/channel.go @@ -6,11 +6,14 @@ import ( "citrons.xyz/talk/server/session" "citrons.xyz/talk/server/validate" "citrons.xyz/talk/server/user" + "strings" + "sort" ) type ChannelStore struct { world *object.World byName map[string]*Channel + directChannels map[string]*Channel deleted map[string]Tombstone } @@ -18,6 +21,7 @@ type Channel struct { store *ChannelStore id string name string + isDirect bool members map[string]Membership messages []proto.Object byId map[string]int @@ -31,7 +35,8 @@ type Tombstone struct { func NewStore(world *object.World) *ChannelStore { return &ChannelStore { - world, make(map[string]*Channel), make(map[string]Tombstone), + world, make(map[string]*Channel), make(map[string]*Channel), + make(map[string]Tombstone), } } @@ -58,12 +63,44 @@ func (cs *ChannelStore) CreateChannel(name string) (*Channel, *proto.Fail) { return &c, nil } +func (cs *ChannelStore) GetDirect(among []string) *Channel { + sort.Strings(among) + key := strings.Join(among, "\x00") + if cs.directChannels[key] == nil { + var c Channel + c.isDirect = true + c.store = cs + c.byId = make(map[string]int) + c.defaultMembership = DefaultMembership + c.members = make(map[string]Membership) + for _, member := range among { + c.members[member] = c.defaultMembership + } + + cs.directChannels[key] = &c + c.id = cs.world.NewObject(&c) + } + return cs.directChannels[key] +} + func (cs *ChannelStore) ByName(name string) *Channel { return cs.byName[validate.Fold(name)] } func (c *Channel) Name() string { - return c.name + if !c.isDirect { + return c.name + } else { + var members []string + for member := range c.members { + u := c.store.world.GetObject(member) + if u != nil { + members = append(members, u.GetInfo().Fields[""]) + } + } + sort.Strings(members) + return strings.Join(members, ", ") + } } func (c *Channel) Id() string { @@ -108,6 +145,9 @@ func (c *Channel) Put(m proto.Object) proto.Object { } func (c *Channel) prune() { + if c.isDirect { + return + } for m, _ := range c.members { switch c.store.world.GetObject(m).(type) { case *user.User: @@ -167,12 +207,19 @@ func (c *Channel) Delete() { c.store.world.PutObject(c.id, deleted) } -func (c *Channel) GetInfo() proto.Object { - return proto.Object { - "channel", c.id, map[string]string {"": c.name}, +func (c *Channel) Kind() string { + switch { + case c.isDirect: + return "direct-channel" + default: + return "channel" } } +func (c *Channel) GetInfo() proto.Object { + return proto.Object {c.Kind(), c.id, map[string]string {"": c.Name()}} +} + func (t Tombstone) GetInfo() proto.Object { return proto.Object { "gone", "", map[string]string {"": t.name, "kind": "channel"}, diff --git a/server/server/command.go b/server/server/command.go index 306a445..14f8168 100644 --- a/server/server/command.go +++ b/server/server/command.go @@ -39,42 +39,41 @@ func (s *server) SendRequest(r session.Request) { } case "lookup": - if len(r.Cmd.Args) != 1 { - r.ReplyInvalid() - return - } - o := r.Cmd.Args[0] - var name string - for k, v := range o.Fields { - switch k { - case "": - name = v + var response []proto.Object + for _, o := range r.Cmd.Args { + var name string + for k, v := range o.Fields { + switch k { + case "": + name = v + default: + r.ReplyInvalid() + return + } + } + var info proto.Object + switch o.Kind { + case "u": + u := s.userStore.ByName(name) + if u == nil { + r.Reply(proto.Fail{"unknown-name", "", nil}.Cmd()) + return + } + info = u.GetInfo() + case "channel": + c := s.channelStore.ByName(name) + if c == nil { + r.Reply(proto.Fail{"unknown-name", "", nil}.Cmd()) + return + } + info = c.GetInfo() default: r.ReplyInvalid() return } + response = append(response, info) } - var info proto.Object - switch o.Kind { - case "u": - u := s.userStore.ByName(name) - if u == nil { - r.Reply(proto.Fail{"unknown-name", "", nil}.Cmd()) - return - } - info = u.GetInfo() - case "channel": - c := s.channelStore.ByName(name) - if c == nil { - r.Reply(proto.Fail{"unknown-name", "", nil}.Cmd()) - return - } - info = c.GetInfo() - default: - r.ReplyInvalid() - return - } - r.Reply(proto.NewCmd("i", "", info)) + r.Reply(proto.NewCmd("i", "", response...)) case "create": if r.From.UserId == "" { @@ -111,6 +110,39 @@ func (s *server) SendRequest(r session.Request) { r.ReplyInvalid() } + case "direct": + if r.From.UserId == "" { + r.ReplyInvalid() + return + } + if len(r.Cmd.Args) < 1 { + r.ReplyInvalid() + return + } + among := []string {r.From.UserId} + duplicate := make(map[string]bool) + for _, member := range r.Cmd.Args { + if member.Kind != "u" { + r.ReplyInvalid() + return + } + if duplicate[member.Fields[""]] { + r.ReplyInvalid() + return + } + duplicate[member.Fields[""]] = true + u := s.world.GetObject(member.Id) + switch u.(type) { + case *user.User: + default: + r.Reply(proto.Fail{"bad-target", "", nil}.Cmd()) + return + } + among = append(among, member.Id) + } + c := s.channelStore.GetDirect(among) + r.Reply(proto.NewCmd("direct", "", c.GetInfo())) + case "channels": if r.From.UserId == "" { r.Reply(proto.NewCmd("channels", "", )) |
