about summary refs log tree commit diff
diff options
context:
space:
mode:
authorhanemile <mail@emile.space>2020-07-10 16:37:52 +0200
committerhanemile <mail@emile.space>2020-07-10 16:37:52 +0200
commit6c242710d7b1e4ba7c7d9b76437529d1d00c7c67 (patch)
tree028706a516365e365dd9807ee6d0ab57433b4807
parent162f9164fdd95281e8e559b5963a02a7a16d25b7 (diff)
the functions handling almost everything I need
-rw-r--r--join.go32
-rw-r--r--login.go35
-rw-r--r--send.go113
-rw-r--r--structs.go72
-rw-r--r--sync.go96
5 files changed, 315 insertions, 33 deletions
diff --git a/join.go b/join.go
new file mode 100644
index 0000000..bc274c0
--- /dev/null
+++ b/join.go
@@ -0,0 +1,32 @@
+package matrix
+
+import (
+	"fmt"
+
+	"gopkg.in/h2non/gentleman.v2"
+	"gopkg.in/h2non/gentleman.v2/plugins/query"
+)
+
+func join(authinfo Authinfo, roomIdentifier string) error {
+	cli := gentleman.New()
+	cli.URL(authinfo.HomeServer)
+
+	req := cli.Request()
+	req.Path(fmt.Sprintf("/_matrix/client/r0/rooms/%s/join", roomIdentifier))
+	req.Method("POST")
+
+	req.Use(query.Set("access_token", authinfo.AccessToken))
+
+	res, err := req.Send()
+	if err != nil {
+		fmt.Println("ERR1")
+		return err
+	}
+	if !res.Ok {
+		fmt.Println("ERR2")
+		fmt.Println(res)
+		return err
+	}
+
+	return nil
+}
diff --git a/login.go b/login.go
index 3e35858..aa24db5 100644
--- a/login.go
+++ b/login.go
@@ -3,9 +3,8 @@ package matrix
 import (
 	"encoding/json"
 
-	"github.com/h2non/gentleman/plugins/body"
 	"gopkg.in/h2non/gentleman.v2"
-	"gopkg.in/h2non/gentleman.v2/plugins/query"
+	"gopkg.in/h2non/gentleman.v2/plugins/body"
 )
 
 // Login logs in to the homeserver and returns an Authinfo struct containing
@@ -38,38 +37,8 @@ func Login(username, password, homeserver string) (Authinfo, error) {
 	if err := json.Unmarshal(res.Bytes(), &authinfo); err != nil {
 		return Authinfo{}, err
 	}
-	return authinfo, nil
-}
 
-func Sync(authinfo Authinfo) {
-	cli := gentleman.New()
-	cli.URL(authinfo.HomeServer)
-
-	req := cli.Request()
-	req.Path("/_matrix/client/r0/sync")
-	req.Method("GET")
-
-    req.Use(query.Set("access_token", authinfo.AccessToken))
-
-	res, err := req.Send()
-	if err != nil {
-		return err
-	}
-	if !res.Ok {
-		return err
-	}
+	authinfo.HomeServer = homeserver
 
-	var authinfo Authinfo
-	if err := json.Unmarshal(res.Bytes(), &authinfo); err != nil {
-		return Authinfo{}, err
-	}
 	return authinfo, nil
 }
-
-// Authinfo defines the fields returned after logging in
-type Authinfo struct {
-	UserID      string `json:"user_id"`
-	HomeServer  string `json:"home_server"`
-	DeviceID    string `json:"device_id"`
-	AccessToken string `json:"access_token"`
-}
diff --git a/send.go b/send.go
new file mode 100644
index 0000000..3547eaa
--- /dev/null
+++ b/send.go
@@ -0,0 +1,113 @@
+package matrix
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"time"
+
+	"gopkg.in/h2non/gentleman.v2"
+	"gopkg.in/h2non/gentleman.v2/plugins/body"
+	"gopkg.in/h2non/gentleman.v2/plugins/headers"
+	"gopkg.in/h2non/gentleman.v2/plugins/query"
+)
+
+// Send allows sending messages
+func Send(authinfo Authinfo, roomID string, message string) error {
+	cli := gentleman.New()
+	cli.URL(authinfo.HomeServer)
+
+	eventType := "m.room.message"
+	txID := fmt.Sprintf("%d", time.Now().UnixNano())
+
+	req := cli.Request()
+	req.Path(fmt.Sprintf("/_matrix/client/r0/rooms/%s/send/%s/%s", roomID, eventType, txID))
+	req.Method("PUT")
+
+	formattedMessage := fmt.Sprintf("<pre><samp style=\"font-family: monospace\">%s\n</samp></pre>\n", message)
+
+	// Define the JSON payload via body plugin
+	data := map[string]string{
+		"msgtype":        "m.text",
+		"body":           "A", // notifications
+		"format":         "org.matrix.custom.html",
+		"formatted_body": formattedMessage,
+	}
+	req.Use(body.JSON(data))
+	req.Use(query.Set("access_token", authinfo.AccessToken))
+
+	res, err := req.Send()
+	if err != nil {
+		fmt.Println("ERR1")
+		return err
+	}
+	if !res.Ok {
+		fmt.Println("ERR2")
+		fmt.Println(res)
+		return err
+	}
+	fmt.Printf("Sent %s\n", message)
+	return nil
+}
+
+// Upload uploads stuff to the matrix homeserver returning the files MXC
+func Upload(authinfo Authinfo, filename string, file *bytes.Buffer) (UploadResponse, error) {
+	cli := gentleman.New()
+	cli.URL(authinfo.HomeServer)
+
+	req := cli.Request()
+	req.Path("/_matrix/media/r0/upload")
+	req.Method("POST")
+
+	req.Use(headers.Set("Content-Type", "image/png"))
+	req.Use(headers.Set("filename", filename))
+
+	req.Use(body.Reader(file))
+	req.Use(query.Set("access_token", authinfo.AccessToken))
+
+	res, err := req.Send()
+	if err != nil {
+		fmt.Println("ERR1")
+		return UploadResponse{}, err
+	}
+	if !res.Ok {
+		fmt.Println("ERR2")
+		fmt.Println(res)
+		return UploadResponse{}, err
+	}
+
+	var uploadResponse UploadResponse
+	if err := json.Unmarshal(res.Bytes(), &uploadResponse); err != nil {
+		return UploadResponse{}, err
+	}
+	return uploadResponse, nil
+}
+
+// SendImage sends the image with the given mxc ID to the room (currently
+// hardcoded some lines below)
+func SendImage(authinfo Authinfo, roomID string, image map[string]interface{}) error {
+	cli := gentleman.New()
+	cli.URL(authinfo.HomeServer)
+
+	eventType := "m.room.message"
+	txID := fmt.Sprintf("%d", time.Now().UnixNano())
+
+	req := cli.Request()
+	req.Path(fmt.Sprintf("/_matrix/client/r0/rooms/%s/send/%s/%s", roomID, eventType, txID))
+	req.Method("PUT")
+
+	req.Use(body.JSON(image))
+	req.Use(query.Set("access_token", authinfo.AccessToken))
+
+	res, err := req.Send()
+	if err != nil {
+		fmt.Println("ERR1")
+		return err
+	}
+	if !res.Ok {
+		fmt.Println("ERR2")
+		fmt.Println(res)
+		return err
+	}
+	return nil
+}
diff --git a/structs.go b/structs.go
new file mode 100644
index 0000000..d07b103
--- /dev/null
+++ b/structs.go
@@ -0,0 +1,72 @@
+package matrix
+
+// Authinfo defines the fields returned after logging in
+type Authinfo struct {
+	UserID      string `json:"user_id"`
+	HomeServer  string `json:"home_server"`
+	DeviceID    string `json:"device_id"`
+	AccessToken string `json:"access_token"`
+}
+
+// RespSync defines the response from the sync
+type RespSync struct {
+	NextBatch   string `json:"next_batch"`
+	AccountData struct {
+		Events []Event `json:"events"`
+	} `json:"account_data"`
+	Presence struct {
+		Events []Event `json:"events"`
+	} `json:"presence"`
+	Rooms struct {
+		Leave map[string]struct {
+			State struct {
+				Events []Event `json:"events"`
+			} `json:"state"`
+			Timeline struct {
+				Events    []Event `json:"events"`
+				Limited   bool    `json:"limited"`
+				PrevBatch string  `json:"prev_batch"`
+			} `json:"timeline"`
+		} `json:"leave"`
+		Join map[string]struct {
+			State struct {
+				Events []Event `json:"events"`
+			} `json:"state"`
+			Timeline struct {
+				Events    []Event `json:"events"`
+				Limited   bool    `json:"limited"`
+				PrevBatch string  `json:"prev_batch"`
+			} `json:"timeline"`
+		} `json:"join"`
+		Invite map[string]struct {
+			State struct {
+				Events []Event
+			} `json:"invite_state"`
+		} `json:"invite"`
+	} `json:"rooms"`
+}
+
+// Event defines an event
+type Event struct {
+	StateKey    *string                `json:"state_key,omitempty"`    // The state key for the event. Only present on State Events.
+	Sender      string                 `json:"sender"`                 // The user ID of the sender of the event
+	Type        string                 `json:"type"`                   // The event type
+	Timestamp   int64                  `json:"origin_server_ts"`       // The unix timestamp when this message was sent by the origin server
+	ID          string                 `json:"event_id"`               // The unique ID of this event
+	RoomID      string                 `json:"room_id"`                // The room the event was sent to. May be nil (e.g. for presence)
+	Redacts     string                 `json:"redacts,omitempty"`      // The event ID that was redacted if a m.room.redaction event
+	Unsigned    map[string]interface{} `json:"unsigned"`               // The unsigned portions of the event, such as age and prev_content
+	Content     map[string]interface{} `json:"content"`                // The JSON content of the event.
+	PrevContent map[string]interface{} `json:"prev_content,omitempty"` // The JSON prev_content of the event.
+}
+
+// PackagedEvent bundles an event with more information regarding it, such as the roomname in which the event was produced.
+type PackagedEvent struct {
+	RoomName string
+	Event    Event
+}
+
+// UploadResponse is the responce recieved from the upload
+type UploadResponse struct {
+	ContentURI string `json:"content_uri,omitempty"`
+}
diff --git a/sync.go b/sync.go
new file mode 100644
index 0000000..b1f5544
--- /dev/null
+++ b/sync.go
@@ -0,0 +1,96 @@
+package matrix
+
+import (
+	"encoding/json"
+	"fmt"
+
+	"gopkg.in/h2non/gentleman.v2"
+	"gopkg.in/h2non/gentleman.v2/plugins/query"
+)
+
+// Sync syncs
+func Sync(authinfo Authinfo) (RespSync, error) {
+	cli := gentleman.New()
+	cli.URL(authinfo.HomeServer)
+
+	req := cli.Request()
+	req.Path("/_matrix/client/r0/sync")
+	req.Method("GET")
+
+	req.Use(query.Set("access_token", authinfo.AccessToken))
+
+	res, err := req.Send()
+	if err != nil {
+		fmt.Println("ERR1")
+		return RespSync{}, err
+	}
+	if !res.Ok {
+		fmt.Println("ERR2")
+		fmt.Println(res)
+		return RespSync{}, err
+	}
+
+	var syncReponse RespSync
+	if err := json.Unmarshal(res.Bytes(), &syncReponse); err != nil {
+		return RespSync{}, err
+	}
+
+	return syncReponse, nil
+}
+
+// SyncPartial syncs the state using sync token obtained in a previous request
+// as a timestamp
+func SyncPartial(authinfo Authinfo, nextBatch string, eventsChannel chan PackagedEvent) (RespSync, error) {
+	cli := gentleman.New()
+	cli.URL(authinfo.HomeServer)
+
+	req := cli.Request()
+	req.Path("/_matrix/client/r0/sync")
+	req.Method("GET")
+
+	req.Use(query.Set("access_token", authinfo.AccessToken))
+	req.Use(query.Set("since", nextBatch))
+	req.Use(query.Set("timeout", "1"))
+
+	res, err := req.Send()
+	if err != nil {
+		fmt.Println("ERR1")
+		return RespSync{}, err
+	}
+	if !res.Ok {
+		fmt.Println("ERR2")
+		fmt.Println(res)
+		return RespSync{}, err
+	}
+
+	// unmarshal the response
+	var syncReponse RespSync
+	if err := json.Unmarshal(res.Bytes(), &syncReponse); err != nil {
+		return RespSync{}, err
+	}
+
+	// accept all room invites
+	for room := range syncReponse.Rooms.Invite {
+		err := join(authinfo, room)
+		if err != nil {
+			return RespSync{}, fmt.Errorf("could not join room: %s", err)
+		}
+	}
+
+	// iterate over all new events, insert all new events not from the bot itself into the eventsChannel
+	for roomname, room := range syncReponse.Rooms.Join {
+		for _, event := range room.Timeline.Events {
+
+			packagedEvent := PackagedEvent{
+				RoomName: roomname,
+				Event:    event,
+			}
+
+			// if the event recieved is not from ourself, insert the event into the eventsChannel
+			if event.Sender != authinfo.UserID {
+				eventsChannel <- packagedEvent
+			}
+		}
+	}
+	return syncReponse, nil
+}