From 4f8f762583cdc21e6092901e7075f1c3b1dec36f Mon Sep 17 00:00:00 2001 From: Travis Ralston <travpc@gmail.com> Date: Fri, 6 Jul 2018 21:26:17 -0600 Subject: [PATCH 01/38] Expose `unsigned` on events Signed-off-by: Travis Ralston <travpc@gmail.com> --- events.go | 1 + 1 file changed, 1 insertion(+) diff --git a/events.go b/events.go index 61f7537..1a82803 100644 --- a/events.go +++ b/events.go @@ -15,6 +15,7 @@ type Event struct { RoomID string `json:"room_id"` // The room the event was sent to. May be nil (e.g. for presence) Content map[string]interface{} `json:"content"` // The JSON content of the event. 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 } // Body returns the value of the "body" key in the event content if it is From b8d1540599f0042cbcddc9822eaa52ead926651c Mon Sep 17 00:00:00 2001 From: DzananGanic <dzanan.ganic@gmail.com> Date: Thu, 13 Dec 2018 13:23:14 +0100 Subject: [PATCH 02/38] fixed wrong json tag for RespUserInteractive Session param --- responses.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/responses.go b/responses.go index fe0eeb3..4055c62 100644 --- a/responses.go +++ b/responses.go @@ -84,7 +84,7 @@ type RespUserInteractive struct { Stages []string `json:"stages"` } `json:"flows"` Params map[string]interface{} `json:"params"` - Session string `json:"string"` + Session string `json:"session"` Completed []string `json:"completed"` ErrCode string `json:"errcode"` Error string `json:"error"` From 998378da2b0d0fce7ccd984a8873159f7c2ba143 Mon Sep 17 00:00:00 2001 From: Brendan Abolivier <babolivier@matrix.org> Date: Wed, 20 Mar 2019 19:31:55 +0000 Subject: [PATCH 03/38] Manually append a trailing slash at the end of a path if needed --- client.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client.go b/client.go index 7725ac3..e328c4a 100644 --- a/client.go +++ b/client.go @@ -13,6 +13,7 @@ import ( "net/url" "path" "strconv" + "strings" "sync" "time" ) @@ -68,6 +69,10 @@ func (cli *Client) BuildBaseURL(urlPath ...string) string { parts := []string{hsURL.Path} parts = append(parts, urlPath...) hsURL.Path = path.Join(parts...) + // Manually add the trailing slash back to the end of the path if it's explicitely needed + if strings.HasSuffix(urlPath[len(urlPath)-1], "/") { + hsURL.Path = hsURL.Path + "/" + } query := hsURL.Query() if cli.AccessToken != "" { query.Set("access_token", cli.AccessToken) From 1ec97e4eaec732530a4ec47a6b468e1aa38ce2ad Mon Sep 17 00:00:00 2001 From: SUMUKHA-PK <sumukhapk46@gmail.com> Date: Tue, 26 Mar 2019 21:08:08 +0530 Subject: [PATCH 04/38] Added tags.go --- tags.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tags.go diff --git a/tags.go b/tags.go new file mode 100644 index 0000000..54af4ef --- /dev/null +++ b/tags.go @@ -0,0 +1,12 @@ +package gomatrix + +// Tags is based on the Room Tagging feature as described in https://matrix.org/docs/spec/client_server/r0.2.0.html#room-tagging +// Tag contains the data for a Tag which can be referenced by the Tag name +type Tag struct { + Tags map[string]TagProperties `json:"tags"` +} + +// TagProperties contains the properties of an MTag +type TagProperties struct { + Order float32 `json:"order,omitempty"` // Empty values must be neglected +} From 8083606ef53dea1a2052538d0402dd59daaeca65 Mon Sep 17 00:00:00 2001 From: SUMUKHA-PK <sumukhapk46@gmail.com> Date: Tue, 26 Mar 2019 21:13:55 +0530 Subject: [PATCH 05/38] Build issues resolved --- tags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tags.go b/tags.go index 54af4ef..d7900ee 100644 --- a/tags.go +++ b/tags.go @@ -1,7 +1,7 @@ package gomatrix -// Tags is based on the Room Tagging feature as described in https://matrix.org/docs/spec/client_server/r0.2.0.html#room-tagging // Tag contains the data for a Tag which can be referenced by the Tag name +// Tags is based on the Room Tagging feature as described in https://matrix.org/docs/spec/client_server/r0.2.0.html#room-tagging type Tag struct { Tags map[string]TagProperties `json:"tags"` } From f96350d0a81d69d635cad170071300c628d7e2fd Mon Sep 17 00:00:00 2001 From: SUMUKHA-PK <sumukhapk46@gmail.com> Date: Tue, 26 Mar 2019 21:15:48 +0530 Subject: [PATCH 06/38] Build issues resolved --- tags.go | 1 - 1 file changed, 1 deletion(-) diff --git a/tags.go b/tags.go index d7900ee..7a3c29e 100644 --- a/tags.go +++ b/tags.go @@ -1,7 +1,6 @@ package gomatrix // Tag contains the data for a Tag which can be referenced by the Tag name -// Tags is based on the Room Tagging feature as described in https://matrix.org/docs/spec/client_server/r0.2.0.html#room-tagging type Tag struct { Tags map[string]TagProperties `json:"tags"` } From 8d38784ec2d933956021a3e989d283d758ccd9db Mon Sep 17 00:00:00 2001 From: SUMUKHA-PK <sumukhapk46@gmail.com> Date: Tue, 26 Mar 2019 21:24:26 +0530 Subject: [PATCH 07/38] Build issues resolved --- client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client.go b/client.go index e328c4a..728f23f 100644 --- a/client.go +++ b/client.go @@ -69,7 +69,7 @@ func (cli *Client) BuildBaseURL(urlPath ...string) string { parts := []string{hsURL.Path} parts = append(parts, urlPath...) hsURL.Path = path.Join(parts...) - // Manually add the trailing slash back to the end of the path if it's explicitely needed + // Manually add the trailing slash back to the end of the path if it's explicitly needed if strings.HasSuffix(urlPath[len(urlPath)-1], "/") { hsURL.Path = hsURL.Path + "/" } From da9261fac60a965e2ee56eb536c2c5598aebacf6 Mon Sep 17 00:00:00 2001 From: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> Date: Tue, 26 Mar 2019 22:01:15 +0530 Subject: [PATCH 08/38] Update tags.go Co-Authored-By: SUMUKHA-PK <sumukhapk46@gmail.com> --- tags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tags.go b/tags.go index 7a3c29e..a5e4a95 100644 --- a/tags.go +++ b/tags.go @@ -1,6 +1,6 @@ package gomatrix -// Tag contains the data for a Tag which can be referenced by the Tag name +// Tag contains the data for an m.tag message type type Tag struct { Tags map[string]TagProperties `json:"tags"` } From 566b83c1b08b3a36b45d9eb03154b09116708dfa Mon Sep 17 00:00:00 2001 From: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> Date: Tue, 26 Mar 2019 22:01:24 +0530 Subject: [PATCH 09/38] Update tags.go Co-Authored-By: SUMUKHA-PK <sumukhapk46@gmail.com> --- tags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tags.go b/tags.go index a5e4a95..c00685b 100644 --- a/tags.go +++ b/tags.go @@ -5,7 +5,7 @@ type Tag struct { Tags map[string]TagProperties `json:"tags"` } -// TagProperties contains the properties of an MTag +// TagProperties contains the properties of a Tag type TagProperties struct { Order float32 `json:"order,omitempty"` // Empty values must be neglected } From 58c78fd8f18fc968d29a69cc8116b3572b244462 Mon Sep 17 00:00:00 2001 From: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> Date: Tue, 26 Mar 2019 22:01:34 +0530 Subject: [PATCH 10/38] Update tags.go Co-Authored-By: SUMUKHA-PK <sumukhapk46@gmail.com> --- tags.go | 1 + 1 file changed, 1 insertion(+) diff --git a/tags.go b/tags.go index c00685b..57edabd 100644 --- a/tags.go +++ b/tags.go @@ -1,6 +1,7 @@ package gomatrix // Tag contains the data for an m.tag message type +// https://matrix.org/docs/spec/client_server/r0.4.0.html#m-tag type Tag struct { Tags map[string]TagProperties `json:"tags"` } From 22498f6a6038accf3800d0baa1969b0f72a21203 Mon Sep 17 00:00:00 2001 From: SUMUKHA-PK <sumukhapk46@gmail.com> Date: Tue, 26 Mar 2019 22:07:07 +0530 Subject: [PATCH 11/38] Added copyright --- tags.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tags.go b/tags.go index 7a3c29e..7b4d5a4 100644 --- a/tags.go +++ b/tags.go @@ -1,3 +1,17 @@ +// Copyright 2019 Sumukha PK +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package gomatrix // Tag contains the data for a Tag which can be referenced by the Tag name From 8aae639f6beb97c665aa1dddd0b612e59b07fec1 Mon Sep 17 00:00:00 2001 From: SUMUKHA-PK <sumukhapk46@gmail.com> Date: Tue, 26 Mar 2019 22:25:40 +0530 Subject: [PATCH 12/38] Tag -> TagContent --- tags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tags.go b/tags.go index 8207e99..7c159ab 100644 --- a/tags.go +++ b/tags.go @@ -16,7 +16,7 @@ package gomatrix // Tag contains the data for an m.tag message type // https://matrix.org/docs/spec/client_server/r0.4.0.html#m-tag -type Tag struct { +type TagContent struct { Tags map[string]TagProperties `json:"tags"` } From c58e5d92a5e2f811ae17e29f6b06b7b161d5ef84 Mon Sep 17 00:00:00 2001 From: SUMUKHA-PK <sumukhapk46@gmail.com> Date: Tue, 26 Mar 2019 22:25:57 +0530 Subject: [PATCH 13/38] Tag->TagContent --- tags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tags.go b/tags.go index 7c159ab..956fb11 100644 --- a/tags.go +++ b/tags.go @@ -14,7 +14,7 @@ package gomatrix -// Tag contains the data for an m.tag message type +// TagContent contains the data for an m.tag message type // https://matrix.org/docs/spec/client_server/r0.4.0.html#m-tag type TagContent struct { Tags map[string]TagProperties `json:"tags"` From 5df0d882cdb5c41f1a0f863595f4dc5bd3e6a960 Mon Sep 17 00:00:00 2001 From: Bernhard Tittelbach <bernhard@tittelbach.org> Date: Sun, 7 Apr 2019 03:59:56 +0200 Subject: [PATCH 14/38] ImageInfo can have a Thumbnail (and it's needed for Riot on Android) --- events.go | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/events.go b/events.go index 1a82803..edf231a 100644 --- a/events.go +++ b/events.go @@ -46,23 +46,33 @@ type TextMessage struct { Body string `json:"body"` } -// ImageInfo contains info about an image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image -type ImageInfo struct { +// ThumbnailInfo contains info about an thumbnail image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image +type ThumbnailInfo struct { Height uint `json:"h,omitempty"` Width uint `json:"w,omitempty"` Mimetype string `json:"mimetype,omitempty"` Size uint `json:"size,omitempty"` } +// ImageInfo contains info about an image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image +type ImageInfo struct { + Height uint `json:"h,omitempty"` + Width uint `json:"w,omitempty"` + Mimetype string `json:"mimetype,omitempty"` + Size uint `json:"size,omitempty"` + ThumbnailInfo ThumbnailInfo `json:"thumbnail_info,omitempty"` + ThumbnailURL string `json:"thumbnail_url,omitempty"` +} + // VideoInfo contains info about a video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video type VideoInfo struct { - Mimetype string `json:"mimetype,omitempty"` - ThumbnailInfo ImageInfo `json:"thumbnail_info"` - ThumbnailURL string `json:"thumbnail_url,omitempty"` - Height uint `json:"h,omitempty"` - Width uint `json:"w,omitempty"` - Duration uint `json:"duration,omitempty"` - Size uint `json:"size,omitempty"` + Mimetype string `json:"mimetype,omitempty"` + ThumbnailInfo ThumbnailInfo `json:"thumbnail_info"` + ThumbnailURL string `json:"thumbnail_url,omitempty"` + Height uint `json:"h,omitempty"` + Width uint `json:"w,omitempty"` + Duration uint `json:"duration,omitempty"` + Size uint `json:"size,omitempty"` } // VideoMessage is an m.video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video From 44bc4ee6221fe4f7a09cbc3ea830d9c427ff8d7e Mon Sep 17 00:00:00 2001 From: Reverite <github@reverite.sh> Date: Thu, 25 Apr 2019 00:24:30 -0700 Subject: [PATCH 15/38] streaming JSON encoding and decoding from HTTP client instead of allocating bytes --- client.go | 122 +++++++++++++++++++++++++++--------------------------- 1 file changed, 60 insertions(+), 62 deletions(-) diff --git a/client.go b/client.go index 728f23f..9eee501 100644 --- a/client.go +++ b/client.go @@ -39,6 +39,7 @@ type Client struct { // HTTPError An HTTP Error response, which may wrap an underlying native Go Error. type HTTPError struct { + Contents []byte WrappedError error Message string Code int @@ -49,7 +50,7 @@ func (e HTTPError) Error() string { if e.WrappedError != nil { wrappedErrMsg = e.WrappedError.Error() } - return fmt.Sprintf("msg=%s code=%d wrapped=%s", e.Message, e.Code, wrappedErrMsg) + return fmt.Sprintf("contents=%v msg=%s code=%d wrapped=%s", e.Contents, e.Message, e.Code, wrappedErrMsg) } // BuildURL builds a URL with the Client's homserver/prefix/access_token set already. @@ -183,27 +184,27 @@ func (cli *Client) StopSync() { } // MakeRequest makes a JSON HTTP request to the given URL. -// If "resBody" is not nil, the response body will be json.Unmarshalled into it. +// The response body will be stream decoded into an interface. This will automatically stop if the response +// body is nil. // -// Returns the HTTP body as bytes on 2xx with a nil error. Returns an error if the response is not 2xx along -// with the HTTP body bytes if it got that far. This error is an HTTPError which includes the returned -// HTTP status code and possibly a RespError as the WrappedError, if the HTTP body could be decoded as a RespError. -func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{}, resBody interface{}) ([]byte, error) { +// Returns an error if the response is not 2xx along with the HTTP body bytes if it got that far. This error is +// an HTTPError which includes the returned HTTP status code, byte contents of the response body and possibly a +// RespError as the WrappedError, if the HTTP body could be decoded as a RespError. +func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{}, resBody interface{}) error { var req *http.Request var err error if reqBody != nil { - var jsonStr []byte - jsonStr, err = json.Marshal(reqBody) - if err != nil { - return nil, err + buf := new(bytes.Buffer) + if err := json.NewEncoder(buf).Encode(reqBody); err != nil { + return err } - req, err = http.NewRequest(method, httpURL, bytes.NewBuffer(jsonStr)) + req, err = http.NewRequest(method, httpURL, buf) } else { req, err = http.NewRequest(method, httpURL, nil) } if err != nil { - return nil, err + return err } req.Header.Set("Content-Type", "application/json") res, err := cli.Client.Do(req) @@ -211,10 +212,14 @@ func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{ defer res.Body.Close() } if err != nil { - return nil, err + return err } - contents, err := ioutil.ReadAll(res.Body) if res.StatusCode/100 != 2 { // not 2xx + contents, err := ioutil.ReadAll(res.Body) + if err != nil { + return err + } + var wrap error var respErr RespError if _ = json.Unmarshal(contents, &respErr); respErr.ErrCode != "" { @@ -228,29 +233,25 @@ func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{ msg = msg + ": " + string(contents) } - return contents, HTTPError{ + return HTTPError{ + Contents: contents, Code: res.StatusCode, Message: msg, WrappedError: wrap, } } - if err != nil { - return nil, err + + if res.ContentLength > 0 { + return json.NewDecoder(res.Body).Decode(&resBody) } - if resBody != nil { - if err = json.Unmarshal(contents, &resBody); err != nil { - return nil, err - } - } - - return contents, nil + return nil } // CreateFilter makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-user-userid-filter func (cli *Client) CreateFilter(filter json.RawMessage) (resp *RespCreateFilter, err error) { urlPath := cli.BuildURL("user", cli.UserID, "filter") - _, err = cli.MakeRequest("POST", urlPath, &filter, &resp) + err = cli.MakeRequest("POST", urlPath, &filter, &resp) return } @@ -272,13 +273,12 @@ func (cli *Client) SyncRequest(timeout int, since, filterID string, fullState bo query["full_state"] = "true" } urlPath := cli.BuildURLWithQuery([]string{"sync"}, query) - _, err = cli.MakeRequest("GET", urlPath, nil, &resp) + err = cli.MakeRequest("GET", urlPath, nil, &resp) return } func (cli *Client) register(u string, req *ReqRegister) (resp *RespRegister, uiaResp *RespUserInteractive, err error) { - var bodyBytes []byte - bodyBytes, err = cli.MakeRequest("POST", u, req, nil) + err = cli.MakeRequest("POST", u, req, &resp) if err != nil { httpErr, ok := err.(HTTPError) if !ok { // network error @@ -286,13 +286,10 @@ func (cli *Client) register(u string, req *ReqRegister) (resp *RespRegister, uia } if httpErr.Code == 401 { // body should be RespUserInteractive, if it isn't, fail with the error - err = json.Unmarshal(bodyBytes, &uiaResp) + err = json.Unmarshal(httpErr.Contents, &uiaResp) return } - return } - // body should be RespRegister - err = json.Unmarshal(bodyBytes, &resp) return } @@ -356,7 +353,7 @@ func (cli *Client) RegisterDummy(req *ReqRegister) (*RespRegister, error) { // This does not set credentials on this client instance. See SetCredentials() instead. func (cli *Client) Login(req *ReqLogin) (resp *RespLogin, err error) { urlPath := cli.BuildURL("login") - _, err = cli.MakeRequest("POST", urlPath, req, &resp) + err = cli.MakeRequest("POST", urlPath, req, &resp) return } @@ -364,14 +361,14 @@ func (cli *Client) Login(req *ReqLogin) (resp *RespLogin, err error) { // This does not clear the credentials from the client instance. See ClearCredentials() instead. func (cli *Client) Logout() (resp *RespLogout, err error) { urlPath := cli.BuildURL("logout") - _, err = cli.MakeRequest("POST", urlPath, nil, &resp) + err = cli.MakeRequest("POST", urlPath, nil, &resp) return } // Versions returns the list of supported Matrix versions on this homeserver. See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-versions func (cli *Client) Versions() (resp *RespVersions, err error) { urlPath := cli.BuildBaseURL("_matrix", "client", "versions") - _, err = cli.MakeRequest("GET", urlPath, nil, &resp) + err = cli.MakeRequest("GET", urlPath, nil, &resp) return } @@ -388,21 +385,21 @@ func (cli *Client) JoinRoom(roomIDorAlias, serverName string, content interface{ } else { urlPath = cli.BuildURL("join", roomIDorAlias) } - _, err = cli.MakeRequest("POST", urlPath, content, &resp) + err = cli.MakeRequest("POST", urlPath, content, &resp) return } // GetDisplayName returns the display name of the user from the specified MXID. See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname func (cli *Client) GetDisplayName(mxid string) (resp *RespUserDisplayName, err error) { urlPath := cli.BuildURL("profile", mxid, "displayname") - _, err = cli.MakeRequest("GET", urlPath, nil, &resp) + err = cli.MakeRequest("GET", urlPath, nil, &resp) return } // GetOwnDisplayName returns the user's display name. See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname func (cli *Client) GetOwnDisplayName() (resp *RespUserDisplayName, err error) { urlPath := cli.BuildURL("profile", cli.UserID, "displayname") - _, err = cli.MakeRequest("GET", urlPath, nil, &resp) + err = cli.MakeRequest("GET", urlPath, nil, &resp) return } @@ -412,18 +409,18 @@ func (cli *Client) SetDisplayName(displayName string) (err error) { s := struct { DisplayName string `json:"displayname"` }{displayName} - _, err = cli.MakeRequest("PUT", urlPath, &s, nil) + err = cli.MakeRequest("PUT", urlPath, &s, nil) return } // GetAvatarURL gets the user's avatar URL. See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-avatar-url -func (cli *Client) GetAvatarURL() (url string, err error) { +func (cli *Client) GetAvatarURL() (string, error) { urlPath := cli.BuildURL("profile", cli.UserID, "avatar_url") s := struct { AvatarURL string `json:"avatar_url"` }{} - _, err = cli.MakeRequest("GET", urlPath, nil, &s) + err := cli.MakeRequest("GET", urlPath, nil, &s) if err != nil { return "", err } @@ -432,12 +429,12 @@ func (cli *Client) GetAvatarURL() (url string, err error) { } // SetAvatarURL sets the user's avatar URL. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-profile-userid-avatar-url -func (cli *Client) SetAvatarURL(url string) (err error) { +func (cli *Client) SetAvatarURL(url string) error { urlPath := cli.BuildURL("profile", cli.UserID, "avatar_url") s := struct { AvatarURL string `json:"avatar_url"` }{url} - _, err = cli.MakeRequest("PUT", urlPath, &s, nil) + err := cli.MakeRequest("PUT", urlPath, &s, nil) if err != nil { return err } @@ -450,7 +447,7 @@ func (cli *Client) SetAvatarURL(url string) (err error) { func (cli *Client) SendMessageEvent(roomID string, eventType string, contentJSON interface{}) (resp *RespSendEvent, err error) { txnID := txnID() urlPath := cli.BuildURL("rooms", roomID, "send", eventType, txnID) - _, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp) + err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp) return } @@ -458,7 +455,7 @@ func (cli *Client) SendMessageEvent(roomID string, eventType string, contentJSON // contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal. func (cli *Client) SendStateEvent(roomID, eventType, stateKey string, contentJSON interface{}) (resp *RespSendEvent, err error) { urlPath := cli.BuildURL("rooms", roomID, "state", eventType, stateKey) - _, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp) + err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp) return } @@ -502,7 +499,7 @@ func (cli *Client) SendNotice(roomID, text string) (*RespSendEvent, error) { func (cli *Client) RedactEvent(roomID, eventID string, req *ReqRedact) (resp *RespSendEvent, err error) { txnID := txnID() urlPath := cli.BuildURL("rooms", roomID, "redact", eventID, txnID) - _, err = cli.MakeRequest("PUT", urlPath, req, &resp) + err = cli.MakeRequest("PUT", urlPath, req, &resp) return } @@ -513,56 +510,56 @@ func (cli *Client) RedactEvent(roomID, eventID string, req *ReqRedact) (resp *Re // fmt.Println("Room:", resp.RoomID) func (cli *Client) CreateRoom(req *ReqCreateRoom) (resp *RespCreateRoom, err error) { urlPath := cli.BuildURL("createRoom") - _, err = cli.MakeRequest("POST", urlPath, req, &resp) + err = cli.MakeRequest("POST", urlPath, req, &resp) return } // LeaveRoom leaves the given room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-leave func (cli *Client) LeaveRoom(roomID string) (resp *RespLeaveRoom, err error) { u := cli.BuildURL("rooms", roomID, "leave") - _, err = cli.MakeRequest("POST", u, struct{}{}, &resp) + err = cli.MakeRequest("POST", u, struct{}{}, &resp) return } // ForgetRoom forgets a room entirely. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-forget func (cli *Client) ForgetRoom(roomID string) (resp *RespForgetRoom, err error) { u := cli.BuildURL("rooms", roomID, "forget") - _, err = cli.MakeRequest("POST", u, struct{}{}, &resp) + err = cli.MakeRequest("POST", u, struct{}{}, &resp) return } // InviteUser invites a user to a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-invite func (cli *Client) InviteUser(roomID string, req *ReqInviteUser) (resp *RespInviteUser, err error) { u := cli.BuildURL("rooms", roomID, "invite") - _, err = cli.MakeRequest("POST", u, req, &resp) + err = cli.MakeRequest("POST", u, req, &resp) return } // InviteUserByThirdParty invites a third-party identifier to a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#invite-by-third-party-id-endpoint func (cli *Client) InviteUserByThirdParty(roomID string, req *ReqInvite3PID) (resp *RespInviteUser, err error) { u := cli.BuildURL("rooms", roomID, "invite") - _, err = cli.MakeRequest("POST", u, req, &resp) + err = cli.MakeRequest("POST", u, req, &resp) return } // KickUser kicks a user from a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-kick func (cli *Client) KickUser(roomID string, req *ReqKickUser) (resp *RespKickUser, err error) { u := cli.BuildURL("rooms", roomID, "kick") - _, err = cli.MakeRequest("POST", u, req, &resp) + err = cli.MakeRequest("POST", u, req, &resp) return } // BanUser bans a user from a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-ban func (cli *Client) BanUser(roomID string, req *ReqBanUser) (resp *RespBanUser, err error) { u := cli.BuildURL("rooms", roomID, "ban") - _, err = cli.MakeRequest("POST", u, req, &resp) + err = cli.MakeRequest("POST", u, req, &resp) return } // UnbanUser unbans a user from a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban func (cli *Client) UnbanUser(roomID string, req *ReqUnbanUser) (resp *RespUnbanUser, err error) { u := cli.BuildURL("rooms", roomID, "unban") - _, err = cli.MakeRequest("POST", u, req, &resp) + err = cli.MakeRequest("POST", u, req, &resp) return } @@ -570,7 +567,7 @@ func (cli *Client) UnbanUser(roomID string, req *ReqUnbanUser) (resp *RespUnbanU func (cli *Client) UserTyping(roomID string, typing bool, timeout int64) (resp *RespTyping, err error) { req := ReqTyping{Typing: typing, Timeout: timeout} u := cli.BuildURL("rooms", roomID, "typing", cli.UserID) - _, err = cli.MakeRequest("PUT", u, req, &resp) + err = cli.MakeRequest("PUT", u, req, &resp) return } @@ -579,7 +576,7 @@ func (cli *Client) UserTyping(roomID string, typing bool, timeout int64) (resp * // See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-state-eventtype-statekey func (cli *Client) StateEvent(roomID, eventType, stateKey string, outContent interface{}) (err error) { u := cli.BuildURL("rooms", roomID, "state", eventType, stateKey) - _, err = cli.MakeRequest("GET", u, nil, outContent) + err = cli.MakeRequest("GET", u, nil, outContent) return } @@ -620,8 +617,9 @@ func (cli *Client) UploadToContentRepo(content io.Reader, contentType string, co } } return nil, HTTPError{ - Message: "Upload request failed: " + string(contents), - Code: res.StatusCode, + Contents: contents, + Message: "Upload request failed: " + string(contents), + Code: res.StatusCode, } } var m RespMediaUpload @@ -637,7 +635,7 @@ func (cli *Client) UploadToContentRepo(content io.Reader, contentType string, co // This API is primarily designed for application services which may want to efficiently look up joined members in a room. func (cli *Client) JoinedMembers(roomID string) (resp *RespJoinedMembers, err error) { u := cli.BuildURL("rooms", roomID, "joined_members") - _, err = cli.MakeRequest("GET", u, nil, &resp) + err = cli.MakeRequest("GET", u, nil, &resp) return } @@ -647,7 +645,7 @@ func (cli *Client) JoinedMembers(roomID string) (resp *RespJoinedMembers, err er // This API is primarily designed for application services which may want to efficiently look up joined rooms. func (cli *Client) JoinedRooms() (resp *RespJoinedRooms, err error) { u := cli.BuildURL("joined_rooms") - _, err = cli.MakeRequest("GET", u, nil, &resp) + err = cli.MakeRequest("GET", u, nil, &resp) return } @@ -667,7 +665,7 @@ func (cli *Client) Messages(roomID, from, to string, dir rune, limit int) (resp } urlPath := cli.BuildURLWithQuery([]string{"rooms", roomID, "messages"}, query) - _, err = cli.MakeRequest("GET", urlPath, nil, &resp) + err = cli.MakeRequest("GET", urlPath, nil, &resp) return } @@ -675,7 +673,7 @@ func (cli *Client) Messages(roomID, from, to string, dir rune, limit int) (resp // See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-voip-turnserver func (cli *Client) TurnServer() (resp *RespTurnServer, err error) { urlPath := cli.BuildURL("voip", "turnServer") - _, err = cli.MakeRequest("GET", urlPath, nil, &resp) + err = cli.MakeRequest("GET", urlPath, nil, &resp) return } From 4fb520ebacc73f8a0059d0cf412b8eb1ead9ebeb Mon Sep 17 00:00:00 2001 From: Reverite <github@reverite.sh> Date: Thu, 25 Apr 2019 15:16:37 -0700 Subject: [PATCH 16/38] Run gofmt --- client.go | 2 +- userids.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client.go b/client.go index 9eee501..72e2cac 100644 --- a/client.go +++ b/client.go @@ -188,7 +188,7 @@ func (cli *Client) StopSync() { // body is nil. // // Returns an error if the response is not 2xx along with the HTTP body bytes if it got that far. This error is -// an HTTPError which includes the returned HTTP status code, byte contents of the response body and possibly a +// an HTTPError which includes the returned HTTP status code, byte contents of the response body and possibly a // RespError as the WrappedError, if the HTTP body could be decoded as a RespError. func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{}, resBody interface{}) error { var req *http.Request diff --git a/userids.go b/userids.go index 23e7807..70002c5 100644 --- a/userids.go +++ b/userids.go @@ -125,6 +125,6 @@ func ExtractUserLocalpart(userID string) (string, error) { } return strings.TrimPrefix( strings.SplitN(userID, ":", 2)[0], // @foo:bar:8448 => [ "@foo", "bar:8448" ] - "@", // remove "@" prefix + "@", // remove "@" prefix ), nil } From f9a6d11d3806c97677c355c58602a372ae3f9df4 Mon Sep 17 00:00:00 2001 From: Reverite <github@reverite.sh> Date: Mon, 29 Apr 2019 13:18:17 -0700 Subject: [PATCH 17/38] Use res.Body check instead of res.ContentLength --- client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client.go b/client.go index 72e2cac..3b278d5 100644 --- a/client.go +++ b/client.go @@ -241,7 +241,7 @@ func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{ } } - if res.ContentLength > 0 { + if res.Body != nil { return json.NewDecoder(res.Body).Decode(&resBody) } From 158bd27e250c83c6103c02697495605ada199c5f Mon Sep 17 00:00:00 2001 From: Reverite <github@reverite.sh> Date: Wed, 8 May 2019 13:29:31 -0700 Subject: [PATCH 18/38] Check for resBody != nil on JSON decode --- client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client.go b/client.go index 3b278d5..fc290ed 100644 --- a/client.go +++ b/client.go @@ -241,7 +241,7 @@ func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{ } } - if res.Body != nil { + if resBody != nil && res.Body != nil { return json.NewDecoder(res.Body).Decode(&resBody) } From 9c5753065f6eadacd11fefc577d91c06718ab337 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 21 Dec 2019 22:26:19 +0000 Subject: [PATCH 19/38] Add methods to query /publicRooms API --- client.go | 46 ++++++++++++++++++++++++++++++++++++++++++++++ responses.go | 20 ++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/client.go b/client.go index fc290ed..acd33df 100644 --- a/client.go +++ b/client.go @@ -372,6 +372,52 @@ func (cli *Client) Versions() (resp *RespVersions, err error) { return } +// PublicRooms returns the list of public rooms on target server. Does not require Auth. See http://matrix.org/speculator/spec/HEAD/client_server/unstable.html#get-matrix-client-unstable-publicrooms +func (cli *Client) PublicRooms(limit int, since string, server string) (resp *RespPublicRooms, err error) { + args := map[string]string{} + + if limit != 0 { + args["limit"] = strconv.Itoa(limit) + } + if since != "" { + args["since"] = since + } + if server != "" { + args["server"] = server + } + + urlPath := cli.BuildURLWithQuery([]string{"publicRooms"}, args) + err = cli.MakeRequest("GET", urlPath, nil, &resp) + return +} + +// PublicRoomsFiltered returns a subset of PublicRooms filtered server side. See http://matrix.org/speculator/spec/HEAD/client_server/unstable.html#post-matrix-client-unstable-publicrooms +func (cli *Client) PublicRoomsFiltered(limit int, since string, server string, filter string) (resp *RespPublicRooms, err error) { + content := map[string]string{} + + if limit != 0 { + content["limit"] = strconv.Itoa(limit) + } + if since != "" { + content["since"] = since + } + if filter != "" { + content["filter"] = filter + } + + var urlPath string + if server == "" { + urlPath = cli.BuildURL("publicRooms") + } else { + urlPath = cli.BuildURLWithQuery([]string{"publicRooms"}, map[string]string{ + "server": server, + }) + } + + err = cli.MakeRequest("POST", urlPath, content, &resp) + return +} + // JoinRoom joins the client to a room ID or alias. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-join-roomidoralias // // If serverName is specified, this will be added as a query param to instruct the homeserver to join via that server. If content is specified, it will diff --git a/responses.go b/responses.go index 7a3a4ce..1c1fd92 100644 --- a/responses.go +++ b/responses.go @@ -22,6 +22,26 @@ type RespVersions struct { Versions []string `json:"versions"` } +// RespPublicRooms is the JSON response for http://matrix.org/speculator/spec/HEAD/client_server/unstable.html#get-matrix-client-unstable-publicrooms +type RespPublicRooms struct { + TotalRoomCountEstimate int `json:"total_room_count_estimate"` + PrevBatch string `json:"prev_batch"` + NextBatch string `json:"next_batch"` + Chunk []PublicRoomsChunk `json:"chunk"` +} + +type PublicRoomsChunk struct { + CanonicalAlias string `json:"canonical_alias"` + Name string `json:"name"` + WorldReadable bool `json:"world_readable"` + Topic string `json:"topic"` + NumJoinedMembers int `json:"num_joined_members"` + AvatarUrl string `json:"avatar_url"` + RoomID string `json:"room_id"` + GuestCanJoin bool `json:"guest_can_join"` + Aliases []string `json:"aliases"` +} + // RespJoinRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-join type RespJoinRoom struct { RoomID string `json:"room_id"` From 48858059f1a63a246ca35a17ae8d51dd473a37bd Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 21 Dec 2019 22:33:03 +0000 Subject: [PATCH 20/38] delint --- responses.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/responses.go b/responses.go index 1c1fd92..ec88ed9 100644 --- a/responses.go +++ b/responses.go @@ -24,19 +24,20 @@ type RespVersions struct { // RespPublicRooms is the JSON response for http://matrix.org/speculator/spec/HEAD/client_server/unstable.html#get-matrix-client-unstable-publicrooms type RespPublicRooms struct { - TotalRoomCountEstimate int `json:"total_room_count_estimate"` - PrevBatch string `json:"prev_batch"` - NextBatch string `json:"next_batch"` - Chunk []PublicRoomsChunk `json:"chunk"` + TotalRoomCountEstimate int `json:"total_room_count_estimate"` + PrevBatch string `json:"prev_batch"` + NextBatch string `json:"next_batch"` + Chunk []PublicRoom `json:"chunk"` } -type PublicRoomsChunk struct { +// PublicRooms is a part of RespPublicRooms representing the disclosed information regarding a public room +type PublicRoom struct { CanonicalAlias string `json:"canonical_alias"` Name string `json:"name"` WorldReadable bool `json:"world_readable"` Topic string `json:"topic"` NumJoinedMembers int `json:"num_joined_members"` - AvatarUrl string `json:"avatar_url"` + AvatarURL string `json:"avatar_url"` RoomID string `json:"room_id"` GuestCanJoin bool `json:"guest_can_join"` Aliases []string `json:"aliases"` From 5f2b2fc8434f60dc0d68caefb3004250b83d3ff6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 21 Dec 2019 22:35:11 +0000 Subject: [PATCH 21/38] rejig --- responses.go | 13 ------------- room.go | 13 +++++++++++++ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/responses.go b/responses.go index ec88ed9..effb609 100644 --- a/responses.go +++ b/responses.go @@ -30,19 +30,6 @@ type RespPublicRooms struct { Chunk []PublicRoom `json:"chunk"` } -// PublicRooms is a part of RespPublicRooms representing the disclosed information regarding a public room -type PublicRoom struct { - CanonicalAlias string `json:"canonical_alias"` - Name string `json:"name"` - WorldReadable bool `json:"world_readable"` - Topic string `json:"topic"` - NumJoinedMembers int `json:"num_joined_members"` - AvatarURL string `json:"avatar_url"` - RoomID string `json:"room_id"` - GuestCanJoin bool `json:"guest_can_join"` - Aliases []string `json:"aliases"` -} - // RespJoinRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-join type RespJoinRoom struct { RoomID string `json:"room_id"` diff --git a/room.go b/room.go index c9b2351..d1d2286 100644 --- a/room.go +++ b/room.go @@ -6,6 +6,19 @@ type Room struct { State map[string]map[string]*Event } +// PublicRoom represents the information about a public room obtainable from the room directory +type PublicRoom struct { + CanonicalAlias string `json:"canonical_alias"` + Name string `json:"name"` + WorldReadable bool `json:"world_readable"` + Topic string `json:"topic"` + NumJoinedMembers int `json:"num_joined_members"` + AvatarURL string `json:"avatar_url"` + RoomID string `json:"room_id"` + GuestCanJoin bool `json:"guest_can_join"` + Aliases []string `json:"aliases"` +} + // UpdateState updates the room's current state with the given Event. This will clobber events based // on the type/state_key combination. func (room Room) UpdateState(event *Event) { From 20d5d55bc2bafb24522a596aaf85c62b1173be39 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 21 Dec 2019 22:48:51 +0000 Subject: [PATCH 22/38] Add test --- client_test.go | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/client_test.go b/client_test.go index 9b7d2da..b975873 100644 --- a/client_test.go +++ b/client_test.go @@ -84,6 +84,55 @@ func TestClient_StateEvent(t *testing.T) { } } +func TestClient_PublicRooms(t *testing.T) { + cli := mockClient(func(req *http.Request) (*http.Response, error) { + if req.Method == "GET" && req.URL.Path == "/_matrix/client/r0/publicRooms" { + return &http.Response{ + StatusCode: 200, + Body: ioutil.NopCloser(bytes.NewBufferString(`{ + "chunk": [ + { + "aliases": [ + "#murrays:cheese.bar" + ], + "avatar_url": "mxc://bleeker.street/CHEDDARandBRIE", + "guest_can_join": false, + "name": "CHEESE", + "num_joined_members": 37, + "room_id": "!ol19s:bleecker.street", + "topic": "Tasty tasty cheese", + "world_readable": true + } + ], + "next_batch": "p190q", + "prev_batch": "p1902", + "total_room_count_estimate": 115 +}`)), + }, nil + } + + return nil, fmt.Errorf("unhandled URL: %s", req.URL.Path) + }) + + publicRooms, err := cli.PublicRooms(0, "", "") + + if err != nil { + t.Fatalf("PublicRooms: error, got %s", err.Error()) + } + if publicRooms.TotalRoomCountEstimate != 115 { + t.Fatalf("PublicRooms: got %d, want %d", publicRooms.TotalRoomCountEstimate, 115) + } + if len(publicRooms.Chunk) != 1 { + t.Fatalf("PublicRooms: got %d, want %d", len(publicRooms.Chunk), 1) + } + if publicRooms.Chunk[0].Name != "CHEESE" { + t.Fatalf("PublicRooms: got %s, want %s", publicRooms.Chunk[0].Name, "CHEESE") + } + if publicRooms.Chunk[0].NumJoinedMembers != 37 { + t.Fatalf("PublicRooms: got %d, want %d", publicRooms.Chunk[0].NumJoinedMembers, 37) + } +} + func mockClient(fn func(*http.Request) (*http.Response, error)) *Client { mrt := MockRoundTripper{ RT: fn, From dc3a2baaffd0fd3e36b6bc07eaf7d8ac5f64f9fb Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 21 Dec 2019 22:52:54 +0000 Subject: [PATCH 23/38] Add go.mod --- go.mod | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 go.mod diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..18e69bf --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/matrix-org/gomatrix + +go 1.12 From fa4abd0af90ca7e72eb1394e47128aedaca36307 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 22 Dec 2019 18:20:50 +0000 Subject: [PATCH 24/38] unrelated: Add PrevContent --- events.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/events.go b/events.go index edf231a..e82a6a3 100644 --- a/events.go +++ b/events.go @@ -7,15 +7,16 @@ import ( // Event represents a single Matrix 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) - Content map[string]interface{} `json:"content"` // The JSON content of the event. - 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 + 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. } // Body returns the value of the "body" key in the event content if it is From 49bed8e0e4317175fad3f10a32579089013cbbf2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 25 Jan 2020 11:55:05 +0000 Subject: [PATCH 25/38] update comments and remove go.mod --- client.go | 5 +++-- go.mod | 3 --- 2 files changed, 3 insertions(+), 5 deletions(-) delete mode 100644 go.mod diff --git a/client.go b/client.go index acd33df..7625a1c 100644 --- a/client.go +++ b/client.go @@ -372,7 +372,7 @@ func (cli *Client) Versions() (resp *RespVersions, err error) { return } -// PublicRooms returns the list of public rooms on target server. Does not require Auth. See http://matrix.org/speculator/spec/HEAD/client_server/unstable.html#get-matrix-client-unstable-publicrooms +// PublicRooms returns the list of public rooms on target server. See https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-unstable-publicrooms func (cli *Client) PublicRooms(limit int, since string, server string) (resp *RespPublicRooms, err error) { args := map[string]string{} @@ -391,7 +391,8 @@ func (cli *Client) PublicRooms(limit int, since string, server string) (resp *Re return } -// PublicRoomsFiltered returns a subset of PublicRooms filtered server side. See http://matrix.org/speculator/spec/HEAD/client_server/unstable.html#post-matrix-client-unstable-publicrooms +// PublicRoomsFiltered returns a subset of PublicRooms filtered server side. +// See https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-unstable-publicrooms func (cli *Client) PublicRoomsFiltered(limit int, since string, server string, filter string) (resp *RespPublicRooms, err error) { content := map[string]string{} diff --git a/go.mod b/go.mod deleted file mode 100644 index 18e69bf..0000000 --- a/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/matrix-org/gomatrix - -go 1.12 From 408fff5e6a974b0b337e42297ac36f4b489686ab Mon Sep 17 00:00:00 2001 From: Kegan Dougal <kegan@matrix.org> Date: Wed, 8 Apr 2020 16:53:10 +0100 Subject: [PATCH 26/38] Add go.mod --- go.mod | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 go.mod diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..18e69bf --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/matrix-org/gomatrix + +go 1.12 From e5578b12c7522e551e7ffcf4e4cc008b1b4f3250 Mon Sep 17 00:00:00 2001 From: Neil Alexander <neilalexander@users.noreply.github.com> Date: Fri, 1 May 2020 13:17:22 +0100 Subject: [PATCH 27/38] Run CI using golangci-lint, Go 1.13.10 (#78) * Run CI using golangci-lint, Go 1.13.10 * Update pre-commit hook * Fix lint issues --- .golangci.yml | 21 +++++++++++++++++++++ .travis.yml | 8 +++----- client.go | 5 +---- client_examples_test.go | 2 +- hooks/pre-commit | 11 ++--------- room.go | 4 ++-- 6 files changed, 30 insertions(+), 21 deletions(-) create mode 100644 .golangci.yml diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..15eb6ef --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,21 @@ +run: + timeout: 5m + linters: + enable: + - vet + - vetshadow + - typecheck + - deadcode + - gocyclo + - golint + - varcheck + - structcheck + - maligned + - ineffassign + - misspell + - unparam + - goimports + - goconst + - unconvert + - errcheck + - interfacer diff --git a/.travis.yml b/.travis.yml index 13585d4..46997ab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,7 @@ language: go go: - - 1.10.x + - 1.13.10 install: - - go get github.com/golang/lint/golint - - go get github.com/fzipp/gocyclo - - go get github.com/client9/misspell/... - - go get github.com/gordonklaus/ineffassign + - go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.24.0 + - go build script: ./hooks/pre-commit diff --git a/client.go b/client.go index 7625a1c..68a7bdd 100644 --- a/client.go +++ b/client.go @@ -55,10 +55,7 @@ func (e HTTPError) Error() string { // BuildURL builds a URL with the Client's homserver/prefix/access_token set already. func (cli *Client) BuildURL(urlPath ...string) string { - ps := []string{cli.Prefix} - for _, p := range urlPath { - ps = append(ps, p) - } + ps := append([]string{cli.Prefix}, urlPath...) return cli.BuildBaseURL(ps...) } diff --git a/client_examples_test.go b/client_examples_test.go index e3c0f09..d6b27fc 100644 --- a/client_examples_test.go +++ b/client_examples_test.go @@ -46,7 +46,7 @@ func Example_customInterfaces() { cli.Client = http.DefaultClient // Once you call a function, you can't safely change the interfaces. - cli.SendText("!foo:bar", "Down the rabbit hole") + _, _ = cli.SendText("!foo:bar", "Down the rabbit hole") } func ExampleClient_BuildURLWithQuery() { diff --git a/hooks/pre-commit b/hooks/pre-commit index bb0a27f..bbbede0 100755 --- a/hooks/pre-commit +++ b/hooks/pre-commit @@ -2,9 +2,6 @@ set -eu -golint -misspell --error . - # gofmt doesn't exit with an error code if the files don't match the expected # format. So we have to run it and see if it outputs anything. if gofmt -l -s . 2>&1 | read @@ -18,9 +15,5 @@ then exit 1 fi -ineffassign . - -go fmt -go tool vet --all --shadow . -gocyclo -over 12 . -go test -timeout 5s -test.v +golangci-lint run +go test -timeout 5s . ./... diff --git a/room.go b/room.go index d1d2286..364deab 100644 --- a/room.go +++ b/room.go @@ -31,8 +31,8 @@ func (room Room) UpdateState(event *Event) { // GetStateEvent returns the state event for the given type/state_key combo, or nil. func (room Room) GetStateEvent(eventType string, stateKey string) *Event { - stateEventMap, _ := room.State[eventType] - event, _ := stateEventMap[stateKey] + stateEventMap := room.State[eventType] + event := stateEventMap[stateKey] return event } From c698fb0c10213cad0c2f6fa63c3d296071e73df2 Mon Sep 17 00:00:00 2001 From: f4814n <me@f4814n.de> Date: Thu, 27 Aug 2020 14:05:45 +0200 Subject: [PATCH 28/38] Update Login() Logout() and LogoutAll() to r0.6.0 (#73) Co-authored-by: Neil Alexander <neilalexander@users.noreply.github.com> --- client.go | 10 +++++++- identifier.go | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++ requests.go | 19 +++++++------- responses.go | 26 ++++++++++++++----- 4 files changed, 108 insertions(+), 16 deletions(-) create mode 100644 identifier.go diff --git a/client.go b/client.go index 68a7bdd..fd31946 100644 --- a/client.go +++ b/client.go @@ -354,7 +354,7 @@ func (cli *Client) Login(req *ReqLogin) (resp *RespLogin, err error) { return } -// Logout the current user. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-logout +// Logout the current user. See http://matrix.org/docs/spec/client_server/r0.6.0.html#post-matrix-client-r0-logout // This does not clear the credentials from the client instance. See ClearCredentials() instead. func (cli *Client) Logout() (resp *RespLogout, err error) { urlPath := cli.BuildURL("logout") @@ -362,6 +362,14 @@ func (cli *Client) Logout() (resp *RespLogout, err error) { return } +// LogoutAll logs the current user out on all devices. See https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-logout-all +// This does not clear the credentials from the client instance. See ClearCredentails() instead. +func (cli *Client) LogoutAll() (resp *RespLogoutAll, err error) { + urlPath := cli.BuildURL("logout/all") + err = cli.MakeRequest("POST", urlPath, nil, &resp) + return +} + // Versions returns the list of supported Matrix versions on this homeserver. See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-versions func (cli *Client) Versions() (resp *RespVersions, err error) { urlPath := cli.BuildBaseURL("_matrix", "client", "versions") diff --git a/identifier.go b/identifier.go new file mode 100644 index 0000000..4a61d08 --- /dev/null +++ b/identifier.go @@ -0,0 +1,69 @@ +package gomatrix + +// Identifier is the interface for https://matrix.org/docs/spec/client_server/r0.6.0#identifier-types +type Identifier interface { + // Returns the identifier type + // https://matrix.org/docs/spec/client_server/r0.6.0#identifier-types + Type() string +} + +// UserIdentifier is the Identifier for https://matrix.org/docs/spec/client_server/r0.6.0#matrix-user-id +type UserIdentifier struct { + IDType string `json:"type"` // Set by NewUserIdentifer + User string `json:"user"` +} + +// Type implements the Identifier interface +func (i UserIdentifier) Type() string { + return "m.id.user" +} + +// NewUserIdentifier creates a new UserIdentifier with IDType set to "m.id.user" +func NewUserIdentifier(user string) UserIdentifier { + return UserIdentifier{ + IDType: "m.id.user", + User: user, + } +} + +// ThirdpartyIdentifier is the Identifier for https://matrix.org/docs/spec/client_server/r0.6.0#third-party-id +type ThirdpartyIdentifier struct { + IDType string `json:"type"` // Set by NewThirdpartyIdentifier + Medium string `json:"medium"` + Address string `json:"address"` +} + +// Type implements the Identifier interface +func (i ThirdpartyIdentifier) Type() string { + return "m.id.thirdparty" +} + +// NewThirdpartyIdentifier creates a new UserIdentifier with IDType set to "m.id.user" +func NewThirdpartyIdentifier(medium, address string) ThirdpartyIdentifier { + return ThirdpartyIdentifier{ + IDType: "m.id.thirdparty", + Medium: medium, + Address: address, + } +} + +// PhoneIdentifier is the Identifier for https://matrix.org/docs/spec/client_server/r0.6.0#phone-number +type PhoneIdentifier struct { + IDType string `json:"type"` // Set by NewPhoneIdentifier + Country string `json:"country"` + Phone string `json:"phone"` +} + +// Type implements the Identifier interface +func (i PhoneIdentifier) Type() string { + return "m.id.phone" +} + +// NewPhoneIdentifier creates a new UserIdentifier with IDType set to "m.id.user" +func NewPhoneIdentifier(country, phone string) PhoneIdentifier { + return PhoneIdentifier{ + IDType: "m.id.phone", + Country: country, + Phone: phone, + } +} diff --git a/requests.go b/requests.go index af99a22..31c426d 100644 --- a/requests.go +++ b/requests.go @@ -10,16 +10,17 @@ type ReqRegister struct { Auth interface{} `json:"auth,omitempty"` } -// ReqLogin is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-login +// ReqLogin is the JSON request for http://matrix.org/docs/spec/client_server/r0.6.0.html#post-matrix-client-r0-login type ReqLogin struct { - Type string `json:"type"` - Password string `json:"password,omitempty"` - Medium string `json:"medium,omitempty"` - User string `json:"user,omitempty"` - Address string `json:"address,omitempty"` - Token string `json:"token,omitempty"` - DeviceID string `json:"device_id,omitempty"` - InitialDeviceDisplayName string `json:"initial_device_display_name,omitempty"` + Type string `json:"type"` + Identifier Identifier `json:"identifier,omitempty"` + Password string `json:"password,omitempty"` + Medium string `json:"medium,omitempty"` + User string `json:"user,omitempty"` + Address string `json:"address,omitempty"` + Token string `json:"token,omitempty"` + DeviceID string `json:"device_id,omitempty"` + InitialDeviceDisplayName string `json:"initial_device_display_name,omitempty"` } // ReqCreateRoom is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom diff --git a/responses.go b/responses.go index effb609..e0b8697 100644 --- a/responses.go +++ b/responses.go @@ -122,17 +122,31 @@ type RespRegister struct { UserID string `json:"user_id"` } -// RespLogin is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-login +// RespLogin is the JSON response for http://matrix.org/docs/spec/client_server/r0.6.0.html#post-matrix-client-r0-login type RespLogin struct { - AccessToken string `json:"access_token"` - DeviceID string `json:"device_id"` - HomeServer string `json:"home_server"` - UserID string `json:"user_id"` + AccessToken string `json:"access_token"` + DeviceID string `json:"device_id"` + HomeServer string `json:"home_server"` + UserID string `json:"user_id"` + WellKnown DiscoveryInformation `json:"well_known"` } -// RespLogout is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-logout +// DiscoveryInformation is the JSON Response for https://matrix.org/docs/spec/client_server/r0.6.0#get-well-known-matrix-client and a part of the JSON Response for https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-login +type DiscoveryInformation struct { + Homeserver struct { + BaseURL string `json:"base_url"` + } `json:"m.homeserver"` + IdentityServer struct { + BaseURL string `json:"base_url"` + } `json:"m.identitiy_server"` +} + +// RespLogout is the JSON response for http://matrix.org/docs/spec/client_server/r0.6.0.html#post-matrix-client-r0-logout type RespLogout struct{} +// RespLogoutAll is the JSON response for https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-logout-all +type RespLogoutAll struct{} + // RespCreateRoom is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom type RespCreateRoom struct { RoomID string `json:"room_id"` From 9523b90244e6eb12f8870c5a2d869f26e4f1de20 Mon Sep 17 00:00:00 2001 From: TheDiscordian <43145244+TheDiscordian@users.noreply.github.com> Date: Thu, 27 Aug 2020 08:07:42 -0400 Subject: [PATCH 29/38] Status functions for interacting with the r0.6.0 presence/status API (#80) * Status functions for interacting with the r0.6.0 presence/status API * private -> Public --- client.go | 23 +++++++++++++++++++++++ responses.go | 8 ++++++++ 2 files changed, 31 insertions(+) diff --git a/client.go b/client.go index fd31946..eeaed99 100644 --- a/client.go +++ b/client.go @@ -494,6 +494,29 @@ func (cli *Client) SetAvatarURL(url string) error { return nil } +// GetStatus returns the status of the user from the specified MXID. See https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-presence-userid-status +func (cli *Client) GetStatus(mxid string) (resp *RespUserStatus, err error) { + urlPath := cli.BuildURL("presence", mxid, "status") + err = cli.MakeRequest("GET", urlPath, nil, &resp) + return +} + +// GetOwnStatus returns the user's status. See https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-presence-userid-status +func (cli *Client) GetOwnStatus() (resp *RespUserStatus, err error) { + return cli.GetStatus(cli.UserID) +} + +// SetStatus sets the user's status. See https://matrix.org/docs/spec/client_server/r0.6.0#put-matrix-client-r0-presence-userid-status +func (cli *Client) SetStatus(presence, status string) (err error) { + urlPath := cli.BuildURL("presence", cli.UserID, "status") + s := struct { + Presence string `json:"presence"` + StatusMsg string `json:"status_msg"` + }{presence, status} + err = cli.MakeRequest("PUT", urlPath, &s, nil) + return +} + // SendMessageEvent sends a message event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid // contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal. func (cli *Client) SendMessageEvent(roomID string, eventType string, contentJSON interface{}) (resp *RespSendEvent, err error) { diff --git a/responses.go b/responses.go index e0b8697..420690e 100644 --- a/responses.go +++ b/responses.go @@ -113,6 +113,14 @@ type RespUserDisplayName struct { DisplayName string `json:"displayname"` } +// RespUserStatus is the JSON response for https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-presence-userid-status +type RespUserStatus struct { + Presence string `json:"presence"` + StatusMsg string `json:"status_msg"` + LastActiveAgo int `json:"last_active_ago"` + CurrentlyActive bool `json:"currently_active"` +} + // RespRegister is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register type RespRegister struct { AccessToken string `json:"access_token"` From 5891715dc5e0374586a2e47a8eb269e3e052a253 Mon Sep 17 00:00:00 2001 From: TheDiscordian <43145244+TheDiscordian@users.noreply.github.com> Date: Thu, 27 Aug 2020 08:08:51 -0400 Subject: [PATCH 30/38] MarkRead method for // MarkRead marks eventID in roomID as read, signifying the event, and all before it have been read. See https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-rooms-roomid-receipt-receipttype-eventid (#81) --- client.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/client.go b/client.go index eeaed99..0ab2d1d 100644 --- a/client.go +++ b/client.go @@ -578,6 +578,12 @@ func (cli *Client) RedactEvent(roomID, eventID string, req *ReqRedact) (resp *Re return } +// MarkRead marks eventID in roomID as read, signifying the event, and all before it have been read. See https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-rooms-roomid-receipt-receipttype-eventid +func (cli *Client) MarkRead(roomID, eventID string) error { + urlPath := cli.BuildURL("rooms", roomID, "receipt", "m.read", eventID) + return cli.MakeRequest("POST", urlPath, nil, nil) +} + // CreateRoom creates a new Matrix room. See https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom // resp, err := cli.CreateRoom(&gomatrix.ReqCreateRoom{ // Preset: "public_chat", From bceb63bac628ad01bb80b6d5cb71d2b6eb35dad2 Mon Sep 17 00:00:00 2001 From: TheDiscordian <43145244+TheDiscordian@users.noreply.github.com> Date: Thu, 27 Aug 2020 08:09:29 -0400 Subject: [PATCH 31/38] Support sending formatted text (r0.6.0). (#82) --- client.go | 11 +++++++++-- events.go | 6 ++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/client.go b/client.go index 0ab2d1d..fd77fce 100644 --- a/client.go +++ b/client.go @@ -538,7 +538,14 @@ func (cli *Client) SendStateEvent(roomID, eventType, stateKey string, contentJSO // See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-text func (cli *Client) SendText(roomID, text string) (*RespSendEvent, error) { return cli.SendMessageEvent(roomID, "m.room.message", - TextMessage{"m.text", text}) + TextMessage{MsgType: "m.text", Body: text}) +} + +// SendFormattedText sends an m.room.message event into the given room with a msgtype of m.text, supports a subset of HTML for formatting. +// See https://matrix.org/docs/spec/client_server/r0.6.0#m-text +func (cli *Client) SendFormattedText(roomID, text, formattedText string) (*RespSendEvent, error) { + return cli.SendMessageEvent(roomID, "m.room.message", + TextMessage{MsgType: "m.text", Body: text, FormattedBody: formattedText, Format: "org.matrix.custom.html"}) } // SendImage sends an m.room.message event into the given room with a msgtype of m.image @@ -567,7 +574,7 @@ func (cli *Client) SendVideo(roomID, body, url string) (*RespSendEvent, error) { // See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-notice func (cli *Client) SendNotice(roomID, text string) (*RespSendEvent, error) { return cli.SendMessageEvent(roomID, "m.room.message", - TextMessage{"m.notice", text}) + TextMessage{MsgType: "m.notice", Body: text}) } // RedactEvent redacts the given event. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid diff --git a/events.go b/events.go index e82a6a3..e45c4f4 100644 --- a/events.go +++ b/events.go @@ -43,8 +43,10 @@ func (event *Event) MessageType() (msgtype string, ok bool) { // TextMessage is the contents of a Matrix formated message event. type TextMessage struct { - MsgType string `json:"msgtype"` - Body string `json:"body"` + MsgType string `json:"msgtype"` + Body string `json:"body"` + FormattedBody string `json:"formatted_body"` + Format string `json:"format"` } // ThumbnailInfo contains info about an thumbnail image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image From 7dd5e2a05bcda194c84dbe6a38c024ae787a568e Mon Sep 17 00:00:00 2001 From: Bernhard Tittelbach <service-github@tittelbach.org> Date: Thu, 27 Aug 2020 14:22:06 +0200 Subject: [PATCH 32/38] support for ephemeral events and more message types (#69) * support ephemeral events * added AudioMessage,LocationMessage,FileMessage Co-authored-by: Bernhard Tittelbach <bernhard@tittelbach.org> Co-authored-by: Neil Alexander <neilalexander@users.noreply.github.com> --- events.go | 41 +++++++++++++++++++++++++++++++++++++++++ responses.go | 3 +++ sync.go | 4 ++++ 3 files changed, 48 insertions(+) diff --git a/events.go b/events.go index e45c4f4..cbc70a8 100644 --- a/events.go +++ b/events.go @@ -102,6 +102,47 @@ type HTMLMessage struct { FormattedBody string `json:"formatted_body"` } +// FileInfo contains info about an file - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-file +type FileInfo struct { + Mimetype string `json:"mimetype,omitempty"` + Size uint `json:"size,omitempty"` //filesize in bytes +} + +// FileMessage is an m.file event - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-file +type FileMessage struct { + MsgType string `json:"msgtype"` + Body string `json:"body"` + URL string `json:"url"` + Filename string `json:"filename"` + Info FileInfo `json:"info,omitempty"` + ThumbnailURL string `json:"thumbnail_url,omitempty"` + ThumbnailInfo ImageInfo `json:"thumbnail_info,omitempty"` +} + +// LocationMessage is an m.location event - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-location +type LocationMessage struct { + MsgType string `json:"msgtype"` + Body string `json:"body"` + GeoURI string `json:"geo_uri"` + ThumbnailURL string `json:"thumbnail_url,omitempty"` + ThumbnailInfo ImageInfo `json:"thumbnail_info,omitempty"` +} + +// AudioInfo contains info about an file - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-audio +type AudioInfo struct { + Mimetype string `json:"mimetype,omitempty"` + Size uint `json:"size,omitempty"` //filesize in bytes + Duration uint `json:"duration,omitempty"` //audio duration in ms +} + +// AudioMessage is an m.audio event - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-audio +type AudioMessage struct { + MsgType string `json:"msgtype"` + Body string `json:"body"` + URL string `json:"url"` + Info AudioInfo `json:"info,omitempty"` +} + var htmlRegex = regexp.MustCompile("<[^<]+?>") // GetHTMLMessage returns an HTMLMessage with the body set to a stripped version of the provided HTML, in addition diff --git a/responses.go b/responses.go index 420690e..f488e69 100644 --- a/responses.go +++ b/responses.go @@ -189,6 +189,9 @@ type RespSync struct { Limited bool `json:"limited"` PrevBatch string `json:"prev_batch"` } `json:"timeline"` + Ephemeral struct { + Events []Event `json:"events"` + } `json:"ephemeral"` } `json:"join"` Invite map[string]struct { State struct { diff --git a/sync.go b/sync.go index c4bea48..ac326c3 100644 --- a/sync.go +++ b/sync.go @@ -64,6 +64,10 @@ func (s *DefaultSyncer) ProcessResponse(res *RespSync, since string) (err error) event.RoomID = roomID s.notifyListeners(&event) } + for _, event := range roomData.Ephemeral.Events { + event.RoomID = roomID + s.notifyListeners(&event) + } } for roomID, roomData := range res.Rooms.Invite { room := s.getOrCreateRoom(roomID) From ee30ee96404a4760f91ac36d1fd40da6052e2523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20T=C3=B6tterman?= <paul.totterman@iki.fi> Date: Thu, 1 Oct 2020 12:09:39 +0300 Subject: [PATCH 33/38] Switch from access_token in URL to token in header --- client.go | 23 +++++++++++++++++------ client_examples_test.go | 6 +++--- client_test.go | 3 +++ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/client.go b/client.go index fd77fce..d0bb0c5 100644 --- a/client.go +++ b/client.go @@ -53,13 +53,13 @@ func (e HTTPError) Error() string { return fmt.Sprintf("contents=%v msg=%s code=%d wrapped=%s", e.Contents, e.Message, e.Code, wrappedErrMsg) } -// BuildURL builds a URL with the Client's homserver/prefix/access_token set already. +// BuildURL builds a URL with the Client's homeserver/prefix set already. func (cli *Client) BuildURL(urlPath ...string) string { ps := append([]string{cli.Prefix}, urlPath...) return cli.BuildBaseURL(ps...) } -// BuildBaseURL builds a URL with the Client's homeserver/access_token set already. You must +// BuildBaseURL builds a URL with the Client's homeserver set already. You must // supply the prefix in the path. func (cli *Client) BuildBaseURL(urlPath ...string) string { // copy the URL. Purposefully ignore error as the input is from a valid URL already @@ -72,9 +72,6 @@ func (cli *Client) BuildBaseURL(urlPath ...string) string { hsURL.Path = hsURL.Path + "/" } query := hsURL.Query() - if cli.AccessToken != "" { - query.Set("access_token", cli.AccessToken) - } if cli.AppServiceUserID != "" { query.Set("user_id", cli.AppServiceUserID) } @@ -82,7 +79,7 @@ func (cli *Client) BuildBaseURL(urlPath ...string) string { return hsURL.String() } -// BuildURLWithQuery builds a URL with query parameters in addition to the Client's homeserver/prefix/access_token set already. +// BuildURLWithQuery builds a URL with query parameters in addition to the Client's homeserver/prefix set already. func (cli *Client) BuildURLWithQuery(urlPath []string, urlQuery map[string]string) string { u, _ := url.Parse(cli.BuildURL(urlPath...)) q := u.Query() @@ -203,7 +200,13 @@ func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{ if err != nil { return err } + req.Header.Set("Content-Type", "application/json") + + if cli.AccessToken != "" { + req.Header.Set("Authorization", "Bearer "+cli.AccessToken) + } + res, err := cli.Client.Do(req) if res != nil { defer res.Body.Close() @@ -687,15 +690,21 @@ func (cli *Client) UploadToContentRepo(content io.Reader, contentType string, co if err != nil { return nil, err } + req.Header.Set("Content-Type", contentType) + req.Header.Set("Authorization", "Bearer "+cli.AccessToken) + req.ContentLength = contentLength + res, err := cli.Client.Do(req) if res != nil { defer res.Body.Close() } + if err != nil { return nil, err } + if res.StatusCode != 200 { contents, err := ioutil.ReadAll(res.Body) if err != nil { @@ -710,10 +719,12 @@ func (cli *Client) UploadToContentRepo(content io.Reader, contentType string, co Code: res.StatusCode, } } + var m RespMediaUpload if err := json.NewDecoder(res.Body).Decode(&m); err != nil { return nil, err } + return &m, nil } diff --git a/client_examples_test.go b/client_examples_test.go index d6b27fc..ccea7c2 100644 --- a/client_examples_test.go +++ b/client_examples_test.go @@ -55,7 +55,7 @@ func ExampleClient_BuildURLWithQuery() { "filter_id": "5", }) fmt.Println(out) - // Output: https://matrix.org/_matrix/client/r0/sync?access_token=abcdef123456&filter_id=5 + // Output: https://matrix.org/_matrix/client/r0/sync?filter_id=5 } func ExampleClient_BuildURL() { @@ -63,7 +63,7 @@ func ExampleClient_BuildURL() { cli, _ := NewClient("https://matrix.org", userID, "abcdef123456") out := cli.BuildURL("user", userID, "filter") fmt.Println(out) - // Output: https://matrix.org/_matrix/client/r0/user/@example:matrix.org/filter?access_token=abcdef123456 + // Output: https://matrix.org/_matrix/client/r0/user/@example:matrix.org/filter } func ExampleClient_BuildBaseURL() { @@ -71,7 +71,7 @@ func ExampleClient_BuildBaseURL() { cli, _ := NewClient("https://matrix.org", userID, "abcdef123456") out := cli.BuildBaseURL("_matrix", "client", "r0", "directory", "room", "#matrix:matrix.org") fmt.Println(out) - // Output: https://matrix.org/_matrix/client/r0/directory/room/%23matrix:matrix.org?access_token=abcdef123456 + // Output: https://matrix.org/_matrix/client/r0/directory/room/%23matrix:matrix.org } // Retrieve the content of a m.room.name state event. diff --git a/client_test.go b/client_test.go index b975873..5b323f8 100644 --- a/client_test.go +++ b/client_test.go @@ -150,5 +150,8 @@ type MockRoundTripper struct { } func (t MockRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + if req.Header.Get("Authorization") == "" { + panic("no auth") + } return t.RT(req) } From 049d2d9eda15e6e18cef996f73176ca3b50c8502 Mon Sep 17 00:00:00 2001 From: northernSage <gfvante@gmail.com> Date: Thu, 25 Feb 2021 07:21:37 -0300 Subject: [PATCH 34/38] add initial docs --- .gitignore | 4 +++ CHANGELOG.md | 1 + README.md | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 CHANGELOG.md diff --git a/.gitignore b/.gitignore index daf913b..0dd5628 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.o *.a *.so +*.out # Folders _obj @@ -22,3 +23,6 @@ _testmain.go *.exe *.test *.prof + +# test editor files +*.swp diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1bc4a9c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1 @@ +## Release 0.1.0 (UNRELEADED) diff --git a/README.md b/README.md index ea9109a..aafec85 100644 --- a/README.md +++ b/README.md @@ -4,3 +4,72 @@ A Golang Matrix client. **THIS IS UNDER ACTIVE DEVELOPMENT: BREAKING CHANGES ARE FREQUENT.** + +# Contributing + +All contributions are greatly appreciated! + +## How to report issues + +Obs. it's important to check the current open issues for similar reports +in order to avoid duplicates. + +Some general guidelines: + +- Include a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) when possible. +- Describe the expected behaviour and what actually happened + including a full trace-back in case of exceptions. +- Make sure to list details about your environment + +## Setting up your environment + +If you intend to contribute to gomatrix you'll first need Go installed on your machine (version 1.12+ is required). Also, make sure to have golangci-lint properly set up since we use it for pre-commit hooks (for instructions on how to install it, check the [official docs](https://golangci-lint.run/usage/install/#local-installation)). + +- Fork gomatrix to your GitHub account by clicking the [Fork](https://github.com/matrix-org/gomatrix/fork) button. +- [Clone](https://help.github.com/en/articles/fork-a-repo#step-2-create-a-local-clone-of-your-fork) the main repository (not your fork) to your local machine. + + ``` + $ git clone https://github.com/matrix-org/gomatrix + $ cd gomatrix + ``` + +- Add your fork as a remote to push your contributions.Replace + ``{username}`` with your username. + + `git remote add fork https://github.com/{username}/gomatrix` + +- Create a new branch to identify what feature you are working on. + + ``` + $ git fetch origin + $ git checkout -b your-branch-name origin/master + ``` + +- Make your changes, including tests that cover any code changes you make, and run them as described below. + +- Execute pre-commit hooks by running + + `<gomatrix dir>/hooks/pre-commit` + +- Push your changes to your fork and [create a pull request](https://help.github.com/en/articles/creating-a-pull-request) describing your changes. + + `$ git push --set-upstream fork your-branch-name` + +- Finally, create a [pull request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests) + +## How to run tests + +You can run the test suite and example code with + + `$ go test -v` + +# Running Coverage + +To run coverage, first generate the coverage report using `go test` + + `go test -v -cover -coverprofile=coverage.out` + +You can now show the generated report as a html page with `go tool` + + `go tool cover -html=coverage.out` + From fffd6ae42688873dcda4140b6295f80789042105 Mon Sep 17 00:00:00 2001 From: northernSage <gfvante@gmail.com> Date: Thu, 25 Feb 2021 07:25:59 -0300 Subject: [PATCH 35/38] fix formatting --- README.md | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index aafec85..abff845 100644 --- a/README.md +++ b/README.md @@ -28,48 +28,45 @@ If you intend to contribute to gomatrix you'll first need Go installed on your m - Fork gomatrix to your GitHub account by clicking the [Fork](https://github.com/matrix-org/gomatrix/fork) button. - [Clone](https://help.github.com/en/articles/fork-a-repo#step-2-create-a-local-clone-of-your-fork) the main repository (not your fork) to your local machine. - ``` + $ git clone https://github.com/matrix-org/gomatrix $ cd gomatrix - ``` + - Add your fork as a remote to push your contributions.Replace ``{username}`` with your username. - `git remote add fork https://github.com/{username}/gomatrix` + git remote add fork https://github.com/{username}/gomatrix - Create a new branch to identify what feature you are working on. - ``` $ git fetch origin $ git checkout -b your-branch-name origin/master - ``` + - Make your changes, including tests that cover any code changes you make, and run them as described below. - Execute pre-commit hooks by running - `<gomatrix dir>/hooks/pre-commit` + <gomatrix dir>/hooks/pre-commit - Push your changes to your fork and [create a pull request](https://help.github.com/en/articles/creating-a-pull-request) describing your changes. - `$ git push --set-upstream fork your-branch-name` + $ git push --set-upstream fork your-branch-name - Finally, create a [pull request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests) ## How to run tests -You can run the test suite and example code with - - `$ go test -v` +You can run the test suite and example code with `$ go test -v` # Running Coverage To run coverage, first generate the coverage report using `go test` - `go test -v -cover -coverprofile=coverage.out` + go test -v -cover -coverprofile=coverage.out You can now show the generated report as a html page with `go tool` - `go tool cover -html=coverage.out` + go tool cover -html=coverage.out From 36e440bcce88658e5a44cac2852646f3f7dc12ed Mon Sep 17 00:00:00 2001 From: northernSage <gfvante@gmail.com> Date: Fri, 26 Feb 2021 03:40:12 -0300 Subject: [PATCH 36/38] add tests for events.go --- events_test.go | 110 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 events_test.go diff --git a/events_test.go b/events_test.go new file mode 100644 index 0000000..3ac5cca --- /dev/null +++ b/events_test.go @@ -0,0 +1,110 @@ +package gomatrix + +import ( + "encoding/json" + "strings" + "testing" +) + +// example events from docs +var testEvents = map[string]string{ + "withFields": `{ + "content": { + "body": "eventbody123", + "msgtype": "m.text", + "format": "org.matrix.custom.html", + "formatted_body": "<b>This is an example text message</b>" + }, + "type": "m.room.message", + "event_id": "$143273582443PhrSn:example.org", + "room_id": "!726s6s6q:example.com", + "sender": "@example:example.org", + "origin_server_ts": 1432735824653, + "unsigned": { + "age": 1234 + } + }`, + + "withoutFields": `{ + "content": { + "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF", + "displayname": "Alice Margatroid", + "membership": "join" + }, + "event_id": "$143273582443PhrSn:example.org", + "origin_server_ts": 1432735824653, + "room_id": "!jEsUZKDJdhlrceRyVU:example.org", + "sender": "@example:example.org", + "state_key": "@alice:example.org", + "type": "m.room.member", + "unsigned": { + "age": 1234 + } + }`, +} + +func TestEventWithBody(t *testing.T) { + var e Event + err := json.NewDecoder(strings.NewReader(testEvents["withFields"])).Decode(&e) + if err != nil { + t.Fatalf("TestFetchEventBody: Something went wrong while parsing: %s", testEvents["withFields"]) + } + body, ok := e.Body() + if !ok || body != "eventbody123" { + t.Fatal("TestEventWithBody: Failed to fetch value of 'body' key in event content") + } +} + +func TestEventWithoutBody(t *testing.T) { + var e Event + err := json.NewDecoder(strings.NewReader(testEvents["withoutFields"])).Decode(&e) + if err != nil { + t.Fatalf("TestEventWithoutBody: Something went wrong while parsing: %s", testEvents["withFields"]) + } + body, ok := e.Body() + if ok || body != "" { + t.Fatal("TestEventWithoutBody: Failed on 'Event.Body' call for event without a 'body' key") + } +} + +func TestEventWithMessageType(t *testing.T) { + var e Event + err := json.NewDecoder(strings.NewReader(testEvents["withFields"])).Decode(&e) + if err != nil { + t.Fatalf("TestEventWithMessageType: Something went wrong while parsing: %s", testEvents["withFields"]) + } + msgtype, ok := e.MessageType() + if !ok || msgtype != "m.text" { + t.Fatal("TestEventWithMessageType: Failed to fetch value of 'msgtype' key in event content") + } +} + +func TestEventWithoutMessageType(t *testing.T) { + var e Event + err := json.NewDecoder(strings.NewReader(testEvents["withoutFields"])).Decode(&e) + if err != nil { + t.Fatalf("TestEventWithMessageType: Something went wrong while parsing: %s", testEvents["withFields"]) + } + msgtype, ok := e.MessageType() + if ok || msgtype != "" { + t.Fatal("TestEventWithoutBody: Failed on 'Event.Body' call for event without a 'msgtype' key") + } +} + +var testHTML = `<div>a<h1>bc</h1>d<p>e<i>fg</i>hi</p>j<p>k<br/>l<b>m</b>no</p>p<small>q</small>rs</div>` + +func TestGetHTMLMessage(t *testing.T) { + msg := GetHTMLMessage("m.text", testHTML) + if expected := "abcdefghijklmnopqrs"; msg.Body != expected { + t.Fatalf("TestGetHTMLMessage: got '%s', expected '%s'", msg.Body, expected) + } + if msg.FormattedBody != testHTML { + t.Fatalf("TestGetHTMLMessage: got '%s', expected '%s'", msg.FormattedBody, testHTML) + } + if msg.MsgType != "m.text" { + t.Fatalf("TestGetHTMLMessage: got '%s', expected 'm.text'", msg.FormattedBody) + } + if expected := "org.matrix.custom.html"; msg.Format != expected { + t.Fatalf("TestGetHTMLMessage: got '%s', expected '%s'", msg.Format, expected) + } +} From e6877ae9c8120cfe9ebe636da254c41d726a459b Mon Sep 17 00:00:00 2001 From: Kegsay <kegsay@gmail.com> Date: Wed, 24 Mar 2021 16:18:25 +0000 Subject: [PATCH 37/38] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bc4a9c..b6a9a16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1 @@ -## Release 0.1.0 (UNRELEADED) +## Release 0.1.0 (UNRELEASED) From dbffaa2b281d474682f70dedb9c632614e95b4f5 Mon Sep 17 00:00:00 2001 From: Kegsay <kegsay@gmail.com> Date: Wed, 24 Mar 2021 16:18:30 +0000 Subject: [PATCH 38/38] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index abff845..a083b46 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ All contributions are greatly appreciated! ## How to report issues -Obs. it's important to check the current open issues for similar reports +Please check the current open issues for similar reports in order to avoid duplicates. Some general guidelines: @@ -69,4 +69,3 @@ To run coverage, first generate the coverage report using `go test` You can now show the generated report as a html page with `go tool` go tool cover -html=coverage.out -