chat_registry.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. package state
  2. import (
  3. "errors"
  4. "fmt"
  5. "sync"
  6. "time"
  7. "github.com/mk6i/retro-aim-server/wire"
  8. "github.com/google/uuid"
  9. )
  10. // ErrChatRoomNotFound indicates that a chat room lookup failed.
  11. var ErrChatRoomNotFound = errors.New("chat room not found")
  12. // ChatRegistry keeps track of chat rooms. A ChatRegistry is safe for
  13. // concurrent use by multiple goroutines.
  14. type ChatRegistry struct {
  15. roomStore map[string]ChatRoom // association of cookie identifier->chat room
  16. valueStore map[string]any // association of cookie identifier->value
  17. mutex sync.RWMutex // ensures thread-safe read-write access to stores
  18. }
  19. // NewChatRegistry creates a new instance of ChatRegistry
  20. func NewChatRegistry() *ChatRegistry {
  21. return &ChatRegistry{
  22. roomStore: make(map[string]ChatRoom),
  23. valueStore: make(map[string]any),
  24. }
  25. }
  26. // Register adds a chat room to the registry and associates an arbitrary value
  27. // with the room.
  28. func (c *ChatRegistry) Register(chatRoom ChatRoom, value any) {
  29. c.mutex.Lock()
  30. defer c.mutex.Unlock()
  31. c.roomStore[chatRoom.Cookie] = chatRoom
  32. c.valueStore[chatRoom.Cookie] = value
  33. }
  34. // Retrieve retrieves a chat room and the arbitrary value associated with it.
  35. // Returns ErrChatRoomNotFound if the room is not registered.
  36. func (c *ChatRegistry) Retrieve(cookie string) (ChatRoom, any, error) {
  37. c.mutex.RLock()
  38. defer c.mutex.RUnlock()
  39. chatRoom, found := c.roomStore[cookie]
  40. if !found {
  41. return ChatRoom{}, nil, fmt.Errorf("%w cookie: %s", ErrChatRoomNotFound, cookie)
  42. }
  43. value, found := c.valueStore[cookie]
  44. if !found {
  45. panic("unable to find value for chat room")
  46. }
  47. return chatRoom, value, nil
  48. }
  49. // Remove removes a chat room and the arbitrary value associated with it.
  50. func (c *ChatRegistry) Remove(cookie string) {
  51. c.mutex.Lock()
  52. defer c.mutex.Unlock()
  53. delete(c.roomStore, cookie)
  54. delete(c.valueStore, cookie)
  55. }
  56. // ChatRoom is the representation of a chat room's metadata.
  57. type ChatRoom struct {
  58. // Cookie is the unique chat room identifier.
  59. Cookie string
  60. // CreateTime indicates when the chat room was created.
  61. CreateTime time.Time
  62. // DetailLevel is the detail level of the chat room. Unclear what this value means.
  63. DetailLevel uint8
  64. // Exchange indicates which exchange the chatroom belongs to. Typically, a canned value.
  65. Exchange uint16
  66. // InstanceNumber indicates which instance chatroom exists in. Typically, a canned value.
  67. InstanceNumber uint16
  68. // Name is the name of the chat room.
  69. Name string
  70. }
  71. // TLVList returns a TLV list of chat room metadata.
  72. func (c ChatRoom) TLVList() []wire.TLV {
  73. return []wire.TLV{
  74. // From protocols/oscar/family_chatnav.c in lib purple, these are the
  75. // room creation flags:
  76. // - 1 Evilable
  77. // - 2 Nav Only
  78. // - 4 Instancing Allowed
  79. // - 8 Occupant Peek Allowed
  80. // It's unclear what effect they actually have.
  81. wire.NewTLV(wire.ChatRoomTLVFlags, uint16(15)),
  82. wire.NewTLV(wire.ChatRoomTLVCreateTime, uint32(c.CreateTime.Unix())),
  83. wire.NewTLV(wire.ChatRoomTLVMaxMsgLen, uint16(1024)),
  84. wire.NewTLV(wire.ChatRoomTLVMaxOccupancy, uint16(100)),
  85. // From protocols/oscar/family_chatnav.c in lib purple, these are the
  86. // room creation permission values:
  87. // - 0 creation not allowed
  88. // - 1 room creation allowed
  89. // - 2 exchange creation allowed
  90. // It's unclear what effect they actually have.
  91. wire.NewTLV(wire.ChatRoomTLVNavCreatePerms, uint8(2)),
  92. wire.NewTLV(wire.ChatRoomTLVFullyQualifiedName, c.Name),
  93. wire.NewTLV(wire.ChatRoomTLVRoomName, c.Name),
  94. }
  95. }
  96. // NewChatRoom creates new state.ChatRoom objects
  97. func NewChatRoom() ChatRoom {
  98. return ChatRoom{
  99. Cookie: uuid.New().String(),
  100. CreateTime: time.Now(),
  101. }
  102. }