4
0

webapi_event_converter.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. package handlers
  2. import "github.com/mk6i/open-oscar-server/server/webapi/types"
  3. // ConvertEventForAMF3 converts a WebAPIEvent to a map suitable for AMF3 encoding,
  4. // ensuring all timestamps are float64 to avoid uint29 overflow issues.
  5. func ConvertEventForAMF3(event types.Event) map[string]interface{} {
  6. result := map[string]interface{}{
  7. "type": string(event.Type),
  8. "seqNum": event.SeqNum,
  9. "timestamp": float64(event.Timestamp), // Convert to float64
  10. }
  11. // Convert event data based on type
  12. switch event.Type {
  13. case types.EventTypeIM:
  14. if imEvent, ok := event.Data.(types.IMEvent); ok {
  15. // Gromit expects 'source' as a user object and 'autoresponse' (lowercase)
  16. eventData := map[string]interface{}{
  17. "source": map[string]interface{}{
  18. "aimId": imEvent.Source.AimID,
  19. "displayId": imEvent.Source.DisplayID,
  20. "userType": imEvent.Source.UserType,
  21. "state": imEvent.Source.State,
  22. },
  23. "message": imEvent.Message,
  24. "timestamp": imEvent.Timestamp, // Already float64
  25. "autoresponse": imEvent.AutoResp,
  26. }
  27. if imEvent.MsgID != "" {
  28. eventData["msgId"] = imEvent.MsgID
  29. }
  30. result["eventData"] = eventData
  31. } else if dataMap, ok := event.Data.(map[string]interface{}); ok {
  32. // Already a map, ensure timestamps are float64
  33. if ts, exists := dataMap["timestamp"]; exists {
  34. if tsInt, ok := ts.(int64); ok {
  35. dataMap["timestamp"] = float64(tsInt)
  36. }
  37. }
  38. result["eventData"] = dataMap
  39. } else {
  40. result["eventData"] = event.Data
  41. }
  42. case types.EventTypeOfflineIM:
  43. if imEvent, ok := event.Data.(types.IMEvent); ok {
  44. result["eventData"] = map[string]interface{}{
  45. "aimId": imEvent.Source.AimID,
  46. "message": imEvent.Message,
  47. "timestamp": float64(imEvent.Timestamp), // Convert to float64
  48. }
  49. } else if dataMap, ok := event.Data.(map[string]interface{}); ok {
  50. // Already a map, ensure timestamps are float64
  51. if ts, exists := dataMap["timestamp"]; exists {
  52. if tsInt, ok := ts.(int64); ok {
  53. dataMap["timestamp"] = float64(tsInt)
  54. }
  55. }
  56. result["eventData"] = dataMap
  57. } else {
  58. result["eventData"] = event.Data
  59. }
  60. case types.EventTypePresence:
  61. if presenceEvent, ok := event.Data.(types.PresenceEvent); ok {
  62. eventData := map[string]interface{}{
  63. "aimId": presenceEvent.AimID,
  64. "state": presenceEvent.State,
  65. "userType": presenceEvent.UserType,
  66. }
  67. // Convert timestamp fields to float64
  68. if presenceEvent.OnlineTime > 0 {
  69. eventData["onlineTime"] = float64(presenceEvent.OnlineTime)
  70. }
  71. result["eventData"] = eventData
  72. } else if dataMap, ok := event.Data.(map[string]interface{}); ok {
  73. // Already a map, ensure timestamps are float64
  74. if ot, exists := dataMap["onlineTime"]; exists {
  75. if otInt, ok := ot.(int64); ok {
  76. dataMap["onlineTime"] = float64(otInt)
  77. }
  78. }
  79. result["eventData"] = dataMap
  80. } else {
  81. result["eventData"] = event.Data
  82. }
  83. case types.EventType("myInfo"):
  84. // MyInfo events often contain timestamps
  85. if dataMap, ok := event.Data.(map[string]interface{}); ok {
  86. // Convert any int64 timestamps to float64
  87. for key, val := range dataMap {
  88. if key == "onlineTime" || key == "memberSince" || key == "awayTime" || key == "statusTime" {
  89. if intVal, ok := val.(int64); ok {
  90. dataMap[key] = float64(intVal)
  91. }
  92. }
  93. }
  94. result["eventData"] = dataMap
  95. } else {
  96. result["eventData"] = event.Data
  97. }
  98. case types.EventTypeBuddyList:
  99. // Buddy list events are already converted to maps in FormatBuddyListEvent
  100. // Just pass through
  101. result["eventData"] = event.Data
  102. case types.EventTypeTyping:
  103. if typingEvent, ok := event.Data.(types.TypingEvent); ok {
  104. // Gromit expects 'aimId' and 'typingStatus'
  105. result["eventData"] = map[string]interface{}{
  106. "aimId": typingEvent.From,
  107. "typingStatus": typingEvent.Typing,
  108. }
  109. } else {
  110. result["eventData"] = event.Data
  111. }
  112. case types.EventTypeSentIM:
  113. if sentIMEvent, ok := event.Data.(types.SentIMEvent); ok {
  114. // Gromit expects both 'source' (sender) and 'dest' (recipient) for sentIM
  115. // The parseIM function needs source even for outgoing messages
  116. eventData := map[string]interface{}{
  117. "source": map[string]interface{}{
  118. "aimId": sentIMEvent.Sender.AimID,
  119. "displayId": sentIMEvent.Sender.DisplayID,
  120. "userType": sentIMEvent.Sender.UserType,
  121. "state": "online",
  122. },
  123. "dest": map[string]interface{}{
  124. "aimId": sentIMEvent.Dest.AimID,
  125. "displayId": sentIMEvent.Dest.DisplayID,
  126. "userType": sentIMEvent.Dest.UserType,
  127. "state": "online",
  128. },
  129. "message": sentIMEvent.Message,
  130. "timestamp": sentIMEvent.Timestamp, // Already float64
  131. "autoresponse": sentIMEvent.AutoResp,
  132. }
  133. if sentIMEvent.MsgID != "" {
  134. eventData["msgId"] = sentIMEvent.MsgID
  135. }
  136. result["eventData"] = eventData
  137. } else {
  138. result["eventData"] = event.Data
  139. }
  140. default:
  141. // For unknown types, check if data is a map and convert any int64 values
  142. if dataMap, ok := event.Data.(map[string]interface{}); ok {
  143. result["eventData"] = convertTimestampsInMap(dataMap)
  144. } else {
  145. result["eventData"] = event.Data
  146. }
  147. }
  148. return result
  149. }
  150. // convertTimestampsInMap recursively converts int64 values that look like timestamps to float64
  151. func convertTimestampsInMap(data map[string]interface{}) map[string]interface{} {
  152. result := make(map[string]interface{})
  153. for key, val := range data {
  154. // Check if key suggests it's a timestamp
  155. if isTimestampField(key) {
  156. if intVal, ok := val.(int64); ok {
  157. result[key] = float64(intVal)
  158. continue
  159. }
  160. }
  161. // Recursively process nested maps
  162. if nestedMap, ok := val.(map[string]interface{}); ok {
  163. result[key] = convertTimestampsInMap(nestedMap)
  164. } else if nestedSlice, ok := val.([]interface{}); ok {
  165. convertedSlice := make([]interface{}, len(nestedSlice))
  166. for i, item := range nestedSlice {
  167. if itemMap, ok := item.(map[string]interface{}); ok {
  168. convertedSlice[i] = convertTimestampsInMap(itemMap)
  169. } else {
  170. convertedSlice[i] = item
  171. }
  172. }
  173. result[key] = convertedSlice
  174. } else {
  175. result[key] = val
  176. }
  177. }
  178. return result
  179. }
  180. // isTimestampField checks if a field name suggests it contains a timestamp
  181. func isTimestampField(fieldName string) bool {
  182. timestampFields := []string{
  183. "timestamp", "Timestamp",
  184. "onlineTime", "OnlineTime",
  185. "memberSince", "MemberSince",
  186. "awayTime", "AwayTime",
  187. "statusTime", "StatusTime",
  188. "idleTime", "IdleTime",
  189. "loginTime", "LoginTime",
  190. "createdAt", "CreatedAt",
  191. "updatedAt", "UpdatedAt",
  192. }
  193. for _, tf := range timestampFields {
  194. if fieldName == tf {
  195. return true
  196. }
  197. }
  198. return false
  199. }
  200. // ConvertEventsForAMF3 converts a slice of WebAPIEvents for AMF3 encoding
  201. func ConvertEventsForAMF3(events []types.Event) []interface{} {
  202. result := make([]interface{}, len(events))
  203. for i, event := range events {
  204. result[i] = ConvertEventForAMF3(event)
  205. }
  206. return result
  207. }