chat.go 4.4 KB

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