emoji_favorites.go 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. // emoji_favorites.go handles persistence of the user's favorite emoji list,
  2. // stored as a JSON file in the cache directory.
  3. package chat
  4. import (
  5. "encoding/json"
  6. "log/slog"
  7. "os"
  8. "path/filepath"
  9. "sync"
  10. "github.com/ayn2op/discordo/internal/consts"
  11. )
  12. const maxFavoriteEmoji = 10
  13. // maxFavoritesFileSize is the upper bound on the emoji favorites JSON file (4 KiB).
  14. const maxFavoritesFileSize = 4096
  15. // maxFavoriteEmojiLen is the maximum byte length of a single emoji favorite string.
  16. const maxFavoriteEmojiLen = 64
  17. type emojiFavorites struct {
  18. Favorites []string `json:"favorites"`
  19. mu sync.RWMutex
  20. }
  21. var emojiFavoritesPath = filepath.Join(consts.CacheDir(), "emoji_favorites.json")
  22. func loadEmojiFavorites() *emojiFavorites {
  23. ef := &emojiFavorites{}
  24. data, err := os.ReadFile(emojiFavoritesPath)
  25. if err != nil {
  26. return ef
  27. }
  28. if len(data) > maxFavoritesFileSize {
  29. slog.Warn("emoji favorites file too large, ignoring", "size", len(data))
  30. return ef
  31. }
  32. if err := json.Unmarshal(data, ef); err != nil {
  33. slog.Warn("failed to parse emoji favorites", "err", err)
  34. return &emojiFavorites{}
  35. }
  36. // Reject any favorite strings that exceed the length limit.
  37. valid := ef.Favorites[:0]
  38. for _, e := range ef.Favorites {
  39. if len(e) <= maxFavoriteEmojiLen {
  40. valid = append(valid, e)
  41. } else {
  42. slog.Warn("emoji favorite too long, skipping", "len", len(e))
  43. }
  44. }
  45. ef.Favorites = valid
  46. return ef
  47. }
  48. // save persists favorites to disk. The caller must hold ef.mu.
  49. func (ef *emojiFavorites) save() {
  50. atomicSaveJSON(emojiFavoritesPath, ef)
  51. }
  52. func (ef *emojiFavorites) toggle(emoji string) {
  53. ef.mu.Lock()
  54. defer ef.mu.Unlock()
  55. for i, e := range ef.Favorites {
  56. if e == emoji {
  57. ef.Favorites = append(ef.Favorites[:i], ef.Favorites[i+1:]...)
  58. ef.save()
  59. return
  60. }
  61. }
  62. if len(ef.Favorites) >= maxFavoriteEmoji {
  63. return
  64. }
  65. ef.Favorites = append(ef.Favorites, emoji)
  66. ef.save()
  67. }
  68. func (ef *emojiFavorites) list() []string {
  69. ef.mu.RLock()
  70. defer ef.mu.RUnlock()
  71. out := make([]string, len(ef.Favorites))
  72. copy(out, ef.Favorites)
  73. return out
  74. }