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
-