webapi_imlog.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. package state
  2. import (
  3. "sort"
  4. "strings"
  5. )
  6. // WebAPIStoredIM is one message in a Web AIM session's in-memory IM log.
  7. // The Web AIM client expects fetchStoredIMs entries with sender, message, msgId, and date.
  8. type WebAPIStoredIM struct {
  9. Sender string
  10. Message string
  11. MsgID string
  12. Date int64 // Unix seconds
  13. }
  14. // AddStoredIM appends a message to the per-partner log for this session.
  15. func (s *WebAPISession) AddStoredIM(partnerAimID, sender, message, msgID string, date int64) {
  16. if s == nil || partnerAimID == "" || message == "" {
  17. return
  18. }
  19. s.imLogMu.Lock()
  20. defer s.imLogMu.Unlock()
  21. if s.imLog == nil {
  22. s.imLog = make(map[string][]WebAPIStoredIM)
  23. }
  24. s.imLog[normalizeWebAPIAimID(partnerAimID)] = append(s.imLog[normalizeWebAPIAimID(partnerAimID)], WebAPIStoredIM{
  25. Sender: sender,
  26. Message: message,
  27. MsgID: msgID,
  28. Date: date,
  29. })
  30. }
  31. // StoredIM is one entry in a fetchStoredIMs reply.
  32. //
  33. // Date is a float because AMF3 encodes whole numbers in 29 bits, which a Unix
  34. // timestamp overflows.
  35. type StoredIM struct {
  36. Sender string `json:"sender" xml:"sender"`
  37. Message string `json:"message" xml:"message"`
  38. MsgID string `json:"msgId" xml:"msgId"`
  39. Date float64 `json:"date" xml:"date"`
  40. }
  41. // StoredIMQuery describes filters for fetchStoredIMs.
  42. type StoredIMQuery struct {
  43. PartnerAimID string
  44. StartTime int64
  45. EndTime int64
  46. NToGet int
  47. SortOrder string
  48. SkipMsgID string
  49. StopMsgID string
  50. }
  51. // GetStoredIMs returns stored messages for a conversation partner, filtered and sorted
  52. // per the Web AIM client's fetchStoredIMs parameters.
  53. func (s *WebAPISession) GetStoredIMs(q StoredIMQuery) []StoredIM {
  54. if s == nil || q.PartnerAimID == "" {
  55. return nil
  56. }
  57. s.imLogMu.Lock()
  58. msgs := append([]WebAPIStoredIM(nil), s.imLog[normalizeWebAPIAimID(q.PartnerAimID)]...)
  59. s.imLogMu.Unlock()
  60. if len(msgs) == 0 {
  61. return []StoredIM{}
  62. }
  63. filtered := make([]WebAPIStoredIM, 0, len(msgs))
  64. for _, msg := range msgs {
  65. if q.StartTime > 0 && msg.Date < q.StartTime {
  66. continue
  67. }
  68. if q.EndTime > 0 && msg.Date > q.EndTime {
  69. continue
  70. }
  71. filtered = append(filtered, msg)
  72. }
  73. descending := strings.EqualFold(q.SortOrder, "descendingDate")
  74. sort.Slice(filtered, func(i, j int) bool {
  75. if descending {
  76. return filtered[i].Date > filtered[j].Date
  77. }
  78. return filtered[i].Date < filtered[j].Date
  79. })
  80. if q.SkipMsgID != "" {
  81. for i, msg := range filtered {
  82. if msg.MsgID == q.SkipMsgID {
  83. filtered = filtered[i+1:]
  84. break
  85. }
  86. }
  87. }
  88. if q.StopMsgID != "" {
  89. for i, msg := range filtered {
  90. if msg.MsgID == q.StopMsgID {
  91. filtered = filtered[:i]
  92. break
  93. }
  94. }
  95. }
  96. n := q.NToGet
  97. if n <= 0 {
  98. n = 100
  99. }
  100. if len(filtered) > n {
  101. filtered = filtered[:n]
  102. }
  103. out := make([]StoredIM, len(filtered))
  104. for i, msg := range filtered {
  105. out[i] = StoredIM{
  106. Sender: msg.Sender,
  107. Message: msg.Message,
  108. MsgID: msg.MsgID,
  109. Date: float64(msg.Date),
  110. }
  111. }
  112. return out
  113. }
  114. // normalizeWebAPIAimID keys the IM log by the same normalization the web client
  115. // applies to aimIds, so a partner stored from a display screen name is still
  116. // found when the client queries by aimId.
  117. func normalizeWebAPIAimID(aimID string) string {
  118. return NewIdentScreenName(aimID).String()
  119. }