diff options
| author | citrons <citrons@mondecitronne.com> | 2025-01-26 01:56:53 -0600 |
|---|---|---|
| committer | citrons <citrons@mondecitronne.com> | 2025-01-26 01:56:53 -0600 |
| commit | 10b8a79389e7073f6bd65695c3d05c77b825bc33 (patch) | |
| tree | b7e6dbd84b5b2c960ab8aafc1f99c3950d679e44 /server/user | |
initial commit
Diffstat (limited to 'server/user')
| -rw-r--r-- | server/user/command.go | 76 | ||||
| -rw-r--r-- | server/user/user.go | 95 |
2 files changed, 171 insertions, 0 deletions
diff --git a/server/user/command.go b/server/user/command.go new file mode 100644 index 0000000..210dede --- /dev/null +++ b/server/user/command.go @@ -0,0 +1,76 @@ +package user + +import ( + "citrons.xyz/talk/server/session" + "citrons.xyz/talk/proto" +) + +func (u *User) SendRequest(r session.Request) { + switch r.Cmd.Kind { + + case "update": + if r.From.UserId != u.Id() { + r.Reply(proto.Fail{"forbidden", "", nil}.Cmd()) + return + } + if len(r.Cmd.Args) != 1 { + r.ReplyInvalid() + return + } + upd := r.Cmd.Args[0] + if upd.Kind != "u" { + r.ReplyInvalid() + return + } + name := u.name + for k, v := range upd.Fields { + switch k { + case "": + name = v + default: + r.ReplyInvalid() + return + } + } + if name != u.name { + err := u.Rename(name) + if err != nil { + r.Reply(err.Cmd()) + return + } + } + u.Stream.Event(r.Cmd) + r.Reply(proto.NewCmd("ok", "")) + + case "i": + r.Reply(proto.NewCmd("i", "", u.GetInfo())) + + case "s": + r.From.Subscribe(&u.Stream) + r.Reply(proto.NewCmd("i", "", u.GetInfo())) + + case "u": + r.From.Unsubscribe(&u.Stream) + r.Reply(proto.NewCmd("ok", "")) + + default: + r.ReplyInvalid() + } +} + +func (t Tombstone) SendRequest(r session.Request) { + switch r.Cmd.Kind { + + case "update": + r.Reply(proto.Fail{"bad-target", "", nil}.Cmd()) + + case "i", "s": + r.Reply(proto.NewCmd("i", "", t.GetInfo())) + + case "u": + r.Reply(proto.NewCmd("ok", "")) + + default: + r.ReplyInvalid() + } +} diff --git a/server/user/user.go b/server/user/user.go new file mode 100644 index 0000000..41a5c4f --- /dev/null +++ b/server/user/user.go @@ -0,0 +1,95 @@ +package user + +import ( + "citrons.xyz/talk/server/object" + "citrons.xyz/talk/server/session" + "citrons.xyz/talk/proto" +) + +type UserStore struct { + world *object.World + byName map[string]*User + gone map[string]Tombstone +} + +type User struct { + store *UserStore + name string + id string + Stream session.Stream + Channels map[string]bool + Anonymous bool +} + +type Tombstone struct { + name string +} + +func NewStore(world *object.World) *UserStore { + return &UserStore { + world, make(map[string]*User), make(map[string]Tombstone), + } +} + +func (us *UserStore) CreateUser(name string) (*User, *proto.Fail) { + if us.byName[name] != nil { + return nil, &proto.Fail { + "name-taken", "", map[string]string {"": name}, + } + } + var u User + u.store = us + u.name = name + us.byName[name] = &u + u.id = us.world.NewObject(&u) + u.Channels = make(map[string]bool) + return &u, nil +} + +func (us *UserStore) ByName(name string) *User { + return us.byName[name] +} + +func (u *User) Name() string { + return u.name +} + +func (u *User) Id() string { + return u.id +} + +func (u *User) Rename(name string) *proto.Fail { + if u.store.byName[name] != nil { + return &proto.Fail { + "name-taken", "", map[string]string {"": name}, + } + } + u.store.byName[u.name] = nil + u.store.byName[name] = u + u.name = name + return nil +} + +func (u *User) Delete() { + u.Stream.Event(proto.NewCmd("delete", u.id)) + u.Stream.UnsubscribeAll() + + delete(u.store.byName, u.name) + u.store.world.RemoveObject(u.id) + + gone := Tombstone {u.name} + u.store.gone[u.id] = gone + u.store.world.PutObject(u.id, gone) +} + +func (u *User) GetInfo() proto.Object { + return proto.Object { + "u", u.id, map[string]string {"": u.name}, + } +} + +func (t Tombstone) GetInfo() proto.Object { + return proto.Object { + "gone", "", map[string]string {"": t.name}, + } +} |
