chat.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. package server
  2. import (
  3. "context"
  4. "github.com/mkaminski/goaim/user"
  5. "io"
  6. "log/slog"
  7. "github.com/mkaminski/goaim/oscar"
  8. )
  9. type ChatHandler interface {
  10. ChannelMsgToHostHandler(ctx context.Context, sess *user.Session, room ChatRoom, snacPayloadIn oscar.SNAC_0x0E_0x05_ChatChannelMsgToHost) (*oscar.XMessage, error)
  11. }
  12. func NewChatRouter(logger *slog.Logger) ChatRouter {
  13. return ChatRouter{
  14. ChatHandler: ChatService{},
  15. RouteLogger: RouteLogger{
  16. Logger: logger,
  17. },
  18. }
  19. }
  20. type ChatRouter struct {
  21. ChatHandler
  22. RouteLogger
  23. }
  24. func (rt *ChatRouter) RouteChat(ctx context.Context, sess *user.Session, room ChatRoom, SNACFrame oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
  25. switch SNACFrame.SubGroup {
  26. case oscar.ChatChannelMsgToHost:
  27. inSNAC := oscar.SNAC_0x0E_0x05_ChatChannelMsgToHost{}
  28. if err := oscar.Unmarshal(&inSNAC, r); err != nil {
  29. return err
  30. }
  31. outSNAC, err := rt.ChannelMsgToHostHandler(ctx, sess, room, inSNAC)
  32. if err != nil {
  33. return err
  34. }
  35. if outSNAC == nil {
  36. return nil
  37. }
  38. rt.Logger.InfoContext(ctx, "user sent a chat message")
  39. rt.logRequestAndResponse(ctx, SNACFrame, inSNAC, outSNAC.SnacFrame, outSNAC.SnacOut)
  40. return writeOutSNAC(SNACFrame, outSNAC.SnacFrame, outSNAC.SnacOut, sequence, w)
  41. default:
  42. return ErrUnsupportedSubGroup
  43. }
  44. }
  45. type ChatService struct {
  46. }
  47. func (s ChatService) ChannelMsgToHostHandler(ctx context.Context, sess *user.Session, room ChatRoom, snacPayloadIn oscar.SNAC_0x0E_0x05_ChatChannelMsgToHost) (*oscar.XMessage, error) {
  48. snacFrameOut := oscar.SnacFrame{
  49. FoodGroup: oscar.CHAT,
  50. SubGroup: oscar.ChatChannelMsgToClient,
  51. }
  52. snacPayloadOut := oscar.SNAC_0x0E_0x06_ChatChannelMsgToClient{
  53. Cookie: snacPayloadIn.Cookie,
  54. Channel: snacPayloadIn.Channel,
  55. TLVRestBlock: oscar.TLVRestBlock{
  56. TLVList: snacPayloadIn.TLVList,
  57. },
  58. }
  59. snacPayloadOut.AddTLV(
  60. oscar.NewTLV(oscar.ChatTLVSenderInformation, oscar.TLVUserInfo{
  61. ScreenName: sess.ScreenName(),
  62. WarningLevel: sess.Warning(),
  63. TLVBlock: oscar.TLVBlock{
  64. TLVList: sess.UserInfo(),
  65. },
  66. }),
  67. )
  68. // send message to all the participants except sender
  69. room.BroadcastExcept(ctx, sess, oscar.XMessage{
  70. SnacFrame: snacFrameOut,
  71. SnacOut: snacPayloadOut,
  72. })
  73. var ret *oscar.XMessage
  74. if _, ackMsg := snacPayloadIn.GetTLV(oscar.ChatTLVEnableReflectionFlag); ackMsg {
  75. // reflect the message back to the sender
  76. ret = &oscar.XMessage{
  77. SnacFrame: snacFrameOut,
  78. SnacOut: snacPayloadOut,
  79. }
  80. }
  81. return ret, nil
  82. }
  83. func SetOnlineChatUsers(ctx context.Context, sess *user.Session, sm ChatRoom) {
  84. snacPayloadOut := oscar.SNAC_0x0E_0x03_ChatUsersJoined{}
  85. sessions := sm.Participants()
  86. for _, uSess := range sessions {
  87. snacPayloadOut.Users = append(snacPayloadOut.Users, oscar.TLVUserInfo{
  88. ScreenName: uSess.ScreenName(),
  89. WarningLevel: uSess.Warning(),
  90. TLVBlock: oscar.TLVBlock{
  91. TLVList: uSess.UserInfo(),
  92. },
  93. })
  94. }
  95. sm.SendToScreenName(ctx, sess.ScreenName(), oscar.XMessage{
  96. SnacFrame: oscar.SnacFrame{
  97. FoodGroup: oscar.CHAT,
  98. SubGroup: oscar.ChatUsersJoined,
  99. },
  100. SnacOut: snacPayloadOut,
  101. })
  102. }
  103. func AlertUserJoined(ctx context.Context, sess *user.Session, sm SessionManager) {
  104. sm.BroadcastExcept(ctx, sess, oscar.XMessage{
  105. SnacFrame: oscar.SnacFrame{
  106. FoodGroup: oscar.CHAT,
  107. SubGroup: oscar.ChatUsersJoined,
  108. },
  109. SnacOut: oscar.SNAC_0x0E_0x03_ChatUsersJoined{
  110. Users: []oscar.TLVUserInfo{
  111. {
  112. ScreenName: sess.ScreenName(),
  113. WarningLevel: sess.Warning(),
  114. TLVBlock: oscar.TLVBlock{
  115. TLVList: sess.UserInfo(),
  116. },
  117. },
  118. },
  119. },
  120. })
  121. }
  122. func AlertUserLeft(ctx context.Context, sess *user.Session, sm SessionManager) {
  123. sm.BroadcastExcept(ctx, sess, oscar.XMessage{
  124. SnacFrame: oscar.SnacFrame{
  125. FoodGroup: oscar.CHAT,
  126. SubGroup: oscar.ChatUsersLeft,
  127. },
  128. SnacOut: oscar.SNAC_0x0E_0x04_ChatUsersLeft{
  129. Users: []oscar.TLVUserInfo{
  130. {
  131. ScreenName: sess.ScreenName(),
  132. WarningLevel: sess.Warning(),
  133. TLVBlock: oscar.TLVBlock{
  134. TLVList: sess.UserInfo(),
  135. },
  136. },
  137. },
  138. },
  139. })
  140. }
  141. func SendChatRoomInfoUpdate(ctx context.Context, sess *user.Session, room ChatRoom) {
  142. room.SendToScreenName(ctx, sess.ScreenName(), oscar.XMessage{
  143. SnacFrame: oscar.SnacFrame{
  144. FoodGroup: oscar.CHAT,
  145. SubGroup: oscar.ChatRoomInfoUpdate,
  146. },
  147. SnacOut: oscar.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
  148. Exchange: 4,
  149. Cookie: room.Cookie,
  150. InstanceNumber: 100,
  151. DetailLevel: 2,
  152. TLVBlock: oscar.TLVBlock{
  153. TLVList: room.TLVList(),
  154. },
  155. },
  156. })
  157. }