You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

241 lines
5.8 KiB

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "log"
  8. "net/http"
  9. "os"
  10. "path/filepath"
  11. "strings"
  12. "github.com/pkg/errors"
  13. "github.com/zorchenhimer/MovieNight/common"
  14. )
  15. const emoteDir = "./static/emotes/"
  16. type TwitchUser struct {
  17. ID string
  18. Login string
  19. }
  20. type EmoteInfo struct {
  21. ID int
  22. Code string
  23. }
  24. func loadEmotes() error {
  25. //fmt.Println(processEmoteDir(emoteDir))
  26. newEmotes, err := processEmoteDir(emoteDir)
  27. if err != nil {
  28. return err
  29. }
  30. common.Emotes = newEmotes
  31. return nil
  32. }
  33. func processEmoteDir(path string) (common.EmotesMap, error) {
  34. dirInfo, err := ioutil.ReadDir(path)
  35. if err != nil {
  36. return nil, errors.Wrap(err, "could not open emoteDir:")
  37. }
  38. subDirs := []string{}
  39. for _, item := range dirInfo {
  40. // Get first level subdirs (eg, "twitch", "discord", etc)
  41. if item.IsDir() {
  42. subDirs = append(subDirs, item.Name())
  43. continue
  44. }
  45. }
  46. em := common.NewEmotesMap()
  47. // Find top level emotes
  48. em, err = findEmotes(path, em)
  49. if err != nil {
  50. return nil, errors.Wrap(err, "could not findEmotes() in top level directory:")
  51. }
  52. // Get second level subdirs (eg, "twitch", "zorchenhimer", etc)
  53. for _, dir := range subDirs {
  54. subd, err := ioutil.ReadDir(filepath.Join(path, dir))
  55. if err != nil {
  56. fmt.Printf("Error reading dir %q: %v\n", subd, err)
  57. continue
  58. }
  59. for _, d := range subd {
  60. if d.IsDir() {
  61. //emotes = append(emotes, findEmotes(filepath.Join(path, dir, d.Name()))...)
  62. p := filepath.Join(path, dir, d.Name())
  63. em, err = findEmotes(p, em)
  64. if err != nil {
  65. fmt.Printf("Error finding emotes in %q: %v\n", p, err)
  66. }
  67. }
  68. }
  69. }
  70. fmt.Printf("processEmoteDir: %d\n", len(em))
  71. return em, nil
  72. }
  73. func findEmotes(dir string, em common.EmotesMap) (common.EmotesMap, error) {
  74. //em := NewEmotesMap()
  75. fmt.Printf("finding emotes in %q\n", dir)
  76. emotePNGs, err := filepath.Glob(filepath.Join(dir, "*.png"))
  77. if err != nil {
  78. return em, fmt.Errorf("unable to glob emote directory: %s\n", err)
  79. }
  80. fmt.Printf("%d emotePNGs\n", len(emotePNGs))
  81. emoteGIFs, err := filepath.Glob(filepath.Join(dir, "*.gif"))
  82. if err != nil {
  83. return em, errors.Wrap(err, "unable to glob emote directory:")
  84. }
  85. fmt.Printf("%d emoteGIFs\n", len(emoteGIFs))
  86. for _, file := range emotePNGs {
  87. em = em.Add(file)
  88. //emotes = append(emotes, common.Emote{FullPath: dir, Code: file})
  89. }
  90. for _, file := range emoteGIFs {
  91. em = em.Add(file)
  92. }
  93. return em, nil
  94. }
  95. func getEmotes(names []string) error {
  96. users := getUserIDs(names)
  97. users = append(users, TwitchUser{ID: "0", Login: "twitch"})
  98. for _, user := range users {
  99. emotes, cheers, err := getChannelEmotes(user.ID)
  100. if err != nil {
  101. return errors.Wrapf(err, "could not get emote data for \"%s\"", user.ID)
  102. }
  103. emoteUserDir := filepath.Join(emoteDir, "twitch", user.Login)
  104. if _, err := os.Stat(emoteUserDir); os.IsNotExist(err) {
  105. os.MkdirAll(emoteUserDir, os.ModePerm)
  106. }
  107. for _, emote := range emotes {
  108. if !strings.ContainsAny(emote.Code, `:;\[]|?&`) {
  109. filePath := filepath.Join(emoteUserDir, emote.Code+".png")
  110. file, err := os.Create(filePath)
  111. if err != nil {
  112. return errors.Wrapf(err, "could not create emote file in path \"%s\":", filePath)
  113. }
  114. err = downloadEmote(emote.ID, file)
  115. if err != nil {
  116. return errors.Wrapf(err, "could not download emote %s:", emote.Code)
  117. }
  118. }
  119. }
  120. for amount, sizes := range cheers {
  121. name := fmt.Sprintf("%sCheer%s.gif", user.Login, amount)
  122. filePath := filepath.Join(emoteUserDir, name)
  123. file, err := os.Create(filePath)
  124. if err != nil {
  125. return errors.Wrapf(err, "could not create emote file in path \"%s\":", filePath)
  126. }
  127. err = downloadCheerEmote(sizes["4"], file)
  128. if err != nil {
  129. return errors.Wrapf(err, "could not download emote %s:", name)
  130. }
  131. }
  132. }
  133. return nil
  134. }
  135. func getUserIDs(names []string) []TwitchUser {
  136. logins := strings.Join(names, "&login=")
  137. request, err := http.NewRequest("GET", fmt.Sprintf("https://api.twitch.tv/helix/users?login=%s", logins), nil)
  138. if err != nil {
  139. log.Fatalln("Error generating new request:", err)
  140. }
  141. request.Header.Set("Client-ID", settings.TwitchClientID)
  142. request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", settings.TwitchClientSecret))
  143. client := http.Client{}
  144. resp, err := client.Do(request)
  145. if err != nil {
  146. log.Fatalln("Error sending request:", err)
  147. }
  148. decoder := json.NewDecoder(resp.Body)
  149. type userResponse struct {
  150. Data []TwitchUser
  151. }
  152. var data userResponse
  153. err = decoder.Decode(&data)
  154. if err != nil {
  155. log.Fatalln("Error decoding data:", err)
  156. }
  157. return data.Data
  158. }
  159. func getChannelEmotes(ID string) ([]EmoteInfo, map[string]map[string]string, error) {
  160. resp, err := http.Get("https://api.twitchemotes.com/api/v4/channels/" + ID)
  161. if err != nil {
  162. return nil, nil, errors.Wrap(err, "could not get emotes")
  163. }
  164. decoder := json.NewDecoder(resp.Body)
  165. type EmoteResponse struct {
  166. Emotes []EmoteInfo
  167. Cheermotes map[string]map[string]string
  168. }
  169. var data EmoteResponse
  170. err = decoder.Decode(&data)
  171. if err != nil {
  172. return nil, nil, errors.Wrap(err, "could not decode emotes")
  173. }
  174. return data.Emotes, data.Cheermotes, nil
  175. }
  176. func downloadEmote(ID int, file *os.File) error {
  177. resp, err := http.Get(fmt.Sprintf("https://static-cdn.jtvnw.net/emoticons/v1/%d/3.0", ID))
  178. if err != nil {
  179. return errors.Errorf("could not download emote file %s: %v", file.Name(), err)
  180. }
  181. defer resp.Body.Close()
  182. _, err = io.Copy(file, resp.Body)
  183. if err != nil {
  184. return errors.Errorf("could not save emote: %v", err)
  185. }
  186. return nil
  187. }
  188. func downloadCheerEmote(url string, file *os.File) error {
  189. resp, err := http.Get(url)
  190. if err != nil {
  191. return errors.Errorf("could not download cheer file %s: %v", file.Name(), err)
  192. }
  193. defer resp.Body.Close()
  194. _, err = io.Copy(file, resp.Body)
  195. if err != nil {
  196. return errors.Errorf("could not save cheer: %v", err)
  197. }
  198. return nil
  199. }