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.

151 lines
3.8 KiB

8 years ago
8 years ago
8 years ago
  1. // Package guggy implements a Service which adds !commands for Guggy.
  2. package guggy
  3. import (
  4. "bytes"
  5. "encoding/json"
  6. "fmt"
  7. "io/ioutil"
  8. "math"
  9. "net/http"
  10. "strings"
  11. "github.com/matrix-org/go-neb/types"
  12. log "github.com/sirupsen/logrus"
  13. mevt "maunium.net/go/mautrix/event"
  14. "maunium.net/go/mautrix/id"
  15. )
  16. // ServiceType of the Guggy service
  17. const ServiceType = "guggy"
  18. var httpClient = &http.Client{}
  19. type guggyQuery struct {
  20. // "mp4" or "gif"
  21. Format string `json:"format"`
  22. // Query sentence
  23. Sentence string `json:"sentence"`
  24. }
  25. type guggyGifResult struct {
  26. ReqID string `json:"reqId"`
  27. GIF string `json:"gif"`
  28. Width float64 `json:"width"`
  29. Height float64 `json:"height"`
  30. }
  31. // Service contains the Config fields for the Guggy service.
  32. //
  33. // Example request:
  34. // {
  35. // "api_key": "fkweugfyuwegfweyg"
  36. // }
  37. type Service struct {
  38. types.DefaultService
  39. // The Guggy API key to use when making HTTP requests to Guggy.
  40. APIKey string `json:"api_key"`
  41. }
  42. // Commands supported:
  43. // !guggy some search query without quotes
  44. // Responds with a suitable GIF into the same room as the command.
  45. func (s *Service) Commands(client types.MatrixClient) []types.Command {
  46. return []types.Command{
  47. {
  48. Path: []string{"guggy"},
  49. Command: func(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
  50. return s.cmdGuggy(client, roomID, userID, args)
  51. },
  52. },
  53. }
  54. }
  55. func (s *Service) cmdGuggy(client types.MatrixClient, roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
  56. // only 1 arg which is the text to search for.
  57. querySentence := strings.Join(args, " ")
  58. gifResult, err := s.text2gifGuggy(querySentence)
  59. if err != nil {
  60. return nil, fmt.Errorf("Failed to query Guggy: %s", err.Error())
  61. }
  62. if gifResult.GIF == "" {
  63. return mevt.MessageEventContent{
  64. MsgType: mevt.MsgNotice,
  65. Body: "No GIF found!",
  66. }, nil
  67. }
  68. resUpload, err := client.UploadLink(gifResult.GIF)
  69. if err != nil {
  70. return nil, fmt.Errorf("Failed to upload Guggy image to matrix: %s", err.Error())
  71. }
  72. return mevt.MessageEventContent{
  73. MsgType: "m.image",
  74. Body: querySentence,
  75. URL: resUpload.ContentURI.CUString(),
  76. Info: &mevt.FileInfo{
  77. Height: int(math.Floor(gifResult.Height)),
  78. Width: int(math.Floor(gifResult.Width)),
  79. MimeType: "image/gif",
  80. },
  81. }, nil
  82. }
  83. // text2gifGuggy returns info about a gif
  84. func (s *Service) text2gifGuggy(querySentence string) (*guggyGifResult, error) {
  85. log.Info("Transforming to GIF query ", querySentence)
  86. var query guggyQuery
  87. query.Format = "gif"
  88. query.Sentence = querySentence
  89. reqBody, err := json.Marshal(query)
  90. if err != nil {
  91. return nil, err
  92. }
  93. reader := bytes.NewReader(reqBody)
  94. req, err := http.NewRequest("POST", "https://text2gif.guggy.com/guggify", reader)
  95. if err != nil {
  96. log.Error(err)
  97. return nil, err
  98. }
  99. req.Header.Add("Content-Type", "application/json")
  100. req.Header.Add("apiKey", s.APIKey)
  101. res, err := httpClient.Do(req)
  102. if res != nil {
  103. defer res.Body.Close()
  104. }
  105. if err != nil {
  106. log.Error(err)
  107. return nil, err
  108. }
  109. if res.StatusCode < 200 || res.StatusCode >= 300 {
  110. resBytes, err := ioutil.ReadAll(res.Body)
  111. if err != nil {
  112. log.WithError(err).Error("Failed to decode Guggy response body")
  113. }
  114. log.WithFields(log.Fields{
  115. "code": res.StatusCode,
  116. "body": string(resBytes),
  117. }).Error("Failed to query Guggy")
  118. return nil, fmt.Errorf("Failed to decode response (HTTP %d)", res.StatusCode)
  119. }
  120. var result guggyGifResult
  121. if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
  122. return nil, fmt.Errorf("Failed to decode response (HTTP %d): %s", res.StatusCode, err.Error())
  123. }
  124. return &result, nil
  125. }
  126. func init() {
  127. types.RegisterService(func(serviceID string, serviceUserID id.UserID, webhookEndpointURL string) types.Service {
  128. return &Service{
  129. DefaultService: types.NewDefaultService(serviceID, serviceUserID, ServiceType),
  130. }
  131. })
  132. }