From 2a0106da26b9a42db5b7f8f1b02af6682f9e0307 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 20 Sep 2018 01:28:37 +0300 Subject: [PATCH] Update dependencies --- Gopkg.lock | 13 +- Gopkg.toml | 4 - vendor/golang.org/x/net/html/parse.go | 99 ++-- vendor/maunium.net/go/gomatrix/client.go | 133 ++++- vendor/maunium.net/go/gomatrix/events.go | 499 +++++++++++++++--- .../go/gomatrix/format/htmlparser.go | 256 +++++++++ .../go/gomatrix/format/markdown.go | 37 ++ vendor/maunium.net/go/gomatrix/reply.go | 96 ++++ vendor/maunium.net/go/gomatrix/requests.go | 8 +- vendor/maunium.net/go/gomatrix/responses.go | 18 +- vendor/maunium.net/go/gomatrix/room.go | 20 +- vendor/maunium.net/go/gomatrix/sync.go | 15 +- vendor/maunium.net/go/gomatrix/userids.go | 2 +- vendor/maunium.net/go/maulogger/defaults.go | 170 +++--- vendor/maunium.net/go/maulogger/level.go | 11 +- vendor/maunium.net/go/maulogger/logger.go | 53 +- vendor/maunium.net/go/maulogger/sublogger.go | 142 +++-- 17 files changed, 1229 insertions(+), 347 deletions(-) create mode 100644 vendor/maunium.net/go/gomatrix/format/htmlparser.go create mode 100644 vendor/maunium.net/go/gomatrix/format/markdown.go create mode 100644 vendor/maunium.net/go/gomatrix/reply.go diff --git a/Gopkg.lock b/Gopkg.lock index 3aacebd..7419819 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -32,7 +32,7 @@ "html", "html/atom" ] - revision = "32a936f46389aa10549d60bd7833e54b01685d09" + revision = "26e67e76b6c3f6ce91f7c52def5af501b4e0f3a2" [[projects]] name = "gopkg.in/russross/blackfriday.v2" @@ -49,8 +49,11 @@ [[projects]] branch = "master" name = "maunium.net/go/gomatrix" - packages = ["."] - revision = "a42d596e48b3ded4a95eae8065fc5cf13afeeacc" + packages = [ + ".", + "format" + ] + revision = "920b154a410aeb5a55200d7b21363732abff3502" [[projects]] branch = "master" @@ -62,11 +65,11 @@ branch = "master" name = "maunium.net/go/maulogger" packages = ["."] - revision = "743010e44ca0ec7bebc89f167d3c8aa7c8e49b13" + revision = "ed98745dedb5f9296c1b2a0ed9424d7347d7e7d4" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "c21e70a3427dd8c0a380c35ab64e74939c873860c3d764d2f41d63c39115b72b" + inputs-digest = "6b56fff780b66591381a1d1c4572951bbad3deea30e0774d34721d89adeee379" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 8e71634..d2f5d33 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -37,10 +37,6 @@ branch = "master" name = "golang.org/x/net" -[[constraint]] - name = "gopkg.in/russross/blackfriday.v2" - version = "2.0.0" - [[constraint]] name = "gopkg.in/yaml.v2" version = "2.2.1" diff --git a/vendor/golang.org/x/net/html/parse.go b/vendor/golang.org/x/net/html/parse.go index 7e539b1..091fb0d 100644 --- a/vendor/golang.org/x/net/html/parse.go +++ b/vendor/golang.org/x/net/html/parse.go @@ -209,27 +209,6 @@ loop: p.oe = p.oe[:i+1] } -// generateAllImpliedEndTags pops nodes off the stack of open elements as long as -// the top node has a tag name of caption, colgroup, dd, div, dt, li, optgroup, option, p, rb, -// rp, rt, rtc, span, tbody, td, tfoot, th, thead or tr. -func (p *parser) generateAllImpliedEndTags() { - var i int - for i = len(p.oe) - 1; i >= 0; i-- { - n := p.oe[i] - if n.Type == ElementNode { - switch n.DataAtom { - // TODO: remove this divergence from the HTML5 spec - case a.Caption, a.Colgroup, a.Dd, a.Div, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rb, - a.Rp, a.Rt, a.Rtc, a.Span, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr: - continue - } - } - break - } - - p.oe = p.oe[:i+1] -} - // addChild adds a child node n to the top element, and pushes n onto the stack // of open elements if it is an element node. func (p *parser) addChild(n *Node) { @@ -276,7 +255,7 @@ func (p *parser) fosterParent(n *Node) { } } - if template != nil && (table == nil || j < i) { + if template != nil && (table == nil || j > i) { template.AppendChild(n) return } @@ -679,11 +658,16 @@ func inHeadIM(p *parser) bool { if !p.oe.contains(a.Template) { return true } - p.generateAllImpliedEndTags() - if n := p.oe.top(); n.DataAtom != a.Template { - return true + // TODO: remove this divergence from the HTML5 spec. + // + // See https://bugs.chromium.org/p/chromium/issues/detail?id=829668 + p.generateImpliedEndTags() + for i := len(p.oe) - 1; i >= 0; i-- { + if n := p.oe[i]; n.Namespace == "" && n.DataAtom == a.Template { + p.oe = p.oe[:i] + break + } } - p.popUntil(defaultScope, a.Template) p.clearActiveFormattingElements() p.templateStack.pop() p.resetInsertionMode() @@ -1074,13 +1058,7 @@ func inBodyIM(p *parser) bool { p.acknowledgeSelfClosingTag() } return true - case a.Frame: - // TODO: remove this divergence from the HTML5 spec. - if p.oe.contains(a.Template) { - p.addElement() - return true - } - case a.Caption, a.Col, a.Colgroup, a.Head, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr: + case a.Caption, a.Col, a.Colgroup, a.Frame, a.Head, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr: // Ignore the token. default: p.reconstructActiveFormattingElements() @@ -1351,9 +1329,6 @@ func textIM(p *parser) bool { // Section 12.2.6.4.9. func inTableIM(p *parser) bool { switch p.tok.Type { - case ErrorToken: - // Stop parsing. - return true case TextToken: p.tok.Data = strings.Replace(p.tok.Data, "\x00", "", -1) switch p.oe.top().DataAtom { @@ -1448,6 +1423,8 @@ func inTableIM(p *parser) bool { case DoctypeToken: // Ignore the token. return true + case ErrorToken: + return inBodyIM(p) } p.fosterParenting = true @@ -1550,6 +1527,8 @@ func inColumnGroupIM(p *parser) bool { case a.Template: return inHeadIM(p) } + case ErrorToken: + return inBodyIM(p) } if p.oe.top().DataAtom != a.Colgroup { return true @@ -1714,9 +1693,6 @@ func inCellIM(p *parser) bool { // Section 12.2.6.4.16. func inSelectIM(p *parser) bool { switch p.tok.Type { - case ErrorToken: - // Stop parsing. - return true case TextToken: p.addText(strings.Replace(p.tok.Data, "\x00", "", -1)) case StartTagToken: @@ -1780,6 +1756,8 @@ func inSelectIM(p *parser) bool { case DoctypeToken: // Ignore the token. return true + case ErrorToken: + return inBodyIM(p) } return true @@ -1846,15 +1824,26 @@ func inTemplateIM(p *parser) bool { // Ignore the token. return true } + case ErrorToken: + if !p.oe.contains(a.Template) { + // Ignore the token. + return true + } + // TODO: remove this divergence from the HTML5 spec. + // + // See https://bugs.chromium.org/p/chromium/issues/detail?id=829668 + p.generateImpliedEndTags() + for i := len(p.oe) - 1; i >= 0; i-- { + if n := p.oe[i]; n.Namespace == "" && n.DataAtom == a.Template { + p.oe = p.oe[:i] + break + } + } + p.clearActiveFormattingElements() + p.templateStack.pop() + p.resetInsertionMode() + return false } - if !p.oe.contains(a.Template) { - // Ignore the token. - return true - } - p.popUntil(defaultScope, a.Template) - p.clearActiveFormattingElements() - p.templateStack.pop() - p.resetInsertionMode() return false } @@ -1928,11 +1917,6 @@ func inFramesetIM(p *parser) bool { p.acknowledgeSelfClosingTag() case a.Noframes: return inHeadIM(p) - case a.Template: - // TODO: remove this divergence from the HTML5 spec. - // - // See https://bugs.chromium.org/p/chromium/issues/detail?id=829668 - return inTemplateIM(p) } case EndTagToken: switch p.tok.DataAtom { @@ -2225,6 +2209,15 @@ func (p *parser) parse() error { } // Parse returns the parse tree for the HTML from the given Reader. +// +// It implements the HTML5 parsing algorithm +// (https://html.spec.whatwg.org/multipage/syntax.html#tree-construction), +// which is very complicated. The resultant tree can contain implicitly created +// nodes that have no explicit listed in r's data, and nodes' parents can +// differ from the nesting implied by a naive processing of start and end +// s. Conversely, explicit s in r's data can be silently dropped, +// with no corresponding node in the resulting tree. +// // The input is assumed to be UTF-8 encoded. func Parse(r io.Reader) (*Node, error) { p := &parser{ @@ -2246,6 +2239,8 @@ func Parse(r io.Reader) (*Node, error) { // ParseFragment parses a fragment of HTML and returns the nodes that were // found. If the fragment is the InnerHTML for an existing element, pass that // element in context. +// +// It has the same intricacies as Parse. func ParseFragment(r io.Reader, context *Node) ([]*Node, error) { contextTag := "" if context != nil { diff --git a/vendor/maunium.net/go/gomatrix/client.go b/vendor/maunium.net/go/gomatrix/client.go index 294ba45..0806138 100644 --- a/vendor/maunium.net/go/gomatrix/client.go +++ b/vendor/maunium.net/go/gomatrix/client.go @@ -6,13 +6,16 @@ package gomatrix import ( "bytes" "encoding/json" + "errors" "fmt" "io" "io/ioutil" + "maunium.net/go/maulogger" "net/http" "net/url" "path" "strconv" + "strings" "sync" "time" ) @@ -26,6 +29,7 @@ type Client struct { Client *http.Client // The underlying HTTP client which will be used to make HTTP requests. Syncer Syncer // The thing which can process /sync responses Store Storer // The thing which can store rooms/tokens/ids + Logger maulogger.Logger // The ?user_id= query parameter for application services. This must be set *prior* to calling a method. If this is empty, // no user_id parameter will be sent. @@ -39,6 +43,7 @@ type Client struct { // HTTPError An HTTP Error response, which may wrap an underlying native Go Error. type HTTPError struct { WrappedError error + RespError *RespError Message string Code int } @@ -177,6 +182,14 @@ func (cli *Client) StopSync() { cli.incrementSyncingID() } +func (cli *Client) LogRequest(req *http.Request, body string) { + if cli.Logger == nil { + return + } + + cli.Logger.Debugfln("%s %s %s", req.Method, req.URL.Path, body) +} + // MakeRequest makes a JSON HTTP request to the given URL. // If "resBody" is not nil, the response body will be json.Unmarshalled into it. // @@ -186,12 +199,14 @@ func (cli *Client) StopSync() { func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{}, resBody interface{}) ([]byte, error) { var req *http.Request var err error + logBody := "{}" if reqBody != nil { var jsonStr []byte jsonStr, err = json.Marshal(reqBody) if err != nil { return nil, err } + logBody = string(jsonStr) req, err = http.NewRequest(method, httpURL, bytes.NewBuffer(jsonStr)) } else { req, err = http.NewRequest(method, httpURL, nil) @@ -201,6 +216,7 @@ func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{ return nil, err } req.Header.Set("Content-Type", "application/json") + cli.LogRequest(req, logBody) res, err := cli.Client.Do(req) if res != nil { defer res.Body.Close() @@ -211,9 +227,11 @@ func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{ contents, err := ioutil.ReadAll(res.Body) if res.StatusCode/100 != 2 { // not 2xx var wrap error - var respErr RespError - if _ = json.Unmarshal(contents, &respErr); respErr.ErrCode != "" { + respErr := &RespError{} + if _ = json.Unmarshal(contents, respErr); respErr.ErrCode != "" { wrap = respErr + } else { + respErr = nil } // If we failed to decode as RespError, don't just drop the HTTP body, include it in the @@ -227,6 +245,7 @@ func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{ Code: res.StatusCode, Message: msg, WrappedError: wrap, + RespError: respErr, } } if err != nil { @@ -342,7 +361,7 @@ func (cli *Client) RegisterDummy(req *ReqRegister) (*RespRegister, error) { } } if res == nil { - return nil, fmt.Errorf("registration failed: does this server support m.login.dummy?") + return nil, fmt.Errorf("registration failed: does this server support m.login.dummy? ") } return res, nil } @@ -442,17 +461,38 @@ func (cli *Client) SetAvatarURL(url string) (err error) { // SendMessageEvent sends a message event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid // contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal. -func (cli *Client) SendMessageEvent(roomID string, eventType string, contentJSON interface{}) (resp *RespSendEvent, err error) { +func (cli *Client) SendMessageEvent(roomID string, eventType EventType, contentJSON interface{}) (resp *RespSendEvent, err error) { txnID := txnID() - urlPath := cli.BuildURL("rooms", roomID, "send", eventType, txnID) + urlPath := cli.BuildURL("rooms", roomID, "send", eventType.String(), txnID) + _, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp) + return +} + +// SendMessageEvent sends a message event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid +// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal. +func (cli *Client) SendMassagedMessageEvent(roomID string, eventType EventType, contentJSON interface{}, ts int64) (resp *RespSendEvent, err error) { + txnID := txnID() + urlPath := cli.BuildURLWithQuery([]string{"rooms", roomID, "send", eventType.String(), txnID}, map[string]string{ + "ts": strconv.FormatInt(ts, 10), + }) _, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp) return } // 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 // contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal. -func (cli *Client) SendStateEvent(roomID, eventType, stateKey string, contentJSON interface{}) (resp *RespSendEvent, err error) { - urlPath := cli.BuildURL("rooms", roomID, "state", eventType, stateKey) +func (cli *Client) SendStateEvent(roomID string, eventType EventType, stateKey string, contentJSON interface{}) (resp *RespSendEvent, err error) { + urlPath := cli.BuildURL("rooms", roomID, "state", eventType.String(), stateKey) + _, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp) + return +} + +// 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 +// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal. +func (cli *Client) SendMassagedStateEvent(roomID string, eventType EventType, stateKey string, contentJSON interface{}, ts int64) (resp *RespSendEvent, err error) { + urlPath := cli.BuildURLWithQuery([]string{"rooms", roomID, "state", eventType.String(), stateKey}, map[string]string{ + "ts": strconv.FormatInt(ts, 10), + }) _, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp) return } @@ -460,37 +500,39 @@ func (cli *Client) SendStateEvent(roomID, eventType, stateKey string, contentJSO // SendText sends an m.room.message event into the given room with a msgtype of m.text // See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-text func (cli *Client) SendText(roomID, text string) (*RespSendEvent, error) { - return cli.SendMessageEvent(roomID, "m.room.message", - TextMessage{"m.text", text}) + return cli.SendMessageEvent(roomID, EventMessage, Content{ + MsgType: MsgText, + Body: text, + }) } // SendImage sends an m.room.message event into the given room with a msgtype of m.image // See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-image func (cli *Client) SendImage(roomID, body, url string) (*RespSendEvent, error) { - return cli.SendMessageEvent(roomID, "m.room.message", - ImageMessage{ - MsgType: "m.image", - Body: body, - URL: url, - }) + return cli.SendMessageEvent(roomID, EventMessage, Content{ + MsgType: MsgImage, + Body: body, + URL: url, + }) } // SendVideo sends an m.room.message event into the given room with a msgtype of m.video // See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-video func (cli *Client) SendVideo(roomID, body, url string) (*RespSendEvent, error) { - return cli.SendMessageEvent(roomID, "m.room.message", - VideoMessage{ - MsgType: "m.video", - Body: body, - URL: url, - }) + return cli.SendMessageEvent(roomID, EventMessage, Content{ + MsgType: MsgVideo, + Body: body, + URL: url, + }) } // SendNotice sends an m.room.message event into the given room with a msgtype of m.notice // See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-notice func (cli *Client) SendNotice(roomID, text string) (*RespSendEvent, error) { - return cli.SendMessageEvent(roomID, "m.room.message", - TextMessage{"m.notice", text}) + return cli.SendMessageEvent(roomID, EventMessage, Content{ + MsgType: MsgNotice, + Body: text, + }) } // RedactEvent redacts the given event. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid @@ -569,11 +611,18 @@ func (cli *Client) UserTyping(roomID string, typing bool, timeout int64) (resp * return } +func (cli *Client) SetPresence(status string) (err error) { + req := ReqPresence{Presence: status} + u := cli.BuildURL("presence", cli.UserID, "status") + _, err = cli.MakeRequest("PUT", u, req, nil) + return +} + // StateEvent gets a single state event in a room. It will attempt to JSON unmarshal into the given "outContent" struct with // the HTTP response body, or return an error. // See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-state-eventtype-statekey -func (cli *Client) StateEvent(roomID, eventType, stateKey string, outContent interface{}) (err error) { - u := cli.BuildURL("rooms", roomID, "state", eventType, stateKey) +func (cli *Client) StateEvent(roomID string, eventType EventType, stateKey string, outContent interface{}) (err error) { + u := cli.BuildURL("rooms", roomID, "state", eventType.String(), stateKey) _, err = cli.MakeRequest("GET", u, nil, outContent) return } @@ -587,18 +636,48 @@ func (cli *Client) UploadLink(link string) (*RespMediaUpload, error) { if err != nil { return nil, err } - return cli.UploadToContentRepo(res.Body, res.Header.Get("Content-Type"), res.ContentLength) + return cli.Upload(res.Body, res.Header.Get("Content-Type"), res.ContentLength) +} + +func (cli *Client) Download(mxcURL string) (io.ReadCloser, error) { + if !strings.HasPrefix(mxcURL, "mxc://") { + return nil, errors.New("invalid Matrix content URL") + } + parts := strings.Split(mxcURL[len("mxc://"):], "/") + if len(parts) != 2 { + return nil, errors.New("invalid Matrix content URL") + } + u := cli.BuildBaseURL("_matrix/media/r0/download", parts[0], parts[1]) + resp, err := cli.Client.Get(u) + if err != nil { + return nil, err + } + return resp.Body, nil +} + +func (cli *Client) DownloadBytes(mxcURL string) ([]byte, error) { + resp, err := cli.Download(mxcURL) + if err != nil { + return nil, err + } + defer resp.Close() + return ioutil.ReadAll(resp) +} + +func (cli *Client) UploadBytes(data []byte, contentType string) (*RespMediaUpload, error) { + return cli.Upload(bytes.NewReader(data), contentType, int64(len(data))) } // UploadToContentRepo uploads the given bytes to the content repository and returns an MXC URI. // See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-media-r0-upload -func (cli *Client) UploadToContentRepo(content io.Reader, contentType string, contentLength int64) (*RespMediaUpload, error) { +func (cli *Client) Upload(content io.Reader, contentType string, contentLength int64) (*RespMediaUpload, error) { req, err := http.NewRequest("POST", cli.BuildBaseURL("_matrix/media/r0/upload"), content) if err != nil { return nil, err } req.Header.Set("Content-Type", contentType) req.ContentLength = contentLength + cli.LogRequest(req, fmt.Sprintf("%d bytes", contentLength)) res, err := cli.Client.Do(req) if res != nil { defer res.Body.Close() diff --git a/vendor/maunium.net/go/gomatrix/events.go b/vendor/maunium.net/go/gomatrix/events.go index 7233c7c..a66aea6 100644 --- a/vendor/maunium.net/go/gomatrix/events.go +++ b/vendor/maunium.net/go/gomatrix/events.go @@ -1,110 +1,447 @@ package gomatrix import ( - "html" - "regexp" + "encoding/json" + "strings" + "sync" +) + +type EventTypeClass int + +const ( + // Normal message events + MessageEventType EventTypeClass = iota + // State events + StateEventType + // Ephemeral events + EphemeralEventType + // Account data events + AccountDataEventType + // Unknown events + UnknownEventType +) + +type EventType struct { + Type string + Class EventTypeClass +} + +func NewEventType(name string) EventType { + evtType := EventType{Type: name} + evtType.Class = evtType.GuessClass() + return evtType +} + +func (et *EventType) IsState() bool { + return et.Class == StateEventType +} + +func (et *EventType) IsEphemeral() bool { + return et.Class == EphemeralEventType +} + +func (et *EventType) IsCustom() bool { + return !strings.HasPrefix(et.Type, "m.") +} + +func (et *EventType) GuessClass() EventTypeClass { + switch et.Type { + case StateAliases.Type, StateCanonicalAlias.Type, StateCreate.Type, StateJoinRules.Type, StateMember.Type, + StatePowerLevels.Type, StateRoomName.Type, StateRoomAvatar.Type, StateTopic.Type, StatePinnedEvents.Type: + return StateEventType + case EphemeralEventReceipt.Type, EphemeralEventTyping.Type: + return EphemeralEventType + case AccountDataDirectChats.Type, AccountDataPushRules.Type, AccountDataRoomTags.Type: + return AccountDataEventType + case EventRedaction.Type, EventMessage.Type, EventSticker.Type: + return MessageEventType + default: + return UnknownEventType + } +} + +func (et *EventType) UnmarshalJSON(data []byte) error { + err := json.Unmarshal(data, &et.Type) + if err != nil { + return err + } + et.Class = et.GuessClass() + return nil +} + +func (et *EventType) MarshalJSON() ([]byte, error) { + return json.Marshal(&et.Type) +} + +func (et *EventType) String() string { + return et.Type +} + +// State events +var ( + StateAliases = EventType{"m.room.aliases", StateEventType} + StateCanonicalAlias = EventType{"m.room.canonical_alias", StateEventType} + StateCreate = EventType{"m.room.create", StateEventType} + StateJoinRules = EventType{"m.room.join_rules", StateEventType} + StateMember = EventType{"m.room.member", StateEventType} + StatePowerLevels = EventType{"m.room.power_levels", StateEventType} + StateRoomName = EventType{"m.room.name", StateEventType} + StateTopic = EventType{"m.room.topic", StateEventType} + StateRoomAvatar = EventType{"m.room.avatar", StateEventType} + StatePinnedEvents = EventType{"m.room.pinned_events", StateEventType} +) + +// Message events +var ( + EventRedaction = EventType{"m.room.redaction", MessageEventType} + EventMessage = EventType{"m.room.message", MessageEventType} + EventSticker = EventType{"m.sticker", MessageEventType} +) + +// Ephemeral events +var ( + EphemeralEventReceipt = EventType{"m.receipt", EphemeralEventType} + EphemeralEventTyping = EventType{"m.typing", EphemeralEventType} +) + +// Account data events +var ( + AccountDataDirectChats = EventType{"m.direct", AccountDataEventType} + AccountDataPushRules = EventType{"m.push_rules", AccountDataEventType} + AccountDataRoomTags = EventType{"m.tag", AccountDataEventType} +) + +type MessageType string + +// Msgtypes +const ( + MsgText MessageType = "m.text" + MsgEmote = "m.emote" + MsgNotice = "m.notice" + MsgImage = "m.image" + MsgLocation = "m.location" + MsgVideo = "m.video" + MsgAudio = "m.audio" + MsgFile = "m.file" +) + +type Format string + +// Message formats +const ( + FormatHTML Format = "org.matrix.custom.html" ) // Event represents a single Matrix event. type Event struct { - StateKey *string `json:"state_key,omitempty"` // The state key for the event. Only present on State Events. - Sender string `json:"sender"` // The user ID of the sender of the event - Type string `json:"type"` // The event type - Timestamp int64 `json:"origin_server_ts"` // The unix timestamp when this message was sent by the origin server - ID string `json:"event_id"` // The unique ID of this event - RoomID string `json:"room_id"` // The room the event was sent to. May be nil (e.g. for presence) - Content map[string]interface{} `json:"content"` // The JSON content of the event. - Redacts string `json:"redacts,omitempty"` // The event ID that was redacted if a m.room.redaction event - Unsigned Unsigned `json:"unsigned,omitempty"` // Unsigned content set by own homeserver. + StateKey *string `json:"state_key,omitempty"` // The state key for the event. Only present on State Events. + Sender string `json:"sender"` // The user ID of the sender of the event + Type EventType `json:"type"` // The event type + Timestamp int64 `json:"origin_server_ts"` // The unix timestamp when this message was sent by the origin server + ID string `json:"event_id"` // The unique ID of this event + RoomID string `json:"room_id"` // The room the event was sent to. May be nil (e.g. for presence) + Content Content `json:"content"` // The JSON content of the event. + Redacts string `json:"redacts,omitempty"` // The event ID that was redacted if a m.room.redaction event + Unsigned Unsigned `json:"unsigned,omitempty"` // Unsigned content set by own homeserver. + + InviteRoomState []StrippedState `json:"invite_room_state"` +} + +func (evt *Event) GetStateKey() string { + if evt.StateKey != nil { + return *evt.StateKey + } + return "" +} + +type StrippedState struct { + Content Content `json:"content"` + Type EventType `json:"type"` + StateKey string `json:"state_key"` } type Unsigned struct { - PrevContent map[string]interface{} `json:"prev_content,omitempty"` - PrevSender string `json:"prev_sender,omitempty"` - ReplacesState string `json:"replaces_state,omitempty"` - Age int64 `json:"age"` + PrevContent *Content `json:"prev_content,omitempty"` + PrevSender string `json:"prev_sender,omitempty"` + ReplacesState string `json:"replaces_state,omitempty"` + Age int64 `json:"age,omitempty"` + + PassiveCommand map[string]*MatchedPassiveCommand `json:"m.passive_command,omitempty"` } -// Body returns the value of the "body" key in the event content if it is -// present and is a string. -func (event *Event) Body() (body string, ok bool) { - value, exists := event.Content["body"] - if !exists { - return +type MatchedPassiveCommand struct { + // Matched string `json:"matched"` + // Value string `json:"value"` + Captured [][]string `json:"captured"` + + BackCompatCommand string `json:"command"` + BackCompatArguments map[string]string `json:"arguments"` +} + +type Content struct { + VeryRaw json.RawMessage `json:"-"` + Raw map[string]interface{} `json:"-"` + + MsgType MessageType `json:"msgtype,omitempty"` + Body string `json:"body,omitempty"` + Format Format `json:"format,omitempty"` + FormattedBody string `json:"formatted_body,omitempty"` + + Info *FileInfo `json:"info,omitempty"` + URL string `json:"url,omitempty"` + + // Membership key for easy access in m.room.member events + Membership Membership `json:"membership,omitempty"` + + RelatesTo *RelatesTo `json:"m.relates_to,omitempty"` + Command *MatchedCommand `json:"m.command,omitempty"` + + PowerLevels + Member + Aliases []string `json:"aliases,omitempty"` + CanonicalAlias + RoomName + RoomTopic + + RoomTags Tags `json:"tags,omitempty"` + TypingUserIDs []string `json:"user_ids,omitempty"` +} + +type serializableContent Content + +var DisableFancyEventParsing = false + +func (content *Content) UnmarshalJSON(data []byte) error { + content.VeryRaw = data + if err := json.Unmarshal(data, &content.Raw); err != nil || DisableFancyEventParsing { + return err } - body, ok = value.(string) + return json.Unmarshal(data, (*serializableContent)(content)) +} + +func (content *Content) GetCommand() *MatchedCommand { + if content.Command == nil { + content.Command = &MatchedCommand{} + } + return content.Command +} + +func (content *Content) GetRelatesTo() *RelatesTo { + if content.RelatesTo == nil { + content.RelatesTo = &RelatesTo{} + } + return content.RelatesTo +} + +func (content *Content) UnmarshalPowerLevels() (pl PowerLevels, err error) { + err = json.Unmarshal(content.VeryRaw, &pl) return } -// MessageType returns the value of the "msgtype" key in the event content if -// it is present and is a string. -func (event *Event) MessageType() (msgtype string, ok bool) { - value, exists := event.Content["msgtype"] - if !exists { - return - } - msgtype, ok = value.(string) +func (content *Content) UnmarshalMember() (m Member, err error) { + err = json.Unmarshal(content.VeryRaw, &m) return } -// TextMessage is the contents of a Matrix formated message event. -type TextMessage struct { - MsgType string `json:"msgtype"` - Body string `json:"body"` +func (content *Content) UnmarshalCanonicalAlias() (ca CanonicalAlias, err error) { + err = json.Unmarshal(content.VeryRaw, &ca) + return } -// ImageInfo contains info about an image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image -type ImageInfo struct { - Height uint `json:"h,omitempty"` - Width uint `json:"w,omitempty"` - Mimetype string `json:"mimetype,omitempty"` - Size uint `json:"size,omitempty"` +func (content *Content) GetInfo() *FileInfo { + if content.Info == nil { + content.Info = &FileInfo{} + } + return content.Info } -// VideoInfo contains info about a video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video -type VideoInfo struct { - Mimetype string `json:"mimetype,omitempty"` - ThumbnailInfo ImageInfo `json:"thumbnail_info"` +type Tags map[string]struct { + Order string `json:"order"` +} + +type RoomName struct { + Name string `json:"name,omitempty"` +} + +type RoomTopic struct { + Topic string `json:"topic,omitempty"` +} + +// Membership is an enum specifying the membership state of a room member. +type Membership string + +// The allowed membership states as specified in spec section 10.5.5. +const ( + MembershipJoin Membership = "join" + MembershipLeave Membership = "leave" + MembershipInvite Membership = "invite" + MembershipBan Membership = "ban" + MembershipKnock Membership = "knock" +) + +type Member struct { + Membership Membership `json:"membership,omitempty"` + AvatarURL string `json:"avatar_url,omitempty"` + Displayname string `json:"displayname,omitempty"` + ThirdPartyInvite *ThirdPartyInvite `json:"third_party_invite,omitempty"` + Reason string `json:"reason,omitempty"` +} + +type ThirdPartyInvite struct { + DisplayName string `json:"display_name"` + Signed struct { + Token string `json:"token"` + Signatures json.RawMessage `json:"signatures"` + MXID string `json:"mxid"` + } +} + +type CanonicalAlias struct { + Alias string `json:"alias,omitempty"` +} + +type PowerLevels struct { + usersLock sync.RWMutex `json:"-"` + Users map[string]int `json:"users,omitempty"` + UsersDefault int `json:"users_default,omitempty"` + + eventsLock sync.RWMutex `json:"-"` + Events map[string]int `json:"events,omitempty"` + EventsDefault int `json:"events_default,omitempty"` + + StateDefaultPtr *int `json:"state_default,omitempty"` + + InvitePtr *int `json:"invite,omitempty"` + KickPtr *int `json:"kick,omitempty"` + BanPtr *int `json:"ban,omitempty"` + RedactPtr *int `json:"redact,omitempty"` +} + +func (pl *PowerLevels) Invite() int { + if pl.InvitePtr != nil { + return *pl.InvitePtr + } + return 50 +} + +func (pl *PowerLevels) Kick() int { + if pl.KickPtr != nil { + return *pl.KickPtr + } + return 50 +} + +func (pl *PowerLevels) Ban() int { + if pl.BanPtr != nil { + return *pl.BanPtr + } + return 50 +} + +func (pl *PowerLevels) Redact() int { + if pl.RedactPtr != nil { + return *pl.RedactPtr + } + return 50 +} + +func (pl *PowerLevels) StateDefault() int { + if pl.StateDefaultPtr != nil { + return *pl.StateDefaultPtr + } + return 50 +} + +func (pl *PowerLevels) GetUserLevel(userID string) int { + pl.usersLock.RLock() + defer pl.usersLock.RUnlock() + level, ok := pl.Users[userID] + if !ok { + return pl.UsersDefault + } + return level +} + +func (pl *PowerLevels) SetUserLevel(userID string, level int) { + pl.usersLock.Lock() + defer pl.usersLock.Unlock() + if level == pl.UsersDefault { + delete(pl.Users, userID) + } else { + pl.Users[userID] = level + } +} + +func (pl *PowerLevels) EnsureUserLevel(userID string, level int) bool { + existingLevel := pl.GetUserLevel(userID) + if existingLevel != level { + pl.SetUserLevel(userID, level) + return true + } + return false +} + +func (pl *PowerLevels) GetEventLevel(eventType EventType) int { + pl.eventsLock.RLock() + defer pl.eventsLock.RUnlock() + level, ok := pl.Events[eventType.String()] + if !ok { + if eventType.IsState() { + return pl.StateDefault() + } + return pl.EventsDefault + } + return level +} + +func (pl *PowerLevels) SetEventLevel(eventType EventType, level int) { + pl.eventsLock.Lock() + defer pl.eventsLock.Unlock() + if (eventType.IsState() && level == pl.StateDefault()) || (!eventType.IsState() && level == pl.EventsDefault) { + delete(pl.Events, eventType.String()) + } else { + pl.Events[eventType.String()] = level + } +} + +func (pl *PowerLevels) EnsureEventLevel(eventType EventType, level int) bool { + existingLevel := pl.GetEventLevel(eventType) + if existingLevel != level { + pl.SetEventLevel(eventType, level) + return true + } + return false +} + +type FileInfo struct { + MimeType string `json:"mimetype,omitempty"` + ThumbnailInfo *FileInfo `json:"thumbnail_info,omitempty"` ThumbnailURL string `json:"thumbnail_url,omitempty"` - Height uint `json:"h,omitempty"` - Width uint `json:"w,omitempty"` + Height int `json:"h,omitempty"` + Width int `json:"w,omitempty"` Duration uint `json:"duration,omitempty"` - Size uint `json:"size,omitempty"` + Size int `json:"size,omitempty"` } -// VideoMessage is an m.video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video -type VideoMessage struct { - MsgType string `json:"msgtype"` - Body string `json:"body"` - URL string `json:"url"` - Info VideoInfo `json:"info"` -} - -// ImageMessage is an m.image event -type ImageMessage struct { - MsgType string `json:"msgtype"` - Body string `json:"body"` - URL string `json:"url"` - Info ImageInfo `json:"info"` -} - -// An HTMLMessage is the contents of a Matrix HTML formated message event. -type HTMLMessage struct { - Body string `json:"body"` - MsgType string `json:"msgtype"` - Format string `json:"format"` - FormattedBody string `json:"formatted_body"` -} - -var htmlRegex = regexp.MustCompile("<[^<]+?>") - -// GetHTMLMessage returns an HTMLMessage with the body set to a stripped version of the provided HTML, in addition -// to the provided HTML. -func GetHTMLMessage(msgtype, htmlText string) HTMLMessage { - return HTMLMessage{ - Body: html.UnescapeString(htmlRegex.ReplaceAllLiteralString(htmlText, "")), - MsgType: msgtype, - Format: "org.matrix.custom.html", - FormattedBody: htmlText, +func (fileInfo *FileInfo) GetThumbnailInfo() *FileInfo { + if fileInfo.ThumbnailInfo == nil { + fileInfo.ThumbnailInfo = &FileInfo{} } + return fileInfo.ThumbnailInfo +} + +type RelatesTo struct { + InReplyTo InReplyTo `json:"m.in_reply_to,omitempty"` +} + +type InReplyTo struct { + EventID string `json:"event_id,omitempty"` + // Not required, just for future-proofing + RoomID string `json:"room_id,omitempty"` +} + +type MatchedCommand struct { + Target string `json:"target"` + Matched string `json:"matched"` + Arguments map[string]string `json:"arguments"` } diff --git a/vendor/maunium.net/go/gomatrix/format/htmlparser.go b/vendor/maunium.net/go/gomatrix/format/htmlparser.go new file mode 100644 index 0000000..85f4105 --- /dev/null +++ b/vendor/maunium.net/go/gomatrix/format/htmlparser.go @@ -0,0 +1,256 @@ +package format + +import ( + "fmt" + "math" + "regexp" + "strings" + + "golang.org/x/net/html" + "strconv" +) + +var MatrixToURL = regexp.MustCompile("^(?:https?://)?(?:www\\.)?matrix\\.to/#/([#@!+].*)(?:/(\\$.+))?") + +type TextConverter func(string) string + +type HTMLParser struct { + PillConverter func(mxid, eventID string) string + TabsToSpaces int + Newline string + BoldConverter TextConverter + ItalicConverter TextConverter + StrikethroughConverter TextConverter + UnderlineConverter TextConverter + MonospaceBlockConverter TextConverter + MonospaceConverter TextConverter +} + +type TaggedString struct { + string + tag string +} + +func (parser *HTMLParser) getAttribute(node *html.Node, attribute string) string { + for _, attr := range node.Attr { + if attr.Key == attribute { + return attr.Val + } + } + return "" +} + +func Digits(num int) int { + return int(math.Floor(math.Log10(float64(num))) + 1) +} + +func (parser *HTMLParser) listToString(node *html.Node, stripLinebreak bool) string { + ordered := node.Data == "ol" + taggedChildren := parser.nodeToTaggedStrings(node.FirstChild, stripLinebreak) + counter := 1 + indentLength := 0 + if ordered { + start := parser.getAttribute(node, "start") + if len(start) > 0 { + counter, _ = strconv.Atoi(start) + } + + longestIndex := (counter - 1) + len(taggedChildren) + indentLength = Digits(longestIndex) + } + indent := strings.Repeat(" ", indentLength+2) + var children []string + for _, child := range taggedChildren { + if child.tag != "li" { + continue + } + var prefix string + if ordered { + indexPadding := indentLength - Digits(counter) + prefix = fmt.Sprintf("%d. %s", counter, strings.Repeat(" ", indexPadding)) + } else { + prefix = "● " + } + str := prefix + child.string + counter++ + parts := strings.Split(str, "\n") + for i, part := range parts[1:] { + parts[i+1] = indent + part + } + str = strings.Join(parts, "\n") + children = append(children, str) + } + return strings.Join(children, "\n") +} + +func (parser *HTMLParser) basicFormatToString(node *html.Node, stripLinebreak bool) string { + str := parser.nodeToTagAwareString(node.FirstChild, stripLinebreak) + switch node.Data { + case "b", "strong": + if parser.BoldConverter != nil { + return parser.BoldConverter(str) + } + return fmt.Sprintf("**%s**", str) + case "i", "em": + if parser.ItalicConverter != nil { + return parser.ItalicConverter(str) + } + return fmt.Sprintf("_%s_", str) + case "s", "del": + if parser.StrikethroughConverter != nil { + return parser.StrikethroughConverter(str) + } + return fmt.Sprintf("~~%s~~", str) + case "u", "ins": + if parser.UnderlineConverter != nil { + return parser.UnderlineConverter(str) + } + case "tt", "code": + if parser.MonospaceConverter != nil { + return parser.MonospaceConverter(str) + } + } + return str +} + +func (parser *HTMLParser) headerToString(node *html.Node, stripLinebreak bool) string { + children := parser.nodeToStrings(node.FirstChild, stripLinebreak) + length := int(node.Data[1] - '0') + prefix := strings.Repeat("#", length) + " " + return prefix + strings.Join(children, "") +} + +func (parser *HTMLParser) blockquoteToString(node *html.Node, stripLinebreak bool) string { + str := parser.nodeToTagAwareString(node.FirstChild, stripLinebreak) + childrenArr := strings.Split(strings.TrimSpace(str), "\n") + for index, child := range childrenArr { + childrenArr[index] = "> " + child + } + return strings.Join(childrenArr, "\n") +} + +func (parser *HTMLParser) linkToString(node *html.Node, stripLinebreak bool) string { + str := parser.nodeToTagAwareString(node.FirstChild, stripLinebreak) + href := parser.getAttribute(node, "href") + if len(href) == 0 { + return str + } + match := MatrixToURL.FindStringSubmatch(href) + if len(match) == 2 || len(match) == 3 { + if parser.PillConverter != nil { + mxid := match[1] + eventID := "" + if len(match) == 3 { + eventID = match[2] + } + return parser.PillConverter(mxid, eventID) + } + return str + } + return fmt.Sprintf("%s (%s)", str, href) +} + +func (parser *HTMLParser) tagToString(node *html.Node, stripLinebreak bool) string { + switch node.Data { + case "blockquote": + return parser.blockquoteToString(node, stripLinebreak) + case "ol", "ul": + return parser.listToString(node, stripLinebreak) + case "h1", "h2", "h3", "h4", "h5", "h6": + return parser.headerToString(node, stripLinebreak) + case "br": + return parser.Newline + case "b", "strong", "i", "em", "s", "del", "u", "ins", "tt", "code": + return parser.basicFormatToString(node, stripLinebreak) + case "a": + return parser.linkToString(node, stripLinebreak) + case "p": + return parser.nodeToTagAwareString(node.FirstChild, stripLinebreak) + "\n" + case "pre": + var preStr string + if node.FirstChild != nil && node.FirstChild.Type == html.ElementNode && node.FirstChild.Data == "code" { + preStr = parser.nodeToString(node.FirstChild.FirstChild, false) + } else { + preStr = parser.nodeToString(node.FirstChild, false) + } + if parser.MonospaceBlockConverter != nil { + return parser.MonospaceBlockConverter(preStr) + } + return preStr + default: + return parser.nodeToTagAwareString(node.FirstChild, stripLinebreak) + } +} + +func (parser *HTMLParser) singleNodeToString(node *html.Node, stripLinebreak bool) TaggedString { + switch node.Type { + case html.TextNode: + if stripLinebreak { + node.Data = strings.Replace(node.Data, "\n", "", -1) + } + return TaggedString{node.Data, "text"} + case html.ElementNode: + return TaggedString{parser.tagToString(node, stripLinebreak), node.Data} + case html.DocumentNode: + return TaggedString{parser.nodeToTagAwareString(node.FirstChild, stripLinebreak), "html"} + default: + return TaggedString{"", "unknown"} + } +} + +func (parser *HTMLParser) nodeToTaggedStrings(node *html.Node, stripLinebreak bool) (strs []TaggedString) { + for ; node != nil; node = node.NextSibling { + strs = append(strs, parser.singleNodeToString(node, stripLinebreak)) + } + return +} + +var BlockTags = []string{"p", "h1", "h2", "h3", "h4", "h5", "h6", "ol", "ul", "pre", "blockquote", "div", "hr", "table"} + +func (parser *HTMLParser) isBlockTag(tag string) bool { + for _, blockTag := range BlockTags { + if tag == blockTag { + return true + } + } + return false +} + +func (parser *HTMLParser) nodeToTagAwareString(node *html.Node, stripLinebreak bool) string { + strs := parser.nodeToTaggedStrings(node, stripLinebreak) + var output strings.Builder + for _, str := range strs { + tstr := str.string + if parser.isBlockTag(str.tag) { + tstr = fmt.Sprintf("\n%s\n", tstr) + } + output.WriteString(tstr) + } + return strings.TrimSpace(output.String()) +} + +func (parser *HTMLParser) nodeToStrings(node *html.Node, stripLinebreak bool) (strs []string) { + for ; node != nil; node = node.NextSibling { + strs = append(strs, parser.singleNodeToString(node, stripLinebreak).string) + } + return +} + +func (parser *HTMLParser) nodeToString(node *html.Node, stripLinebreak bool) string { + return strings.Join(parser.nodeToStrings(node, stripLinebreak), "") +} + +func (parser *HTMLParser) Parse(htmlData string) string { + if parser.TabsToSpaces >= 0 { + htmlData = strings.Replace(htmlData, "\t", strings.Repeat(" ", parser.TabsToSpaces), -1) + } + node, _ := html.Parse(strings.NewReader(htmlData)) + return parser.nodeToTagAwareString(node, true) +} + +func HTMLToText(html string) string { + return (&HTMLParser{ + TabsToSpaces: 4, + Newline: "\n", + }).Parse(html) +} diff --git a/vendor/maunium.net/go/gomatrix/format/markdown.go b/vendor/maunium.net/go/gomatrix/format/markdown.go new file mode 100644 index 0000000..3bfa1dc --- /dev/null +++ b/vendor/maunium.net/go/gomatrix/format/markdown.go @@ -0,0 +1,37 @@ +package format + +import ( + "gopkg.in/russross/blackfriday.v2" + "maunium.net/go/gomatrix" + "strings" +) + +func RenderMarkdown(text string) gomatrix.Content { + parser := blackfriday.New( + blackfriday.WithExtensions(blackfriday.NoIntraEmphasis | + blackfriday.Tables | + blackfriday.FencedCode | + blackfriday.Strikethrough | + blackfriday.SpaceHeadings | + blackfriday.DefinitionLists)) + ast := parser.Parse([]byte(text)) + + renderer := blackfriday.NewHTMLRenderer(blackfriday.HTMLRendererParameters{ + Flags: blackfriday.UseXHTML, + }) + + var buf strings.Builder + renderer.RenderHeader(&buf, ast) + ast.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus { + return renderer.RenderNode(&buf, node, entering) + }) + renderer.RenderFooter(&buf, ast) + htmlBody := buf.String() + + return gomatrix.Content{ + FormattedBody: htmlBody, + Format: gomatrix.FormatHTML, + MsgType: gomatrix.MsgText, + Body: HTMLToText(htmlBody), + } +} diff --git a/vendor/maunium.net/go/gomatrix/reply.go b/vendor/maunium.net/go/gomatrix/reply.go new file mode 100644 index 0000000..6985421 --- /dev/null +++ b/vendor/maunium.net/go/gomatrix/reply.go @@ -0,0 +1,96 @@ +package gomatrix + +import ( + "fmt" + "regexp" + "strings" + + "golang.org/x/net/html" +) + +var HTMLReplyFallbackRegex = regexp.MustCompile(`^[\s\S]+?`) + +func TrimReplyFallbackHTML(html string) string { + return HTMLReplyFallbackRegex.ReplaceAllString(html, "") +} + +func TrimReplyFallbackText(text string) string { + if !strings.HasPrefix(text, "> ") || !strings.Contains(text, "\n") { + return text + } + + lines := strings.Split(text, "\n") + for len(lines) > 0 && strings.HasPrefix(lines[0], "> ") { + lines = lines[1:] + } + return strings.TrimSpace(strings.Join(lines, "\n")) +} + +func (content *Content) RemoveReplyFallback() { + if len(content.GetReplyTo()) > 0 { + if content.Format == FormatHTML { + content.FormattedBody = TrimReplyFallbackHTML(content.FormattedBody) + } + content.Body = TrimReplyFallbackText(content.Body) + } +} + +func (content *Content) GetReplyTo() string { + if content.RelatesTo != nil { + return content.RelatesTo.InReplyTo.EventID + } + return "" +} + +const ReplyFormat = `
+In reply to +%s +%s +
+` + +func (evt *Event) GenerateReplyFallbackHTML() string { + body := evt.Content.FormattedBody + if len(body) == 0 { + body = html.EscapeString(evt.Content.Body) + } + + senderDisplayName := evt.Sender + + return fmt.Sprintf(ReplyFormat, evt.RoomID, evt.ID, evt.Sender, senderDisplayName, body) +} + +func (evt *Event) GenerateReplyFallbackText() string { + body := evt.Content.Body + lines := strings.Split(strings.TrimSpace(body), "\n") + firstLine, lines := lines[0], lines[1:] + + senderDisplayName := evt.Sender + + var fallbackText strings.Builder + fmt.Fprintf(&fallbackText, "> <%s> %s", senderDisplayName, firstLine) + for _, line := range lines { + fmt.Fprintf(&fallbackText, "\n> %s", line) + } + fallbackText.WriteString("\n\n") + return fallbackText.String() +} + +func (content *Content) SetReply(inReplyTo *Event) { + if content.RelatesTo == nil { + content.RelatesTo = &RelatesTo{} + } + content.RelatesTo.InReplyTo = InReplyTo{ + EventID: inReplyTo.ID, + RoomID: inReplyTo.RoomID, + } + + if content.MsgType == MsgText || content.MsgType == MsgNotice { + if len(content.FormattedBody) == 0 || content.Format != FormatHTML { + content.FormattedBody = html.EscapeString(content.Body) + content.Format = FormatHTML + } + content.FormattedBody = inReplyTo.GenerateReplyFallbackHTML() + content.FormattedBody + content.Body = inReplyTo.GenerateReplyFallbackText() + content.Body + } +} diff --git a/vendor/maunium.net/go/gomatrix/requests.go b/vendor/maunium.net/go/gomatrix/requests.go index af99a22..81ff361 100644 --- a/vendor/maunium.net/go/gomatrix/requests.go +++ b/vendor/maunium.net/go/gomatrix/requests.go @@ -31,7 +31,7 @@ type ReqCreateRoom struct { Invite []string `json:"invite,omitempty"` Invite3PID []ReqInvite3PID `json:"invite_3pid,omitempty"` CreationContent map[string]interface{} `json:"creation_content,omitempty"` - InitialState []Event `json:"initial_state,omitempty"` + InitialState []*Event `json:"initial_state,omitempty"` Preset string `json:"preset,omitempty"` IsDirect bool `json:"is_direct,omitempty"` } @@ -74,5 +74,9 @@ type ReqUnbanUser struct { // 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 type ReqTyping struct { Typing bool `json:"typing"` - Timeout int64 `json:"timeout"` + Timeout int64 `json:"timeout,omitempty"` +} + +type ReqPresence struct { + Presence string `json:"presence"` } diff --git a/vendor/maunium.net/go/gomatrix/responses.go b/vendor/maunium.net/go/gomatrix/responses.go index 6d43bd3..c5f6eeb 100644 --- a/vendor/maunium.net/go/gomatrix/responses.go +++ b/vendor/maunium.net/go/gomatrix/responses.go @@ -63,9 +63,9 @@ type RespJoinedMembers struct { // RespMessages is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages type RespMessages struct { - Start string `json:"start"` - Chunk []Event `json:"chunk"` - End string `json:"end"` + Start string `json:"start"` + Chunk []*Event `json:"chunk"` + End string `json:"end"` } // 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 @@ -146,8 +146,8 @@ type RespSync struct { } `json:"state"` Timeline struct { Events []*Event `json:"events"` - Limited bool `json:"limited"` - PrevBatch string `json:"prev_batch"` + Limited bool `json:"limited"` + PrevBatch string `json:"prev_batch"` } `json:"timeline"` } `json:"leave"` Join map[string]struct { @@ -156,14 +156,14 @@ type RespSync struct { } `json:"state"` Timeline struct { Events []*Event `json:"events"` - Limited bool `json:"limited"` - PrevBatch string `json:"prev_batch"` + Limited bool `json:"limited"` + PrevBatch string `json:"prev_batch"` } `json:"timeline"` Ephemeral struct { - Events []*Event `json:"events"` + Events []*Event `json:"events"` } `json:"ephemeral"` AccountData struct { - Events []*Event `json:"events"` + Events []*Event `json:"events"` } `json:"account_data"` } `json:"join"` Invite map[string]struct { diff --git a/vendor/maunium.net/go/gomatrix/room.go b/vendor/maunium.net/go/gomatrix/room.go index c9b2351..80a91d8 100644 --- a/vendor/maunium.net/go/gomatrix/room.go +++ b/vendor/maunium.net/go/gomatrix/room.go @@ -3,7 +3,7 @@ package gomatrix // Room represents a single Matrix room. type Room struct { ID string - State map[string]map[string]*Event + State map[EventType]map[string]*Event } // UpdateState updates the room's current state with the given Event. This will clobber events based @@ -17,7 +17,7 @@ func (room Room) UpdateState(event *Event) { } // GetStateEvent returns the state event for the given type/state_key combo, or nil. -func (room Room) GetStateEvent(eventType string, stateKey string) *Event { +func (room Room) GetStateEvent(eventType EventType, stateKey string) *Event { stateEventMap, _ := room.State[eventType] event, _ := stateEventMap[stateKey] return event @@ -25,17 +25,11 @@ func (room Room) GetStateEvent(eventType string, stateKey string) *Event { // GetMembershipState returns the membership state of the given user ID in this room. If there is // no entry for this member, 'leave' is returned for consistency with left users. -func (room Room) GetMembershipState(userID string) string { - state := "leave" - event := room.GetStateEvent("m.room.member", userID) +func (room Room) GetMembershipState(userID string) Membership { + state := MembershipLeave + event := room.GetStateEvent(StateMember, userID) if event != nil { - membershipState, found := event.Content["membership"] - if found { - mState, isString := membershipState.(string) - if isString { - state = mState - } - } + state = event.Content.Membership } return state } @@ -45,6 +39,6 @@ func NewRoom(roomID string) *Room { // Init the State map and return a pointer to the Room return &Room{ ID: roomID, - State: make(map[string]map[string]*Event), + State: make(map[EventType]map[string]*Event), } } diff --git a/vendor/maunium.net/go/gomatrix/sync.go b/vendor/maunium.net/go/gomatrix/sync.go index e1233a4..09170d7 100644 --- a/vendor/maunium.net/go/gomatrix/sync.go +++ b/vendor/maunium.net/go/gomatrix/sync.go @@ -25,7 +25,7 @@ type Syncer interface { type DefaultSyncer struct { UserID string Store Storer - listeners map[string][]OnEventListener // event type to listeners array + listeners map[EventType][]OnEventListener // event type to listeners array } // OnEventListener can be used with DefaultSyncer.OnEventType to be informed of incoming events. @@ -36,7 +36,7 @@ func NewDefaultSyncer(userID string, store Storer) *DefaultSyncer { return &DefaultSyncer{ UserID: userID, Store: store, - listeners: make(map[string][]OnEventListener), + listeners: make(map[EventType][]OnEventListener), } } @@ -88,7 +88,7 @@ func (s *DefaultSyncer) ProcessResponse(res *RespSync, since string) (err error) // OnEventType allows callers to be notified when there are new events for the given event type. // There are no duplicate checks. -func (s *DefaultSyncer) OnEventType(eventType string, callback OnEventListener) { +func (s *DefaultSyncer) OnEventType(eventType EventType, callback OnEventListener) { _, exists := s.listeners[eventType] if !exists { s.listeners[eventType] = []OnEventListener{} @@ -112,13 +112,8 @@ func (s *DefaultSyncer) shouldProcessResponse(resp *RespSync, since string) bool for roomID, roomData := range resp.Rooms.Join { for i := len(roomData.Timeline.Events) - 1; i >= 0; i-- { e := roomData.Timeline.Events[i] - if e.Type == "m.room.member" && e.StateKey != nil && *e.StateKey == s.UserID { - m := e.Content["membership"] - mship, ok := m.(string) - if !ok { - continue - } - if mship == "join" { + if e.Type == StateMember && e.GetStateKey() == s.UserID { + if e.Content.Membership == "join" { _, ok := resp.Rooms.Join[roomID] if !ok { continue diff --git a/vendor/maunium.net/go/gomatrix/userids.go b/vendor/maunium.net/go/gomatrix/userids.go index 23e7807..70002c5 100644 --- a/vendor/maunium.net/go/gomatrix/userids.go +++ b/vendor/maunium.net/go/gomatrix/userids.go @@ -125,6 +125,6 @@ func ExtractUserLocalpart(userID string) (string, error) { } return strings.TrimPrefix( strings.SplitN(userID, ":", 2)[0], // @foo:bar:8448 => [ "@foo", "bar:8448" ] - "@", // remove "@" prefix + "@", // remove "@" prefix ), nil } diff --git a/vendor/maunium.net/go/maulogger/defaults.go b/vendor/maunium.net/go/maulogger/defaults.go index cd405e2..ac02801 100644 --- a/vendor/maunium.net/go/maulogger/defaults.go +++ b/vendor/maunium.net/go/maulogger/defaults.go @@ -1,20 +1,19 @@ // mauLogger - A logger for Go programs -// Copyright (C) 2016 Tulir Asokan +// Copyright (C) 2016-2018 Tulir Asokan // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. - +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. - +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// Package maulogger ... package maulogger import ( @@ -22,214 +21,249 @@ import ( ) // DefaultLogger ... -var DefaultLogger = Create() +var DefaultLogger = Create().(*BasicLogger) -// SetWriter formats the given parts with fmt.Sprint and log them with the SetWriter level +// SetWriter formats the given parts with fmt.Sprint and logs the result with the SetWriter level func SetWriter(w *os.File) { DefaultLogger.SetWriter(w) } -// OpenFile formats the given parts with fmt.Sprint and log them with the OpenFile level +// OpenFile formats the given parts with fmt.Sprint and logs the result with the OpenFile level func OpenFile() error { return DefaultLogger.OpenFile() } -// Close formats the given parts with fmt.Sprint and log them with the Close level +// Close formats the given parts with fmt.Sprint and logs the result with the Close level func Close() { DefaultLogger.Close() } -// CreateSublogger creates a Sublogger -func CreateSublogger(module string, DefaultLevel Level) *Sublogger { - return DefaultLogger.CreateSublogger(module, DefaultLevel) +// Sub creates a Sublogger +func Sub(module string) Logger { + return DefaultLogger.Sub(module) } -// Raw formats the given parts with fmt.Sprint and log them with the Raw level +// Raw formats the given parts with fmt.Sprint and logs the result with the Raw level func Raw(level Level, module, message string) { DefaultLogger.Raw(level, module, message) } -// Log formats the given parts with fmt.Sprint and log them with the Log level +// Log formats the given parts with fmt.Sprint and logs the result with the given level func Log(level Level, parts ...interface{}) { DefaultLogger.DefaultSub.Log(level, parts...) } -// Logln formats the given parts with fmt.Sprintln and log them with the Log level +// Logln formats the given parts with fmt.Sprintln and logs the result with the given level func Logln(level Level, parts ...interface{}) { DefaultLogger.DefaultSub.Logln(level, parts...) } -// Logf formats the given message and args with fmt.Sprintf and log them with the Log level +// Logf formats the given message and args with fmt.Sprintf and logs the result with the given level func Logf(level Level, message string, args ...interface{}) { DefaultLogger.DefaultSub.Logf(level, message, args...) } -// Debug formats the given parts with fmt.Sprint and log them with the Debug level +// Logfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the given level +func Logfln(level Level, message string, args ...interface{}) { + DefaultLogger.DefaultSub.Logfln(level, message, args...) +} + +// Debug formats the given parts with fmt.Sprint and logs the result with the Debug level func Debug(parts ...interface{}) { DefaultLogger.DefaultSub.Debug(parts...) } -// Debugln formats the given parts with fmt.Sprintln and log them with the Debug level +// Debugln formats the given parts with fmt.Sprintln and logs the result with the Debug level func Debugln(parts ...interface{}) { DefaultLogger.DefaultSub.Debugln(parts...) } -// Debugf formats the given message and args with fmt.Sprintf and log them with the Debug level +// Debugf formats the given message and args with fmt.Sprintf and logs the result with the Debug level func Debugf(message string, args ...interface{}) { DefaultLogger.DefaultSub.Debugf(message, args...) } -// Info formats the given parts with fmt.Sprint and log them with the Info level +// Info formats the given parts with fmt.Sprint and logs the result with the Info level func Info(parts ...interface{}) { DefaultLogger.DefaultSub.Info(parts...) } -// Infoln formats the given parts with fmt.Sprintln and log them with the Info level +// Infoln formats the given parts with fmt.Sprintln and logs the result with the Info level func Infoln(parts ...interface{}) { DefaultLogger.DefaultSub.Infoln(parts...) } -// Infof formats the given message and args with fmt.Sprintf and log them with the Info level +// Infof formats the given message and args with fmt.Sprintf and logs the result with the Info level func Infof(message string, args ...interface{}) { DefaultLogger.DefaultSub.Infof(message, args...) } -// Warn formats the given parts with fmt.Sprint and log them with the Warn level +// Warn formats the given parts with fmt.Sprint and logs the result with the Warn level func Warn(parts ...interface{}) { DefaultLogger.DefaultSub.Warn(parts...) } -// Warnln formats the given parts with fmt.Sprintln and log them with the Warn level +// Warnln formats the given parts with fmt.Sprintln and logs the result with the Warn level func Warnln(parts ...interface{}) { DefaultLogger.DefaultSub.Warnln(parts...) } -// Warnf formats the given message and args with fmt.Sprintf and log them with the Warn level +// Warnf formats the given message and args with fmt.Sprintf and logs the result with the Warn level func Warnf(message string, args ...interface{}) { DefaultLogger.DefaultSub.Warnf(message, args...) } -// Error formats the given parts with fmt.Sprint and log them with the Error level +// Error formats the given parts with fmt.Sprint and logs the result with the Error level func Error(parts ...interface{}) { DefaultLogger.DefaultSub.Error(parts...) } -// Errorln formats the given parts with fmt.Sprintln and log them with the Error level +// Errorln formats the given parts with fmt.Sprintln and logs the result with the Error level func Errorln(parts ...interface{}) { DefaultLogger.DefaultSub.Errorln(parts...) } -// Errorf formats the given message and args with fmt.Sprintf and log them with the Error level +// Errorf formats the given message and args with fmt.Sprintf and logs the result with the Error level func Errorf(message string, args ...interface{}) { DefaultLogger.DefaultSub.Errorf(message, args...) } -// Fatal formats the given parts with fmt.Sprint and log them with the Fatal level +// Fatal formats the given parts with fmt.Sprint and logs the result with the Fatal level func Fatal(parts ...interface{}) { DefaultLogger.DefaultSub.Fatal(parts...) } -// Fatalln formats the given parts with fmt.Sprintln and log them with the Fatal level +// Fatalln formats the given parts with fmt.Sprintln and logs the result with the Fatal level func Fatalln(parts ...interface{}) { DefaultLogger.DefaultSub.Fatalln(parts...) } -// Fatalf formats the given message and args with fmt.Sprintf and log them with the Fatal level +// Fatalf formats the given message and args with fmt.Sprintf and logs the result with the Fatal level func Fatalf(message string, args ...interface{}) { DefaultLogger.DefaultSub.Fatalf(message, args...) } -// Write formats the given parts with fmt.Sprint and log them with the Write level -func (log *Logger) Write(p []byte) (n int, err error) { +// Write formats the given parts with fmt.Sprint and logs the result with the Write level +func (log *BasicLogger) Write(p []byte) (n int, err error) { return log.DefaultSub.Write(p) } -// Log formats the given parts with fmt.Sprint and log them with the Log level -func (log *Logger) Log(level Level, parts ...interface{}) { +// Log formats the given parts with fmt.Sprint and logs the result with the given level +func (log *BasicLogger) Log(level Level, parts ...interface{}) { log.DefaultSub.Log(level, parts...) } -// Logln formats the given parts with fmt.Sprintln and log them with the Log level -func (log *Logger) Logln(level Level, parts ...interface{}) { +// Logln formats the given parts with fmt.Sprintln and logs the result with the given level +func (log *BasicLogger) Logln(level Level, parts ...interface{}) { log.DefaultSub.Logln(level, parts...) } -// Logf formats the given message and args with fmt.Sprintf and log them with the Log level -func (log *Logger) Logf(level Level, message string, args ...interface{}) { +// Logf formats the given message and args with fmt.Sprintf and logs the result with the given level +func (log *BasicLogger) Logf(level Level, message string, args ...interface{}) { log.DefaultSub.Logf(level, message, args...) } -// Debug formats the given parts with fmt.Sprint and log them with the Debug level -func (log *Logger) Debug(parts ...interface{}) { +// Logfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the given level +func (log *BasicLogger) Logfln(level Level, message string, args ...interface{}) { + log.DefaultSub.Logfln(level, message, args...) +} + +// Debug formats the given parts with fmt.Sprint and logs the result with the Debug level +func (log *BasicLogger) Debug(parts ...interface{}) { log.DefaultSub.Debug(parts...) } -// Debugln formats the given parts with fmt.Sprintln and log them with the Debug level -func (log *Logger) Debugln(parts ...interface{}) { +// Debugln formats the given parts with fmt.Sprintln and logs the result with the Debug level +func (log *BasicLogger) Debugln(parts ...interface{}) { log.DefaultSub.Debugln(parts...) } -// Debugf formats the given message and args with fmt.Sprintf and log them with the Debug level -func (log *Logger) Debugf(message string, args ...interface{}) { +// Debugf formats the given message and args with fmt.Sprintf and logs the result with the Debug level +func (log *BasicLogger) Debugf(message string, args ...interface{}) { log.DefaultSub.Debugf(message, args...) } -// Info formats the given parts with fmt.Sprint and log them with the Info level -func (log *Logger) Info(parts ...interface{}) { +// Debugfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Debug level +func (log *BasicLogger) Debugfln(message string, args ...interface{}) { + log.DefaultSub.Debugfln(message, args...) +} + +// Info formats the given parts with fmt.Sprint and logs the result with the Info level +func (log *BasicLogger) Info(parts ...interface{}) { log.DefaultSub.Info(parts...) } -// Infoln formats the given parts with fmt.Sprintln and log them with the Info level -func (log *Logger) Infoln(parts ...interface{}) { +// Infoln formats the given parts with fmt.Sprintln and logs the result with the Info level +func (log *BasicLogger) Infoln(parts ...interface{}) { log.DefaultSub.Infoln(parts...) } -// Infof formats the given message and args with fmt.Sprintf and log them with the Info level -func (log *Logger) Infof(message string, args ...interface{}) { +// Infofln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Info level +func (log *BasicLogger) Infofln(message string, args ...interface{}) { + log.DefaultSub.Infofln(message, args...) +} + +// Infof formats the given message and args with fmt.Sprintf and logs the result with the Info level +func (log *BasicLogger) Infof(message string, args ...interface{}) { log.DefaultSub.Infof(message, args...) } -// Warn formats the given parts with fmt.Sprint and log them with the Warn level -func (log *Logger) Warn(parts ...interface{}) { +// Warn formats the given parts with fmt.Sprint and logs the result with the Warn level +func (log *BasicLogger) Warn(parts ...interface{}) { log.DefaultSub.Warn(parts...) } -// Warnln formats the given parts with fmt.Sprintln and log them with the Warn level -func (log *Logger) Warnln(parts ...interface{}) { +// Warnln formats the given parts with fmt.Sprintln and logs the result with the Warn level +func (log *BasicLogger) Warnln(parts ...interface{}) { log.DefaultSub.Warnln(parts...) } -// Warnf formats the given message and args with fmt.Sprintf and log them with the Warn level -func (log *Logger) Warnf(message string, args ...interface{}) { +// Warnfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Warn level +func (log *BasicLogger) Warnfln(message string, args ...interface{}) { + log.DefaultSub.Warnfln(message, args...) +} + +// Warnf formats the given message and args with fmt.Sprintf and logs the result with the Warn level +func (log *BasicLogger) Warnf(message string, args ...interface{}) { log.DefaultSub.Warnf(message, args...) } -// Error formats the given parts with fmt.Sprint and log them with the Error level -func (log *Logger) Error(parts ...interface{}) { +// Error formats the given parts with fmt.Sprint and logs the result with the Error level +func (log *BasicLogger) Error(parts ...interface{}) { log.DefaultSub.Error(parts...) } -// Errorln formats the given parts with fmt.Sprintln and log them with the Error level -func (log *Logger) Errorln(parts ...interface{}) { +// Errorln formats the given parts with fmt.Sprintln and logs the result with the Error level +func (log *BasicLogger) Errorln(parts ...interface{}) { log.DefaultSub.Errorln(parts...) } -// Errorf formats the given message and args with fmt.Sprintf and log them with the Error level -func (log *Logger) Errorf(message string, args ...interface{}) { +// Errorf formats the given message and args with fmt.Sprintf and logs the result with the Error level +func (log *BasicLogger) Errorf(message string, args ...interface{}) { log.DefaultSub.Errorf(message, args...) } -// Fatal formats the given parts with fmt.Sprint and log them with the Fatal level -func (log *Logger) Fatal(parts ...interface{}) { +// Errorfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Error level +func (log *BasicLogger) Errorfln(message string, args ...interface{}) { + log.DefaultSub.Errorfln(message, args...) +} + +// Fatal formats the given parts with fmt.Sprint and logs the result with the Fatal level +func (log *BasicLogger) Fatal(parts ...interface{}) { log.DefaultSub.Fatal(parts...) } -// Fatalln formats the given parts with fmt.Sprintln and log them with the Fatal level -func (log *Logger) Fatalln(parts ...interface{}) { +// Fatalln formats the given parts with fmt.Sprintln and logs the result with the Fatal level +func (log *BasicLogger) Fatalln(parts ...interface{}) { log.DefaultSub.Fatalln(parts...) } -// Fatalf formats the given message and args with fmt.Sprintf and log them with the Fatal level -func (log *Logger) Fatalf(message string, args ...interface{}) { +// Fatalf formats the given message and args with fmt.Sprintf and logs the result with the Fatal level +func (log *BasicLogger) Fatalf(message string, args ...interface{}) { log.DefaultSub.Fatalf(message, args...) } + +// Fatalfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Fatal level +func (log *BasicLogger) Fatalfln(message string, args ...interface{}) { + log.DefaultSub.Fatalfln(message, args...) +} diff --git a/vendor/maunium.net/go/maulogger/level.go b/vendor/maunium.net/go/maulogger/level.go index 18f80a0..2c171b2 100644 --- a/vendor/maunium.net/go/maulogger/level.go +++ b/vendor/maunium.net/go/maulogger/level.go @@ -1,20 +1,19 @@ // mauLogger - A logger for Go programs -// Copyright (C) 2016 Tulir Asokan +// Copyright (C) 2016-2018 Tulir Asokan // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. - +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. - +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// Package maulogger ... package maulogger import ( @@ -29,9 +28,9 @@ type Level struct { var ( // LevelDebug is the level for debug messages. - LevelDebug = Level{Name: "DEBUG", Color: 36, Severity: 0} + LevelDebug = Level{Name: "DEBUG", Color: -1, Severity: 0} // LevelInfo is the level for basic log messages. - LevelInfo = Level{Name: "INFO", Color: -1, Severity: 10} + LevelInfo = Level{Name: "INFO", Color: 36, Severity: 10} // LevelWarn is the level saying that something went wrong, but the program will continue operating mostly normally. LevelWarn = Level{Name: "WARN", Color: 33, Severity: 50} // LevelError is the level saying that something went wrong and the program may not operate as expected, but will still continue. diff --git a/vendor/maunium.net/go/maulogger/logger.go b/vendor/maunium.net/go/maulogger/logger.go index e8649dd..5ce7c6c 100644 --- a/vendor/maunium.net/go/maulogger/logger.go +++ b/vendor/maunium.net/go/maulogger/logger.go @@ -1,20 +1,19 @@ // mauLogger - A logger for Go programs -// Copyright (C) 2016 Tulir Asokan +// Copyright (C) 2016-2018 Tulir Asokan // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. - +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. - +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// Package maulogger ... package maulogger import ( @@ -26,47 +25,57 @@ import ( // LoggerFileFormat ... type LoggerFileFormat func(now string, i int) string -// Logger ... -type Logger struct { +type BasicLogger struct { PrintLevel int FlushLineThreshold int FileTimeFormat string FileFormat LoggerFileFormat TimeFormat string FileMode os.FileMode - DefaultSub *Sublogger + DefaultSub Logger writer *os.File lines int prefixPrinted bool } -// GeneralLogger contains advanced logging functions and also implements io.Writer -type GeneralLogger interface { +// Logger contains advanced logging functions and also implements io.Writer +type Logger interface { + Sub(module string) Logger + GetParent() Logger + Write(p []byte) (n int, err error) + Log(level Level, parts ...interface{}) Logln(level Level, parts ...interface{}) Logf(level Level, message string, args ...interface{}) + Logfln(level Level, message string, args ...interface{}) + Debug(parts ...interface{}) Debugln(parts ...interface{}) Debugf(message string, args ...interface{}) + Debugfln(message string, args ...interface{}) Info(parts ...interface{}) Infoln(parts ...interface{}) Infof(message string, args ...interface{}) + Infofln(message string, args ...interface{}) Warn(parts ...interface{}) Warnln(parts ...interface{}) Warnf(message string, args ...interface{}) + Warnfln(message string, args ...interface{}) Error(parts ...interface{}) Errorln(parts ...interface{}) Errorf(message string, args ...interface{}) + Errorfln(message string, args ...interface{}) Fatal(parts ...interface{}) Fatalln(parts ...interface{}) Fatalf(message string, args ...interface{}) + Fatalfln(message string, args ...interface{}) } // Create a Logger -func Create() *Logger { - var log = &Logger{ +func Create() Logger { + var log = &BasicLogger{ PrintLevel: 10, FileTimeFormat: "2006-01-02", FileFormat: func(now string, i int) string { return fmt.Sprintf("%[1]s-%02[2]d.log", now, i) }, @@ -75,17 +84,21 @@ func Create() *Logger { FlushLineThreshold: 5, lines: 0, } - log.DefaultSub = log.CreateSublogger("", LevelInfo) + log.DefaultSub = log.Sub("") return log } -// SetWriter formats the given parts with fmt.Sprint and log them with the SetWriter level -func (log *Logger) SetWriter(w *os.File) { +func (log *BasicLogger) GetParent() Logger { + return nil +} + +// SetWriter formats the given parts with fmt.Sprint and logs the result with the SetWriter level +func (log *BasicLogger) SetWriter(w *os.File) { log.writer = w } -// OpenFile formats the given parts with fmt.Sprint and log them with the OpenFile level -func (log *Logger) OpenFile() error { +// OpenFile formats the given parts with fmt.Sprint and logs the result with the OpenFile level +func (log *BasicLogger) OpenFile() error { now := time.Now().Format(log.FileTimeFormat) i := 1 for ; ; i++ { @@ -106,15 +119,15 @@ func (log *Logger) OpenFile() error { return nil } -// Close formats the given parts with fmt.Sprint and log them with the Close level -func (log *Logger) Close() { +// Close formats the given parts with fmt.Sprint and logs the result with the Close level +func (log *BasicLogger) Close() { if log.writer != nil { log.writer.Close() } } -// Raw formats the given parts with fmt.Sprint and log them with the Raw level -func (log *Logger) Raw(level Level, module, message string) { +// Raw formats the given parts with fmt.Sprint and logs the result with the Raw level +func (log *BasicLogger) Raw(level Level, module, message string) { if !log.prefixPrinted { if len(module) == 0 { message = fmt.Sprintf("[%s] [%s] %s", time.Now().Format(log.TimeFormat), level.Name, message) diff --git a/vendor/maunium.net/go/maulogger/sublogger.go b/vendor/maunium.net/go/maulogger/sublogger.go index eeb7c3f..3b10120 100644 --- a/vendor/maunium.net/go/maulogger/sublogger.go +++ b/vendor/maunium.net/go/maulogger/sublogger.go @@ -1,39 +1,53 @@ // mauLogger - A logger for Go programs -// Copyright (C) 2016 Tulir Asokan +// Copyright (C) 2016-2018 Tulir Asokan // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. - +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. - +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// Package maulogger ... package maulogger import ( "fmt" ) -// Sublogger ... type Sublogger struct { - Parent *Logger + topLevel *BasicLogger + parent Logger Module string DefaultLevel Level } -// CreateSublogger creates a Sublogger -func (log *Logger) CreateSublogger(module string, DefaultLevel Level) *Sublogger { +// Sub creates a Sublogger +func (log *BasicLogger) Sub(module string) Logger { return &Sublogger{ - Parent: log, + topLevel: log, + parent: log, Module: module, - DefaultLevel: DefaultLevel, + DefaultLevel: LevelInfo, + } +} + +func (log *Sublogger) GetParent() Logger { + return log.parent +} + +// Sub creates a Sublogger +func (log *Sublogger) Sub(module string) Logger { + return &Sublogger{ + topLevel: log.topLevel, + parent: log, + Module: fmt.Sprintf("%s/%s", log.Module, module), + DefaultLevel: log.DefaultLevel, } } @@ -48,102 +62,132 @@ func (log *Sublogger) SetDefaultLevel(lvl Level) { } // SetParent changes the parent of this Sublogger -func (log *Sublogger) SetParent(parent *Logger) { - log.Parent = parent +func (log *Sublogger) SetParent(parent *BasicLogger) { + log.topLevel = parent } //Write ... func (log *Sublogger) Write(p []byte) (n int, err error) { - log.Parent.Raw(log.DefaultLevel, log.Module, string(p)) + log.topLevel.Raw(log.DefaultLevel, log.Module, string(p)) return len(p), nil } -// Log formats the given parts with fmt.Sprint and log them with the Log level +// Log formats the given parts with fmt.Sprint and logs the result with the given level func (log *Sublogger) Log(level Level, parts ...interface{}) { - log.Parent.Raw(level, "", fmt.Sprint(parts...)) + log.topLevel.Raw(level, "", fmt.Sprint(parts...)) } -// Logln formats the given parts with fmt.Sprintln and log them with the Log level +// Logln formats the given parts with fmt.Sprintln and logs the result with the given level func (log *Sublogger) Logln(level Level, parts ...interface{}) { - log.Parent.Raw(level, "", fmt.Sprintln(parts...)) + log.topLevel.Raw(level, "", fmt.Sprintln(parts...)) } -// Logf formats the given message and args with fmt.Sprintf and log them with the Log level +// Logf formats the given message and args with fmt.Sprintf and logs the result with the given level func (log *Sublogger) Logf(level Level, message string, args ...interface{}) { - log.Parent.Raw(level, "", fmt.Sprintf(message, args...)) + log.topLevel.Raw(level, "", fmt.Sprintf(message, args...)) } -// Debug formats the given parts with fmt.Sprint and log them with the Debug level +// Logfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the given level +func (log *Sublogger) Logfln(level Level, message string, args ...interface{}) { + log.topLevel.Raw(level, log.Module, fmt.Sprintf(message+"\n", args...)) +} + +// Debug formats the given parts with fmt.Sprint and logs the result with the Debug level func (log *Sublogger) Debug(parts ...interface{}) { - log.Parent.Raw(LevelDebug, log.Module, fmt.Sprint(parts...)) + log.topLevel.Raw(LevelDebug, log.Module, fmt.Sprint(parts...)) } -// Debugln formats the given parts with fmt.Sprintln and log them with the Debug level +// Debugln formats the given parts with fmt.Sprintln and logs the result with the Debug level func (log *Sublogger) Debugln(parts ...interface{}) { - log.Parent.Raw(LevelDebug, log.Module, fmt.Sprintln(parts...)) + log.topLevel.Raw(LevelDebug, log.Module, fmt.Sprintln(parts...)) } -// Debugf formats the given message and args with fmt.Sprintf and log them with the Debug level +// Debugf formats the given message and args with fmt.Sprintf and logs the result with the Debug level func (log *Sublogger) Debugf(message string, args ...interface{}) { - log.Parent.Raw(LevelDebug, log.Module, fmt.Sprintf(message, args...)) + log.topLevel.Raw(LevelDebug, log.Module, fmt.Sprintf(message, args...)) } -// Info formats the given parts with fmt.Sprint and log them with the Info level +// Debugfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Debug level +func (log *Sublogger) Debugfln(message string, args ...interface{}) { + log.topLevel.Raw(LevelDebug, log.Module, fmt.Sprintf(message+"\n", args...)) +} + +// Info formats the given parts with fmt.Sprint and logs the result with the Info level func (log *Sublogger) Info(parts ...interface{}) { - log.Parent.Raw(LevelInfo, log.Module, fmt.Sprint(parts...)) + log.topLevel.Raw(LevelInfo, log.Module, fmt.Sprint(parts...)) } -// Infoln formats the given parts with fmt.Sprintln and log them with the Info level +// Infoln formats the given parts with fmt.Sprintln and logs the result with the Info level func (log *Sublogger) Infoln(parts ...interface{}) { - log.Parent.Raw(LevelInfo, log.Module, fmt.Sprintln(parts...)) + log.topLevel.Raw(LevelInfo, log.Module, fmt.Sprintln(parts...)) } -// Infof formats the given message and args with fmt.Sprintf and log them with the Info level +// Infof formats the given message and args with fmt.Sprintf and logs the result with the Info level func (log *Sublogger) Infof(message string, args ...interface{}) { - log.Parent.Raw(LevelInfo, log.Module, fmt.Sprintf(message, args...)) + log.topLevel.Raw(LevelInfo, log.Module, fmt.Sprintf(message, args...)) } -// Warn formats the given parts with fmt.Sprint and log them with the Warn level +// Infofln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Info level +func (log *Sublogger) Infofln(message string, args ...interface{}) { + log.topLevel.Raw(LevelInfo, log.Module, fmt.Sprintf(message+"\n", args...)) +} + +// Warn formats the given parts with fmt.Sprint and logs the result with the Warn level func (log *Sublogger) Warn(parts ...interface{}) { - log.Parent.Raw(LevelWarn, log.Module, fmt.Sprint(parts...)) + log.topLevel.Raw(LevelWarn, log.Module, fmt.Sprint(parts...)) } -// Warnln formats the given parts with fmt.Sprintln and log them with the Warn level +// Warnln formats the given parts with fmt.Sprintln and logs the result with the Warn level func (log *Sublogger) Warnln(parts ...interface{}) { - log.Parent.Raw(LevelWarn, log.Module, fmt.Sprintln(parts...)) + log.topLevel.Raw(LevelWarn, log.Module, fmt.Sprintln(parts...)) } -// Warnf formats the given message and args with fmt.Sprintf and log them with the Warn level +// Warnf formats the given message and args with fmt.Sprintf and logs the result with the Warn level func (log *Sublogger) Warnf(message string, args ...interface{}) { - log.Parent.Raw(LevelWarn, log.Module, fmt.Sprintf(message, args...)) + log.topLevel.Raw(LevelWarn, log.Module, fmt.Sprintf(message, args...)) } -// Error formats the given parts with fmt.Sprint and log them with the Error level +// Warnfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Warn level +func (log *Sublogger) Warnfln(message string, args ...interface{}) { + log.topLevel.Raw(LevelWarn, log.Module, fmt.Sprintf(message+"\n", args...)) +} + +// Error formats the given parts with fmt.Sprint and logs the result with the Error level func (log *Sublogger) Error(parts ...interface{}) { - log.Parent.Raw(LevelError, log.Module, fmt.Sprint(parts...)) + log.topLevel.Raw(LevelError, log.Module, fmt.Sprint(parts...)) } -// Errorln formats the given parts with fmt.Sprintln and log them with the Error level +// Errorln formats the given parts with fmt.Sprintln and logs the result with the Error level func (log *Sublogger) Errorln(parts ...interface{}) { - log.Parent.Raw(LevelError, log.Module, fmt.Sprintln(parts...)) + log.topLevel.Raw(LevelError, log.Module, fmt.Sprintln(parts...)) } -// Errorf formats the given message and args with fmt.Sprintf and log them with the Error level +// Errorf formats the given message and args with fmt.Sprintf and logs the result with the Error level func (log *Sublogger) Errorf(message string, args ...interface{}) { - log.Parent.Raw(LevelError, log.Module, fmt.Sprintf(message, args...)) + log.topLevel.Raw(LevelError, log.Module, fmt.Sprintf(message, args...)) } -// Fatal formats the given parts with fmt.Sprint and log them with the Fatal level +// Errorfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Error level +func (log *Sublogger) Errorfln(message string, args ...interface{}) { + log.topLevel.Raw(LevelError, log.Module, fmt.Sprintf(message+"\n", args...)) +} + +// Fatal formats the given parts with fmt.Sprint and logs the result with the Fatal level func (log *Sublogger) Fatal(parts ...interface{}) { - log.Parent.Raw(LevelFatal, log.Module, fmt.Sprint(parts...)) + log.topLevel.Raw(LevelFatal, log.Module, fmt.Sprint(parts...)) } -// Fatalln formats the given parts with fmt.Sprintln and log them with the Fatal level +// Fatalln formats the given parts with fmt.Sprintln and logs the result with the Fatal level func (log *Sublogger) Fatalln(parts ...interface{}) { - log.Parent.Raw(LevelFatal, log.Module, fmt.Sprintln(parts...)) + log.topLevel.Raw(LevelFatal, log.Module, fmt.Sprintln(parts...)) } -// Fatalf formats the given message and args with fmt.Sprintf and log them with the Fatal level +// Fatalf formats the given message and args with fmt.Sprintf and logs the result with the Fatal level func (log *Sublogger) Fatalf(message string, args ...interface{}) { - log.Parent.Raw(LevelFatal, log.Module, fmt.Sprintf(message, args...)) + log.topLevel.Raw(LevelFatal, log.Module, fmt.Sprintf(message, args...)) +} + +// Fatalfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Fatal level +func (log *Sublogger) Fatalfln(message string, args ...interface{}) { + log.topLevel.Raw(LevelFatal, log.Module, fmt.Sprintf(message+"\n", args...)) }