Update dependencies

This commit is contained in:
Tulir Asokan 2018-09-20 01:28:37 +03:00
parent 22ef8195dd
commit 2a0106da26
17 changed files with 1229 additions and 347 deletions

13
Gopkg.lock generated
View file

@ -32,7 +32,7 @@
"html", "html",
"html/atom" "html/atom"
] ]
revision = "32a936f46389aa10549d60bd7833e54b01685d09" revision = "26e67e76b6c3f6ce91f7c52def5af501b4e0f3a2"
[[projects]] [[projects]]
name = "gopkg.in/russross/blackfriday.v2" name = "gopkg.in/russross/blackfriday.v2"
@ -49,8 +49,11 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
name = "maunium.net/go/gomatrix" name = "maunium.net/go/gomatrix"
packages = ["."] packages = [
revision = "a42d596e48b3ded4a95eae8065fc5cf13afeeacc" ".",
"format"
]
revision = "920b154a410aeb5a55200d7b21363732abff3502"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -62,11 +65,11 @@
branch = "master" branch = "master"
name = "maunium.net/go/maulogger" name = "maunium.net/go/maulogger"
packages = ["."] packages = ["."]
revision = "743010e44ca0ec7bebc89f167d3c8aa7c8e49b13" revision = "ed98745dedb5f9296c1b2a0ed9424d7347d7e7d4"
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
analyzer-version = 1 analyzer-version = 1
inputs-digest = "c21e70a3427dd8c0a380c35ab64e74939c873860c3d764d2f41d63c39115b72b" inputs-digest = "6b56fff780b66591381a1d1c4572951bbad3deea30e0774d34721d89adeee379"
solver-name = "gps-cdcl" solver-name = "gps-cdcl"
solver-version = 1 solver-version = 1

View file

@ -37,10 +37,6 @@
branch = "master" branch = "master"
name = "golang.org/x/net" name = "golang.org/x/net"
[[constraint]]
name = "gopkg.in/russross/blackfriday.v2"
version = "2.0.0"
[[constraint]] [[constraint]]
name = "gopkg.in/yaml.v2" name = "gopkg.in/yaml.v2"
version = "2.2.1" version = "2.2.1"

View file

@ -209,27 +209,6 @@ loop:
p.oe = p.oe[:i+1] 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 // 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. // of open elements if it is an element node.
func (p *parser) addChild(n *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) template.AppendChild(n)
return return
} }
@ -679,11 +658,16 @@ func inHeadIM(p *parser) bool {
if !p.oe.contains(a.Template) { if !p.oe.contains(a.Template) {
return true return true
} }
p.generateAllImpliedEndTags() // TODO: remove this divergence from the HTML5 spec.
if n := p.oe.top(); n.DataAtom != a.Template { //
return true // 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.clearActiveFormattingElements()
p.templateStack.pop() p.templateStack.pop()
p.resetInsertionMode() p.resetInsertionMode()
@ -1074,13 +1058,7 @@ func inBodyIM(p *parser) bool {
p.acknowledgeSelfClosingTag() p.acknowledgeSelfClosingTag()
} }
return true return true
case a.Frame: case a.Caption, a.Col, a.Colgroup, a.Frame, a.Head, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
// 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:
// Ignore the token. // Ignore the token.
default: default:
p.reconstructActiveFormattingElements() p.reconstructActiveFormattingElements()
@ -1351,9 +1329,6 @@ func textIM(p *parser) bool {
// Section 12.2.6.4.9. // Section 12.2.6.4.9.
func inTableIM(p *parser) bool { func inTableIM(p *parser) bool {
switch p.tok.Type { switch p.tok.Type {
case ErrorToken:
// Stop parsing.
return true
case TextToken: case TextToken:
p.tok.Data = strings.Replace(p.tok.Data, "\x00", "", -1) p.tok.Data = strings.Replace(p.tok.Data, "\x00", "", -1)
switch p.oe.top().DataAtom { switch p.oe.top().DataAtom {
@ -1448,6 +1423,8 @@ func inTableIM(p *parser) bool {
case DoctypeToken: case DoctypeToken:
// Ignore the token. // Ignore the token.
return true return true
case ErrorToken:
return inBodyIM(p)
} }
p.fosterParenting = true p.fosterParenting = true
@ -1550,6 +1527,8 @@ func inColumnGroupIM(p *parser) bool {
case a.Template: case a.Template:
return inHeadIM(p) return inHeadIM(p)
} }
case ErrorToken:
return inBodyIM(p)
} }
if p.oe.top().DataAtom != a.Colgroup { if p.oe.top().DataAtom != a.Colgroup {
return true return true
@ -1714,9 +1693,6 @@ func inCellIM(p *parser) bool {
// Section 12.2.6.4.16. // Section 12.2.6.4.16.
func inSelectIM(p *parser) bool { func inSelectIM(p *parser) bool {
switch p.tok.Type { switch p.tok.Type {
case ErrorToken:
// Stop parsing.
return true
case TextToken: case TextToken:
p.addText(strings.Replace(p.tok.Data, "\x00", "", -1)) p.addText(strings.Replace(p.tok.Data, "\x00", "", -1))
case StartTagToken: case StartTagToken:
@ -1780,6 +1756,8 @@ func inSelectIM(p *parser) bool {
case DoctypeToken: case DoctypeToken:
// Ignore the token. // Ignore the token.
return true return true
case ErrorToken:
return inBodyIM(p)
} }
return true return true
@ -1846,16 +1824,27 @@ func inTemplateIM(p *parser) bool {
// Ignore the token. // Ignore the token.
return true return true
} }
} case ErrorToken:
if !p.oe.contains(a.Template) { if !p.oe.contains(a.Template) {
// Ignore the token. // Ignore the token.
return true return true
} }
p.popUntil(defaultScope, a.Template) // 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.clearActiveFormattingElements()
p.templateStack.pop() p.templateStack.pop()
p.resetInsertionMode() p.resetInsertionMode()
return false return false
}
return false
} }
// Section 12.2.6.4.19. // Section 12.2.6.4.19.
@ -1928,11 +1917,6 @@ func inFramesetIM(p *parser) bool {
p.acknowledgeSelfClosingTag() p.acknowledgeSelfClosingTag()
case a.Noframes: case a.Noframes:
return inHeadIM(p) 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: case EndTagToken:
switch p.tok.DataAtom { 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. // 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 <tag> listed in r's data, and nodes' parents can
// differ from the nesting implied by a naive processing of start and end
// <tag>s. Conversely, explicit <tag>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. // The input is assumed to be UTF-8 encoded.
func Parse(r io.Reader) (*Node, error) { func Parse(r io.Reader) (*Node, error) {
p := &parser{ 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 // 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 // found. If the fragment is the InnerHTML for an existing element, pass that
// element in context. // element in context.
//
// It has the same intricacies as Parse.
func ParseFragment(r io.Reader, context *Node) ([]*Node, error) { func ParseFragment(r io.Reader, context *Node) ([]*Node, error) {
contextTag := "" contextTag := ""
if context != nil { if context != nil {

View file

@ -6,13 +6,16 @@ package gomatrix
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"maunium.net/go/maulogger"
"net/http" "net/http"
"net/url" "net/url"
"path" "path"
"strconv" "strconv"
"strings"
"sync" "sync"
"time" "time"
) )
@ -26,6 +29,7 @@ type Client struct {
Client *http.Client // The underlying HTTP client which will be used to make HTTP requests. Client *http.Client // The underlying HTTP client which will be used to make HTTP requests.
Syncer Syncer // The thing which can process /sync responses Syncer Syncer // The thing which can process /sync responses
Store Storer // The thing which can store rooms/tokens/ids 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, // 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. // 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. // HTTPError An HTTP Error response, which may wrap an underlying native Go Error.
type HTTPError struct { type HTTPError struct {
WrappedError error WrappedError error
RespError *RespError
Message string Message string
Code int Code int
} }
@ -177,6 +182,14 @@ func (cli *Client) StopSync() {
cli.incrementSyncingID() 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. // MakeRequest makes a JSON HTTP request to the given URL.
// If "resBody" is not nil, the response body will be json.Unmarshalled into it. // 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) { func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{}, resBody interface{}) ([]byte, error) {
var req *http.Request var req *http.Request
var err error var err error
logBody := "{}"
if reqBody != nil { if reqBody != nil {
var jsonStr []byte var jsonStr []byte
jsonStr, err = json.Marshal(reqBody) jsonStr, err = json.Marshal(reqBody)
if err != nil { if err != nil {
return nil, err return nil, err
} }
logBody = string(jsonStr)
req, err = http.NewRequest(method, httpURL, bytes.NewBuffer(jsonStr)) req, err = http.NewRequest(method, httpURL, bytes.NewBuffer(jsonStr))
} else { } else {
req, err = http.NewRequest(method, httpURL, nil) req, err = http.NewRequest(method, httpURL, nil)
@ -201,6 +216,7 @@ func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{
return nil, err return nil, err
} }
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
cli.LogRequest(req, logBody)
res, err := cli.Client.Do(req) res, err := cli.Client.Do(req)
if res != nil { if res != nil {
defer res.Body.Close() defer res.Body.Close()
@ -211,9 +227,11 @@ func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{
contents, err := ioutil.ReadAll(res.Body) contents, err := ioutil.ReadAll(res.Body)
if res.StatusCode/100 != 2 { // not 2xx if res.StatusCode/100 != 2 { // not 2xx
var wrap error var wrap error
var respErr RespError respErr := &RespError{}
if _ = json.Unmarshal(contents, &respErr); respErr.ErrCode != "" { if _ = json.Unmarshal(contents, respErr); respErr.ErrCode != "" {
wrap = respErr wrap = respErr
} else {
respErr = nil
} }
// If we failed to decode as RespError, don't just drop the HTTP body, include it in the // 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, Code: res.StatusCode,
Message: msg, Message: msg,
WrappedError: wrap, WrappedError: wrap,
RespError: respErr,
} }
} }
if err != nil { if err != nil {
@ -342,7 +361,7 @@ func (cli *Client) RegisterDummy(req *ReqRegister) (*RespRegister, error) {
} }
} }
if res == nil { 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 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 // 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. // 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() 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) _, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp)
return 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 // 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. // 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) { func (cli *Client) SendStateEvent(roomID string, eventType EventType, stateKey string, contentJSON interface{}) (resp *RespSendEvent, err error) {
urlPath := cli.BuildURL("rooms", roomID, "state", eventType, stateKey) 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) _, err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp)
return return
} }
@ -460,16 +500,17 @@ 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 // 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 // See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-text
func (cli *Client) SendText(roomID, text string) (*RespSendEvent, error) { func (cli *Client) SendText(roomID, text string) (*RespSendEvent, error) {
return cli.SendMessageEvent(roomID, "m.room.message", return cli.SendMessageEvent(roomID, EventMessage, Content{
TextMessage{"m.text", text}) MsgType: MsgText,
Body: text,
})
} }
// SendImage sends an m.room.message event into the given room with a msgtype of m.image // 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 // See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-image
func (cli *Client) SendImage(roomID, body, url string) (*RespSendEvent, error) { func (cli *Client) SendImage(roomID, body, url string) (*RespSendEvent, error) {
return cli.SendMessageEvent(roomID, "m.room.message", return cli.SendMessageEvent(roomID, EventMessage, Content{
ImageMessage{ MsgType: MsgImage,
MsgType: "m.image",
Body: body, Body: body,
URL: url, URL: url,
}) })
@ -478,9 +519,8 @@ func (cli *Client) SendImage(roomID, body, url string) (*RespSendEvent, error) {
// SendVideo sends an m.room.message event into the given room with a msgtype of m.video // 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 // See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
func (cli *Client) SendVideo(roomID, body, url string) (*RespSendEvent, error) { func (cli *Client) SendVideo(roomID, body, url string) (*RespSendEvent, error) {
return cli.SendMessageEvent(roomID, "m.room.message", return cli.SendMessageEvent(roomID, EventMessage, Content{
VideoMessage{ MsgType: MsgVideo,
MsgType: "m.video",
Body: body, Body: body,
URL: url, URL: url,
}) })
@ -489,8 +529,10 @@ func (cli *Client) SendVideo(roomID, body, url string) (*RespSendEvent, error) {
// SendNotice sends an m.room.message event into the given room with a msgtype of m.notice // 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 // See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-notice
func (cli *Client) SendNotice(roomID, text string) (*RespSendEvent, error) { func (cli *Client) SendNotice(roomID, text string) (*RespSendEvent, error) {
return cli.SendMessageEvent(roomID, "m.room.message", return cli.SendMessageEvent(roomID, EventMessage, Content{
TextMessage{"m.notice", text}) 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 // 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 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 // 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. // 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 // 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) { func (cli *Client) StateEvent(roomID string, eventType EventType, stateKey string, outContent interface{}) (err error) {
u := cli.BuildURL("rooms", roomID, "state", eventType, stateKey) u := cli.BuildURL("rooms", roomID, "state", eventType.String(), stateKey)
_, err = cli.MakeRequest("GET", u, nil, outContent) _, err = cli.MakeRequest("GET", u, nil, outContent)
return return
} }
@ -587,18 +636,48 @@ func (cli *Client) UploadLink(link string) (*RespMediaUpload, error) {
if err != nil { if err != nil {
return nil, err 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. // 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 // 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) req, err := http.NewRequest("POST", cli.BuildBaseURL("_matrix/media/r0/upload"), content)
if err != nil { if err != nil {
return nil, err return nil, err
} }
req.Header.Set("Content-Type", contentType) req.Header.Set("Content-Type", contentType)
req.ContentLength = contentLength req.ContentLength = contentLength
cli.LogRequest(req, fmt.Sprintf("%d bytes", contentLength))
res, err := cli.Client.Do(req) res, err := cli.Client.Do(req)
if res != nil { if res != nil {
defer res.Body.Close() defer res.Body.Close()

View file

@ -1,110 +1,447 @@
package gomatrix package gomatrix
import ( import (
"html" "encoding/json"
"regexp" "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. // Event represents a single Matrix event.
type Event struct { type Event struct {
StateKey *string `json:"state_key,omitempty"` // The state key for the event. Only present on State Events. 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 Sender string `json:"sender"` // The user ID of the sender of the event
Type string `json:"type"` // The event type 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 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 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) 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. 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 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. 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 { type Unsigned struct {
PrevContent map[string]interface{} `json:"prev_content,omitempty"` PrevContent *Content `json:"prev_content,omitempty"`
PrevSender string `json:"prev_sender,omitempty"` PrevSender string `json:"prev_sender,omitempty"`
ReplacesState string `json:"replaces_state,omitempty"` ReplacesState string `json:"replaces_state,omitempty"`
Age int64 `json:"age"` 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 type MatchedPassiveCommand struct {
// present and is a string. // Matched string `json:"matched"`
func (event *Event) Body() (body string, ok bool) { // Value string `json:"value"`
value, exists := event.Content["body"] Captured [][]string `json:"captured"`
if !exists {
return 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))
return
} }
// MessageType returns the value of the "msgtype" key in the event content if func (content *Content) GetCommand() *MatchedCommand {
// it is present and is a string. if content.Command == nil {
func (event *Event) MessageType() (msgtype string, ok bool) { content.Command = &MatchedCommand{}
value, exists := event.Content["msgtype"]
if !exists {
return
} }
msgtype, ok = value.(string) 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 return
} }
// TextMessage is the contents of a Matrix formated message event. func (content *Content) UnmarshalMember() (m Member, err error) {
type TextMessage struct { err = json.Unmarshal(content.VeryRaw, &m)
MsgType string `json:"msgtype"` return
Body string `json:"body"`
} }
// ImageInfo contains info about an image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image func (content *Content) UnmarshalCanonicalAlias() (ca CanonicalAlias, err error) {
type ImageInfo struct { err = json.Unmarshal(content.VeryRaw, &ca)
Height uint `json:"h,omitempty"` return
Width uint `json:"w,omitempty"`
Mimetype string `json:"mimetype,omitempty"`
Size uint `json:"size,omitempty"`
} }
// VideoInfo contains info about a video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video func (content *Content) GetInfo() *FileInfo {
type VideoInfo struct { if content.Info == nil {
Mimetype string `json:"mimetype,omitempty"` content.Info = &FileInfo{}
ThumbnailInfo ImageInfo `json:"thumbnail_info"` }
return content.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"` ThumbnailURL string `json:"thumbnail_url,omitempty"`
Height uint `json:"h,omitempty"` Height int `json:"h,omitempty"`
Width uint `json:"w,omitempty"` Width int `json:"w,omitempty"`
Duration uint `json:"duration,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 func (fileInfo *FileInfo) GetThumbnailInfo() *FileInfo {
type VideoMessage struct { if fileInfo.ThumbnailInfo == nil {
MsgType string `json:"msgtype"` fileInfo.ThumbnailInfo = &FileInfo{}
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,
} }
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"`
} }

256
vendor/maunium.net/go/gomatrix/format/htmlparser.go generated vendored Normal file
View file

@ -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)
}

37
vendor/maunium.net/go/gomatrix/format/markdown.go generated vendored Normal file
View file

@ -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),
}
}

96
vendor/maunium.net/go/gomatrix/reply.go generated vendored Normal file
View file

@ -0,0 +1,96 @@
package gomatrix
import (
"fmt"
"regexp"
"strings"
"golang.org/x/net/html"
)
var HTMLReplyFallbackRegex = regexp.MustCompile(`^<mx-reply>[\s\S]+?</mx-reply>`)
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 = `<mx-reply><blockquote>
<a href="https://matrix.to/#/%s/%s">In reply to</a>
<a href="https://matrix.to/#/%s">%s</a>
%s
</blockquote></mx-reply>
`
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
}
}

View file

@ -31,7 +31,7 @@ type ReqCreateRoom struct {
Invite []string `json:"invite,omitempty"` Invite []string `json:"invite,omitempty"`
Invite3PID []ReqInvite3PID `json:"invite_3pid,omitempty"` Invite3PID []ReqInvite3PID `json:"invite_3pid,omitempty"`
CreationContent map[string]interface{} `json:"creation_content,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"` Preset string `json:"preset,omitempty"`
IsDirect bool `json:"is_direct,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 // 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 { type ReqTyping struct {
Typing bool `json:"typing"` Typing bool `json:"typing"`
Timeout int64 `json:"timeout"` Timeout int64 `json:"timeout,omitempty"`
}
type ReqPresence struct {
Presence string `json:"presence"`
} }

View file

@ -64,7 +64,7 @@ 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 // 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 { type RespMessages struct {
Start string `json:"start"` Start string `json:"start"`
Chunk []Event `json:"chunk"` Chunk []*Event `json:"chunk"`
End string `json:"end"` End string `json:"end"`
} }

View file

@ -3,7 +3,7 @@ package gomatrix
// Room represents a single Matrix room. // Room represents a single Matrix room.
type Room struct { type Room struct {
ID string 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 // 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. // 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] stateEventMap, _ := room.State[eventType]
event, _ := stateEventMap[stateKey] event, _ := stateEventMap[stateKey]
return event 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 // 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. // no entry for this member, 'leave' is returned for consistency with left users.
func (room Room) GetMembershipState(userID string) string { func (room Room) GetMembershipState(userID string) Membership {
state := "leave" state := MembershipLeave
event := room.GetStateEvent("m.room.member", userID) event := room.GetStateEvent(StateMember, userID)
if event != nil { if event != nil {
membershipState, found := event.Content["membership"] state = event.Content.Membership
if found {
mState, isString := membershipState.(string)
if isString {
state = mState
}
}
} }
return state return state
} }
@ -45,6 +39,6 @@ func NewRoom(roomID string) *Room {
// Init the State map and return a pointer to the Room // Init the State map and return a pointer to the Room
return &Room{ return &Room{
ID: roomID, ID: roomID,
State: make(map[string]map[string]*Event), State: make(map[EventType]map[string]*Event),
} }
} }

View file

@ -25,7 +25,7 @@ type Syncer interface {
type DefaultSyncer struct { type DefaultSyncer struct {
UserID string UserID string
Store Storer 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. // 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{ return &DefaultSyncer{
UserID: userID, UserID: userID,
Store: store, 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. // OnEventType allows callers to be notified when there are new events for the given event type.
// There are no duplicate checks. // 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] _, exists := s.listeners[eventType]
if !exists { if !exists {
s.listeners[eventType] = []OnEventListener{} 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 roomID, roomData := range resp.Rooms.Join {
for i := len(roomData.Timeline.Events) - 1; i >= 0; i-- { for i := len(roomData.Timeline.Events) - 1; i >= 0; i-- {
e := roomData.Timeline.Events[i] e := roomData.Timeline.Events[i]
if e.Type == "m.room.member" && e.StateKey != nil && *e.StateKey == s.UserID { if e.Type == StateMember && e.GetStateKey() == s.UserID {
m := e.Content["membership"] if e.Content.Membership == "join" {
mship, ok := m.(string)
if !ok {
continue
}
if mship == "join" {
_, ok := resp.Rooms.Join[roomID] _, ok := resp.Rooms.Join[roomID]
if !ok { if !ok {
continue continue

View file

@ -1,20 +1,19 @@
// mauLogger - A logger for Go programs // 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 // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// Package maulogger ...
package maulogger package maulogger
import ( import (
@ -22,214 +21,249 @@ import (
) )
// DefaultLogger ... // 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) { func SetWriter(w *os.File) {
DefaultLogger.SetWriter(w) 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 { func OpenFile() error {
return DefaultLogger.OpenFile() 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() { func Close() {
DefaultLogger.Close() DefaultLogger.Close()
} }
// CreateSublogger creates a Sublogger // Sub creates a Sublogger
func CreateSublogger(module string, DefaultLevel Level) *Sublogger { func Sub(module string) Logger {
return DefaultLogger.CreateSublogger(module, DefaultLevel) 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) { func Raw(level Level, module, message string) {
DefaultLogger.Raw(level, module, message) 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{}) { func Log(level Level, parts ...interface{}) {
DefaultLogger.DefaultSub.Log(level, parts...) 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{}) { func Logln(level Level, parts ...interface{}) {
DefaultLogger.DefaultSub.Logln(level, parts...) 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{}) { func Logf(level Level, message string, args ...interface{}) {
DefaultLogger.DefaultSub.Logf(level, message, args...) 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{}) { func Debug(parts ...interface{}) {
DefaultLogger.DefaultSub.Debug(parts...) 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{}) { func Debugln(parts ...interface{}) {
DefaultLogger.DefaultSub.Debugln(parts...) 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{}) { func Debugf(message string, args ...interface{}) {
DefaultLogger.DefaultSub.Debugf(message, args...) 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{}) { func Info(parts ...interface{}) {
DefaultLogger.DefaultSub.Info(parts...) 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{}) { func Infoln(parts ...interface{}) {
DefaultLogger.DefaultSub.Infoln(parts...) 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{}) { func Infof(message string, args ...interface{}) {
DefaultLogger.DefaultSub.Infof(message, args...) 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{}) { func Warn(parts ...interface{}) {
DefaultLogger.DefaultSub.Warn(parts...) 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{}) { func Warnln(parts ...interface{}) {
DefaultLogger.DefaultSub.Warnln(parts...) 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{}) { func Warnf(message string, args ...interface{}) {
DefaultLogger.DefaultSub.Warnf(message, args...) 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{}) { func Error(parts ...interface{}) {
DefaultLogger.DefaultSub.Error(parts...) 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{}) { func Errorln(parts ...interface{}) {
DefaultLogger.DefaultSub.Errorln(parts...) 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{}) { func Errorf(message string, args ...interface{}) {
DefaultLogger.DefaultSub.Errorf(message, args...) 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{}) { func Fatal(parts ...interface{}) {
DefaultLogger.DefaultSub.Fatal(parts...) 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{}) { func Fatalln(parts ...interface{}) {
DefaultLogger.DefaultSub.Fatalln(parts...) 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{}) { func Fatalf(message string, args ...interface{}) {
DefaultLogger.DefaultSub.Fatalf(message, args...) DefaultLogger.DefaultSub.Fatalf(message, args...)
} }
// Write formats the given parts with fmt.Sprint and log them with the Write level // Write formats the given parts with fmt.Sprint and logs the result with the Write level
func (log *Logger) Write(p []byte) (n int, err error) { func (log *BasicLogger) Write(p []byte) (n int, err error) {
return log.DefaultSub.Write(p) return log.DefaultSub.Write(p)
} }
// 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 *Logger) Log(level Level, parts ...interface{}) { func (log *BasicLogger) Log(level Level, parts ...interface{}) {
log.DefaultSub.Log(level, parts...) log.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 (log *Logger) Logln(level Level, parts ...interface{}) { func (log *BasicLogger) Logln(level Level, parts ...interface{}) {
log.DefaultSub.Logln(level, parts...) log.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 (log *Logger) Logf(level Level, message string, args ...interface{}) { func (log *BasicLogger) Logf(level Level, message string, args ...interface{}) {
log.DefaultSub.Logf(level, message, args...) log.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 (log *Logger) Debug(parts ...interface{}) { 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...) log.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 (log *Logger) Debugln(parts ...interface{}) { func (log *BasicLogger) Debugln(parts ...interface{}) {
log.DefaultSub.Debugln(parts...) log.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 (log *Logger) Debugf(message string, args ...interface{}) { func (log *BasicLogger) Debugf(message string, args ...interface{}) {
log.DefaultSub.Debugf(message, args...) log.DefaultSub.Debugf(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 *Logger) Info(parts ...interface{}) { 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...) log.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 (log *Logger) Infoln(parts ...interface{}) { func (log *BasicLogger) Infoln(parts ...interface{}) {
log.DefaultSub.Infoln(parts...) log.DefaultSub.Infoln(parts...)
} }
// Infof formats the given message and args with fmt.Sprintf and log them with the Info level // Infofln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Info level
func (log *Logger) Infof(message string, args ...interface{}) { 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...) log.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 (log *Logger) Warn(parts ...interface{}) { func (log *BasicLogger) Warn(parts ...interface{}) {
log.DefaultSub.Warn(parts...) log.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 (log *Logger) Warnln(parts ...interface{}) { func (log *BasicLogger) Warnln(parts ...interface{}) {
log.DefaultSub.Warnln(parts...) log.DefaultSub.Warnln(parts...)
} }
// Warnf formats the given message and args with fmt.Sprintf and log them with the Warn level // Warnfln formats the given message and args with fmt.Sprintf, appends a newline and logs the result with the Warn level
func (log *Logger) Warnf(message string, args ...interface{}) { 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...) log.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 (log *Logger) Error(parts ...interface{}) { func (log *BasicLogger) Error(parts ...interface{}) {
log.DefaultSub.Error(parts...) log.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 (log *Logger) Errorln(parts ...interface{}) { func (log *BasicLogger) Errorln(parts ...interface{}) {
log.DefaultSub.Errorln(parts...) log.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 (log *Logger) Errorf(message string, args ...interface{}) { func (log *BasicLogger) Errorf(message string, args ...interface{}) {
log.DefaultSub.Errorf(message, args...) log.DefaultSub.Errorf(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 *Logger) Fatal(parts ...interface{}) { 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...) log.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 (log *Logger) Fatalln(parts ...interface{}) { func (log *BasicLogger) Fatalln(parts ...interface{}) {
log.DefaultSub.Fatalln(parts...) log.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 (log *Logger) Fatalf(message string, args ...interface{}) { func (log *BasicLogger) Fatalf(message string, args ...interface{}) {
log.DefaultSub.Fatalf(message, args...) 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...)
}

View file

@ -1,20 +1,19 @@
// mauLogger - A logger for Go programs // 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 // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// Package maulogger ...
package maulogger package maulogger
import ( import (
@ -29,9 +28,9 @@ type Level struct {
var ( var (
// LevelDebug is the level for debug messages. // 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 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 is the level saying that something went wrong, but the program will continue operating mostly normally.
LevelWarn = Level{Name: "WARN", Color: 33, Severity: 50} 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. // LevelError is the level saying that something went wrong and the program may not operate as expected, but will still continue.

View file

@ -1,20 +1,19 @@
// mauLogger - A logger for Go programs // 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 // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// Package maulogger ...
package maulogger package maulogger
import ( import (
@ -26,47 +25,57 @@ import (
// LoggerFileFormat ... // LoggerFileFormat ...
type LoggerFileFormat func(now string, i int) string type LoggerFileFormat func(now string, i int) string
// Logger ... type BasicLogger struct {
type Logger struct {
PrintLevel int PrintLevel int
FlushLineThreshold int FlushLineThreshold int
FileTimeFormat string FileTimeFormat string
FileFormat LoggerFileFormat FileFormat LoggerFileFormat
TimeFormat string TimeFormat string
FileMode os.FileMode FileMode os.FileMode
DefaultSub *Sublogger DefaultSub Logger
writer *os.File writer *os.File
lines int lines int
prefixPrinted bool prefixPrinted bool
} }
// GeneralLogger contains advanced logging functions and also implements io.Writer // Logger contains advanced logging functions and also implements io.Writer
type GeneralLogger interface { type Logger interface {
Sub(module string) Logger
GetParent() Logger
Write(p []byte) (n int, err error) Write(p []byte) (n int, err error)
Log(level Level, parts ...interface{}) Log(level Level, parts ...interface{})
Logln(level Level, parts ...interface{}) Logln(level Level, parts ...interface{})
Logf(level Level, message string, args ...interface{}) Logf(level Level, message string, args ...interface{})
Logfln(level Level, message string, args ...interface{})
Debug(parts ...interface{}) Debug(parts ...interface{})
Debugln(parts ...interface{}) Debugln(parts ...interface{})
Debugf(message string, args ...interface{}) Debugf(message string, args ...interface{})
Debugfln(message string, args ...interface{})
Info(parts ...interface{}) Info(parts ...interface{})
Infoln(parts ...interface{}) Infoln(parts ...interface{})
Infof(message string, args ...interface{}) Infof(message string, args ...interface{})
Infofln(message string, args ...interface{})
Warn(parts ...interface{}) Warn(parts ...interface{})
Warnln(parts ...interface{}) Warnln(parts ...interface{})
Warnf(message string, args ...interface{}) Warnf(message string, args ...interface{})
Warnfln(message string, args ...interface{})
Error(parts ...interface{}) Error(parts ...interface{})
Errorln(parts ...interface{}) Errorln(parts ...interface{})
Errorf(message string, args ...interface{}) Errorf(message string, args ...interface{})
Errorfln(message string, args ...interface{})
Fatal(parts ...interface{}) Fatal(parts ...interface{})
Fatalln(parts ...interface{}) Fatalln(parts ...interface{})
Fatalf(message string, args ...interface{}) Fatalf(message string, args ...interface{})
Fatalfln(message string, args ...interface{})
} }
// Create a Logger // Create a Logger
func Create() *Logger { func Create() Logger {
var log = &Logger{ var log = &BasicLogger{
PrintLevel: 10, PrintLevel: 10,
FileTimeFormat: "2006-01-02", FileTimeFormat: "2006-01-02",
FileFormat: func(now string, i int) string { return fmt.Sprintf("%[1]s-%02[2]d.log", now, i) }, 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, FlushLineThreshold: 5,
lines: 0, lines: 0,
} }
log.DefaultSub = log.CreateSublogger("", LevelInfo) log.DefaultSub = log.Sub("")
return log return log
} }
// SetWriter formats the given parts with fmt.Sprint and log them with the SetWriter level func (log *BasicLogger) GetParent() Logger {
func (log *Logger) SetWriter(w *os.File) { 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 log.writer = 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 (log *Logger) OpenFile() error { func (log *BasicLogger) OpenFile() error {
now := time.Now().Format(log.FileTimeFormat) now := time.Now().Format(log.FileTimeFormat)
i := 1 i := 1
for ; ; i++ { for ; ; i++ {
@ -106,15 +119,15 @@ func (log *Logger) OpenFile() error {
return nil return nil
} }
// 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 (log *Logger) Close() { func (log *BasicLogger) Close() {
if log.writer != nil { if log.writer != nil {
log.writer.Close() log.writer.Close()
} }
} }
// 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 (log *Logger) Raw(level Level, module, message string) { func (log *BasicLogger) Raw(level Level, module, message string) {
if !log.prefixPrinted { if !log.prefixPrinted {
if len(module) == 0 { if len(module) == 0 {
message = fmt.Sprintf("[%s] [%s] %s", time.Now().Format(log.TimeFormat), level.Name, message) message = fmt.Sprintf("[%s] [%s] %s", time.Now().Format(log.TimeFormat), level.Name, message)

View file

@ -1,39 +1,53 @@
// mauLogger - A logger for Go programs // 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 // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// Package maulogger ...
package maulogger package maulogger
import ( import (
"fmt" "fmt"
) )
// Sublogger ...
type Sublogger struct { type Sublogger struct {
Parent *Logger topLevel *BasicLogger
parent Logger
Module string Module string
DefaultLevel Level DefaultLevel Level
} }
// CreateSublogger creates a Sublogger // Sub creates a Sublogger
func (log *Logger) CreateSublogger(module string, DefaultLevel Level) *Sublogger { func (log *BasicLogger) Sub(module string) Logger {
return &Sublogger{ return &Sublogger{
Parent: log, topLevel: log,
parent: log,
Module: module, 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 // SetParent changes the parent of this Sublogger
func (log *Sublogger) SetParent(parent *Logger) { func (log *Sublogger) SetParent(parent *BasicLogger) {
log.Parent = parent log.topLevel = parent
} }
//Write ... //Write ...
func (log *Sublogger) Write(p []byte) (n int, err error) { 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 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{}) { 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{}) { 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{}) { 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{}) { 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{}) { 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{}) { 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{}) { 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{}) { 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{}) { 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{}) { 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{}) { 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{}) { 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{}) { 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{}) { 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{}) { 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{}) { 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{}) { 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{}) { 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...))
} }