chat_nav.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. package server
  2. import (
  3. "context"
  4. "errors"
  5. "github.com/google/uuid"
  6. "github.com/mkaminski/goaim/oscar"
  7. "io"
  8. "log/slog"
  9. "time"
  10. )
  11. type ChatNavHandler interface {
  12. CreateRoomHandler(ctx context.Context, sess *Session, cr *ChatRegistry, newChatRoom ChatRoomFactory, snacPayloadIn oscar.SNAC_0x0E_0x02_ChatRoomInfoUpdate) (XMessage, error)
  13. RequestChatRightsHandler(ctx context.Context) XMessage
  14. RequestRoomInfoHandler(ctx context.Context, cr *ChatRegistry, snacPayloadIn oscar.SNAC_0x0D_0x04_ChatNavRequestRoomInfo) (XMessage, error)
  15. }
  16. func NewChatNavRouter(logger *slog.Logger) ChatNavRouter {
  17. return ChatNavRouter{
  18. ChatNavHandler: ChatNavService{
  19. Logger: logger,
  20. },
  21. RouteLogger: RouteLogger{
  22. Logger: logger,
  23. },
  24. }
  25. }
  26. type ChatNavRouter struct {
  27. ChatNavHandler
  28. RouteLogger
  29. }
  30. func (rt *ChatNavRouter) RouteChatNav(ctx context.Context, sess *Session, cr *ChatRegistry, SNACFrame oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
  31. switch SNACFrame.SubGroup {
  32. case oscar.ChatNavRequestChatRights:
  33. outSNAC := rt.RequestChatRightsHandler(ctx)
  34. rt.logRequestAndResponse(ctx, SNACFrame, nil, outSNAC.snacFrame, outSNAC.snacOut)
  35. return writeOutSNAC(SNACFrame, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
  36. case oscar.ChatNavRequestRoomInfo:
  37. inSNAC := oscar.SNAC_0x0D_0x04_ChatNavRequestRoomInfo{}
  38. if err := oscar.Unmarshal(&inSNAC, r); err != nil {
  39. return err
  40. }
  41. outSNAC, err := rt.RequestRoomInfoHandler(ctx, cr, inSNAC)
  42. if err != nil {
  43. return err
  44. }
  45. rt.logRequestAndResponse(ctx, SNACFrame, inSNAC, outSNAC.snacFrame, outSNAC.snacOut)
  46. return writeOutSNAC(SNACFrame, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
  47. case oscar.ChatNavCreateRoom:
  48. inSNAC := oscar.SNAC_0x0E_0x02_ChatRoomInfoUpdate{}
  49. if err := oscar.Unmarshal(&inSNAC, r); err != nil {
  50. return err
  51. }
  52. outSNAC, err := rt.CreateRoomHandler(ctx, sess, cr, NewChatRoom, inSNAC)
  53. if err != nil {
  54. return err
  55. }
  56. roomName, _ := inSNAC.GetString(oscar.ChatTLVRoomName)
  57. rt.Logger.InfoContext(ctx, "user started a chat room", slog.String("roomName", roomName))
  58. rt.logRequestAndResponse(ctx, SNACFrame, inSNAC, outSNAC.snacFrame, outSNAC.snacOut)
  59. return writeOutSNAC(SNACFrame, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
  60. default:
  61. return ErrUnsupportedSubGroup
  62. }
  63. }
  64. type ChatNavService struct {
  65. Logger *slog.Logger
  66. }
  67. type ChatCookie struct {
  68. Cookie []byte `len_prefix:"uint16"`
  69. SessID string `len_prefix:"uint16"`
  70. }
  71. func (s ChatNavService) RequestChatRightsHandler(context.Context) XMessage {
  72. return XMessage{
  73. snacFrame: oscar.SnacFrame{
  74. FoodGroup: oscar.CHAT_NAV,
  75. SubGroup: oscar.ChatNavNavInfo,
  76. },
  77. snacOut: oscar.SNAC_0x0D_0x09_ChatNavNavInfo{
  78. TLVRestBlock: oscar.TLVRestBlock{
  79. TLVList: oscar.TLVList{
  80. oscar.NewTLV(0x02, uint8(10)),
  81. oscar.NewTLV(0x03, oscar.SNAC_0x0D_0x09_TLVExchangeInfo{
  82. Identifier: 4,
  83. TLVBlock: oscar.TLVBlock{
  84. TLVList: oscar.TLVList{
  85. oscar.NewTLV(0x0002, uint16(0x0010)),
  86. oscar.NewTLV(0x00c9, uint16(15)),
  87. oscar.NewTLV(0x00d3, "default Exchange"),
  88. oscar.NewTLV(0x00d5, uint8(2)),
  89. oscar.NewTLV(0xd6, "us-ascii"),
  90. oscar.NewTLV(0xd7, "en"),
  91. oscar.NewTLV(0xd8, "us-ascii"),
  92. oscar.NewTLV(0xd9, "en"),
  93. },
  94. },
  95. }),
  96. },
  97. },
  98. },
  99. }
  100. }
  101. func NewChatRoom(logger *slog.Logger) ChatRoom {
  102. return ChatRoom{
  103. Cookie: uuid.New().String(),
  104. CreateTime: time.Now(),
  105. SessionManager: NewSessionManager(logger),
  106. }
  107. }
  108. type ChatRoomFactory func(logger *slog.Logger) ChatRoom
  109. func (s ChatNavService) CreateRoomHandler(_ context.Context, sess *Session, cr *ChatRegistry, newChatRoom ChatRoomFactory, snacPayloadIn oscar.SNAC_0x0E_0x02_ChatRoomInfoUpdate) (XMessage, error) {
  110. name, hasName := snacPayloadIn.GetString(oscar.ChatTLVRoomName)
  111. if !hasName {
  112. return XMessage{}, errors.New("unable to find chat name")
  113. }
  114. room := newChatRoom(s.Logger)
  115. room.DetailLevel = snacPayloadIn.DetailLevel
  116. room.Exchange = snacPayloadIn.Exchange
  117. room.InstanceNumber = snacPayloadIn.InstanceNumber
  118. room.Name = name
  119. cr.Register(room)
  120. // add user to chat room
  121. room.NewSessionWithSN(sess.ID, sess.ScreenName)
  122. return XMessage{
  123. snacFrame: oscar.SnacFrame{
  124. FoodGroup: oscar.CHAT_NAV,
  125. SubGroup: oscar.ChatNavNavInfo,
  126. },
  127. snacOut: oscar.SNAC_0x0D_0x09_ChatNavNavInfo{
  128. TLVRestBlock: oscar.TLVRestBlock{
  129. TLVList: oscar.TLVList{
  130. oscar.NewTLV(oscar.ChatNavTLVRoomInfo, oscar.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
  131. Exchange: snacPayloadIn.Exchange,
  132. Cookie: room.Cookie,
  133. InstanceNumber: snacPayloadIn.InstanceNumber,
  134. DetailLevel: snacPayloadIn.DetailLevel,
  135. TLVBlock: oscar.TLVBlock{
  136. TLVList: room.TLVList(),
  137. },
  138. }),
  139. },
  140. },
  141. },
  142. }, nil
  143. }
  144. func (s ChatNavService) RequestRoomInfoHandler(_ context.Context, cr *ChatRegistry, snacPayloadIn oscar.SNAC_0x0D_0x04_ChatNavRequestRoomInfo) (XMessage, error) {
  145. room, err := cr.Retrieve(string(snacPayloadIn.Cookie))
  146. if err != nil {
  147. return XMessage{}, err
  148. }
  149. return XMessage{
  150. snacFrame: oscar.SnacFrame{
  151. FoodGroup: oscar.CHAT_NAV,
  152. SubGroup: oscar.ChatNavNavInfo,
  153. },
  154. snacOut: oscar.SNAC_0x0D_0x09_ChatNavNavInfo{
  155. TLVRestBlock: oscar.TLVRestBlock{
  156. TLVList: oscar.TLVList{
  157. oscar.NewTLV(0x04, oscar.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
  158. Exchange: 4,
  159. Cookie: room.Cookie,
  160. InstanceNumber: 100,
  161. DetailLevel: 2,
  162. TLVBlock: oscar.TLVBlock{
  163. TLVList: room.TLVList(),
  164. },
  165. }),
  166. },
  167. },
  168. },
  169. }, nil
  170. }