aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--appservice/server.go6
-rw-r--r--connector/connector.go19
-rw-r--r--connector/mediaobject.go50
-rw-r--r--main.go8
-rw-r--r--mxlib/api.go8
-rw-r--r--mxlib/client.go62
-rw-r--r--mxlib/mediaobject.go54
7 files changed, 196 insertions, 11 deletions
diff --git a/appservice/server.go b/appservice/server.go
index d3ecb43..48e26a6 100644
--- a/appservice/server.go
+++ b/appservice/server.go
@@ -45,6 +45,12 @@ func Start(r *mxlib.Registration, c *Config) (chan error, error) {
if err != nil {
return nil, err
}
+ err = mx.ProfileAvatar(ezbrMxId(), &connector.FileMediaObject{
+ Path: "easybridge.jpg",
+ })
+ if err != nil {
+ return nil, err
+ }
router := mux.NewRouter()
router.HandleFunc("/_matrix/app/v1/transactions/{txnId}", handleTxn)
diff --git a/connector/connector.go b/connector/connector.go
index 5df010a..d05f4a2 100644
--- a/connector/connector.go
+++ b/connector/connector.go
@@ -1,5 +1,9 @@
package connector
+import (
+ "io"
+)
+
/*
A generic connector framework for instant messaging protocols.
@@ -126,13 +130,14 @@ type RoomInfo struct {
}
type MediaObject interface {
- Size() int
- MimeType() string
+ Filename() string
+ Size() int64
+ Mimetype() string
- // AsBytes: must always be implemented
- AsBytes() ([]byte, error)
+ // Read: must always be implemented
+ Read() (io.ReadCloser, error)
- // AsString: not mandatory, may return an empty string
- // If so, AsBytes() is the only way to retrieve the object
- AsURL() string
+ // URL(): not mandatory, may return an empty string
+ // If so, Read() is the only way to retrieve the object
+ URL() string
}
diff --git a/connector/mediaobject.go b/connector/mediaobject.go
new file mode 100644
index 0000000..75635ee
--- /dev/null
+++ b/connector/mediaobject.go
@@ -0,0 +1,50 @@
+package connector
+
+import (
+ "io"
+ "net/http"
+ "os"
+ "path/filepath"
+
+ log "github.com/sirupsen/logrus"
+)
+
+type FileMediaObject struct {
+ Path string
+}
+
+func (m *FileMediaObject) Filename() string {
+ return filepath.Base(m.Path)
+}
+
+func (m *FileMediaObject) Size() int64 {
+ fi, err := os.Stat(m.Path)
+ if err != nil {
+ log.Fatal(err)
+ }
+ return fi.Size()
+}
+
+func (m *FileMediaObject) Mimetype() string {
+ f, err := os.Open(m.Path)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer f.Close()
+
+ buffer := make([]byte, 512)
+ _, err = f.Read(buffer)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ return http.DetectContentType(buffer)
+}
+
+func (m *FileMediaObject) Read() (io.ReadCloser, error) {
+ return os.Open(m.Path)
+}
+
+func (m *FileMediaObject) URL() string {
+ return ""
+}
diff --git a/main.go b/main.go
index 50f8bc7..60272a7 100644
--- a/main.go
+++ b/main.go
@@ -200,7 +200,7 @@ func main() {
}
conn.SetHandler(account)
appservice.AddAccount(account)
- go connectAndJoin(conn, params)
+ go connectAndJoin(account, params)
}
}
@@ -210,15 +210,15 @@ func main() {
}
}
-func connectAndJoin(conn connector.Connector, params ConfigAccount) {
+func connectAndJoin(account *appservice.Account, params ConfigAccount) {
log.Printf("Connecting to %s", params.Protocol)
- err := conn.Configure(params.Config)
+ err := account.Conn.Configure(params.Config)
if err != nil {
log.Printf("Could not connect to %s: %s", params.Protocol, err)
} else {
log.Printf("Connected to %s, now joining %#v", params.Protocol, params.Rooms)
for _, room := range params.Rooms {
- err := conn.Join(connector.RoomID(room))
+ err := account.Conn.Join(connector.RoomID(room))
if err != nil {
log.Printf("Could not join %s: %s", room, err)
}
diff --git a/mxlib/api.go b/mxlib/api.go
index 6dfe56e..7752abc 100644
--- a/mxlib/api.go
+++ b/mxlib/api.go
@@ -83,3 +83,11 @@ type RoomJoinResponse struct {
type RoomSendResponse struct {
EventId string `json:"event_id"`
}
+
+type UploadResponse struct {
+ ContentUri string `json:"content_uri"`
+}
+
+type ProfileAvatarUrl struct {
+ AvatarUrl string `json:"avatar_url"`
+}
diff --git a/mxlib/client.go b/mxlib/client.go
index 2385d6e..6d87663 100644
--- a/mxlib/client.go
+++ b/mxlib/client.go
@@ -1,6 +1,7 @@
package mxlib
import (
+ "strings"
"bytes"
"encoding/json"
"fmt"
@@ -9,6 +10,8 @@ import (
"time"
log "github.com/sirupsen/logrus"
+
+ "git.deuxfleurs.fr/Deuxfleurs/easybridge/connector"
)
type Client struct {
@@ -123,6 +126,29 @@ func (mx *Client) ProfileDisplayname(userid string, displayname string) error {
return err
}
+func (mx *Client) ProfileAvatar(userid string, m connector.MediaObject) error {
+ var mxc *MediaObject
+ if mxm, ok := m.(*MediaObject); ok {
+ mxc = mxm
+ } else {
+ mxm, err := mx.UploadMedia(m)
+ if err != nil {
+ return err
+ }
+ mxc = mxm
+ }
+ mxc_uri := fmt.Sprintf("mxc://%s/%s", mxc.MxcServer, mxc.MxcMediaId)
+
+ req := ProfileAvatarUrl{
+ AvatarUrl: mxc_uri,
+ }
+ var rep struct{}
+ err := mx.PutApiCall(fmt.Sprintf("/_matrix/client/r0/profile/%s/avatar_url?user_id=%s",
+ url.QueryEscape(userid), url.QueryEscape(userid)),
+ &req, &rep)
+ return err
+}
+
func (mx *Client) DirectoryRoom(alias string) (string, error) {
var rep DirectoryRoomResponse
err := mx.GetApiCall("/_matrix/client/r0/directory/room/"+url.QueryEscape(alias), &rep)
@@ -252,3 +278,39 @@ func (mx *Client) RoomTopicAs(room string, topic string, as_user string) error {
}
return mx.PutStateAs(room, "m.room.topic", "", content, as_user)
}
+
+func (mx *Client) UploadMedia(m connector.MediaObject) (*MediaObject, error) {
+ reader, err := m.Read()
+ if err != nil {
+ return nil, err
+ }
+ defer reader.Close()
+
+ req, err := http.NewRequest("POST",
+ mx.Server+"/_matrix/media/r0/upload?filename="+url.QueryEscape(m.Filename()),
+ reader)
+ req.Header.Add("Content-Type", m.Mimetype())
+ req.ContentLength = m.Size()
+
+ var resp UploadResponse
+ err = mx.DoAndParse(req, &resp)
+ if err != nil {
+ return nil, err
+ }
+
+ mxc := strings.Split(strings.Replace(resp.ContentUri, "mxc://", "", 1), "/")
+ if len(mxc) != 2 {
+ return nil, fmt.Errorf("Invalid mxc:// returned: %s", resp.ContentUri)
+ }
+
+ media := &MediaObject{
+ mxClient: mx,
+ filename: m.Filename(),
+ size: m.Size(),
+ mimetype: m.Mimetype(),
+ MxcServer: mxc[0],
+ MxcMediaId: mxc[1],
+ }
+ return media, nil
+}
+
diff --git a/mxlib/mediaobject.go b/mxlib/mediaobject.go
new file mode 100644
index 0000000..8a730d2
--- /dev/null
+++ b/mxlib/mediaobject.go
@@ -0,0 +1,54 @@
+package mxlib
+
+import (
+ "io"
+ "fmt"
+ "net/url"
+ "net/http"
+)
+
+type MediaObject struct {
+ mxClient *Client
+ filename string
+ size int64
+ mimetype string
+ MxcServer string
+ MxcMediaId string
+}
+
+func (m *MediaObject) Filename() string {
+ return m.filename
+}
+
+func (m *MediaObject) Size() int64 {
+ return m.size
+}
+
+func (m *MediaObject) Mimetype() string {
+ return m.mimetype
+}
+
+func (m *MediaObject) Read() (io.ReadCloser, error) {
+ req, err := http.NewRequest("GET", m.URL(), nil)
+ if err != nil {
+ return nil, err
+ }
+
+ req.Header.Add("Authorization", "Bearer "+m.mxClient.Token)
+
+ resp, err := m.mxClient.httpClient.Do(req)
+ if err != nil {
+ return nil, err
+ }
+
+ if resp.StatusCode != http.StatusOK {
+ return nil, fmt.Errorf("HTTP error %d", resp.StatusCode)
+ }
+
+ return resp.Body, nil
+}
+
+func (m *MediaObject) URL() string {
+ return fmt.Sprintf("%s/_matrix/media/r0/download/%s/%s/%s",
+ m.mxClient.Server, m.MxcServer, m.MxcMediaId, url.QueryEscape(m.filename))
+}