Browse Source

Use github.com/matrix-org/gomatrix to interact with matrix instead of custom code

master
Lomanic 1 year ago
parent
commit
7ab566fcfa

+ 2
- 0
go.mod View File

@@ -1,3 +1,5 @@
1 1
 module github.com/Lomanic/presence-button-web
2 2
 
3 3
 go 1.12
4
+
5
+require github.com/matrix-org/gomatrix v0.0.0-20200827122206-7dd5e2a05bcd

+ 2
- 0
go.sum View File

@@ -0,0 +1,2 @@
1
+github.com/matrix-org/gomatrix v0.0.0-20200827122206-7dd5e2a05bcd h1:xVrqJK3xHREMNjwjljkAUaadalWc0rRbmVuQatzmgwg=
2
+github.com/matrix-org/gomatrix v0.0.0-20200827122206-7dd5e2a05bcd/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s=

+ 12
- 47
main.go View File

@@ -1,18 +1,17 @@
1 1
 package main
2 2
 
3 3
 import (
4
-	"bytes"
5 4
 	"encoding/json"
6 5
 	"fmt"
7
-	"io/ioutil"
8 6
 	"log"
9 7
 	"net/http"
10
-	"net/url"
11 8
 	"os"
12 9
 	"path/filepath"
13 10
 	"strconv"
14 11
 	"strings"
15 12
 	"time"
13
+
14
+	"github.com/matrix-org/gomatrix"
16 15
 )
17 16
 
18 17
 type Status struct {
@@ -54,7 +53,8 @@ var (
54 53
 		// formerly https://www.flaticon.com/free-icon/closed_1234190, maybe try https://flaticons.net/customize.php?dir=Miscellaneous&icon=Closed.png without attribution
55 54
 		false: `<?xml version="1.0" ?><svg viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg"><defs><style>.cls-1{fill:#f8991d;}.cls-2{fill:#fd0;}.cls-3{fill:#314967;}</style></defs><title/><g data-name="18 Closed Sign" id="_18_Closed_Sign"><rect class="cls-1" height="36" rx="6" ry="6" width="96" x="16" y="64"/><circle class="cls-2" cx="64" cy="28" r="6"/><path class="cls-3" d="M71.85,77.83a4.37,4.37,0,0,1,2.34,1,1.93,1.93,0,1,0,2.08-3.25A7.94,7.94,0,0,0,71.85,74c-3.13,0-5.55,2.06-5.55,4.58,0,2.74,2.64,4.21,5.35,4.58.55.11,2,.45,2.26,1,0,.57-1.38,1-2,1a5.25,5.25,0,0,1-2.79-1.16,1.93,1.93,0,1,0-2.42,3,8.73,8.73,0,0,0,5.23,2c3.12,0,5.88-2,5.88-4.82s-2.88-4.4-5.66-4.78c-1.6-.31-2-.77-2-.77C70.15,78.39,70.8,77.83,71.85,77.83Z"/><path class="cls-3" d="M40,76.34V87a2,2,0,0,0,2,2h5.83a2,2,0,0,0,0-4H44V76.34A2,2,0,0,0,40,76.34Z"/><path class="cls-3" d="M50,81.48A7.16,7.16,0,1,0,57.23,74,7.32,7.32,0,0,0,50,81.48Zm10.26,0c0,2.89-3.21,4.64-5.27,2.43-1.94-2-.71-5.86,2.2-5.86A3.29,3.29,0,0,1,60.3,81.48Z"/><path class="cls-3" d="M34.44,78.82a2,2,0,0,0,2.49-3.21A7.7,7.7,0,0,0,32.15,74a7.6,7.6,0,0,0-7.6,7.48h0c0,6.3,7.44,9.7,12.39,5.86a2,2,0,0,0-2.51-3.2,3.52,3.52,0,1,1,0-5.31Z"/><path class="cls-3" d="M87.69,78.35a2,2,0,0,0,0-4H81.8a2,2,0,0,0-2,2V87a2,2,0,0,0,1,1.75V89h6.83a2,2,0,0,0,0-4H83.81V83.65H87a2,2,0,0,0,0-4h-3.2V78.35Z"/><path class="cls-3" d="M103.09,81.64a7.29,7.29,0,0,0-7.28-7.28H93.69a2,2,0,0,0-2,2V87a2,2,0,0,0,2,2h2.13A7.31,7.31,0,0,0,103.09,81.64ZM95.81,85h-.12V78.35h.12C100.14,78.35,100.18,84.93,95.81,85Z"/><path class="cls-3" d="M92.73,98H22a4,4,0,0,1-4-4V70a4,4,0,0,1,4-4H42.55a2,2,0,0,0,0-4H32.83L59.93,34.89a8,8,0,0,0,8.13,0L81.71,48.54a2,2,0,0,0,2.83-2.83L70.88,32.06A8,8,0,0,0,64,20a2,2,0,0,0,0,4,4,4,0,0,1,2.79,6.86C63.58,34,58.24,30.08,60.51,26a2,2,0,0,0-3.49-2,8,8,0,0,0,.09,8L27.17,62H22a8,8,0,0,0-8,8V94a8,8,0,0,0,8,8H92.73A2,2,0,0,0,92.73,98Z"/><path class="cls-3" d="M106,62h-5.17L88.09,49.26a2,2,0,0,0-2.83,2.83L95.17,62H76a2,2,0,0,0,0,4h30a4,4,0,0,1,4,4V94a4,4,0,0,1-4,4H98.51a2,2,0,0,0,0,4H106a8,8,0,0,0,8-8V70A8,8,0,0,0,106,62Z"/><path class="cls-3" d="M70,62H49.22a2,2,0,0,0,0,4H70A2,2,0,0,0,70,62Z"/></g></svg>`,
56 55
 	}
57
-	db *os.File
56
+	db     *os.File
57
+	matrix *gomatrix.Client
58 58
 )
59 59
 
60 60
 func init() {
@@ -79,6 +79,11 @@ func init() {
79 79
 	if config.MATRIXACCESSTOKEN == "" {
80 80
 		panic("MATRIXACCESSTOKEN is empty")
81 81
 	}
82
+	var err error
83
+	matrix, err = gomatrix.NewClient(fmt.Sprintf("https://%s", config.MATRIXUSERNAME[strings.Index(config.MATRIXUSERNAME, ":")+1:]), config.MATRIXUSERNAME, config.MATRIXACCESSTOKEN)
84
+	if err != nil {
85
+		panic(fmt.Sprintf("error creating matrix client: %s", err))
86
+	}
82 87
 
83 88
 	if config.MATRIXROOM == "" {
84 89
 		panic("MATRIXROOM is empty")
@@ -97,7 +102,7 @@ func init() {
97 102
 		panic("ESPPASSWORD is empty")
98 103
 	}
99 104
 
100
-	err := os.MkdirAll(filepath.Dir(dbPath), 0755)
105
+	err = os.MkdirAll(filepath.Dir(dbPath), 0755)
101 106
 	if err != nil {
102 107
 		panic(err)
103 108
 	}
@@ -124,7 +129,7 @@ func checkClosure() {
124 129
 			// the Fuz is newly closed, notify on matrix and write file to survive reboot
125 130
 			// TODO: matrix msg
126 131
 			fmt.Println("the Fuz is newly closed, notify on matrix and write file to survive reboot")
127
-			err := sendMatrixMessage(config.MATRIXUSERNAME, config.MATRIXACCESSTOKEN, config.MATRIXROOM, config.MATRIXCLOSINGMESSAGE)
132
+			_, err := matrix.SendText(config.MATRIXROOM, config.MATRIXCLOSINGMESSAGE)
128 133
 			if err != nil {
129 134
 				fmt.Println("err:", err)
130 135
 				time.Sleep(10 * time.Second)
@@ -210,7 +215,7 @@ func statusHandler(w http.ResponseWriter, r *http.Request) {
210 215
 	if status.FuzIsOpen && (status.LastOpened.Equal(status.LastClosed) || status.LastOpened.Before(status.LastClosed)) {
211 216
 		// the Fuz is newly opened, notify on matrix and write file to survive reboot
212 217
 		fmt.Println("the Fuz is newly opened, notify on matrix and write file to survive reboot")
213
-		err := sendMatrixMessage(config.MATRIXUSERNAME, config.MATRIXACCESSTOKEN, config.MATRIXROOM, config.MATRIXOPENINGMESSAGE)
218
+		_, err := matrix.SendText(config.MATRIXROOM, config.MATRIXOPENINGMESSAGE)
214 219
 		if err != nil {
215 220
 			fmt.Println("err:", err)
216 221
 			return
@@ -225,46 +230,6 @@ func statusHandler(w http.ResponseWriter, r *http.Request) {
225 230
 	}
226 231
 }
227 232
 
228
-func sendMatrixMessage(username, accessToken, room, messageText string) error {
229
-	type Message struct {
230
-		Msgtype string `json:"msgtype"`
231
-		Body    string `json:"body"`
232
-	}
233
-	client := &http.Client{}
234
-	message := Message{
235
-		Msgtype: "m.text",
236
-		Body:    messageText,
237
-	}
238
-	body := new(bytes.Buffer)
239
-	err := json.NewEncoder(body).Encode(message)
240
-	if err != nil {
241
-		return err
242
-	}
243
-	v := url.Values{}
244
-	v.Set("access_token", accessToken)
245
-	v.Set("limit", "1")
246
-	url := url.URL{
247
-		Scheme:   "https",
248
-		Host:     username[strings.Index(username, ":")+1:],
249
-		Path:     fmt.Sprintf("/_matrix/client/r0/rooms/%s/send/m.room.message/%d", room, time.Now().UnixNano()/1000000),
250
-		RawQuery: v.Encode(),
251
-	}
252
-	req, err := http.NewRequest(http.MethodPut, url.String(), body)
253
-	if err != nil {
254
-		return err
255
-	}
256
-	req.Header.Set("Content-Type", "application/json")
257
-	res, err := client.Do(req)
258
-	if err != nil {
259
-		return err
260
-	}
261
-	defer res.Body.Close()
262
-	resBody, _ := ioutil.ReadAll(res.Body)
263
-	fmt.Println(string(resBody))
264
-
265
-	return nil
266
-}
267
-
268 233
 func main() {
269 234
 	go updateUptime()
270 235
 	go checkClosure()

+ 24
- 0
vendor/github.com/matrix-org/gomatrix/.gitignore View File

@@ -0,0 +1,24 @@
1
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
2
+*.o
3
+*.a
4
+*.so
5
+
6
+# Folders
7
+_obj
8
+_test
9
+
10
+# Architecture specific extensions/prefixes
11
+*.[568vq]
12
+[568vq].out
13
+
14
+*.cgo1.go
15
+*.cgo2.c
16
+_cgo_defun.c
17
+_cgo_gotypes.go
18
+_cgo_export.*
19
+
20
+_testmain.go
21
+
22
+*.exe
23
+*.test
24
+*.prof

+ 21
- 0
vendor/github.com/matrix-org/gomatrix/.golangci.yml View File

@@ -0,0 +1,21 @@
1
+run:
2
+  timeout: 5m
3
+  linters:
4
+    enable:
5
+      - vet
6
+      - vetshadow
7
+      - typecheck
8
+      - deadcode
9
+      - gocyclo
10
+      - golint
11
+      - varcheck
12
+      - structcheck
13
+      - maligned
14
+      - ineffassign
15
+      - misspell
16
+      - unparam
17
+      - goimports
18
+      - goconst
19
+      - unconvert
20
+      - errcheck
21
+      - interfacer

+ 7
- 0
vendor/github.com/matrix-org/gomatrix/.travis.yml View File

@@ -0,0 +1,7 @@
1
+language: go
2
+go:
3
+ - 1.13.10
4
+install:
5
+ - go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.24.0
6
+ - go build
7
+script: ./hooks/pre-commit

+ 201
- 0
vendor/github.com/matrix-org/gomatrix/LICENSE View File

@@ -0,0 +1,201 @@
1
+                                 Apache License
2
+                           Version 2.0, January 2004
3
+                        http://www.apache.org/licenses/
4
+
5
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+   1. Definitions.
8
+
9
+      "License" shall mean the terms and conditions for use, reproduction,
10
+      and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+      "Licensor" shall mean the copyright owner or entity authorized by
13
+      the copyright owner that is granting the License.
14
+
15
+      "Legal Entity" shall mean the union of the acting entity and all
16
+      other entities that control, are controlled by, or are under common
17
+      control with that entity. For the purposes of this definition,
18
+      "control" means (i) the power, direct or indirect, to cause the
19
+      direction or management of such entity, whether by contract or
20
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+      outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+      "You" (or "Your") shall mean an individual or Legal Entity
24
+      exercising permissions granted by this License.
25
+
26
+      "Source" form shall mean the preferred form for making modifications,
27
+      including but not limited to software source code, documentation
28
+      source, and configuration files.
29
+
30
+      "Object" form shall mean any form resulting from mechanical
31
+      transformation or translation of a Source form, including but
32
+      not limited to compiled object code, generated documentation,
33
+      and conversions to other media types.
34
+
35
+      "Work" shall mean the work of authorship, whether in Source or
36
+      Object form, made available under the License, as indicated by a
37
+      copyright notice that is included in or attached to the work
38
+      (an example is provided in the Appendix below).
39
+
40
+      "Derivative Works" shall mean any work, whether in Source or Object
41
+      form, that is based on (or derived from) the Work and for which the
42
+      editorial revisions, annotations, elaborations, or other modifications
43
+      represent, as a whole, an original work of authorship. For the purposes
44
+      of this License, Derivative Works shall not include works that remain
45
+      separable from, or merely link (or bind by name) to the interfaces of,
46
+      the Work and Derivative Works thereof.
47
+
48
+      "Contribution" shall mean any work of authorship, including
49
+      the original version of the Work and any modifications or additions
50
+      to that Work or Derivative Works thereof, that is intentionally
51
+      submitted to Licensor for inclusion in the Work by the copyright owner
52
+      or by an individual or Legal Entity authorized to submit on behalf of
53
+      the copyright owner. For the purposes of this definition, "submitted"
54
+      means any form of electronic, verbal, or written communication sent
55
+      to the Licensor or its representatives, including but not limited to
56
+      communication on electronic mailing lists, source code control systems,
57
+      and issue tracking systems that are managed by, or on behalf of, the
58
+      Licensor for the purpose of discussing and improving the Work, but
59
+      excluding communication that is conspicuously marked or otherwise
60
+      designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+      "Contributor" shall mean Licensor and any individual or Legal Entity
63
+      on behalf of whom a Contribution has been received by Licensor and
64
+      subsequently incorporated within the Work.
65
+
66
+   2. Grant of Copyright License. Subject to the terms and conditions of
67
+      this License, each Contributor hereby grants to You a perpetual,
68
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+      copyright license to reproduce, prepare Derivative Works of,
70
+      publicly display, publicly perform, sublicense, and distribute the
71
+      Work and such Derivative Works in Source or Object form.
72
+
73
+   3. Grant of Patent License. Subject to the terms and conditions of
74
+      this License, each Contributor hereby grants to You a perpetual,
75
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+      (except as stated in this section) patent license to make, have made,
77
+      use, offer to sell, sell, import, and otherwise transfer the Work,
78
+      where such license applies only to those patent claims licensable
79
+      by such Contributor that are necessarily infringed by their
80
+      Contribution(s) alone or by combination of their Contribution(s)
81
+      with the Work to which such Contribution(s) was submitted. If You
82
+      institute patent litigation against any entity (including a
83
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+      or a Contribution incorporated within the Work constitutes direct
85
+      or contributory patent infringement, then any patent licenses
86
+      granted to You under this License for that Work shall terminate
87
+      as of the date such litigation is filed.
88
+
89
+   4. Redistribution. You may reproduce and distribute copies of the
90
+      Work or Derivative Works thereof in any medium, with or without
91
+      modifications, and in Source or Object form, provided that You
92
+      meet the following conditions:
93
+
94
+      (a) You must give any other recipients of the Work or
95
+          Derivative Works a copy of this License; and
96
+
97
+      (b) You must cause any modified files to carry prominent notices
98
+          stating that You changed the files; and
99
+
100
+      (c) You must retain, in the Source form of any Derivative Works
101
+          that You distribute, all copyright, patent, trademark, and
102
+          attribution notices from the Source form of the Work,
103
+          excluding those notices that do not pertain to any part of
104
+          the Derivative Works; and
105
+
106
+      (d) If the Work includes a "NOTICE" text file as part of its
107
+          distribution, then any Derivative Works that You distribute must
108
+          include a readable copy of the attribution notices contained
109
+          within such NOTICE file, excluding those notices that do not
110
+          pertain to any part of the Derivative Works, in at least one
111
+          of the following places: within a NOTICE text file distributed
112
+          as part of the Derivative Works; within the Source form or
113
+          documentation, if provided along with the Derivative Works; or,
114
+          within a display generated by the Derivative Works, if and
115
+          wherever such third-party notices normally appear. The contents
116
+          of the NOTICE file are for informational purposes only and
117
+          do not modify the License. You may add Your own attribution
118
+          notices within Derivative Works that You distribute, alongside
119
+          or as an addendum to the NOTICE text from the Work, provided
120
+          that such additional attribution notices cannot be construed
121
+          as modifying the License.
122
+
123
+      You may add Your own copyright statement to Your modifications and
124
+      may provide additional or different license terms and conditions
125
+      for use, reproduction, or distribution of Your modifications, or
126
+      for any such Derivative Works as a whole, provided Your use,
127
+      reproduction, and distribution of the Work otherwise complies with
128
+      the conditions stated in this License.
129
+
130
+   5. Submission of Contributions. Unless You explicitly state otherwise,
131
+      any Contribution intentionally submitted for inclusion in the Work
132
+      by You to the Licensor shall be under the terms and conditions of
133
+      this License, without any additional terms or conditions.
134
+      Notwithstanding the above, nothing herein shall supersede or modify
135
+      the terms of any separate license agreement you may have executed
136
+      with Licensor regarding such Contributions.
137
+
138
+   6. Trademarks. This License does not grant permission to use the trade
139
+      names, trademarks, service marks, or product names of the Licensor,
140
+      except as required for reasonable and customary use in describing the
141
+      origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+   7. Disclaimer of Warranty. Unless required by applicable law or
144
+      agreed to in writing, Licensor provides the Work (and each
145
+      Contributor provides its Contributions) on an "AS IS" BASIS,
146
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+      implied, including, without limitation, any warranties or conditions
148
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+      PARTICULAR PURPOSE. You are solely responsible for determining the
150
+      appropriateness of using or redistributing the Work and assume any
151
+      risks associated with Your exercise of permissions under this License.
152
+
153
+   8. Limitation of Liability. In no event and under no legal theory,
154
+      whether in tort (including negligence), contract, or otherwise,
155
+      unless required by applicable law (such as deliberate and grossly
156
+      negligent acts) or agreed to in writing, shall any Contributor be
157
+      liable to You for damages, including any direct, indirect, special,
158
+      incidental, or consequential damages of any character arising as a
159
+      result of this License or out of the use or inability to use the
160
+      Work (including but not limited to damages for loss of goodwill,
161
+      work stoppage, computer failure or malfunction, or any and all
162
+      other commercial damages or losses), even if such Contributor
163
+      has been advised of the possibility of such damages.
164
+
165
+   9. Accepting Warranty or Additional Liability. While redistributing
166
+      the Work or Derivative Works thereof, You may choose to offer,
167
+      and charge a fee for, acceptance of support, warranty, indemnity,
168
+      or other liability obligations and/or rights consistent with this
169
+      License. However, in accepting such obligations, You may act only
170
+      on Your own behalf and on Your sole responsibility, not on behalf
171
+      of any other Contributor, and only if You agree to indemnify,
172
+      defend, and hold each Contributor harmless for any liability
173
+      incurred by, or claims asserted against, such Contributor by reason
174
+      of your accepting any such warranty or additional liability.
175
+
176
+   END OF TERMS AND CONDITIONS
177
+
178
+   APPENDIX: How to apply the Apache License to your work.
179
+
180
+      To apply the Apache License to your work, attach the following
181
+      boilerplate notice, with the fields enclosed by brackets "{}"
182
+      replaced with your own identifying information. (Don't include
183
+      the brackets!)  The text should be enclosed in the appropriate
184
+      comment syntax for the file format. We also recommend that a
185
+      file or class name and description of purpose be included on the
186
+      same "printed page" as the copyright notice for easier
187
+      identification within third-party archives.
188
+
189
+   Copyright {yyyy} {name of copyright owner}
190
+
191
+   Licensed under the Apache License, Version 2.0 (the "License");
192
+   you may not use this file except in compliance with the License.
193
+   You may obtain a copy of the License at
194
+
195
+       http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+   Unless required by applicable law or agreed to in writing, software
198
+   distributed under the License is distributed on an "AS IS" BASIS,
199
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+   See the License for the specific language governing permissions and
201
+   limitations under the License.

+ 6
- 0
vendor/github.com/matrix-org/gomatrix/README.md View File

@@ -0,0 +1,6 @@
1
+# gomatrix
2
+[![GoDoc](https://godoc.org/github.com/matrix-org/gomatrix?status.svg)](https://godoc.org/github.com/matrix-org/gomatrix)
3
+
4
+A Golang Matrix client.
5
+
6
+**THIS IS UNDER ACTIVE DEVELOPMENT: BREAKING CHANGES ARE FREQUENT.**

+ 794
- 0
vendor/github.com/matrix-org/gomatrix/client.go View File

@@ -0,0 +1,794 @@
1
+// Package gomatrix implements the Matrix Client-Server API.
2
+//
3
+// Specification can be found at http://matrix.org/docs/spec/client_server/r0.2.0.html
4
+package gomatrix
5
+
6
+import (
7
+	"bytes"
8
+	"encoding/json"
9
+	"fmt"
10
+	"io"
11
+	"io/ioutil"
12
+	"net/http"
13
+	"net/url"
14
+	"path"
15
+	"strconv"
16
+	"strings"
17
+	"sync"
18
+	"time"
19
+)
20
+
21
+// Client represents a Matrix client.
22
+type Client struct {
23
+	HomeserverURL *url.URL     // The base homeserver URL
24
+	Prefix        string       // The API prefix eg '/_matrix/client/r0'
25
+	UserID        string       // The user ID of the client. Used for forming HTTP paths which use the client's user ID.
26
+	AccessToken   string       // The access_token for the client.
27
+	Client        *http.Client // The underlying HTTP client which will be used to make HTTP requests.
28
+	Syncer        Syncer       // The thing which can process /sync responses
29
+	Store         Storer       // The thing which can store rooms/tokens/ids
30
+
31
+	// The ?user_id= query parameter for application services. This must be set *prior* to calling a method. If this is empty,
32
+	// no user_id parameter will be sent.
33
+	// See http://matrix.org/docs/spec/application_service/unstable.html#identity-assertion
34
+	AppServiceUserID string
35
+
36
+	syncingMutex sync.Mutex // protects syncingID
37
+	syncingID    uint32     // Identifies the current Sync. Only one Sync can be active at any given time.
38
+}
39
+
40
+// HTTPError An HTTP Error response, which may wrap an underlying native Go Error.
41
+type HTTPError struct {
42
+	Contents     []byte
43
+	WrappedError error
44
+	Message      string
45
+	Code         int
46
+}
47
+
48
+func (e HTTPError) Error() string {
49
+	var wrappedErrMsg string
50
+	if e.WrappedError != nil {
51
+		wrappedErrMsg = e.WrappedError.Error()
52
+	}
53
+	return fmt.Sprintf("contents=%v msg=%s code=%d wrapped=%s", e.Contents, e.Message, e.Code, wrappedErrMsg)
54
+}
55
+
56
+// BuildURL builds a URL with the Client's homserver/prefix/access_token set already.
57
+func (cli *Client) BuildURL(urlPath ...string) string {
58
+	ps := append([]string{cli.Prefix}, urlPath...)
59
+	return cli.BuildBaseURL(ps...)
60
+}
61
+
62
+// BuildBaseURL builds a URL with the Client's homeserver/access_token set already. You must
63
+// supply the prefix in the path.
64
+func (cli *Client) BuildBaseURL(urlPath ...string) string {
65
+	// copy the URL. Purposefully ignore error as the input is from a valid URL already
66
+	hsURL, _ := url.Parse(cli.HomeserverURL.String())
67
+	parts := []string{hsURL.Path}
68
+	parts = append(parts, urlPath...)
69
+	hsURL.Path = path.Join(parts...)
70
+	// Manually add the trailing slash back to the end of the path if it's explicitly needed
71
+	if strings.HasSuffix(urlPath[len(urlPath)-1], "/") {
72
+		hsURL.Path = hsURL.Path + "/"
73
+	}
74
+	query := hsURL.Query()
75
+	if cli.AccessToken != "" {
76
+		query.Set("access_token", cli.AccessToken)
77
+	}
78
+	if cli.AppServiceUserID != "" {
79
+		query.Set("user_id", cli.AppServiceUserID)
80
+	}
81
+	hsURL.RawQuery = query.Encode()
82
+	return hsURL.String()
83
+}
84
+
85
+// BuildURLWithQuery builds a URL with query parameters in addition to the Client's homeserver/prefix/access_token set already.
86
+func (cli *Client) BuildURLWithQuery(urlPath []string, urlQuery map[string]string) string {
87
+	u, _ := url.Parse(cli.BuildURL(urlPath...))
88
+	q := u.Query()
89
+	for k, v := range urlQuery {
90
+		q.Set(k, v)
91
+	}
92
+	u.RawQuery = q.Encode()
93
+	return u.String()
94
+}
95
+
96
+// SetCredentials sets the user ID and access token on this client instance.
97
+func (cli *Client) SetCredentials(userID, accessToken string) {
98
+	cli.AccessToken = accessToken
99
+	cli.UserID = userID
100
+}
101
+
102
+// ClearCredentials removes the user ID and access token on this client instance.
103
+func (cli *Client) ClearCredentials() {
104
+	cli.AccessToken = ""
105
+	cli.UserID = ""
106
+}
107
+
108
+// Sync starts syncing with the provided Homeserver. If Sync() is called twice then the first sync will be stopped and the
109
+// error will be nil.
110
+//
111
+// This function will block until a fatal /sync error occurs, so it should almost always be started as a new goroutine.
112
+// Fatal sync errors can be caused by:
113
+//   - The failure to create a filter.
114
+//   - Client.Syncer.OnFailedSync returning an error in response to a failed sync.
115
+//   - Client.Syncer.ProcessResponse returning an error.
116
+// If you wish to continue retrying in spite of these fatal errors, call Sync() again.
117
+func (cli *Client) Sync() error {
118
+	// Mark the client as syncing.
119
+	// We will keep syncing until the syncing state changes. Either because
120
+	// Sync is called or StopSync is called.
121
+	syncingID := cli.incrementSyncingID()
122
+	nextBatch := cli.Store.LoadNextBatch(cli.UserID)
123
+	filterID := cli.Store.LoadFilterID(cli.UserID)
124
+	if filterID == "" {
125
+		filterJSON := cli.Syncer.GetFilterJSON(cli.UserID)
126
+		resFilter, err := cli.CreateFilter(filterJSON)
127
+		if err != nil {
128
+			return err
129
+		}
130
+		filterID = resFilter.FilterID
131
+		cli.Store.SaveFilterID(cli.UserID, filterID)
132
+	}
133
+
134
+	for {
135
+		resSync, err := cli.SyncRequest(30000, nextBatch, filterID, false, "")
136
+		if err != nil {
137
+			duration, err2 := cli.Syncer.OnFailedSync(resSync, err)
138
+			if err2 != nil {
139
+				return err2
140
+			}
141
+			time.Sleep(duration)
142
+			continue
143
+		}
144
+
145
+		// Check that the syncing state hasn't changed
146
+		// Either because we've stopped syncing or another sync has been started.
147
+		// We discard the response from our sync.
148
+		if cli.getSyncingID() != syncingID {
149
+			return nil
150
+		}
151
+
152
+		// Save the token now *before* processing it. This means it's possible
153
+		// to not process some events, but it means that we won't get constantly stuck processing
154
+		// a malformed/buggy event which keeps making us panic.
155
+		cli.Store.SaveNextBatch(cli.UserID, resSync.NextBatch)
156
+		if err = cli.Syncer.ProcessResponse(resSync, nextBatch); err != nil {
157
+			return err
158
+		}
159
+
160
+		nextBatch = resSync.NextBatch
161
+	}
162
+}
163
+
164
+func (cli *Client) incrementSyncingID() uint32 {
165
+	cli.syncingMutex.Lock()
166
+	defer cli.syncingMutex.Unlock()
167
+	cli.syncingID++
168
+	return cli.syncingID
169
+}
170
+
171
+func (cli *Client) getSyncingID() uint32 {
172
+	cli.syncingMutex.Lock()
173
+	defer cli.syncingMutex.Unlock()
174
+	return cli.syncingID
175
+}
176
+
177
+// StopSync stops the ongoing sync started by Sync.
178
+func (cli *Client) StopSync() {
179
+	// Advance the syncing state so that any running Syncs will terminate.
180
+	cli.incrementSyncingID()
181
+}
182
+
183
+// MakeRequest makes a JSON HTTP request to the given URL.
184
+// The response body will be stream decoded into an interface. This will automatically stop if the response
185
+// body is nil.
186
+//
187
+// Returns an error if the response is not 2xx along with the HTTP body bytes if it got that far. This error is
188
+// an HTTPError which includes the returned HTTP status code, byte contents of the response body and possibly a
189
+// RespError as the WrappedError, if the HTTP body could be decoded as a RespError.
190
+func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{}, resBody interface{}) error {
191
+	var req *http.Request
192
+	var err error
193
+	if reqBody != nil {
194
+		buf := new(bytes.Buffer)
195
+		if err := json.NewEncoder(buf).Encode(reqBody); err != nil {
196
+			return err
197
+		}
198
+		req, err = http.NewRequest(method, httpURL, buf)
199
+	} else {
200
+		req, err = http.NewRequest(method, httpURL, nil)
201
+	}
202
+
203
+	if err != nil {
204
+		return err
205
+	}
206
+	req.Header.Set("Content-Type", "application/json")
207
+	res, err := cli.Client.Do(req)
208
+	if res != nil {
209
+		defer res.Body.Close()
210
+	}
211
+	if err != nil {
212
+		return err
213
+	}
214
+	if res.StatusCode/100 != 2 { // not 2xx
215
+		contents, err := ioutil.ReadAll(res.Body)
216
+		if err != nil {
217
+			return err
218
+		}
219
+
220
+		var wrap error
221
+		var respErr RespError
222
+		if _ = json.Unmarshal(contents, &respErr); respErr.ErrCode != "" {
223
+			wrap = respErr
224
+		}
225
+
226
+		// If we failed to decode as RespError, don't just drop the HTTP body, include it in the
227
+		// HTTP error instead (e.g proxy errors which return HTML).
228
+		msg := "Failed to " + method + " JSON to " + req.URL.Path
229
+		if wrap == nil {
230
+			msg = msg + ": " + string(contents)
231
+		}
232
+
233
+		return HTTPError{
234
+			Contents:     contents,
235
+			Code:         res.StatusCode,
236
+			Message:      msg,
237
+			WrappedError: wrap,
238
+		}
239
+	}
240
+
241
+	if resBody != nil && res.Body != nil {
242
+		return json.NewDecoder(res.Body).Decode(&resBody)
243
+	}
244
+
245
+	return nil
246
+}
247
+
248
+// 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
249
+func (cli *Client) CreateFilter(filter json.RawMessage) (resp *RespCreateFilter, err error) {
250
+	urlPath := cli.BuildURL("user", cli.UserID, "filter")
251
+	err = cli.MakeRequest("POST", urlPath, &filter, &resp)
252
+	return
253
+}
254
+
255
+// SyncRequest makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-sync
256
+func (cli *Client) SyncRequest(timeout int, since, filterID string, fullState bool, setPresence string) (resp *RespSync, err error) {
257
+	query := map[string]string{
258
+		"timeout": strconv.Itoa(timeout),
259
+	}
260
+	if since != "" {
261
+		query["since"] = since
262
+	}
263
+	if filterID != "" {
264
+		query["filter"] = filterID
265
+	}
266
+	if setPresence != "" {
267
+		query["set_presence"] = setPresence
268
+	}
269
+	if fullState {
270
+		query["full_state"] = "true"
271
+	}
272
+	urlPath := cli.BuildURLWithQuery([]string{"sync"}, query)
273
+	err = cli.MakeRequest("GET", urlPath, nil, &resp)
274
+	return
275
+}
276
+
277
+func (cli *Client) register(u string, req *ReqRegister) (resp *RespRegister, uiaResp *RespUserInteractive, err error) {
278
+	err = cli.MakeRequest("POST", u, req, &resp)
279
+	if err != nil {
280
+		httpErr, ok := err.(HTTPError)
281
+		if !ok { // network error
282
+			return
283
+		}
284
+		if httpErr.Code == 401 {
285
+			// body should be RespUserInteractive, if it isn't, fail with the error
286
+			err = json.Unmarshal(httpErr.Contents, &uiaResp)
287
+			return
288
+		}
289
+	}
290
+	return
291
+}
292
+
293
+// Register makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
294
+//
295
+// Registers with kind=user. For kind=guest, see RegisterGuest.
296
+func (cli *Client) Register(req *ReqRegister) (*RespRegister, *RespUserInteractive, error) {
297
+	u := cli.BuildURL("register")
298
+	return cli.register(u, req)
299
+}
300
+
301
+// RegisterGuest makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
302
+// with kind=guest.
303
+//
304
+// For kind=user, see Register.
305
+func (cli *Client) RegisterGuest(req *ReqRegister) (*RespRegister, *RespUserInteractive, error) {
306
+	query := map[string]string{
307
+		"kind": "guest",
308
+	}
309
+	u := cli.BuildURLWithQuery([]string{"register"}, query)
310
+	return cli.register(u, req)
311
+}
312
+
313
+// RegisterDummy performs m.login.dummy registration according to https://matrix.org/docs/spec/client_server/r0.2.0.html#dummy-auth
314
+//
315
+// Only a username and password need to be provided on the ReqRegister struct. Most local/developer homeservers will allow registration
316
+// this way. If the homeserver does not, an error is returned.
317
+//
318
+// This does not set credentials on the client instance. See SetCredentials() instead.
319
+//
320
+// 	res, err := cli.RegisterDummy(&gomatrix.ReqRegister{
321
+//		Username: "alice",
322
+//		Password: "wonderland",
323
+//	})
324
+//  if err != nil {
325
+// 		panic(err)
326
+// 	}
327
+// 	token := res.AccessToken
328
+func (cli *Client) RegisterDummy(req *ReqRegister) (*RespRegister, error) {
329
+	res, uia, err := cli.Register(req)
330
+	if err != nil && uia == nil {
331
+		return nil, err
332
+	}
333
+	if uia != nil && uia.HasSingleStageFlow("m.login.dummy") {
334
+		req.Auth = struct {
335
+			Type    string `json:"type"`
336
+			Session string `json:"session,omitempty"`
337
+		}{"m.login.dummy", uia.Session}
338
+		res, _, err = cli.Register(req)
339
+		if err != nil {
340
+			return nil, err
341
+		}
342
+	}
343
+	if res == nil {
344
+		return nil, fmt.Errorf("registration failed: does this server support m.login.dummy?")
345
+	}
346
+	return res, nil
347
+}
348
+
349
+// Login a user to the homeserver according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-login
350
+// This does not set credentials on this client instance. See SetCredentials() instead.
351
+func (cli *Client) Login(req *ReqLogin) (resp *RespLogin, err error) {
352
+	urlPath := cli.BuildURL("login")
353
+	err = cli.MakeRequest("POST", urlPath, req, &resp)
354
+	return
355
+}
356
+
357
+// Logout the current user. See http://matrix.org/docs/spec/client_server/r0.6.0.html#post-matrix-client-r0-logout
358
+// This does not clear the credentials from the client instance. See ClearCredentials() instead.
359
+func (cli *Client) Logout() (resp *RespLogout, err error) {
360
+	urlPath := cli.BuildURL("logout")
361
+	err = cli.MakeRequest("POST", urlPath, nil, &resp)
362
+	return
363
+}
364
+
365
+// 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
366
+// This does not clear the credentials from the client instance. See ClearCredentails() instead.
367
+func (cli *Client) LogoutAll() (resp *RespLogoutAll, err error) {
368
+	urlPath := cli.BuildURL("logout/all")
369
+	err = cli.MakeRequest("POST", urlPath, nil, &resp)
370
+	return
371
+}
372
+
373
+// 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
374
+func (cli *Client) Versions() (resp *RespVersions, err error) {
375
+	urlPath := cli.BuildBaseURL("_matrix", "client", "versions")
376
+	err = cli.MakeRequest("GET", urlPath, nil, &resp)
377
+	return
378
+}
379
+
380
+// 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
381
+func (cli *Client) PublicRooms(limit int, since string, server string) (resp *RespPublicRooms, err error) {
382
+	args := map[string]string{}
383
+
384
+	if limit != 0 {
385
+		args["limit"] = strconv.Itoa(limit)
386
+	}
387
+	if since != "" {
388
+		args["since"] = since
389
+	}
390
+	if server != "" {
391
+		args["server"] = server
392
+	}
393
+
394
+	urlPath := cli.BuildURLWithQuery([]string{"publicRooms"}, args)
395
+	err = cli.MakeRequest("GET", urlPath, nil, &resp)
396
+	return
397
+}
398
+
399
+// PublicRoomsFiltered returns a subset of PublicRooms filtered server side.
400
+// See https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-unstable-publicrooms
401
+func (cli *Client) PublicRoomsFiltered(limit int, since string, server string, filter string) (resp *RespPublicRooms, err error) {
402
+	content := map[string]string{}
403
+
404
+	if limit != 0 {
405
+		content["limit"] = strconv.Itoa(limit)
406
+	}
407
+	if since != "" {
408
+		content["since"] = since
409
+	}
410
+	if filter != "" {
411
+		content["filter"] = filter
412
+	}
413
+
414
+	var urlPath string
415
+	if server == "" {
416
+		urlPath = cli.BuildURL("publicRooms")
417
+	} else {
418
+		urlPath = cli.BuildURLWithQuery([]string{"publicRooms"}, map[string]string{
419
+			"server": server,
420
+		})
421
+	}
422
+
423
+	err = cli.MakeRequest("POST", urlPath, content, &resp)
424
+	return
425
+}
426
+
427
+// 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
428
+//
429
+// 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
430
+// be JSON encoded and used as the request body.
431
+func (cli *Client) JoinRoom(roomIDorAlias, serverName string, content interface{}) (resp *RespJoinRoom, err error) {
432
+	var urlPath string
433
+	if serverName != "" {
434
+		urlPath = cli.BuildURLWithQuery([]string{"join", roomIDorAlias}, map[string]string{
435
+			"server_name": serverName,
436
+		})
437
+	} else {
438
+		urlPath = cli.BuildURL("join", roomIDorAlias)
439
+	}
440
+	err = cli.MakeRequest("POST", urlPath, content, &resp)
441
+	return
442
+}
443
+
444
+// 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
445
+func (cli *Client) GetDisplayName(mxid string) (resp *RespUserDisplayName, err error) {
446
+	urlPath := cli.BuildURL("profile", mxid, "displayname")
447
+	err = cli.MakeRequest("GET", urlPath, nil, &resp)
448
+	return
449
+}
450
+
451
+// 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
452
+func (cli *Client) GetOwnDisplayName() (resp *RespUserDisplayName, err error) {
453
+	urlPath := cli.BuildURL("profile", cli.UserID, "displayname")
454
+	err = cli.MakeRequest("GET", urlPath, nil, &resp)
455
+	return
456
+}
457
+
458
+// SetDisplayName sets the user's profile display name. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-profile-userid-displayname
459
+func (cli *Client) SetDisplayName(displayName string) (err error) {
460
+	urlPath := cli.BuildURL("profile", cli.UserID, "displayname")
461
+	s := struct {
462
+		DisplayName string `json:"displayname"`
463
+	}{displayName}
464
+	err = cli.MakeRequest("PUT", urlPath, &s, nil)
465
+	return
466
+}
467
+
468
+// 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
469
+func (cli *Client) GetAvatarURL() (string, error) {
470
+	urlPath := cli.BuildURL("profile", cli.UserID, "avatar_url")
471
+	s := struct {
472
+		AvatarURL string `json:"avatar_url"`
473
+	}{}
474
+
475
+	err := cli.MakeRequest("GET", urlPath, nil, &s)
476
+	if err != nil {
477
+		return "", err
478
+	}
479
+
480
+	return s.AvatarURL, nil
481
+}
482
+
483
+// 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
484
+func (cli *Client) SetAvatarURL(url string) error {
485
+	urlPath := cli.BuildURL("profile", cli.UserID, "avatar_url")
486
+	s := struct {
487
+		AvatarURL string `json:"avatar_url"`
488
+	}{url}
489
+	err := cli.MakeRequest("PUT", urlPath, &s, nil)
490
+	if err != nil {
491
+		return err
492
+	}
493
+
494
+	return nil
495
+}
496
+
497
+// 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
498
+func (cli *Client) GetStatus(mxid string) (resp *RespUserStatus, err error) {
499
+	urlPath := cli.BuildURL("presence", mxid, "status")
500
+	err = cli.MakeRequest("GET", urlPath, nil, &resp)
501
+	return
502
+}
503
+
504
+// GetOwnStatus returns the user's status. See https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-presence-userid-status
505
+func (cli *Client) GetOwnStatus() (resp *RespUserStatus, err error) {
506
+	return cli.GetStatus(cli.UserID)
507
+}
508
+
509
+// SetStatus sets the user's status. See https://matrix.org/docs/spec/client_server/r0.6.0#put-matrix-client-r0-presence-userid-status
510
+func (cli *Client) SetStatus(presence, status string) (err error) {
511
+	urlPath := cli.BuildURL("presence", cli.UserID, "status")
512
+	s := struct {
513
+		Presence  string `json:"presence"`
514
+		StatusMsg string `json:"status_msg"`
515
+	}{presence, status}
516
+	err = cli.MakeRequest("PUT", urlPath, &s, nil)
517
+	return
518
+}
519
+
520
+// 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
521
+// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal.
522
+func (cli *Client) SendMessageEvent(roomID string, eventType string, contentJSON interface{}) (resp *RespSendEvent, err error) {
523
+	txnID := txnID()
524
+	urlPath := cli.BuildURL("rooms", roomID, "send", eventType, txnID)
525
+	err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp)
526
+	return
527
+}
528
+
529
+// SendStateEvent sends a state event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-state-eventtype-statekey
530
+// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal.
531
+func (cli *Client) SendStateEvent(roomID, eventType, stateKey string, contentJSON interface{}) (resp *RespSendEvent, err error) {
532
+	urlPath := cli.BuildURL("rooms", roomID, "state", eventType, stateKey)
533
+	err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp)
534
+	return
535
+}
536
+
537
+// SendText sends an m.room.message event into the given room with a msgtype of m.text
538
+// See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-text
539
+func (cli *Client) SendText(roomID, text string) (*RespSendEvent, error) {
540
+	return cli.SendMessageEvent(roomID, "m.room.message",
541
+		TextMessage{MsgType: "m.text", Body: text})
542
+}
543
+
544
+// SendFormattedText sends an m.room.message event into the given room with a msgtype of m.text, supports a subset of HTML for formatting.
545
+// See https://matrix.org/docs/spec/client_server/r0.6.0#m-text
546
+func (cli *Client) SendFormattedText(roomID, text, formattedText string) (*RespSendEvent, error) {
547
+	return cli.SendMessageEvent(roomID, "m.room.message",
548
+		TextMessage{MsgType: "m.text", Body: text, FormattedBody: formattedText, Format: "org.matrix.custom.html"})
549
+}
550
+
551
+// SendImage sends an m.room.message event into the given room with a msgtype of m.image
552
+// See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-image
553
+func (cli *Client) SendImage(roomID, body, url string) (*RespSendEvent, error) {
554
+	return cli.SendMessageEvent(roomID, "m.room.message",
555
+		ImageMessage{
556
+			MsgType: "m.image",
557
+			Body:    body,
558
+			URL:     url,
559
+		})
560
+}
561
+
562
+// SendVideo sends an m.room.message event into the given room with a msgtype of m.video
563
+// See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
564
+func (cli *Client) SendVideo(roomID, body, url string) (*RespSendEvent, error) {
565
+	return cli.SendMessageEvent(roomID, "m.room.message",
566
+		VideoMessage{
567
+			MsgType: "m.video",
568
+			Body:    body,
569
+			URL:     url,
570
+		})
571
+}
572
+
573
+// SendNotice sends an m.room.message event into the given room with a msgtype of m.notice
574
+// See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-notice
575
+func (cli *Client) SendNotice(roomID, text string) (*RespSendEvent, error) {
576
+	return cli.SendMessageEvent(roomID, "m.room.message",
577
+		TextMessage{MsgType: "m.notice", Body: text})
578
+}
579
+
580
+// 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
581
+func (cli *Client) RedactEvent(roomID, eventID string, req *ReqRedact) (resp *RespSendEvent, err error) {
582
+	txnID := txnID()
583
+	urlPath := cli.BuildURL("rooms", roomID, "redact", eventID, txnID)
584
+	err = cli.MakeRequest("PUT", urlPath, req, &resp)
585
+	return
586
+}
587
+
588
+// 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
589
+func (cli *Client) MarkRead(roomID, eventID string) error {
590
+	urlPath := cli.BuildURL("rooms", roomID, "receipt", "m.read", eventID)
591
+	return cli.MakeRequest("POST", urlPath, nil, nil)
592
+}
593
+
594
+// CreateRoom creates a new Matrix room. See https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
595
+//  resp, err := cli.CreateRoom(&gomatrix.ReqCreateRoom{
596
+//  	Preset: "public_chat",
597
+//  })
598
+//  fmt.Println("Room:", resp.RoomID)
599
+func (cli *Client) CreateRoom(req *ReqCreateRoom) (resp *RespCreateRoom, err error) {
600
+	urlPath := cli.BuildURL("createRoom")
601
+	err = cli.MakeRequest("POST", urlPath, req, &resp)
602
+	return
603
+}
604
+
605
+// LeaveRoom leaves the given room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-leave
606
+func (cli *Client) LeaveRoom(roomID string) (resp *RespLeaveRoom, err error) {
607
+	u := cli.BuildURL("rooms", roomID, "leave")
608
+	err = cli.MakeRequest("POST", u, struct{}{}, &resp)
609
+	return
610
+}
611
+
612
+// ForgetRoom forgets a room entirely. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-forget
613
+func (cli *Client) ForgetRoom(roomID string) (resp *RespForgetRoom, err error) {
614
+	u := cli.BuildURL("rooms", roomID, "forget")
615
+	err = cli.MakeRequest("POST", u, struct{}{}, &resp)
616
+	return
617
+}
618
+
619
+// 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
620
+func (cli *Client) InviteUser(roomID string, req *ReqInviteUser) (resp *RespInviteUser, err error) {
621
+	u := cli.BuildURL("rooms", roomID, "invite")
622
+	err = cli.MakeRequest("POST", u, req, &resp)
623
+	return
624
+}
625
+
626
+// 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
627
+func (cli *Client) InviteUserByThirdParty(roomID string, req *ReqInvite3PID) (resp *RespInviteUser, err error) {
628
+	u := cli.BuildURL("rooms", roomID, "invite")
629
+	err = cli.MakeRequest("POST", u, req, &resp)
630
+	return
631
+}
632
+
633
+// 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
634
+func (cli *Client) KickUser(roomID string, req *ReqKickUser) (resp *RespKickUser, err error) {
635
+	u := cli.BuildURL("rooms", roomID, "kick")
636
+	err = cli.MakeRequest("POST", u, req, &resp)
637
+	return
638
+}
639
+
640
+// 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
641
+func (cli *Client) BanUser(roomID string, req *ReqBanUser) (resp *RespBanUser, err error) {
642
+	u := cli.BuildURL("rooms", roomID, "ban")
643
+	err = cli.MakeRequest("POST", u, req, &resp)
644
+	return
645
+}
646
+
647
+// 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
648
+func (cli *Client) UnbanUser(roomID string, req *ReqUnbanUser) (resp *RespUnbanUser, err error) {
649
+	u := cli.BuildURL("rooms", roomID, "unban")
650
+	err = cli.MakeRequest("POST", u, req, &resp)
651
+	return
652
+}
653
+
654
+// UserTyping sets the typing status of the user. See https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
655
+func (cli *Client) UserTyping(roomID string, typing bool, timeout int64) (resp *RespTyping, err error) {
656
+	req := ReqTyping{Typing: typing, Timeout: timeout}
657
+	u := cli.BuildURL("rooms", roomID, "typing", cli.UserID)
658
+	err = cli.MakeRequest("PUT", u, req, &resp)
659
+	return
660
+}
661
+
662
+// StateEvent gets a single state event in a room. It will attempt to JSON unmarshal into the given "outContent" struct with
663
+// the HTTP response body, or return an error.
664
+// See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-state-eventtype-statekey
665
+func (cli *Client) StateEvent(roomID, eventType, stateKey string, outContent interface{}) (err error) {
666
+	u := cli.BuildURL("rooms", roomID, "state", eventType, stateKey)
667
+	err = cli.MakeRequest("GET", u, nil, outContent)
668
+	return
669
+}
670
+
671
+// UploadLink uploads an HTTP URL and then returns an MXC URI.
672
+func (cli *Client) UploadLink(link string) (*RespMediaUpload, error) {
673
+	res, err := cli.Client.Get(link)
674
+	if res != nil {
675
+		defer res.Body.Close()
676
+	}
677
+	if err != nil {
678
+		return nil, err
679
+	}
680
+	return cli.UploadToContentRepo(res.Body, res.Header.Get("Content-Type"), res.ContentLength)
681
+}
682
+
683
+// UploadToContentRepo uploads the given bytes to the content repository and returns an MXC URI.
684
+// See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-media-r0-upload
685
+func (cli *Client) UploadToContentRepo(content io.Reader, contentType string, contentLength int64) (*RespMediaUpload, error) {
686
+	req, err := http.NewRequest("POST", cli.BuildBaseURL("_matrix/media/r0/upload"), content)
687
+	if err != nil {
688
+		return nil, err
689
+	}
690
+	req.Header.Set("Content-Type", contentType)
691
+	req.ContentLength = contentLength
692
+	res, err := cli.Client.Do(req)
693
+	if res != nil {
694
+		defer res.Body.Close()
695
+	}
696
+	if err != nil {
697
+		return nil, err
698
+	}
699
+	if res.StatusCode != 200 {
700
+		contents, err := ioutil.ReadAll(res.Body)
701
+		if err != nil {
702
+			return nil, HTTPError{
703
+				Message: "Upload request failed - Failed to read response body: " + err.Error(),
704
+				Code:    res.StatusCode,
705
+			}
706
+		}
707
+		return nil, HTTPError{
708
+			Contents: contents,
709
+			Message:  "Upload request failed: " + string(contents),
710
+			Code:     res.StatusCode,
711
+		}
712
+	}
713
+	var m RespMediaUpload
714
+	if err := json.NewDecoder(res.Body).Decode(&m); err != nil {
715
+		return nil, err
716
+	}
717
+	return &m, nil
718
+}
719
+
720
+// JoinedMembers returns a map of joined room members. See TODO-SPEC. https://github.com/matrix-org/synapse/pull/1680
721
+//
722
+// In general, usage of this API is discouraged in favour of /sync, as calling this API can race with incoming membership changes.
723
+// This API is primarily designed for application services which may want to efficiently look up joined members in a room.
724
+func (cli *Client) JoinedMembers(roomID string) (resp *RespJoinedMembers, err error) {
725
+	u := cli.BuildURL("rooms", roomID, "joined_members")
726
+	err = cli.MakeRequest("GET", u, nil, &resp)
727
+	return
728
+}
729
+
730
+// JoinedRooms returns a list of rooms which the client is joined to. See TODO-SPEC. https://github.com/matrix-org/synapse/pull/1680
731
+//
732
+// In general, usage of this API is discouraged in favour of /sync, as calling this API can race with incoming membership changes.
733
+// This API is primarily designed for application services which may want to efficiently look up joined rooms.
734
+func (cli *Client) JoinedRooms() (resp *RespJoinedRooms, err error) {
735
+	u := cli.BuildURL("joined_rooms")
736
+	err = cli.MakeRequest("GET", u, nil, &resp)
737
+	return
738
+}
739
+
740
+// Messages returns a list of message and state events for a room. It uses
741
+// pagination query parameters to paginate history in the room.
742
+// See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages
743
+func (cli *Client) Messages(roomID, from, to string, dir rune, limit int) (resp *RespMessages, err error) {
744
+	query := map[string]string{
745
+		"from": from,
746
+		"dir":  string(dir),
747
+	}
748
+	if to != "" {
749
+		query["to"] = to
750
+	}
751
+	if limit != 0 {
752
+		query["limit"] = strconv.Itoa(limit)
753
+	}
754
+
755
+	urlPath := cli.BuildURLWithQuery([]string{"rooms", roomID, "messages"}, query)
756
+	err = cli.MakeRequest("GET", urlPath, nil, &resp)
757
+	return
758
+}
759
+
760
+// TurnServer returns turn server details and credentials for the client to use when initiating calls.
761
+// See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-voip-turnserver
762
+func (cli *Client) TurnServer() (resp *RespTurnServer, err error) {
763
+	urlPath := cli.BuildURL("voip", "turnServer")
764
+	err = cli.MakeRequest("GET", urlPath, nil, &resp)
765
+	return
766
+}
767
+
768
+func txnID() string {
769
+	return "go" + strconv.FormatInt(time.Now().UnixNano(), 10)
770
+}
771
+
772
+// NewClient creates a new Matrix Client ready for syncing
773
+func NewClient(homeserverURL, userID, accessToken string) (*Client, error) {
774
+	hsURL, err := url.Parse(homeserverURL)
775
+	if err != nil {
776
+		return nil, err
777
+	}
778
+	// By default, use an in-memory store which will never save filter ids / next batch tokens to disk.
779
+	// The client will work with this storer: it just won't remember across restarts.
780
+	// In practice, a database backend should be used.
781
+	store := NewInMemoryStore()
782
+	cli := Client{
783
+		AccessToken:   accessToken,
784
+		HomeserverURL: hsURL,
785
+		UserID:        userID,
786
+		Prefix:        "/_matrix/client/r0",
787
+		Syncer:        NewDefaultSyncer(userID, store),
788
+		Store:         store,
789
+	}
790
+	// By default, use the default HTTP client.
791
+	cli.Client = http.DefaultClient
792
+
793
+	return &cli, nil
794
+}

+ 157
- 0
vendor/github.com/matrix-org/gomatrix/events.go View File

@@ -0,0 +1,157 @@
1
+package gomatrix
2
+
3
+import (
4
+	"html"
5
+	"regexp"
6
+)
7
+
8
+// Event represents a single Matrix event.
9
+type Event struct {
10
+	StateKey    *string                `json:"state_key,omitempty"`    // The state key for the event. Only present on State Events.
11
+	Sender      string                 `json:"sender"`                 // The user ID of the sender of the event
12
+	Type        string                 `json:"type"`                   // The event type
13
+	Timestamp   int64                  `json:"origin_server_ts"`       // The unix timestamp when this message was sent by the origin server
14
+	ID          string                 `json:"event_id"`               // The unique ID of this event
15
+	RoomID      string                 `json:"room_id"`                // The room the event was sent to. May be nil (e.g. for presence)
16
+	Redacts     string                 `json:"redacts,omitempty"`      // The event ID that was redacted if a m.room.redaction event
17
+	Unsigned    map[string]interface{} `json:"unsigned"`               // The unsigned portions of the event, such as age and prev_content
18
+	Content     map[string]interface{} `json:"content"`                // The JSON content of the event.
19
+	PrevContent map[string]interface{} `json:"prev_content,omitempty"` // The JSON prev_content of the event.
20
+}
21
+
22
+// Body returns the value of the "body" key in the event content if it is
23
+// present and is a string.
24
+func (event *Event) Body() (body string, ok bool) {
25
+	value, exists := event.Content["body"]
26
+	if !exists {
27
+		return
28
+	}
29
+	body, ok = value.(string)
30
+	return
31
+}
32
+
33
+// MessageType returns the value of the "msgtype" key in the event content if
34
+// it is present and is a string.
35
+func (event *Event) MessageType() (msgtype string, ok bool) {
36
+	value, exists := event.Content["msgtype"]
37
+	if !exists {
38
+		return
39
+	}
40
+	msgtype, ok = value.(string)
41
+	return
42
+}
43
+
44
+// TextMessage is the contents of a Matrix formated message event.
45
+type TextMessage struct {
46
+	MsgType       string `json:"msgtype"`
47
+	Body          string `json:"body"`
48
+	FormattedBody string `json:"formatted_body"`
49
+	Format        string `json:"format"`
50
+}
51
+
52
+// ThumbnailInfo contains info about an thumbnail image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image
53
+type ThumbnailInfo struct {
54
+	Height   uint   `json:"h,omitempty"`
55
+	Width    uint   `json:"w,omitempty"`
56
+	Mimetype string `json:"mimetype,omitempty"`
57
+	Size     uint   `json:"size,omitempty"`
58
+}
59
+
60
+// ImageInfo contains info about an image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image
61
+type ImageInfo struct {
62
+	Height        uint          `json:"h,omitempty"`
63
+	Width         uint          `json:"w,omitempty"`
64
+	Mimetype      string        `json:"mimetype,omitempty"`
65
+	Size          uint          `json:"size,omitempty"`
66
+	ThumbnailInfo ThumbnailInfo `json:"thumbnail_info,omitempty"`
67
+	ThumbnailURL  string        `json:"thumbnail_url,omitempty"`
68
+}
69
+
70
+// VideoInfo contains info about a video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
71
+type VideoInfo struct {
72
+	Mimetype      string        `json:"mimetype,omitempty"`
73
+	ThumbnailInfo ThumbnailInfo `json:"thumbnail_info"`
74
+	ThumbnailURL  string        `json:"thumbnail_url,omitempty"`
75
+	Height        uint          `json:"h,omitempty"`
76
+	Width         uint          `json:"w,omitempty"`
77
+	Duration      uint          `json:"duration,omitempty"`
78
+	Size          uint          `json:"size,omitempty"`
79
+}
80
+
81
+// VideoMessage is an m.video  - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
82
+type VideoMessage struct {
83
+	MsgType string    `json:"msgtype"`
84
+	Body    string    `json:"body"`
85
+	URL     string    `json:"url"`
86
+	Info    VideoInfo `json:"info"`
87
+}
88
+
89
+// ImageMessage is an m.image event
90
+type ImageMessage struct {
91
+	MsgType string    `json:"msgtype"`
92
+	Body    string    `json:"body"`
93
+	URL     string    `json:"url"`
94
+	Info    ImageInfo `json:"info"`
95
+}
96
+
97
+// An HTMLMessage is the contents of a Matrix HTML formated message event.
98
+type HTMLMessage struct {
99
+	Body          string `json:"body"`
100
+	MsgType       string `json:"msgtype"`
101
+	Format        string `json:"format"`
102
+	FormattedBody string `json:"formatted_body"`
103
+}
104
+
105
+// FileInfo contains info about an file - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-file
106
+type FileInfo struct {
107
+	Mimetype string `json:"mimetype,omitempty"`
108
+	Size     uint   `json:"size,omitempty"` //filesize in bytes
109
+}
110
+
111
+// FileMessage is an m.file event - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-file
112
+type FileMessage struct {
113
+	MsgType       string    `json:"msgtype"`
114
+	Body          string    `json:"body"`
115
+	URL           string    `json:"url"`
116
+	Filename      string    `json:"filename"`
117
+	Info          FileInfo  `json:"info,omitempty"`
118
+	ThumbnailURL  string    `json:"thumbnail_url,omitempty"`
119
+	ThumbnailInfo ImageInfo `json:"thumbnail_info,omitempty"`
120
+}
121
+
122
+// LocationMessage is an m.location event - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-location
123
+type LocationMessage struct {
124
+	MsgType       string    `json:"msgtype"`
125
+	Body          string    `json:"body"`
126
+	GeoURI        string    `json:"geo_uri"`
127
+	ThumbnailURL  string    `json:"thumbnail_url,omitempty"`
128
+	ThumbnailInfo ImageInfo `json:"thumbnail_info,omitempty"`
129
+}
130
+
131
+// AudioInfo contains info about an file - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-audio
132
+type AudioInfo struct {
133
+	Mimetype string `json:"mimetype,omitempty"`
134
+	Size     uint   `json:"size,omitempty"`     //filesize in bytes
135
+	Duration uint   `json:"duration,omitempty"` //audio duration in ms
136
+}
137
+
138
+// AudioMessage is an m.audio event - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-audio
139
+type AudioMessage struct {
140
+	MsgType string    `json:"msgtype"`
141
+	Body    string    `json:"body"`
142
+	URL     string    `json:"url"`
143
+	Info    AudioInfo `json:"info,omitempty"`
144
+}
145
+
146
+var htmlRegex = regexp.MustCompile("<[^<]+?>")
147
+
148
+// GetHTMLMessage returns an HTMLMessage with the body set to a stripped version of the provided HTML, in addition
149
+// to the provided HTML.
150
+func GetHTMLMessage(msgtype, htmlText string) HTMLMessage {
151
+	return HTMLMessage{
152
+		Body:          html.UnescapeString(htmlRegex.ReplaceAllLiteralString(htmlText, "")),
153
+		MsgType:       msgtype,
154
+		Format:        "org.matrix.custom.html",
155
+		FormattedBody: htmlText,
156
+	}
157
+}

+ 90
- 0
vendor/github.com/matrix-org/gomatrix/filter.go View File

@@ -0,0 +1,90 @@
1
+// Copyright 2017 Jan Christian Grünhage
2
+//
3
+// Licensed under the Apache License, Version 2.0 (the "License");
4
+// you may not use this file except in compliance with the License.
5
+// You may obtain a copy of the License at
6
+//
7
+//     http://www.apache.org/licenses/LICENSE-2.0
8
+//
9
+// Unless required by applicable law or agreed to in writing, software
10
+// distributed under the License is distributed on an "AS IS" BASIS,
11
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+// See the License for the specific language governing permissions and
13
+// limitations under the License.
14
+
15
+package gomatrix
16
+
17
+import "errors"
18
+
19
+//Filter is used by clients to specify how the server should filter responses to e.g. sync requests
20
+//Specified by: https://matrix.org/docs/spec/client_server/r0.2.0.html#filtering
21
+type Filter struct {
22
+	AccountData FilterPart `json:"account_data,omitempty"`
23
+	EventFields []string   `json:"event_fields,omitempty"`
24
+	EventFormat string     `json:"event_format,omitempty"`
25
+	Presence    FilterPart `json:"presence,omitempty"`
26
+	Room        RoomFilter `json:"room,omitempty"`
27
+}
28
+
29
+// RoomFilter is used to define filtering rules for room events
30
+type RoomFilter struct {
31
+	AccountData  FilterPart `json:"account_data,omitempty"`
32
+	Ephemeral    FilterPart `json:"ephemeral,omitempty"`
33
+	IncludeLeave bool       `json:"include_leave,omitempty"`
34
+	NotRooms     []string   `json:"not_rooms,omitempty"`
35
+	Rooms        []string   `json:"rooms,omitempty"`
36
+	State        FilterPart `json:"state,omitempty"`
37
+	Timeline     FilterPart `json:"timeline,omitempty"`
38
+}
39
+
40
+// FilterPart is used to define filtering rules for specific categories of events
41
+type FilterPart struct {
42
+	NotRooms    []string `json:"not_rooms,omitempty"`
43
+	Rooms       []string `json:"rooms,omitempty"`
44
+	Limit       int      `json:"limit,omitempty"`
45
+	NotSenders  []string `json:"not_senders,omitempty"`
46
+	NotTypes    []string `json:"not_types,omitempty"`
47
+	Senders     []string `json:"senders,omitempty"`
48
+	Types       []string `json:"types,omitempty"`
49
+	ContainsURL *bool    `json:"contains_url,omitempty"`
50
+}
51
+
52
+// Validate checks if the filter contains valid property values
53
+func (filter *Filter) Validate() error {
54
+	if filter.EventFormat != "client" && filter.EventFormat != "federation" {
55
+		return errors.New("Bad event_format value. Must be one of [\"client\", \"federation\"]")
56
+	}
57
+	return nil
58
+}
59
+
60
+// DefaultFilter returns the default filter used by the Matrix server if no filter is provided in the request
61
+func DefaultFilter() Filter {
62
+	return Filter{
63
+		AccountData: DefaultFilterPart(),
64
+		EventFields: nil,
65
+		EventFormat: "client",
66
+		Presence:    DefaultFilterPart(),
67
+		Room: RoomFilter{
68
+			AccountData:  DefaultFilterPart(),
69
+			Ephemeral:    DefaultFilterPart(),
70
+			IncludeLeave: false,
71
+			NotRooms:     nil,
72
+			Rooms:        nil,
73
+			State:        DefaultFilterPart(),
74
+			Timeline:     DefaultFilterPart(),
75
+		},
76
+	}
77
+}
78
+
79
+// DefaultFilterPart returns the default filter part used by the Matrix server if no filter is provided in the request
80
+func DefaultFilterPart() FilterPart {
81
+	return FilterPart{
82
+		NotRooms:   nil,
83
+		Rooms:      nil,
84
+		Limit:      20,
85
+		NotSenders: nil,
86
+		NotTypes:   nil,
87
+		Senders:    nil,
88
+		Types:      nil,
89
+	}
90
+}

+ 3
- 0
vendor/github.com/matrix-org/gomatrix/go.mod View File

@@ -0,0 +1,3 @@
1
+module github.com/matrix-org/gomatrix
2
+
3
+go 1.12

+ 69
- 0
vendor/github.com/matrix-org/gomatrix/identifier.go View File

@@ -0,0 +1,69 @@
1
+package gomatrix
2
+
3
+// Identifier is the interface for https://matrix.org/docs/spec/client_server/r0.6.0#identifier-types
4
+type Identifier interface {
5
+	// Returns the identifier type
6
+	// https://matrix.org/docs/spec/client_server/r0.6.0#identifier-types
7
+	Type() string
8
+}
9
+
10
+// UserIdentifier is the Identifier for https://matrix.org/docs/spec/client_server/r0.6.0#matrix-user-id
11
+type UserIdentifier struct {
12
+	IDType string `json:"type"` // Set by NewUserIdentifer
13
+	User   string `json:"user"`
14
+}
15
+
16
+// Type implements the Identifier interface
17
+func (i UserIdentifier) Type() string {
18
+	return "m.id.user"
19
+}
20
+
21
+// NewUserIdentifier creates a new UserIdentifier with IDType set to "m.id.user"
22
+func NewUserIdentifier(user string) UserIdentifier {
23
+	return UserIdentifier{
24
+		IDType: "m.id.user",
25
+		User:   user,
26
+	}
27
+}
28
+
29
+// ThirdpartyIdentifier is the Identifier for https://matrix.org/docs/spec/client_server/r0.6.0#third-party-id
30
+type ThirdpartyIdentifier struct {
31
+	IDType  string `json:"type"` // Set by NewThirdpartyIdentifier
32
+	Medium  string `json:"medium"`
33
+	Address string `json:"address"`
34
+}
35
+
36
+// Type implements the Identifier interface
37
+func (i ThirdpartyIdentifier) Type() string {
38
+	return "m.id.thirdparty"
39
+}
40
+
41
+// NewThirdpartyIdentifier creates a new UserIdentifier with IDType set to "m.id.user"
42
+func NewThirdpartyIdentifier(medium, address string) ThirdpartyIdentifier {
43
+	return ThirdpartyIdentifier{
44
+		IDType:  "m.id.thirdparty",
45
+		Medium:  medium,
46
+		Address: address,
47
+	}
48
+}
49
+
50
+// PhoneIdentifier is the Identifier for https://matrix.org/docs/spec/client_server/r0.6.0#phone-number
51
+type PhoneIdentifier struct {
52
+	IDType  string `json:"type"` // Set by NewPhoneIdentifier
53
+	Country string `json:"country"`
54
+	Phone   string `json:"phone"`
55
+}
56
+
57
+// Type implements the Identifier interface
58
+func (i PhoneIdentifier) Type() string {
59
+	return "m.id.phone"
60
+}
61
+
62
+// NewPhoneIdentifier creates a new UserIdentifier with IDType set to "m.id.user"
63
+func NewPhoneIdentifier(country, phone string) PhoneIdentifier {
64
+	return PhoneIdentifier{
65
+		IDType:  "m.id.phone",
66
+		Country: country,
67
+		Phone:   phone,
68
+	}
69
+}

+ 79
- 0
vendor/github.com/matrix-org/gomatrix/requests.go View File

@@ -0,0 +1,79 @@
1
+package gomatrix
2
+
3
+// ReqRegister is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
4
+type ReqRegister struct {
5
+	Username                 string      `json:"username,omitempty"`
6
+	BindEmail                bool        `json:"bind_email,omitempty"`
7
+	Password                 string      `json:"password,omitempty"`
8
+	DeviceID                 string      `json:"device_id,omitempty"`
9
+	InitialDeviceDisplayName string      `json:"initial_device_display_name"`
10
+	Auth                     interface{} `json:"auth,omitempty"`
11
+}
12
+
13
+// ReqLogin is the JSON request for http://matrix.org/docs/spec/client_server/r0.6.0.html#post-matrix-client-r0-login
14
+type ReqLogin struct {
15
+	Type                     string     `json:"type"`
16
+	Identifier               Identifier `json:"identifier,omitempty"`
17
+	Password                 string     `json:"password,omitempty"`
18
+	Medium                   string     `json:"medium,omitempty"`
19
+	User                     string     `json:"user,omitempty"`
20
+	Address                  string     `json:"address,omitempty"`
21
+	Token                    string     `json:"token,omitempty"`
22
+	DeviceID                 string     `json:"device_id,omitempty"`
23
+	InitialDeviceDisplayName string     `json:"initial_device_display_name,omitempty"`
24
+}
25
+
26
+// ReqCreateRoom is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
27
+type ReqCreateRoom struct {
28
+	Visibility      string                 `json:"visibility,omitempty"`
29
+	RoomAliasName   string                 `json:"room_alias_name,omitempty"`
30
+	Name            string                 `json:"name,omitempty"`
31
+	Topic           string                 `json:"topic,omitempty"`
32
+	Invite          []string               `json:"invite,omitempty"`
33
+	Invite3PID      []ReqInvite3PID        `json:"invite_3pid,omitempty"`
34
+	CreationContent map[string]interface{} `json:"creation_content,omitempty"`
35
+	InitialState    []Event                `json:"initial_state,omitempty"`
36
+	Preset          string                 `json:"preset,omitempty"`
37
+	IsDirect        bool                   `json:"is_direct,omitempty"`
38
+}
39
+
40
+// ReqRedact is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid
41
+type ReqRedact struct {
42
+	Reason string `json:"reason,omitempty"`
43
+}
44
+
45
+// ReqInvite3PID is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#id57
46
+// It is also a JSON object used in https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
47
+type ReqInvite3PID struct {
48
+	IDServer string `json:"id_server"`
49
+	Medium   string `json:"medium"`
50
+	Address  string `json:"address"`
51
+}
52
+
53
+// ReqInviteUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-invite
54
+type ReqInviteUser struct {
55
+	UserID string `json:"user_id"`
56
+}
57
+
58
+// ReqKickUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-kick
59
+type ReqKickUser struct {
60
+	Reason string `json:"reason,omitempty"`
61
+	UserID string `json:"user_id"`
62
+}
63
+
64
+// ReqBanUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-ban
65
+type ReqBanUser struct {
66
+	Reason string `json:"reason,omitempty"`
67
+	UserID string `json:"user_id"`
68
+}
69
+
70
+// ReqUnbanUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban
71
+type ReqUnbanUser struct {
72
+	UserID string `json:"user_id"`
73
+}
74
+
75
+// ReqTyping is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
76
+type ReqTyping struct {
77
+	Typing  bool  `json:"typing"`
78
+	Timeout int64 `json:"timeout"`
79
+}

+ 210
- 0
vendor/github.com/matrix-org/gomatrix/responses.go View File

@@ -0,0 +1,210 @@
1
+package gomatrix
2
+
3
+// RespError is the standard JSON error response from Homeservers. It also implements the Golang "error" interface.
4
+// See http://matrix.org/docs/spec/client_server/r0.2.0.html#api-standards
5
+type RespError struct {
6
+	ErrCode string `json:"errcode"`
7
+	Err     string `json:"error"`
8
+}
9
+
10
+// Error returns the errcode and error message.
11
+func (e RespError) Error() string {
12
+	return e.ErrCode + ": " + e.Err
13
+}
14
+
15
+// RespCreateFilter is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-user-userid-filter
16
+type RespCreateFilter struct {
17
+	FilterID string `json:"filter_id"`
18
+}
19
+
20
+// RespVersions is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-versions
21
+type RespVersions struct {
22
+	Versions []string `json:"versions"`
23
+}
24
+
25
+// RespPublicRooms is the JSON response for http://matrix.org/speculator/spec/HEAD/client_server/unstable.html#get-matrix-client-unstable-publicrooms
26
+type RespPublicRooms struct {
27
+	TotalRoomCountEstimate int          `json:"total_room_count_estimate"`
28
+	PrevBatch              string       `json:"prev_batch"`
29
+	NextBatch              string       `json:"next_batch"`
30
+	Chunk                  []PublicRoom `json:"chunk"`
31
+}
32
+
33
+// RespJoinRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-join
34
+type RespJoinRoom struct {
35
+	RoomID string `json:"room_id"`
36
+}
37
+
38
+// RespLeaveRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-leave
39
+type RespLeaveRoom struct{}
40
+
41
+// RespForgetRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-forget
42
+type RespForgetRoom struct{}
43
+
44
+// RespInviteUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-invite
45
+type RespInviteUser struct{}
46
+
47
+// RespKickUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-kick
48
+type RespKickUser struct{}
49
+
50
+// RespBanUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-ban
51
+type RespBanUser struct{}
52
+
53
+// RespUnbanUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban
54
+type RespUnbanUser struct{}
55
+
56
+// RespTyping is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
57
+type RespTyping struct{}
58
+
59
+// RespJoinedRooms is the JSON response for TODO-SPEC https://github.com/matrix-org/synapse/pull/1680
60
+type RespJoinedRooms struct {
61
+	JoinedRooms []string `json:"joined_rooms"`
62
+}
63
+
64
+// RespJoinedMembers is the JSON response for TODO-SPEC https://github.com/matrix-org/synapse/pull/1680
65
+type RespJoinedMembers struct {
66
+	Joined map[string]struct {
67
+		DisplayName *string `json:"display_name"`
68
+		AvatarURL   *string `json:"avatar_url"`
69
+	} `json:"joined"`
70
+}
71
+
72
+// RespMessages is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages
73
+type RespMessages struct {
74
+	Start string  `json:"start"`
75
+	Chunk []Event `json:"chunk"`
76
+	End   string  `json:"end"`
77
+}
78
+
79
+// RespSendEvent is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid
80
+type RespSendEvent struct {
81
+	EventID string `json:"event_id"`
82
+}
83
+
84
+// RespMediaUpload is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-media-r0-upload
85
+type RespMediaUpload struct {
86
+	ContentURI string `json:"content_uri"`
87
+}
88
+
89
+// RespUserInteractive is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#user-interactive-authentication-api
90
+type RespUserInteractive struct {
91
+	Flows []struct {
92
+		Stages []string `json:"stages"`
93
+	} `json:"flows"`
94
+	Params    map[string]interface{} `json:"params"`
95
+	Session   string                 `json:"session"`
96
+	Completed []string               `json:"completed"`
97
+	ErrCode   string                 `json:"errcode"`
98
+	Error     string                 `json:"error"`
99
+}
100
+
101
+// HasSingleStageFlow returns true if there exists at least 1 Flow with a single stage of stageName.
102
+func (r RespUserInteractive) HasSingleStageFlow(stageName string) bool {
103
+	for _, f := range r.Flows {
104
+		if len(f.Stages) == 1 && f.Stages[0] == stageName {
105
+			return true
106
+		}
107
+	}
108
+	return false
109
+}
110
+
111
+// RespUserDisplayName is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname
112
+type RespUserDisplayName struct {
113
+	DisplayName string `json:"displayname"`
114
+}
115
+
116
+// RespUserStatus is the JSON response for https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-presence-userid-status
117
+type RespUserStatus struct {
118
+	Presence        string `json:"presence"`
119
+	StatusMsg       string `json:"status_msg"`
120
+	LastActiveAgo   int    `json:"last_active_ago"`
121
+	CurrentlyActive bool   `json:"currently_active"`
122
+}
123
+
124
+// RespRegister is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
125
+type RespRegister struct {
126
+	AccessToken  string `json:"access_token"`
127
+	DeviceID     string `json:"device_id"`
128
+	HomeServer   string `json:"home_server"`
129
+	RefreshToken string `json:"refresh_token"`
130
+	UserID       string `json:"user_id"`
131
+}
132
+
133
+// RespLogin is the JSON response for http://matrix.org/docs/spec/client_server/r0.6.0.html#post-matrix-client-r0-login
134
+type RespLogin struct {
135
+	AccessToken string               `json:"access_token"`
136
+	DeviceID    string               `json:"device_id"`
137
+	HomeServer  string               `json:"home_server"`
138
+	UserID      string               `json:"user_id"`
139
+	WellKnown   DiscoveryInformation `json:"well_known"`
140
+}
141
+
142
+// 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
143
+type DiscoveryInformation struct {
144
+	Homeserver struct {
145
+		BaseURL string `json:"base_url"`
146
+	} `json:"m.homeserver"`
147
+	IdentityServer struct {
148
+		BaseURL string `json:"base_url"`
149
+	} `json:"m.identitiy_server"`
150
+}
151
+
152
+// RespLogout is the JSON response for http://matrix.org/docs/spec/client_server/r0.6.0.html#post-matrix-client-r0-logout
153
+type RespLogout struct{}
154
+
155
+// RespLogoutAll is the JSON response for https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-logout-all
156
+type RespLogoutAll struct{}
157
+
158
+// RespCreateRoom is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
159
+type RespCreateRoom struct {
160
+	RoomID string `json:"room_id"`
161
+}
162
+
163
+// RespSync is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-sync
164
+type RespSync struct {
165
+	NextBatch   string `json:"next_batch"`
166
+	AccountData struct {
167
+		Events []Event `json:"events"`
168
+	} `json:"account_data"`
169
+	Presence struct {
170
+		Events []Event `json:"events"`
171
+	} `json:"presence"`
172
+	Rooms struct {
173
+		Leave map[string]struct {
174
+			State struct {
175
+				Events []Event `json:"events"`
176
+			} `json:"state"`
177
+			Timeline struct {
178
+				Events    []Event `json:"events"`
179
+				Limited   bool    `json:"limited"`
180
+				PrevBatch string  `json:"prev_batch"`
181
+			} `json:"timeline"`
182
+		} `json:"leave"`
183
+		Join map[string]struct {
184
+			State struct {
185
+				Events []Event `json:"events"`
186
+			} `json:"state"`
187
+			Timeline struct {
188
+				Events    []Event `json:"events"`
189
+				Limited   bool    `json:"limited"`
190
+				PrevBatch string  `json:"prev_batch"`
191
+			} `json:"timeline"`
192
+			Ephemeral struct {
193
+				Events []Event `json:"events"`
194
+			} `json:"ephemeral"`
195
+		} `json:"join"`
196
+		Invite map[string]struct {
197
+			State struct {
198
+				Events []Event
199
+			} `json:"invite_state"`
200
+		} `json:"invite"`
201
+	} `json:"rooms"`
202
+}
203
+
204
+// RespTurnServer is the JSON response from a Turn Server
205
+type RespTurnServer struct {
206
+	Username string   `json:"username"`
207
+	Password string   `json:"password"`
208
+	TTL      int      `json:"ttl"`
209
+	URIs     []string `json:"uris"`
210
+}

+ 63
- 0
vendor/github.com/matrix-org/gomatrix/room.go View File

@@ -0,0 +1,63 @@
1
+package gomatrix
2
+
3
+// Room represents a single Matrix room.
4
+type Room struct {
5
+	ID    string
6
+	State map[string]map[string]*Event
7
+}
8
+
9
+// PublicRoom represents the information about a public room obtainable from the room directory
10
+type PublicRoom struct {
11
+	CanonicalAlias   string   `json:"canonical_alias"`
12
+	Name             string   `json:"name"`
13
+	WorldReadable    bool     `json:"world_readable"`
14
+	Topic            string   `json:"topic"`
15
+	NumJoinedMembers int      `json:"num_joined_members"`
16
+	AvatarURL        string   `json:"avatar_url"`
17
+	RoomID           string   `json:"room_id"`
18
+	GuestCanJoin     bool     `json:"guest_can_join"`
19
+	Aliases          []string `json:"aliases"`
20
+}
21
+
22
+// UpdateState updates the room's current state with the given Event. This will clobber events based
23
+// on the type/state_key combination.
24
+func (room Room) UpdateState(event *Event) {
25
+	_, exists := room.State[event.Type]
26
+	if !exists {
27
+		room.State[event.Type] = make(map[string]*Event)
28
+	}
29
+	room.State[event.Type][*event.StateKey] = event
30
+}
31
+
32
+// GetStateEvent returns the state event for the given type/state_key combo, or nil.
33
+func (room Room) GetStateEvent(eventType string, stateKey string) *Event {
34
+	stateEventMap := room.State[eventType]
35
+	event := stateEventMap[stateKey]
36
+	return event
37
+}
38
+
39
+// GetMembershipState returns the membership state of the given user ID in this room. If there is
40
+// no entry for this member, 'leave' is returned for consistency with left users.
41
+func (room Room) GetMembershipState(userID string) string {
42
+	state := "leave"
43
+	event := room.GetStateEvent("m.room.member", userID)
44
+	if event != nil {
45
+		membershipState, found := event.Content["membership"]
46
+		if found {
47
+			mState, isString := membershipState.(string)
48
+			if isString {
49
+				state = mState
50
+			}
51
+		}
52
+	}
53
+	return state
54
+}
55
+
56
+// NewRoom creates a new Room with the given ID
57
+func NewRoom(roomID string) *Room {
58
+	// Init the State map and return a pointer to the Room
59
+	return &Room{
60
+		ID:    roomID,
61
+		State: make(map[string]map[string]*Event),
62
+	}
63
+}

+ 65
- 0
vendor/github.com/matrix-org/gomatrix/store.go View File

@@ -0,0 +1,65 @@
1
+package gomatrix
2
+
3
+// Storer is an interface which must be satisfied to store client data.
4
+//
5
+// You can either write a struct which persists this data to disk, or you can use the
6
+// provided "InMemoryStore" which just keeps data around in-memory which is lost on
7
+// restarts.
8
+type Storer interface {
9
+	SaveFilterID(userID, filterID string)
10
+	LoadFilterID(userID string) string
11
+	SaveNextBatch(userID, nextBatchToken string)
12
+	LoadNextBatch(userID string) string
13
+	SaveRoom(room *Room)
14
+	LoadRoom(roomID string) *Room
15
+}
16
+
17
+// InMemoryStore implements the Storer interface.
18
+//
19
+// Everything is persisted in-memory as maps. It is not safe to load/save filter IDs
20
+// or next batch tokens on any goroutine other than the syncing goroutine: the one
21
+// which called Client.Sync().
22
+type InMemoryStore struct {
23
+	Filters   map[string]string
24
+	NextBatch map[string]string
25
+	Rooms     map[string]*Room
26
+}
27
+
28
+// SaveFilterID to memory.
29
+func (s *InMemoryStore) SaveFilterID(userID, filterID string) {
30
+	s.Filters[userID] = filterID
31
+}
32
+
33
+// LoadFilterID from memory.
34
+func (s *InMemoryStore) LoadFilterID(userID string) string {
35
+	return s.Filters[userID]
36
+}
37
+
38
+// SaveNextBatch to memory.
39
+func (s *InMemoryStore) SaveNextBatch(userID, nextBatchToken string) {
40
+	s.NextBatch[userID] = nextBatchToken
41
+}
42
+
43
+// LoadNextBatch from memory.
44
+func (s *InMemoryStore) LoadNextBatch(userID string) string {
45
+	return s.NextBatch[userID]
46
+}
47
+
48
+// SaveRoom to memory.
49
+func (s *InMemoryStore) SaveRoom(room *Room) {
50
+	s.Rooms[room.ID] = room
51
+}
52
+
53
+// LoadRoom from memory.
54
+func (s *InMemoryStore) LoadRoom(roomID string) *Room {
55
+	return s.Rooms[roomID]
56
+}
57
+
58
+// NewInMemoryStore constructs a new InMemoryStore.
59
+func NewInMemoryStore() *InMemoryStore {
60
+	return &InMemoryStore{
61
+		Filters:   make(map[string]string),
62
+		NextBatch: make(map[string]string),
63
+		Rooms:     make(map[string]*Room),
64
+	}
65
+}

+ 168
- 0
vendor/github.com/matrix-org/gomatrix/sync.go View File

@@ -0,0 +1,168 @@
1
+package gomatrix
2
+
3
+import (
4
+	"encoding/json"
5
+	"fmt"
6
+	"runtime/debug"
7
+	"time"
8
+)
9
+
10
+// Syncer represents an interface that must be satisfied in order to do /sync requests on a client.
11
+type Syncer interface {
12
+	// Process the /sync response. The since parameter is the since= value that was used to produce the response.
13
+	// This is useful for detecting the very first sync (since=""). If an error is return, Syncing will be stopped
14
+	// permanently.
15
+	ProcessResponse(resp *RespSync, since string) error
16
+	// OnFailedSync returns either the time to wait before retrying or an error to stop syncing permanently.
17
+	OnFailedSync(res *RespSync, err error) (time.Duration, error)
18
+	// GetFilterJSON for the given user ID. NOT the filter ID.
19
+	GetFilterJSON(userID string) json.RawMessage
20
+}
21
+
22
+// DefaultSyncer is the default syncing implementation. You can either write your own syncer, or selectively
23
+// replace parts of this default syncer (e.g. the ProcessResponse method). The default syncer uses the observer
24
+// pattern to notify callers about incoming events. See DefaultSyncer.OnEventType for more information.
25
+type DefaultSyncer struct {
26
+	UserID    string
27
+	Store     Storer
28
+	listeners map[string][]OnEventListener // event type to listeners array
29
+}
30
+
31
+// OnEventListener can be used with DefaultSyncer.OnEventType to be informed of incoming events.
32
+type OnEventListener func(*Event)
33
+
34
+// NewDefaultSyncer returns an instantiated DefaultSyncer
35
+func NewDefaultSyncer(userID string, store Storer) *DefaultSyncer {
36
+	return &DefaultSyncer{
37
+		UserID:    userID,
38
+		Store:     store,
39
+		listeners: make(map[string][]OnEventListener),
40
+	}
41
+}
42
+
43
+// ProcessResponse processes the /sync response in a way suitable for bots. "Suitable for bots" means a stream of
44
+// unrepeating events. Returns a fatal error if a listener panics.
45
+func (s *DefaultSyncer) ProcessResponse(res *RespSync, since string) (err error) {
46
+	if !s.shouldProcessResponse(res, since) {
47
+		return
48
+	}
49
+
50
+	defer func() {
51
+		if r := recover(); r != nil {
52
+			err = fmt.Errorf("ProcessResponse panicked! userID=%s since=%s panic=%s\n%s", s.UserID, since, r, debug.Stack())
53
+		}
54
+	}()
55
+
56
+	for roomID, roomData := range res.Rooms.Join {
57
+		room := s.getOrCreateRoom(roomID)
58
+		for _, event := range roomData.State.Events {
59
+			event.RoomID = roomID
60
+			room.UpdateState(&event)
61
+			s.notifyListeners(&event)
62
+		}
63
+		for _, event := range roomData.Timeline.Events {
64
+			event.RoomID = roomID
65
+			s.notifyListeners(&event)
66
+		}
67
+		for _, event := range roomData.Ephemeral.Events {
68
+			event.RoomID = roomID
69
+			s.notifyListeners(&event)
70
+		}
71
+	}
72
+	for roomID, roomData := range res.Rooms.Invite {
73
+		room := s.getOrCreateRoom(roomID)
74
+		for _, event := range roomData.State.Events {
75
+			event.RoomID = roomID
76
+			room.UpdateState(&event)
77
+			s.notifyListeners(&event)
78
+		}
79
+	}
80
+	for roomID, roomData := range res.Rooms.Leave {
81
+		room := s.getOrCreateRoom(roomID)
82
+		for _, event := range roomData.Timeline.Events {
83
+			if event.StateKey != nil {
84
+				event.RoomID = roomID
85
+				room.UpdateState(&event)
86
+				s.notifyListeners(&event)
87
+			}
88
+		}
89
+	}
90
+	return
91
+}
92
+
93
+// OnEventType allows callers to be notified when there are new events for the given event type.
94
+// There are no duplicate checks.
95
+func (s *DefaultSyncer) OnEventType(eventType string, callback OnEventListener) {
96
+	_, exists := s.listeners[eventType]
97
+	if !exists {
98
+		s.listeners[eventType] = []OnEventListener{}
99
+	}
100
+	s.listeners[eventType] = append(s.listeners[eventType], callback)
101
+}
102
+
103
+// shouldProcessResponse returns true if the response should be processed. May modify the response to remove
104
+// stuff that shouldn't be processed.
105
+func (s *DefaultSyncer) shouldProcessResponse(resp *RespSync, since string) bool {
106
+	if since == "" {
107
+		return false
108
+	}
109
+	// This is a horrible hack because /sync will return the most recent messages for a room
110
+	// as soon as you /join it. We do NOT want to process those events in that particular room
111
+	// because they may have already been processed (if you toggle the bot in/out of the room).
112
+	//
113
+	// Work around this by inspecting each room's timeline and seeing if an m.room.member event for us
114
+	// exists and is "join" and then discard processing that room entirely if so.
115
+	// TODO: We probably want to process messages from after the last join event in the timeline.
116
+	for roomID, roomData := range resp.Rooms.Join {
117
+		for i := len(roomData.Timeline.Events) - 1; i >= 0; i-- {
118
+			e := roomData.Timeline.Events[i]
119
+			if e.Type == "m.room.member" && e.StateKey != nil && *e.StateKey == s.UserID {
120
+				m := e.Content["membership"]
121
+				mship, ok := m.(string)
122
+				if !ok {
123
+					continue
124
+				}
125
+				if mship == "join" {
126
+					_, ok := resp.Rooms.Join[roomID]
127
+					if !ok {
128
+						continue
129
+					}
130
+					delete(resp.Rooms.Join, roomID)   // don't re-process messages
131
+					delete(resp.Rooms.Invite, roomID) // don't re-process invites
132
+					break
133
+				}
134
+			}
135
+		}
136
+	}
137
+	return true
138
+}
139
+
140
+// getOrCreateRoom must only be called by the Sync() goroutine which calls ProcessResponse()
141
+func (s *DefaultSyncer) getOrCreateRoom(roomID string) *Room {
142
+	room := s.Store.LoadRoom(roomID)
143
+	if room == nil { // create a new Room
144
+		room = NewRoom(roomID)
145
+		s.Store.SaveRoom(room)
146
+	}
147
+	return room
148
+}
149
+
150
+func (s *DefaultSyncer) notifyListeners(event *Event) {
151
+	listeners, exists := s.listeners[event.Type]
152
+	if !exists {
153
+		return
154
+	}
155
+	for _, fn := range listeners {
156
+		fn(event)
157
+	}
158
+}
159
+
160
+// OnFailedSync always returns a 10 second wait period between failed /syncs, never a fatal error.
161
+func (s *DefaultSyncer) OnFailedSync(res *RespSync, err error) (time.Duration, error) {
162
+	return 10 * time.Second, nil
163
+}
164
+
165
+// GetFilterJSON returns a filter with a timeline limit of 50.
166
+func (s *DefaultSyncer) GetFilterJSON(userID string) json.RawMessage {
167
+	return json.RawMessage(`{"room":{"timeline":{"limit":50}}}`)
168
+}

+ 26
- 0
vendor/github.com/matrix-org/gomatrix/tags.go View File

@@ -0,0 +1,26 @@
1
+// Copyright 2019 Sumukha PK
2
+//
3
+// Licensed under the Apache License, Version 2.0 (the "License");
4
+// you may not use this file except in compliance with the License.
5
+// You may obtain a copy of the License at
6
+//
7
+//     http://www.apache.org/licenses/LICENSE-2.0
8
+//
9
+// Unless required by applicable law or agreed to in writing, software
10
+// distributed under the License is distributed on an "AS IS" BASIS,
11
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+// See the License for the specific language governing permissions and
13
+// limitations under the License.
14
+
15
+package gomatrix
16
+
17
+// TagContent contains the data for an m.tag message type
18
+// https://matrix.org/docs/spec/client_server/r0.4.0.html#m-tag
19
+type TagContent struct {
20
+	Tags map[string]TagProperties `json:"tags"`
21
+}
22
+
23
+// TagProperties contains the properties of a Tag
24
+type TagProperties struct {
25
+	Order float32 `json:"order,omitempty"` // Empty values must be neglected
26
+}

+ 130
- 0
vendor/github.com/matrix-org/gomatrix/userids.go View File

@@ -0,0 +1,130 @@
1
+package gomatrix
2
+
3
+import (
4
+	"bytes"
5
+	"encoding/hex"
6
+	"fmt"
7
+	"strings"
8
+)
9
+
10
+const lowerhex = "0123456789abcdef"
11
+
12
+// encode the given byte using quoted-printable encoding (e.g "=2f")
13
+// and writes it to the buffer
14
+// See https://golang.org/src/mime/quotedprintable/writer.go
15
+func encode(buf *bytes.Buffer, b byte) {
16
+	buf.WriteByte('=')
17
+	buf.WriteByte(lowerhex[b>>4])
18
+	buf.WriteByte(lowerhex[b&0x0f])
19
+}
20
+
21
+// escape the given alpha character and writes it to the buffer
22
+func escape(buf *bytes.Buffer, b byte) {
23
+	buf.WriteByte('_')
24
+	if b == '_' {
25
+		buf.WriteByte('_') // another _
26
+	} else {
27
+		buf.WriteByte(b + 0x20) // ASCII shift A-Z to a-z
28
+	}
29
+}
30
+
31
+func shouldEncode(b byte) bool {
32
+	return b != '-' && b != '.' && b != '_' && !(b >= '0' && b <= '9') && !(b >= 'a' && b <= 'z') && !(b >= 'A' && b <= 'Z')
33
+}
34
+
35
+func shouldEscape(b byte) bool {
36
+	return (b >= 'A' && b <= 'Z') || b == '_'
37
+}
38
+
39
+func isValidByte(b byte) bool {
40
+	return isValidEscapedChar(b) || (b >= '0' && b <= '9') || b == '.' || b == '=' || b == '-'
41
+}
42
+
43
+func isValidEscapedChar(b byte) bool {
44
+	return b == '_' || (b >= 'a' && b <= 'z')
45
+}
46
+
47
+// EncodeUserLocalpart encodes the given string into Matrix-compliant user ID localpart form.
48
+// See http://matrix.org/docs/spec/intro.html#mapping-from-other-character-sets
49
+//
50
+// This returns a string with only the characters "a-z0-9._=-". The uppercase range A-Z
51
+// are encoded using leading underscores ("_"). Characters outside the aforementioned ranges
52
+// (including literal underscores ("_") and equals ("=")) are encoded as UTF8 code points (NOT NCRs)
53
+// and converted to lower-case hex with a leading "=". For example:
54
+//   Alph@Bet_50up  => _alph=40_bet=5f50up
55
+func EncodeUserLocalpart(str string) string {
56
+	strBytes := []byte(str)
57
+	var outputBuffer bytes.Buffer
58
+	for _, b := range strBytes {
59
+		if shouldEncode(b) {
60
+			encode(&outputBuffer, b)
61
+		} else if shouldEscape(b) {
62
+			escape(&outputBuffer, b)
63
+		} else {
64
+			outputBuffer.WriteByte(b)
65
+		}
66
+	}
67
+	return outputBuffer.String()
68
+}
69
+
70
+// DecodeUserLocalpart decodes the given string back into the original input string.
71
+// Returns an error if the given string is not a valid user ID localpart encoding.
72
+// See http://matrix.org/docs/spec/intro.html#mapping-from-other-character-sets
73
+//
74
+// This decodes quoted-printable bytes back into UTF8, and unescapes casing. For
75
+// example:
76
+//  _alph=40_bet=5f50up  =>  Alph@Bet_50up
77
+// Returns an error if the input string contains characters outside the
78
+// range "a-z0-9._=-", has an invalid quote-printable byte (e.g. not hex), or has
79
+// an invalid _ escaped byte (e.g. "_5").
80
+func DecodeUserLocalpart(str string) (string, error) {
81
+	strBytes := []byte(str)
82
+	var outputBuffer bytes.Buffer
83
+	for i := 0; i < len(strBytes); i++ {
84
+		b := strBytes[i]
85
+		if !isValidByte(b) {
86
+			return "", fmt.Errorf("Byte pos %d: Invalid byte", i)
87
+		}
88
+
89
+		if b == '_' { // next byte is a-z and should be upper-case or is another _ and should be a literal _
90
+			if i+1 >= len(strBytes) {
91
+				return "", fmt.Errorf("Byte pos %d: expected _[a-z_] encoding but ran out of string", i)
92
+			}
93
+			if !isValidEscapedChar(strBytes[i+1]) { // invalid escaping
94
+				return "", fmt.Errorf("Byte pos %d: expected _[a-z_] encoding", i)
95
+			}
96
+			if strBytes[i+1] == '_' {
97
+				outputBuffer.WriteByte('_')
98
+			} else {
99
+				outputBuffer.WriteByte(strBytes[i+1] - 0x20) // ASCII shift a-z to A-Z
100
+			}
101
+			i++ // skip next byte since we just handled it
102
+		} else if b == '=' { // next 2 bytes are hex and should be buffered ready to be read as utf8
103
+			if i+2 >= len(strBytes) {
104
+				return "", fmt.Errorf("Byte pos: %d: expected quote-printable encoding but ran out of string", i)
105
+			}
106
+			dst := make([]byte, 1)
107
+			_, err := hex.Decode(dst, strBytes[i+1:i+3])
108
+			if err != nil {
109
+				return "", err
110
+			}
111
+			outputBuffer.WriteByte(dst[0])
112
+			i += 2 // skip next 2 bytes since we just handled it
113
+		} else { // pass through
114
+			outputBuffer.WriteByte(b)
115
+		}
116
+	}
117
+	return outputBuffer.String(), nil
118
+}
119
+
120
+// ExtractUserLocalpart extracts the localpart portion of a user ID.
121
+// See http://matrix.org/docs/spec/intro.html#user-identifiers
122
+func ExtractUserLocalpart(userID string) (string, error) {
123
+	if len(userID) == 0 || userID[0] != '@' {
124
+		return "", fmt.Errorf("%s is not a valid user id", userID)
125
+	}
126
+	return strings.TrimPrefix(
127
+		strings.SplitN(userID, ":", 2)[0], // @foo:bar:8448 => [ "@foo", "bar:8448" ]
128
+		"@",                               // remove "@" prefix
129
+	), nil
130
+}

+ 2
- 0
vendor/modules.txt View File

@@ -0,0 +1,2 @@
1
+# github.com/matrix-org/gomatrix v0.0.0-20200827122206-7dd5e2a05bcd
2
+github.com/matrix-org/gomatrix

Loading…
Cancel
Save