Browse Source

Add giphy integration

kegan/giphy
Kegan Dougal 8 years ago
parent
commit
6f2d9dc72c
  1. 15
      README.md
  2. 2
      src/github.com/matrix-org/go-neb/clients/clients.go
  3. 1
      src/github.com/matrix-org/go-neb/goneb.go
  4. 58
      src/github.com/matrix-org/go-neb/matrix/matrix.go
  5. 15
      src/github.com/matrix-org/go-neb/matrix/types.go
  6. 2
      src/github.com/matrix-org/go-neb/services/echo/echo.go
  7. 127
      src/github.com/matrix-org/go-neb/services/giphy/giphy.go
  8. 2
      src/github.com/matrix-org/go-neb/services/github/github.go
  9. 2
      src/github.com/matrix-org/go-neb/services/jira/jira.go
  10. 2
      src/github.com/matrix-org/go-neb/types/types.go

15
README.md

@ -303,6 +303,21 @@ curl -X POST localhost:4050/admin/configureService --data-binary '{
}'
```
## Starting a Giphy Service
### Create a Giphy bot
```
curl -X POST localhost:4050/admin/configureService --data-binary '{
"Type": "giphy",
"Id": "giphyid",
"UserID": "@goneb:localhost",
"Config": {
"APIKey": "YOUR_API_KEY"
}
}'
```
# Developing on go-neb.

2
src/github.com/matrix-org/go-neb/clients/clients.go

@ -151,7 +151,7 @@ func (c *Clients) newClient(config types.ClientConfig) (*matrix.Client, error) {
}
var plugins []plugin.Plugin
for _, service := range services {
plugins = append(plugins, service.Plugin(event.RoomID))
plugins = append(plugins, service.Plugin(client, event.RoomID))
}
plugin.OnMessage(plugins, client, event)
})

1
src/github.com/matrix-org/go-neb/goneb.go

@ -9,6 +9,7 @@ import (
_ "github.com/matrix-org/go-neb/realms/jira"
"github.com/matrix-org/go-neb/server"
_ "github.com/matrix-org/go-neb/services/echo"
_ "github.com/matrix-org/go-neb/services/giphy"
_ "github.com/matrix-org/go-neb/services/github"
_ "github.com/matrix-org/go-neb/services/jira"
"github.com/matrix-org/go-neb/types"

58
src/github.com/matrix-org/go-neb/matrix/matrix.go

@ -14,8 +14,10 @@ package matrix
import (
"bytes"
"encoding/json"
"fmt"
log "github.com/Sirupsen/logrus"
"github.com/matrix-org/go-neb/errors"
"io"
"io/ioutil"
"net/http"
"net/url"
@ -44,9 +46,17 @@ type Client struct {
}
func (cli *Client) buildURL(urlPath ...string) string {
ps := []string{cli.Prefix}
for _, p := range urlPath {
ps = append(ps, p)
}
return cli.buildBaseURL(ps...)
}
func (cli *Client) buildBaseURL(urlPath ...string) string {
// copy the URL. Purposefully ignore error as the input is from a valid URL already
hsURL, _ := url.Parse(cli.HomeserverURL.String())
parts := []string{hsURL.Path, cli.Prefix}
parts := []string{hsURL.Path}
parts = append(parts, urlPath...)
hsURL.Path = path.Join(parts...)
query := hsURL.Query()
@ -116,6 +126,52 @@ func (cli *Client) SendText(roomID, text string) (string, error) {
TextMessage{"m.text", text})
}
// UploadLink uploads an HTTP URL and then returns an MXC URI.
func (cli *Client) UploadLink(link string) (string, error) {
res, err := http.Get(link)
if res != nil {
defer res.Body.Close()
}
if err != nil {
return "", err
}
log.Print("GOT GIF BODY ", link)
return cli.UploadToContentRepo(res.Body, res.Header.Get("Content-Type"), res.Header.Get("Content-Length"))
}
// UploadToContentRepo uploads the given bytes to the content repository and returns an MXC URI.
func (cli *Client) UploadToContentRepo(content io.Reader, contentType, contentLength string) (string, error) {
req, err := http.NewRequest("POST", cli.buildBaseURL("_matrix/media/r0/upload"), content)
if err != nil {
return "", err
}
req.Header.Set("Content-Type", contentType)
cl, err := strconv.Atoi(contentLength)
if err != nil {
return "", err
}
req.ContentLength = int64(cl)
log.Print("Doing upload request to ", req.URL)
log.Print("Type=", contentType, "Length=", contentLength)
res, err := cli.httpClient.Do(req)
if res != nil {
defer res.Body.Close()
}
if err != nil {
return "", err
}
if res.StatusCode != 200 {
return "", fmt.Errorf("Upload request returned HTTP %d", res.StatusCode)
}
m := struct {
ContentURI string `json:"content_uri"`
}{}
if err := json.NewDecoder(res.Body).Decode(&m); err != nil {
return "", err
}
return m.ContentURI, nil
}
// Sync starts syncing with the provided Homeserver. This function will be invoked continually.
// If Sync is called twice then the first sync will be stopped.
func (cli *Client) Sync() {

15
src/github.com/matrix-org/go-neb/matrix/types.go

@ -95,6 +95,21 @@ type TextMessage struct {
Body string `json:"body"`
}
type ImageInfo struct {
Height uint `json:"h"`
Width uint `json:"w"`
Mimetype string `json:"mimetype"`
Size uint `json:"size"`
}
// ImageMessage is an m.image event
type ImageMessage struct {
MsgType string `json:"msgtype"`
Body string `json:"body"`
URL string `json:"url"`
Info ImageInfo `json:"info"`
}
// An HTMLMessage is the contents of a Matrix HTML formated message event.
type HTMLMessage struct {
Body string `json:"body"`

2
src/github.com/matrix-org/go-neb/services/echo/echo.go

@ -17,7 +17,7 @@ func (e *echoService) ServiceUserID() string
func (e *echoService) ServiceID() string { return e.id }
func (e *echoService) ServiceType() string { return "echo" }
func (e *echoService) Register(oldService types.Service, client *matrix.Client) error { return nil }
func (e *echoService) Plugin(roomID string) plugin.Plugin {
func (e *echoService) Plugin(cli *matrix.Client, roomID string) plugin.Plugin {
return plugin.Plugin{
Commands: []plugin.Command{
plugin.Command{

127
src/github.com/matrix-org/go-neb/services/giphy/giphy.go

@ -0,0 +1,127 @@
package services
import (
"encoding/json"
"errors"
log "github.com/Sirupsen/logrus"
"github.com/matrix-org/go-neb/matrix"
"github.com/matrix-org/go-neb/plugin"
"github.com/matrix-org/go-neb/types"
"net/http"
"net/url"
"strconv"
"strings"
)
type result struct {
Slug string `json:"slug"`
Images struct {
Original struct {
URL string `json:"url"`
// Giphy returns ints as strings..
Width string `json:"width"`
Height string `json:"height"`
Size string `json:"size"`
} `json:"original"`
} `json:"images"`
}
type giphySearch struct {
Data []result
}
type giphyService struct {
id string
serviceUserID string
APIKey string // beta key is dc6zaTOxFJmzC
}
func (s *giphyService) ServiceUserID() string { return s.serviceUserID }
func (s *giphyService) ServiceID() string { return s.id }
func (s *giphyService) ServiceType() string { return "giphy" }
func (s *giphyService) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *matrix.Client) {
}
func (s *giphyService) Register(oldService types.Service, client *matrix.Client) error { return nil }
func (s *giphyService) Plugin(client *matrix.Client, roomID string) plugin.Plugin {
return plugin.Plugin{
Commands: []plugin.Command{
plugin.Command{
Path: []string{"giphy"},
Command: func(roomID, userID string, args []string) (interface{}, error) {
return s.cmdGiphy(client, roomID, userID, args)
},
},
},
}
}
func (s *giphyService) cmdGiphy(client *matrix.Client, roomID, userID string, 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
}
log.Print("GOT ", gifResult)
mxc, err := client.UploadLink(gifResult.Images.Original.URL)
if err != nil {
return nil, err
}
log.Print("GOT MXC ", mxc)
return matrix.ImageMessage{
MsgType: "m.image",
Body: gifResult.Slug,
URL: mxc,
Info: matrix.ImageInfo{
Height: asInt(gifResult.Images.Original.Height),
Width: asInt(gifResult.Images.Original.Width),
Mimetype: "image/gif",
Size: asInt(gifResult.Images.Original.Size),
},
}, nil
}
// searchGiphy returns info about a gif
func (s *giphyService) searchGiphy(query string) (*result, error) {
u, err := url.Parse("http://api.giphy.com/v1/gifs/search")
if err != nil {
return nil, err
}
q := u.Query()
q.Set("q", 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 {
return nil, err
}
if len(search.Data) == 0 {
return nil, errors.New("No results")
}
return &search.Data[0], nil
}
func asInt(strInt string) uint {
u64, err := strconv.ParseUint(strInt, 10, 32)
if err != nil {
return 0 // default to 0 since these are all just hints to the client
}
return uint(u64)
}
func init() {
types.RegisterService(func(serviceID, serviceUserID, webhookEndpointURL string) types.Service {
return &giphyService{
id: serviceID,
serviceUserID: serviceUserID,
}
})
}

2
src/github.com/matrix-org/go-neb/services/github/github.go

@ -131,7 +131,7 @@ func (s *githubService) expandIssue(roomID, userID, owner, repo string, issueNum
}
}
func (s *githubService) Plugin(roomID string) plugin.Plugin {
func (s *githubService) Plugin(cli *matrix.Client, roomID string) plugin.Plugin {
return plugin.Plugin{
Commands: []plugin.Command{
plugin.Command{

2
src/github.com/matrix-org/go-neb/services/jira/jira.go

@ -198,7 +198,7 @@ func (s *jiraService) expandIssue(roomID, userID string, issueKeyGroups []string
)
}
func (s *jiraService) Plugin(roomID string) plugin.Plugin {
func (s *jiraService) Plugin(cli *matrix.Client, roomID string) plugin.Plugin {
return plugin.Plugin{
Commands: []plugin.Command{
plugin.Command{

2
src/github.com/matrix-org/go-neb/types/types.go

@ -44,7 +44,7 @@ type Service interface {
ServiceUserID() string
ServiceID() string
ServiceType() string
Plugin(roomID string) plugin.Plugin
Plugin(cli *matrix.Client, roomID string) plugin.Plugin
OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *matrix.Client)
Register(oldService Service, client *matrix.Client) error
}

Loading…
Cancel
Save