|
|
// Package giphy implements a Service which adds !commands for Giphy.
package giphy
import ( "encoding/json" "fmt" "net/http" "net/url" "strconv" "strings"
"github.com/matrix-org/go-neb/types" log "github.com/sirupsen/logrus" "maunium.net/go/mautrix/event" mevt "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/id" )
// ServiceType of the Giphy service.
const ServiceType = "giphy"
type image struct { URL string `json:"url"` // Giphy returns ints as strings..
Width string `json:"width"` Height string `json:"height"` Size string `json:"size"` }
type result struct { Slug string `json:"slug"` Images struct { Downsized image `json:"downsized"` Original image `json:"original"` } `json:"images"` }
type giphySearch struct { Data result `json:"data"` }
// Service contains the Config fields for the Giphy Service.
//
// Example request:
// {
// "api_key": "dc6zaTOxFJmzC",
// "use_downsized": false
// }
type Service struct { types.DefaultService // The Giphy API key to use when making HTTP requests to Giphy.
// The public beta API key is "dc6zaTOxFJmzC".
APIKey string `json:"api_key"` // Whether to use the downsized image from Giphy.
// Uses the original image when set to false.
// Defaults to false.
UseDownsized bool `json:"use_downsized"` }
// Commands supported:
// !giphy some search query without quotes
// Responds with a suitable GIF into the same room as the command.
func (s *Service) Commands(client types.MatrixClient) []types.Command { return []types.Command{ types.Command{ Path: []string{"giphy"}, Command: func(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) { return s.cmdGiphy(client, roomID, userID, args) }, }, } }
func (s *Service) cmdGiphy(client types.MatrixClient, roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) { // only 1 arg which is the text to search for.
query := strings.Join(args, " ") gifResult, err := s.searchGiphy(query) if err != nil { return nil, err }
image := gifResult.Images.Original if s.UseDownsized { image = gifResult.Images.Downsized }
if image.URL == "" { return nil, fmt.Errorf("No results") } resUpload, err := client.UploadLink(image.URL) if err != nil { return nil, err }
return mevt.MessageEventContent{ MsgType: event.MsgImage, Body: gifResult.Slug, URL: resUpload.ContentURI.CUString(), Info: &mevt.FileInfo{ Height: asInt(image.Height), Width: asInt(image.Width), MimeType: "image/gif", Size: asInt(image.Size), }, }, nil }
// searchGiphy returns info about a gif
func (s *Service) searchGiphy(query string) (*result, error) { log.Info("Searching giphy for ", query) u, err := url.Parse("http://api.giphy.com/v1/gifs/translate") if err != nil { return nil, err } q := u.Query() q.Set("s", query) q.Set("api_key", s.APIKey) u.RawQuery = q.Encode() res, err := http.Get(u.String()) if res != nil { defer res.Body.Close() } if err != nil { return nil, err } var search giphySearch if err := json.NewDecoder(res.Body).Decode(&search); err != nil { // Giphy returns a JSON object which has { data: [] } if there are 0 results.
// This fails to be deserialised by Go.
return nil, fmt.Errorf("No results") } return &search.Data, nil }
func asInt(strInt string) int { i64, err := strconv.ParseInt(strInt, 10, 32) if err != nil { return 0 // default to 0 since these are all just hints to the client
} return int(i64) }
func init() { types.RegisterService(func(serviceID string, serviceUserID id.UserID, webhookEndpointURL string) types.Service { return &Service{ DefaultService: types.NewDefaultService(serviceID, serviceUserID, ServiceType), } }) }
|