diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 00000000..acdc612e --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,213 @@ +# For full documentation of the configuration options please +# see: https://github.com/golangci/golangci-lint#config-file. + +# options for analysis running +run: + # default concurrency is the available CPU number + # concurrency: 4 + + # timeout for analysis, e.g. 30s, 5m, default is 1m + deadline: 1m + + # exit code when at least one issue was found, default is 1 + issues-exit-code: 1 + + # include test files or not, default is true + tests: true + + # list of build tags, all linters use it. Default is empty list. + build-tags: + + # which dirs to skip: they won't be analyzed; + # can use regexp here: generated.*, regexp is applied on full path; + # default value is empty list, but next dirs are always skipped independently + # from this option's value: + # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ + skip-dirs: + + # which files to skip: they will be analyzed, but issues from them + # won't be reported. Default value is empty list, but there is + # no need to include all autogenerated files, we confidently recognize + # autogenerated files. If it's not please let us know. + skip-files: + + +# output configuration options +output: + # colored-line-number|line-number|json|tab|checkstyle, default is "colored-line-number" + format: colored-line-number + + # print lines of code with issue, default is true + print-issued-lines: true + + # print linter name in the end of issue text, default is true + print-linter-name: true + + +# all available settings of specific linters, we can set an option for +# a given linter even if we deactivate that same linter at runtime +linters-settings: + errcheck: + # report about not checking of errors in type assertions: `a := b.(MyStruct)`; + # default is false: such cases aren't reported by default. + check-type-assertions: false + + # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; + # default is false: such cases aren't reported by default. + check-blank: false + govet: + # report about shadowed variables + check-shadowing: true + golint: + # minimal confidence for issues, default is 0.8 + min-confidence: 0.8 + gofmt: + # simplify code: gofmt with `-s` option, true by default + simplify: true + goimports: + # put imports beginning with prefix after 3rd-party packages; + # it's a comma-separated list of prefixes + local-prefixes: github.com + gocyclo: + # minimal code complexity to report, 30 by default (but we recommend 10-20) + min-complexity: 15 + maligned: + # print struct with more effective memory layout or not, false by default + suggest-new: true + dupl: + # tokens count to trigger issue, 150 by default + threshold: 150 + goconst: + # minimal length of string constant, 3 by default + min-len: 3 + # minimal occurrences count to trigger, 3 by default + min-occurrences: 3 + depguard: + list-type: blacklist + include-go-root: false + packages: + # List of packages that we would want to blacklist for... reasons. + misspell: + # Correct spellings using locale preferences for US or UK. + # Default is to use a neutral variety of English. + # Setting locale to US will correct the British spelling of 'colour' to 'color'. + locale: US + lll: + # max line length, lines longer will be reported. Default is 120. + # '\t' is counted as 1 character by default, and can be changed with the tab-width option + line-length: 150 + # tab width in spaces. Default to 1. + tab-width: 1 + unused: + # treat code as a program (not a library) and report unused exported identifiers; default is false. + # XXX: if you enable this setting, unused will report a lot of false-positives in text editors: + # if it's called for subdir of a project it can't find funcs usages. All text editor integrations + # with golangci-lint call it on a directory with the changed file. + check-exported: false + unparam: + # call graph construction algorithm (cha, rta). In general, use cha for libraries, + # and rta for programs with main packages. Default is cha. + algo: rta + + # Inspect exported functions, default is false. Set to true if no external program/library imports your code. + # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: + # if it's called for subdir of a project it can't find external interfaces. All text editor integrations + # with golangci-lint call it on a directory with the changed file. + check-exported: false + nakedret: + # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 + max-func-lines: 0 # Warn on all naked returns. + prealloc: + # XXX: we don't recommend using this linter before doing performance profiling. + # For most programs usage of prealloc will be a premature optimization. + + # Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them. + # True by default. + simple: true + range-loops: true # Report preallocation suggestions on range loops, true by default + for-loops: false # Report preallocation suggestions on for loops, false by default + gocritic: + # which checks should be enabled; can't be combined with 'disabled-checks'; + # default are: [appendAssign assignOp caseOrder dupArg dupBranchBody dupCase flagDeref + # ifElseChain regexpMust singleCaseSwitch sloppyLen switchTrue typeSwitchVar underef + # unlambda unslice rangeValCopy defaultCaseOrder]; + # all checks list: https://github.com/go-critic/checkers + enabled-checks: + - appendAssign + - assignOp + - boolExprSimplify + - builtinShadow + - captLocal + - caseOrder + - commentedOutImport + - defaultCaseOrder + - dupArg + - dupBranchBody + - dupCase + - dupSubExpr + - elseif + - emptyFallthrough + - hugeParam + - ifElseChain + - importShadow + - indexAlloc + - methodExprCall + - nestingReduce + - offBy1 + - ptrToRefParam + - regexpMust + - singleCaseSwitch + - sloppyLen + - sloppyReassign + - switchTrue + - typeSwitchVar + - typeUnparen + - underef + - unlambda + - unnecessaryBlock + - unslice + - valSwap + - wrapperFunc + - yodaStyleExpr + + +# linters that we should / shouldn't run +linters: + enable-all: true + disable: + - gochecknoglobals + - lll + - maligned + - prealloc + + +# rules to deal with reported isues +issues: + # List of regexps of issue texts to exclude, empty list by default. + # But independently from this option we use default exclude patterns, + # it can be disabled by `exclude-use-default: false`. To list all + # excluded by default patterns execute `golangci-lint run --help` + exclude: + + # Independently from option `exclude` we use default exclude patterns, + # it can be disabled by this option. To list all + # excluded by default patterns execute `golangci-lint run --help`. + # Default value for this option is true. + exclude-use-default: true + + # Maximum issues count per one linter. Set to 0 to disable. Default is 50. + max-per-linter: 0 + + # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. + max-same-issues: 0 + + # Show only new issues: if there are unstaged changes or untracked files, + # only those changes are analyzed, else only changes in HEAD~ are analyzed. + # It's a super-useful option for integration of golangci-lint into existing + # large codebase. It's not practical to fix all existing issues at the moment + # of integration: much better don't allow issues in new code. + # Default is false. + new: false + + # Show only new issues created after git revision `REV` + new-from-rev: "HEAD~1" diff --git a/.travis.yml b/.travis.yml index 2ed49274..4dd02ee5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,18 @@ language: go go: - #- 1.7.x - - 1.10.x - # - tip + - 1.11.x +go_import_path: github.com/42wim/matterbridge # we have everything vendored install: true +git: + depth: 200 + env: + global: - GOOS=linux GOARCH=amd64 - # - GOOS=windows GOARCH=amd64 - #- GOOS=linux GOARCH=arm + - GOLANGCI_VERSION="v1.12.3" matrix: # It's ok if our code fails on unstable development versions of Go. @@ -24,22 +26,26 @@ notifications: email: false before_script: - - MY_VERSION=$(git describe --tags) - - GO_FILES=$(find . -iname '*.go' | grep -v /vendor/) # All the .go files, excluding vendor/ - - PKGS=$(go list ./... | grep -v /vendor/) # All the import paths, excluding vendor/ -# - go get github.com/golang/lint/golint # Linter - - go get honnef.co/go/tools/cmd/megacheck # Badass static analyzer/linter + # Get version info from tags. + - MY_VERSION="$(git describe --tags)" + # Retrieve the golangci-lint linter binary. + - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b ${GOPATH}/bin ${GOLANGCI_VERSION} + # Retrieve and prepare CodeClimate's test coverage reporter. + - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter + - chmod +x ./cc-test-reporter + - ./cc-test-reporter before-build -# Anything in before_script: that returns a nonzero exit code will -# flunk the build and immediately stop. It's sorta like having -# set -e enabled in bash. script: - #- test -z $(gofmt -s -l $GO_FILES) # Fail if a .go file hasn't been formatted with gofmt - - go test -v -race $PKGS # Run all the tests with the race detector enabled - # - go vet $PKGS # go vet is the official Go static analyzer - - megacheck $PKGS # "go vet on steroids" + linter + # Run the linter. + - golangci-lint run + # Run all the tests with the race detector and generate coverage. + - go test -v -race -coverprofile c.out ./... + # Run the build script to generate the necessary binaries and images. - /bin/bash ci/bintray.sh - #- golint -set_exit_status $PKGS # one last linter + +after_script: + # Upload test coverage to CodeClimate. + - ./cc-test-reporter after-build --exit-code ${TRAVIS_TEST_RESULT} deploy: provider: bintray @@ -48,4 +54,4 @@ deploy: file: ci/deploy.json user: 42wim key: - secure: "CeXXe6JOmt7HYR81MdWLua0ltQHhDdkIeRGBFbgd7hkb1wi8eF9DgpAcQrTso8NIlHNZmSAP46uhFgsRvkuezzX0ygalZ7DCJyAyn3sAMEh+UQSHV1WGThRehTtidqRGjetzsIGSwdrJOWil+XTfbO1Z8DGzfakhSuAZka8CM4BAoe3YeP9rYK8h+84x0GHfczvsLtXZ3mWLvQuwe4pK6+ItBCUg0ae7O7ZUpWHy0xQQkkWztY/6RAzXfaG7DuGjIw+20fhx3WOXRNpHCtZ6Bc3qERCpk0s1HhlQWlrN9wDaFTBWYwlvSnNgvxxMbNXJ6RrRJ0l0bA7FUswYwyroxhzrGLdzWDg8dHaQkypocngdalfhpsnoO9j3ApJhomUFJ3UoEq5nOGRUrKn8MPi+dP0zE4kNQ3e4VNa1ufNrvfpWolMg3xh8OXuhQdD5wIM5zFAbRJLqWSCVAjPq4DDPecmvXBOlIial7oa312lN5qnBnUjvAcxszZ+FUyDHT1Grxzna4tMwxY9obPzZUzm7359AOCCwIQFVB8GLqD2nwIstcXS0zGRz+fhviPipHuBa02q5bGUZwmkvrSNab0s8Jo7pCrel2Rz3nWPKaiCfq2WjbW1CLheSMkOQrjsdUd1hhbqNWFPUjJPInTc77NAKCfm5runv5uyowRLh4NNd0sI=" + secure: "CeXXe6JOmt7HYR81MdWLua0ltQHhDdkIeRGBFbgd7hkb1wi8eF9DgpAcQrTso8NIlHNZmSAP46uhFgsRvkuezzX0ygalZ7DCJyAyn3sAMEh+UQSHV1WGThRehTtidqRGjetzsIGSwdrJOWil+XTfbO1Z8DGzfakhSuAZka8CM4BAoe3YeP9rYK8h+84x0GHfczvsLtXZ3mWLvQuwe4pK6+ItBCUg0ae7O7ZUpWHy0xQQkkWztY/6RAzXfaG7DuGjIw+20fhx3WOXRNpHCtZ6Bc3qERCpk0s1HhlQWlrN9wDaFTBWYwlvSnNgvxxMbNXJ6RrRJ0l0bA7FUswYwyroxhzrGLdzWDg8dHaQkypocngdalfhpsnoO9j3ApJhomUFJ3UoEq5nOGRUrKn8MPi+dP0zE4kNQ3e4VNa1ufNrvfpWolMg3xh8OXuhQdD5wIM5zFAbRJLqWSCVAjPq4DDPecmvXBOlIial7oa312lN5qnBnUjvAcxszZ+FUyDHT1Grxzna4tMwxY9obPzZUzm7359AOCCwIQFVB8GLqD2nwIstcXS0zGRz+fhviPipHuBa02q5bGUZwmkvrSNab0s8Jo7pCrel2Rz3nWPKaiCfq2WjbW1CLheSMkOQrjsdUd1hhbqNWFPUjJPInTc77NAKCfm5runv5uyowRLh4NNd0sI=" \ No newline at end of file diff --git a/README.md b/README.md index 68cca9c2..7d3d304e 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,40 @@ +
\n"); err != nil {
- return
+ data := struct {
+ Name string
+ Files []interface{}
+ }{
+ Name: name,
}
- for _, d := range dirs {
- name := d.Name()
- color := "#212121"
- if d.IsDir() {
- color = "#e91e63"
- name += "/"
- }
- if _, err = fmt.Fprintf(res, "%s\n", name, color, name); err != nil {
- return
- }
+ for _, f := range files {
+ data.Files = append(data.Files, struct {
+ Name string
+ Dir bool
+ Size string
+ }{f.Name(), f.IsDir(), bytes.Format(f.Size())})
}
- _, err = fmt.Fprintf(res, "\n")
- return
+ return t.Execute(res, data)
}
diff --git a/vendor/github.com/labstack/echo/response.go b/vendor/github.com/labstack/echo/response.go
index 93204098..6244783b 100644
--- a/vendor/github.com/labstack/echo/response.go
+++ b/vendor/github.com/labstack/echo/response.go
@@ -4,7 +4,6 @@ import (
"bufio"
"net"
"net/http"
- "strconv"
)
type (
@@ -12,14 +11,13 @@ type (
// by an HTTP handler to construct an HTTP response.
// See: https://golang.org/pkg/net/http/#ResponseWriter
Response struct {
- echo *Echo
- contentLength int64
- beforeFuncs []func()
- afterFuncs []func()
- Writer http.ResponseWriter
- Status int
- Size int64
- Committed bool
+ echo *Echo
+ beforeFuncs []func()
+ afterFuncs []func()
+ Writer http.ResponseWriter
+ Status int
+ Size int64
+ Committed bool
}
)
@@ -64,7 +62,6 @@ func (r *Response) WriteHeader(code int) {
r.Status = code
r.Writer.WriteHeader(code)
r.Committed = true
- r.contentLength, _ = strconv.ParseInt(r.Header().Get(HeaderContentLength), 10, 0)
}
// Write writes the data to the connection as part of an HTTP reply.
@@ -74,10 +71,8 @@ func (r *Response) Write(b []byte) (n int, err error) {
}
n, err = r.Writer.Write(b)
r.Size += int64(n)
- if r.Size == r.contentLength {
- for _, fn := range r.afterFuncs {
- fn()
- }
+ for _, fn := range r.afterFuncs {
+ fn()
}
return
}
@@ -106,7 +101,6 @@ func (r *Response) CloseNotify() <-chan bool {
}
func (r *Response) reset(w http.ResponseWriter) {
- r.contentLength = 0
r.beforeFuncs = nil
r.afterFuncs = nil
r.Writer = w
diff --git a/vendor/github.com/labstack/echo/router.go b/vendor/github.com/labstack/echo/router.go
index 3af4be0b..ff53da87 100644
--- a/vendor/github.com/labstack/echo/router.go
+++ b/vendor/github.com/labstack/echo/router.go
@@ -21,15 +21,16 @@ type (
kind uint8
children []*node
methodHandler struct {
- connect HandlerFunc
- delete HandlerFunc
- get HandlerFunc
- head HandlerFunc
- options HandlerFunc
- patch HandlerFunc
- post HandlerFunc
- put HandlerFunc
- trace HandlerFunc
+ connect HandlerFunc
+ delete HandlerFunc
+ get HandlerFunc
+ head HandlerFunc
+ options HandlerFunc
+ patch HandlerFunc
+ post HandlerFunc
+ propfind HandlerFunc
+ put HandlerFunc
+ trace HandlerFunc
}
)
@@ -59,8 +60,8 @@ func (r *Router) Add(method, path string, h HandlerFunc) {
if path[0] != '/' {
path = "/" + path
}
- ppath := path // Pristine path
pnames := []string{} // Param names
+ ppath := path // Pristine path
for i, l := 0, len(path); i < l; i++ {
if path[i] == ':' {
@@ -225,22 +226,24 @@ func (n *node) findChildByKind(t kind) *node {
func (n *node) addHandler(method string, h HandlerFunc) {
switch method {
- case GET:
- n.methodHandler.get = h
- case POST:
- n.methodHandler.post = h
- case PUT:
- n.methodHandler.put = h
- case DELETE:
- n.methodHandler.delete = h
- case PATCH:
- n.methodHandler.patch = h
- case OPTIONS:
- n.methodHandler.options = h
- case HEAD:
- n.methodHandler.head = h
case CONNECT:
n.methodHandler.connect = h
+ case DELETE:
+ n.methodHandler.delete = h
+ case GET:
+ n.methodHandler.get = h
+ case HEAD:
+ n.methodHandler.head = h
+ case OPTIONS:
+ n.methodHandler.options = h
+ case PATCH:
+ n.methodHandler.patch = h
+ case POST:
+ n.methodHandler.post = h
+ case PROPFIND:
+ n.methodHandler.propfind = h
+ case PUT:
+ n.methodHandler.put = h
case TRACE:
n.methodHandler.trace = h
}
@@ -248,22 +251,24 @@ func (n *node) addHandler(method string, h HandlerFunc) {
func (n *node) findHandler(method string) HandlerFunc {
switch method {
- case GET:
- return n.methodHandler.get
- case POST:
- return n.methodHandler.post
- case PUT:
- return n.methodHandler.put
- case DELETE:
- return n.methodHandler.delete
- case PATCH:
- return n.methodHandler.patch
- case OPTIONS:
- return n.methodHandler.options
- case HEAD:
- return n.methodHandler.head
case CONNECT:
return n.methodHandler.connect
+ case DELETE:
+ return n.methodHandler.delete
+ case GET:
+ return n.methodHandler.get
+ case HEAD:
+ return n.methodHandler.head
+ case OPTIONS:
+ return n.methodHandler.options
+ case PATCH:
+ return n.methodHandler.patch
+ case POST:
+ return n.methodHandler.post
+ case PROPFIND:
+ return n.methodHandler.propfind
+ case PUT:
+ return n.methodHandler.put
case TRACE:
return n.methodHandler.trace
default:
diff --git a/vendor/github.com/labstack/echo/util_go17.go b/vendor/github.com/labstack/echo/util_go17.go
deleted file mode 100644
index eaae17e3..00000000
--- a/vendor/github.com/labstack/echo/util_go17.go
+++ /dev/null
@@ -1,12 +0,0 @@
-// +build go1.7, !go1.8
-
-package echo
-
-import (
- "net/url"
-)
-
-// PathUnescape is wraps `url.QueryUnescape`
-func PathUnescape(s string) (string, error) {
- return url.QueryUnescape(s)
-}
diff --git a/vendor/github.com/labstack/echo/util_go18.go b/vendor/github.com/labstack/echo/util_go18.go
deleted file mode 100644
index 8a37785b..00000000
--- a/vendor/github.com/labstack/echo/util_go18.go
+++ /dev/null
@@ -1,10 +0,0 @@
-// +build go1.8
-
-package echo
-
-import "net/url"
-
-// PathUnescape is wraps `url.PathUnescape`
-func PathUnescape(s string) (string, error) {
- return url.PathUnescape(s)
-}
diff --git a/vendor/github.com/lrstanley/girc/.travis.yml b/vendor/github.com/lrstanley/girc/.travis.yml
index 658e65d9..96a1bb8a 100644
--- a/vendor/github.com/lrstanley/girc/.travis.yml
+++ b/vendor/github.com/lrstanley/girc/.travis.yml
@@ -1,14 +1,13 @@
language: go
go:
-- 1.8
-- 1.9
+- 1.11.x
- tip
before_install:
-- go get -v github.com/golang/lint/golint
+- go get -v golang.org/x/lint/golint
script:
- $HOME/gopath/bin/golint -min_confidence 0.9 -set_exit_status
- GORACE="exitcode=1 halt_on_error=1" go test -v -coverprofile=coverage.txt -race -timeout 3m -count 3 -cpu 1,4
-- go tool vet -v -all .
+- go vet -v .
after_success:
- bash <(curl -s https://codecov.io/bash)
branches:
diff --git a/vendor/github.com/lrstanley/girc/cap.go b/vendor/github.com/lrstanley/girc/cap.go
index e7037f9f..89146484 100644
--- a/vendor/github.com/lrstanley/girc/cap.go
+++ b/vendor/github.com/lrstanley/girc/cap.go
@@ -102,7 +102,7 @@ func handleCAP(c *Client, e Event) {
possible := possibleCapList(c)
- if len(e.Params) >= 2 && len(e.Trailing) > 1 && e.Params[1] == CAP_LS {
+ if len(e.Params) >= 2 && e.Params[1] == CAP_LS {
c.state.Lock()
caps := parseCap(e.Trailing)
diff --git a/vendor/github.com/lrstanley/girc/client.go b/vendor/github.com/lrstanley/girc/client.go
index 4f823e16..63f47eaa 100644
--- a/vendor/github.com/lrstanley/girc/client.go
+++ b/vendor/github.com/lrstanley/girc/client.go
@@ -135,7 +135,7 @@ type Config struct {
// PingDelay is the frequency between when the client sends a keep-alive
// PING to the server, and awaits a response (and times out if the server
// doesn't respond in time). This should be between 20-600 seconds. See
- // Client.Lag() if you want to determine the delay between the server
+ // Client.Latency() if you want to determine the delay between the server
// and the client. If this is set to -1, the client will not attempt to
// send client -> server PING requests.
PingDelay time.Duration
diff --git a/vendor/github.com/lrstanley/girc/commands.go b/vendor/github.com/lrstanley/girc/commands.go
index d22f7616..300db9ed 100644
--- a/vendor/github.com/lrstanley/girc/commands.go
+++ b/vendor/github.com/lrstanley/girc/commands.go
@@ -70,7 +70,7 @@ func (cmd *Commands) PartMessage(channel, message string) {
// PRIVMSG specifically. ctcpType is the CTCP command, e.g. "FINGER", "TIME",
// "VERSION", etc.
func (cmd *Commands) SendCTCP(target, ctcpType, message string) {
- out := encodeCTCPRaw(ctcpType, message)
+ out := EncodeCTCPRaw(ctcpType, message)
if out == "" {
panic(fmt.Sprintf("invalid CTCP: %s -> %s: %s", target, ctcpType, message))
}
@@ -95,7 +95,7 @@ func (cmd *Commands) SendCTCPReplyf(target, ctcpType, format string, a ...interf
// SendCTCPReply sends a CTCP response to target. Note that this method uses
// NOTICE specifically.
func (cmd *Commands) SendCTCPReply(target, ctcpType, message string) {
- out := encodeCTCPRaw(ctcpType, message)
+ out := EncodeCTCPRaw(ctcpType, message)
if out == "" {
panic(fmt.Sprintf("invalid CTCP: %s -> %s: %s", target, ctcpType, message))
}
diff --git a/vendor/github.com/lrstanley/girc/constants.go b/vendor/github.com/lrstanley/girc/constants.go
index 4d3c65bc..76ba482d 100644
--- a/vendor/github.com/lrstanley/girc/constants.go
+++ b/vendor/github.com/lrstanley/girc/constants.go
@@ -6,6 +6,7 @@ package girc
// Standard CTCP based constants.
const (
+ CTCP_ACTION = "ACTION"
CTCP_PING = "PING"
CTCP_PONG = "PONG"
CTCP_VERSION = "VERSION"
diff --git a/vendor/github.com/lrstanley/girc/ctcp.go b/vendor/github.com/lrstanley/girc/ctcp.go
index 6076ab10..45fc6be5 100644
--- a/vendor/github.com/lrstanley/girc/ctcp.go
+++ b/vendor/github.com/lrstanley/girc/ctcp.go
@@ -30,18 +30,22 @@ type CTCPEvent struct {
Reply bool `json:"reply"`
}
-// decodeCTCP decodes an incoming CTCP event, if it is CTCP. nil is returned
-// if the incoming event does not match a valid CTCP.
-func decodeCTCP(e *Event) *CTCPEvent {
+// DecodeCTCP decodes an incoming CTCP event, if it is CTCP. nil is returned
+// if the incoming event does not have valid CTCP encoding.
+func DecodeCTCP(e *Event) *CTCPEvent {
// http://www.irchelp.org/protocol/ctcpspec.html
+ if e == nil {
+ return nil
+ }
+
// Must be targeting a user/channel, AND trailing must have
// DELIM+TAG+DELIM minimum (at least 3 chars).
if len(e.Params) != 1 || len(e.Trailing) < 3 {
return nil
}
- if (e.Command != PRIVMSG && e.Command != NOTICE) || !IsValidNick(e.Params[0]) {
+ if e.Command != PRIVMSG && e.Command != NOTICE {
return nil
}
@@ -88,18 +92,18 @@ func decodeCTCP(e *Event) *CTCPEvent {
}
}
-// encodeCTCP encodes a CTCP event into a string, including delimiters.
-func encodeCTCP(ctcp *CTCPEvent) (out string) {
+// EncodeCTCP encodes a CTCP event into a string, including delimiters.
+func EncodeCTCP(ctcp *CTCPEvent) (out string) {
if ctcp == nil {
return ""
}
- return encodeCTCPRaw(ctcp.Command, ctcp.Text)
+ return EncodeCTCPRaw(ctcp.Command, ctcp.Text)
}
-// encodeCTCPRaw is much like encodeCTCP, however accepts a raw command and
+// EncodeCTCPRaw is much like EncodeCTCP, however accepts a raw command and
// string as input.
-func encodeCTCPRaw(cmd, text string) (out string) {
+func EncodeCTCPRaw(cmd, text string) (out string) {
if len(cmd) <= 0 {
return ""
}
@@ -145,6 +149,11 @@ func (c *CTCP) call(client *Client, event *CTCPEvent) {
}
if _, ok := c.handlers[event.Command]; !ok {
+ // If ACTION, don't do anything.
+ if event.Command == CTCP_ACTION {
+ return
+ }
+
// Send a ERRMSG reply, if we know who sent it.
if event.Source != nil && IsValidNick(event.Source.Name) {
client.Cmd.SendCTCPReply(event.Source.Name, CTCP_ERRMSG, "that is an unknown CTCP query")
diff --git a/vendor/github.com/lrstanley/girc/event.go b/vendor/github.com/lrstanley/girc/event.go
index 20182340..0b40b40b 100644
--- a/vendor/github.com/lrstanley/girc/event.go
+++ b/vendor/github.com/lrstanley/girc/event.go
@@ -51,7 +51,7 @@ type Event struct {
// Trailing text. e.g. with a PRIVMSG, this is the message text (part
// after the colon.)
Trailing string `json:"trailing"`
- // EmptyTrailign, if true, the text prefix (:) will be added even if
+ // EmptyTrailing, if true, the text prefix (:) will be added even if
// Event.Trailing is empty.
EmptyTrailing bool `json:"empty_trailing"`
// Sensitive should be true if the message is sensitive (e.g. and should
@@ -355,11 +355,15 @@ func (e *Event) Pretty() (out string, ok bool) {
}
if (e.Command == PRIVMSG || e.Command == NOTICE) && len(e.Params) > 0 {
- if ctcp := decodeCTCP(e); ctcp != nil {
+ if ctcp := DecodeCTCP(e); ctcp != nil {
if ctcp.Reply {
return
}
+ if ctcp.Command == CTCP_ACTION {
+ return fmt.Sprintf("[%s] **%s** %s", strings.Join(e.Params, ","), ctcp.Source.Name, ctcp.Text), true
+ }
+
return fmt.Sprintf("[*] CTCP query from %s: %s%s", ctcp.Source.Name, ctcp.Command, " "+ctcp.Text), true
}
return fmt.Sprintf("[%s] (%s) %s", strings.Join(e.Params, ","), e.Source.Name, e.Trailing), true
@@ -383,12 +387,30 @@ func (e *Event) Pretty() (out string, ok bool) {
return fmt.Sprintf("[*] %s has quit (%s)", e.Source.Name, e.Trailing), true
}
- if e.Command == KICK && len(e.Params) == 2 {
+ if e.Command == INVITE && len(e.Params) == 1 {
+ return fmt.Sprintf("[*] %s invited to %s by %s", e.Params[0], e.Trailing, e.Source.Name), true
+ }
+
+ if e.Command == KICK && len(e.Params) >= 2 {
+ if e.Trailing == "" && len(e.Params) == 3 {
+ e.Trailing = e.Params[2]
+ }
+
return fmt.Sprintf("[%s] *** %s has kicked %s: %s", e.Params[0], e.Source.Name, e.Params[1], e.Trailing), true
}
- if e.Command == NICK && len(e.Params) == 1 {
- return fmt.Sprintf("[*] %s is now known as %s", e.Source.Name, e.Params[0]), true
+ if e.Command == NICK {
+ // Workaround, see https://github.com/lrstanley/girc/pull/15#issuecomment-413845482
+ var name string
+ if len(e.Params) == 1 {
+ name = e.Params[0]
+ } else if len(e.Trailing) > 0 {
+ name = e.Trailing
+ }
+
+ if name != "" {
+ return fmt.Sprintf("[*] %s is now known as %s", e.Source.Name, name), true
+ }
}
if e.Command == TOPIC && len(e.Params) > 0 {
@@ -430,23 +452,27 @@ func (e *Event) Pretty() (out string, ok bool) {
return "", false
}
-// IsAction checks to see if the event is a PRIVMSG, and is an ACTION (/me).
+// IsAction checks to see if the event is an ACTION (/me).
func (e *Event) IsAction() bool {
- if e.Source == nil || e.Command != PRIVMSG || len(e.Trailing) < 9 {
+ if e.Command != PRIVMSG {
return false
}
- if !strings.HasPrefix(e.Trailing, "\001ACTION") || e.Trailing[len(e.Trailing)-1] != ctcpDelim {
- return false
- }
+ ok, ctcp := e.IsCTCP()
+ return ok && ctcp.Command == CTCP_ACTION
+}
- return true
+// IsCTCP checks to see if the event is a CTCP event, and if so, returns the
+// converted CTCP event.
+func (e *Event) IsCTCP() (ok bool, ctcp *CTCPEvent) {
+ ctcp = DecodeCTCP(e)
+ return ctcp != nil, ctcp
}
// IsFromChannel checks to see if a message was from a channel (rather than
// a private message).
func (e *Event) IsFromChannel() bool {
- if e.Source == nil || e.Command != PRIVMSG || len(e.Params) < 1 {
+ if e.Source == nil || (e.Command != PRIVMSG && e.Command != NOTICE) || len(e.Params) < 1 {
return false
}
@@ -460,7 +486,7 @@ func (e *Event) IsFromChannel() bool {
// IsFromUser checks to see if a message was from a user (rather than a
// channel).
func (e *Event) IsFromUser() bool {
- if e.Source == nil || e.Command != PRIVMSG || len(e.Params) < 1 {
+ if e.Source == nil || (e.Command != PRIVMSG && e.Command != NOTICE) || len(e.Params) < 1 {
return false
}
diff --git a/vendor/github.com/lrstanley/girc/format.go b/vendor/github.com/lrstanley/girc/format.go
index b974c3bd..762f602b 100644
--- a/vendor/github.com/lrstanley/girc/format.go
+++ b/vendor/github.com/lrstanley/girc/format.go
@@ -113,7 +113,7 @@ func Fmt(text string) string {
if last > -1 {
// A-Z, a-z, and ","
- if text[i] != ',' && (text[i] <= 'A' || text[i] >= 'Z') && (text[i] <= 'a' || text[i] >= 'z') {
+ if text[i] != ',' && (text[i] < 'A' || text[i] > 'Z') && (text[i] < 'a' || text[i] > 'z') {
last = -1
continue
}
diff --git a/vendor/github.com/lrstanley/girc/go.mod b/vendor/github.com/lrstanley/girc/go.mod
new file mode 100644
index 00000000..57b39ae8
--- /dev/null
+++ b/vendor/github.com/lrstanley/girc/go.mod
@@ -0,0 +1 @@
+module github.com/lrstanley/girc
diff --git a/vendor/github.com/lrstanley/girc/handler.go b/vendor/github.com/lrstanley/girc/handler.go
index bde08976..ec717de6 100644
--- a/vendor/github.com/lrstanley/girc/handler.go
+++ b/vendor/github.com/lrstanley/girc/handler.go
@@ -46,7 +46,7 @@ func (c *Client) RunHandlers(event *Event) {
}
// Check if it's a CTCP.
- if ctcp := decodeCTCP(event.Copy()); ctcp != nil {
+ if ctcp := DecodeCTCP(event.Copy()); ctcp != nil {
// Execute it.
c.CTCP.call(c, ctcp)
}
diff --git a/vendor/github.com/magiconair/properties/.travis.yml b/vendor/github.com/magiconair/properties/.travis.yml
index 923e8664..3e7c3d2c 100644
--- a/vendor/github.com/magiconair/properties/.travis.yml
+++ b/vendor/github.com/magiconair/properties/.travis.yml
@@ -6,5 +6,5 @@ go:
- 1.7.x
- 1.8.x
- 1.9.x
- - "1.10"
+ - "1.10.x"
- tip
diff --git a/vendor/github.com/magiconair/properties/CHANGELOG.md b/vendor/github.com/magiconair/properties/CHANGELOG.md
index adefa17e..f83adc20 100644
--- a/vendor/github.com/magiconair/properties/CHANGELOG.md
+++ b/vendor/github.com/magiconair/properties/CHANGELOG.md
@@ -1,5 +1,13 @@
## Changelog
+### [1.8](https://github.com/magiconair/properties/tree/v1.8) - 15 May 2018
+
+ * [PR #26](https://github.com/magiconair/properties/pull/26): Disable expansion during loading
+
+ This adds the option to disable property expansion during loading.
+
+ Thanks to [@kmala](https://github.com/kmala) for the patch.
+
### [1.7.6](https://github.com/magiconair/properties/tree/v1.7.6) - 14 Feb 2018
* [PR #29](https://github.com/magiconair/properties/pull/29): Reworked expansion logic to handle more complex cases.
diff --git a/vendor/github.com/magiconair/properties/decode.go b/vendor/github.com/magiconair/properties/decode.go
index 0a961bb0..3ebf8049 100644
--- a/vendor/github.com/magiconair/properties/decode.go
+++ b/vendor/github.com/magiconair/properties/decode.go
@@ -1,4 +1,4 @@
-// Copyright 2017 Frank Schroeder. All rights reserved.
+// Copyright 2018 Frank Schroeder. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
diff --git a/vendor/github.com/magiconair/properties/doc.go b/vendor/github.com/magiconair/properties/doc.go
index 36c83680..f8822da2 100644
--- a/vendor/github.com/magiconair/properties/doc.go
+++ b/vendor/github.com/magiconair/properties/doc.go
@@ -1,4 +1,4 @@
-// Copyright 2017 Frank Schroeder. All rights reserved.
+// Copyright 2018 Frank Schroeder. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
@@ -73,7 +73,7 @@
// # refers to the users' home dir
// home = ${HOME}
//
-// # local key takes precendence over env var: u = foo
+// # local key takes precedence over env var: u = foo
// USER = foo
// u = ${USER}
//
@@ -102,7 +102,7 @@
// v = p.GetString("key", "def")
// v = p.GetDuration("key", 999)
//
-// As an alterantive properties may be applied with the standard
+// As an alternative properties may be applied with the standard
// library's flag implementation at any time.
//
// # Standard configuration
diff --git a/vendor/github.com/magiconair/properties/integrate.go b/vendor/github.com/magiconair/properties/integrate.go
index 0d775e03..74d38dc6 100644
--- a/vendor/github.com/magiconair/properties/integrate.go
+++ b/vendor/github.com/magiconair/properties/integrate.go
@@ -1,4 +1,4 @@
-// Copyright 2017 Frank Schroeder. All rights reserved.
+// Copyright 2018 Frank Schroeder. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
diff --git a/vendor/github.com/magiconair/properties/lex.go b/vendor/github.com/magiconair/properties/lex.go
index c63fcc60..367166d5 100644
--- a/vendor/github.com/magiconair/properties/lex.go
+++ b/vendor/github.com/magiconair/properties/lex.go
@@ -1,4 +1,4 @@
-// Copyright 2017 Frank Schroeder. All rights reserved.
+// Copyright 2018 Frank Schroeder. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
diff --git a/vendor/github.com/magiconair/properties/load.go b/vendor/github.com/magiconair/properties/load.go
index 9c83fd63..c8e1b580 100644
--- a/vendor/github.com/magiconair/properties/load.go
+++ b/vendor/github.com/magiconair/properties/load.go
@@ -1,4 +1,4 @@
-// Copyright 2017 Frank Schroeder. All rights reserved.
+// Copyright 2018 Frank Schroeder. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
@@ -16,21 +16,157 @@ import (
type Encoding uint
const (
+ // utf8Default is a private placeholder for the zero value of Encoding to
+ // ensure that it has the correct meaning. UTF8 is the default encoding but
+ // was assigned a non-zero value which cannot be changed without breaking
+ // existing code. Clients should continue to use the public constants.
+ utf8Default Encoding = iota
+
// UTF8 interprets the input data as UTF-8.
- UTF8 Encoding = 1 << iota
+ UTF8
// ISO_8859_1 interprets the input data as ISO-8859-1.
ISO_8859_1
)
+type Loader struct {
+ // Encoding determines how the data from files and byte buffers
+ // is interpreted. For URLs the Content-Type header is used
+ // to determine the encoding of the data.
+ Encoding Encoding
+
+ // DisableExpansion configures the property expansion of the
+ // returned property object. When set to true, the property values
+ // will not be expanded and the Property object will not be checked
+ // for invalid expansion expressions.
+ DisableExpansion bool
+
+ // IgnoreMissing configures whether missing files or URLs which return
+ // 404 are reported as errors. When set to true, missing files and 404
+ // status codes are not reported as errors.
+ IgnoreMissing bool
+}
+
+// Load reads a buffer into a Properties struct.
+func (l *Loader) LoadBytes(buf []byte) (*Properties, error) {
+ return l.loadBytes(buf, l.Encoding)
+}
+
+// LoadAll reads the content of multiple URLs or files in the given order into
+// a Properties struct. If IgnoreMissing is true then a 404 status code or
+// missing file will not be reported as error. Encoding sets the encoding for
+// files. For the URLs see LoadURL for the Content-Type header and the
+// encoding.
+func (l *Loader) LoadAll(names []string) (*Properties, error) {
+ all := NewProperties()
+ for _, name := range names {
+ n, err := expandName(name)
+ if err != nil {
+ return nil, err
+ }
+
+ var p *Properties
+ switch {
+ case strings.HasPrefix(n, "http://"):
+ p, err = l.LoadURL(n)
+ case strings.HasPrefix(n, "https://"):
+ p, err = l.LoadURL(n)
+ default:
+ p, err = l.LoadFile(n)
+ }
+ if err != nil {
+ return nil, err
+ }
+ all.Merge(p)
+ }
+
+ all.DisableExpansion = l.DisableExpansion
+ if all.DisableExpansion {
+ return all, nil
+ }
+ return all, all.check()
+}
+
+// LoadFile reads a file into a Properties struct.
+// If IgnoreMissing is true then a missing file will not be
+// reported as error.
+func (l *Loader) LoadFile(filename string) (*Properties, error) {
+ data, err := ioutil.ReadFile(filename)
+ if err != nil {
+ if l.IgnoreMissing && os.IsNotExist(err) {
+ LogPrintf("properties: %s not found. skipping", filename)
+ return NewProperties(), nil
+ }
+ return nil, err
+ }
+ return l.loadBytes(data, l.Encoding)
+}
+
+// LoadURL reads the content of the URL into a Properties struct.
+//
+// The encoding is determined via the Content-Type header which
+// should be set to 'text/plain'. If the 'charset' parameter is
+// missing, 'iso-8859-1' or 'latin1' the encoding is set to
+// ISO-8859-1. If the 'charset' parameter is set to 'utf-8' the
+// encoding is set to UTF-8. A missing content type header is
+// interpreted as 'text/plain; charset=utf-8'.
+func (l *Loader) LoadURL(url string) (*Properties, error) {
+ resp, err := http.Get(url)
+ if err != nil {
+ return nil, fmt.Errorf("properties: error fetching %q. %s", url, err)
+ }
+
+ if resp.StatusCode == 404 && l.IgnoreMissing {
+ LogPrintf("properties: %s returned %d. skipping", url, resp.StatusCode)
+ return NewProperties(), nil
+ }
+
+ if resp.StatusCode != 200 {
+ return nil, fmt.Errorf("properties: %s returned %d", url, resp.StatusCode)
+ }
+
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return nil, fmt.Errorf("properties: %s error reading response. %s", url, err)
+ }
+ defer resp.Body.Close()
+
+ ct := resp.Header.Get("Content-Type")
+ var enc Encoding
+ switch strings.ToLower(ct) {
+ case "text/plain", "text/plain; charset=iso-8859-1", "text/plain; charset=latin1":
+ enc = ISO_8859_1
+ case "", "text/plain; charset=utf-8":
+ enc = UTF8
+ default:
+ return nil, fmt.Errorf("properties: invalid content type %s", ct)
+ }
+
+ return l.loadBytes(body, enc)
+}
+
+func (l *Loader) loadBytes(buf []byte, enc Encoding) (*Properties, error) {
+ p, err := parse(convert(buf, enc))
+ if err != nil {
+ return nil, err
+ }
+ p.DisableExpansion = l.DisableExpansion
+ if p.DisableExpansion {
+ return p, nil
+ }
+ return p, p.check()
+}
+
// Load reads a buffer into a Properties struct.
func Load(buf []byte, enc Encoding) (*Properties, error) {
- return loadBuf(buf, enc)
+ l := &Loader{Encoding: enc}
+ return l.LoadBytes(buf)
}
// LoadString reads an UTF8 string into a properties struct.
func LoadString(s string) (*Properties, error) {
- return loadBuf([]byte(s), UTF8)
+ l := &Loader{Encoding: UTF8}
+ return l.LoadBytes([]byte(s))
}
// LoadMap creates a new Properties struct from a string map.
@@ -44,34 +180,32 @@ func LoadMap(m map[string]string) *Properties {
// LoadFile reads a file into a Properties struct.
func LoadFile(filename string, enc Encoding) (*Properties, error) {
- return loadAll([]string{filename}, enc, false)
+ l := &Loader{Encoding: enc}
+ return l.LoadAll([]string{filename})
}
// LoadFiles reads multiple files in the given order into
// a Properties struct. If 'ignoreMissing' is true then
// non-existent files will not be reported as error.
func LoadFiles(filenames []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
- return loadAll(filenames, enc, ignoreMissing)
+ l := &Loader{Encoding: enc, IgnoreMissing: ignoreMissing}
+ return l.LoadAll(filenames)
}
// LoadURL reads the content of the URL into a Properties struct.
-//
-// The encoding is determined via the Content-Type header which
-// should be set to 'text/plain'. If the 'charset' parameter is
-// missing, 'iso-8859-1' or 'latin1' the encoding is set to
-// ISO-8859-1. If the 'charset' parameter is set to 'utf-8' the
-// encoding is set to UTF-8. A missing content type header is
-// interpreted as 'text/plain; charset=utf-8'.
+// See Loader#LoadURL for details.
func LoadURL(url string) (*Properties, error) {
- return loadAll([]string{url}, UTF8, false)
+ l := &Loader{Encoding: UTF8}
+ return l.LoadAll([]string{url})
}
// LoadURLs reads the content of multiple URLs in the given order into a
-// Properties struct. If 'ignoreMissing' is true then a 404 status code will
-// not be reported as error. See LoadURL for the Content-Type header
+// Properties struct. If IgnoreMissing is true then a 404 status code will
+// not be reported as error. See Loader#LoadURL for the Content-Type header
// and the encoding.
func LoadURLs(urls []string, ignoreMissing bool) (*Properties, error) {
- return loadAll(urls, UTF8, ignoreMissing)
+ l := &Loader{Encoding: UTF8, IgnoreMissing: ignoreMissing}
+ return l.LoadAll(urls)
}
// LoadAll reads the content of multiple URLs or files in the given order into a
@@ -79,7 +213,8 @@ func LoadURLs(urls []string, ignoreMissing bool) (*Properties, error) {
// not be reported as error. Encoding sets the encoding for files. For the URLs please see
// LoadURL for the Content-Type header and the encoding.
func LoadAll(names []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
- return loadAll(names, enc, ignoreMissing)
+ l := &Loader{Encoding: enc, IgnoreMissing: ignoreMissing}
+ return l.LoadAll(names)
}
// MustLoadString reads an UTF8 string into a Properties struct and
@@ -122,90 +257,6 @@ func MustLoadAll(names []string, enc Encoding, ignoreMissing bool) *Properties {
return must(LoadAll(names, enc, ignoreMissing))
}
-func loadBuf(buf []byte, enc Encoding) (*Properties, error) {
- p, err := parse(convert(buf, enc))
- if err != nil {
- return nil, err
- }
- return p, p.check()
-}
-
-func loadAll(names []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
- result := NewProperties()
- for _, name := range names {
- n, err := expandName(name)
- if err != nil {
- return nil, err
- }
- var p *Properties
- if strings.HasPrefix(n, "http://") || strings.HasPrefix(n, "https://") {
- p, err = loadURL(n, ignoreMissing)
- } else {
- p, err = loadFile(n, enc, ignoreMissing)
- }
- if err != nil {
- return nil, err
- }
- result.Merge(p)
-
- }
- return result, result.check()
-}
-
-func loadFile(filename string, enc Encoding, ignoreMissing bool) (*Properties, error) {
- data, err := ioutil.ReadFile(filename)
- if err != nil {
- if ignoreMissing && os.IsNotExist(err) {
- LogPrintf("properties: %s not found. skipping", filename)
- return NewProperties(), nil
- }
- return nil, err
- }
- p, err := parse(convert(data, enc))
- if err != nil {
- return nil, err
- }
- return p, nil
-}
-
-func loadURL(url string, ignoreMissing bool) (*Properties, error) {
- resp, err := http.Get(url)
- if err != nil {
- return nil, fmt.Errorf("properties: error fetching %q. %s", url, err)
- }
- if resp.StatusCode == 404 && ignoreMissing {
- LogPrintf("properties: %s returned %d. skipping", url, resp.StatusCode)
- return NewProperties(), nil
- }
- if resp.StatusCode != 200 {
- return nil, fmt.Errorf("properties: %s returned %d", url, resp.StatusCode)
- }
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return nil, fmt.Errorf("properties: %s error reading response. %s", url, err)
- }
- if err = resp.Body.Close(); err != nil {
- return nil, fmt.Errorf("properties: %s error reading response. %s", url, err)
- }
-
- ct := resp.Header.Get("Content-Type")
- var enc Encoding
- switch strings.ToLower(ct) {
- case "text/plain", "text/plain; charset=iso-8859-1", "text/plain; charset=latin1":
- enc = ISO_8859_1
- case "", "text/plain; charset=utf-8":
- enc = UTF8
- default:
- return nil, fmt.Errorf("properties: invalid content type %s", ct)
- }
-
- p, err := parse(convert(body, enc))
- if err != nil {
- return nil, err
- }
- return p, nil
-}
-
func must(p *Properties, err error) *Properties {
if err != nil {
ErrorHandler(err)
@@ -226,7 +277,7 @@ func expandName(name string) (string, error) {
// first 256 unicode code points cover ISO-8859-1.
func convert(buf []byte, enc Encoding) string {
switch enc {
- case UTF8:
+ case utf8Default, UTF8:
return string(buf)
case ISO_8859_1:
runes := make([]rune, len(buf))
diff --git a/vendor/github.com/magiconair/properties/parser.go b/vendor/github.com/magiconair/properties/parser.go
index 90f555cb..cdc4a803 100644
--- a/vendor/github.com/magiconair/properties/parser.go
+++ b/vendor/github.com/magiconair/properties/parser.go
@@ -1,4 +1,4 @@
-// Copyright 2017 Frank Schroeder. All rights reserved.
+// Copyright 2018 Frank Schroeder. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
diff --git a/vendor/github.com/magiconair/properties/properties.go b/vendor/github.com/magiconair/properties/properties.go
index 53f5b2ff..cb3d1a33 100644
--- a/vendor/github.com/magiconair/properties/properties.go
+++ b/vendor/github.com/magiconair/properties/properties.go
@@ -1,4 +1,4 @@
-// Copyright 2017 Frank Schroeder. All rights reserved.
+// Copyright 2018 Frank Schroeder. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
@@ -83,6 +83,17 @@ func NewProperties() *Properties {
}
}
+// Load reads a buffer into the given Properties struct.
+func (p *Properties) Load(buf []byte, enc Encoding) error {
+ l := &Loader{Encoding: enc, DisableExpansion: p.DisableExpansion}
+ newProperties, err := l.LoadBytes(buf)
+ if err != nil {
+ return err
+ }
+ p.Merge(newProperties)
+ return nil
+}
+
// Get returns the expanded value for the given key if exists.
// Otherwise, ok is false.
func (p *Properties) Get(key string) (value string, ok bool) {
diff --git a/vendor/github.com/magiconair/properties/rangecheck.go b/vendor/github.com/magiconair/properties/rangecheck.go
index 2e907d54..b013a2e5 100644
--- a/vendor/github.com/magiconair/properties/rangecheck.go
+++ b/vendor/github.com/magiconair/properties/rangecheck.go
@@ -1,4 +1,4 @@
-// Copyright 2017 Frank Schroeder. All rights reserved.
+// Copyright 2018 Frank Schroeder. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
diff --git a/vendor/github.com/matterbridge/discordgo/user.go b/vendor/github.com/matterbridge/discordgo/user.go
deleted file mode 100644
index a710f286..00000000
--- a/vendor/github.com/matterbridge/discordgo/user.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package discordgo
-
-import (
- "fmt"
- "strings"
-)
-
-// A User stores all data for an individual Discord user.
-type User struct {
- ID string `json:"id"`
- Email string `json:"email"`
- Username string `json:"username"`
- Avatar string `json:"avatar"`
- Discriminator string `json:"discriminator"`
- Token string `json:"token"`
- Verified bool `json:"verified"`
- MFAEnabled bool `json:"mfa_enabled"`
- Bot bool `json:"bot"`
-}
-
-// String returns a unique identifier of the form username#discriminator
-func (u *User) String() string {
- return fmt.Sprintf("%s#%s", u.Username, u.Discriminator)
-}
-
-// Mention return a string which mentions the user
-func (u *User) Mention() string {
- return fmt.Sprintf("<@%s>", u.ID)
-}
-
-// AvatarURL returns a URL to the user's avatar.
-// size: The size of the user's avatar as a power of two
-// if size is an empty string, no size parameter will
-// be added to the URL.
-func (u *User) AvatarURL(size string) string {
- var URL string
- if strings.HasPrefix(u.Avatar, "a_") {
- URL = EndpointUserAvatarAnimated(u.ID, u.Avatar)
- } else {
- URL = EndpointUserAvatar(u.ID, u.Avatar)
- }
-
- if size != "" {
- return URL + "?size=" + size
- }
- return URL
-}
diff --git a/vendor/github.com/mattermost/platform/LICENSE.txt b/vendor/github.com/mattermost/mattermost-server/LICENSE.txt
similarity index 99%
rename from vendor/github.com/mattermost/platform/LICENSE.txt
rename to vendor/github.com/mattermost/mattermost-server/LICENSE.txt
index ead98cf0..76e4c455 100644
--- a/vendor/github.com/mattermost/platform/LICENSE.txt
+++ b/vendor/github.com/mattermost/mattermost-server/LICENSE.txt
@@ -12,7 +12,7 @@ You may be licensed to use source code to create compiled versions not produced
2. Under a commercial license available from Mattermost, Inc. by contacting commercial@mattermost.com
You are licensed to use the source code in Admin Tools and Configuration Files (templates/, config/, model/,
-webapp/client, webapp/fonts, webapp/i18n, webapp/images and all subdirectories thereof) under the Apache License v2.0.
+plugin/ and all subdirectories thereof) under the Apache License v2.0.
We promise that we will not enforce the copyleft provisions in AGPL v3.0 against you if your application (a) does not
link to the Mattermost Platform directly, but exclusively uses the Mattermost Admin Tools and Configuration Files, and
diff --git a/vendor/github.com/mattermost/mattermost-server/NOTICE.txt b/vendor/github.com/mattermost/mattermost-server/NOTICE.txt
new file mode 100644
index 00000000..55141290
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/NOTICE.txt
@@ -0,0 +1,3721 @@
+Mattermost Server
+© 2015-present Mattermost, Inc. All Rights Reserved. See LICENSE.txt for license information.
+
+NOTICES:
+--------
+
+This document includes a list of open source components used in Mattermost Server, including those that have been modified.
+
+---
+
+## NYTimes/gziphandler
+
+This product contains 'gziphandler' by The New York Times.
+
+Golang middleware to gzip HTTP responses
+
+* HOMEPAGE:
+ * https://github.com/NYTimes/gziphandler
+
+* LICENSE: Apache-2.0
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2016-2017 The New York Times Company
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+---
+
+## avct/uasurfer
+
+This product contains 'uasurfer' by Avocet.
+
+Go package for fast and reliable abstraction of browser user agent strings.
+
+* HOMEPAGE:
+ * https://github.com/avct/uasurfer
+
+* LICENSE: Apache-2.0
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ Copyright 2015 Avocet Systems Ltd.
+ http://avocet.io/opensource
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ Copyright 2015 Avocet Systems Ltd.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+---
+
+## dgryski/dgoogauth
+
+This product contains 'dgoogauth' by Damian Gryski.
+
+Google Authenticator for Go
+
+* HOMEPAGE:
+ * https://github.com/dgryski/dgoogauth
+
+* LICENSE: Apache-2.0
+
+Note: An original license file for this dependency is not available. We determined the type of license based on the official project website. The following text has been prepared using a template from the SPDX Workgroup (https://spdx.org) for this type of license.
+
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+ (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
+
+Copyright 2018 Damian Gryski
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+---
+
+## disintegration/imaging
+
+This product contains 'imaging' by Grigory Dryapak.
+
+Imaging is a simple image processing package for Go
+
+* HOMEPAGE:
+ * https://github.com/disintegration/imaging
+
+* LICENSE: MIT
+
+The MIT License (MIT)
+
+Copyright (c) 2012-2018 Grigory Dryapak
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+---
+
+## dyatlov/go-opengraph
+
+This product contains 'go-opengraph' by Vitaly Dyatlov.
+
+Golang package for parsing OpenGraph data from HTML into regular structures
+
+* HOMEPAGE:
+ * https://github.com/dyatlov/go-opengraph
+
+* LICENSE: MIT
+
+The MIT License (MIT)
+
+Copyright (c) 2015 Vitaly Dyatlov
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+---
+
+## fsnotify/fsnotify
+
+This product contains 'fsnotify' by GitHub user "fsnotify".
+
+Cross-platform file system notifications for Go.
+
+* HOMEPAGE:
+ * https://github.com/fsnotify/fsnotify
+
+* LICENSE: BSD-3-Clause
+
+Copyright (c) 2012 The Go Authors. All rights reserved.
+Copyright (c) 2012 fsnotify Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---
+
+## go-ldap/ldap
+
+This product contains 'ldap' by GitHub user "go-ldap".
+
+Basic LDAP v3 functionality for the GO programming language.
+
+* HOMEPAGE:
+ * https://github.com/go-ldap/ldap
+
+* LICENSE: MIT
+
+The MIT License (MIT)
+
+Copyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com)
+Portions copyright (c) 2015-2016 go-ldap Authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+---
+
+## go-redis/redis
+
+This product contains 'redis' by GitHub user "go-redis".
+
+Type-safe Redis client for Golang
+
+* HOMEPAGE:
+ * https://github.com/go-redis/redis
+
+* LICENSE: BSD-2-Clause
+
+Copyright (c) 2013 The github.com/go-redis/redis Authors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---
+
+## go-sql-driver/mysql
+
+This product contains 'mysql' by Go SQL Drivers.
+
+Go MySQL Driver is a MySQL driver for Go's (golang) database/sql package
+
+* HOMEPAGE:
+ * https://github.com/go-sql-driver/mysql
+
+* LICENSE: MPL-2.0
+
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+ means each individual or legal entity that creates, contributes to
+ the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+ means the combination of the Contributions of others (if any) used
+ by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+ means Source Code Form to which the initial Contributor has attached
+ the notice in Exhibit A, the Executable Form of such Source Code
+ Form, and Modifications of such Source Code Form, in each case
+ including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ (a) that the initial Contributor has attached the notice described
+ in Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the
+ terms of a Secondary License.
+
+1.6. "Executable Form"
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+ means a work that combines Covered Software with other material, in
+ a separate file or files, that is not Covered Software.
+
+1.8. "License"
+ means this document.
+
+1.9. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently, any and
+ all of the rights conveyed by this License.
+
+1.10. "Modifications"
+ means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered
+ Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+1.11. "Patent Claims" of a Contributor
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the
+ License, by the making, using, selling, offering for sale, having
+ made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+1.12. "Secondary License"
+ means either the GNU General Public License, Version 2.0, the GNU
+ Lesser General Public License, Version 2.1, the GNU Affero General
+ Public License, Version 3.0, or any later versions of those
+ licenses.
+
+1.13. "Source Code Form"
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that
+ controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct
+ or indirect, to cause the direction or management of such entity,
+ whether by contract or otherwise, or (b) ownership of more than
+ fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+ or
+
+(b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more
+ than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter
+ the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+* *
+* 6. Disclaimer of Warranty *
+* ------------------------- *
+* *
+* Covered Software is provided under this License on an "as is" *
+* basis, without warranty of any kind, either expressed, implied, or *
+* statutory, including, without limitation, warranties that the *
+* Covered Software is free of defects, merchantable, fit for a *
+* particular purpose or non-infringing. The entire risk as to the *
+* quality and performance of the Covered Software is with You. *
+* Should any Covered Software prove defective in any respect, You *
+* (not any Contributor) assume the cost of any necessary servicing, *
+* repair, or correction. This disclaimer of warranty constitutes an *
+* essential part of this License. No use of any Covered Software is *
+* authorized under this License except under this disclaimer. *
+* *
+************************************************************************
+
+************************************************************************
+* *
+* 7. Limitation of Liability *
+* -------------------------- *
+* *
+* Under no circumstances and under no legal theory, whether tort *
+* (including negligence), contract, or otherwise, shall any *
+* Contributor, or anyone who distributes Covered Software as *
+* permitted above, be liable to You for any direct, indirect, *
+* special, incidental, or consequential damages of any character *
+* including, without limitation, damages for lost profits, loss of *
+* goodwill, work stoppage, computer failure or malfunction, or any *
+* and all other commercial damages or losses, even if such party *
+* shall have been informed of the possibility of such damages. This *
+* limitation of liability shall not apply to liability for death or *
+* personal injury resulting from such party's negligence to the *
+* extent applicable law prohibits such limitation. Some *
+* jurisdictions do not allow the exclusion or limitation of *
+* incidental or consequential damages, so this exclusion and *
+* limitation may not apply to You. *
+* *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+ This Source Code Form is "Incompatible With Secondary Licenses", as
+ defined by the Mozilla Public License, v. 2.0.
+
+---
+
+## golang/freetype
+
+This product contains 'freetype' by Go.
+
+The Freetype font rasterizer in the Go programming language.
+
+* HOMEPAGE:
+ * https://github.com/golang/freetype
+
+* LICENSE: (FTL OR GPL-2.0)
+
+Use of the Freetype-Go software is subject to your choice of exactly one of
+the following two licenses:
+ * The FreeType License, which is similar to the original BSD license with
+ an advertising clause, or
+ * The GNU General Public License (GPL), version 2 or later.
+
+The text of these licenses are available in the licenses/ftl.txt and the
+licenses/gpl.txt files respectively. They are also available at
+http://freetype.sourceforge.net/license.html
+
+The Luxi fonts in the testdata directory are licensed separately. See the
+testdata/COPYING file for details.
+
+---
+
+## gorilla/handlers
+
+This product contains 'handlers' by Gorilla web toolkit.
+
+A collection of useful handlers for Go's net/http package.
+
+* HOMEPAGE:
+ * https://github.com/gorilla/handlers
+
+* LICENSE: BSD-2-Clause
+
+Copyright (c) 2013 The Gorilla Handlers Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---
+
+## gorilla/mux
+
+This product contains 'mux' by Gorilla web toolkit.
+
+A powerful URL router and dispatcher for golang.
+
+* HOMEPAGE:
+ * https://github.com/gorilla/mux
+
+* LICENSE: BSD-3-Clause
+
+Copyright (c) 2012-2018 The Gorilla Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---
+
+## gorilla/schema
+
+This product contains 'schema' by Gorilla web toolkit.
+
+Package gorilla/schema fills a struct with form values.
+
+* HOMEPAGE:
+ * https://github.com/gorilla/schema
+
+* LICENSE: BSD-3-Clause
+
+Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---
+
+## gorilla/websocket
+
+This product contains 'websocket' by Gorilla web toolkit.
+
+A WebSocket implementation for Go.
+
+* HOMEPAGE:
+ * https://github.com/gorilla/websocket
+
+* LICENSE: BSD-2-Clause
+
+Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---
+
+## hako/durafmt
+
+This product contains 'durafmt' by Wesley Hill.
+
+:clock8: Better time duration formatting in Go!
+
+* HOMEPAGE:
+ * https://github.com/hako/durafmt
+
+* LICENSE: MIT
+
+The MIT License (MIT)
+
+Copyright (c) 2016 Wesley Hill
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+---
+
+## hashicorp/go-hclog
+
+This product contains 'go-hclog' by HashiCorp.
+
+A common logging package for HashiCorp tools
+
+* HOMEPAGE:
+ * https://github.com/hashicorp/go-hclog
+
+* LICENSE: MIT
+
+MIT License
+
+Copyright (c) 2017 HashiCorp
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+---
+
+## hashicorp/go-plugin
+
+This product contains 'go-plugin' by HashiCorp.
+
+Golang plugin system over RPC.
+
+* HOMEPAGE:
+ * https://github.com/hashicorp/go-plugin
+
+* LICENSE: MPL-2.0
+
+Mozilla Public License, version 2.0
+
+1. Definitions
+
+1.1. “Contributor”
+
+ means each individual or legal entity that creates, contributes to the
+ creation of, or owns Covered Software.
+
+1.2. “Contributor Version”
+
+ means the combination of the Contributions of others (if any) used by a
+ Contributor and that particular Contributor’s Contribution.
+
+1.3. “Contribution”
+
+ means Covered Software of a particular Contributor.
+
+1.4. “Covered Software”
+
+ means Source Code Form to which the initial Contributor has attached the
+ notice in Exhibit A, the Executable Form of such Source Code Form, and
+ Modifications of such Source Code Form, in each case including portions
+ thereof.
+
+1.5. “Incompatible With Secondary Licenses”
+ means
+
+ a. that the initial Contributor has attached the notice described in
+ Exhibit B to the Covered Software; or
+
+ b. that the Covered Software was made available under the terms of version
+ 1.1 or earlier of the License, but not also under the terms of a
+ Secondary License.
+
+1.6. “Executable Form”
+
+ means any form of the work other than Source Code Form.
+
+1.7. “Larger Work”
+
+ means a work that combines Covered Software with other material, in a separate
+ file or files, that is not Covered Software.
+
+1.8. “License”
+
+ means this document.
+
+1.9. “Licensable”
+
+ means having the right to grant, to the maximum extent possible, whether at the
+ time of the initial grant or subsequently, any and all of the rights conveyed by
+ this License.
+
+1.10. “Modifications”
+
+ means any of the following:
+
+ a. any file in Source Code Form that results from an addition to, deletion
+ from, or modification of the contents of Covered Software; or
+
+ b. any new file in Source Code Form that contains any Covered Software.
+
+1.11. “Patent Claims” of a Contributor
+
+ means any patent claim(s), including without limitation, method, process,
+ and apparatus claims, in any patent Licensable by such Contributor that
+ would be infringed, but for the grant of the License, by the making,
+ using, selling, offering for sale, having made, import, or transfer of
+ either its Contributions or its Contributor Version.
+
+1.12. “Secondary License”
+
+ means either the GNU General Public License, Version 2.0, the GNU Lesser
+ General Public License, Version 2.1, the GNU Affero General Public
+ License, Version 3.0, or any later versions of those licenses.
+
+1.13. “Source Code Form”
+
+ means the form of the work preferred for making modifications.
+
+1.14. “You” (or “Your”)
+
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, “You” includes any entity that controls, is
+ controlled by, or is under common control with You. For purposes of this
+ definition, “control” means (a) the power, direct or indirect, to cause
+ the direction or management of such entity, whether by contract or
+ otherwise, or (b) ownership of more than fifty percent (50%) of the
+ outstanding shares or beneficial ownership of such entity.
+
+
+2. License Grants and Conditions
+
+2.1. Grants
+
+ Each Contributor hereby grants You a world-wide, royalty-free,
+ non-exclusive license:
+
+ a. under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or as
+ part of a Larger Work; and
+
+ b. under Patent Claims of such Contributor to make, use, sell, offer for
+ sale, have made, import, and otherwise transfer either its Contributions
+ or its Contributor Version.
+
+2.2. Effective Date
+
+ The licenses granted in Section 2.1 with respect to any Contribution become
+ effective for each Contribution on the date the Contributor first distributes
+ such Contribution.
+
+2.3. Limitations on Grant Scope
+
+ The licenses granted in this Section 2 are the only rights granted under this
+ License. No additional rights or licenses will be implied from the distribution
+ or licensing of Covered Software under this License. Notwithstanding Section
+ 2.1(b) above, no patent license is granted by a Contributor:
+
+ a. for any code that a Contributor has removed from Covered Software; or
+
+ b. for infringements caused by: (i) Your and any other third party’s
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+ c. under Patent Claims infringed by Covered Software in the absence of its
+ Contributions.
+
+ This License does not grant any rights in the trademarks, service marks, or
+ logos of any Contributor (except as may be necessary to comply with the
+ notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+ No Contributor makes additional grants as a result of Your choice to
+ distribute the Covered Software under a subsequent version of this License
+ (see Section 10.2) or under the terms of a Secondary License (if permitted
+ under the terms of Section 3.3).
+
+2.5. Representation
+
+ Each Contributor represents that the Contributor believes its Contributions
+ are its original creation(s) or it has sufficient rights to grant the
+ rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+ This License is not intended to limit any rights You have under applicable
+ copyright doctrines of fair use, fair dealing, or other equivalents.
+
+2.7. Conditions
+
+ Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
+ Section 2.1.
+
+
+3. Responsibilities
+
+3.1. Distribution of Source Form
+
+ All distribution of Covered Software in Source Code Form, including any
+ Modifications that You create or to which You contribute, must be under the
+ terms of this License. You must inform recipients that the Source Code Form
+ of the Covered Software is governed by the terms of this License, and how
+ they can obtain a copy of this License. You may not attempt to alter or
+ restrict the recipients’ rights in the Source Code Form.
+
+3.2. Distribution of Executable Form
+
+ If You distribute Covered Software in Executable Form then:
+
+ a. such Covered Software must also be made available in Source Code Form,
+ as described in Section 3.1, and You must inform recipients of the
+ Executable Form how they can obtain a copy of such Source Code Form by
+ reasonable means in a timely manner, at a charge no more than the cost
+ of distribution to the recipient; and
+
+ b. You may distribute such Executable Form under the terms of this License,
+ or sublicense it under different terms, provided that the license for
+ the Executable Form does not attempt to limit or alter the recipients’
+ rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+ You may create and distribute a Larger Work under terms of Your choice,
+ provided that You also comply with the requirements of this License for the
+ Covered Software. If the Larger Work is a combination of Covered Software
+ with a work governed by one or more Secondary Licenses, and the Covered
+ Software is not Incompatible With Secondary Licenses, this License permits
+ You to additionally distribute such Covered Software under the terms of
+ such Secondary License(s), so that the recipient of the Larger Work may, at
+ their option, further distribute the Covered Software under the terms of
+ either this License or such Secondary License(s).
+
+3.4. Notices
+
+ You may not remove or alter the substance of any license notices (including
+ copyright notices, patent notices, disclaimers of warranty, or limitations
+ of liability) contained within the Source Code Form of the Covered
+ Software, except that You may alter any license notices to the extent
+ required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+ You may choose to offer, and to charge a fee for, warranty, support,
+ indemnity or liability obligations to one or more recipients of Covered
+ Software. However, You may do so only on Your own behalf, and not on behalf
+ of any Contributor. You must make it absolutely clear that any such
+ warranty, support, indemnity, or liability obligation is offered by You
+ alone, and You hereby agree to indemnify every Contributor for any
+ liability incurred by such Contributor as a result of warranty, support,
+ indemnity or liability terms You offer. You may include additional
+ disclaimers of warranty and limitations of liability specific to any
+ jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+
+ If it is impossible for You to comply with any of the terms of this License
+ with respect to some or all of the Covered Software due to statute, judicial
+ order, or regulation then You must: (a) comply with the terms of this License
+ to the maximum extent possible; and (b) describe the limitations and the code
+ they affect. Such description must be placed in a text file included with all
+ distributions of the Covered Software under this License. Except to the
+ extent prohibited by statute or regulation, such description must be
+ sufficiently detailed for a recipient of ordinary skill to be able to
+ understand it.
+
+5. Termination
+
+5.1. The rights granted under this License will terminate automatically if You
+ fail to comply with any of its terms. However, if You become compliant,
+ then the rights granted under this License from a particular Contributor
+ are reinstated (a) provisionally, unless and until such Contributor
+ explicitly and finally terminates Your grants, and (b) on an ongoing basis,
+ if such Contributor fails to notify You of the non-compliance by some
+ reasonable means prior to 60 days after You have come back into compliance.
+ Moreover, Your grants from a particular Contributor are reinstated on an
+ ongoing basis if such Contributor notifies You of the non-compliance by
+ some reasonable means, this is the first time You have received notice of
+ non-compliance with this License from such Contributor, and You become
+ compliant prior to 30 days after Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+ infringement claim (excluding declaratory judgment actions, counter-claims,
+ and cross-claims) alleging that a Contributor Version directly or
+ indirectly infringes any patent, then the rights granted to You by any and
+ all Contributors for the Covered Software under Section 2.1 of this License
+ shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
+ license agreements (excluding distributors and resellers) which have been
+ validly granted by You or Your distributors under this License prior to
+ termination shall survive termination.
+
+6. Disclaimer of Warranty
+
+ Covered Software is provided under this License on an “as is” basis, without
+ warranty of any kind, either expressed, implied, or statutory, including,
+ without limitation, warranties that the Covered Software is free of defects,
+ merchantable, fit for a particular purpose or non-infringing. The entire
+ risk as to the quality and performance of the Covered Software is with You.
+ Should any Covered Software prove defective in any respect, You (not any
+ Contributor) assume the cost of any necessary servicing, repair, or
+ correction. This disclaimer of warranty constitutes an essential part of this
+ License. No use of any Covered Software is authorized under this License
+ except under this disclaimer.
+
+7. Limitation of Liability
+
+ Under no circumstances and under no legal theory, whether tort (including
+ negligence), contract, or otherwise, shall any Contributor, or anyone who
+ distributes Covered Software as permitted above, be liable to You for any
+ direct, indirect, special, incidental, or consequential damages of any
+ character including, without limitation, damages for lost profits, loss of
+ goodwill, work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses, even if such party shall have been
+ informed of the possibility of such damages. This limitation of liability
+ shall not apply to liability for death or personal injury resulting from such
+ party’s negligence to the extent applicable law prohibits such limitation.
+ Some jurisdictions do not allow the exclusion or limitation of incidental or
+ consequential damages, so this exclusion and limitation may not apply to You.
+
+8. Litigation
+
+ Any litigation relating to this License may be brought only in the courts of
+ a jurisdiction where the defendant maintains its principal place of business
+ and such litigation shall be governed by laws of that jurisdiction, without
+ reference to its conflict-of-law provisions. Nothing in this Section shall
+ prevent a party’s ability to bring cross-claims or counter-claims.
+
+9. Miscellaneous
+
+ This License represents the complete agreement concerning the subject matter
+ hereof. If any provision of this License is held to be unenforceable, such
+ provision shall be reformed only to the extent necessary to make it
+ enforceable. Any law or regulation which provides that the language of a
+ contract shall be construed against the drafter shall not be used to construe
+ this License against a Contributor.
+
+
+10. Versions of the License
+
+10.1. New Versions
+
+ Mozilla Foundation is the license steward. Except as provided in Section
+ 10.3, no one other than the license steward has the right to modify or
+ publish new versions of this License. Each version will be given a
+ distinguishing version number.
+
+10.2. Effect of New Versions
+
+ You may distribute the Covered Software under the terms of the version of
+ the License under which You originally received the Covered Software, or
+ under the terms of any subsequent version published by the license
+ steward.
+
+10.3. Modified Versions
+
+ If you create software not governed by this License, and you want to
+ create a new license for such software, you may create and use a modified
+ version of this License if you rename the license and remove any
+ references to the name of the license steward (except to note that such
+ modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
+ If You choose to distribute Source Code Form that is Incompatible With
+ Secondary Licenses under the terms of this version of the License, the
+ notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+
+ This Source Code Form is subject to the
+ terms of the Mozilla Public License, v.
+ 2.0. If a copy of the MPL was not
+ distributed with this file, You can
+ obtain one at
+ http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular file, then
+You may include the notice in a location (such as a LICENSE file in a relevant
+directory) where a recipient would be likely to look for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - “Incompatible With Secondary Licenses” Notice
+
+ This Source Code Form is “Incompatible
+ With Secondary Licenses”, as defined by
+ the Mozilla Public License, v. 2.0.
+
+---
+
+## hashicorp/memberlist
+
+This product contains 'memberlist' by HashiCorp.
+
+Golang package for gossip based membership and failure detection
+
+* HOMEPAGE:
+ * https://github.com/hashicorp/memberlist
+
+* LICENSE: MPL-2.0
+
+Mozilla Public License, version 2.0
+
+1. Definitions
+
+1.1. “Contributor”
+
+ means each individual or legal entity that creates, contributes to the
+ creation of, or owns Covered Software.
+
+1.2. “Contributor Version”
+
+ means the combination of the Contributions of others (if any) used by a
+ Contributor and that particular Contributor’s Contribution.
+
+1.3. “Contribution”
+
+ means Covered Software of a particular Contributor.
+
+1.4. “Covered Software”
+
+ means Source Code Form to which the initial Contributor has attached the
+ notice in Exhibit A, the Executable Form of such Source Code Form, and
+ Modifications of such Source Code Form, in each case including portions
+ thereof.
+
+1.5. “Incompatible With Secondary Licenses”
+ means
+
+ a. that the initial Contributor has attached the notice described in
+ Exhibit B to the Covered Software; or
+
+ b. that the Covered Software was made available under the terms of version
+ 1.1 or earlier of the License, but not also under the terms of a
+ Secondary License.
+
+1.6. “Executable Form”
+
+ means any form of the work other than Source Code Form.
+
+1.7. “Larger Work”
+
+ means a work that combines Covered Software with other material, in a separate
+ file or files, that is not Covered Software.
+
+1.8. “License”
+
+ means this document.
+
+1.9. “Licensable”
+
+ means having the right to grant, to the maximum extent possible, whether at the
+ time of the initial grant or subsequently, any and all of the rights conveyed by
+ this License.
+
+1.10. “Modifications”
+
+ means any of the following:
+
+ a. any file in Source Code Form that results from an addition to, deletion
+ from, or modification of the contents of Covered Software; or
+
+ b. any new file in Source Code Form that contains any Covered Software.
+
+1.11. “Patent Claims” of a Contributor
+
+ means any patent claim(s), including without limitation, method, process,
+ and apparatus claims, in any patent Licensable by such Contributor that
+ would be infringed, but for the grant of the License, by the making,
+ using, selling, offering for sale, having made, import, or transfer of
+ either its Contributions or its Contributor Version.
+
+1.12. “Secondary License”
+
+ means either the GNU General Public License, Version 2.0, the GNU Lesser
+ General Public License, Version 2.1, the GNU Affero General Public
+ License, Version 3.0, or any later versions of those licenses.
+
+1.13. “Source Code Form”
+
+ means the form of the work preferred for making modifications.
+
+1.14. “You” (or “Your”)
+
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, “You” includes any entity that controls, is
+ controlled by, or is under common control with You. For purposes of this
+ definition, “control” means (a) the power, direct or indirect, to cause
+ the direction or management of such entity, whether by contract or
+ otherwise, or (b) ownership of more than fifty percent (50%) of the
+ outstanding shares or beneficial ownership of such entity.
+
+
+2. License Grants and Conditions
+
+2.1. Grants
+
+ Each Contributor hereby grants You a world-wide, royalty-free,
+ non-exclusive license:
+
+ a. under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or as
+ part of a Larger Work; and
+
+ b. under Patent Claims of such Contributor to make, use, sell, offer for
+ sale, have made, import, and otherwise transfer either its Contributions
+ or its Contributor Version.
+
+2.2. Effective Date
+
+ The licenses granted in Section 2.1 with respect to any Contribution become
+ effective for each Contribution on the date the Contributor first distributes
+ such Contribution.
+
+2.3. Limitations on Grant Scope
+
+ The licenses granted in this Section 2 are the only rights granted under this
+ License. No additional rights or licenses will be implied from the distribution
+ or licensing of Covered Software under this License. Notwithstanding Section
+ 2.1(b) above, no patent license is granted by a Contributor:
+
+ a. for any code that a Contributor has removed from Covered Software; or
+
+ b. for infringements caused by: (i) Your and any other third party’s
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+ c. under Patent Claims infringed by Covered Software in the absence of its
+ Contributions.
+
+ This License does not grant any rights in the trademarks, service marks, or
+ logos of any Contributor (except as may be necessary to comply with the
+ notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+ No Contributor makes additional grants as a result of Your choice to
+ distribute the Covered Software under a subsequent version of this License
+ (see Section 10.2) or under the terms of a Secondary License (if permitted
+ under the terms of Section 3.3).
+
+2.5. Representation
+
+ Each Contributor represents that the Contributor believes its Contributions
+ are its original creation(s) or it has sufficient rights to grant the
+ rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+ This License is not intended to limit any rights You have under applicable
+ copyright doctrines of fair use, fair dealing, or other equivalents.
+
+2.7. Conditions
+
+ Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
+ Section 2.1.
+
+
+3. Responsibilities
+
+3.1. Distribution of Source Form
+
+ All distribution of Covered Software in Source Code Form, including any
+ Modifications that You create or to which You contribute, must be under the
+ terms of this License. You must inform recipients that the Source Code Form
+ of the Covered Software is governed by the terms of this License, and how
+ they can obtain a copy of this License. You may not attempt to alter or
+ restrict the recipients’ rights in the Source Code Form.
+
+3.2. Distribution of Executable Form
+
+ If You distribute Covered Software in Executable Form then:
+
+ a. such Covered Software must also be made available in Source Code Form,
+ as described in Section 3.1, and You must inform recipients of the
+ Executable Form how they can obtain a copy of such Source Code Form by
+ reasonable means in a timely manner, at a charge no more than the cost
+ of distribution to the recipient; and
+
+ b. You may distribute such Executable Form under the terms of this License,
+ or sublicense it under different terms, provided that the license for
+ the Executable Form does not attempt to limit or alter the recipients’
+ rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+ You may create and distribute a Larger Work under terms of Your choice,
+ provided that You also comply with the requirements of this License for the
+ Covered Software. If the Larger Work is a combination of Covered Software
+ with a work governed by one or more Secondary Licenses, and the Covered
+ Software is not Incompatible With Secondary Licenses, this License permits
+ You to additionally distribute such Covered Software under the terms of
+ such Secondary License(s), so that the recipient of the Larger Work may, at
+ their option, further distribute the Covered Software under the terms of
+ either this License or such Secondary License(s).
+
+3.4. Notices
+
+ You may not remove or alter the substance of any license notices (including
+ copyright notices, patent notices, disclaimers of warranty, or limitations
+ of liability) contained within the Source Code Form of the Covered
+ Software, except that You may alter any license notices to the extent
+ required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+ You may choose to offer, and to charge a fee for, warranty, support,
+ indemnity or liability obligations to one or more recipients of Covered
+ Software. However, You may do so only on Your own behalf, and not on behalf
+ of any Contributor. You must make it absolutely clear that any such
+ warranty, support, indemnity, or liability obligation is offered by You
+ alone, and You hereby agree to indemnify every Contributor for any
+ liability incurred by such Contributor as a result of warranty, support,
+ indemnity or liability terms You offer. You may include additional
+ disclaimers of warranty and limitations of liability specific to any
+ jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+
+ If it is impossible for You to comply with any of the terms of this License
+ with respect to some or all of the Covered Software due to statute, judicial
+ order, or regulation then You must: (a) comply with the terms of this License
+ to the maximum extent possible; and (b) describe the limitations and the code
+ they affect. Such description must be placed in a text file included with all
+ distributions of the Covered Software under this License. Except to the
+ extent prohibited by statute or regulation, such description must be
+ sufficiently detailed for a recipient of ordinary skill to be able to
+ understand it.
+
+5. Termination
+
+5.1. The rights granted under this License will terminate automatically if You
+ fail to comply with any of its terms. However, if You become compliant,
+ then the rights granted under this License from a particular Contributor
+ are reinstated (a) provisionally, unless and until such Contributor
+ explicitly and finally terminates Your grants, and (b) on an ongoing basis,
+ if such Contributor fails to notify You of the non-compliance by some
+ reasonable means prior to 60 days after You have come back into compliance.
+ Moreover, Your grants from a particular Contributor are reinstated on an
+ ongoing basis if such Contributor notifies You of the non-compliance by
+ some reasonable means, this is the first time You have received notice of
+ non-compliance with this License from such Contributor, and You become
+ compliant prior to 30 days after Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+ infringement claim (excluding declaratory judgment actions, counter-claims,
+ and cross-claims) alleging that a Contributor Version directly or
+ indirectly infringes any patent, then the rights granted to You by any and
+ all Contributors for the Covered Software under Section 2.1 of this License
+ shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
+ license agreements (excluding distributors and resellers) which have been
+ validly granted by You or Your distributors under this License prior to
+ termination shall survive termination.
+
+6. Disclaimer of Warranty
+
+ Covered Software is provided under this License on an “as is” basis, without
+ warranty of any kind, either expressed, implied, or statutory, including,
+ without limitation, warranties that the Covered Software is free of defects,
+ merchantable, fit for a particular purpose or non-infringing. The entire
+ risk as to the quality and performance of the Covered Software is with You.
+ Should any Covered Software prove defective in any respect, You (not any
+ Contributor) assume the cost of any necessary servicing, repair, or
+ correction. This disclaimer of warranty constitutes an essential part of this
+ License. No use of any Covered Software is authorized under this License
+ except under this disclaimer.
+
+7. Limitation of Liability
+
+ Under no circumstances and under no legal theory, whether tort (including
+ negligence), contract, or otherwise, shall any Contributor, or anyone who
+ distributes Covered Software as permitted above, be liable to You for any
+ direct, indirect, special, incidental, or consequential damages of any
+ character including, without limitation, damages for lost profits, loss of
+ goodwill, work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses, even if such party shall have been
+ informed of the possibility of such damages. This limitation of liability
+ shall not apply to liability for death or personal injury resulting from such
+ party’s negligence to the extent applicable law prohibits such limitation.
+ Some jurisdictions do not allow the exclusion or limitation of incidental or
+ consequential damages, so this exclusion and limitation may not apply to You.
+
+8. Litigation
+
+ Any litigation relating to this License may be brought only in the courts of
+ a jurisdiction where the defendant maintains its principal place of business
+ and such litigation shall be governed by laws of that jurisdiction, without
+ reference to its conflict-of-law provisions. Nothing in this Section shall
+ prevent a party’s ability to bring cross-claims or counter-claims.
+
+9. Miscellaneous
+
+ This License represents the complete agreement concerning the subject matter
+ hereof. If any provision of this License is held to be unenforceable, such
+ provision shall be reformed only to the extent necessary to make it
+ enforceable. Any law or regulation which provides that the language of a
+ contract shall be construed against the drafter shall not be used to construe
+ this License against a Contributor.
+
+
+10. Versions of the License
+
+10.1. New Versions
+
+ Mozilla Foundation is the license steward. Except as provided in Section
+ 10.3, no one other than the license steward has the right to modify or
+ publish new versions of this License. Each version will be given a
+ distinguishing version number.
+
+10.2. Effect of New Versions
+
+ You may distribute the Covered Software under the terms of the version of
+ the License under which You originally received the Covered Software, or
+ under the terms of any subsequent version published by the license
+ steward.
+
+10.3. Modified Versions
+
+ If you create software not governed by this License, and you want to
+ create a new license for such software, you may create and use a modified
+ version of this License if you rename the license and remove any
+ references to the name of the license steward (except to note that such
+ modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
+ If You choose to distribute Source Code Form that is Incompatible With
+ Secondary Licenses under the terms of this version of the License, the
+ notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+
+ This Source Code Form is subject to the
+ terms of the Mozilla Public License, v.
+ 2.0. If a copy of the MPL was not
+ distributed with this file, You can
+ obtain one at
+ http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular file, then
+You may include the notice in a location (such as a LICENSE file in a relevant
+directory) where a recipient would be likely to look for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - “Incompatible With Secondary Licenses” Notice
+
+ This Source Code Form is “Incompatible
+ With Secondary Licenses”, as defined by
+ the Mozilla Public License, v. 2.0.
+
+---
+
+## icrowley/fake
+
+This product contains 'fake' by GitHub user "icrowley".
+
+Fake data generator for Go (Golang)
+
+* HOMEPAGE:
+ * https://github.com/icrowley/fake
+
+* LICENSE: MIT
+
+The MIT License (MIT)
+
+Copyright (c) 2014 Dmitry Afanasyev
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+---
+
+## jaytaylor/html2text
+
+This product contains 'html2text' by J. Elliot Taylor.
+
+Golang HTML to plaintext conversion library
+
+* HOMEPAGE:
+ * https://github.com/jaytaylor/html2text
+
+* LICENSE: MIT
+
+The MIT License (MIT)
+
+Copyright (c) 2015 Jay Taylor
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+---
+
+## lib/pq
+
+This product contains 'pq' by GitHub user "lib".
+
+Pure Go Postgres driver for database/sql
+
+* HOMEPAGE:
+ * https://github.com/lib/pq
+
+* LICENSE: MIT
+
+Copyright (c) 2011-2013, 'pq' Contributors
+Portions Copyright (C) 2011 Blake Mizerany
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+---
+
+## mattermost/gorp
+
+This product contains 'gorp' by Mattermost (forked from original GitHub repo 'go-gorp/gorp' owned by GitHub user "go-gorp").
+
+Go Relational Persistence - an ORM-ish library for Go
+
+* HOMEPAGE:
+ * https://github.com/mattermost/gorp
+
+* LICENSE: MIT
+
+(The MIT License)
+
+Copyright (c) 2012 James Cooper " + } + for _, inline := range v.ParseInlines(referenceDefinitions) { + result += RenderInlineHTML(inline) + } + if !isTightList { + result += "
" + } + case *List: + if v.IsOrdered { + if v.OrderedStart != 1 { + result += fmt.Sprintf(`" + for _, block := range v.Children { + result += RenderBlockHTML(block, referenceDefinitions) + } + result += "" + case *FencedCode: + if info := v.Info(); info != "" { + language := strings.Fields(info)[0] + result += `
`
+ } else {
+ result += ""
+ }
+ result += htmlEscaper.Replace(v.Code()) + "
"
+ case *IndentedCode:
+ result += "" + htmlEscaper.Replace(v.Code()) + "
"
+ default:
+ panic(fmt.Sprintf("missing case for type %T", v))
+ }
+ return
+}
+
+func escapeURL(url string) (result string) {
+ for i := 0; i < len(url); {
+ switch b := url[i]; b {
+ case ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '-', '_', '.', '!', '~', '*', '\'', '(', ')', '#':
+ result += string(b)
+ i++
+ default:
+ if b == '%' && i+2 < len(url) && isHexByte(url[i+1]) && isHexByte(url[i+2]) {
+ result += url[i : i+3]
+ i += 3
+ } else if (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') {
+ result += string(b)
+ i++
+ } else {
+ result += fmt.Sprintf("%%%0X", b)
+ i++
+ }
+ }
+ }
+ return
+}
+
+func RenderInlineHTML(inline Inline) (result string) {
+ switch v := inline.(type) {
+ case *Text:
+ return htmlEscaper.Replace(v.Text)
+ case *HardLineBreak:
+ return "
"
+ case *SoftLineBreak:
+ return "\n"
+ case *CodeSpan:
+ return "" + htmlEscaper.Replace(v.Code) + ""
+ case *InlineImage:
+ result += `
`
+ case *ReferenceImage:
+ result += `
`
+ case *InlineLink:
+ result += ``
+ for _, inline := range v.Children {
+ result += RenderInlineHTML(inline)
+ }
+ result += ""
+ case *ReferenceLink:
+ result += ``
+ for _, inline := range v.Children {
+ result += RenderInlineHTML(inline)
+ }
+ result += ""
+ case *Autolink:
+ result += ``
+ for _, inline := range v.Children {
+ result += RenderInlineHTML(inline)
+ }
+ result += ""
+ default:
+ panic(fmt.Sprintf("missing case for type %T", v))
+ }
+ return
+}
+
+func renderImageAltText(children []Inline) (result string) {
+ for _, inline := range children {
+ result += renderImageChildAltText(inline)
+ }
+ return
+}
+
+func renderImageChildAltText(inline Inline) (result string) {
+ switch v := inline.(type) {
+ case *Text:
+ return v.Text
+ case *InlineImage:
+ for _, inline := range v.Children {
+ result += renderImageChildAltText(inline)
+ }
+ case *InlineLink:
+ for _, inline := range v.Children {
+ result += renderImageChildAltText(inline)
+ }
+ }
+ return
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/html_entities.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/html_entities.go
new file mode 100644
index 00000000..8fe24811
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/html_entities.go
@@ -0,0 +1,2132 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+var htmlEntities = map[string]string{
+ "AElig": "\u00C6",
+ "AMP": "\u0026",
+ "Aacute": "\u00C1",
+ "Abreve": "\u0102",
+ "Acirc": "\u00C2",
+ "Acy": "\u0410",
+ "Afr": "\U0001D504",
+ "Agrave": "\u00C0",
+ "Alpha": "\u0391",
+ "Amacr": "\u0100",
+ "And": "\u2A53",
+ "Aogon": "\u0104",
+ "Aopf": "\U0001D538",
+ "ApplyFunction": "\u2061",
+ "Aring": "\u00C5",
+ "Ascr": "\U0001D49C",
+ "Assign": "\u2254",
+ "Atilde": "\u00C3",
+ "Auml": "\u00C4",
+ "Backslash": "\u2216",
+ "Barv": "\u2AE7",
+ "Barwed": "\u2306",
+ "Bcy": "\u0411",
+ "Because": "\u2235",
+ "Bernoullis": "\u212C",
+ "Beta": "\u0392",
+ "Bfr": "\U0001D505",
+ "Bopf": "\U0001D539",
+ "Breve": "\u02D8",
+ "Bscr": "\u212C",
+ "Bumpeq": "\u224E",
+ "CHcy": "\u0427",
+ "COPY": "\u00A9",
+ "Cacute": "\u0106",
+ "Cap": "\u22D2",
+ "CapitalDifferentialD": "\u2145",
+ "Cayleys": "\u212D",
+ "Ccaron": "\u010C",
+ "Ccedil": "\u00C7",
+ "Ccirc": "\u0108",
+ "Cconint": "\u2230",
+ "Cdot": "\u010A",
+ "Cedilla": "\u00B8",
+ "CenterDot": "\u00B7",
+ "Cfr": "\u212D",
+ "Chi": "\u03A7",
+ "CircleDot": "\u2299",
+ "CircleMinus": "\u2296",
+ "CirclePlus": "\u2295",
+ "CircleTimes": "\u2297",
+ "ClockwiseContourIntegral": "\u2232",
+ "CloseCurlyDoubleQuote": "\u201D",
+ "CloseCurlyQuote": "\u2019",
+ "Colon": "\u2237",
+ "Colone": "\u2A74",
+ "Congruent": "\u2261",
+ "Conint": "\u222F",
+ "ContourIntegral": "\u222E",
+ "Copf": "\u2102",
+ "Coproduct": "\u2210",
+ "CounterClockwiseContourIntegral": "\u2233",
+ "Cross": "\u2A2F",
+ "Cscr": "\U0001D49E",
+ "Cup": "\u22D3",
+ "CupCap": "\u224D",
+ "DD": "\u2145",
+ "DDotrahd": "\u2911",
+ "DJcy": "\u0402",
+ "DScy": "\u0405",
+ "DZcy": "\u040F",
+ "Dagger": "\u2021",
+ "Darr": "\u21A1",
+ "Dashv": "\u2AE4",
+ "Dcaron": "\u010E",
+ "Dcy": "\u0414",
+ "Del": "\u2207",
+ "Delta": "\u0394",
+ "Dfr": "\U0001D507",
+ "DiacriticalAcute": "\u00B4",
+ "DiacriticalDot": "\u02D9",
+ "DiacriticalDoubleAcute": "\u02DD",
+ "DiacriticalGrave": "\u0060",
+ "DiacriticalTilde": "\u02DC",
+ "Diamond": "\u22C4",
+ "DifferentialD": "\u2146",
+ "Dopf": "\U0001D53B",
+ "Dot": "\u00A8",
+ "DotDot": "\u20DC",
+ "DotEqual": "\u2250",
+ "DoubleContourIntegral": "\u222F",
+ "DoubleDot": "\u00A8",
+ "DoubleDownArrow": "\u21D3",
+ "DoubleLeftArrow": "\u21D0",
+ "DoubleLeftRightArrow": "\u21D4",
+ "DoubleLeftTee": "\u2AE4",
+ "DoubleLongLeftArrow": "\u27F8",
+ "DoubleLongLeftRightArrow": "\u27FA",
+ "DoubleLongRightArrow": "\u27F9",
+ "DoubleRightArrow": "\u21D2",
+ "DoubleRightTee": "\u22A8",
+ "DoubleUpArrow": "\u21D1",
+ "DoubleUpDownArrow": "\u21D5",
+ "DoubleVerticalBar": "\u2225",
+ "DownArrow": "\u2193",
+ "DownArrowBar": "\u2913",
+ "DownArrowUpArrow": "\u21F5",
+ "DownBreve": "\u0311",
+ "DownLeftRightVector": "\u2950",
+ "DownLeftTeeVector": "\u295E",
+ "DownLeftVector": "\u21BD",
+ "DownLeftVectorBar": "\u2956",
+ "DownRightTeeVector": "\u295F",
+ "DownRightVector": "\u21C1",
+ "DownRightVectorBar": "\u2957",
+ "DownTee": "\u22A4",
+ "DownTeeArrow": "\u21A7",
+ "Downarrow": "\u21D3",
+ "Dscr": "\U0001D49F",
+ "Dstrok": "\u0110",
+ "ENG": "\u014A",
+ "ETH": "\u00D0",
+ "Eacute": "\u00C9",
+ "Ecaron": "\u011A",
+ "Ecirc": "\u00CA",
+ "Ecy": "\u042D",
+ "Edot": "\u0116",
+ "Efr": "\U0001D508",
+ "Egrave": "\u00C8",
+ "Element": "\u2208",
+ "Emacr": "\u0112",
+ "EmptySmallSquare": "\u25FB",
+ "EmptyVerySmallSquare": "\u25AB",
+ "Eogon": "\u0118",
+ "Eopf": "\U0001D53C",
+ "Epsilon": "\u0395",
+ "Equal": "\u2A75",
+ "EqualTilde": "\u2242",
+ "Equilibrium": "\u21CC",
+ "Escr": "\u2130",
+ "Esim": "\u2A73",
+ "Eta": "\u0397",
+ "Euml": "\u00CB",
+ "Exists": "\u2203",
+ "ExponentialE": "\u2147",
+ "Fcy": "\u0424",
+ "Ffr": "\U0001D509",
+ "FilledSmallSquare": "\u25FC",
+ "FilledVerySmallSquare": "\u25AA",
+ "Fopf": "\U0001D53D",
+ "ForAll": "\u2200",
+ "Fouriertrf": "\u2131",
+ "Fscr": "\u2131",
+ "GJcy": "\u0403",
+ "GT": "\u003E",
+ "Gamma": "\u0393",
+ "Gammad": "\u03DC",
+ "Gbreve": "\u011E",
+ "Gcedil": "\u0122",
+ "Gcirc": "\u011C",
+ "Gcy": "\u0413",
+ "Gdot": "\u0120",
+ "Gfr": "\U0001D50A",
+ "Gg": "\u22D9",
+ "Gopf": "\U0001D53E",
+ "GreaterEqual": "\u2265",
+ "GreaterEqualLess": "\u22DB",
+ "GreaterFullEqual": "\u2267",
+ "GreaterGreater": "\u2AA2",
+ "GreaterLess": "\u2277",
+ "GreaterSlantEqual": "\u2A7E",
+ "GreaterTilde": "\u2273",
+ "Gscr": "\U0001D4A2",
+ "Gt": "\u226B",
+ "HARDcy": "\u042A",
+ "Hacek": "\u02C7",
+ "Hat": "\u005E",
+ "Hcirc": "\u0124",
+ "Hfr": "\u210C",
+ "HilbertSpace": "\u210B",
+ "Hopf": "\u210D",
+ "HorizontalLine": "\u2500",
+ "Hscr": "\u210B",
+ "Hstrok": "\u0126",
+ "HumpDownHump": "\u224E",
+ "HumpEqual": "\u224F",
+ "IEcy": "\u0415",
+ "IJlig": "\u0132",
+ "IOcy": "\u0401",
+ "Iacute": "\u00CD",
+ "Icirc": "\u00CE",
+ "Icy": "\u0418",
+ "Idot": "\u0130",
+ "Ifr": "\u2111",
+ "Igrave": "\u00CC",
+ "Im": "\u2111",
+ "Imacr": "\u012A",
+ "ImaginaryI": "\u2148",
+ "Implies": "\u21D2",
+ "Int": "\u222C",
+ "Integral": "\u222B",
+ "Intersection": "\u22C2",
+ "InvisibleComma": "\u2063",
+ "InvisibleTimes": "\u2062",
+ "Iogon": "\u012E",
+ "Iopf": "\U0001D540",
+ "Iota": "\u0399",
+ "Iscr": "\u2110",
+ "Itilde": "\u0128",
+ "Iukcy": "\u0406",
+ "Iuml": "\u00CF",
+ "Jcirc": "\u0134",
+ "Jcy": "\u0419",
+ "Jfr": "\U0001D50D",
+ "Jopf": "\U0001D541",
+ "Jscr": "\U0001D4A5",
+ "Jsercy": "\u0408",
+ "Jukcy": "\u0404",
+ "KHcy": "\u0425",
+ "KJcy": "\u040C",
+ "Kappa": "\u039A",
+ "Kcedil": "\u0136",
+ "Kcy": "\u041A",
+ "Kfr": "\U0001D50E",
+ "Kopf": "\U0001D542",
+ "Kscr": "\U0001D4A6",
+ "LJcy": "\u0409",
+ "LT": "\u003C",
+ "Lacute": "\u0139",
+ "Lambda": "\u039B",
+ "Lang": "\u27EA",
+ "Laplacetrf": "\u2112",
+ "Larr": "\u219E",
+ "Lcaron": "\u013D",
+ "Lcedil": "\u013B",
+ "Lcy": "\u041B",
+ "LeftAngleBracket": "\u27E8",
+ "LeftArrow": "\u2190",
+ "LeftArrowBar": "\u21E4",
+ "LeftArrowRightArrow": "\u21C6",
+ "LeftCeiling": "\u2308",
+ "LeftDoubleBracket": "\u27E6",
+ "LeftDownTeeVector": "\u2961",
+ "LeftDownVector": "\u21C3",
+ "LeftDownVectorBar": "\u2959",
+ "LeftFloor": "\u230A",
+ "LeftRightArrow": "\u2194",
+ "LeftRightVector": "\u294E",
+ "LeftTee": "\u22A3",
+ "LeftTeeArrow": "\u21A4",
+ "LeftTeeVector": "\u295A",
+ "LeftTriangle": "\u22B2",
+ "LeftTriangleBar": "\u29CF",
+ "LeftTriangleEqual": "\u22B4",
+ "LeftUpDownVector": "\u2951",
+ "LeftUpTeeVector": "\u2960",
+ "LeftUpVector": "\u21BF",
+ "LeftUpVectorBar": "\u2958",
+ "LeftVector": "\u21BC",
+ "LeftVectorBar": "\u2952",
+ "Leftarrow": "\u21D0",
+ "Leftrightarrow": "\u21D4",
+ "LessEqualGreater": "\u22DA",
+ "LessFullEqual": "\u2266",
+ "LessGreater": "\u2276",
+ "LessLess": "\u2AA1",
+ "LessSlantEqual": "\u2A7D",
+ "LessTilde": "\u2272",
+ "Lfr": "\U0001D50F",
+ "Ll": "\u22D8",
+ "Lleftarrow": "\u21DA",
+ "Lmidot": "\u013F",
+ "LongLeftArrow": "\u27F5",
+ "LongLeftRightArrow": "\u27F7",
+ "LongRightArrow": "\u27F6",
+ "Longleftarrow": "\u27F8",
+ "Longleftrightarrow": "\u27FA",
+ "Longrightarrow": "\u27F9",
+ "Lopf": "\U0001D543",
+ "LowerLeftArrow": "\u2199",
+ "LowerRightArrow": "\u2198",
+ "Lscr": "\u2112",
+ "Lsh": "\u21B0",
+ "Lstrok": "\u0141",
+ "Lt": "\u226A",
+ "Map": "\u2905",
+ "Mcy": "\u041C",
+ "MediumSpace": "\u205F",
+ "Mellintrf": "\u2133",
+ "Mfr": "\U0001D510",
+ "MinusPlus": "\u2213",
+ "Mopf": "\U0001D544",
+ "Mscr": "\u2133",
+ "Mu": "\u039C",
+ "NJcy": "\u040A",
+ "Nacute": "\u0143",
+ "Ncaron": "\u0147",
+ "Ncedil": "\u0145",
+ "Ncy": "\u041D",
+ "NegativeMediumSpace": "\u200B",
+ "NegativeThickSpace": "\u200B",
+ "NegativeThinSpace": "\u200B",
+ "NegativeVeryThinSpace": "\u200B",
+ "NestedGreaterGreater": "\u226B",
+ "NestedLessLess": "\u226A",
+ "NewLine": "\u000A",
+ "Nfr": "\U0001D511",
+ "NoBreak": "\u2060",
+ "NonBreakingSpace": "\u00A0",
+ "Nopf": "\u2115",
+ "Not": "\u2AEC",
+ "NotCongruent": "\u2262",
+ "NotCupCap": "\u226D",
+ "NotDoubleVerticalBar": "\u2226",
+ "NotElement": "\u2209",
+ "NotEqual": "\u2260",
+ "NotEqualTilde": "\u2242\u0338",
+ "NotExists": "\u2204",
+ "NotGreater": "\u226F",
+ "NotGreaterEqual": "\u2271",
+ "NotGreaterFullEqual": "\u2267\u0338",
+ "NotGreaterGreater": "\u226B\u0338",
+ "NotGreaterLess": "\u2279",
+ "NotGreaterSlantEqual": "\u2A7E\u0338",
+ "NotGreaterTilde": "\u2275",
+ "NotHumpDownHump": "\u224E\u0338",
+ "NotHumpEqual": "\u224F\u0338",
+ "NotLeftTriangle": "\u22EA",
+ "NotLeftTriangleBar": "\u29CF\u0338",
+ "NotLeftTriangleEqual": "\u22EC",
+ "NotLess": "\u226E",
+ "NotLessEqual": "\u2270",
+ "NotLessGreater": "\u2278",
+ "NotLessLess": "\u226A\u0338",
+ "NotLessSlantEqual": "\u2A7D\u0338",
+ "NotLessTilde": "\u2274",
+ "NotNestedGreaterGreater": "\u2AA2\u0338",
+ "NotNestedLessLess": "\u2AA1\u0338",
+ "NotPrecedes": "\u2280",
+ "NotPrecedesEqual": "\u2AAF\u0338",
+ "NotPrecedesSlantEqual": "\u22E0",
+ "NotReverseElement": "\u220C",
+ "NotRightTriangle": "\u22EB",
+ "NotRightTriangleBar": "\u29D0\u0338",
+ "NotRightTriangleEqual": "\u22ED",
+ "NotSquareSubset": "\u228F\u0338",
+ "NotSquareSubsetEqual": "\u22E2",
+ "NotSquareSuperset": "\u2290\u0338",
+ "NotSquareSupersetEqual": "\u22E3",
+ "NotSubset": "\u2282\u20D2",
+ "NotSubsetEqual": "\u2288",
+ "NotSucceeds": "\u2281",
+ "NotSucceedsEqual": "\u2AB0\u0338",
+ "NotSucceedsSlantEqual": "\u22E1",
+ "NotSucceedsTilde": "\u227F\u0338",
+ "NotSuperset": "\u2283\u20D2",
+ "NotSupersetEqual": "\u2289",
+ "NotTilde": "\u2241",
+ "NotTildeEqual": "\u2244",
+ "NotTildeFullEqual": "\u2247",
+ "NotTildeTilde": "\u2249",
+ "NotVerticalBar": "\u2224",
+ "Nscr": "\U0001D4A9",
+ "Ntilde": "\u00D1",
+ "Nu": "\u039D",
+ "OElig": "\u0152",
+ "Oacute": "\u00D3",
+ "Ocirc": "\u00D4",
+ "Ocy": "\u041E",
+ "Odblac": "\u0150",
+ "Ofr": "\U0001D512",
+ "Ograve": "\u00D2",
+ "Omacr": "\u014C",
+ "Omega": "\u03A9",
+ "Omicron": "\u039F",
+ "Oopf": "\U0001D546",
+ "OpenCurlyDoubleQuote": "\u201C",
+ "OpenCurlyQuote": "\u2018",
+ "Or": "\u2A54",
+ "Oscr": "\U0001D4AA",
+ "Oslash": "\u00D8",
+ "Otilde": "\u00D5",
+ "Otimes": "\u2A37",
+ "Ouml": "\u00D6",
+ "OverBar": "\u203E",
+ "OverBrace": "\u23DE",
+ "OverBracket": "\u23B4",
+ "OverParenthesis": "\u23DC",
+ "PartialD": "\u2202",
+ "Pcy": "\u041F",
+ "Pfr": "\U0001D513",
+ "Phi": "\u03A6",
+ "Pi": "\u03A0",
+ "PlusMinus": "\u00B1",
+ "Poincareplane": "\u210C",
+ "Popf": "\u2119",
+ "Pr": "\u2ABB",
+ "Precedes": "\u227A",
+ "PrecedesEqual": "\u2AAF",
+ "PrecedesSlantEqual": "\u227C",
+ "PrecedesTilde": "\u227E",
+ "Prime": "\u2033",
+ "Product": "\u220F",
+ "Proportion": "\u2237",
+ "Proportional": "\u221D",
+ "Pscr": "\U0001D4AB",
+ "Psi": "\u03A8",
+ "QUOT": "\u0022",
+ "Qfr": "\U0001D514",
+ "Qopf": "\u211A",
+ "Qscr": "\U0001D4AC",
+ "RBarr": "\u2910",
+ "REG": "\u00AE",
+ "Racute": "\u0154",
+ "Rang": "\u27EB",
+ "Rarr": "\u21A0",
+ "Rarrtl": "\u2916",
+ "Rcaron": "\u0158",
+ "Rcedil": "\u0156",
+ "Rcy": "\u0420",
+ "Re": "\u211C",
+ "ReverseElement": "\u220B",
+ "ReverseEquilibrium": "\u21CB",
+ "ReverseUpEquilibrium": "\u296F",
+ "Rfr": "\u211C",
+ "Rho": "\u03A1",
+ "RightAngleBracket": "\u27E9",
+ "RightArrow": "\u2192",
+ "RightArrowBar": "\u21E5",
+ "RightArrowLeftArrow": "\u21C4",
+ "RightCeiling": "\u2309",
+ "RightDoubleBracket": "\u27E7",
+ "RightDownTeeVector": "\u295D",
+ "RightDownVector": "\u21C2",
+ "RightDownVectorBar": "\u2955",
+ "RightFloor": "\u230B",
+ "RightTee": "\u22A2",
+ "RightTeeArrow": "\u21A6",
+ "RightTeeVector": "\u295B",
+ "RightTriangle": "\u22B3",
+ "RightTriangleBar": "\u29D0",
+ "RightTriangleEqual": "\u22B5",
+ "RightUpDownVector": "\u294F",
+ "RightUpTeeVector": "\u295C",
+ "RightUpVector": "\u21BE",
+ "RightUpVectorBar": "\u2954",
+ "RightVector": "\u21C0",
+ "RightVectorBar": "\u2953",
+ "Rightarrow": "\u21D2",
+ "Ropf": "\u211D",
+ "RoundImplies": "\u2970",
+ "Rrightarrow": "\u21DB",
+ "Rscr": "\u211B",
+ "Rsh": "\u21B1",
+ "RuleDelayed": "\u29F4",
+ "SHCHcy": "\u0429",
+ "SHcy": "\u0428",
+ "SOFTcy": "\u042C",
+ "Sacute": "\u015A",
+ "Sc": "\u2ABC",
+ "Scaron": "\u0160",
+ "Scedil": "\u015E",
+ "Scirc": "\u015C",
+ "Scy": "\u0421",
+ "Sfr": "\U0001D516",
+ "ShortDownArrow": "\u2193",
+ "ShortLeftArrow": "\u2190",
+ "ShortRightArrow": "\u2192",
+ "ShortUpArrow": "\u2191",
+ "Sigma": "\u03A3",
+ "SmallCircle": "\u2218",
+ "Sopf": "\U0001D54A",
+ "Sqrt": "\u221A",
+ "Square": "\u25A1",
+ "SquareIntersection": "\u2293",
+ "SquareSubset": "\u228F",
+ "SquareSubsetEqual": "\u2291",
+ "SquareSuperset": "\u2290",
+ "SquareSupersetEqual": "\u2292",
+ "SquareUnion": "\u2294",
+ "Sscr": "\U0001D4AE",
+ "Star": "\u22C6",
+ "Sub": "\u22D0",
+ "Subset": "\u22D0",
+ "SubsetEqual": "\u2286",
+ "Succeeds": "\u227B",
+ "SucceedsEqual": "\u2AB0",
+ "SucceedsSlantEqual": "\u227D",
+ "SucceedsTilde": "\u227F",
+ "SuchThat": "\u220B",
+ "Sum": "\u2211",
+ "Sup": "\u22D1",
+ "Superset": "\u2283",
+ "SupersetEqual": "\u2287",
+ "Supset": "\u22D1",
+ "THORN": "\u00DE",
+ "TRADE": "\u2122",
+ "TSHcy": "\u040B",
+ "TScy": "\u0426",
+ "Tab": "\u0009",
+ "Tau": "\u03A4",
+ "Tcaron": "\u0164",
+ "Tcedil": "\u0162",
+ "Tcy": "\u0422",
+ "Tfr": "\U0001D517",
+ "Therefore": "\u2234",
+ "Theta": "\u0398",
+ "ThickSpace": "\u205F\u200A",
+ "ThinSpace": "\u2009",
+ "Tilde": "\u223C",
+ "TildeEqual": "\u2243",
+ "TildeFullEqual": "\u2245",
+ "TildeTilde": "\u2248",
+ "Topf": "\U0001D54B",
+ "TripleDot": "\u20DB",
+ "Tscr": "\U0001D4AF",
+ "Tstrok": "\u0166",
+ "Uacute": "\u00DA",
+ "Uarr": "\u219F",
+ "Uarrocir": "\u2949",
+ "Ubrcy": "\u040E",
+ "Ubreve": "\u016C",
+ "Ucirc": "\u00DB",
+ "Ucy": "\u0423",
+ "Udblac": "\u0170",
+ "Ufr": "\U0001D518",
+ "Ugrave": "\u00D9",
+ "Umacr": "\u016A",
+ "UnderBar": "\u005F",
+ "UnderBrace": "\u23DF",
+ "UnderBracket": "\u23B5",
+ "UnderParenthesis": "\u23DD",
+ "Union": "\u22C3",
+ "UnionPlus": "\u228E",
+ "Uogon": "\u0172",
+ "Uopf": "\U0001D54C",
+ "UpArrow": "\u2191",
+ "UpArrowBar": "\u2912",
+ "UpArrowDownArrow": "\u21C5",
+ "UpDownArrow": "\u2195",
+ "UpEquilibrium": "\u296E",
+ "UpTee": "\u22A5",
+ "UpTeeArrow": "\u21A5",
+ "Uparrow": "\u21D1",
+ "Updownarrow": "\u21D5",
+ "UpperLeftArrow": "\u2196",
+ "UpperRightArrow": "\u2197",
+ "Upsi": "\u03D2",
+ "Upsilon": "\u03A5",
+ "Uring": "\u016E",
+ "Uscr": "\U0001D4B0",
+ "Utilde": "\u0168",
+ "Uuml": "\u00DC",
+ "VDash": "\u22AB",
+ "Vbar": "\u2AEB",
+ "Vcy": "\u0412",
+ "Vdash": "\u22A9",
+ "Vdashl": "\u2AE6",
+ "Vee": "\u22C1",
+ "Verbar": "\u2016",
+ "Vert": "\u2016",
+ "VerticalBar": "\u2223",
+ "VerticalLine": "\u007C",
+ "VerticalSeparator": "\u2758",
+ "VerticalTilde": "\u2240",
+ "VeryThinSpace": "\u200A",
+ "Vfr": "\U0001D519",
+ "Vopf": "\U0001D54D",
+ "Vscr": "\U0001D4B1",
+ "Vvdash": "\u22AA",
+ "Wcirc": "\u0174",
+ "Wedge": "\u22C0",
+ "Wfr": "\U0001D51A",
+ "Wopf": "\U0001D54E",
+ "Wscr": "\U0001D4B2",
+ "Xfr": "\U0001D51B",
+ "Xi": "\u039E",
+ "Xopf": "\U0001D54F",
+ "Xscr": "\U0001D4B3",
+ "YAcy": "\u042F",
+ "YIcy": "\u0407",
+ "YUcy": "\u042E",
+ "Yacute": "\u00DD",
+ "Ycirc": "\u0176",
+ "Ycy": "\u042B",
+ "Yfr": "\U0001D51C",
+ "Yopf": "\U0001D550",
+ "Yscr": "\U0001D4B4",
+ "Yuml": "\u0178",
+ "ZHcy": "\u0416",
+ "Zacute": "\u0179",
+ "Zcaron": "\u017D",
+ "Zcy": "\u0417",
+ "Zdot": "\u017B",
+ "ZeroWidthSpace": "\u200B",
+ "Zeta": "\u0396",
+ "Zfr": "\u2128",
+ "Zopf": "\u2124",
+ "Zscr": "\U0001D4B5",
+ "aacute": "\u00E1",
+ "abreve": "\u0103",
+ "ac": "\u223E",
+ "acE": "\u223E\u0333",
+ "acd": "\u223F",
+ "acirc": "\u00E2",
+ "acute": "\u00B4",
+ "acy": "\u0430",
+ "aelig": "\u00E6",
+ "af": "\u2061",
+ "afr": "\U0001D51E",
+ "agrave": "\u00E0",
+ "alefsym": "\u2135",
+ "aleph": "\u2135",
+ "alpha": "\u03B1",
+ "amacr": "\u0101",
+ "amalg": "\u2A3F",
+ "amp": "\u0026",
+ "and": "\u2227",
+ "andand": "\u2A55",
+ "andd": "\u2A5C",
+ "andslope": "\u2A58",
+ "andv": "\u2A5A",
+ "ang": "\u2220",
+ "ange": "\u29A4",
+ "angle": "\u2220",
+ "angmsd": "\u2221",
+ "angmsdaa": "\u29A8",
+ "angmsdab": "\u29A9",
+ "angmsdac": "\u29AA",
+ "angmsdad": "\u29AB",
+ "angmsdae": "\u29AC",
+ "angmsdaf": "\u29AD",
+ "angmsdag": "\u29AE",
+ "angmsdah": "\u29AF",
+ "angrt": "\u221F",
+ "angrtvb": "\u22BE",
+ "angrtvbd": "\u299D",
+ "angsph": "\u2222",
+ "angst": "\u00C5",
+ "angzarr": "\u237C",
+ "aogon": "\u0105",
+ "aopf": "\U0001D552",
+ "ap": "\u2248",
+ "apE": "\u2A70",
+ "apacir": "\u2A6F",
+ "ape": "\u224A",
+ "apid": "\u224B",
+ "apos": "\u0027",
+ "approx": "\u2248",
+ "approxeq": "\u224A",
+ "aring": "\u00E5",
+ "ascr": "\U0001D4B6",
+ "ast": "\u002A",
+ "asymp": "\u2248",
+ "asympeq": "\u224D",
+ "atilde": "\u00E3",
+ "auml": "\u00E4",
+ "awconint": "\u2233",
+ "awint": "\u2A11",
+ "bNot": "\u2AED",
+ "backcong": "\u224C",
+ "backepsilon": "\u03F6",
+ "backprime": "\u2035",
+ "backsim": "\u223D",
+ "backsimeq": "\u22CD",
+ "barvee": "\u22BD",
+ "barwed": "\u2305",
+ "barwedge": "\u2305",
+ "bbrk": "\u23B5",
+ "bbrktbrk": "\u23B6",
+ "bcong": "\u224C",
+ "bcy": "\u0431",
+ "bdquo": "\u201E",
+ "becaus": "\u2235",
+ "because": "\u2235",
+ "bemptyv": "\u29B0",
+ "bepsi": "\u03F6",
+ "bernou": "\u212C",
+ "beta": "\u03B2",
+ "beth": "\u2136",
+ "between": "\u226C",
+ "bfr": "\U0001D51F",
+ "bigcap": "\u22C2",
+ "bigcirc": "\u25EF",
+ "bigcup": "\u22C3",
+ "bigodot": "\u2A00",
+ "bigoplus": "\u2A01",
+ "bigotimes": "\u2A02",
+ "bigsqcup": "\u2A06",
+ "bigstar": "\u2605",
+ "bigtriangledown": "\u25BD",
+ "bigtriangleup": "\u25B3",
+ "biguplus": "\u2A04",
+ "bigvee": "\u22C1",
+ "bigwedge": "\u22C0",
+ "bkarow": "\u290D",
+ "blacklozenge": "\u29EB",
+ "blacksquare": "\u25AA",
+ "blacktriangle": "\u25B4",
+ "blacktriangledown": "\u25BE",
+ "blacktriangleleft": "\u25C2",
+ "blacktriangleright": "\u25B8",
+ "blank": "\u2423",
+ "blk12": "\u2592",
+ "blk14": "\u2591",
+ "blk34": "\u2593",
+ "block": "\u2588",
+ "bne": "\u003D\u20E5",
+ "bnequiv": "\u2261\u20E5",
+ "bnot": "\u2310",
+ "bopf": "\U0001D553",
+ "bot": "\u22A5",
+ "bottom": "\u22A5",
+ "bowtie": "\u22C8",
+ "boxDL": "\u2557",
+ "boxDR": "\u2554",
+ "boxDl": "\u2556",
+ "boxDr": "\u2553",
+ "boxH": "\u2550",
+ "boxHD": "\u2566",
+ "boxHU": "\u2569",
+ "boxHd": "\u2564",
+ "boxHu": "\u2567",
+ "boxUL": "\u255D",
+ "boxUR": "\u255A",
+ "boxUl": "\u255C",
+ "boxUr": "\u2559",
+ "boxV": "\u2551",
+ "boxVH": "\u256C",
+ "boxVL": "\u2563",
+ "boxVR": "\u2560",
+ "boxVh": "\u256B",
+ "boxVl": "\u2562",
+ "boxVr": "\u255F",
+ "boxbox": "\u29C9",
+ "boxdL": "\u2555",
+ "boxdR": "\u2552",
+ "boxdl": "\u2510",
+ "boxdr": "\u250C",
+ "boxh": "\u2500",
+ "boxhD": "\u2565",
+ "boxhU": "\u2568",
+ "boxhd": "\u252C",
+ "boxhu": "\u2534",
+ "boxminus": "\u229F",
+ "boxplus": "\u229E",
+ "boxtimes": "\u22A0",
+ "boxuL": "\u255B",
+ "boxuR": "\u2558",
+ "boxul": "\u2518",
+ "boxur": "\u2514",
+ "boxv": "\u2502",
+ "boxvH": "\u256A",
+ "boxvL": "\u2561",
+ "boxvR": "\u255E",
+ "boxvh": "\u253C",
+ "boxvl": "\u2524",
+ "boxvr": "\u251C",
+ "bprime": "\u2035",
+ "breve": "\u02D8",
+ "brvbar": "\u00A6",
+ "bscr": "\U0001D4B7",
+ "bsemi": "\u204F",
+ "bsim": "\u223D",
+ "bsime": "\u22CD",
+ "bsol": "\u005C",
+ "bsolb": "\u29C5",
+ "bsolhsub": "\u27C8",
+ "bull": "\u2022",
+ "bullet": "\u2022",
+ "bump": "\u224E",
+ "bumpE": "\u2AAE",
+ "bumpe": "\u224F",
+ "bumpeq": "\u224F",
+ "cacute": "\u0107",
+ "cap": "\u2229",
+ "capand": "\u2A44",
+ "capbrcup": "\u2A49",
+ "capcap": "\u2A4B",
+ "capcup": "\u2A47",
+ "capdot": "\u2A40",
+ "caps": "\u2229\uFE00",
+ "caret": "\u2041",
+ "caron": "\u02C7",
+ "ccaps": "\u2A4D",
+ "ccaron": "\u010D",
+ "ccedil": "\u00E7",
+ "ccirc": "\u0109",
+ "ccups": "\u2A4C",
+ "ccupssm": "\u2A50",
+ "cdot": "\u010B",
+ "cedil": "\u00B8",
+ "cemptyv": "\u29B2",
+ "cent": "\u00A2",
+ "centerdot": "\u00B7",
+ "cfr": "\U0001D520",
+ "chcy": "\u0447",
+ "check": "\u2713",
+ "checkmark": "\u2713",
+ "chi": "\u03C7",
+ "cir": "\u25CB",
+ "cirE": "\u29C3",
+ "circ": "\u02C6",
+ "circeq": "\u2257",
+ "circlearrowleft": "\u21BA",
+ "circlearrowright": "\u21BB",
+ "circledR": "\u00AE",
+ "circledS": "\u24C8",
+ "circledast": "\u229B",
+ "circledcirc": "\u229A",
+ "circleddash": "\u229D",
+ "cire": "\u2257",
+ "cirfnint": "\u2A10",
+ "cirmid": "\u2AEF",
+ "cirscir": "\u29C2",
+ "clubs": "\u2663",
+ "clubsuit": "\u2663",
+ "colon": "\u003A",
+ "colone": "\u2254",
+ "coloneq": "\u2254",
+ "comma": "\u002C",
+ "commat": "\u0040",
+ "comp": "\u2201",
+ "compfn": "\u2218",
+ "complement": "\u2201",
+ "complexes": "\u2102",
+ "cong": "\u2245",
+ "congdot": "\u2A6D",
+ "conint": "\u222E",
+ "copf": "\U0001D554",
+ "coprod": "\u2210",
+ "copy": "\u00A9",
+ "copysr": "\u2117",
+ "crarr": "\u21B5",
+ "cross": "\u2717",
+ "cscr": "\U0001D4B8",
+ "csub": "\u2ACF",
+ "csube": "\u2AD1",
+ "csup": "\u2AD0",
+ "csupe": "\u2AD2",
+ "ctdot": "\u22EF",
+ "cudarrl": "\u2938",
+ "cudarrr": "\u2935",
+ "cuepr": "\u22DE",
+ "cuesc": "\u22DF",
+ "cularr": "\u21B6",
+ "cularrp": "\u293D",
+ "cup": "\u222A",
+ "cupbrcap": "\u2A48",
+ "cupcap": "\u2A46",
+ "cupcup": "\u2A4A",
+ "cupdot": "\u228D",
+ "cupor": "\u2A45",
+ "cups": "\u222A\uFE00",
+ "curarr": "\u21B7",
+ "curarrm": "\u293C",
+ "curlyeqprec": "\u22DE",
+ "curlyeqsucc": "\u22DF",
+ "curlyvee": "\u22CE",
+ "curlywedge": "\u22CF",
+ "curren": "\u00A4",
+ "curvearrowleft": "\u21B6",
+ "curvearrowright": "\u21B7",
+ "cuvee": "\u22CE",
+ "cuwed": "\u22CF",
+ "cwconint": "\u2232",
+ "cwint": "\u2231",
+ "cylcty": "\u232D",
+ "dArr": "\u21D3",
+ "dHar": "\u2965",
+ "dagger": "\u2020",
+ "daleth": "\u2138",
+ "darr": "\u2193",
+ "dash": "\u2010",
+ "dashv": "\u22A3",
+ "dbkarow": "\u290F",
+ "dblac": "\u02DD",
+ "dcaron": "\u010F",
+ "dcy": "\u0434",
+ "dd": "\u2146",
+ "ddagger": "\u2021",
+ "ddarr": "\u21CA",
+ "ddotseq": "\u2A77",
+ "deg": "\u00B0",
+ "delta": "\u03B4",
+ "demptyv": "\u29B1",
+ "dfisht": "\u297F",
+ "dfr": "\U0001D521",
+ "dharl": "\u21C3",
+ "dharr": "\u21C2",
+ "diam": "\u22C4",
+ "diamond": "\u22C4",
+ "diamondsuit": "\u2666",
+ "diams": "\u2666",
+ "die": "\u00A8",
+ "digamma": "\u03DD",
+ "disin": "\u22F2",
+ "div": "\u00F7",
+ "divide": "\u00F7",
+ "divideontimes": "\u22C7",
+ "divonx": "\u22C7",
+ "djcy": "\u0452",
+ "dlcorn": "\u231E",
+ "dlcrop": "\u230D",
+ "dollar": "\u0024",
+ "dopf": "\U0001D555",
+ "dot": "\u02D9",
+ "doteq": "\u2250",
+ "doteqdot": "\u2251",
+ "dotminus": "\u2238",
+ "dotplus": "\u2214",
+ "dotsquare": "\u22A1",
+ "doublebarwedge": "\u2306",
+ "downarrow": "\u2193",
+ "downdownarrows": "\u21CA",
+ "downharpoonleft": "\u21C3",
+ "downharpoonright": "\u21C2",
+ "drbkarow": "\u2910",
+ "drcorn": "\u231F",
+ "drcrop": "\u230C",
+ "dscr": "\U0001D4B9",
+ "dscy": "\u0455",
+ "dsol": "\u29F6",
+ "dstrok": "\u0111",
+ "dtdot": "\u22F1",
+ "dtri": "\u25BF",
+ "dtrif": "\u25BE",
+ "duarr": "\u21F5",
+ "duhar": "\u296F",
+ "dwangle": "\u29A6",
+ "dzcy": "\u045F",
+ "dzigrarr": "\u27FF",
+ "eDDot": "\u2A77",
+ "eDot": "\u2251",
+ "eacute": "\u00E9",
+ "easter": "\u2A6E",
+ "ecaron": "\u011B",
+ "ecir": "\u2256",
+ "ecirc": "\u00EA",
+ "ecolon": "\u2255",
+ "ecy": "\u044D",
+ "edot": "\u0117",
+ "ee": "\u2147",
+ "efDot": "\u2252",
+ "efr": "\U0001D522",
+ "eg": "\u2A9A",
+ "egrave": "\u00E8",
+ "egs": "\u2A96",
+ "egsdot": "\u2A98",
+ "el": "\u2A99",
+ "elinters": "\u23E7",
+ "ell": "\u2113",
+ "els": "\u2A95",
+ "elsdot": "\u2A97",
+ "emacr": "\u0113",
+ "empty": "\u2205",
+ "emptyset": "\u2205",
+ "emptyv": "\u2205",
+ "emsp": "\u2003",
+ "emsp13": "\u2004",
+ "emsp14": "\u2005",
+ "eng": "\u014B",
+ "ensp": "\u2002",
+ "eogon": "\u0119",
+ "eopf": "\U0001D556",
+ "epar": "\u22D5",
+ "eparsl": "\u29E3",
+ "eplus": "\u2A71",
+ "epsi": "\u03B5",
+ "epsilon": "\u03B5",
+ "epsiv": "\u03F5",
+ "eqcirc": "\u2256",
+ "eqcolon": "\u2255",
+ "eqsim": "\u2242",
+ "eqslantgtr": "\u2A96",
+ "eqslantless": "\u2A95",
+ "equals": "\u003D",
+ "equest": "\u225F",
+ "equiv": "\u2261",
+ "equivDD": "\u2A78",
+ "eqvparsl": "\u29E5",
+ "erDot": "\u2253",
+ "erarr": "\u2971",
+ "escr": "\u212F",
+ "esdot": "\u2250",
+ "esim": "\u2242",
+ "eta": "\u03B7",
+ "eth": "\u00F0",
+ "euml": "\u00EB",
+ "euro": "\u20AC",
+ "excl": "\u0021",
+ "exist": "\u2203",
+ "expectation": "\u2130",
+ "exponentiale": "\u2147",
+ "fallingdotseq": "\u2252",
+ "fcy": "\u0444",
+ "female": "\u2640",
+ "ffilig": "\uFB03",
+ "fflig": "\uFB00",
+ "ffllig": "\uFB04",
+ "ffr": "\U0001D523",
+ "filig": "\uFB01",
+ "fjlig": "\u0066\u006A",
+ "flat": "\u266D",
+ "fllig": "\uFB02",
+ "fltns": "\u25B1",
+ "fnof": "\u0192",
+ "fopf": "\U0001D557",
+ "forall": "\u2200",
+ "fork": "\u22D4",
+ "forkv": "\u2AD9",
+ "fpartint": "\u2A0D",
+ "frac12": "\u00BD",
+ "frac13": "\u2153",
+ "frac14": "\u00BC",
+ "frac15": "\u2155",
+ "frac16": "\u2159",
+ "frac18": "\u215B",
+ "frac23": "\u2154",
+ "frac25": "\u2156",
+ "frac34": "\u00BE",
+ "frac35": "\u2157",
+ "frac38": "\u215C",
+ "frac45": "\u2158",
+ "frac56": "\u215A",
+ "frac58": "\u215D",
+ "frac78": "\u215E",
+ "frasl": "\u2044",
+ "frown": "\u2322",
+ "fscr": "\U0001D4BB",
+ "gE": "\u2267",
+ "gEl": "\u2A8C",
+ "gacute": "\u01F5",
+ "gamma": "\u03B3",
+ "gammad": "\u03DD",
+ "gap": "\u2A86",
+ "gbreve": "\u011F",
+ "gcirc": "\u011D",
+ "gcy": "\u0433",
+ "gdot": "\u0121",
+ "ge": "\u2265",
+ "gel": "\u22DB",
+ "geq": "\u2265",
+ "geqq": "\u2267",
+ "geqslant": "\u2A7E",
+ "ges": "\u2A7E",
+ "gescc": "\u2AA9",
+ "gesdot": "\u2A80",
+ "gesdoto": "\u2A82",
+ "gesdotol": "\u2A84",
+ "gesl": "\u22DB\uFE00",
+ "gesles": "\u2A94",
+ "gfr": "\U0001D524",
+ "gg": "\u226B",
+ "ggg": "\u22D9",
+ "gimel": "\u2137",
+ "gjcy": "\u0453",
+ "gl": "\u2277",
+ "glE": "\u2A92",
+ "gla": "\u2AA5",
+ "glj": "\u2AA4",
+ "gnE": "\u2269",
+ "gnap": "\u2A8A",
+ "gnapprox": "\u2A8A",
+ "gne": "\u2A88",
+ "gneq": "\u2A88",
+ "gneqq": "\u2269",
+ "gnsim": "\u22E7",
+ "gopf": "\U0001D558",
+ "grave": "\u0060",
+ "gscr": "\u210A",
+ "gsim": "\u2273",
+ "gsime": "\u2A8E",
+ "gsiml": "\u2A90",
+ "gt": "\u003E",
+ "gtcc": "\u2AA7",
+ "gtcir": "\u2A7A",
+ "gtdot": "\u22D7",
+ "gtlPar": "\u2995",
+ "gtquest": "\u2A7C",
+ "gtrapprox": "\u2A86",
+ "gtrarr": "\u2978",
+ "gtrdot": "\u22D7",
+ "gtreqless": "\u22DB",
+ "gtreqqless": "\u2A8C",
+ "gtrless": "\u2277",
+ "gtrsim": "\u2273",
+ "gvertneqq": "\u2269\uFE00",
+ "gvnE": "\u2269\uFE00",
+ "hArr": "\u21D4",
+ "hairsp": "\u200A",
+ "half": "\u00BD",
+ "hamilt": "\u210B",
+ "hardcy": "\u044A",
+ "harr": "\u2194",
+ "harrcir": "\u2948",
+ "harrw": "\u21AD",
+ "hbar": "\u210F",
+ "hcirc": "\u0125",
+ "hearts": "\u2665",
+ "heartsuit": "\u2665",
+ "hellip": "\u2026",
+ "hercon": "\u22B9",
+ "hfr": "\U0001D525",
+ "hksearow": "\u2925",
+ "hkswarow": "\u2926",
+ "hoarr": "\u21FF",
+ "homtht": "\u223B",
+ "hookleftarrow": "\u21A9",
+ "hookrightarrow": "\u21AA",
+ "hopf": "\U0001D559",
+ "horbar": "\u2015",
+ "hscr": "\U0001D4BD",
+ "hslash": "\u210F",
+ "hstrok": "\u0127",
+ "hybull": "\u2043",
+ "hyphen": "\u2010",
+ "iacute": "\u00ED",
+ "ic": "\u2063",
+ "icirc": "\u00EE",
+ "icy": "\u0438",
+ "iecy": "\u0435",
+ "iexcl": "\u00A1",
+ "iff": "\u21D4",
+ "ifr": "\U0001D526",
+ "igrave": "\u00EC",
+ "ii": "\u2148",
+ "iiiint": "\u2A0C",
+ "iiint": "\u222D",
+ "iinfin": "\u29DC",
+ "iiota": "\u2129",
+ "ijlig": "\u0133",
+ "imacr": "\u012B",
+ "image": "\u2111",
+ "imagline": "\u2110",
+ "imagpart": "\u2111",
+ "imath": "\u0131",
+ "imof": "\u22B7",
+ "imped": "\u01B5",
+ "in": "\u2208",
+ "incare": "\u2105",
+ "infin": "\u221E",
+ "infintie": "\u29DD",
+ "inodot": "\u0131",
+ "int": "\u222B",
+ "intcal": "\u22BA",
+ "integers": "\u2124",
+ "intercal": "\u22BA",
+ "intlarhk": "\u2A17",
+ "intprod": "\u2A3C",
+ "iocy": "\u0451",
+ "iogon": "\u012F",
+ "iopf": "\U0001D55A",
+ "iota": "\u03B9",
+ "iprod": "\u2A3C",
+ "iquest": "\u00BF",
+ "iscr": "\U0001D4BE",
+ "isin": "\u2208",
+ "isinE": "\u22F9",
+ "isindot": "\u22F5",
+ "isins": "\u22F4",
+ "isinsv": "\u22F3",
+ "isinv": "\u2208",
+ "it": "\u2062",
+ "itilde": "\u0129",
+ "iukcy": "\u0456",
+ "iuml": "\u00EF",
+ "jcirc": "\u0135",
+ "jcy": "\u0439",
+ "jfr": "\U0001D527",
+ "jmath": "\u0237",
+ "jopf": "\U0001D55B",
+ "jscr": "\U0001D4BF",
+ "jsercy": "\u0458",
+ "jukcy": "\u0454",
+ "kappa": "\u03BA",
+ "kappav": "\u03F0",
+ "kcedil": "\u0137",
+ "kcy": "\u043A",
+ "kfr": "\U0001D528",
+ "kgreen": "\u0138",
+ "khcy": "\u0445",
+ "kjcy": "\u045C",
+ "kopf": "\U0001D55C",
+ "kscr": "\U0001D4C0",
+ "lAarr": "\u21DA",
+ "lArr": "\u21D0",
+ "lAtail": "\u291B",
+ "lBarr": "\u290E",
+ "lE": "\u2266",
+ "lEg": "\u2A8B",
+ "lHar": "\u2962",
+ "lacute": "\u013A",
+ "laemptyv": "\u29B4",
+ "lagran": "\u2112",
+ "lambda": "\u03BB",
+ "lang": "\u27E8",
+ "langd": "\u2991",
+ "langle": "\u27E8",
+ "lap": "\u2A85",
+ "laquo": "\u00AB",
+ "larr": "\u2190",
+ "larrb": "\u21E4",
+ "larrbfs": "\u291F",
+ "larrfs": "\u291D",
+ "larrhk": "\u21A9",
+ "larrlp": "\u21AB",
+ "larrpl": "\u2939",
+ "larrsim": "\u2973",
+ "larrtl": "\u21A2",
+ "lat": "\u2AAB",
+ "latail": "\u2919",
+ "late": "\u2AAD",
+ "lates": "\u2AAD\uFE00",
+ "lbarr": "\u290C",
+ "lbbrk": "\u2772",
+ "lbrace": "\u007B",
+ "lbrack": "\u005B",
+ "lbrke": "\u298B",
+ "lbrksld": "\u298F",
+ "lbrkslu": "\u298D",
+ "lcaron": "\u013E",
+ "lcedil": "\u013C",
+ "lceil": "\u2308",
+ "lcub": "\u007B",
+ "lcy": "\u043B",
+ "ldca": "\u2936",
+ "ldquo": "\u201C",
+ "ldquor": "\u201E",
+ "ldrdhar": "\u2967",
+ "ldrushar": "\u294B",
+ "ldsh": "\u21B2",
+ "le": "\u2264",
+ "leftarrow": "\u2190",
+ "leftarrowtail": "\u21A2",
+ "leftharpoondown": "\u21BD",
+ "leftharpoonup": "\u21BC",
+ "leftleftarrows": "\u21C7",
+ "leftrightarrow": "\u2194",
+ "leftrightarrows": "\u21C6",
+ "leftrightharpoons": "\u21CB",
+ "leftrightsquigarrow": "\u21AD",
+ "leftthreetimes": "\u22CB",
+ "leg": "\u22DA",
+ "leq": "\u2264",
+ "leqq": "\u2266",
+ "leqslant": "\u2A7D",
+ "les": "\u2A7D",
+ "lescc": "\u2AA8",
+ "lesdot": "\u2A7F",
+ "lesdoto": "\u2A81",
+ "lesdotor": "\u2A83",
+ "lesg": "\u22DA\uFE00",
+ "lesges": "\u2A93",
+ "lessapprox": "\u2A85",
+ "lessdot": "\u22D6",
+ "lesseqgtr": "\u22DA",
+ "lesseqqgtr": "\u2A8B",
+ "lessgtr": "\u2276",
+ "lesssim": "\u2272",
+ "lfisht": "\u297C",
+ "lfloor": "\u230A",
+ "lfr": "\U0001D529",
+ "lg": "\u2276",
+ "lgE": "\u2A91",
+ "lhard": "\u21BD",
+ "lharu": "\u21BC",
+ "lharul": "\u296A",
+ "lhblk": "\u2584",
+ "ljcy": "\u0459",
+ "ll": "\u226A",
+ "llarr": "\u21C7",
+ "llcorner": "\u231E",
+ "llhard": "\u296B",
+ "lltri": "\u25FA",
+ "lmidot": "\u0140",
+ "lmoust": "\u23B0",
+ "lmoustache": "\u23B0",
+ "lnE": "\u2268",
+ "lnap": "\u2A89",
+ "lnapprox": "\u2A89",
+ "lne": "\u2A87",
+ "lneq": "\u2A87",
+ "lneqq": "\u2268",
+ "lnsim": "\u22E6",
+ "loang": "\u27EC",
+ "loarr": "\u21FD",
+ "lobrk": "\u27E6",
+ "longleftarrow": "\u27F5",
+ "longleftrightarrow": "\u27F7",
+ "longmapsto": "\u27FC",
+ "longrightarrow": "\u27F6",
+ "looparrowleft": "\u21AB",
+ "looparrowright": "\u21AC",
+ "lopar": "\u2985",
+ "lopf": "\U0001D55D",
+ "loplus": "\u2A2D",
+ "lotimes": "\u2A34",
+ "lowast": "\u2217",
+ "lowbar": "\u005F",
+ "loz": "\u25CA",
+ "lozenge": "\u25CA",
+ "lozf": "\u29EB",
+ "lpar": "\u0028",
+ "lparlt": "\u2993",
+ "lrarr": "\u21C6",
+ "lrcorner": "\u231F",
+ "lrhar": "\u21CB",
+ "lrhard": "\u296D",
+ "lrm": "\u200E",
+ "lrtri": "\u22BF",
+ "lsaquo": "\u2039",
+ "lscr": "\U0001D4C1",
+ "lsh": "\u21B0",
+ "lsim": "\u2272",
+ "lsime": "\u2A8D",
+ "lsimg": "\u2A8F",
+ "lsqb": "\u005B",
+ "lsquo": "\u2018",
+ "lsquor": "\u201A",
+ "lstrok": "\u0142",
+ "lt": "\u003C",
+ "ltcc": "\u2AA6",
+ "ltcir": "\u2A79",
+ "ltdot": "\u22D6",
+ "lthree": "\u22CB",
+ "ltimes": "\u22C9",
+ "ltlarr": "\u2976",
+ "ltquest": "\u2A7B",
+ "ltrPar": "\u2996",
+ "ltri": "\u25C3",
+ "ltrie": "\u22B4",
+ "ltrif": "\u25C2",
+ "lurdshar": "\u294A",
+ "luruhar": "\u2966",
+ "lvertneqq": "\u2268\uFE00",
+ "lvnE": "\u2268\uFE00",
+ "mDDot": "\u223A",
+ "macr": "\u00AF",
+ "male": "\u2642",
+ "malt": "\u2720",
+ "maltese": "\u2720",
+ "map": "\u21A6",
+ "mapsto": "\u21A6",
+ "mapstodown": "\u21A7",
+ "mapstoleft": "\u21A4",
+ "mapstoup": "\u21A5",
+ "marker": "\u25AE",
+ "mcomma": "\u2A29",
+ "mcy": "\u043C",
+ "mdash": "\u2014",
+ "measuredangle": "\u2221",
+ "mfr": "\U0001D52A",
+ "mho": "\u2127",
+ "micro": "\u00B5",
+ "mid": "\u2223",
+ "midast": "\u002A",
+ "midcir": "\u2AF0",
+ "middot": "\u00B7",
+ "minus": "\u2212",
+ "minusb": "\u229F",
+ "minusd": "\u2238",
+ "minusdu": "\u2A2A",
+ "mlcp": "\u2ADB",
+ "mldr": "\u2026",
+ "mnplus": "\u2213",
+ "models": "\u22A7",
+ "mopf": "\U0001D55E",
+ "mp": "\u2213",
+ "mscr": "\U0001D4C2",
+ "mstpos": "\u223E",
+ "mu": "\u03BC",
+ "multimap": "\u22B8",
+ "mumap": "\u22B8",
+ "nGg": "\u22D9\u0338",
+ "nGt": "\u226B\u20D2",
+ "nGtv": "\u226B\u0338",
+ "nLeftarrow": "\u21CD",
+ "nLeftrightarrow": "\u21CE",
+ "nLl": "\u22D8\u0338",
+ "nLt": "\u226A\u20D2",
+ "nLtv": "\u226A\u0338",
+ "nRightarrow": "\u21CF",
+ "nVDash": "\u22AF",
+ "nVdash": "\u22AE",
+ "nabla": "\u2207",
+ "nacute": "\u0144",
+ "nang": "\u2220\u20D2",
+ "nap": "\u2249",
+ "napE": "\u2A70\u0338",
+ "napid": "\u224B\u0338",
+ "napos": "\u0149",
+ "napprox": "\u2249",
+ "natur": "\u266E",
+ "natural": "\u266E",
+ "naturals": "\u2115",
+ "nbsp": "\u00A0",
+ "nbump": "\u224E\u0338",
+ "nbumpe": "\u224F\u0338",
+ "ncap": "\u2A43",
+ "ncaron": "\u0148",
+ "ncedil": "\u0146",
+ "ncong": "\u2247",
+ "ncongdot": "\u2A6D\u0338",
+ "ncup": "\u2A42",
+ "ncy": "\u043D",
+ "ndash": "\u2013",
+ "ne": "\u2260",
+ "neArr": "\u21D7",
+ "nearhk": "\u2924",
+ "nearr": "\u2197",
+ "nearrow": "\u2197",
+ "nedot": "\u2250\u0338",
+ "nequiv": "\u2262",
+ "nesear": "\u2928",
+ "nesim": "\u2242\u0338",
+ "nexist": "\u2204",
+ "nexists": "\u2204",
+ "nfr": "\U0001D52B",
+ "ngE": "\u2267\u0338",
+ "nge": "\u2271",
+ "ngeq": "\u2271",
+ "ngeqq": "\u2267\u0338",
+ "ngeqslant": "\u2A7E\u0338",
+ "nges": "\u2A7E\u0338",
+ "ngsim": "\u2275",
+ "ngt": "\u226F",
+ "ngtr": "\u226F",
+ "nhArr": "\u21CE",
+ "nharr": "\u21AE",
+ "nhpar": "\u2AF2",
+ "ni": "\u220B",
+ "nis": "\u22FC",
+ "nisd": "\u22FA",
+ "niv": "\u220B",
+ "njcy": "\u045A",
+ "nlArr": "\u21CD",
+ "nlE": "\u2266\u0338",
+ "nlarr": "\u219A",
+ "nldr": "\u2025",
+ "nle": "\u2270",
+ "nleftarrow": "\u219A",
+ "nleftrightarrow": "\u21AE",
+ "nleq": "\u2270",
+ "nleqq": "\u2266\u0338",
+ "nleqslant": "\u2A7D\u0338",
+ "nles": "\u2A7D\u0338",
+ "nless": "\u226E",
+ "nlsim": "\u2274",
+ "nlt": "\u226E",
+ "nltri": "\u22EA",
+ "nltrie": "\u22EC",
+ "nmid": "\u2224",
+ "nopf": "\U0001D55F",
+ "not": "\u00AC",
+ "notin": "\u2209",
+ "notinE": "\u22F9\u0338",
+ "notindot": "\u22F5\u0338",
+ "notinva": "\u2209",
+ "notinvb": "\u22F7",
+ "notinvc": "\u22F6",
+ "notni": "\u220C",
+ "notniva": "\u220C",
+ "notnivb": "\u22FE",
+ "notnivc": "\u22FD",
+ "npar": "\u2226",
+ "nparallel": "\u2226",
+ "nparsl": "\u2AFD\u20E5",
+ "npart": "\u2202\u0338",
+ "npolint": "\u2A14",
+ "npr": "\u2280",
+ "nprcue": "\u22E0",
+ "npre": "\u2AAF\u0338",
+ "nprec": "\u2280",
+ "npreceq": "\u2AAF\u0338",
+ "nrArr": "\u21CF",
+ "nrarr": "\u219B",
+ "nrarrc": "\u2933\u0338",
+ "nrarrw": "\u219D\u0338",
+ "nrightarrow": "\u219B",
+ "nrtri": "\u22EB",
+ "nrtrie": "\u22ED",
+ "nsc": "\u2281",
+ "nsccue": "\u22E1",
+ "nsce": "\u2AB0\u0338",
+ "nscr": "\U0001D4C3",
+ "nshortmid": "\u2224",
+ "nshortparallel": "\u2226",
+ "nsim": "\u2241",
+ "nsime": "\u2244",
+ "nsimeq": "\u2244",
+ "nsmid": "\u2224",
+ "nspar": "\u2226",
+ "nsqsube": "\u22E2",
+ "nsqsupe": "\u22E3",
+ "nsub": "\u2284",
+ "nsubE": "\u2AC5\u0338",
+ "nsube": "\u2288",
+ "nsubset": "\u2282\u20D2",
+ "nsubseteq": "\u2288",
+ "nsubseteqq": "\u2AC5\u0338",
+ "nsucc": "\u2281",
+ "nsucceq": "\u2AB0\u0338",
+ "nsup": "\u2285",
+ "nsupE": "\u2AC6\u0338",
+ "nsupe": "\u2289",
+ "nsupset": "\u2283\u20D2",
+ "nsupseteq": "\u2289",
+ "nsupseteqq": "\u2AC6\u0338",
+ "ntgl": "\u2279",
+ "ntilde": "\u00F1",
+ "ntlg": "\u2278",
+ "ntriangleleft": "\u22EA",
+ "ntrianglelefteq": "\u22EC",
+ "ntriangleright": "\u22EB",
+ "ntrianglerighteq": "\u22ED",
+ "nu": "\u03BD",
+ "num": "\u0023",
+ "numero": "\u2116",
+ "numsp": "\u2007",
+ "nvDash": "\u22AD",
+ "nvHarr": "\u2904",
+ "nvap": "\u224D\u20D2",
+ "nvdash": "\u22AC",
+ "nvge": "\u2265\u20D2",
+ "nvgt": "\u003E\u20D2",
+ "nvinfin": "\u29DE",
+ "nvlArr": "\u2902",
+ "nvle": "\u2264\u20D2",
+ "nvlt": "\u003C\u20D2",
+ "nvltrie": "\u22B4\u20D2",
+ "nvrArr": "\u2903",
+ "nvrtrie": "\u22B5\u20D2",
+ "nvsim": "\u223C\u20D2",
+ "nwArr": "\u21D6",
+ "nwarhk": "\u2923",
+ "nwarr": "\u2196",
+ "nwarrow": "\u2196",
+ "nwnear": "\u2927",
+ "oS": "\u24C8",
+ "oacute": "\u00F3",
+ "oast": "\u229B",
+ "ocir": "\u229A",
+ "ocirc": "\u00F4",
+ "ocy": "\u043E",
+ "odash": "\u229D",
+ "odblac": "\u0151",
+ "odiv": "\u2A38",
+ "odot": "\u2299",
+ "odsold": "\u29BC",
+ "oelig": "\u0153",
+ "ofcir": "\u29BF",
+ "ofr": "\U0001D52C",
+ "ogon": "\u02DB",
+ "ograve": "\u00F2",
+ "ogt": "\u29C1",
+ "ohbar": "\u29B5",
+ "ohm": "\u03A9",
+ "oint": "\u222E",
+ "olarr": "\u21BA",
+ "olcir": "\u29BE",
+ "olcross": "\u29BB",
+ "oline": "\u203E",
+ "olt": "\u29C0",
+ "omacr": "\u014D",
+ "omega": "\u03C9",
+ "omicron": "\u03BF",
+ "omid": "\u29B6",
+ "ominus": "\u2296",
+ "oopf": "\U0001D560",
+ "opar": "\u29B7",
+ "operp": "\u29B9",
+ "oplus": "\u2295",
+ "or": "\u2228",
+ "orarr": "\u21BB",
+ "ord": "\u2A5D",
+ "order": "\u2134",
+ "orderof": "\u2134",
+ "ordf": "\u00AA",
+ "ordm": "\u00BA",
+ "origof": "\u22B6",
+ "oror": "\u2A56",
+ "orslope": "\u2A57",
+ "orv": "\u2A5B",
+ "oscr": "\u2134",
+ "oslash": "\u00F8",
+ "osol": "\u2298",
+ "otilde": "\u00F5",
+ "otimes": "\u2297",
+ "otimesas": "\u2A36",
+ "ouml": "\u00F6",
+ "ovbar": "\u233D",
+ "par": "\u2225",
+ "para": "\u00B6",
+ "parallel": "\u2225",
+ "parsim": "\u2AF3",
+ "parsl": "\u2AFD",
+ "part": "\u2202",
+ "pcy": "\u043F",
+ "percnt": "\u0025",
+ "period": "\u002E",
+ "permil": "\u2030",
+ "perp": "\u22A5",
+ "pertenk": "\u2031",
+ "pfr": "\U0001D52D",
+ "phi": "\u03C6",
+ "phiv": "\u03D5",
+ "phmmat": "\u2133",
+ "phone": "\u260E",
+ "pi": "\u03C0",
+ "pitchfork": "\u22D4",
+ "piv": "\u03D6",
+ "planck": "\u210F",
+ "planckh": "\u210E",
+ "plankv": "\u210F",
+ "plus": "\u002B",
+ "plusacir": "\u2A23",
+ "plusb": "\u229E",
+ "pluscir": "\u2A22",
+ "plusdo": "\u2214",
+ "plusdu": "\u2A25",
+ "pluse": "\u2A72",
+ "plusmn": "\u00B1",
+ "plussim": "\u2A26",
+ "plustwo": "\u2A27",
+ "pm": "\u00B1",
+ "pointint": "\u2A15",
+ "popf": "\U0001D561",
+ "pound": "\u00A3",
+ "pr": "\u227A",
+ "prE": "\u2AB3",
+ "prap": "\u2AB7",
+ "prcue": "\u227C",
+ "pre": "\u2AAF",
+ "prec": "\u227A",
+ "precapprox": "\u2AB7",
+ "preccurlyeq": "\u227C",
+ "preceq": "\u2AAF",
+ "precnapprox": "\u2AB9",
+ "precneqq": "\u2AB5",
+ "precnsim": "\u22E8",
+ "precsim": "\u227E",
+ "prime": "\u2032",
+ "primes": "\u2119",
+ "prnE": "\u2AB5",
+ "prnap": "\u2AB9",
+ "prnsim": "\u22E8",
+ "prod": "\u220F",
+ "profalar": "\u232E",
+ "profline": "\u2312",
+ "profsurf": "\u2313",
+ "prop": "\u221D",
+ "propto": "\u221D",
+ "prsim": "\u227E",
+ "prurel": "\u22B0",
+ "pscr": "\U0001D4C5",
+ "psi": "\u03C8",
+ "puncsp": "\u2008",
+ "qfr": "\U0001D52E",
+ "qint": "\u2A0C",
+ "qopf": "\U0001D562",
+ "qprime": "\u2057",
+ "qscr": "\U0001D4C6",
+ "quaternions": "\u210D",
+ "quatint": "\u2A16",
+ "quest": "\u003F",
+ "questeq": "\u225F",
+ "quot": "\u0022",
+ "rAarr": "\u21DB",
+ "rArr": "\u21D2",
+ "rAtail": "\u291C",
+ "rBarr": "\u290F",
+ "rHar": "\u2964",
+ "race": "\u223D\u0331",
+ "racute": "\u0155",
+ "radic": "\u221A",
+ "raemptyv": "\u29B3",
+ "rang": "\u27E9",
+ "rangd": "\u2992",
+ "range": "\u29A5",
+ "rangle": "\u27E9",
+ "raquo": "\u00BB",
+ "rarr": "\u2192",
+ "rarrap": "\u2975",
+ "rarrb": "\u21E5",
+ "rarrbfs": "\u2920",
+ "rarrc": "\u2933",
+ "rarrfs": "\u291E",
+ "rarrhk": "\u21AA",
+ "rarrlp": "\u21AC",
+ "rarrpl": "\u2945",
+ "rarrsim": "\u2974",
+ "rarrtl": "\u21A3",
+ "rarrw": "\u219D",
+ "ratail": "\u291A",
+ "ratio": "\u2236",
+ "rationals": "\u211A",
+ "rbarr": "\u290D",
+ "rbbrk": "\u2773",
+ "rbrace": "\u007D",
+ "rbrack": "\u005D",
+ "rbrke": "\u298C",
+ "rbrksld": "\u298E",
+ "rbrkslu": "\u2990",
+ "rcaron": "\u0159",
+ "rcedil": "\u0157",
+ "rceil": "\u2309",
+ "rcub": "\u007D",
+ "rcy": "\u0440",
+ "rdca": "\u2937",
+ "rdldhar": "\u2969",
+ "rdquo": "\u201D",
+ "rdquor": "\u201D",
+ "rdsh": "\u21B3",
+ "real": "\u211C",
+ "realine": "\u211B",
+ "realpart": "\u211C",
+ "reals": "\u211D",
+ "rect": "\u25AD",
+ "reg": "\u00AE",
+ "rfisht": "\u297D",
+ "rfloor": "\u230B",
+ "rfr": "\U0001D52F",
+ "rhard": "\u21C1",
+ "rharu": "\u21C0",
+ "rharul": "\u296C",
+ "rho": "\u03C1",
+ "rhov": "\u03F1",
+ "rightarrow": "\u2192",
+ "rightarrowtail": "\u21A3",
+ "rightharpoondown": "\u21C1",
+ "rightharpoonup": "\u21C0",
+ "rightleftarrows": "\u21C4",
+ "rightleftharpoons": "\u21CC",
+ "rightrightarrows": "\u21C9",
+ "rightsquigarrow": "\u219D",
+ "rightthreetimes": "\u22CC",
+ "ring": "\u02DA",
+ "risingdotseq": "\u2253",
+ "rlarr": "\u21C4",
+ "rlhar": "\u21CC",
+ "rlm": "\u200F",
+ "rmoust": "\u23B1",
+ "rmoustache": "\u23B1",
+ "rnmid": "\u2AEE",
+ "roang": "\u27ED",
+ "roarr": "\u21FE",
+ "robrk": "\u27E7",
+ "ropar": "\u2986",
+ "ropf": "\U0001D563",
+ "roplus": "\u2A2E",
+ "rotimes": "\u2A35",
+ "rpar": "\u0029",
+ "rpargt": "\u2994",
+ "rppolint": "\u2A12",
+ "rrarr": "\u21C9",
+ "rsaquo": "\u203A",
+ "rscr": "\U0001D4C7",
+ "rsh": "\u21B1",
+ "rsqb": "\u005D",
+ "rsquo": "\u2019",
+ "rsquor": "\u2019",
+ "rthree": "\u22CC",
+ "rtimes": "\u22CA",
+ "rtri": "\u25B9",
+ "rtrie": "\u22B5",
+ "rtrif": "\u25B8",
+ "rtriltri": "\u29CE",
+ "ruluhar": "\u2968",
+ "rx": "\u211E",
+ "sacute": "\u015B",
+ "sbquo": "\u201A",
+ "sc": "\u227B",
+ "scE": "\u2AB4",
+ "scap": "\u2AB8",
+ "scaron": "\u0161",
+ "sccue": "\u227D",
+ "sce": "\u2AB0",
+ "scedil": "\u015F",
+ "scirc": "\u015D",
+ "scnE": "\u2AB6",
+ "scnap": "\u2ABA",
+ "scnsim": "\u22E9",
+ "scpolint": "\u2A13",
+ "scsim": "\u227F",
+ "scy": "\u0441",
+ "sdot": "\u22C5",
+ "sdotb": "\u22A1",
+ "sdote": "\u2A66",
+ "seArr": "\u21D8",
+ "searhk": "\u2925",
+ "searr": "\u2198",
+ "searrow": "\u2198",
+ "sect": "\u00A7",
+ "semi": "\u003B",
+ "seswar": "\u2929",
+ "setminus": "\u2216",
+ "setmn": "\u2216",
+ "sext": "\u2736",
+ "sfr": "\U0001D530",
+ "sfrown": "\u2322",
+ "sharp": "\u266F",
+ "shchcy": "\u0449",
+ "shcy": "\u0448",
+ "shortmid": "\u2223",
+ "shortparallel": "\u2225",
+ "shy": "\u00AD",
+ "sigma": "\u03C3",
+ "sigmaf": "\u03C2",
+ "sigmav": "\u03C2",
+ "sim": "\u223C",
+ "simdot": "\u2A6A",
+ "sime": "\u2243",
+ "simeq": "\u2243",
+ "simg": "\u2A9E",
+ "simgE": "\u2AA0",
+ "siml": "\u2A9D",
+ "simlE": "\u2A9F",
+ "simne": "\u2246",
+ "simplus": "\u2A24",
+ "simrarr": "\u2972",
+ "slarr": "\u2190",
+ "smallsetminus": "\u2216",
+ "smashp": "\u2A33",
+ "smeparsl": "\u29E4",
+ "smid": "\u2223",
+ "smile": "\u2323",
+ "smt": "\u2AAA",
+ "smte": "\u2AAC",
+ "smtes": "\u2AAC\uFE00",
+ "softcy": "\u044C",
+ "sol": "\u002F",
+ "solb": "\u29C4",
+ "solbar": "\u233F",
+ "sopf": "\U0001D564",
+ "spades": "\u2660",
+ "spadesuit": "\u2660",
+ "spar": "\u2225",
+ "sqcap": "\u2293",
+ "sqcaps": "\u2293\uFE00",
+ "sqcup": "\u2294",
+ "sqcups": "\u2294\uFE00",
+ "sqsub": "\u228F",
+ "sqsube": "\u2291",
+ "sqsubset": "\u228F",
+ "sqsubseteq": "\u2291",
+ "sqsup": "\u2290",
+ "sqsupe": "\u2292",
+ "sqsupset": "\u2290",
+ "sqsupseteq": "\u2292",
+ "squ": "\u25A1",
+ "square": "\u25A1",
+ "squarf": "\u25AA",
+ "squf": "\u25AA",
+ "srarr": "\u2192",
+ "sscr": "\U0001D4C8",
+ "ssetmn": "\u2216",
+ "ssmile": "\u2323",
+ "sstarf": "\u22C6",
+ "star": "\u2606",
+ "starf": "\u2605",
+ "straightepsilon": "\u03F5",
+ "straightphi": "\u03D5",
+ "strns": "\u00AF",
+ "sub": "\u2282",
+ "subE": "\u2AC5",
+ "subdot": "\u2ABD",
+ "sube": "\u2286",
+ "subedot": "\u2AC3",
+ "submult": "\u2AC1",
+ "subnE": "\u2ACB",
+ "subne": "\u228A",
+ "subplus": "\u2ABF",
+ "subrarr": "\u2979",
+ "subset": "\u2282",
+ "subseteq": "\u2286",
+ "subseteqq": "\u2AC5",
+ "subsetneq": "\u228A",
+ "subsetneqq": "\u2ACB",
+ "subsim": "\u2AC7",
+ "subsub": "\u2AD5",
+ "subsup": "\u2AD3",
+ "succ": "\u227B",
+ "succapprox": "\u2AB8",
+ "succcurlyeq": "\u227D",
+ "succeq": "\u2AB0",
+ "succnapprox": "\u2ABA",
+ "succneqq": "\u2AB6",
+ "succnsim": "\u22E9",
+ "succsim": "\u227F",
+ "sum": "\u2211",
+ "sung": "\u266A",
+ "sup": "\u2283",
+ "sup1": "\u00B9",
+ "sup2": "\u00B2",
+ "sup3": "\u00B3",
+ "supE": "\u2AC6",
+ "supdot": "\u2ABE",
+ "supdsub": "\u2AD8",
+ "supe": "\u2287",
+ "supedot": "\u2AC4",
+ "suphsol": "\u27C9",
+ "suphsub": "\u2AD7",
+ "suplarr": "\u297B",
+ "supmult": "\u2AC2",
+ "supnE": "\u2ACC",
+ "supne": "\u228B",
+ "supplus": "\u2AC0",
+ "supset": "\u2283",
+ "supseteq": "\u2287",
+ "supseteqq": "\u2AC6",
+ "supsetneq": "\u228B",
+ "supsetneqq": "\u2ACC",
+ "supsim": "\u2AC8",
+ "supsub": "\u2AD4",
+ "supsup": "\u2AD6",
+ "swArr": "\u21D9",
+ "swarhk": "\u2926",
+ "swarr": "\u2199",
+ "swarrow": "\u2199",
+ "swnwar": "\u292A",
+ "szlig": "\u00DF",
+ "target": "\u2316",
+ "tau": "\u03C4",
+ "tbrk": "\u23B4",
+ "tcaron": "\u0165",
+ "tcedil": "\u0163",
+ "tcy": "\u0442",
+ "tdot": "\u20DB",
+ "telrec": "\u2315",
+ "tfr": "\U0001D531",
+ "there4": "\u2234",
+ "therefore": "\u2234",
+ "theta": "\u03B8",
+ "thetasym": "\u03D1",
+ "thetav": "\u03D1",
+ "thickapprox": "\u2248",
+ "thicksim": "\u223C",
+ "thinsp": "\u2009",
+ "thkap": "\u2248",
+ "thksim": "\u223C",
+ "thorn": "\u00FE",
+ "tilde": "\u02DC",
+ "times": "\u00D7",
+ "timesb": "\u22A0",
+ "timesbar": "\u2A31",
+ "timesd": "\u2A30",
+ "tint": "\u222D",
+ "toea": "\u2928",
+ "top": "\u22A4",
+ "topbot": "\u2336",
+ "topcir": "\u2AF1",
+ "topf": "\U0001D565",
+ "topfork": "\u2ADA",
+ "tosa": "\u2929",
+ "tprime": "\u2034",
+ "trade": "\u2122",
+ "triangle": "\u25B5",
+ "triangledown": "\u25BF",
+ "triangleleft": "\u25C3",
+ "trianglelefteq": "\u22B4",
+ "triangleq": "\u225C",
+ "triangleright": "\u25B9",
+ "trianglerighteq": "\u22B5",
+ "tridot": "\u25EC",
+ "trie": "\u225C",
+ "triminus": "\u2A3A",
+ "triplus": "\u2A39",
+ "trisb": "\u29CD",
+ "tritime": "\u2A3B",
+ "trpezium": "\u23E2",
+ "tscr": "\U0001D4C9",
+ "tscy": "\u0446",
+ "tshcy": "\u045B",
+ "tstrok": "\u0167",
+ "twixt": "\u226C",
+ "twoheadleftarrow": "\u219E",
+ "twoheadrightarrow": "\u21A0",
+ "uArr": "\u21D1",
+ "uHar": "\u2963",
+ "uacute": "\u00FA",
+ "uarr": "\u2191",
+ "ubrcy": "\u045E",
+ "ubreve": "\u016D",
+ "ucirc": "\u00FB",
+ "ucy": "\u0443",
+ "udarr": "\u21C5",
+ "udblac": "\u0171",
+ "udhar": "\u296E",
+ "ufisht": "\u297E",
+ "ufr": "\U0001D532",
+ "ugrave": "\u00F9",
+ "uharl": "\u21BF",
+ "uharr": "\u21BE",
+ "uhblk": "\u2580",
+ "ulcorn": "\u231C",
+ "ulcorner": "\u231C",
+ "ulcrop": "\u230F",
+ "ultri": "\u25F8",
+ "umacr": "\u016B",
+ "uml": "\u00A8",
+ "uogon": "\u0173",
+ "uopf": "\U0001D566",
+ "uparrow": "\u2191",
+ "updownarrow": "\u2195",
+ "upharpoonleft": "\u21BF",
+ "upharpoonright": "\u21BE",
+ "uplus": "\u228E",
+ "upsi": "\u03C5",
+ "upsih": "\u03D2",
+ "upsilon": "\u03C5",
+ "upuparrows": "\u21C8",
+ "urcorn": "\u231D",
+ "urcorner": "\u231D",
+ "urcrop": "\u230E",
+ "uring": "\u016F",
+ "urtri": "\u25F9",
+ "uscr": "\U0001D4CA",
+ "utdot": "\u22F0",
+ "utilde": "\u0169",
+ "utri": "\u25B5",
+ "utrif": "\u25B4",
+ "uuarr": "\u21C8",
+ "uuml": "\u00FC",
+ "uwangle": "\u29A7",
+ "vArr": "\u21D5",
+ "vBar": "\u2AE8",
+ "vBarv": "\u2AE9",
+ "vDash": "\u22A8",
+ "vangrt": "\u299C",
+ "varepsilon": "\u03F5",
+ "varkappa": "\u03F0",
+ "varnothing": "\u2205",
+ "varphi": "\u03D5",
+ "varpi": "\u03D6",
+ "varpropto": "\u221D",
+ "varr": "\u2195",
+ "varrho": "\u03F1",
+ "varsigma": "\u03C2",
+ "varsubsetneq": "\u228A\uFE00",
+ "varsubsetneqq": "\u2ACB\uFE00",
+ "varsupsetneq": "\u228B\uFE00",
+ "varsupsetneqq": "\u2ACC\uFE00",
+ "vartheta": "\u03D1",
+ "vartriangleleft": "\u22B2",
+ "vartriangleright": "\u22B3",
+ "vcy": "\u0432",
+ "vdash": "\u22A2",
+ "vee": "\u2228",
+ "veebar": "\u22BB",
+ "veeeq": "\u225A",
+ "vellip": "\u22EE",
+ "verbar": "\u007C",
+ "vert": "\u007C",
+ "vfr": "\U0001D533",
+ "vltri": "\u22B2",
+ "vnsub": "\u2282\u20D2",
+ "vnsup": "\u2283\u20D2",
+ "vopf": "\U0001D567",
+ "vprop": "\u221D",
+ "vrtri": "\u22B3",
+ "vscr": "\U0001D4CB",
+ "vsubnE": "\u2ACB\uFE00",
+ "vsubne": "\u228A\uFE00",
+ "vsupnE": "\u2ACC\uFE00",
+ "vsupne": "\u228B\uFE00",
+ "vzigzag": "\u299A",
+ "wcirc": "\u0175",
+ "wedbar": "\u2A5F",
+ "wedge": "\u2227",
+ "wedgeq": "\u2259",
+ "weierp": "\u2118",
+ "wfr": "\U0001D534",
+ "wopf": "\U0001D568",
+ "wp": "\u2118",
+ "wr": "\u2240",
+ "wreath": "\u2240",
+ "wscr": "\U0001D4CC",
+ "xcap": "\u22C2",
+ "xcirc": "\u25EF",
+ "xcup": "\u22C3",
+ "xdtri": "\u25BD",
+ "xfr": "\U0001D535",
+ "xhArr": "\u27FA",
+ "xharr": "\u27F7",
+ "xi": "\u03BE",
+ "xlArr": "\u27F8",
+ "xlarr": "\u27F5",
+ "xmap": "\u27FC",
+ "xnis": "\u22FB",
+ "xodot": "\u2A00",
+ "xopf": "\U0001D569",
+ "xoplus": "\u2A01",
+ "xotime": "\u2A02",
+ "xrArr": "\u27F9",
+ "xrarr": "\u27F6",
+ "xscr": "\U0001D4CD",
+ "xsqcup": "\u2A06",
+ "xuplus": "\u2A04",
+ "xutri": "\u25B3",
+ "xvee": "\u22C1",
+ "xwedge": "\u22C0",
+ "yacute": "\u00FD",
+ "yacy": "\u044F",
+ "ycirc": "\u0177",
+ "ycy": "\u044B",
+ "yen": "\u00A5",
+ "yfr": "\U0001D536",
+ "yicy": "\u0457",
+ "yopf": "\U0001D56A",
+ "yscr": "\U0001D4CE",
+ "yucy": "\u044E",
+ "yuml": "\u00FF",
+ "zacute": "\u017A",
+ "zcaron": "\u017E",
+ "zcy": "\u0437",
+ "zdot": "\u017C",
+ "zeetrf": "\u2128",
+ "zeta": "\u03B6",
+ "zfr": "\U0001D537",
+ "zhcy": "\u0436",
+ "zigrarr": "\u21DD",
+ "zopf": "\U0001D56B",
+ "zscr": "\U0001D4CF",
+ "zwj": "\u200D",
+ "zwnj": "\u200C",
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/indented_code.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/indented_code.go
new file mode 100644
index 00000000..dc5dce1a
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/indented_code.go
@@ -0,0 +1,98 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+import (
+ "strings"
+)
+
+type IndentedCodeLine struct {
+ Indentation int
+ Range Range
+}
+
+type IndentedCode struct {
+ blockBase
+ markdown string
+
+ RawCode []IndentedCodeLine
+}
+
+func (b *IndentedCode) Code() (result string) {
+ for _, code := range b.RawCode {
+ result += strings.Repeat(" ", code.Indentation) + b.markdown[code.Range.Position:code.Range.End]
+ }
+ return
+}
+
+func (b *IndentedCode) Continuation(indentation int, r Range) *continuation {
+ if indentation >= 4 {
+ return &continuation{
+ Indentation: indentation - 4,
+ Remaining: r,
+ }
+ }
+ s := b.markdown[r.Position:r.End]
+ if strings.TrimSpace(s) == "" {
+ return &continuation{
+ Remaining: r,
+ }
+ }
+ return nil
+}
+
+func (b *IndentedCode) AddLine(indentation int, r Range) bool {
+ b.RawCode = append(b.RawCode, IndentedCodeLine{
+ Indentation: indentation,
+ Range: r,
+ })
+ return true
+}
+
+func (b *IndentedCode) Close() {
+ for {
+ last := b.RawCode[len(b.RawCode)-1]
+ s := b.markdown[last.Range.Position:last.Range.End]
+ if strings.TrimRight(s, "\r\n") == "" {
+ b.RawCode = b.RawCode[:len(b.RawCode)-1]
+ } else {
+ break
+ }
+ }
+}
+
+func (b *IndentedCode) AllowsBlockStarts() bool {
+ return false
+}
+
+func indentedCodeStart(markdown string, indentation int, r Range, matchedBlocks, unmatchedBlocks []Block) []Block {
+ if len(unmatchedBlocks) > 0 {
+ if _, ok := unmatchedBlocks[len(unmatchedBlocks)-1].(*Paragraph); ok {
+ return nil
+ }
+ } else if len(matchedBlocks) > 0 {
+ if _, ok := matchedBlocks[len(matchedBlocks)-1].(*Paragraph); ok {
+ return nil
+ }
+ }
+
+ if indentation < 4 {
+ return nil
+ }
+
+ s := markdown[r.Position:r.End]
+ if strings.TrimSpace(s) == "" {
+ return nil
+ }
+
+ return []Block{
+ &IndentedCode{
+ markdown: markdown,
+ RawCode: []IndentedCodeLine{{
+ Indentation: indentation - 4,
+ Range: r,
+ }},
+ },
+ }
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/inlines.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/inlines.go
new file mode 100644
index 00000000..a3abccef
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/inlines.go
@@ -0,0 +1,664 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+import (
+ "container/list"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+type Inline interface {
+ IsInline() bool
+}
+
+type inlineBase struct{}
+
+func (inlineBase) IsInline() bool { return true }
+
+type Text struct {
+ inlineBase
+
+ Text string
+ Range Range
+}
+
+type CodeSpan struct {
+ inlineBase
+
+ Code string
+}
+
+type HardLineBreak struct {
+ inlineBase
+}
+
+type SoftLineBreak struct {
+ inlineBase
+}
+
+type InlineLinkOrImage struct {
+ inlineBase
+
+ Children []Inline
+
+ RawDestination Range
+
+ markdown string
+ rawTitle string
+}
+
+func (i *InlineLinkOrImage) Destination() string {
+ return Unescape(i.markdown[i.RawDestination.Position:i.RawDestination.End])
+}
+
+func (i *InlineLinkOrImage) Title() string {
+ return Unescape(i.rawTitle)
+}
+
+type InlineLink struct {
+ InlineLinkOrImage
+}
+
+type InlineImage struct {
+ InlineLinkOrImage
+}
+
+type ReferenceLinkOrImage struct {
+ inlineBase
+ *ReferenceDefinition
+
+ Children []Inline
+}
+
+type ReferenceLink struct {
+ ReferenceLinkOrImage
+}
+
+type ReferenceImage struct {
+ ReferenceLinkOrImage
+}
+
+type Autolink struct {
+ inlineBase
+
+ Children []Inline
+
+ RawDestination Range
+
+ markdown string
+}
+
+func (i *Autolink) Destination() string {
+ destination := Unescape(i.markdown[i.RawDestination.Position:i.RawDestination.End])
+
+ if strings.HasPrefix(destination, "www") {
+ destination = "http://" + destination
+ }
+
+ return destination
+}
+
+type delimiterType int
+
+const (
+ linkOpeningDelimiter delimiterType = iota
+ imageOpeningDelimiter
+)
+
+type delimiter struct {
+ Type delimiterType
+ IsInactive bool
+ TextNode int
+ Range Range
+}
+
+type inlineParser struct {
+ markdown string
+ ranges []Range
+ referenceDefinitions []*ReferenceDefinition
+
+ raw string
+ position int
+ inlines []Inline
+ delimiterStack *list.List
+}
+
+func newInlineParser(markdown string, ranges []Range, referenceDefinitions []*ReferenceDefinition) *inlineParser {
+ return &inlineParser{
+ markdown: markdown,
+ ranges: ranges,
+ referenceDefinitions: referenceDefinitions,
+ delimiterStack: list.New(),
+ }
+}
+
+func (p *inlineParser) parseBackticks() {
+ count := 1
+ for i := p.position + 1; i < len(p.raw) && p.raw[i] == '`'; i++ {
+ count++
+ }
+ opening := p.raw[p.position : p.position+count]
+ search := p.position + count
+ for search < len(p.raw) {
+ end := strings.Index(p.raw[search:], opening)
+ if end == -1 {
+ break
+ }
+ if search+end+count < len(p.raw) && p.raw[search+end+count] == '`' {
+ search += end + count
+ for search < len(p.raw) && p.raw[search] == '`' {
+ search++
+ }
+ continue
+ }
+ code := strings.Join(strings.Fields(p.raw[p.position+count:search+end]), " ")
+ p.position = search + end + count
+ p.inlines = append(p.inlines, &CodeSpan{
+ Code: code,
+ })
+ return
+ }
+ p.position += len(opening)
+ absPos := relativeToAbsolutePosition(p.ranges, p.position-len(opening))
+ p.inlines = append(p.inlines, &Text{
+ Text: opening,
+ Range: Range{absPos, absPos + len(opening)},
+ })
+}
+
+func (p *inlineParser) parseLineEnding() {
+ if p.position >= 1 && p.raw[p.position-1] == '\t' {
+ p.inlines = append(p.inlines, &HardLineBreak{})
+ } else if p.position >= 2 && p.raw[p.position-1] == ' ' && (p.raw[p.position-2] == '\t' || p.raw[p.position-1] == ' ') {
+ p.inlines = append(p.inlines, &HardLineBreak{})
+ } else {
+ p.inlines = append(p.inlines, &SoftLineBreak{})
+ }
+ p.position++
+ if p.position < len(p.raw) && p.raw[p.position] == '\n' {
+ p.position++
+ }
+}
+
+func (p *inlineParser) parseEscapeCharacter() {
+ if p.position+1 < len(p.raw) && isEscapableByte(p.raw[p.position+1]) {
+ absPos := relativeToAbsolutePosition(p.ranges, p.position+1)
+ p.inlines = append(p.inlines, &Text{
+ Text: string(p.raw[p.position+1]),
+ Range: Range{absPos, absPos + len(string(p.raw[p.position+1]))},
+ })
+ p.position += 2
+ } else {
+ absPos := relativeToAbsolutePosition(p.ranges, p.position)
+ p.inlines = append(p.inlines, &Text{
+ Text: `\`,
+ Range: Range{absPos, absPos + 1},
+ })
+ p.position++
+ }
+}
+
+func (p *inlineParser) parseText() {
+ if next := strings.IndexAny(p.raw[p.position:], "\r\n\\`&![]wW:"); next == -1 {
+ absPos := relativeToAbsolutePosition(p.ranges, p.position)
+ p.inlines = append(p.inlines, &Text{
+ Text: strings.TrimRightFunc(p.raw[p.position:], isWhitespace),
+ Range: Range{absPos, absPos + len(p.raw[p.position:])},
+ })
+ p.position = len(p.raw)
+ } else {
+ absPos := relativeToAbsolutePosition(p.ranges, p.position)
+ if p.raw[p.position+next] == '\r' || p.raw[p.position+next] == '\n' {
+ s := strings.TrimRightFunc(p.raw[p.position:p.position+next], isWhitespace)
+ p.inlines = append(p.inlines, &Text{
+ Text: s,
+ Range: Range{absPos, absPos + len(s)},
+ })
+ } else {
+ if next == 0 {
+ // Always read at least one character since 'w', 'W', and ':' may not actually match another
+ // type of node
+ next = 1
+ }
+
+ p.inlines = append(p.inlines, &Text{
+ Text: p.raw[p.position : p.position+next],
+ Range: Range{absPos, absPos + next},
+ })
+ }
+ p.position += next
+ }
+}
+
+func (p *inlineParser) parseLinkOrImageDelimiter() {
+ absPos := relativeToAbsolutePosition(p.ranges, p.position)
+ if p.raw[p.position] == '[' {
+ p.inlines = append(p.inlines, &Text{
+ Text: "[",
+ Range: Range{absPos, absPos + 1},
+ })
+ p.delimiterStack.PushBack(&delimiter{
+ Type: linkOpeningDelimiter,
+ TextNode: len(p.inlines) - 1,
+ Range: Range{p.position, p.position + 1},
+ })
+ p.position++
+ } else if p.raw[p.position] == '!' && p.position+1 < len(p.raw) && p.raw[p.position+1] == '[' {
+ p.inlines = append(p.inlines, &Text{
+ Text: "![",
+ Range: Range{absPos, absPos + 2},
+ })
+ p.delimiterStack.PushBack(&delimiter{
+ Type: imageOpeningDelimiter,
+ TextNode: len(p.inlines) - 1,
+ Range: Range{p.position, p.position + 2},
+ })
+ p.position += 2
+ } else {
+ p.inlines = append(p.inlines, &Text{
+ Text: "!",
+ Range: Range{absPos, absPos + 1},
+ })
+ p.position++
+ }
+}
+
+func (p *inlineParser) peekAtInlineLinkDestinationAndTitle(position int, isImage bool) (destination, title Range, end int, ok bool) {
+ if position >= len(p.raw) || p.raw[position] != '(' {
+ return
+ }
+ position++
+
+ destinationStart := nextNonWhitespace(p.raw, position)
+ if destinationStart >= len(p.raw) {
+ return
+ } else if p.raw[destinationStart] == ')' {
+ return Range{destinationStart, destinationStart}, Range{destinationStart, destinationStart}, destinationStart + 1, true
+ }
+
+ destination, end, ok = parseLinkDestination(p.raw, destinationStart)
+ if !ok {
+ return
+ }
+ position = end
+
+ if isImage && position < len(p.raw) && isWhitespaceByte(p.raw[position]) {
+ dimensionsStart := nextNonWhitespace(p.raw, position)
+ if dimensionsStart >= len(p.raw) {
+ return
+ }
+
+ if p.raw[dimensionsStart] == '=' {
+ // Read optional image dimensions even if we don't use them
+ _, end, ok = parseImageDimensions(p.raw, dimensionsStart)
+ if !ok {
+ return
+ }
+
+ position = end
+ }
+ }
+
+ if position < len(p.raw) && isWhitespaceByte(p.raw[position]) {
+ titleStart := nextNonWhitespace(p.raw, position)
+ if titleStart >= len(p.raw) {
+ return
+ } else if p.raw[titleStart] == ')' {
+ return destination, Range{titleStart, titleStart}, titleStart + 1, true
+ }
+
+ if p.raw[titleStart] == '"' || p.raw[titleStart] == '\'' || p.raw[titleStart] == '(' {
+ title, end, ok = parseLinkTitle(p.raw, titleStart)
+ if !ok {
+ return
+ }
+ position = end
+ }
+ }
+
+ closingPosition := nextNonWhitespace(p.raw, position)
+ if closingPosition >= len(p.raw) || p.raw[closingPosition] != ')' {
+ return Range{}, Range{}, 0, false
+ }
+
+ return destination, title, closingPosition + 1, true
+}
+
+func (p *inlineParser) referenceDefinition(label string) *ReferenceDefinition {
+ clean := strings.Join(strings.Fields(label), " ")
+ for _, d := range p.referenceDefinitions {
+ if strings.EqualFold(clean, strings.Join(strings.Fields(d.Label()), " ")) {
+ return d
+ }
+ }
+ return nil
+}
+
+func (p *inlineParser) lookForLinkOrImage() {
+ for element := p.delimiterStack.Back(); element != nil; element = element.Prev() {
+ d := element.Value.(*delimiter)
+ if d.Type != imageOpeningDelimiter && d.Type != linkOpeningDelimiter {
+ continue
+ }
+ if d.IsInactive {
+ p.delimiterStack.Remove(element)
+ break
+ }
+
+ isImage := d.Type == imageOpeningDelimiter
+
+ var inline Inline
+
+ if destination, title, next, ok := p.peekAtInlineLinkDestinationAndTitle(p.position+1, isImage); ok {
+ destinationMarkdownPosition := relativeToAbsolutePosition(p.ranges, destination.Position)
+ linkOrImage := InlineLinkOrImage{
+ Children: append([]Inline(nil), p.inlines[d.TextNode+1:]...),
+ RawDestination: Range{destinationMarkdownPosition, destinationMarkdownPosition + destination.End - destination.Position},
+ markdown: p.markdown,
+ rawTitle: p.raw[title.Position:title.End],
+ }
+ if d.Type == imageOpeningDelimiter {
+ inline = &InlineImage{linkOrImage}
+ } else {
+ inline = &InlineLink{linkOrImage}
+ }
+ p.position = next
+ } else {
+ referenceLabel := ""
+ label, next, hasLinkLabel := parseLinkLabel(p.raw, p.position+1)
+ if hasLinkLabel && label.End > label.Position {
+ referenceLabel = p.raw[label.Position:label.End]
+ } else {
+ referenceLabel = p.raw[d.Range.End:p.position]
+ if !hasLinkLabel {
+ next = p.position + 1
+ }
+ }
+ if referenceLabel != "" {
+ if reference := p.referenceDefinition(referenceLabel); reference != nil {
+ linkOrImage := ReferenceLinkOrImage{
+ ReferenceDefinition: reference,
+ Children: append([]Inline(nil), p.inlines[d.TextNode+1:]...),
+ }
+ if d.Type == imageOpeningDelimiter {
+ inline = &ReferenceImage{linkOrImage}
+ } else {
+ inline = &ReferenceLink{linkOrImage}
+ }
+ p.position = next
+ }
+ }
+ }
+
+ if inline != nil {
+ if d.Type == imageOpeningDelimiter {
+ p.inlines = append(p.inlines[:d.TextNode], inline)
+ } else {
+ p.inlines = append(p.inlines[:d.TextNode], inline)
+ for element := element.Prev(); element != nil; element = element.Prev() {
+ if d := element.Value.(*delimiter); d.Type == linkOpeningDelimiter {
+ d.IsInactive = true
+ }
+ }
+ }
+ p.delimiterStack.Remove(element)
+ return
+ } else {
+ p.delimiterStack.Remove(element)
+ break
+ }
+ }
+ absPos := relativeToAbsolutePosition(p.ranges, p.position)
+ p.inlines = append(p.inlines, &Text{
+ Text: "]",
+ Range: Range{absPos, absPos + 1},
+ })
+ p.position++
+}
+
+func CharacterReference(ref string) string {
+ if ref == "" {
+ return ""
+ }
+ if ref[0] == '#' {
+ if len(ref) < 2 {
+ return ""
+ }
+ n := 0
+ if ref[1] == 'X' || ref[1] == 'x' {
+ if len(ref) < 3 {
+ return ""
+ }
+ for i := 2; i < len(ref); i++ {
+ if i > 9 {
+ return ""
+ }
+ d := ref[i]
+ switch {
+ case d >= '0' && d <= '9':
+ n = n*16 + int(d-'0')
+ case d >= 'a' && d <= 'f':
+ n = n*16 + 10 + int(d-'a')
+ case d >= 'A' && d <= 'F':
+ n = n*16 + 10 + int(d-'A')
+ default:
+ return ""
+ }
+ }
+ } else {
+ for i := 1; i < len(ref); i++ {
+ if i > 8 || ref[i] < '0' || ref[i] > '9' {
+ return ""
+ }
+ n = n*10 + int(ref[i]-'0')
+ }
+ }
+ c := rune(n)
+ if c == '\u0000' || !utf8.ValidRune(c) {
+ return string(unicode.ReplacementChar)
+ }
+ return string(c)
+ }
+ if entity, ok := htmlEntities[ref]; ok {
+ return entity
+ }
+ return ""
+}
+
+func (p *inlineParser) parseCharacterReference() {
+ absPos := relativeToAbsolutePosition(p.ranges, p.position)
+ p.position++
+ if semicolon := strings.IndexByte(p.raw[p.position:], ';'); semicolon == -1 {
+ p.inlines = append(p.inlines, &Text{
+ Text: "&",
+ Range: Range{absPos, absPos + 1},
+ })
+ } else if s := CharacterReference(p.raw[p.position : p.position+semicolon]); s != "" {
+ p.position += semicolon + 1
+ p.inlines = append(p.inlines, &Text{
+ Text: s,
+ Range: Range{absPos, absPos + len(s)},
+ })
+ } else {
+ p.inlines = append(p.inlines, &Text{
+ Text: "&",
+ Range: Range{absPos, absPos + 1},
+ })
+ }
+}
+
+func (p *inlineParser) parseAutolink(c rune) bool {
+ for element := p.delimiterStack.Back(); element != nil; element = element.Prev() {
+ d := element.Value.(*delimiter)
+ if !d.IsInactive {
+ return false
+ }
+ }
+
+ var link Range
+ if c == ':' {
+ var ok bool
+ link, ok = parseURLAutolink(p.raw, p.position)
+
+ if !ok {
+ return false
+ }
+
+ // Since the current position is at the colon, we have to rewind the parsing slightly so that
+ // we don't duplicate the URL scheme
+ rewind := strings.Index(p.raw[link.Position:link.End], ":")
+ if rewind != -1 {
+ lastInline := p.inlines[len(p.inlines)-1]
+ lastText, ok := lastInline.(*Text)
+
+ if !ok {
+ // This should never occur since parseURLAutolink will only return a non-empty value
+ // when the previous text ends in a valid URL protocol which would mean that the previous
+ // node is a Text node
+ return false
+ }
+
+ p.inlines = p.inlines[0 : len(p.inlines)-1]
+ p.inlines = append(p.inlines, &Text{
+ Text: lastText.Text[:len(lastText.Text)-rewind],
+ Range: Range{lastText.Range.Position, lastText.Range.End - rewind},
+ })
+ p.position -= rewind
+ }
+ } else if c == 'w' || c == 'W' {
+ var ok bool
+ link, ok = parseWWWAutolink(p.raw, p.position)
+
+ if !ok {
+ return false
+ }
+ }
+
+ linkMarkdownPosition := relativeToAbsolutePosition(p.ranges, link.Position)
+ linkRange := Range{linkMarkdownPosition, linkMarkdownPosition + link.End - link.Position}
+
+ p.inlines = append(p.inlines, &Autolink{
+ Children: []Inline{
+ &Text{
+ Text: p.raw[link.Position:link.End],
+ Range: linkRange,
+ },
+ },
+ RawDestination: linkRange,
+ markdown: p.markdown,
+ })
+ p.position += (link.End - link.Position)
+
+ return true
+}
+
+func (p *inlineParser) Parse() []Inline {
+ for _, r := range p.ranges {
+ p.raw += p.markdown[r.Position:r.End]
+ }
+
+ for p.position < len(p.raw) {
+ c, _ := utf8.DecodeRuneInString(p.raw[p.position:])
+
+ switch c {
+ case '\r', '\n':
+ p.parseLineEnding()
+ case '\\':
+ p.parseEscapeCharacter()
+ case '`':
+ p.parseBackticks()
+ case '&':
+ p.parseCharacterReference()
+ case '!', '[':
+ p.parseLinkOrImageDelimiter()
+ case ']':
+ p.lookForLinkOrImage()
+ case 'w', 'W', ':':
+ matched := p.parseAutolink(c)
+
+ if !matched {
+ p.parseText()
+ }
+ default:
+ p.parseText()
+ }
+ }
+
+ return p.inlines
+}
+
+func ParseInlines(markdown string, ranges []Range, referenceDefinitions []*ReferenceDefinition) (inlines []Inline) {
+ return newInlineParser(markdown, ranges, referenceDefinitions).Parse()
+}
+
+func MergeInlineText(inlines []Inline) []Inline {
+ var ret []Inline
+ for i, v := range inlines {
+ // always add first node
+ if i == 0 {
+ ret = append(ret, v)
+ continue
+ }
+ // not a text node? nothing to merge
+ text, ok := v.(*Text)
+ if !ok {
+ ret = append(ret, v)
+ continue
+ }
+ // previous node is not a text node? nothing to merge
+ prevText, ok := ret[len(ret)-1].(*Text)
+ if !ok {
+ ret = append(ret, v)
+ continue
+ }
+ // previous node is not right before this one
+ if prevText.Range.End != text.Range.Position {
+ ret = append(ret, v)
+ continue
+ }
+ // we have two consecutive text nodes
+ ret[len(ret)-1] = &Text{
+ Text: prevText.Text + text.Text,
+ Range: Range{prevText.Range.Position, text.Range.End},
+ }
+ }
+ return ret
+}
+
+func Unescape(markdown string) string {
+ ret := ""
+
+ position := 0
+ for position < len(markdown) {
+ c, cSize := utf8.DecodeRuneInString(markdown[position:])
+
+ switch c {
+ case '\\':
+ if position+1 < len(markdown) && isEscapableByte(markdown[position+1]) {
+ ret += string(markdown[position+1])
+ position += 2
+ } else {
+ ret += `\`
+ position++
+ }
+ case '&':
+ position++
+ if semicolon := strings.IndexByte(markdown[position:], ';'); semicolon == -1 {
+ ret += "&"
+ } else if s := CharacterReference(markdown[position : position+semicolon]); s != "" {
+ position += semicolon + 1
+ ret += s
+ } else {
+ ret += "&"
+ }
+ default:
+ ret += string(c)
+ position += cSize
+ }
+ }
+
+ return ret
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/inspect.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/inspect.go
new file mode 100644
index 00000000..80b5bc24
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/inspect.go
@@ -0,0 +1,78 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+// Inspect traverses the markdown tree in depth-first order. If f returns true, Inspect invokes f
+// recursively for each child of the block or inline, followed by a call of f(nil).
+func Inspect(markdown string, f func(interface{}) bool) {
+ document, referenceDefinitions := Parse(markdown)
+ InspectBlock(document, func(block Block) bool {
+ if !f(block) {
+ return false
+ }
+ switch v := block.(type) {
+ case *Paragraph:
+ for _, inline := range MergeInlineText(v.ParseInlines(referenceDefinitions)) {
+ InspectInline(inline, func(inline Inline) bool {
+ return f(inline)
+ })
+ }
+ }
+ return true
+ })
+}
+
+// InspectBlock traverses the blocks in depth-first order, starting with block. If f returns true,
+// InspectBlock invokes f recursively for each child of the block, followed by a call of f(nil).
+func InspectBlock(block Block, f func(Block) bool) {
+ if !f(block) {
+ return
+ }
+ switch v := block.(type) {
+ case *Document:
+ for _, child := range v.Children {
+ InspectBlock(child, f)
+ }
+ case *List:
+ for _, child := range v.Children {
+ InspectBlock(child, f)
+ }
+ case *ListItem:
+ for _, child := range v.Children {
+ InspectBlock(child, f)
+ }
+ case *BlockQuote:
+ for _, child := range v.Children {
+ InspectBlock(child, f)
+ }
+ }
+ f(nil)
+}
+
+// InspectInline traverses the blocks in depth-first order, starting with block. If f returns true,
+// InspectInline invokes f recursively for each child of the block, followed by a call of f(nil).
+func InspectInline(inline Inline, f func(Inline) bool) {
+ if !f(inline) {
+ return
+ }
+ switch v := inline.(type) {
+ case *InlineImage:
+ for _, child := range v.Children {
+ InspectInline(child, f)
+ }
+ case *InlineLink:
+ for _, child := range v.Children {
+ InspectInline(child, f)
+ }
+ case *ReferenceImage:
+ for _, child := range v.Children {
+ InspectInline(child, f)
+ }
+ case *ReferenceLink:
+ for _, child := range v.Children {
+ InspectInline(child, f)
+ }
+ }
+ f(nil)
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/lines.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/lines.go
new file mode 100644
index 00000000..a38b5164
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/lines.go
@@ -0,0 +1,27 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+type Line struct {
+ Range
+}
+
+func ParseLines(markdown string) (lines []Line) {
+ lineStartPosition := 0
+ isAfterCarriageReturn := false
+ for position, r := range markdown {
+ if r == '\n' {
+ lines = append(lines, Line{Range{lineStartPosition, position + 1}})
+ lineStartPosition = position + 1
+ } else if isAfterCarriageReturn {
+ lines = append(lines, Line{Range{lineStartPosition, position}})
+ lineStartPosition = position
+ }
+ isAfterCarriageReturn = r == '\r'
+ }
+ if lineStartPosition < len(markdown) {
+ lines = append(lines, Line{Range{lineStartPosition, len(markdown)}})
+ }
+ return
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/links.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/links.go
new file mode 100644
index 00000000..9f3128c4
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/links.go
@@ -0,0 +1,184 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+import (
+ "unicode/utf8"
+)
+
+func parseLinkDestination(markdown string, position int) (raw Range, next int, ok bool) {
+ if position >= len(markdown) {
+ return
+ }
+
+ if markdown[position] == '<' {
+ isEscaped := false
+
+ for offset, c := range []byte(markdown[position+1:]) {
+ if isEscaped {
+ isEscaped = false
+ if isEscapableByte(c) {
+ continue
+ }
+ }
+
+ if c == '\\' {
+ isEscaped = true
+ } else if c == '<' {
+ break
+ } else if c == '>' {
+ return Range{position + 1, position + 1 + offset}, position + 1 + offset + 1, true
+ } else if isWhitespaceByte(c) {
+ break
+ }
+ }
+ }
+
+ openCount := 0
+ isEscaped := false
+ for offset, c := range []byte(markdown[position:]) {
+ if isEscaped {
+ isEscaped = false
+ if isEscapableByte(c) {
+ continue
+ }
+ }
+
+ switch c {
+ case '\\':
+ isEscaped = true
+ case '(':
+ openCount++
+ case ')':
+ if openCount < 1 {
+ return Range{position, position + offset}, position + offset, true
+ }
+ openCount--
+ default:
+ if isWhitespaceByte(c) {
+ return Range{position, position + offset}, position + offset, true
+ }
+ }
+ }
+ return Range{position, len(markdown)}, len(markdown), true
+}
+
+func parseLinkTitle(markdown string, position int) (raw Range, next int, ok bool) {
+ if position >= len(markdown) {
+ return
+ }
+
+ originalPosition := position
+
+ var closer byte
+ switch markdown[position] {
+ case '"', '\'':
+ closer = markdown[position]
+ case '(':
+ closer = ')'
+ default:
+ return
+ }
+ position++
+
+ for position < len(markdown) {
+ switch markdown[position] {
+ case '\\':
+ position++
+ if position < len(markdown) && isEscapableByte(markdown[position]) {
+ position++
+ }
+ case closer:
+ return Range{originalPosition + 1, position}, position + 1, true
+ default:
+ position++
+ }
+ }
+
+ return
+}
+
+func parseLinkLabel(markdown string, position int) (raw Range, next int, ok bool) {
+ if position >= len(markdown) || markdown[position] != '[' {
+ return
+ }
+
+ originalPosition := position
+ position++
+
+ for position < len(markdown) {
+ switch markdown[position] {
+ case '\\':
+ position++
+ if position < len(markdown) && isEscapableByte(markdown[position]) {
+ position++
+ }
+ case '[':
+ return
+ case ']':
+ if position-originalPosition >= 1000 && utf8.RuneCountInString(markdown[originalPosition:position]) >= 1000 {
+ return
+ }
+ return Range{originalPosition + 1, position}, position + 1, true
+ default:
+ position++
+ }
+ }
+
+ return
+}
+
+// As a non-standard feature, we allow image links to specify dimensions of the image by adding "=WIDTHxHEIGHT"
+// after the image destination but before the image title like .
+// Both width and height are optional, but at least one of them must be specified.
+func parseImageDimensions(markdown string, position int) (raw Range, next int, ok bool) {
+ if position >= len(markdown) {
+ return
+ }
+
+ originalPosition := position
+
+ // Read =
+ position += 1
+ if position >= len(markdown) {
+ return
+ }
+
+ // Read width
+ hasWidth := false
+ for isNumericByte(markdown[position]) {
+ hasWidth = true
+ position += 1
+ }
+
+ // Look for early end of dimensions
+ if isWhitespaceByte(markdown[position]) || markdown[position] == ')' {
+ return Range{originalPosition, position - 1}, position, true
+ }
+
+ // Read the x
+ if markdown[position] != 'x' && markdown[position] != 'X' {
+ return
+ }
+ position += 1
+
+ // Read height
+ hasHeight := false
+ for isNumericByte(markdown[position]) {
+ hasHeight = true
+ position += 1
+ }
+
+ // Make sure the there's no trailing characters
+ if !isWhitespaceByte(markdown[position]) && markdown[position] != ')' {
+ return
+ }
+
+ if !hasWidth && !hasHeight {
+ // At least one of width or height is required
+ return
+ }
+
+ return Range{originalPosition, position - 1}, position, true
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/list.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/list.go
new file mode 100644
index 00000000..aea71156
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/list.go
@@ -0,0 +1,220 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+import (
+ "strings"
+)
+
+type ListItem struct {
+ blockBase
+ markdown string
+ hasTrailingBlankLine bool
+ hasBlankLineBetweenChildren bool
+
+ Indentation int
+ Children []Block
+}
+
+func (b *ListItem) Continuation(indentation int, r Range) *continuation {
+ s := b.markdown[r.Position:r.End]
+ if strings.TrimSpace(s) == "" {
+ if b.Children == nil {
+ return nil
+ }
+ return &continuation{
+ Remaining: r,
+ }
+ }
+ if indentation < b.Indentation {
+ return nil
+ }
+ return &continuation{
+ Indentation: indentation - b.Indentation,
+ Remaining: r,
+ }
+}
+
+func (b *ListItem) AddChild(openBlocks []Block) []Block {
+ b.Children = append(b.Children, openBlocks[0])
+ if b.hasTrailingBlankLine {
+ b.hasBlankLineBetweenChildren = true
+ }
+ b.hasTrailingBlankLine = false
+ return openBlocks
+}
+
+func (b *ListItem) AddLine(indentation int, r Range) bool {
+ isBlank := strings.TrimSpace(b.markdown[r.Position:r.End]) == ""
+ if isBlank {
+ b.hasTrailingBlankLine = true
+ }
+ return false
+}
+
+func (b *ListItem) HasTrailingBlankLine() bool {
+ return b.hasTrailingBlankLine || (len(b.Children) > 0 && b.Children[len(b.Children)-1].HasTrailingBlankLine())
+}
+
+func (b *ListItem) isLoose() bool {
+ if b.hasBlankLineBetweenChildren {
+ return true
+ }
+ for i, child := range b.Children {
+ if i < len(b.Children)-1 && child.HasTrailingBlankLine() {
+ return true
+ }
+ }
+ return false
+}
+
+type List struct {
+ blockBase
+ markdown string
+ hasTrailingBlankLine bool
+ hasBlankLineBetweenChildren bool
+
+ IsLoose bool
+ IsOrdered bool
+ OrderedStart int
+ BulletOrDelimiter byte
+ Children []*ListItem
+}
+
+func (b *List) Continuation(indentation int, r Range) *continuation {
+ s := b.markdown[r.Position:r.End]
+ if strings.TrimSpace(s) == "" {
+ return &continuation{
+ Remaining: r,
+ }
+ }
+ return &continuation{
+ Indentation: indentation,
+ Remaining: r,
+ }
+}
+
+func (b *List) AddChild(openBlocks []Block) []Block {
+ if item, ok := openBlocks[0].(*ListItem); ok {
+ b.Children = append(b.Children, item)
+ if b.hasTrailingBlankLine {
+ b.hasBlankLineBetweenChildren = true
+ }
+ b.hasTrailingBlankLine = false
+ return openBlocks
+ } else if list, ok := openBlocks[0].(*List); ok {
+ if len(list.Children) == 1 && list.IsOrdered == b.IsOrdered && list.BulletOrDelimiter == b.BulletOrDelimiter {
+ return b.AddChild(openBlocks[1:])
+ }
+ }
+ return nil
+}
+
+func (b *List) AddLine(indentation int, r Range) bool {
+ isBlank := strings.TrimSpace(b.markdown[r.Position:r.End]) == ""
+ if isBlank {
+ b.hasTrailingBlankLine = true
+ }
+ return false
+}
+
+func (b *List) HasTrailingBlankLine() bool {
+ return b.hasTrailingBlankLine || (len(b.Children) > 0 && b.Children[len(b.Children)-1].HasTrailingBlankLine())
+}
+
+func (b *List) isLoose() bool {
+ if b.hasBlankLineBetweenChildren {
+ return true
+ }
+ for i, child := range b.Children {
+ if child.isLoose() || (i < len(b.Children)-1 && child.HasTrailingBlankLine()) {
+ return true
+ }
+ }
+ return false
+}
+
+func (b *List) Close() {
+ b.IsLoose = b.isLoose()
+}
+
+func parseListMarker(markdown string, r Range) (success, isOrdered bool, orderedStart int, bulletOrDelimiter byte, markerWidth int, remaining Range) {
+ digits := 0
+ n := 0
+ for i := r.Position; i < r.End && markdown[i] >= '0' && markdown[i] <= '9'; i++ {
+ digits++
+ n = n*10 + int(markdown[i]-'0')
+ }
+ if digits > 0 {
+ if digits > 9 || r.Position+digits >= r.End {
+ return
+ }
+ next := markdown[r.Position+digits]
+ if next != '.' && next != ')' {
+ return
+ }
+ return true, true, n, next, digits + 1, Range{r.Position + digits + 1, r.End}
+ }
+ if r.Position >= r.End {
+ return
+ }
+ next := markdown[r.Position]
+ if next != '-' && next != '+' && next != '*' {
+ return
+ }
+ return true, false, 0, next, 1, Range{r.Position + 1, r.End}
+}
+
+func listStart(markdown string, indent int, r Range, matchedBlocks, unmatchedBlocks []Block) []Block {
+ afterList := false
+ if len(matchedBlocks) > 0 {
+ _, afterList = matchedBlocks[len(matchedBlocks)-1].(*List)
+ }
+ if !afterList && indent > 3 {
+ return nil
+ }
+
+ success, isOrdered, orderedStart, bulletOrDelimiter, markerWidth, remaining := parseListMarker(markdown, r)
+ if !success {
+ return nil
+ }
+
+ isBlank := strings.TrimSpace(markdown[remaining.Position:remaining.End]) == ""
+ if len(matchedBlocks) > 0 && len(unmatchedBlocks) == 0 {
+ if _, ok := matchedBlocks[len(matchedBlocks)-1].(*Paragraph); ok {
+ if isBlank || (isOrdered && orderedStart != 1) {
+ return nil
+ }
+ }
+ }
+
+ indentAfterMarker, indentBytesAfterMarker := countIndentation(markdown, remaining)
+ if !isBlank && indentAfterMarker < 1 {
+ return nil
+ }
+
+ remaining = Range{remaining.Position + indentBytesAfterMarker, remaining.End}
+ consumedIndentAfterMarker := indentAfterMarker
+ if isBlank || indentAfterMarker >= 5 {
+ consumedIndentAfterMarker = 1
+ }
+
+ listItem := &ListItem{
+ markdown: markdown,
+ Indentation: indent + markerWidth + consumedIndentAfterMarker,
+ }
+ list := &List{
+ markdown: markdown,
+ IsOrdered: isOrdered,
+ OrderedStart: orderedStart,
+ BulletOrDelimiter: bulletOrDelimiter,
+ Children: []*ListItem{listItem},
+ }
+ ret := []Block{list, listItem}
+ if descendants := blockStartOrParagraph(markdown, indentAfterMarker-consumedIndentAfterMarker, remaining, nil, nil); descendants != nil {
+ listItem.Children = append(listItem.Children, descendants[0])
+ ret = append(ret, descendants...)
+ }
+ return ret
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/markdown.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/markdown.go
new file mode 100644
index 00000000..57b10f3f
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/markdown.go
@@ -0,0 +1,148 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+// This package implements a parser for the subset of the CommonMark spec necessary for us to do
+// server-side processing. It is not a full implementation and lacks many features. But it is
+// complete enough to efficiently and accurately allow us to do what we need to like rewrite image
+// URLs for proxying.
+package markdown
+
+import (
+ "strings"
+)
+
+func isEscapable(c rune) bool {
+ return c > ' ' && (c < '0' || (c > '9' && (c < 'A' || (c > 'Z' && (c < 'a' || (c > 'z' && c <= '~'))))))
+}
+
+func isEscapableByte(c byte) bool {
+ return isEscapable(rune(c))
+}
+
+func isWhitespace(c rune) bool {
+ switch c {
+ case ' ', '\t', '\n', '\u000b', '\u000c', '\r':
+ return true
+ default:
+ return false
+ }
+}
+
+func isWhitespaceByte(c byte) bool {
+ return isWhitespace(rune(c))
+}
+
+func isNumeric(c rune) bool {
+ return c >= '0' && c <= '9'
+}
+
+func isNumericByte(c byte) bool {
+ return isNumeric(rune(c))
+}
+
+func isHex(c rune) bool {
+ return isNumeric(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')
+}
+
+func isHexByte(c byte) bool {
+ return isHex(rune(c))
+}
+
+func isAlphanumeric(c rune) bool {
+ return isNumeric(c) || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
+}
+
+func isAlphanumericByte(c byte) bool {
+ return isAlphanumeric(rune(c))
+}
+
+func nextNonWhitespace(markdown string, position int) int {
+ for offset, c := range []byte(markdown[position:]) {
+ if !isWhitespaceByte(c) {
+ return position + offset
+ }
+ }
+ return len(markdown)
+}
+
+func nextLine(markdown string, position int) (linePosition int, skippedNonWhitespace bool) {
+ for i := position; i < len(markdown); i++ {
+ c := markdown[i]
+ if c == '\r' {
+ if i+1 < len(markdown) && markdown[i+1] == '\n' {
+ return i + 2, skippedNonWhitespace
+ }
+ return i + 1, skippedNonWhitespace
+ } else if c == '\n' {
+ return i + 1, skippedNonWhitespace
+ } else if !isWhitespaceByte(c) {
+ skippedNonWhitespace = true
+ }
+ }
+ return len(markdown), skippedNonWhitespace
+}
+
+func countIndentation(markdown string, r Range) (spaces, bytes int) {
+ for i := r.Position; i < r.End; i++ {
+ if markdown[i] == ' ' {
+ spaces++
+ bytes++
+ } else if markdown[i] == '\t' {
+ spaces += 4
+ bytes++
+ } else {
+ break
+ }
+ }
+ return
+}
+
+func trimLeftSpace(markdown string, r Range) Range {
+ s := markdown[r.Position:r.End]
+ trimmed := strings.TrimLeftFunc(s, isWhitespace)
+ return Range{r.Position, r.End - (len(s) - len(trimmed))}
+}
+
+func trimRightSpace(markdown string, r Range) Range {
+ s := markdown[r.Position:r.End]
+ trimmed := strings.TrimRightFunc(s, isWhitespace)
+ return Range{r.Position, r.End - (len(s) - len(trimmed))}
+}
+
+func relativeToAbsolutePosition(ranges []Range, position int) int {
+ rem := position
+ for _, r := range ranges {
+ l := r.End - r.Position
+ if rem < l {
+ return r.Position + rem
+ }
+ rem -= l
+ }
+ if len(ranges) == 0 {
+ return 0
+ }
+ return ranges[len(ranges)-1].End
+}
+
+func trimBytesFromRanges(ranges []Range, bytes int) (result []Range) {
+ rem := bytes
+ for _, r := range ranges {
+ if rem == 0 {
+ result = append(result, r)
+ continue
+ }
+ l := r.End - r.Position
+ if rem < l {
+ result = append(result, Range{r.Position + rem, r.End})
+ rem = 0
+ continue
+ }
+ rem -= l
+ }
+ return
+}
+
+func Parse(markdown string) (*Document, []*ReferenceDefinition) {
+ lines := ParseLines(markdown)
+ return ParseBlocks(markdown, lines)
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/paragraph.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/paragraph.go
new file mode 100644
index 00000000..6a40fdf8
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/paragraph.go
@@ -0,0 +1,71 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+import (
+ "strings"
+)
+
+type Paragraph struct {
+ blockBase
+ markdown string
+
+ Text []Range
+ ReferenceDefinitions []*ReferenceDefinition
+}
+
+func (b *Paragraph) ParseInlines(referenceDefinitions []*ReferenceDefinition) []Inline {
+ return ParseInlines(b.markdown, b.Text, referenceDefinitions)
+}
+
+func (b *Paragraph) Continuation(indentation int, r Range) *continuation {
+ s := b.markdown[r.Position:r.End]
+ if strings.TrimSpace(s) == "" {
+ return nil
+ }
+ return &continuation{
+ Indentation: indentation,
+ Remaining: r,
+ }
+}
+
+func (b *Paragraph) Close() {
+ for {
+ for i := 0; i < len(b.Text); i++ {
+ b.Text[i] = trimLeftSpace(b.markdown, b.Text[i])
+ if b.Text[i].Position < b.Text[i].End {
+ break
+ }
+ }
+
+ if len(b.Text) == 0 || b.Text[0].Position < b.Text[0].End && b.markdown[b.Text[0].Position] != '[' {
+ break
+ }
+
+ definition, remaining := parseReferenceDefinition(b.markdown, b.Text)
+ if definition == nil {
+ break
+ }
+ b.ReferenceDefinitions = append(b.ReferenceDefinitions, definition)
+ b.Text = remaining
+ }
+
+ for i := len(b.Text) - 1; i >= 0; i-- {
+ b.Text[i] = trimRightSpace(b.markdown, b.Text[i])
+ if b.Text[i].Position < b.Text[i].End {
+ break
+ }
+ }
+}
+
+func newParagraph(markdown string, r Range) *Paragraph {
+ s := markdown[r.Position:r.End]
+ if strings.TrimSpace(s) == "" {
+ return nil
+ }
+ return &Paragraph{
+ markdown: markdown,
+ Text: []Range{r},
+ }
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/reference_definition.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/reference_definition.go
new file mode 100644
index 00000000..e2d0be35
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/reference_definition.go
@@ -0,0 +1,75 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+type ReferenceDefinition struct {
+ RawDestination Range
+
+ markdown string
+ rawLabel string
+ rawTitle string
+}
+
+func (d *ReferenceDefinition) Destination() string {
+ return Unescape(d.markdown[d.RawDestination.Position:d.RawDestination.End])
+}
+
+func (d *ReferenceDefinition) Label() string {
+ return d.rawLabel
+}
+
+func (d *ReferenceDefinition) Title() string {
+ return Unescape(d.rawTitle)
+}
+
+func parseReferenceDefinition(markdown string, ranges []Range) (*ReferenceDefinition, []Range) {
+ raw := ""
+ for _, r := range ranges {
+ raw += markdown[r.Position:r.End]
+ }
+
+ label, next, ok := parseLinkLabel(raw, 0)
+ if !ok {
+ return nil, nil
+ }
+ position := next
+
+ if position >= len(raw) || raw[position] != ':' {
+ return nil, nil
+ }
+ position++
+
+ destination, next, ok := parseLinkDestination(raw, nextNonWhitespace(raw, position))
+ if !ok {
+ return nil, nil
+ }
+ position = next
+
+ absoluteDestination := relativeToAbsolutePosition(ranges, destination.Position)
+ ret := &ReferenceDefinition{
+ RawDestination: Range{absoluteDestination, absoluteDestination + destination.End - destination.Position},
+ markdown: markdown,
+ rawLabel: raw[label.Position:label.End],
+ }
+
+ if position < len(raw) && isWhitespaceByte(raw[position]) {
+ title, next, ok := parseLinkTitle(raw, nextNonWhitespace(raw, position))
+ if !ok {
+ if nextLine, skippedNonWhitespace := nextLine(raw, position); !skippedNonWhitespace {
+ return ret, trimBytesFromRanges(ranges, nextLine)
+ }
+ return nil, nil
+ }
+ if nextLine, skippedNonWhitespace := nextLine(raw, next); !skippedNonWhitespace {
+ ret.rawTitle = raw[title.Position:title.End]
+ return ret, trimBytesFromRanges(ranges, nextLine)
+ }
+ }
+
+ if nextLine, skippedNonWhitespace := nextLine(raw, position); !skippedNonWhitespace {
+ return ret, trimBytesFromRanges(ranges, nextLine)
+ }
+
+ return nil, nil
+}
diff --git a/vendor/github.com/mattermost/platform/NOTICE.txt b/vendor/github.com/mattermost/platform/NOTICE.txt
deleted file mode 100644
index f68ef872..00000000
--- a/vendor/github.com/mattermost/platform/NOTICE.txt
+++ /dev/null
@@ -1,2935 +0,0 @@
-Mattermost Server
-© 2015-present Mattermost, Inc. All Rights Reserved. See LICENSE.txt for license information.
-
-NOTICES:
---------
-
-This document includes a list of open source components used in Mattermost Server, including those that have been modified.
-
----
-
-This product contains a modified portion of 'jquery-dragster', a drag and drop listener
-by Jan Martin.
-
-* HOMEPAGE:
- * https://github.com/catmanjan/jquery-dragster
-
-* LICENSE:
-
-The MIT License (MIT)
-
-Copyright (c) 2015 Jan Martin
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
----
-
-This product contains a modified portion of 'golang-lru', a golang LRU cache by hashicorp,
-based on Groupcache by Google Inc.
-
-* HOMEPAGE:
- * https://github.com/hashicorp/golang-lru
-
-* LICENSE:
-
-This Source Code Form is subject to the terms of the Mozilla Public License, v.2.0. If a
-copy of the MPL was not distributed with this file, You can obtain one at
-http://mozilla.org/MPL/2.0/.
-
----
-
-This product contains a modified portion of 'golang-freetype', a port of the Freetype
-font rasterizer (www.freetype.org) to the Go programming.
-
-Portions of this software are copyright © 2010 The FreeType Project (www.freetype.org).
-All rights reserved.
-
-* HOMEPAGE:
- * http://www.freetype.org
-
-* LICENSE:
-
- The FreeType Project LICENSE
- ----------------------------
-
- 2006-Jan-27
-
- Copyright 1996-2002, 2006 by
- David Turner, Robert Wilhelm, and Werner Lemberg
-
-
-
-Introduction
-============
-
- The FreeType Project is distributed in several archive packages;
- some of them may contain, in addition to the FreeType font engine,
- various tools and contributions which rely on, or relate to, the
- FreeType Project.
-
- This license applies to all files found in such packages, and
- which do not fall under their own explicit license. The license
- affects thus the FreeType font engine, the test programs,
- documentation and makefiles, at the very least.
-
- This license was inspired by the BSD, Artistic, and IJG
- (Independent JPEG Group) licenses, which all encourage inclusion
- and use of free software in commercial and freeware products
- alike. As a consequence, its main points are that:
-
- o We don't promise that this software works. However, we will be
- interested in any kind of bug reports. (`as is' distribution)
-
- o You can use this software for whatever you want, in parts or
- full form, without having to pay us. (`royalty-free' usage)
-
- o You may not pretend that you wrote this software. If you use
- it, or only parts of it, in a program, you must acknowledge
- somewhere in your documentation that you have used the
- FreeType code. (`credits')
-
- We specifically permit and encourage the inclusion of this
- software, with or without modifications, in commercial products.
- We disclaim all warranties covering The FreeType Project and
- assume no liability related to The FreeType Project.
-
-
- Finally, many people asked us for a preferred form for a
- credit/disclaimer to use in compliance with this license. We thus
- encourage you to use the following text:
-
- """
- Portions of this software are copyright � The FreeType
- Project (www.freetype.org). All rights reserved.
- """
-
- Please replace with the value from the FreeType version you
- actually use.
-
-
-Legal Terms
-===========
-
-0. Definitions
---------------
-
- Throughout this license, the terms `package', `FreeType Project',
- and `FreeType archive' refer to the set of files originally
- distributed by the authors (David Turner, Robert Wilhelm, and
- Werner Lemberg) as the `FreeType Project', be they named as alpha,
- beta or final release.
-
- `You' refers to the licensee, or person using the project, where
- `using' is a generic term including compiling the project's source
- code as well as linking it to form a `program' or `executable'.
- This program is referred to as `a program using the FreeType
- engine'.
-
- This license applies to all files distributed in the original
- FreeType Project, including all source code, binaries and
- documentation, unless otherwise stated in the file in its
- original, unmodified form as distributed in the original archive.
- If you are unsure whether or not a particular file is covered by
- this license, you must contact us to verify this.
-
- The FreeType Project is copyright (C) 1996-2000 by David Turner,
- Robert Wilhelm, and Werner Lemberg. All rights reserved except as
- specified below.
-
-1. No Warranty
---------------
-
- THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY
- KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS
- BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO
- USE, OF THE FREETYPE PROJECT.
-
-2. Redistribution
------------------
-
- This license grants a worldwide, royalty-free, perpetual and
- irrevocable right and license to use, execute, perform, compile,
- display, copy, create derivative works of, distribute and
- sublicense the FreeType Project (in both source and object code
- forms) and derivative works thereof for any purpose; and to
- authorize others to exercise some or all of the rights granted
- herein, subject to the following conditions:
-
- o Redistribution of source code must retain this license file
- (`FTL.TXT') unaltered; any additions, deletions or changes to
- the original files must be clearly indicated in accompanying
- documentation. The copyright notices of the unaltered,
- original files must be preserved in all copies of source
- files.
-
- o Redistribution in binary form must provide a disclaimer that
- states that the software is based in part of the work of the
- FreeType Team, in the distribution documentation. We also
- encourage you to put an URL to the FreeType web page in your
- documentation, though this isn't mandatory.
-
- These conditions apply to any software derived from or based on
- the FreeType Project, not just the unmodified files. If you use
- our work, you must acknowledge us. However, no fee need be paid
- to us.
-
-3. Advertising
---------------
-
- Neither the FreeType authors and contributors nor you shall use
- the name of the other for commercial, advertising, or promotional
- purposes without specific prior written permission.
-
- We suggest, but do not require, that you use one or more of the
- following phrases to refer to this software in your documentation
- or advertising materials: `FreeType Project', `FreeType Engine',
- `FreeType library', or `FreeType Distribution'.
-
- As you have not signed this license, you are not required to
- accept it. However, as the FreeType Project is copyrighted
- material, only this license, or another one contracted with the
- authors, grants you the right to use, distribute, and modify it.
- Therefore, by using, distributing, or modifying the FreeType
- Project, you indicate that you understand and accept all the terms
- of this license.
-
-4. Contacts
------------
-
- There are two mailing lists related to FreeType:
-
- o freetype@nongnu.org
-
- Discusses general use and applications of FreeType, as well as
- future and wanted additions to the library and distribution.
- If you are looking for support, start in this list if you
- haven't found anything to help you in the documentation.
-
- o freetype-devel@nongnu.org
-
- Discusses bugs, as well as engine internals, design issues,
- specific licenses, porting, etc.
-
- Our home page can be found at
-
- http://www.freetype.org
-
---- end of FTL.TXT ---
-
----
-
-This product contains a modified portion of 'gemoji', a collection of emoji images and names by Apple Inc. and other contributors.
-
-* HOMEPAGE:
- * https://github.com/github/gemoji/blob/master/LICENSE
-
-* LICENSE:
-
-octocat, squirrel, shipit
-Copyright (c) 2013 GitHub Inc. All rights reserved.
-
-bowtie, neckbeard, fu
-Copyright (c) 2013 37signals, LLC. All rights reserved.
-
-feelsgood, finnadie, goberserk, godmode, hurtrealbad, rage 1-4, suspect
-Copyright (c) 2013 id Software. All rights reserved.
-
-trollface
-Copyright (c) 2013 whynne@deviantart. All rights reserved.
-
-All other images
-Copyright (c) 2013 Apple Inc. All rights reserved.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this
-software and associated documentation files (the "Software"), to deal in the Software
-without restriction, including without limitation the rights to use, copy, modify,
-merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to the following
-conditions:
-
-The above copyright notice and this permission notice shall be included in all copies
-or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
-PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
-FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
-
----
-
-This product contains a modified portion of 'goexif', which provides decoding of basic exif and tiff encoded data.
-
-by Robert Carlsen & Contributors
-
-* HOMEPAGE:
- * https://github.com/rwcarlsen/goexif
-
-* LICENSE:
-
-Copyright (c) 2012, Robert Carlsen & Contributors
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'graphics-go', an implementation of basic image manipulation operations in the Go programming language.
-
-by The Graphics-Go Authors
-
-* HOMEPAGE:
- * https://code.google.com/p/graphics-go/
-
-* LICENSE:
-
-Copyright (c) 2011 The Graphics-Go Authors. All rights reserved.
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'babel-es6-polyfill', a pure ES6 polyfill for Babel that only includes standardised features.
-
-by Jason Berry
-
-* HOMEPAGE:
- * https://github.com/JasonBerry/babel-es6-polyfill
-
-* LICENSE:
-
-Copyright (c) 2015 Jason Berry
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
----
-
-This product contains a modified portion of 'go-ldap', basic LDAP v3 functionality for the GO programming language.
-
-by The Go Authors
-
-* HOMEPAGE:
- * https://github.com/go-ldap/ldap
-
-* LICENSE:
-
-Copyright (c) 2012 The Go Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'Google Fonts', various Open Source fonts for use on the web.
-
-by Google Inc.
-
-* HOMEPAGE:
- * https://www.google.com/fonts
-
-* LICENSE:
-
-# Code licensed under the Apache 2.0 License:
-
-Copyright 2011 Google Inc.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-# Fonts licensed under CC-BY 3.0:
-
-CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
-"Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
-"Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License.
-"Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership.
-"Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
-"Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
-"Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
-"You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-"Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
-"Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
-
-2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
-to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections;
-to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified.";
-to Distribute and Publicly Perform the Work including as incorporated in Collections; and,
-to Distribute and Publicly Perform Adaptations.
-For the avoidance of doubt:
-
-Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
-Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and,
-Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License.
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
-You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(b), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(b), as requested.
-If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4 (b) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
-Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
-This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
-Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
-
-Creative Commons Notice
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License.
-
-Creative Commons may be contacted at https://creativecommons.org/.
-
----
-
-This product contains a modified portion of 'go-i18n', a Go package and a command that translates Go programs into multiple languages
-by Nick Snyder.
-
-* HOMEPAGE:
- * https://github.com/nicksnyder/go-i18n
-
-* LICENSE:
-
-Copyright (c) 2014 Nick Snyder https://github.com/nicksnyder
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
----
-
-This product contains a modified portion of 'dgoogauth', a go implementation of the Google Authenticator library by Damian Gryski.
-
-* HOMEPAGE:
- * https://github.com/dgryski/dgoogauth
-
-* LICENSE:
-
-Copyright 2012 Damian Gryski
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
----
-
-This product contains a modified portion of 'fbjs', a collection of JavaScript utilities by Facebook, Inc.
-
-* HOMEPAGE:
- * https://github.com/facebook/fbjs
-
-* LICENSE:
-
-BSD License
-
-For fbjs software
-
-Copyright (c) 2013-present, Facebook, Inc.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
- * Neither the name Facebook nor the names of its contributors may be used to
- endorse or promote products derived from this software without specific
- prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'marked', a full-featured markdown parser and compiler, written in JavaScript. Built for speed by Christopher Jeffrey.
-
-* HOMEPAGE:
- * https://github.com/chjj/marked
-
-* LICENSE:
-
-The MIT License (MIT)
-
-Copyright (c) 2011-2014, Christopher Jeffrey.
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-No attribution is required by products that make use of this software.
-
----
-
-This product contains a modified portion of 'gziphandler', a tiny Go package which wraps HTTP handlers to transparently gzip the response body, for clients which support it by The New York Times Company.
-
-* HOMEPAGE:
- * https://github.com/NYTimes/gziphandler
-
-* LICENSE:
-
-Apache License 2.0
-
-Copyright (c) 2015 The New York Times Company
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this library except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
----
-
-This product contains a modified portion of 'handlers', a collection of handlers (aka "HTTP middleware") for use with Go's net/http package (or any framework supporting http.Handler) by The Gorilla Handlers Authors.
-
-* HOMEPAGE:
- * https://github.com/gorilla/handlers
-
-* LICENSE:
-
-Copyright (c) 2013 The Gorilla Handlers Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-
- Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'log4go', an unmaintained fork, left only so it doesn't break imports by Kyle Lemons.
-
-* HOMEPAGE:
- * https://github.com/alecthomas/log4go
-
-* LICENSE:
-
-Copyright (c) 2010, Kyle Lemons . All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'manners', a package imaging providing basic image manipulation functions (resize, rotate, flip, crop, etc.) by Grigory Dryapak.
-
-* HOMEPAGE:
- * https://github.com/disintegration/imaging
-
-* LICENSE:
-
-The MIT License (MIT)
-
-Copyright (c) 2012-2014 Grigory Dryapak
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
----
-
-This product contains a modified portion of 'gorp', to save time, minimize the drudgery of getting data in and out of the database, and help code focus on algorithms, not infrastructure by James Cooper.
-
-* HOMEPAGE:
- * https://github.com/go-gorp/gorp
-
-* LICENSE:
-
-The MIT License (MIT)
-
-Copyright (c) 2012 James Cooper
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-'Software'), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
----
-
-This product contains a modified portion of 'mysql', a lightweight and fast MySQL driver for Go's (golang) database/sql package by Mozilla Public.
-
-* HOMEPAGE:
- * https://github.com/go-sql-driver/mysql
-
-* LICENSE:
-
-Mozilla Public License Version 2.0
-==================================
-
-1. Definitions
---------------
-
-1.1. "Contributor"
- means each individual or legal entity that creates, contributes to
- the creation of, or owns Covered Software.
-
-1.2. "Contributor Version"
- means the combination of the Contributions of others (if any) used
- by a Contributor and that particular Contributor's Contribution.
-
-1.3. "Contribution"
- means Covered Software of a particular Contributor.
-
-1.4. "Covered Software"
- means Source Code Form to which the initial Contributor has attached
- the notice in Exhibit A, the Executable Form of such Source Code
- Form, and Modifications of such Source Code Form, in each case
- including portions thereof.
-
-1.5. "Incompatible With Secondary Licenses"
- means
-
- (a) that the initial Contributor has attached the notice described
- in Exhibit B to the Covered Software; or
-
- (b) that the Covered Software was made available under the terms of
- version 1.1 or earlier of the License, but not also under the
- terms of a Secondary License.
-
-1.6. "Executable Form"
- means any form of the work other than Source Code Form.
-
-1.7. "Larger Work"
- means a work that combines Covered Software with other material, in
- a separate file or files, that is not Covered Software.
-
-1.8. "License"
- means this document.
-
-1.9. "Licensable"
- means having the right to grant, to the maximum extent possible,
- whether at the time of the initial grant or subsequently, any and
- all of the rights conveyed by this License.
-
-1.10. "Modifications"
- means any of the following:
-
- (a) any file in Source Code Form that results from an addition to,
- deletion from, or modification of the contents of Covered
- Software; or
-
- (b) any new file in Source Code Form that contains any Covered
- Software.
-
-1.11. "Patent Claims" of a Contributor
- means any patent claim(s), including without limitation, method,
- process, and apparatus claims, in any patent Licensable by such
- Contributor that would be infringed, but for the grant of the
- License, by the making, using, selling, offering for sale, having
- made, import, or transfer of either its Contributions or its
- Contributor Version.
-
-1.12. "Secondary License"
- means either the GNU General Public License, Version 2.0, the GNU
- Lesser General Public License, Version 2.1, the GNU Affero General
- Public License, Version 3.0, or any later versions of those
- licenses.
-
-1.13. "Source Code Form"
- means the form of the work preferred for making modifications.
-
-1.14. "You" (or "Your")
- means an individual or a legal entity exercising rights under this
- License. For legal entities, "You" includes any entity that
- controls, is controlled by, or is under common control with You. For
- purposes of this definition, "control" means (a) the power, direct
- or indirect, to cause the direction or management of such entity,
- whether by contract or otherwise, or (b) ownership of more than
- fifty percent (50%) of the outstanding shares or beneficial
- ownership of such entity.
-
-2. License Grants and Conditions
---------------------------------
-
-2.1. Grants
-
-Each Contributor hereby grants You a world-wide, royalty-free,
-non-exclusive license:
-
-(a) under intellectual property rights (other than patent or trademark)
- Licensable by such Contributor to use, reproduce, make available,
- modify, display, perform, distribute, and otherwise exploit its
- Contributions, either on an unmodified basis, with Modifications, or
- as part of a Larger Work; and
-
-(b) under Patent Claims of such Contributor to make, use, sell, offer
- for sale, have made, import, and otherwise transfer either its
- Contributions or its Contributor Version.
-
-2.2. Effective Date
-
-The licenses granted in Section 2.1 with respect to any Contribution
-become effective for each Contribution on the date the Contributor first
-distributes such Contribution.
-
-2.3. Limitations on Grant Scope
-
-The licenses granted in this Section 2 are the only rights granted under
-this License. No additional rights or licenses will be implied from the
-distribution or licensing of Covered Software under this License.
-Notwithstanding Section 2.1(b) above, no patent license is granted by a
-Contributor:
-
-(a) for any code that a Contributor has removed from Covered Software;
- or
-
-(b) for infringements caused by: (i) Your and any other third party's
- modifications of Covered Software, or (ii) the combination of its
- Contributions with other software (except as part of its Contributor
- Version); or
-
-(c) under Patent Claims infringed by Covered Software in the absence of
- its Contributions.
-
-This License does not grant any rights in the trademarks, service marks,
-or logos of any Contributor (except as may be necessary to comply with
-the notice requirements in Section 3.4).
-
-2.4. Subsequent Licenses
-
-No Contributor makes additional grants as a result of Your choice to
-distribute the Covered Software under a subsequent version of this
-License (see Section 10.2) or under the terms of a Secondary License (if
-permitted under the terms of Section 3.3).
-
-2.5. Representation
-
-Each Contributor represents that the Contributor believes its
-Contributions are its original creation(s) or it has sufficient rights
-to grant the rights to its Contributions conveyed by this License.
-
-2.6. Fair Use
-
-This License is not intended to limit any rights You have under
-applicable copyright doctrines of fair use, fair dealing, or other
-equivalents.
-
-2.7. Conditions
-
-Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
-in Section 2.1.
-
-3. Responsibilities
--------------------
-
-3.1. Distribution of Source Form
-
-All distribution of Covered Software in Source Code Form, including any
-Modifications that You create or to which You contribute, must be under
-the terms of this License. You must inform recipients that the Source
-Code Form of the Covered Software is governed by the terms of this
-License, and how they can obtain a copy of this License. You may not
-attempt to alter or restrict the recipients' rights in the Source Code
-Form.
-
-3.2. Distribution of Executable Form
-
-If You distribute Covered Software in Executable Form then:
-
-(a) such Covered Software must also be made available in Source Code
- Form, as described in Section 3.1, and You must inform recipients of
- the Executable Form how they can obtain a copy of such Source Code
- Form by reasonable means in a timely manner, at a charge no more
- than the cost of distribution to the recipient; and
-
-(b) You may distribute such Executable Form under the terms of this
- License, or sublicense it under different terms, provided that the
- license for the Executable Form does not attempt to limit or alter
- the recipients' rights in the Source Code Form under this License.
-
-3.3. Distribution of a Larger Work
-
-You may create and distribute a Larger Work under terms of Your choice,
-provided that You also comply with the requirements of this License for
-the Covered Software. If the Larger Work is a combination of Covered
-Software with a work governed by one or more Secondary Licenses, and the
-Covered Software is not Incompatible With Secondary Licenses, this
-License permits You to additionally distribute such Covered Software
-under the terms of such Secondary License(s), so that the recipient of
-the Larger Work may, at their option, further distribute the Covered
-Software under the terms of either this License or such Secondary
-License(s).
-
-3.4. Notices
-
-You may not remove or alter the substance of any license notices
-(including copyright notices, patent notices, disclaimers of warranty,
-or limitations of liability) contained within the Source Code Form of
-the Covered Software, except that You may alter any license notices to
-the extent required to remedy known factual inaccuracies.
-
-3.5. Application of Additional Terms
-
-You may choose to offer, and to charge a fee for, warranty, support,
-indemnity or liability obligations to one or more recipients of Covered
-Software. However, You may do so only on Your own behalf, and not on
-behalf of any Contributor. You must make it absolutely clear that any
-such warranty, support, indemnity, or liability obligation is offered by
-You alone, and You hereby agree to indemnify every Contributor for any
-liability incurred by such Contributor as a result of warranty, support,
-indemnity or liability terms You offer. You may include additional
-disclaimers of warranty and limitations of liability specific to any
-jurisdiction.
-
-4. Inability to Comply Due to Statute or Regulation
----------------------------------------------------
-
-If it is impossible for You to comply with any of the terms of this
-License with respect to some or all of the Covered Software due to
-statute, judicial order, or regulation then You must: (a) comply with
-the terms of this License to the maximum extent possible; and (b)
-describe the limitations and the code they affect. Such description must
-be placed in a text file included with all distributions of the Covered
-Software under this License. Except to the extent prohibited by statute
-or regulation, such description must be sufficiently detailed for a
-recipient of ordinary skill to be able to understand it.
-
-5. Termination
---------------
-
-5.1. The rights granted under this License will terminate automatically
-if You fail to comply with any of its terms. However, if You become
-compliant, then the rights granted under this License from a particular
-Contributor are reinstated (a) provisionally, unless and until such
-Contributor explicitly and finally terminates Your grants, and (b) on an
-ongoing basis, if such Contributor fails to notify You of the
-non-compliance by some reasonable means prior to 60 days after You have
-come back into compliance. Moreover, Your grants from a particular
-Contributor are reinstated on an ongoing basis if such Contributor
-notifies You of the non-compliance by some reasonable means, this is the
-first time You have received notice of non-compliance with this License
-from such Contributor, and You become compliant prior to 30 days after
-Your receipt of the notice.
-
-5.2. If You initiate litigation against any entity by asserting a patent
-infringement claim (excluding declaratory judgment actions,
-counter-claims, and cross-claims) alleging that a Contributor Version
-directly or indirectly infringes any patent, then the rights granted to
-You by any and all Contributors for the Covered Software under Section
-2.1 of this License shall terminate.
-
-5.3. In the event of termination under Sections 5.1 or 5.2 above, all
-end user license agreements (excluding distributors and resellers) which
-have been validly granted by You or Your distributors under this License
-prior to termination shall survive termination.
-
-************************************************************************
-* *
-* 6. Disclaimer of Warranty *
-* ------------------------- *
-* *
-* Covered Software is provided under this License on an "as is" *
-* basis, without warranty of any kind, either expressed, implied, or *
-* statutory, including, without limitation, warranties that the *
-* Covered Software is free of defects, merchantable, fit for a *
-* particular purpose or non-infringing. The entire risk as to the *
-* quality and performance of the Covered Software is with You. *
-* Should any Covered Software prove defective in any respect, You *
-* (not any Contributor) assume the cost of any necessary servicing, *
-* repair, or correction. This disclaimer of warranty constitutes an *
-* essential part of this License. No use of any Covered Software is *
-* authorized under this License except under this disclaimer. *
-* *
-************************************************************************
-
-************************************************************************
-* *
-* 7. Limitation of Liability *
-* -------------------------- *
-* *
-* Under no circumstances and under no legal theory, whether tort *
-* (including negligence), contract, or otherwise, shall any *
-* Contributor, or anyone who distributes Covered Software as *
-* permitted above, be liable to You for any direct, indirect, *
-* special, incidental, or consequential damages of any character *
-* including, without limitation, damages for lost profits, loss of *
-* goodwill, work stoppage, computer failure or malfunction, or any *
-* and all other commercial damages or losses, even if such party *
-* shall have been informed of the possibility of such damages. This *
-* limitation of liability shall not apply to liability for death or *
-* personal injury resulting from such party's negligence to the *
-* extent applicable law prohibits such limitation. Some *
-* jurisdictions do not allow the exclusion or limitation of *
-* incidental or consequential damages, so this exclusion and *
-* limitation may not apply to You. *
-* *
-************************************************************************
-
-8. Litigation
--------------
-
-Any litigation relating to this License may be brought only in the
-courts of a jurisdiction where the defendant maintains its principal
-place of business and such litigation shall be governed by laws of that
-jurisdiction, without reference to its conflict-of-law provisions.
-Nothing in this Section shall prevent a party's ability to bring
-cross-claims or counter-claims.
-
-9. Miscellaneous
-----------------
-
-This License represents the complete agreement concerning the subject
-matter hereof. If any provision of this License is held to be
-unenforceable, such provision shall be reformed only to the extent
-necessary to make it enforceable. Any law or regulation which provides
-that the language of a contract shall be construed against the drafter
-shall not be used to construe this License against a Contributor.
-
-10. Versions of the License
----------------------------
-
-10.1. New Versions
-
-Mozilla Foundation is the license steward. Except as provided in Section
-10.3, no one other than the license steward has the right to modify or
-publish new versions of this License. Each version will be given a
-distinguishing version number.
-
-10.2. Effect of New Versions
-
-You may distribute the Covered Software under the terms of the version
-of the License under which You originally received the Covered Software,
-or under the terms of any subsequent version published by the license
-steward.
-
-10.3. Modified Versions
-
-If you create software not governed by this License, and you want to
-create a new license for such software, you may create and use a
-modified version of this License if you rename the license and remove
-any references to the name of the license steward (except to note that
-such modified license differs from this License).
-
-10.4. Distributing Source Code Form that is Incompatible With Secondary
-Licenses
-
-If You choose to distribute Source Code Form that is Incompatible With
-Secondary Licenses under the terms of this version of the License, the
-notice described in Exhibit B of this License must be attached.
-
-Exhibit A - Source Code Form License Notice
--------------------------------------------
-
- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-If it is not possible or desirable to put the notice in a particular
-file, then You may include the notice in a location (such as a LICENSE
-file in a relevant directory) where a recipient would be likely to look
-for such a notice.
-
-You may add additional accurate notices of copyright ownership.
-
-Exhibit B - "Incompatible With Secondary Licenses" Notice
----------------------------------------------------------
-
- This Source Code Form is "Incompatible With Secondary Licenses", as
- defined by the Mozilla Public License, v. 2.0.
-
----
-
-This product contains a modified portion of 'context', a golang registry for global request variables by Rodrigo Moraes.
-
-* HOMEPAGE:
- * https://github.com/gorilla/context
-
-* LICENSE:
-
-Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'mux', a powerful URL router and dispatcher for golang by Rodrigo Moraes.
-
-* HOMEPAGE:
- * https://github.com/gorilla/mux
-
-* LICENSE:
-
-Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'websocket', a WebSocket implementation for Go by The Gorilla WebSocket Authors.
-
-* HOMEPAGE:
- * https://github.com/gorilla/websocket
-
-* LICENSE:
-
-Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-
- Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-## pq
-
-This product contains a modified portion of 'pq', a Pure Go Postgres driver for database/sql by Blake Mizerany and Contributors.
-
-* HOMEPAGE:
- * https://github.com/lib/pq
-
-* LICENSE:
-
-Copyright (c) 2011-2013, 'pq' Contributors
-Portions Copyright (C) 2011 Blake Mizerany
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction,
-including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
----
-
-This product contains a modified portion of 'user_agent', a Go library that parses HTTP User Agents by Miquel Sabaté Solà.
-
-* HOMEPAGE:
- * https://github.com/mssola/user_agent
-
-* LICENSE:
-
-Copyright (c) 2012-2016 Miquel Sabaté Solà
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
----
-
-This product contains a modified portion of 'uuid', a package generates and inspects UUIDs based on RFC 412 and DCE 1.1: Authentication and Security Services by Google Inc.
-
-* HOMEPAGE:
- * https://github.com/pborman/uuid
-
-* LICENSE:
-
-Copyright (c) 2009,2014 Google Inc. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'yaml', a YAML support for the Go language by Canonical Inc.
-
-* HOMEPAGE:
- * https://gopkg.in/yaml.v2
-
-* LICENSE:
-
-
-Copyright (c) 2011-2014 - Canonical Inc.
-
-This software is licensed under the LGPLv3, included below.
-
-As a special exception to the GNU Lesser General Public License version 3
-("LGPL3"), the copyright holders of this Library give you permission to
-convey to a third party a Combined Work that links statically or dynamically
-to this Library without providing any Minimal Corresponding Source or
-Minimal Application Code as set out in 4d or providing the installation
-information set out in section 4e, provided that you comply with the other
-provisions of LGPL3 and provided that you meet, for the Application the
-terms and conditions of the license(s) which apply to the Application.
-
-Except as stated in this special exception, the provisions of LGPL3 will
-continue to comply in full to this Library. If you modify this Library, you
-may apply this exception to your version of this Library, but you are not
-obliged to do so. If you do not wish to do so, delete this exception
-statement from your version. This exception does not (and cannot) modify any
-license terms which apply to the Application, with which you must still
-comply.
-
-
- GNU LESSER GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-
- This version of the GNU Lesser General Public License incorporates
-the terms and conditions of version 3 of the GNU General Public
-License, supplemented by the additional permissions listed below.
-
- 0. Additional Definitions.
-
- As used herein, "this License" refers to version 3 of the GNU Lesser
-General Public License, and the "GNU GPL" refers to version 3 of the GNU
-General Public License.
-
- "The Library" refers to a covered work governed by this License,
-other than an Application or a Combined Work as defined below.
-
- An "Application" is any work that makes use of an interface provided
-by the Library, but which is not otherwise based on the Library.
-Defining a subclass of a class defined by the Library is deemed a mode
-of using an interface provided by the Library.
-
- A "Combined Work" is a work produced by combining or linking an
-Application with the Library. The particular version of the Library
-with which the Combined Work was made is also called the "Linked
-Version".
-
- The "Minimal Corresponding Source" for a Combined Work means the
-Corresponding Source for the Combined Work, excluding any source code
-for portions of the Combined Work that, considered in isolation, are
-based on the Application, and not on the Linked Version.
-
- The "Corresponding Application Code" for a Combined Work means the
-object code and/or source code for the Application, including any data
-and utility programs needed for reproducing the Combined Work from the
-Application, but excluding the System Libraries of the Combined Work.
-
- 1. Exception to Section 3 of the GNU GPL.
-
- You may convey a covered work under sections 3 and 4 of this License
-without being bound by section 3 of the GNU GPL.
-
- 2. Conveying Modified Versions.
-
- If you modify a copy of the Library, and, in your modifications, a
-facility refers to a function or data to be supplied by an Application
-that uses the facility (other than as an argument passed when the
-facility is invoked), then you may convey a copy of the modified
-version:
-
- a) under this License, provided that you make a good faith effort to
- ensure that, in the event an Application does not supply the
- function or data, the facility still operates, and performs
- whatever part of its purpose remains meaningful, or
-
- b) under the GNU GPL, with none of the additional permissions of
- this License applicable to that copy.
-
- 3. Object Code Incorporating Material from Library Header Files.
-
- The object code form of an Application may incorporate material from
-a header file that is part of the Library. You may convey such object
-code under terms of your choice, provided that, if the incorporated
-material is not limited to numerical parameters, data structure
-layouts and accessors, or small macros, inline functions and templates
-(ten or fewer lines in length), you do both of the following:
-
- a) Give prominent notice with each copy of the object code that the
- Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the object code with a copy of the GNU GPL and this license
- document.
-
- 4. Combined Works.
-
- You may convey a Combined Work under terms of your choice that,
-taken together, effectively do not restrict modification of the
-portions of the Library contained in the Combined Work and reverse
-engineering for debugging such modifications, if you also do each of
-the following:
-
- a) Give prominent notice with each copy of the Combined Work that
- the Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the Combined Work with a copy of the GNU GPL and this license
- document.
-
- c) For a Combined Work that displays copyright notices during
- execution, include the copyright notice for the Library among
- these notices, as well as a reference directing the user to the
- copies of the GNU GPL and this license document.
-
- d) Do one of the following:
-
- 0) Convey the Minimal Corresponding Source under the terms of this
- License, and the Corresponding Application Code in a form
- suitable for, and under terms that permit, the user to
- recombine or relink the Application with a modified version of
- the Linked Version to produce a modified Combined Work, in the
- manner specified by section 6 of the GNU GPL for conveying
- Corresponding Source.
-
- 1) Use a suitable shared library mechanism for linking with the
- Library. A suitable mechanism is one that (a) uses at run time
- a copy of the Library already present on the user's computer
- system, and (b) will operate properly with a modified version
- of the Library that is interface-compatible with the Linked
- Version.
-
- e) Provide Installation Information, but only if you would otherwise
- be required to provide such information under section 6 of the
- GNU GPL, and only to the extent that such information is
- necessary to install and execute a modified version of the
- Combined Work produced by recombining or relinking the
- Application with a modified version of the Linked Version. (If
- you use option 4d0, the Installation Information must accompany
- the Minimal Corresponding Source and Corresponding Application
- Code. If you use option 4d1, you must provide the Installation
- Information in the manner specified by section 6 of the GNU GPL
- for conveying Corresponding Source.)
-
- 5. Combined Libraries.
-
- You may place library facilities that are a work based on the
-Library side by side in a single library together with other library
-facilities that are not Applications and are not covered by this
-License, and convey such a combined library under terms of your
-choice, if you do both of the following:
-
- a) Accompany the combined library with a copy of the same work based
- on the Library, uncombined with any other library facilities,
- conveyed under the terms of this License.
-
- b) Give prominent notice with the combined library that part of it
- is a work based on the Library, and explaining where to find the
- accompanying uncombined form of the same work.
-
- 6. Revised Versions of the GNU Lesser General Public License.
-
- The Free Software Foundation may publish revised and/or new versions
-of the GNU Lesser General Public License from time to time. Such new
-versions will be similar in spirit to the present version, but may
-differ in detail to address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Library as you received it specifies that a certain numbered version
-of the GNU Lesser General Public License "or any later version"
-applies to it, you have the option of following the terms and
-conditions either of that published version or of any later version
-published by the Free Software Foundation. If the Library as you
-received it does not specify a version number of the GNU Lesser
-General Public License, you may choose any version of the GNU Lesser
-General Public License ever published by the Free Software Foundation.
-
- If the Library as you received it specifies that a proxy can decide
-whether future versions of the GNU Lesser General Public License shall
-apply, that proxy's public statement of acceptance of any version is
-permanent authorization for you to choose that version for the
-Library.
-
----
-
-This product contains a modified portion of 'throttled', a package store offering a memory-based and a Redis-based throttled by Martin Angers.
-
-* HOMEPAGE:
- * http://gopkg.in/throttled/throttled.v1/store
-
-* LICENSE:
-
-Copyright (c) 2014, Martin Angers
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
-EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'throttled', a package throttled implementing different throttling strategies for controlling access to HTTP handlers by Martin Angers and Contributors.
-
-* HOMEPAGE:
- * http://gopkg.in/throttled/throttled.v1
-
-* LICENSE:
-
-Copyright (c) 2014, Martin Angers and Contributors.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
-EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'fsnotify', to utilize golang.org/x/sys rather than syscall from the standard library by The Go Authors and fsnotify Authors.
-
-* HOMEPAGE:
- * http://gopkg.in/fsnotify.v1
-
-* LICENSE:
-
-Copyright (c) 2012 The Go Authors. All rights reserved.
-Copyright (c) 2012 fsnotify Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-## asn1-ber
-
-This product contains a modified portion of 'asn1-ber', an ASN1 BER Encoding / Decoding Library for the GO programming language by The Go Authors.
-
-* HOMEPAGE:
- * http://gopkg.in/asn1-ber.v1
-
-* LICENSE:
-
-Copyright (c) 2012 The Go Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'asn1-ber', an ASN1 BER Encoding / Decoding Library for the GO programming language by The Go Authors.
-
-* HOMEPAGE:
- * http://gopkg.in/asn1-ber.v1
-
-* LICENSE:
-
-Copyright (c) 2012 The Go Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-## redigo
-
-This product contains a modified portion of 'redigo', a Go client for the Redis database.
-
-* HOMEPAGE:
- * https://github.com/garyburd/redigo
-
-* LICENSE:
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
----
-
-## crypto
-
-This product contains a modified portion of 'crypto', a set of Go cryptographic libraries.
-
-* HOMEPAGE:
- * https://github.com/golang/crypto
-
-* LICENSE:
-
-Copyright (c) 2009 The Go Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
----
-
-## image
-
-This product contains a modified portion of 'image', a set of Go image libraries.
-
-* HOMEPAGE:
- * https://github.com/golang/image
-
-* LICENSE:
-
-Copyright (c) 2009 The Go Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
----
-
-## rsc
-
-This product contains a modified portion of 'rsc', a random source code library from Google
-
-* HOMEPAGE:
- * https://code.google.com/archive/p/rsc/
-
-* LICENSE:
-
-Copyright (c) ,
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-## mattermost-driver-javascript
-
-This product contains a modified portion of 'mattermost-driver-javascript', a Javascript library for interacting with the Mattermost API.
-
-* HOMEPAGE:
- * https://github.com/mattermost/mattermost-driver-javascript
-
-* LICENSE:
-
-Copyright 2016-present Mattermost
-
-Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
-
----
-
-This product contains a modified portion of 'osext', providing a function that returns an absolute path to the current program executable, built by kardianos.
-
-* HOMEPAGE:
- * https://github.com/kardianos/osext
-
-* LICENSE:
-
-Copyright (c) 2012 The Go Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'go-saml', a SAML client library for Go written by RobotsAndPencils.
-
-* HOMEPAGE:
- * https://github.com/RobotsAndPencils/go-saml
-
-* LICENSE:
-
-The MIT License (MIT)
-
-Copyright (c) 2015 Robots and Pencils
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
----
-
-This product contains a modified portion of 'minio-go', a Golang Client SDK providing simple APIs to access any Amazon S3 compatible object storage server, built by Minio.
-
-* HOMEPAGE:
- * https://github.com/minio/minio-go
-
-* LICENSE:
-
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
----
-
-This product contains a modified portion of 'graceful', a Go package enabling graceful shutdown of an http.Handler server, built by Tyler Bunnell.
-
-* HOMEPAGE:
- * https://github.com/tylerb/graceful
-
-* LICENSE:
-
-The MIT License (MIT)
-
-Copyright (c) 2014 Tyler Bunnell
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
----
-
-This product contains a modified portion of 'letsencrypt' to manage TLS certificates automatically via LetsEncrypt.org, built by Russ Cox.
-
-* HOMEPAGE:
- * https://github.com/rsc/letsencrypt
-
-* LICENSE:
-
-Copyright (c) 2009 The Go Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of ‘manners’ a polite webserver for Go, built by Braintree, a division of PayPal, Inc.
-
-* HOMEPAGE:
- * https://github.com/braintree/manners
-
-* LICENSE:
-
-Copyright (c) 2014 Braintree, a division of PayPal, Inc.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
----
-
-This product contains a modified portion of ‘perks’ which contains the Go package quantile that computes approximate quantiles over an unbounded data stream within low memory and CPU bounds, built by Blake Mizerany.
-
-* HOMEPAGE:
- * https://github.com/beorn7/perks
-
-* LICENSE:
-
-Copyright (C) 2013 Blake Mizerany
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
----
-
-This product contains a modified portion of ‘protobuf’ for Go support for Protocol Buffers, built by The Go Authors.
-
-* HOMEPAGE:
- * https://github.com/golang/protobuf
-
-* LICENSE:
-
-Go support for Protocol Buffers - Google's data interchange format
-
-Copyright 2010 The Go Authors. All rights reserved.
-https://github.com/golang/protobuf
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of ‘golang_protobuf_extensions’ which provides various Protocol Buffer extensions, built by matttproud.
-
-* HOMEPAGE:
- * https://github.com/matttproud/golang_protobuf_extensions
-
-* LICENSE:
-
-# Code licensed under the Apache 2.0 License:
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
----
-
-This product contains a modified portion of ‘cobra’, a Commander for modern Go CLI interactions, built by spf13.
-
-* HOMEPAGE:
- * https://github.com/spf13/cobra
-
-* LICENSE:
-
-# Code licensed under the Apache 2.0 License:
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
----
-
-This product contains a modified portion of ‘pflag’, a Commander for modern Go CLI interactions, built by Alex Ogier.
-
-* HOMEPAGE:
- * https://github.com/spf13/pflag
-
-* LICENSE:
-
-Copyright (c) 2012 Alex Ogier. All rights reserved.
-Copyright (c) 2012 The Go Authors. All rights reserved.
-
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains modified portions of Prometheus ‘client_golang’, which is the Go library for Prometheus. Prometheus is a systems and service monitoring system, started by Matt T. Proud and Julius Volz in 2012.
-
-* HOMEPAGE:
- * https://github.com/prometheus/client_golang
-
-* LICENSE:
-
-# Code licensed under the Apache 2.0 License:
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
----
-
-This product contains modified portions of Prometheus ‘client_model’, which provides data model artifacts for Prometheus. Prometheus is a systems and service monitoring system, started by Matt T. Proud and Julius Volz in 2012.
-
-* HOMEPAGE:
- * https://github.com/prometheus/client_model
-
-* LICENSE:
-
-# Code licensed under the Apache 2.0 License:
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
----
-
-This product contains modified portions of Prometheus ‘common’, which is the part of the Prometheus project that contains Go libraries that are shared across Prometheus components and libraries. Prometheus is a systems and service monitoring system, started by Matt T. Proud and Julius Volz in 2012.
-
-* HOMEPAGE:
- * https://github.com/prometheus/common
-
-* LICENSE:
-
-# Code licensed under the Apache 2.0 License:
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
----
-
-This product contains modified portions of Prometheus ‘procfs’, which is the part of the Prometheus project that provides functions to retrieve system, kernel and process metrics from the pseudo-filesystem proc. Prometheus is a systems and service monitoring system, started by Matt T. Proud and Julius Volz in 2012.
-
-* HOMEPAGE:
- * https://github.com/prometheus/procfs
-
-* LICENSE:
-
-# Code licensed under the Apache 2.0 License:
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-----
-
-This product contains a modified portion of 'go-opengraph', for parsing OpenGraph data from HTML into regular structures by Vitaly Dyatlov.
-
-* HOMEPAGE:
- * https://github.com/dyatlov/go-opengraph
-
-* LICENSE:
-
-The MIT License (MIT)
-
-Copyright (c) 2015 Vitaly Dyatlov
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-----
-
-This product contains a modified portion of `viper` for handling configuration in Go programs. Built by Steve Francia.
-
-* HOMEPAGE:
- * https://github.com/spf13/viper
-
-* LICENSE:
-
-The MIT License (MIT)
-
-Copyright (c) 2014 Steve Francia
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-----
-
-This product contains a modified version of `gomail` for sending emails. Built by Alexandre Cesaro.
-
-* HOMEPAGE:
- * https://github.com/go-gomail/gomail
-
-* LICENSE:
-
-The MIT License (MIT)
-
-Copyright (c) 2014 Alexandre Cesaro
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-----
-
-This product contains a modified version of `fsnotify` for file system notifications. Built by the Go Authors and the fsnotify Authors.
-
-* HOMEPAGE:
- * https://github.com/fsnotify/fsnotify
-
-* LICENSE:
-
-Copyright (c) 2012 The Go Authors. All rights reserved.
-Copyright (c) 2012 fsnotify Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-----
-
-## analytics-go
-
-This product contains a modified portion of `analytics-go`, a segment analytics client for Go by Segment.
-
-* HOMEPAGE:
- * https://github.com/segmentio/analytics-go
-
-* LICENSE:
-
-MIT license
-
-----
-
-## html2text
-
-This product contains a modified portion of 'html2text', an HTML to raw text converter by Carlos Tadeu Panato Junior, based on `html2text` by Jay Taylor.
-
-* HOMEPAGE
- * https://github.com/mattermost/html2text
-
-* LICENSE
-
-The MIT License (MIT)
-
-Copyright (c) 2015 Jay Taylor
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
----
-
-## elastic
-
-This product contains a modified portion of `elastic`, an Elasticsearch client for the Go programming language by Oliver Eilhard.
-
-* HOMEPAGE
- * https://github.com/olivere/elastic
-
-* LICENSE
-
-The MIT License (MIT)
-Copyright © 2012-2015 Oliver Eilhard
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the “Software”), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included
-in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-IN THE SOFTWARE.
-
----
-
-## redis
-
-This product contains a modified portion of `redis`, a type-safe Redis client for Golang, by go-redis.
-
-* HOMEPAGE
- * https://github.com/go-redis/redis
-
-* LICENSE
-
-Copyright (c) 2013 The github.com/go-redis/redis Authors.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-## testify
-
-This product contains a modified portion of testify, a Golang toolkit providing tools for testifying that code behaves as intended.
-
-* HOMEPAGE
- * https://github.com/stretchr/testify
-
-* LICENSE
-
-Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell
-
-Please consider promoting this project if you find it useful.
-
-Permission is hereby granted, free of charge, to any person
-obtaining a copy of this software and associated documentation
-files (the "Software"), to deal in the Software without restriction,
-including without limitation the rights to use, copy, modify, merge,
-publish, distribute, sublicense, and/or sell copies of the Software,
-and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included
-in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
-OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
-OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
----
-
-## html2text
-
-This product contains a modified portion of `html2text`, a Golang library for HTML to plaintext conversion.
-
-* HOMEPAGE
- * https://github.com/mattermost/html2text
-
-* LICENSE
-
-The MIT License (MIT)
-
-Copyright (c) 2015 Jay Taylor
-Modified work: Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/vendor/github.com/mattermost/platform/model/client.go b/vendor/github.com/mattermost/platform/model/client.go
deleted file mode 100644
index ef890b59..00000000
--- a/vendor/github.com/mattermost/platform/model/client.go
+++ /dev/null
@@ -1,2379 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-package model
-
-import (
- "bytes"
- "fmt"
- "io"
- "io/ioutil"
- "mime/multipart"
- "net/http"
- "net/url"
- "strconv"
- "strings"
- "time"
-
- l4g "github.com/alecthomas/log4go"
-)
-
-var UsedApiV3 *int32 = new(int32)
-
-const (
- HEADER_REQUEST_ID = "X-Request-ID"
- HEADER_VERSION_ID = "X-Version-ID"
- HEADER_CLUSTER_ID = "X-Cluster-ID"
- HEADER_ETAG_SERVER = "ETag"
- HEADER_ETAG_CLIENT = "If-None-Match"
- HEADER_FORWARDED = "X-Forwarded-For"
- HEADER_REAL_IP = "X-Real-IP"
- HEADER_FORWARDED_PROTO = "X-Forwarded-Proto"
- HEADER_TOKEN = "token"
- HEADER_BEARER = "BEARER"
- HEADER_AUTH = "Authorization"
- HEADER_REQUESTED_WITH = "X-Requested-With"
- HEADER_REQUESTED_WITH_XML = "XMLHttpRequest"
- STATUS = "status"
- STATUS_OK = "OK"
- STATUS_FAIL = "FAIL"
- STATUS_REMOVE = "REMOVE"
-
- CLIENT_DIR = "client"
-
- API_URL_SUFFIX_V1 = "/api/v1"
- API_URL_SUFFIX_V3 = "/api/v3"
- API_URL_SUFFIX_V4 = "/api/v4"
- API_URL_SUFFIX = API_URL_SUFFIX_V4
-)
-
-type Result struct {
- RequestId string
- Etag string
- Data interface{}
-}
-
-type ResponseMetadata struct {
- StatusCode int
- Error *AppError
- RequestId string
- Etag string
-}
-
-type Client struct {
- Url string // The location of the server like "http://localhost:8065"
- ApiUrl string // The api location of the server like "http://localhost:8065/api/v3"
- HttpClient *http.Client // The http client
- AuthToken string
- AuthType string
- TeamId string
- RequestId string
- Etag string
- ServerVersion string
-}
-
-// NewClient constructs a new client with convienence methods for talking to
-// the server.
-func NewClient(url string) *Client {
- return &Client{url, url + API_URL_SUFFIX_V3, &http.Client{}, "", "", "", "", "", ""}
-}
-
-func closeBody(r *http.Response) {
- if r.Body != nil {
- ioutil.ReadAll(r.Body)
- r.Body.Close()
- }
-}
-
-func (c *Client) SetOAuthToken(token string) {
- c.AuthToken = token
- c.AuthType = HEADER_TOKEN
-}
-
-func (c *Client) ClearOAuthToken() {
- c.AuthToken = ""
- c.AuthType = HEADER_BEARER
-}
-
-func (c *Client) SetTeamId(teamId string) {
- c.TeamId = teamId
-}
-
-func (c *Client) GetTeamId() string {
- if len(c.TeamId) == 0 {
- println(`You are trying to use a route that requires a team_id,
- but you have not called SetTeamId() in client.go`)
- }
-
- return c.TeamId
-}
-
-func (c *Client) ClearTeamId() {
- c.TeamId = ""
-}
-
-func (c *Client) GetTeamRoute() string {
- return fmt.Sprintf("/teams/%v", c.GetTeamId())
-}
-
-func (c *Client) GetChannelRoute(channelId string) string {
- return fmt.Sprintf("/teams/%v/channels/%v", c.GetTeamId(), channelId)
-}
-
-func (c *Client) GetUserRequiredRoute(userId string) string {
- return fmt.Sprintf("/users/%v", userId)
-}
-
-func (c *Client) GetChannelNameRoute(channelName string) string {
- return fmt.Sprintf("/teams/%v/channels/name/%v", c.GetTeamId(), channelName)
-}
-
-func (c *Client) GetEmojiRoute() string {
- return "/emoji"
-}
-
-func (c *Client) GetGeneralRoute() string {
- return "/general"
-}
-
-func (c *Client) GetFileRoute(fileId string) string {
- return fmt.Sprintf("/files/%v", fileId)
-}
-
-func (c *Client) DoPost(url, data, contentType string) (*http.Response, *AppError) {
- rq, _ := http.NewRequest("POST", c.Url+url, strings.NewReader(data))
- rq.Header.Set("Content-Type", contentType)
- rq.Close = true
-
- if rp, err := c.HttpClient.Do(rq); err != nil {
- return nil, NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0)
- } else if rp.StatusCode >= 300 {
- defer closeBody(rp)
- return nil, AppErrorFromJson(rp.Body)
- } else {
- return rp, nil
- }
-}
-
-func (c *Client) DoApiPost(url string, data string) (*http.Response, *AppError) {
- rq, _ := http.NewRequest("POST", c.ApiUrl+url, strings.NewReader(data))
- rq.Close = true
-
- if len(c.AuthToken) > 0 {
- rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
- }
-
- if rp, err := c.HttpClient.Do(rq); err != nil {
- return nil, NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0)
- } else if rp.StatusCode >= 300 {
- defer closeBody(rp)
- return nil, AppErrorFromJson(rp.Body)
- } else {
- return rp, nil
- }
-}
-
-func (c *Client) DoApiGet(url string, data string, etag string) (*http.Response, *AppError) {
- rq, _ := http.NewRequest("GET", c.ApiUrl+url, strings.NewReader(data))
- rq.Close = true
-
- if len(etag) > 0 {
- rq.Header.Set(HEADER_ETAG_CLIENT, etag)
- }
-
- if len(c.AuthToken) > 0 {
- rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
- }
-
- if rp, err := c.HttpClient.Do(rq); err != nil {
- return nil, NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0)
- } else if rp.StatusCode == 304 {
- return rp, nil
- } else if rp.StatusCode >= 300 {
- defer closeBody(rp)
- return rp, AppErrorFromJson(rp.Body)
- } else {
- return rp, nil
- }
-}
-
-func getCookie(name string, resp *http.Response) *http.Cookie {
- for _, cookie := range resp.Cookies() {
- if cookie.Name == name {
- return cookie
- }
- }
-
- return nil
-}
-
-// Must is a convenience function used for testing.
-func (c *Client) Must(result *Result, err *AppError) *Result {
- if err != nil {
- l4g.Close()
- time.Sleep(time.Second)
- panic(err)
- }
-
- return result
-}
-
-// MustGeneric is a convenience function used for testing.
-func (c *Client) MustGeneric(result interface{}, err *AppError) interface{} {
- if err != nil {
- l4g.Close()
- time.Sleep(time.Second)
- panic(err)
- }
-
- return result
-}
-
-// CheckStatusOK is a convenience function for checking the return of Web Service
-// call that return the a map of status=OK.
-func (c *Client) CheckStatusOK(r *http.Response) bool {
- m := MapFromJson(r.Body)
- defer closeBody(r)
-
- if m != nil && m[STATUS] == STATUS_OK {
- return true
- }
-
- return false
-}
-
-func (c *Client) fillInExtraProperties(r *http.Response) {
- c.RequestId = r.Header.Get(HEADER_REQUEST_ID)
- c.Etag = r.Header.Get(HEADER_ETAG_SERVER)
- c.ServerVersion = r.Header.Get(HEADER_VERSION_ID)
-}
-
-func (c *Client) clearExtraProperties() {
- c.RequestId = ""
- c.Etag = ""
- c.ServerVersion = ""
-}
-
-// General Routes Section
-
-// GetClientProperties returns properties needed by the client to show/hide
-// certian features. It returns a map of strings.
-func (c *Client) GetClientProperties() (map[string]string, *AppError) {
- c.clearExtraProperties()
- if r, err := c.DoApiGet(c.GetGeneralRoute()+"/client_props", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return MapFromJson(r.Body), nil
- }
-}
-
-// LogClient is a convenience Web Service call so clients can log messages into
-// the server-side logs. For example we typically log javascript error messages
-// into the server-side. It returns true if the logging was successful.
-func (c *Client) LogClient(message string) (bool, *AppError) {
- c.clearExtraProperties()
- m := make(map[string]string)
- m["level"] = "ERROR"
- m["message"] = message
-
- if r, err := c.DoApiPost(c.GetGeneralRoute()+"/log_client", MapToJson(m)); err != nil {
- return false, err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return c.CheckStatusOK(r), nil
- }
-}
-
-// GetPing returns a map of strings with server time, server version, and node Id.
-// Systems that want to check on health status of the server should check the
-// url /api/v3/ping for a 200 status response.
-func (c *Client) GetPing() (map[string]string, *AppError) {
- c.clearExtraProperties()
- if r, err := c.DoApiGet(c.GetGeneralRoute()+"/ping", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return MapFromJson(r.Body), nil
- }
-}
-
-// Team Routes Section
-
-// CreateTeam creates a team based on the provided Team struct. On success it returns
-// the Team struct with the Id, CreateAt and other server-decided fields populated.
-func (c *Client) CreateTeam(team *Team) (*Result, *AppError) {
- if r, err := c.DoApiPost("/teams/create", team.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamFromJson(r.Body)}, nil
- }
-}
-
-// GetAllTeams returns a map of all teams using team ids as the key.
-func (c *Client) GetAllTeams() (*Result, *AppError) {
- if r, err := c.DoApiGet("/teams/all", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamMapFromJson(r.Body)}, nil
- }
-}
-
-// GetAllTeamListings returns a map of all teams that are available to join
-// using team ids as the key. Must be authenticated.
-func (c *Client) GetAllTeamListings() (*Result, *AppError) {
- if r, err := c.DoApiGet("/teams/all_team_listings", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamMapFromJson(r.Body)}, nil
- }
-}
-
-// FindTeamByName returns the strings "true" or "false" depending on if a team
-// with the provided name was found.
-func (c *Client) FindTeamByName(name string) (*Result, *AppError) {
- m := make(map[string]string)
- m["name"] = name
- if r, err := c.DoApiPost("/teams/find_team_by_name", MapToJson(m)); err != nil {
- return nil, err
- } else {
- val := false
- if body, _ := ioutil.ReadAll(r.Body); string(body) == "true" {
- val = true
- }
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), val}, nil
- }
-}
-
-// Adds a user directly to the team without sending an invite.
-// The teamId and userId are required. You must be a valid member of the team and/or
-// have the correct role to add new users to the team. Returns a map of user_id=userId
-// if successful, otherwise returns an AppError.
-func (c *Client) AddUserToTeam(teamId string, userId string) (*Result, *AppError) {
- if len(teamId) == 0 {
- teamId = c.GetTeamId()
- }
-
- data := make(map[string]string)
- data["user_id"] = userId
- if r, err := c.DoApiPost(fmt.Sprintf("/teams/%v", teamId)+"/add_user_to_team", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-// AddUserToTeamFromInvite adds a user to a team based off data provided in an invite link.
-// Either hash and dataToHash are required or inviteId is required.
-func (c *Client) AddUserToTeamFromInvite(hash, dataToHash, inviteId string) (*Result, *AppError) {
- data := make(map[string]string)
- data["hash"] = hash
- data["data"] = dataToHash
- data["invite_id"] = inviteId
- if r, err := c.DoApiPost("/teams/add_user_to_team_from_invite", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamFromJson(r.Body)}, nil
- }
-}
-
-// Removes a user directly from the team.
-// The teamId and userId are required. You must be a valid member of the team and/or
-// have the correct role to remove a user from the team. Returns a map of user_id=userId
-// if successful, otherwise returns an AppError.
-func (c *Client) RemoveUserFromTeam(teamId string, userId string) (*Result, *AppError) {
- if len(teamId) == 0 {
- teamId = c.GetTeamId()
- }
-
- data := make(map[string]string)
- data["user_id"] = userId
- if r, err := c.DoApiPost(fmt.Sprintf("/teams/%v", teamId)+"/remove_user_from_team", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) InviteMembers(invites *Invites) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/invite_members", invites.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), InvitesFromJson(r.Body)}, nil
- }
-}
-
-// UpdateTeam updates a team based on the changes in the provided team struct. On success
-// it returns a sanitized version of the updated team. Must be authenticated as a team admin
-// for that team or a system admin.
-func (c *Client) UpdateTeam(team *Team) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/update", team.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamFromJson(r.Body)}, nil
- }
-}
-
-// User Routes Section
-
-// CreateUser creates a user in the system based on the provided user struct.
-func (c *Client) CreateUser(user *User, hash string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/create", user.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
- }
-}
-
-// CreateUserWithInvite creates a user based on the provided user struct. Either the hash and
-// data strings or the inviteId is required from the invite.
-func (c *Client) CreateUserWithInvite(user *User, hash string, data string, inviteId string) (*Result, *AppError) {
-
- url := "/users/create?d=" + url.QueryEscape(data) + "&h=" + url.QueryEscape(hash) + "&iid=" + url.QueryEscape(inviteId)
-
- if r, err := c.DoApiPost(url, user.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) CreateUserFromSignup(user *User, data string, hash string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/create?d="+url.QueryEscape(data)+"&h="+hash, user.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
- }
-}
-
-// GetUser returns a user based on a provided user id string. Must be authenticated.
-func (c *Client) GetUser(id string, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/users/"+id+"/get", "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
- }
-}
-
-// getByUsername returns a user based on a provided username string. Must be authenticated.
-func (c *Client) GetByUsername(username string, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(fmt.Sprintf("/users/name/%v", username), "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
- }
-}
-
-// getByEmail returns a user based on a provided username string. Must be authenticated.
-func (c *Client) GetByEmail(email string, etag string) (*User, *ResponseMetadata) {
- if r, err := c.DoApiGet(fmt.Sprintf("/users/email/%v", email), "", etag); err != nil {
- return nil, &ResponseMetadata{StatusCode: r.StatusCode, Error: err}
- } else {
- defer closeBody(r)
- return UserFromJson(r.Body),
- &ResponseMetadata{
- StatusCode: r.StatusCode,
- RequestId: r.Header.Get(HEADER_REQUEST_ID),
- Etag: r.Header.Get(HEADER_ETAG_SERVER),
- }
- }
-}
-
-// GetMe returns the current user.
-func (c *Client) GetMe(etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/users/me", "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
- }
-}
-
-// GetProfiles returns a map of users using user id as the key. Must be authenticated.
-func (c *Client) GetProfiles(offset int, limit int, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(fmt.Sprintf("/users/%v/%v", offset, limit), "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserMapFromJson(r.Body)}, nil
- }
-}
-
-// GetProfilesInTeam returns a map of users for a team using user id as the key. Must
-// be authenticated.
-func (c *Client) GetProfilesInTeam(teamId string, offset int, limit int, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(fmt.Sprintf("/teams/%v/users/%v/%v", teamId, offset, limit), "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserMapFromJson(r.Body)}, nil
- }
-}
-
-// GetProfilesInChannel returns a map of users for a channel using user id as the key. Must
-// be authenticated.
-func (c *Client) GetProfilesInChannel(channelId string, offset int, limit int, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(fmt.Sprintf(c.GetChannelRoute(channelId)+"/users/%v/%v", offset, limit), "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserMapFromJson(r.Body)}, nil
- }
-}
-
-// GetProfilesNotInChannel returns a map of users not in a channel but on the team using user id as the key. Must
-// be authenticated.
-func (c *Client) GetProfilesNotInChannel(channelId string, offset int, limit int, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(fmt.Sprintf(c.GetChannelRoute(channelId)+"/users/not_in_channel/%v/%v", offset, limit), "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserMapFromJson(r.Body)}, nil
- }
-}
-
-// GetProfilesByIds returns a map of users based on the user ids provided. Must
-// be authenticated.
-func (c *Client) GetProfilesByIds(userIds []string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/ids", ArrayToJson(userIds)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserMapFromJson(r.Body)}, nil
- }
-}
-
-// SearchUsers returns a list of users that have a username matching or similar to the search term. Must
-// be authenticated.
-func (c *Client) SearchUsers(params UserSearch) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/search", params.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserListFromJson(r.Body)}, nil
- }
-}
-
-// AutocompleteUsersInChannel returns two lists for autocompletion of users in a channel. The first list "in_channel",
-// specifies users in the channel. The second list "out_of_channel" specifies users outside of the
-// channel. Term, the string to search against, is required, channel id is also required. Must be authenticated.
-func (c *Client) AutocompleteUsersInChannel(term string, channelId string) (*Result, *AppError) {
- url := fmt.Sprintf("%s/users/autocomplete?term=%s", c.GetChannelRoute(channelId), url.QueryEscape(term))
- if r, err := c.DoApiGet(url, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserAutocompleteInChannelFromJson(r.Body)}, nil
- }
-}
-
-// AutocompleteUsersInTeam returns a list for autocompletion of users in a team. The list "in_team" specifies
-// the users in the team that match the provided term, matching against username, full name and
-// nickname. Must be authenticated.
-func (c *Client) AutocompleteUsersInTeam(term string) (*Result, *AppError) {
- url := fmt.Sprintf("%s/users/autocomplete?term=%s", c.GetTeamRoute(), url.QueryEscape(term))
- if r, err := c.DoApiGet(url, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserAutocompleteInTeamFromJson(r.Body)}, nil
- }
-}
-
-// AutocompleteUsers returns a list for autocompletion of users on the system that match the provided term,
-// matching against username, full name and nickname. Must be authenticated.
-func (c *Client) AutocompleteUsers(term string) (*Result, *AppError) {
- url := fmt.Sprintf("/users/autocomplete?term=%s", url.QueryEscape(term))
- if r, err := c.DoApiGet(url, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserListFromJson(r.Body)}, nil
- }
-}
-
-// LoginById authenticates a user by user id and password.
-func (c *Client) LoginById(id string, password string) (*Result, *AppError) {
- m := make(map[string]string)
- m["id"] = id
- m["password"] = password
- return c.login(m)
-}
-
-// Login authenticates a user by login id, which can be username, email or some sort
-// of SSO identifier based on configuration, and a password.
-func (c *Client) Login(loginId string, password string) (*Result, *AppError) {
- m := make(map[string]string)
- m["login_id"] = loginId
- m["password"] = password
- return c.login(m)
-}
-
-// LoginByLdap authenticates a user by LDAP id and password.
-func (c *Client) LoginByLdap(loginId string, password string) (*Result, *AppError) {
- m := make(map[string]string)
- m["login_id"] = loginId
- m["password"] = password
- m["ldap_only"] = "true"
- return c.login(m)
-}
-
-// LoginWithDevice authenticates a user by login id (username, email or some sort
-// of SSO identifier based on configuration), password and attaches a device id to
-// the session.
-func (c *Client) LoginWithDevice(loginId string, password string, deviceId string) (*Result, *AppError) {
- m := make(map[string]string)
- m["login_id"] = loginId
- m["password"] = password
- m["device_id"] = deviceId
- return c.login(m)
-}
-
-func (c *Client) login(m map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/login", MapToJson(m)); err != nil {
- return nil, err
- } else {
- c.AuthToken = r.Header.Get(HEADER_TOKEN)
- c.AuthType = HEADER_BEARER
- sessionToken := getCookie(SESSION_COOKIE_TOKEN, r)
-
- if c.AuthToken != sessionToken.Value {
- NewAppError("/users/login", "model.client.login.app_error", nil, "", 0)
- }
-
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
- }
-}
-
-// Logout terminates the current user's session.
-func (c *Client) Logout() (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/logout", ""); err != nil {
- return nil, err
- } else {
- c.AuthToken = ""
- c.AuthType = HEADER_BEARER
- c.TeamId = ""
-
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-// CheckMfa returns a map with key "mfa_required" with the string value "true" or "false",
-// indicating whether MFA is required to log the user in, based on a provided login id
-// (username, email or some sort of SSO identifier based on configuration).
-func (c *Client) CheckMfa(loginId string) (*Result, *AppError) {
- m := make(map[string]string)
- m["login_id"] = loginId
-
- if r, err := c.DoApiPost("/users/mfa", MapToJson(m)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-// GenerateMfaSecret returns a QR code image containing the secret, to be scanned
-// by a multi-factor authentication mobile application. It also returns the secret
-// for manual entry. Must be authenticated.
-func (c *Client) GenerateMfaSecret() (*Result, *AppError) {
- if r, err := c.DoApiGet("/users/generate_mfa_secret", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-// UpdateMfa activates multi-factor authenticates for the current user if activate
-// is true and a valid token is provided. If activate is false, then token is not
-// required and multi-factor authentication is disabled for the current user.
-func (c *Client) UpdateMfa(activate bool, token string) (*Result, *AppError) {
- m := make(map[string]interface{})
- m["activate"] = activate
- m["token"] = token
-
- if r, err := c.DoApiPost("/users/update_mfa", StringInterfaceToJson(m)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) AdminResetMfa(userId string) (*Result, *AppError) {
- m := make(map[string]string)
- m["user_id"] = userId
-
- if r, err := c.DoApiPost("/admin/reset_mfa", MapToJson(m)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) RevokeSession(sessionAltId string) (*Result, *AppError) {
- m := make(map[string]string)
- m["id"] = sessionAltId
-
- if r, err := c.DoApiPost("/users/revoke_session", MapToJson(m)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetSessions(id string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/users/"+id+"/sessions", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), SessionsFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) EmailToOAuth(m map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/claim/email_to_oauth", MapToJson(m)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) OAuthToEmail(m map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/claim/oauth_to_email", MapToJson(m)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) LDAPToEmail(m map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/claim/ldap_to_email", MapToJson(m)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) EmailToLDAP(m map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/claim/ldap_to_email", MapToJson(m)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) Command(channelId string, command string) (*Result, *AppError) {
- args := &CommandArgs{ChannelId: channelId, Command: command}
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/commands/execute", args.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), CommandResponseFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) ListCommands() (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetTeamRoute()+"/commands/list", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), CommandListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) ListTeamCommands() (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetTeamRoute()+"/commands/list_team_commands", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), CommandListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) CreateCommand(cmd *Command) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/commands/create", cmd.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), CommandFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateCommand(cmd *Command) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/commands/update", cmd.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), CommandFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) RegenCommandToken(data map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/commands/regen_token", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), CommandFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) DeleteCommand(data map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/commands/delete", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetAudits(id string, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/users/"+id+"/audits", "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), AuditsFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetLogs() (*Result, *AppError) {
- if r, err := c.DoApiGet("/admin/logs", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ArrayFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetClusterStatus() ([]*ClusterInfo, *AppError) {
- if r, err := c.DoApiGet("/admin/cluster_status", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return ClusterInfosFromJson(r.Body), nil
- }
-}
-
-// GetRecentlyActiveUsers returns a map of users including lastActivityAt using user id as the key
-func (c *Client) GetRecentlyActiveUsers(teamId string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/admin/recently_active_users/"+teamId, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserMapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetAllAudits() (*Result, *AppError) {
- if r, err := c.DoApiGet("/admin/audits", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), AuditsFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetConfig() (*Result, *AppError) {
- if r, err := c.DoApiGet("/admin/config", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ConfigFromJson(r.Body)}, nil
- }
-}
-
-// ReloadConfig will reload the config.json file from disk. Properties
-// requiring a server restart will still need a server restart. You must
-// have the system admin role to call this method. It will return status=OK
-// if it's successfully reloaded the config file, otherwise check the returned error.
-func (c *Client) ReloadConfig() (bool, *AppError) {
- c.clearExtraProperties()
- if r, err := c.DoApiGet("/admin/reload_config", "", ""); err != nil {
- return false, err
- } else {
- c.fillInExtraProperties(r)
- return c.CheckStatusOK(r), nil
- }
-}
-
-func (c *Client) InvalidateAllCaches() (bool, *AppError) {
- c.clearExtraProperties()
- if r, err := c.DoApiGet("/admin/invalidate_all_caches", "", ""); err != nil {
- return false, err
- } else {
- c.fillInExtraProperties(r)
- return c.CheckStatusOK(r), nil
- }
-}
-
-func (c *Client) SaveConfig(config *Config) (*Result, *AppError) {
- if r, err := c.DoApiPost("/admin/save_config", config.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-// RecycleDatabaseConnection will attempt to recycle the database connections.
-// You must have the system admin role to call this method. It will return status=OK
-// if it's successfully recycled the connections, otherwise check the returned error.
-func (c *Client) RecycleDatabaseConnection() (bool, *AppError) {
- c.clearExtraProperties()
- if r, err := c.DoApiGet("/admin/recycle_db_conn", "", ""); err != nil {
- return false, err
- } else {
- c.fillInExtraProperties(r)
- return c.CheckStatusOK(r), nil
- }
-}
-
-func (c *Client) TestEmail(config *Config) (*Result, *AppError) {
- if r, err := c.DoApiPost("/admin/test_email", config.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-// TestLdap will run a connection test on the current LDAP settings.
-// It will return the standard OK response if settings work. Otherwise
-// it will return an appropriate error.
-func (c *Client) TestLdap(config *Config) (*Result, *AppError) {
- if r, err := c.DoApiPost("/admin/ldap_test", config.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetComplianceReports() (*Result, *AppError) {
- if r, err := c.DoApiGet("/admin/compliance_reports", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), CompliancesFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) SaveComplianceReport(job *Compliance) (*Result, *AppError) {
- if r, err := c.DoApiPost("/admin/save_compliance_report", job.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ComplianceFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) DownloadComplianceReport(id string) (*Result, *AppError) {
- var rq *http.Request
- rq, _ = http.NewRequest("GET", c.ApiUrl+"/admin/download_compliance_report/"+id, nil)
- rq.Close = true
-
- if len(c.AuthToken) > 0 {
- rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
- }
-
- if rp, err := c.HttpClient.Do(rq); err != nil {
- return nil, NewAppError("/admin/download_compliance_report", "model.client.connecting.app_error", nil, err.Error(), 0)
- } else if rp.StatusCode >= 300 {
- defer rp.Body.Close()
- return nil, AppErrorFromJson(rp.Body)
- } else {
- defer closeBody(rp)
- return &Result{rp.Header.Get(HEADER_REQUEST_ID),
- rp.Header.Get(HEADER_ETAG_SERVER), rp.Body}, nil
- }
-}
-
-func (c *Client) GetTeamAnalytics(teamId, name string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/admin/analytics/"+teamId+"/"+name, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), AnalyticsRowsFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetSystemAnalytics(name string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/admin/analytics/"+name, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), AnalyticsRowsFromJson(r.Body)}, nil
- }
-}
-
-// Initiate immediate synchronization of LDAP users.
-// The synchronization will be performed asynchronously and this function will
-// always return OK unless you don't have permissions.
-// You must be the system administrator to use this function.
-func (c *Client) LdapSyncNow() (*Result, *AppError) {
- if r, err := c.DoApiPost("/admin/ldap_sync_now", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) CreateChannel(channel *Channel) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/create", channel.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) CreateDirectChannel(userId string) (*Result, *AppError) {
- data := make(map[string]string)
- data["user_id"] = userId
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/create_direct", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) CreateGroupChannel(userIds []string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/create_group", ArrayToJson(userIds)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateChannel(channel *Channel) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/update", channel.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateChannelHeader(data map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/update_header", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateChannelPurpose(data map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/update_purpose", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateNotifyProps(data map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/update_notify_props", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetMyChannelMembers() (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetTeamRoute()+"/channels/members", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelMembersFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetChannel(id, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetChannelRoute(id)+"/", "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelDataFromJson(r.Body)}, nil
- }
-}
-
-// GetMoreChannelsPage will return a page of open channels the user is not in based on
-// the provided offset and limit. Must be authenticated.
-func (c *Client) GetMoreChannelsPage(offset int, limit int) (*Result, *AppError) {
- if r, err := c.DoApiGet(fmt.Sprintf(c.GetTeamRoute()+"/channels/more/%v/%v", offset, limit), "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelListFromJson(r.Body)}, nil
- }
-}
-
-// SearchMoreChannels will return a list of open channels the user is not in, that matches
-// the search criteria provided. Must be authenticated.
-func (c *Client) SearchMoreChannels(channelSearch ChannelSearch) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/more/search", channelSearch.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelListFromJson(r.Body)}, nil
- }
-}
-
-// AutocompleteChannels will return a list of open channels that match the provided
-// string. Must be authenticated.
-func (c *Client) AutocompleteChannels(term string) (*Result, *AppError) {
- url := fmt.Sprintf("%s/channels/autocomplete?term=%s", c.GetTeamRoute(), url.QueryEscape(term))
- if r, err := c.DoApiGet(url, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetChannelCounts(etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetTeamRoute()+"/channels/counts", "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelCountsFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetChannels(etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetTeamRoute()+"/channels/", "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetChannelByName(channelName string) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetChannelNameRoute(channelName), "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) JoinChannel(id string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetChannelRoute(id)+"/join", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), nil}, nil
- }
-}
-
-func (c *Client) JoinChannelByName(name string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetChannelNameRoute(name)+"/join", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), nil}, nil
- }
-}
-
-func (c *Client) LeaveChannel(id string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetChannelRoute(id)+"/leave", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), nil}, nil
- }
-}
-
-func (c *Client) DeleteChannel(id string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetChannelRoute(id)+"/delete", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), nil}, nil
- }
-}
-
-func (c *Client) AddChannelMember(id, user_id string) (*Result, *AppError) {
- data := make(map[string]string)
- data["user_id"] = user_id
- if r, err := c.DoApiPost(c.GetChannelRoute(id)+"/add", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), nil}, nil
- }
-}
-
-func (c *Client) RemoveChannelMember(id, user_id string) (*Result, *AppError) {
- data := make(map[string]string)
- data["user_id"] = user_id
- if r, err := c.DoApiPost(c.GetChannelRoute(id)+"/remove", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), nil}, nil
- }
-}
-
-// ViewChannel performs all the actions related to viewing a channel. This includes marking
-// the channel and the previous one as read, and marking the channel as being actively viewed.
-// ChannelId is required but may be blank to indicate no channel is being viewed.
-// PrevChannelId is optional, populate to indicate a channel switch occurred.
-func (c *Client) ViewChannel(params ChannelView) (bool, *ResponseMetadata) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/view", params.ToJson()); err != nil {
- return false, &ResponseMetadata{StatusCode: r.StatusCode, Error: err}
- } else {
- return c.CheckStatusOK(r),
- &ResponseMetadata{
- StatusCode: r.StatusCode,
- RequestId: r.Header.Get(HEADER_REQUEST_ID),
- Etag: r.Header.Get(HEADER_ETAG_SERVER),
- }
- }
-}
-
-func (c *Client) GetChannelStats(id string, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetChannelRoute(id)+"/stats", "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelStatsFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetChannelMember(channelId string, userId string) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/members/"+userId, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelMemberFromJson(r.Body)}, nil
- }
-}
-
-// GetChannelMembersByIds will return channel member objects as an array based on the
-// channel id and a list of user ids provided. Must be authenticated.
-func (c *Client) GetChannelMembersByIds(channelId string, userIds []string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/members/ids", ArrayToJson(userIds)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelMembersFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) CreatePost(post *Post) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetChannelRoute(post.ChannelId)+"/posts/create", post.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdatePost(post *Post) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetChannelRoute(post.ChannelId)+"/posts/update", post.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetPosts(channelId string, offset int, limit int, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/page/%v/%v", offset, limit), "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetPostsSince(channelId string, time int64) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/since/%v", time), "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetPostsBefore(channelId string, postid string, offset int, limit int, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/before/%v/%v", postid, offset, limit), "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetPostsAfter(channelId string, postid string, offset int, limit int, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(fmt.Sprintf(c.GetChannelRoute(channelId)+"/posts/%v/after/%v/%v", postid, offset, limit), "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetPost(channelId string, postId string, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/get", postId), "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil
- }
-}
-
-// GetPostById returns a post and any posts in the same thread by post id
-func (c *Client) GetPostById(postId string, etag string) (*PostList, *ResponseMetadata) {
- if r, err := c.DoApiGet(c.GetTeamRoute()+fmt.Sprintf("/posts/%v", postId), "", etag); err != nil {
- return nil, &ResponseMetadata{StatusCode: r.StatusCode, Error: err}
- } else {
- defer closeBody(r)
- return PostListFromJson(r.Body),
- &ResponseMetadata{
- StatusCode: r.StatusCode,
- RequestId: r.Header.Get(HEADER_REQUEST_ID),
- Etag: r.Header.Get(HEADER_ETAG_SERVER),
- }
- }
-}
-
-// GetPermalink returns a post list, based on the provided channel and post ID.
-func (c *Client) GetPermalink(channelId string, postId string, etag string) (*PostList, *ResponseMetadata) {
- if r, err := c.DoApiGet(c.GetTeamRoute()+fmt.Sprintf("/pltmp/%v", postId), "", etag); err != nil {
- return nil, &ResponseMetadata{StatusCode: r.StatusCode, Error: err}
- } else {
- defer closeBody(r)
- return PostListFromJson(r.Body),
- &ResponseMetadata{
- StatusCode: r.StatusCode,
- RequestId: r.Header.Get(HEADER_REQUEST_ID),
- Etag: r.Header.Get(HEADER_ETAG_SERVER),
- }
- }
-}
-
-func (c *Client) DeletePost(channelId string, postId string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/delete", postId), ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) SearchPosts(terms string, isOrSearch bool) (*Result, *AppError) {
- data := map[string]interface{}{}
- data["terms"] = terms
- data["is_or_search"] = isOrSearch
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/posts/search", StringInterfaceToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil
- }
-}
-
-// GetFlaggedPosts will return a post list of posts that have been flagged by the user.
-// The page is set by the integer parameters offset and limit.
-func (c *Client) GetFlaggedPosts(offset int, limit int) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetTeamRoute()+fmt.Sprintf("/posts/flagged/%v/%v", offset, limit), "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetPinnedPosts(channelId string) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/pinned", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UploadProfileFile(data []byte, contentType string) (*Result, *AppError) {
- return c.uploadFile(c.ApiUrl+"/users/newimage", data, contentType)
-}
-
-func (c *Client) UploadPostAttachment(data []byte, channelId string, filename string) (*FileUploadResponse, *AppError) {
- c.clearExtraProperties()
-
- body := &bytes.Buffer{}
- writer := multipart.NewWriter(body)
-
- if part, err := writer.CreateFormFile("files", filename); err != nil {
- return nil, NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.file.app_error", nil, err.Error(), 0)
- } else if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil {
- return nil, NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.file.app_error", nil, err.Error(), 0)
- }
-
- if part, err := writer.CreateFormField("channel_id"); err != nil {
- return nil, NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.channel_id.app_error", nil, err.Error(), 0)
- } else if _, err = io.Copy(part, strings.NewReader(channelId)); err != nil {
- return nil, NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.channel_id.app_error", nil, err.Error(), 0)
- }
-
- if err := writer.Close(); err != nil {
- return nil, NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.writer.app_error", nil, err.Error(), 0)
- }
-
- if result, err := c.uploadFile(c.ApiUrl+c.GetTeamRoute()+"/files/upload", body.Bytes(), writer.FormDataContentType()); err != nil {
- return nil, err
- } else {
- return result.Data.(*FileUploadResponse), nil
- }
-}
-
-func (c *Client) uploadFile(url string, data []byte, contentType string) (*Result, *AppError) {
- rq, _ := http.NewRequest("POST", url, bytes.NewReader(data))
- rq.Header.Set("Content-Type", contentType)
- rq.Close = true
-
- if len(c.AuthToken) > 0 {
- rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
- }
-
- if rp, err := c.HttpClient.Do(rq); err != nil {
- return nil, NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0)
- } else if rp.StatusCode >= 300 {
- return nil, AppErrorFromJson(rp.Body)
- } else {
- defer closeBody(rp)
- return &Result{rp.Header.Get(HEADER_REQUEST_ID),
- rp.Header.Get(HEADER_ETAG_SERVER), FileUploadResponseFromJson(rp.Body)}, nil
- }
-}
-
-func (c *Client) GetFile(fileId string) (io.ReadCloser, *AppError) {
- if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/get", "", ""); err != nil {
- return nil, err
- } else {
- c.fillInExtraProperties(r)
- return r.Body, nil
- }
-}
-
-func (c *Client) GetFileThumbnail(fileId string) (io.ReadCloser, *AppError) {
- if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/get_thumbnail", "", ""); err != nil {
- return nil, err
- } else {
- c.fillInExtraProperties(r)
- return r.Body, nil
- }
-}
-
-func (c *Client) GetFilePreview(fileId string) (io.ReadCloser, *AppError) {
- if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/get_preview", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return r.Body, nil
- }
-}
-
-func (c *Client) GetFileInfo(fileId string) (*FileInfo, *AppError) {
- if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/get_info", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return FileInfoFromJson(r.Body), nil
- }
-}
-
-func (c *Client) GetPublicLink(fileId string) (string, *AppError) {
- if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/get_public_link", "", ""); err != nil {
- return "", err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return StringFromJson(r.Body), nil
- }
-}
-
-func (c *Client) UpdateUser(user *User) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/update", user.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateUserRoles(userId string, roles string) (*Result, *AppError) {
- data := make(map[string]string)
- data["new_roles"] = roles
-
- if r, err := c.DoApiPost(c.GetUserRequiredRoute(userId)+"/update_roles", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateTeamRoles(userId string, roles string) (*Result, *AppError) {
- data := make(map[string]string)
- data["new_roles"] = roles
- data["user_id"] = userId
-
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/update_member_roles", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) AttachDeviceId(deviceId string) (*Result, *AppError) {
- data := make(map[string]string)
- data["device_id"] = deviceId
- if r, err := c.DoApiPost("/users/attach_device", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateActive(userId string, active bool) (*Result, *AppError) {
- data := make(map[string]string)
- data["user_id"] = userId
- data["active"] = strconv.FormatBool(active)
- if r, err := c.DoApiPost("/users/update_active", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateUserNotify(data map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/update_notify", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateUserPassword(userId, currentPassword, newPassword string) (*Result, *AppError) {
- data := make(map[string]string)
- data["current_password"] = currentPassword
- data["new_password"] = newPassword
- data["user_id"] = userId
-
- if r, err := c.DoApiPost("/users/newpassword", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) SendPasswordReset(email string) (*Result, *AppError) {
- data := map[string]string{}
- data["email"] = email
- if r, err := c.DoApiPost("/users/send_password_reset", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) ResetPassword(code, newPassword string) (*Result, *AppError) {
- data := map[string]string{}
- data["code"] = code
- data["new_password"] = newPassword
- if r, err := c.DoApiPost("/users/reset_password", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) AdminResetPassword(userId, newPassword string) (*Result, *AppError) {
- data := map[string]string{}
- data["user_id"] = userId
- data["new_password"] = newPassword
- if r, err := c.DoApiPost("/admin/reset_password", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-// GetStatuses returns a map of string statuses using user id as the key
-func (c *Client) GetStatuses() (*Result, *AppError) {
- if r, err := c.DoApiGet("/users/status", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-// GetStatusesByIds returns a map of string statuses using user id as the key,
-// based on the provided user ids
-func (c *Client) GetStatusesByIds(userIds []string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/status/ids", ArrayToJson(userIds)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetMyTeam(etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetTeamRoute()+"/me", "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamFromJson(r.Body)}, nil
- }
-}
-
-// GetTeamMembers will return a page of team member objects as an array paged based on the
-// team id, offset and limit provided. Must be authenticated.
-func (c *Client) GetTeamMembers(teamId string, offset int, limit int) (*Result, *AppError) {
- if r, err := c.DoApiGet(fmt.Sprintf("/teams/%v/members/%v/%v", teamId, offset, limit), "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamMembersFromJson(r.Body)}, nil
- }
-}
-
-// GetMyTeamMembers will return an array with team member objects that the current user
-// is a member of. Must be authenticated.
-func (c *Client) GetMyTeamMembers() (*Result, *AppError) {
- if r, err := c.DoApiGet("/teams/members", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamMembersFromJson(r.Body)}, nil
- }
-}
-
-// GetMyTeamsUnread will return an array with TeamUnread objects that contain the amount of
-// unread messages and mentions the current user has for the teams it belongs to.
-// An optional team ID can be set to exclude that team from the results. Must be authenticated.
-func (c *Client) GetMyTeamsUnread(teamId string) (*Result, *AppError) {
- endpoint := "/teams/unread"
-
- if teamId != "" {
- endpoint += fmt.Sprintf("?id=%s", url.QueryEscape(teamId))
- }
- if r, err := c.DoApiGet(endpoint, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamsUnreadFromJson(r.Body)}, nil
- }
-}
-
-// GetTeamMember will return a team member object based on the team id and user id provided.
-// Must be authenticated.
-func (c *Client) GetTeamMember(teamId string, userId string) (*Result, *AppError) {
- if r, err := c.DoApiGet(fmt.Sprintf("/teams/%v/members/%v", teamId, userId), "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamMemberFromJson(r.Body)}, nil
- }
-}
-
-// GetTeamStats will return a team stats object containing the number of users on the team
-// based on the team id provided. Must be authenticated.
-func (c *Client) GetTeamStats(teamId string) (*Result, *AppError) {
- if r, err := c.DoApiGet(fmt.Sprintf("/teams/%v/stats", teamId), "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamStatsFromJson(r.Body)}, nil
- }
-}
-
-// GetTeamByName will return a team object based on the team name provided. Must be authenticated.
-func (c *Client) GetTeamByName(teamName string) (*Result, *AppError) {
- if r, err := c.DoApiGet(fmt.Sprintf("/teams/name/%v", teamName), "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamFromJson(r.Body)}, nil
- }
-}
-
-// GetTeamMembersByIds will return team member objects as an array based on the
-// team id and a list of user ids provided. Must be authenticated.
-func (c *Client) GetTeamMembersByIds(teamId string, userIds []string) (*Result, *AppError) {
- if r, err := c.DoApiPost(fmt.Sprintf("/teams/%v/members/ids", teamId), ArrayToJson(userIds)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamMembersFromJson(r.Body)}, nil
- }
-}
-
-// RegisterApp creates a new OAuth2 app to be used with the OAuth2 Provider. On success
-// it returns the created app. Must be authenticated as a user.
-func (c *Client) RegisterApp(app *OAuthApp) (*Result, *AppError) {
- if r, err := c.DoApiPost("/oauth/register", app.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), OAuthAppFromJson(r.Body)}, nil
- }
-}
-
-// AllowOAuth allows a new session by an OAuth2 App. On success
-// it returns the url to be redirected back to the app which initiated the oauth2 flow.
-// Must be authenticated as a user.
-func (c *Client) AllowOAuth(rspType, clientId, redirect, scope, state string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/oauth/allow?response_type="+rspType+"&client_id="+clientId+"&redirect_uri="+url.QueryEscape(redirect)+"&scope="+scope+"&state="+url.QueryEscape(state), "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-// GetOAuthAppsByUser returns the OAuth2 Apps registered by the user. On success
-// it returns a list of OAuth2 Apps from the same user or all the registered apps if the user
-// is a System Administrator. Must be authenticated as a user.
-func (c *Client) GetOAuthAppsByUser() (*Result, *AppError) {
- if r, err := c.DoApiGet("/oauth/list", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), OAuthAppListFromJson(r.Body)}, nil
- }
-}
-
-// GetOAuthAppInfo lookup an OAuth2 App using the client_id. On success
-// it returns a Sanitized OAuth2 App. Must be authenticated as a user.
-func (c *Client) GetOAuthAppInfo(clientId string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/oauth/app/"+clientId, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), OAuthAppFromJson(r.Body)}, nil
- }
-}
-
-// DeleteOAuthApp deletes an OAuth2 app, the app must be deleted by the same user who created it or
-// a System Administrator. On success returs Status OK. Must be authenticated as a user.
-func (c *Client) DeleteOAuthApp(id string) (*Result, *AppError) {
- data := make(map[string]string)
- data["id"] = id
- if r, err := c.DoApiPost("/oauth/delete", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-// GetOAuthAuthorizedApps returns the OAuth2 Apps authorized by the user. On success
-// it returns a list of sanitized OAuth2 Authorized Apps by the user.
-func (c *Client) GetOAuthAuthorizedApps() (*Result, *AppError) {
- if r, err := c.DoApiGet("/oauth/authorized", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), OAuthAppListFromJson(r.Body)}, nil
- }
-}
-
-// OAuthDeauthorizeApp deauthorize a user an OAuth 2.0 app. On success
-// it returns status OK or an AppError on fail.
-func (c *Client) OAuthDeauthorizeApp(clientId string) *AppError {
- if r, err := c.DoApiPost("/oauth/"+clientId+"/deauthorize", ""); err != nil {
- return err
- } else {
- defer closeBody(r)
- return nil
- }
-}
-
-// RegenerateOAuthAppSecret generates a new OAuth App Client Secret. On success
-// it returns an OAuth2 App. Must be authenticated as a user and the same user who
-// registered the app or a System Admin.
-func (c *Client) RegenerateOAuthAppSecret(clientId string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/oauth/"+clientId+"/regen_secret", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), OAuthAppFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetAccessToken(data url.Values) (*Result, *AppError) {
- if r, err := c.DoPost("/oauth/access_token", data.Encode(), "application/x-www-form-urlencoded"); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), AccessResponseFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) CreateIncomingWebhook(hook *IncomingWebhook) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/incoming/create", hook.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), IncomingWebhookFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateIncomingWebhook(hook *IncomingWebhook) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/incoming/update", hook.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), IncomingWebhookFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) PostToWebhook(id, payload string) (*Result, *AppError) {
- if r, err := c.DoPost("/hooks/"+id, payload, "application/x-www-form-urlencoded"); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), nil}, nil
- }
-}
-
-func (c *Client) DeleteIncomingWebhook(id string) (*Result, *AppError) {
- data := make(map[string]string)
- data["id"] = id
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/incoming/delete", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) ListIncomingWebhooks() (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetTeamRoute()+"/hooks/incoming/list", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), IncomingWebhookListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetAllPreferences() (*Result, *AppError) {
- if r, err := c.DoApiGet("/preferences/", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- preferences, _ := PreferencesFromJson(r.Body)
- return &Result{r.Header.Get(HEADER_REQUEST_ID), r.Header.Get(HEADER_ETAG_SERVER), preferences}, nil
- }
-}
-
-func (c *Client) SetPreferences(preferences *Preferences) (*Result, *AppError) {
- if r, err := c.DoApiPost("/preferences/save", preferences.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), preferences}, nil
- }
-}
-
-func (c *Client) GetPreference(category string, name string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/preferences/"+category+"/"+name, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID), r.Header.Get(HEADER_ETAG_SERVER), PreferenceFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetPreferenceCategory(category string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/preferences/"+category, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- preferences, _ := PreferencesFromJson(r.Body)
- return &Result{r.Header.Get(HEADER_REQUEST_ID), r.Header.Get(HEADER_ETAG_SERVER), preferences}, nil
- }
-}
-
-// DeletePreferences deletes a list of preferences owned by the current user. If successful,
-// it will return status=ok. Otherwise, an error will be returned.
-func (c *Client) DeletePreferences(preferences *Preferences) (bool, *AppError) {
- if r, err := c.DoApiPost("/preferences/delete", preferences.ToJson()); err != nil {
- return false, err
- } else {
- return c.CheckStatusOK(r), nil
- }
-}
-
-func (c *Client) CreateOutgoingWebhook(hook *OutgoingWebhook) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/outgoing/create", hook.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), OutgoingWebhookFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateOutgoingWebhook(hook *OutgoingWebhook) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/outgoing/update", hook.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), OutgoingWebhookFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) DeleteOutgoingWebhook(id string) (*Result, *AppError) {
- data := make(map[string]string)
- data["id"] = id
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/outgoing/delete", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) ListOutgoingWebhooks() (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetTeamRoute()+"/hooks/outgoing/list", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), OutgoingWebhookListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) RegenOutgoingWebhookToken(id string) (*Result, *AppError) {
- data := make(map[string]string)
- data["id"] = id
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/outgoing/regen_token", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), OutgoingWebhookFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) MockSession(sessionToken string) {
- c.AuthToken = sessionToken
- c.AuthType = HEADER_BEARER
-}
-
-func (c *Client) GetClientLicenceConfig(etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/license/client_config", "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetInitialLoad() (*Result, *AppError) {
- if r, err := c.DoApiGet("/users/initial_load", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), InitialLoadFromJson(r.Body)}, nil
- }
-}
-
-// ListEmoji returns a list of all user-created emoji for the server.
-func (c *Client) ListEmoji() ([]*Emoji, *AppError) {
- if r, err := c.DoApiGet(c.GetEmojiRoute()+"/list", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return EmojiListFromJson(r.Body), nil
- }
-}
-
-// CreateEmoji will save an emoji to the server if the current user has permission
-// to do so. If successful, the provided emoji will be returned with its Id field
-// filled in. Otherwise, an error will be returned.
-func (c *Client) CreateEmoji(emoji *Emoji, image []byte, filename string) (*Emoji, *AppError) {
- c.clearExtraProperties()
-
- body := &bytes.Buffer{}
- writer := multipart.NewWriter(body)
-
- if part, err := writer.CreateFormFile("image", filename); err != nil {
- return nil, NewAppError("CreateEmoji", "model.client.create_emoji.image.app_error", nil, err.Error(), 0)
- } else if _, err = io.Copy(part, bytes.NewBuffer(image)); err != nil {
- return nil, NewAppError("CreateEmoji", "model.client.create_emoji.image.app_error", nil, err.Error(), 0)
- }
-
- if err := writer.WriteField("emoji", emoji.ToJson()); err != nil {
- return nil, NewAppError("CreateEmoji", "model.client.create_emoji.emoji.app_error", nil, err.Error(), 0)
- }
-
- if err := writer.Close(); err != nil {
- return nil, NewAppError("CreateEmoji", "model.client.create_emoji.writer.app_error", nil, err.Error(), 0)
- }
-
- rq, _ := http.NewRequest("POST", c.ApiUrl+c.GetEmojiRoute()+"/create", body)
- rq.Header.Set("Content-Type", writer.FormDataContentType())
- rq.Close = true
-
- if len(c.AuthToken) > 0 {
- rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
- }
-
- if r, err := c.HttpClient.Do(rq); err != nil {
- return nil, NewAppError("CreateEmoji", "model.client.connecting.app_error", nil, err.Error(), 0)
- } else if r.StatusCode >= 300 {
- return nil, AppErrorFromJson(r.Body)
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return EmojiFromJson(r.Body), nil
- }
-}
-
-// DeleteEmoji will delete an emoji from the server if the current user has permission
-// to do so. If successful, it will return status=ok. Otherwise, an error will be returned.
-func (c *Client) DeleteEmoji(id string) (bool, *AppError) {
- data := map[string]string{"id": id}
-
- if r, err := c.DoApiPost(c.GetEmojiRoute()+"/delete", MapToJson(data)); err != nil {
- return false, err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return c.CheckStatusOK(r), nil
- }
-}
-
-// GetCustomEmojiImageUrl returns the API route that can be used to get the image used by
-// the given emoji.
-func (c *Client) GetCustomEmojiImageUrl(id string) string {
- return c.GetEmojiRoute() + "/" + id
-}
-
-// Uploads a x509 base64 Certificate or Private Key file to be used with SAML.
-// data byte array is required and needs to be a Multi-Part with 'certificate' as the field name
-// contentType is also required. Returns nil if succesful, otherwise returns an AppError
-func (c *Client) UploadCertificateFile(data []byte, contentType string) *AppError {
- url := c.ApiUrl + "/admin/add_certificate"
- rq, _ := http.NewRequest("POST", url, bytes.NewReader(data))
- rq.Header.Set("Content-Type", contentType)
- rq.Close = true
-
- if len(c.AuthToken) > 0 {
- rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
- }
-
- if rp, err := c.HttpClient.Do(rq); err != nil {
- return NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0)
- } else if rp.StatusCode >= 300 {
- return AppErrorFromJson(rp.Body)
- } else {
- defer closeBody(rp)
- c.fillInExtraProperties(rp)
- return nil
- }
-}
-
-// Removes a x509 base64 Certificate or Private Key file used with SAML.
-// filename is required. Returns nil if successful, otherwise returns an AppError
-func (c *Client) RemoveCertificateFile(filename string) *AppError {
- if r, err := c.DoApiPost("/admin/remove_certificate", MapToJson(map[string]string{"filename": filename})); err != nil {
- return err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return nil
- }
-}
-
-// Checks if the x509 base64 Certificates and Private Key files used with SAML exists on the file system.
-// Returns a map[string]interface{} if successful, otherwise returns an AppError. Must be System Admin authenticated.
-func (c *Client) SamlCertificateStatus(filename string) (map[string]interface{}, *AppError) {
- if r, err := c.DoApiGet("/admin/remove_certificate", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return StringInterfaceFromJson(r.Body), nil
- }
-}
-
-// GetWebrtcToken if Successful returns a map with a valid token, stun server and turn server with credentials to use with
-// the Mattermost WebRTC service, otherwise returns an AppError. Must be authenticated user.
-func (c *Client) GetWebrtcToken() (map[string]string, *AppError) {
- if r, err := c.DoApiPost("/webrtc/token", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return MapFromJson(r.Body), nil
- }
-}
-
-// GetFileInfosForPost returns a list of FileInfo objects for a given post id, if successful.
-// Otherwise, it returns an error.
-func (c *Client) GetFileInfosForPost(channelId string, postId string, etag string) ([]*FileInfo, *AppError) {
- c.clearExtraProperties()
-
- if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/get_file_infos", postId), "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return FileInfosFromJson(r.Body), nil
- }
-}
-
-// Saves an emoji reaction for a post in the given channel. Returns the saved reaction if successful, otherwise returns an AppError.
-func (c *Client) SaveReaction(channelId string, reaction *Reaction) (*Reaction, *AppError) {
- if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/reactions/save", reaction.PostId), reaction.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return ReactionFromJson(r.Body), nil
- }
-}
-
-// Removes an emoji reaction for a post in the given channel. Returns nil if successful, otherwise returns an AppError.
-func (c *Client) DeleteReaction(channelId string, reaction *Reaction) *AppError {
- if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/reactions/delete", reaction.PostId), reaction.ToJson()); err != nil {
- return err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return nil
- }
-}
-
-// Lists all emoji reactions made for the given post in the given channel. Returns a list of Reactions if successful, otherwise returns an AppError.
-func (c *Client) ListReactions(channelId string, postId string) ([]*Reaction, *AppError) {
- if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/reactions", postId), "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return ReactionsFromJson(r.Body), nil
- }
-}
-
-// Updates the user's roles in the channel by replacing them with the roles provided.
-func (c *Client) UpdateChannelRoles(channelId string, userId string, roles string) (map[string]string, *ResponseMetadata) {
- data := make(map[string]string)
- data["new_roles"] = roles
- data["user_id"] = userId
-
- if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/update_member_roles", MapToJson(data)); err != nil {
- metadata := ResponseMetadata{Error: err}
- if r != nil {
- metadata.StatusCode = r.StatusCode
- }
- return nil, &metadata
- } else {
- defer closeBody(r)
- return MapFromJson(r.Body),
- &ResponseMetadata{
- StatusCode: r.StatusCode,
- RequestId: r.Header.Get(HEADER_REQUEST_ID),
- Etag: r.Header.Get(HEADER_ETAG_SERVER),
- }
- }
-}
-
-func (c *Client) PinPost(channelId string, postId string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/posts/"+postId+"/pin", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UnpinPost(channelId string, postId string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/posts/"+postId+"/unpin", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostFromJson(r.Body)}, nil
- }
-}
diff --git a/vendor/github.com/mattermost/platform/model/message_export.go b/vendor/github.com/mattermost/platform/model/message_export.go
deleted file mode 100644
index b59b114d..00000000
--- a/vendor/github.com/mattermost/platform/model/message_export.go
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-package model
-
-type MessageExport struct {
- ChannelId *string
- ChannelDisplayName *string
-
- UserId *string
- UserEmail *string
-
- PostId *string
- PostCreateAt *int64
- PostMessage *string
- PostType *string
- PostFileIds StringArray
-}
diff --git a/vendor/github.com/mattermost/platform/model/post.go b/vendor/github.com/mattermost/platform/model/post.go
deleted file mode 100644
index 6b282fbf..00000000
--- a/vendor/github.com/mattermost/platform/model/post.go
+++ /dev/null
@@ -1,394 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-package model
-
-import (
- "encoding/json"
- "io"
- "net/http"
- "regexp"
- "strings"
- "unicode/utf8"
-)
-
-const (
- POST_SYSTEM_MESSAGE_PREFIX = "system_"
- POST_DEFAULT = ""
- POST_SLACK_ATTACHMENT = "slack_attachment"
- POST_SYSTEM_GENERIC = "system_generic"
- POST_JOIN_LEAVE = "system_join_leave" // Deprecated, use POST_JOIN_CHANNEL or POST_LEAVE_CHANNEL instead
- POST_JOIN_CHANNEL = "system_join_channel"
- POST_LEAVE_CHANNEL = "system_leave_channel"
- POST_JOIN_TEAM = "system_join_team"
- POST_LEAVE_TEAM = "system_leave_team"
- POST_ADD_REMOVE = "system_add_remove" // Deprecated, use POST_ADD_TO_CHANNEL or POST_REMOVE_FROM_CHANNEL instead
- POST_ADD_TO_CHANNEL = "system_add_to_channel"
- POST_REMOVE_FROM_CHANNEL = "system_remove_from_channel"
- POST_ADD_TO_TEAM = "system_add_to_team"
- POST_REMOVE_FROM_TEAM = "system_remove_from_team"
- POST_HEADER_CHANGE = "system_header_change"
- POST_DISPLAYNAME_CHANGE = "system_displayname_change"
- POST_PURPOSE_CHANGE = "system_purpose_change"
- POST_CHANNEL_DELETED = "system_channel_deleted"
- POST_EPHEMERAL = "system_ephemeral"
- POST_FILEIDS_MAX_RUNES = 150
- POST_FILENAMES_MAX_RUNES = 4000
- POST_HASHTAGS_MAX_RUNES = 1000
- POST_MESSAGE_MAX_RUNES = 4000
- POST_PROPS_MAX_RUNES = 8000
- POST_PROPS_MAX_USER_RUNES = POST_PROPS_MAX_RUNES - 400 // Leave some room for system / pre-save modifications
- POST_CUSTOM_TYPE_PREFIX = "custom_"
- PROPS_ADD_CHANNEL_MEMBER = "add_channel_member"
-)
-
-type Post struct {
- Id string `json:"id"`
- CreateAt int64 `json:"create_at"`
- UpdateAt int64 `json:"update_at"`
- EditAt int64 `json:"edit_at"`
- DeleteAt int64 `json:"delete_at"`
- IsPinned bool `json:"is_pinned"`
- UserId string `json:"user_id"`
- ChannelId string `json:"channel_id"`
- RootId string `json:"root_id"`
- ParentId string `json:"parent_id"`
- OriginalId string `json:"original_id"`
- Message string `json:"message"`
- Type string `json:"type"`
- Props StringInterface `json:"props"`
- Hashtags string `json:"hashtags"`
- Filenames StringArray `json:"filenames,omitempty"` // Deprecated, do not use this field any more
- FileIds StringArray `json:"file_ids,omitempty"`
- PendingPostId string `json:"pending_post_id" db:"-"`
- HasReactions bool `json:"has_reactions,omitempty"`
-}
-
-type PostPatch struct {
- IsPinned *bool `json:"is_pinned"`
- Message *string `json:"message"`
- Props *StringInterface `json:"props"`
- FileIds *StringArray `json:"file_ids"`
- HasReactions *bool `json:"has_reactions"`
-}
-
-type PostForIndexing struct {
- Post
- TeamId string `json:"team_id"`
- ParentCreateAt *int64 `json:"parent_create_at"`
-}
-
-type PostAction struct {
- Id string `json:"id"`
- Name string `json:"name"`
- Integration *PostActionIntegration `json:"integration,omitempty"`
-}
-
-type PostActionIntegration struct {
- URL string `json:"url,omitempty"`
- Context StringInterface `json:"context,omitempty"`
-}
-
-type PostActionIntegrationRequest struct {
- UserId string `json:"user_id"`
- Context StringInterface `json:"context,omitempty"`
-}
-
-type PostActionIntegrationResponse struct {
- Update *Post `json:"update"`
- EphemeralText string `json:"ephemeral_text"`
-}
-
-func (o *Post) ToJson() string {
- copy := *o
- copy.StripActionIntegrations()
- b, err := json.Marshal(©)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
-}
-
-func PostFromJson(data io.Reader) *Post {
- decoder := json.NewDecoder(data)
- var o Post
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
-}
-
-func (o *Post) Etag() string {
- return Etag(o.Id, o.UpdateAt)
-}
-
-func (o *Post) IsValid() *AppError {
-
- if len(o.Id) != 26 {
- return NewAppError("Post.IsValid", "model.post.is_valid.id.app_error", nil, "", http.StatusBadRequest)
- }
-
- if o.CreateAt == 0 {
- return NewAppError("Post.IsValid", "model.post.is_valid.create_at.app_error", nil, "id="+o.Id, http.StatusBadRequest)
- }
-
- if o.UpdateAt == 0 {
- return NewAppError("Post.IsValid", "model.post.is_valid.update_at.app_error", nil, "id="+o.Id, http.StatusBadRequest)
- }
-
- if len(o.UserId) != 26 {
- return NewAppError("Post.IsValid", "model.post.is_valid.user_id.app_error", nil, "", http.StatusBadRequest)
- }
-
- if len(o.ChannelId) != 26 {
- return NewAppError("Post.IsValid", "model.post.is_valid.channel_id.app_error", nil, "", http.StatusBadRequest)
- }
-
- if !(len(o.RootId) == 26 || len(o.RootId) == 0) {
- return NewAppError("Post.IsValid", "model.post.is_valid.root_id.app_error", nil, "", http.StatusBadRequest)
- }
-
- if !(len(o.ParentId) == 26 || len(o.ParentId) == 0) {
- return NewAppError("Post.IsValid", "model.post.is_valid.parent_id.app_error", nil, "", http.StatusBadRequest)
- }
-
- if len(o.ParentId) == 26 && len(o.RootId) == 0 {
- return NewAppError("Post.IsValid", "model.post.is_valid.root_parent.app_error", nil, "", http.StatusBadRequest)
- }
-
- if !(len(o.OriginalId) == 26 || len(o.OriginalId) == 0) {
- return NewAppError("Post.IsValid", "model.post.is_valid.original_id.app_error", nil, "", http.StatusBadRequest)
- }
-
- if utf8.RuneCountInString(o.Message) > POST_MESSAGE_MAX_RUNES {
- return NewAppError("Post.IsValid", "model.post.is_valid.msg.app_error", nil, "id="+o.Id, http.StatusBadRequest)
- }
-
- if utf8.RuneCountInString(o.Hashtags) > POST_HASHTAGS_MAX_RUNES {
- return NewAppError("Post.IsValid", "model.post.is_valid.hashtags.app_error", nil, "id="+o.Id, http.StatusBadRequest)
- }
-
- switch o.Type {
- case
- POST_DEFAULT,
- POST_JOIN_LEAVE,
- POST_ADD_REMOVE,
- POST_JOIN_CHANNEL,
- POST_LEAVE_CHANNEL,
- POST_JOIN_TEAM,
- POST_LEAVE_TEAM,
- POST_ADD_TO_CHANNEL,
- POST_REMOVE_FROM_CHANNEL,
- POST_ADD_TO_TEAM,
- POST_REMOVE_FROM_TEAM,
- POST_SLACK_ATTACHMENT,
- POST_HEADER_CHANGE,
- POST_PURPOSE_CHANGE,
- POST_DISPLAYNAME_CHANGE,
- POST_CHANNEL_DELETED:
- default:
- if !strings.HasPrefix(o.Type, POST_CUSTOM_TYPE_PREFIX) {
- return NewAppError("Post.IsValid", "model.post.is_valid.type.app_error", nil, "id="+o.Type, http.StatusBadRequest)
- }
- }
-
- if utf8.RuneCountInString(ArrayToJson(o.Filenames)) > POST_FILENAMES_MAX_RUNES {
- return NewAppError("Post.IsValid", "model.post.is_valid.filenames.app_error", nil, "id="+o.Id, http.StatusBadRequest)
- }
-
- if utf8.RuneCountInString(ArrayToJson(o.FileIds)) > POST_FILEIDS_MAX_RUNES {
- return NewAppError("Post.IsValid", "model.post.is_valid.file_ids.app_error", nil, "id="+o.Id, http.StatusBadRequest)
- }
-
- if utf8.RuneCountInString(StringInterfaceToJson(o.Props)) > POST_PROPS_MAX_RUNES {
- return NewAppError("Post.IsValid", "model.post.is_valid.props.app_error", nil, "id="+o.Id, http.StatusBadRequest)
- }
-
- return nil
-}
-
-func (o *Post) SanitizeProps() {
- membersToSanitize := []string{
- PROPS_ADD_CHANNEL_MEMBER,
- }
-
- for _, member := range membersToSanitize {
- if _, ok := o.Props[member]; ok {
- delete(o.Props, member)
- }
- }
-}
-
-func (o *Post) PreSave() {
- if o.Id == "" {
- o.Id = NewId()
- }
-
- o.OriginalId = ""
-
- if o.CreateAt == 0 {
- o.CreateAt = GetMillis()
- }
-
- o.UpdateAt = o.CreateAt
- o.PreCommit()
-}
-
-func (o *Post) PreCommit() {
- if o.Props == nil {
- o.Props = make(map[string]interface{})
- }
-
- if o.Filenames == nil {
- o.Filenames = []string{}
- }
-
- if o.FileIds == nil {
- o.FileIds = []string{}
- }
-
- o.GenerateActionIds()
-}
-
-func (o *Post) MakeNonNil() {
- if o.Props == nil {
- o.Props = make(map[string]interface{})
- }
-}
-
-func (o *Post) AddProp(key string, value interface{}) {
-
- o.MakeNonNil()
-
- o.Props[key] = value
-}
-
-func (o *Post) IsSystemMessage() bool {
- return len(o.Type) >= len(POST_SYSTEM_MESSAGE_PREFIX) && o.Type[:len(POST_SYSTEM_MESSAGE_PREFIX)] == POST_SYSTEM_MESSAGE_PREFIX
-}
-
-func (p *Post) Patch(patch *PostPatch) {
- if patch.IsPinned != nil {
- p.IsPinned = *patch.IsPinned
- }
-
- if patch.Message != nil {
- p.Message = *patch.Message
- }
-
- if patch.Props != nil {
- p.Props = *patch.Props
- }
-
- if patch.FileIds != nil {
- p.FileIds = *patch.FileIds
- }
-
- if patch.HasReactions != nil {
- p.HasReactions = *patch.HasReactions
- }
-}
-
-func (o *PostPatch) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- }
-
- return string(b)
-}
-
-func PostPatchFromJson(data io.Reader) *PostPatch {
- decoder := json.NewDecoder(data)
- var post PostPatch
- err := decoder.Decode(&post)
- if err != nil {
- return nil
- }
-
- return &post
-}
-
-var channelMentionRegexp = regexp.MustCompile(`\B~[a-zA-Z0-9\-_]+`)
-
-func (o *Post) ChannelMentions() (names []string) {
- if strings.Contains(o.Message, "~") {
- alreadyMentioned := make(map[string]bool)
- for _, match := range channelMentionRegexp.FindAllString(o.Message, -1) {
- name := match[1:]
- if !alreadyMentioned[name] {
- names = append(names, name)
- alreadyMentioned[name] = true
- }
- }
- }
- return
-}
-
-func (r *PostActionIntegrationRequest) ToJson() string {
- b, err := json.Marshal(r)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
-}
-
-func (o *Post) Attachments() []*SlackAttachment {
- if attachments, ok := o.Props["attachments"].([]*SlackAttachment); ok {
- return attachments
- }
- var ret []*SlackAttachment
- if attachments, ok := o.Props["attachments"].([]interface{}); ok {
- for _, attachment := range attachments {
- if enc, err := json.Marshal(attachment); err == nil {
- var decoded SlackAttachment
- if json.Unmarshal(enc, &decoded) == nil {
- ret = append(ret, &decoded)
- }
- }
- }
- }
- return ret
-}
-
-func (o *Post) StripActionIntegrations() {
- attachments := o.Attachments()
- if o.Props["attachments"] != nil {
- o.Props["attachments"] = attachments
- }
- for _, attachment := range attachments {
- for _, action := range attachment.Actions {
- action.Integration = nil
- }
- }
-}
-
-func (o *Post) GetAction(id string) *PostAction {
- for _, attachment := range o.Attachments() {
- for _, action := range attachment.Actions {
- if action.Id == id {
- return action
- }
- }
- }
- return nil
-}
-
-func (o *Post) GenerateActionIds() {
- if o.Props["attachments"] != nil {
- o.Props["attachments"] = o.Attachments()
- }
- if attachments, ok := o.Props["attachments"].([]*SlackAttachment); ok {
- for _, attachment := range attachments {
- for _, action := range attachment.Actions {
- if action.Id == "" {
- action.Id = NewId()
- }
- }
- }
- }
-}
diff --git a/vendor/github.com/mattermost/platform/model/scheduled_task.go b/vendor/github.com/mattermost/platform/model/scheduled_task.go
deleted file mode 100644
index 453828bd..00000000
--- a/vendor/github.com/mattermost/platform/model/scheduled_task.go
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-package model
-
-import (
- "fmt"
- "sync"
- "time"
-)
-
-type TaskFunc func()
-
-type ScheduledTask struct {
- Name string `json:"name"`
- Interval time.Duration `json:"interval"`
- Recurring bool `json:"recurring"`
- function TaskFunc
- timer *time.Timer
-}
-
-var taskMutex = sync.Mutex{}
-var tasks = make(map[string]*ScheduledTask)
-
-func addTask(task *ScheduledTask) {
- taskMutex.Lock()
- defer taskMutex.Unlock()
- tasks[task.Name] = task
-}
-
-func removeTaskByName(name string) {
- taskMutex.Lock()
- defer taskMutex.Unlock()
- delete(tasks, name)
-}
-
-func GetTaskByName(name string) *ScheduledTask {
- taskMutex.Lock()
- defer taskMutex.Unlock()
- if task, ok := tasks[name]; ok {
- return task
- }
- return nil
-}
-
-func GetAllTasks() *map[string]*ScheduledTask {
- taskMutex.Lock()
- defer taskMutex.Unlock()
- return &tasks
-}
-
-func CreateTask(name string, function TaskFunc, timeToExecution time.Duration) *ScheduledTask {
- task := &ScheduledTask{
- Name: name,
- Interval: timeToExecution,
- Recurring: false,
- function: function,
- }
-
- taskRunner := func() {
- go task.function()
- removeTaskByName(task.Name)
- }
-
- task.timer = time.AfterFunc(timeToExecution, taskRunner)
-
- addTask(task)
-
- return task
-}
-
-func CreateRecurringTask(name string, function TaskFunc, interval time.Duration) *ScheduledTask {
- task := &ScheduledTask{
- Name: name,
- Interval: interval,
- Recurring: true,
- function: function,
- }
-
- taskRecurer := func() {
- go task.function()
- task.timer.Reset(task.Interval)
- }
-
- task.timer = time.AfterFunc(interval, taskRecurer)
-
- addTask(task)
-
- return task
-}
-
-func (task *ScheduledTask) Cancel() {
- task.timer.Stop()
- removeTaskByName(task.Name)
-}
-
-// Executes the task immediatly. A recurring task will be run regularally after interval.
-func (task *ScheduledTask) Execute() {
- task.function()
- task.timer.Reset(task.Interval)
-}
-
-func (task *ScheduledTask) String() string {
- return fmt.Sprintf(
- "%s\nInterval: %s\nRecurring: %t\n",
- task.Name,
- task.Interval.String(),
- task.Recurring,
- )
-}
diff --git a/vendor/github.com/mattermost/platform/model/system.go b/vendor/github.com/mattermost/platform/model/system.go
deleted file mode 100644
index e2f4283a..00000000
--- a/vendor/github.com/mattermost/platform/model/system.go
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-package model
-
-import (
- "encoding/json"
- "io"
-)
-
-const (
- SYSTEM_DIAGNOSTIC_ID = "DiagnosticId"
- SYSTEM_RAN_UNIT_TESTS = "RanUnitTests"
- SYSTEM_LAST_SECURITY_TIME = "LastSecurityTime"
- SYSTEM_ACTIVE_LICENSE_ID = "ActiveLicenseId"
- SYSTEM_LAST_COMPLIANCE_TIME = "LastComplianceTime"
-)
-
-type System struct {
- Name string `json:"name"`
- Value string `json:"value"`
-}
-
-func (o *System) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
-}
-
-func SystemFromJson(data io.Reader) *System {
- decoder := json.NewDecoder(data)
- var o System
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
-}
diff --git a/vendor/github.com/mattermost/platform/model/websocket_message.go b/vendor/github.com/mattermost/platform/model/websocket_message.go
deleted file mode 100644
index bf2535dc..00000000
--- a/vendor/github.com/mattermost/platform/model/websocket_message.go
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-package model
-
-import (
- "encoding/json"
- "io"
-)
-
-const (
- WEBSOCKET_EVENT_TYPING = "typing"
- WEBSOCKET_EVENT_POSTED = "posted"
- WEBSOCKET_EVENT_POST_EDITED = "post_edited"
- WEBSOCKET_EVENT_POST_DELETED = "post_deleted"
- WEBSOCKET_EVENT_CHANNEL_DELETED = "channel_deleted"
- WEBSOCKET_EVENT_CHANNEL_CREATED = "channel_created"
- WEBSOCKET_EVENT_CHANNEL_UPDATED = "channel_updated"
- WEBSOCKET_EVENT_DIRECT_ADDED = "direct_added"
- WEBSOCKET_EVENT_GROUP_ADDED = "group_added"
- WEBSOCKET_EVENT_NEW_USER = "new_user"
- WEBSOCKET_EVENT_ADDED_TO_TEAM = "added_to_team"
- WEBSOCKET_EVENT_LEAVE_TEAM = "leave_team"
- WEBSOCKET_EVENT_UPDATE_TEAM = "update_team"
- WEBSOCKET_EVENT_USER_ADDED = "user_added"
- WEBSOCKET_EVENT_USER_UPDATED = "user_updated"
- WEBSOCKET_EVENT_USER_ROLE_UPDATED = "user_role_updated"
- WEBSOCKET_EVENT_MEMBERROLE_UPDATED = "memberrole_updated"
- WEBSOCKET_EVENT_USER_REMOVED = "user_removed"
- WEBSOCKET_EVENT_PREFERENCE_CHANGED = "preference_changed"
- WEBSOCKET_EVENT_PREFERENCES_CHANGED = "preferences_changed"
- WEBSOCKET_EVENT_PREFERENCES_DELETED = "preferences_deleted"
- WEBSOCKET_EVENT_EPHEMERAL_MESSAGE = "ephemeral_message"
- WEBSOCKET_EVENT_STATUS_CHANGE = "status_change"
- WEBSOCKET_EVENT_HELLO = "hello"
- WEBSOCKET_EVENT_WEBRTC = "webrtc"
- WEBSOCKET_AUTHENTICATION_CHALLENGE = "authentication_challenge"
- WEBSOCKET_EVENT_REACTION_ADDED = "reaction_added"
- WEBSOCKET_EVENT_REACTION_REMOVED = "reaction_removed"
- WEBSOCKET_EVENT_RESPONSE = "response"
- WEBSOCKET_EVENT_EMOJI_ADDED = "emoji_added"
- WEBSOCKET_EVENT_CHANNEL_VIEWED = "channel_viewed"
- WEBSOCKET_EVENT_PLUGIN_ACTIVATED = "plugin_activated" // EXPERIMENTAL - SUBJECT TO CHANGE
- WEBSOCKET_EVENT_PLUGIN_DEACTIVATED = "plugin_deactivated" // EXPERIMENTAL - SUBJECT TO CHANGE
-)
-
-type WebSocketMessage interface {
- ToJson() string
- IsValid() bool
- EventType() string
-}
-
-type WebsocketBroadcast struct {
- OmitUsers map[string]bool `json:"omit_users"` // broadcast is omitted for users listed here
- UserId string `json:"user_id"` // broadcast only occurs for this user
- ChannelId string `json:"channel_id"` // broadcast only occurs for users in this channel
- TeamId string `json:"team_id"` // broadcast only occurs for users in this team
-}
-
-type WebSocketEvent struct {
- Event string `json:"event"`
- Data map[string]interface{} `json:"data"`
- Broadcast *WebsocketBroadcast `json:"broadcast"`
- Sequence int64 `json:"seq"`
-}
-
-func (m *WebSocketEvent) Add(key string, value interface{}) {
- m.Data[key] = value
-}
-
-func NewWebSocketEvent(event, teamId, channelId, userId string, omitUsers map[string]bool) *WebSocketEvent {
- return &WebSocketEvent{Event: event, Data: make(map[string]interface{}),
- Broadcast: &WebsocketBroadcast{TeamId: teamId, ChannelId: channelId, UserId: userId, OmitUsers: omitUsers}}
-}
-
-func (o *WebSocketEvent) IsValid() bool {
- return o.Event != ""
-}
-
-func (o *WebSocketEvent) EventType() string {
- return o.Event
-}
-
-func (o *WebSocketEvent) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
-}
-
-func WebSocketEventFromJson(data io.Reader) *WebSocketEvent {
- decoder := json.NewDecoder(data)
- var o WebSocketEvent
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
-}
-
-type WebSocketResponse struct {
- Status string `json:"status"`
- SeqReply int64 `json:"seq_reply,omitempty"`
- Data map[string]interface{} `json:"data,omitempty"`
- Error *AppError `json:"error,omitempty"`
-}
-
-func (m *WebSocketResponse) Add(key string, value interface{}) {
- m.Data[key] = value
-}
-
-func NewWebSocketResponse(status string, seqReply int64, data map[string]interface{}) *WebSocketResponse {
- return &WebSocketResponse{Status: status, SeqReply: seqReply, Data: data}
-}
-
-func NewWebSocketError(seqReply int64, err *AppError) *WebSocketResponse {
- return &WebSocketResponse{Status: STATUS_FAIL, SeqReply: seqReply, Error: err}
-}
-
-func (o *WebSocketResponse) IsValid() bool {
- return o.Status != ""
-}
-
-func (o *WebSocketResponse) EventType() string {
- return WEBSOCKET_EVENT_RESPONSE
-}
-
-func (o *WebSocketResponse) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
-}
-
-func WebSocketResponseFromJson(data io.Reader) *WebSocketResponse {
- decoder := json.NewDecoder(data)
- var o WebSocketResponse
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
-}
diff --git a/vendor/github.com/mitchellh/mapstructure/.travis.yml b/vendor/github.com/mitchellh/mapstructure/.travis.yml
index d9deadb8..1689c7d7 100644
--- a/vendor/github.com/mitchellh/mapstructure/.travis.yml
+++ b/vendor/github.com/mitchellh/mapstructure/.travis.yml
@@ -1,7 +1,7 @@
language: go
go:
- - 1.9.x
+ - "1.11.x"
- tip
script:
diff --git a/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md b/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md
new file mode 100644
index 00000000..3b3cb723
--- /dev/null
+++ b/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md
@@ -0,0 +1,21 @@
+## 1.1.2
+
+* Fix error when decode hook decodes interface implementation into interface
+ type. [GH-140]
+
+## 1.1.1
+
+* Fix panic that can happen in `decodePtr`
+
+## 1.1.0
+
+* Added `StringToIPHookFunc` to convert `string` to `net.IP` and `net.IPNet` [GH-133]
+* Support struct to struct decoding [GH-137]
+* If source map value is nil, then destination map value is nil (instead of empty)
+* If source slice value is nil, then destination slice value is nil (instead of empty)
+* If source pointer is nil, then destination pointer is set to nil (instead of
+ allocated zero value of type)
+
+## 1.0.0
+
+* Initial tagged stable release.
diff --git a/vendor/github.com/mitchellh/mapstructure/decode_hooks.go b/vendor/github.com/mitchellh/mapstructure/decode_hooks.go
index 2a727575..1f0abc65 100644
--- a/vendor/github.com/mitchellh/mapstructure/decode_hooks.go
+++ b/vendor/github.com/mitchellh/mapstructure/decode_hooks.go
@@ -2,6 +2,8 @@ package mapstructure
import (
"errors"
+ "fmt"
+ "net"
"reflect"
"strconv"
"strings"
@@ -115,6 +117,50 @@ func StringToTimeDurationHookFunc() DecodeHookFunc {
}
}
+// StringToIPHookFunc returns a DecodeHookFunc that converts
+// strings to net.IP
+func StringToIPHookFunc() DecodeHookFunc {
+ return func(
+ f reflect.Type,
+ t reflect.Type,
+ data interface{}) (interface{}, error) {
+ if f.Kind() != reflect.String {
+ return data, nil
+ }
+ if t != reflect.TypeOf(net.IP{}) {
+ return data, nil
+ }
+
+ // Convert it by parsing
+ ip := net.ParseIP(data.(string))
+ if ip == nil {
+ return net.IP{}, fmt.Errorf("failed parsing ip %v", data)
+ }
+
+ return ip, nil
+ }
+}
+
+// StringToIPNetHookFunc returns a DecodeHookFunc that converts
+// strings to net.IPNet
+func StringToIPNetHookFunc() DecodeHookFunc {
+ return func(
+ f reflect.Type,
+ t reflect.Type,
+ data interface{}) (interface{}, error) {
+ if f.Kind() != reflect.String {
+ return data, nil
+ }
+ if t != reflect.TypeOf(net.IPNet{}) {
+ return data, nil
+ }
+
+ // Convert it by parsing
+ _, net, err := net.ParseCIDR(data.(string))
+ return net, err
+ }
+}
+
// StringToTimeHookFunc returns a DecodeHookFunc that converts
// strings to time.Time.
func StringToTimeHookFunc(layout string) DecodeHookFunc {
diff --git a/vendor/github.com/mitchellh/mapstructure/go.mod b/vendor/github.com/mitchellh/mapstructure/go.mod
new file mode 100644
index 00000000..d2a71256
--- /dev/null
+++ b/vendor/github.com/mitchellh/mapstructure/go.mod
@@ -0,0 +1 @@
+module github.com/mitchellh/mapstructure
diff --git a/vendor/github.com/mitchellh/mapstructure/mapstructure.go b/vendor/github.com/mitchellh/mapstructure/mapstructure.go
index aaf12a29..256ee63f 100644
--- a/vendor/github.com/mitchellh/mapstructure/mapstructure.go
+++ b/vendor/github.com/mitchellh/mapstructure/mapstructure.go
@@ -224,6 +224,17 @@ func (d *Decoder) Decode(input interface{}) error {
// Decodes an unknown data type into a specific reflection value.
func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) error {
+ var inputVal reflect.Value
+ if input != nil {
+ inputVal = reflect.ValueOf(input)
+
+ // We need to check here if input is a typed nil. Typed nils won't
+ // match the "input == nil" below so we check that here.
+ if inputVal.Kind() == reflect.Ptr && inputVal.IsNil() {
+ input = nil
+ }
+ }
+
if input == nil {
// If the data is nil, then we don't set anything, unless ZeroFields is set
// to true.
@@ -237,7 +248,6 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
return nil
}
- inputVal := reflect.ValueOf(input)
if !inputVal.IsValid() {
// If the input value is invalid, then we just set the value
// to be the zero value.
@@ -260,8 +270,8 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
}
var err error
- inputKind := getKind(outVal)
- switch inputKind {
+ outputKind := getKind(outVal)
+ switch outputKind {
case reflect.Bool:
err = d.decodeBool(name, input, outVal)
case reflect.Interface:
@@ -288,7 +298,7 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
err = d.decodeFunc(name, input, outVal)
default:
// If we reached this point then we weren't able to decode it
- return fmt.Errorf("%s: unsupported type: %s", name, inputKind)
+ return fmt.Errorf("%s: unsupported type: %s", name, outputKind)
}
// If we reached here, then we successfully decoded SOMETHING, so
@@ -306,7 +316,16 @@ func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value)
if val.IsValid() && val.Elem().IsValid() {
return d.decode(name, data, val.Elem())
}
+
dataVal := reflect.ValueOf(data)
+
+ // If the input data is a pointer, and the assigned type is the dereference
+ // of that exact pointer, then indirect it so that we can assign it.
+ // Example: *string to string
+ if dataVal.Kind() == reflect.Ptr && dataVal.Type().Elem() == val.Type() {
+ dataVal = reflect.Indirect(dataVal)
+ }
+
if !dataVal.IsValid() {
dataVal = reflect.Zero(val.Type())
}
@@ -323,7 +342,7 @@ func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value)
}
func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value) error {
- dataVal := reflect.ValueOf(data)
+ dataVal := reflect.Indirect(reflect.ValueOf(data))
dataKind := getKind(dataVal)
converted := true
@@ -375,7 +394,7 @@ func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value)
}
func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) error {
- dataVal := reflect.ValueOf(data)
+ dataVal := reflect.Indirect(reflect.ValueOf(data))
dataKind := getKind(dataVal)
dataType := dataVal.Type()
@@ -417,7 +436,7 @@ func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) er
}
func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) error {
- dataVal := reflect.ValueOf(data)
+ dataVal := reflect.Indirect(reflect.ValueOf(data))
dataKind := getKind(dataVal)
switch {
@@ -460,7 +479,7 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e
}
func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) error {
- dataVal := reflect.ValueOf(data)
+ dataVal := reflect.Indirect(reflect.ValueOf(data))
dataKind := getKind(dataVal)
switch {
@@ -491,7 +510,7 @@ func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) e
}
func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value) error {
- dataVal := reflect.ValueOf(data)
+ dataVal := reflect.Indirect(reflect.ValueOf(data))
dataKind := getKind(dataVal)
dataType := dataVal.Type()
@@ -595,6 +614,20 @@ func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val refle
// Accumulate errors
errors := make([]string, 0)
+ // If the input data is empty, then we just match what the input data is.
+ if dataVal.Len() == 0 {
+ if dataVal.IsNil() {
+ if !val.IsNil() {
+ val.Set(dataVal)
+ }
+ } else {
+ // Set to empty allocated value
+ val.Set(valMap)
+ }
+
+ return nil
+ }
+
for _, k := range dataVal.MapKeys() {
fieldName := fmt.Sprintf("%s[%s]", name, k)
@@ -644,16 +677,28 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
return fmt.Errorf("cannot assign type '%s' to map value field of type '%s'", v.Type(), valMap.Type().Elem())
}
+ tagValue := f.Tag.Get(d.config.TagName)
+ tagParts := strings.Split(tagValue, ",")
+
// Determine the name of the key in the map
keyName := f.Name
- tagValue := f.Tag.Get(d.config.TagName)
- tagValue = strings.SplitN(tagValue, ",", 2)[0]
- if tagValue != "" {
- if tagValue == "-" {
+ if tagParts[0] != "" {
+ if tagParts[0] == "-" {
continue
}
+ keyName = tagParts[0]
+ }
- keyName = tagValue
+ // If "squash" is specified in the tag, we squash the field down.
+ squash := false
+ for _, tag := range tagParts[1:] {
+ if tag == "squash" {
+ squash = true
+ break
+ }
+ }
+ if squash && v.Kind() != reflect.Struct {
+ return fmt.Errorf("cannot squash non-struct type '%s'", v.Type())
}
switch v.Kind() {
@@ -673,7 +718,13 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
return err
}
- valMap.SetMapIndex(reflect.ValueOf(keyName), vMap)
+ if squash {
+ for _, k := range vMap.MapKeys() {
+ valMap.SetMapIndex(k, vMap.MapIndex(k))
+ }
+ } else {
+ valMap.SetMapIndex(reflect.ValueOf(keyName), vMap)
+ }
default:
valMap.SetMapIndex(reflect.ValueOf(keyName), v)
@@ -688,11 +739,33 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
}
func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) error {
+ // If the input data is nil, then we want to just set the output
+ // pointer to be nil as well.
+ isNil := data == nil
+ if !isNil {
+ switch v := reflect.Indirect(reflect.ValueOf(data)); v.Kind() {
+ case reflect.Chan,
+ reflect.Func,
+ reflect.Interface,
+ reflect.Map,
+ reflect.Ptr,
+ reflect.Slice:
+ isNil = v.IsNil()
+ }
+ }
+ if isNil {
+ if !val.IsNil() && val.CanSet() {
+ nilValue := reflect.New(val.Type()).Elem()
+ val.Set(nilValue)
+ }
+
+ return nil
+ }
+
// Create an element of the concrete (non pointer) type and decode
// into that. Then set the value of the pointer to this type.
valType := val.Type()
valElemType := valType.Elem()
-
if val.CanSet() {
realVal := val
if realVal.IsNil() || d.config.ZeroFields {
@@ -734,30 +807,44 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
valSlice := val
if valSlice.IsNil() || d.config.ZeroFields {
+ if d.config.WeaklyTypedInput {
+ switch {
+ // Slice and array we use the normal logic
+ case dataValKind == reflect.Slice, dataValKind == reflect.Array:
+ break
+
+ // Empty maps turn into empty slices
+ case dataValKind == reflect.Map:
+ if dataVal.Len() == 0 {
+ val.Set(reflect.MakeSlice(sliceType, 0, 0))
+ return nil
+ }
+ // Create slice of maps of other sizes
+ return d.decodeSlice(name, []interface{}{data}, val)
+
+ case dataValKind == reflect.String && valElemType.Kind() == reflect.Uint8:
+ return d.decodeSlice(name, []byte(dataVal.String()), val)
+
+ // All other types we try to convert to the slice type
+ // and "lift" it into it. i.e. a string becomes a string slice.
+ default:
+ // Just re-try this function with data as a slice.
+ return d.decodeSlice(name, []interface{}{data}, val)
+ }
+ }
+
// Check input type
if dataValKind != reflect.Array && dataValKind != reflect.Slice {
- if d.config.WeaklyTypedInput {
- switch {
- // Empty maps turn into empty slices
- case dataValKind == reflect.Map:
- if dataVal.Len() == 0 {
- val.Set(reflect.MakeSlice(sliceType, 0, 0))
- return nil
- }
- case dataValKind == reflect.String && valElemType.Kind() == reflect.Uint8:
- return d.decodeSlice(name, []byte(dataVal.String()), val)
- // All other types we try to convert to the slice type
- // and "lift" it into it. i.e. a string becomes a string slice.
- default:
- // Just re-try this function with data as a slice.
- return d.decodeSlice(name, []interface{}{data}, val)
- }
- }
return fmt.Errorf(
"'%s': source data must be an array or slice, got %s", name, dataValKind)
}
+ // If the input value is empty, then don't allocate since non-nil != nil
+ if dataVal.Len() == 0 {
+ return nil
+ }
+
// Make a new slice to hold our result, same size as the original data.
valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len())
}
@@ -867,10 +954,29 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value)
}
dataValKind := dataVal.Kind()
- if dataValKind != reflect.Map {
- return fmt.Errorf("'%s' expected a map, got '%s'", name, dataValKind)
- }
+ switch dataValKind {
+ case reflect.Map:
+ return d.decodeStructFromMap(name, dataVal, val)
+ case reflect.Struct:
+ // Not the most efficient way to do this but we can optimize later if
+ // we want to. To convert from struct to struct we go to map first
+ // as an intermediary.
+ m := make(map[string]interface{})
+ mval := reflect.Indirect(reflect.ValueOf(&m))
+ if err := d.decodeMapFromStruct(name, dataVal, mval, mval); err != nil {
+ return err
+ }
+
+ result := d.decodeStructFromMap(name, mval, val)
+ return result
+
+ default:
+ return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind())
+ }
+}
+
+func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) error {
dataValType := dataVal.Type()
if kind := dataValType.Key().Kind(); kind != reflect.String && kind != reflect.Interface {
return fmt.Errorf(
diff --git a/vendor/github.com/nlopes/slack/.travis.yml b/vendor/github.com/nlopes/slack/.travis.yml
index bd0539e0..b0615288 100644
--- a/vendor/github.com/nlopes/slack/.travis.yml
+++ b/vendor/github.com/nlopes/slack/.travis.yml
@@ -4,6 +4,8 @@ go:
- 1.7.x
- 1.8.x
- 1.9.x
+ - 1.10.x
+ - 1.11.x
- tip
before_install:
diff --git a/vendor/github.com/nlopes/slack/CHANGELOG.md b/vendor/github.com/nlopes/slack/CHANGELOG.md
index a79ea50c..cf0fc2cc 100644
--- a/vendor/github.com/nlopes/slack/CHANGELOG.md
+++ b/vendor/github.com/nlopes/slack/CHANGELOG.md
@@ -1,3 +1,15 @@
+### v0.4.0 - October 06, 2018
+full differences can be viewed using `git log --oneline --decorate --color v0.3.0..v0.4.0`
+- Breaking Change: renamed ApplyMessageOption, to mark it as unsafe,
+this means it may break without warning in the future.
+- Breaking: Msg structure files field changed to an array.
+- General: implementation for new security headers.
+- RTM: deadlock fix between connect/disconnect.
+- Events: various new fields added.
+- Web: various fixes, new fields exposed, new methods added.
+- Interactions: minor additions expect breaking changes in next release for dialogs/button clicks.
+- Utils: new methods added.
+
### v0.3.0 - July 30, 2018
full differences can be viewed using `git log --oneline --decorate --color v0.2.0..v0.3.0`
- slack events initial support added. (still considered experimental and undergoing changes, stability not promised)
diff --git a/vendor/github.com/nlopes/slack/Gopkg.lock b/vendor/github.com/nlopes/slack/Gopkg.lock
index 5cc0520e..9c33d0dc 100644
--- a/vendor/github.com/nlopes/slack/Gopkg.lock
+++ b/vendor/github.com/nlopes/slack/Gopkg.lock
@@ -13,6 +13,12 @@
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
version = "v1.2.0"
+[[projects]]
+ name = "github.com/pkg/errors"
+ packages = ["."]
+ revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
+ version = "v0.8.0"
+
[[projects]]
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
@@ -28,6 +34,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
- inputs-digest = "888307bf47ee004aaaa4c45e6139929b4984f2253e48e382246bfb8c66f3cd65"
+ inputs-digest = "596fa546322c2a1e9708a10c9f39aca2e04792b477fab86fb2899fbaab776070"
solver-name = "gps-cdcl"
solver-version = 1
diff --git a/vendor/github.com/nlopes/slack/Gopkg.toml b/vendor/github.com/nlopes/slack/Gopkg.toml
new file mode 100644
index 00000000..257870d6
--- /dev/null
+++ b/vendor/github.com/nlopes/slack/Gopkg.toml
@@ -0,0 +1,17 @@
+ignored = ["github.com/lusis/slack-test"]
+
+[[constraint]]
+ name = "github.com/gorilla/websocket"
+ version = "1.2.0"
+
+[[constraint]]
+ name = "github.com/stretchr/testify"
+ version = "1.2.1"
+
+[[constraint]]
+ name = "github.com/pkg/errors"
+ version = "0.8.0"
+
+[prune]
+ go-tests = true
+ unused-packages = true
diff --git a/vendor/github.com/nlopes/slack/admin.go b/vendor/github.com/nlopes/slack/admin.go
index a2aa7e5c..db44aa38 100644
--- a/vendor/github.com/nlopes/slack/admin.go
+++ b/vendor/github.com/nlopes/slack/admin.go
@@ -12,9 +12,9 @@ type adminResponse struct {
Error string `json:"error"`
}
-func adminRequest(ctx context.Context, client HTTPRequester, method string, teamName string, values url.Values, debug bool) (*adminResponse, error) {
+func adminRequest(ctx context.Context, client httpClient, method string, teamName string, values url.Values, d debug) (*adminResponse, error) {
adminResponse := &adminResponse{}
- err := parseAdminResponse(ctx, client, method, teamName, values, adminResponse, debug)
+ err := parseAdminResponse(ctx, client, method, teamName, values, adminResponse, d)
if err != nil {
return nil, err
}
@@ -40,7 +40,7 @@ func (api *Client) DisableUserContext(ctx context.Context, teamName string, uid
"_attempts": {"1"},
}
- _, err := adminRequest(ctx, api.httpclient, "setInactive", teamName, values, api.debug)
+ _, err := adminRequest(ctx, api.httpclient, "setInactive", teamName, values, api)
if err != nil {
return fmt.Errorf("Failed to disable user with id '%s': %s", uid, err)
}
@@ -67,7 +67,7 @@ func (api *Client) InviteGuestContext(ctx context.Context, teamName, channel, fi
"_attempts": {"1"},
}
- _, err := adminRequest(ctx, api.httpclient, "invite", teamName, values, api.debug)
+ _, err := adminRequest(ctx, api.httpclient, "invite", teamName, values, api)
if err != nil {
return fmt.Errorf("Failed to invite single-channel guest: %s", err)
}
@@ -94,7 +94,7 @@ func (api *Client) InviteRestrictedContext(ctx context.Context, teamName, channe
"_attempts": {"1"},
}
- _, err := adminRequest(ctx, api.httpclient, "invite", teamName, values, api.debug)
+ _, err := adminRequest(ctx, api.httpclient, "invite", teamName, values, api)
if err != nil {
return fmt.Errorf("Failed to restricted account: %s", err)
}
@@ -118,7 +118,7 @@ func (api *Client) InviteToTeamContext(ctx context.Context, teamName, firstName,
"_attempts": {"1"},
}
- _, err := adminRequest(ctx, api.httpclient, "invite", teamName, values, api.debug)
+ _, err := adminRequest(ctx, api.httpclient, "invite", teamName, values, api)
if err != nil {
return fmt.Errorf("Failed to invite to team: %s", err)
}
@@ -140,7 +140,7 @@ func (api *Client) SetRegularContext(ctx context.Context, teamName, user string)
"_attempts": {"1"},
}
- _, err := adminRequest(ctx, api.httpclient, "setRegular", teamName, values, api.debug)
+ _, err := adminRequest(ctx, api.httpclient, "setRegular", teamName, values, api)
if err != nil {
return fmt.Errorf("Failed to change the user (%s) to a regular user: %s", user, err)
}
@@ -162,7 +162,7 @@ func (api *Client) SendSSOBindingEmailContext(ctx context.Context, teamName, use
"_attempts": {"1"},
}
- _, err := adminRequest(ctx, api.httpclient, "sendSSOBind", teamName, values, api.debug)
+ _, err := adminRequest(ctx, api.httpclient, "sendSSOBind", teamName, values, api)
if err != nil {
return fmt.Errorf("Failed to send SSO binding email for user (%s): %s", user, err)
}
@@ -185,7 +185,7 @@ func (api *Client) SetUltraRestrictedContext(ctx context.Context, teamName, uid,
"_attempts": {"1"},
}
- _, err := adminRequest(ctx, api.httpclient, "setUltraRestricted", teamName, values, api.debug)
+ _, err := adminRequest(ctx, api.httpclient, "setUltraRestricted", teamName, values, api)
if err != nil {
return fmt.Errorf("Failed to ultra-restrict account: %s", err)
}
@@ -207,7 +207,7 @@ func (api *Client) SetRestrictedContext(ctx context.Context, teamName, uid strin
"_attempts": {"1"},
}
- _, err := adminRequest(ctx, api.httpclient, "setRestricted", teamName, values, api.debug)
+ _, err := adminRequest(ctx, api.httpclient, "setRestricted", teamName, values, api)
if err != nil {
return fmt.Errorf("Failed to restrict account: %s", err)
}
diff --git a/vendor/github.com/nlopes/slack/attachments.go b/vendor/github.com/nlopes/slack/attachments.go
index 326fc010..06f59fa3 100644
--- a/vendor/github.com/nlopes/slack/attachments.go
+++ b/vendor/github.com/nlopes/slack/attachments.go
@@ -42,24 +42,14 @@ type AttachmentActionOptionGroup struct {
}
// AttachmentActionCallback is sent from Slack when a user clicks a button in an interactive message (aka AttachmentAction)
-type AttachmentActionCallback struct {
- Actions []AttachmentAction `json:"actions"`
- CallbackID string `json:"callback_id"`
- Team Team `json:"team"`
- Channel Channel `json:"channel"`
- User User `json:"user"`
+// DEPRECATED: use InteractionCallback
+type AttachmentActionCallback InteractionCallback
- Name string `json:"name"`
- Value string `json:"value"`
-
- OriginalMessage Message `json:"original_message"`
-
- ActionTs string `json:"action_ts"`
- MessageTs string `json:"message_ts"`
- AttachmentID string `json:"attachment_id"`
- Token string `json:"token"`
- ResponseURL string `json:"response_url"`
- TriggerID string `json:"trigger_id"`
+// ActionCallback specific fields for the action callback.
+type ActionCallback struct {
+ MessageTs string `json:"message_ts"`
+ AttachmentID string `json:"attachment_id"`
+ Actions []AttachmentAction `json:"actions"`
}
// ConfirmationField are used to ask users to confirm actions
diff --git a/vendor/github.com/nlopes/slack/auth.go b/vendor/github.com/nlopes/slack/auth.go
new file mode 100644
index 00000000..f8fe1f9d
--- /dev/null
+++ b/vendor/github.com/nlopes/slack/auth.go
@@ -0,0 +1,40 @@
+package slack
+
+import (
+ "context"
+ "net/url"
+)
+
+// AuthRevokeResponse contains our Auth response from the auth.revoke endpoint
+type AuthRevokeResponse struct {
+ SlackResponse // Contains the "ok", and "Error", if any
+ Revoked bool `json:"revoked,omitempty"`
+}
+
+// authRequest sends the actual request, and unmarshals the response
+func authRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (*AuthRevokeResponse, error) {
+ response := &AuthRevokeResponse{}
+ err := postSlackMethod(ctx, client, path, values, response, d)
+ if err != nil {
+ return nil, err
+ }
+
+ return response, response.Err()
+}
+
+// SendAuthRevoke will send a revocation for our token
+func (api *Client) SendAuthRevoke(token string) (*AuthRevokeResponse, error) {
+ return api.SendAuthRevokeContext(context.Background(), token)
+}
+
+// SendAuthRevokeContext will retrieve the satus from api.test
+func (api *Client) SendAuthRevokeContext(ctx context.Context, token string) (*AuthRevokeResponse, error) {
+ if token == "" {
+ token = api.token
+ }
+ values := url.Values{
+ "token": {token},
+ }
+
+ return authRequest(ctx, api.httpclient, "auth.revoke", values, api)
+}
diff --git a/vendor/github.com/nlopes/slack/bots.go b/vendor/github.com/nlopes/slack/bots.go
index 92570a04..e27e76ab 100644
--- a/vendor/github.com/nlopes/slack/bots.go
+++ b/vendor/github.com/nlopes/slack/bots.go
@@ -19,9 +19,9 @@ type botResponseFull struct {
SlackResponse
}
-func botRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*botResponseFull, error) {
+func botRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (*botResponseFull, error) {
response := &botResponseFull{}
- err := postSlackMethod(ctx, client, path, values, response, debug)
+ err := postSlackMethod(ctx, client, path, values, response, d)
if err != nil {
return nil, err
}
@@ -43,7 +43,7 @@ func (api *Client) GetBotInfoContext(ctx context.Context, bot string) (*Bot, err
"bot": {bot},
}
- response, err := botRequest(ctx, api.httpclient, "bots.info", values, api.debug)
+ response, err := botRequest(ctx, api.httpclient, "bots.info", values, api)
if err != nil {
return nil, err
}
diff --git a/vendor/github.com/nlopes/slack/channels.go b/vendor/github.com/nlopes/slack/channels.go
index 6204315a..007985bc 100644
--- a/vendor/github.com/nlopes/slack/channels.go
+++ b/vendor/github.com/nlopes/slack/channels.go
@@ -26,9 +26,9 @@ type Channel struct {
Locale string `json:"locale"`
}
-func channelRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*channelResponseFull, error) {
+func channelRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (*channelResponseFull, error) {
response := &channelResponseFull{}
- err := postForm(ctx, client, SLACK_API+path, values, response, debug)
+ err := postForm(ctx, client, APIURL+path, values, response, d)
if err != nil {
return nil, err
}
@@ -52,7 +52,7 @@ func (api *Client) ArchiveChannelContext(ctx context.Context, channelID string)
"channel": {channelID},
}
- _, err = channelRequest(ctx, api.httpclient, "channels.archive", values, api.debug)
+ _, err = channelRequest(ctx, api.httpclient, "channels.archive", values, api)
return err
}
@@ -70,7 +70,7 @@ func (api *Client) UnarchiveChannelContext(ctx context.Context, channelID string
"channel": {channelID},
}
- _, err = channelRequest(ctx, api.httpclient, "channels.unarchive", values, api.debug)
+ _, err = channelRequest(ctx, api.httpclient, "channels.unarchive", values, api)
return err
}
@@ -88,7 +88,7 @@ func (api *Client) CreateChannelContext(ctx context.Context, channelName string)
"name": {channelName},
}
- response, err := channelRequest(ctx, api.httpclient, "channels.create", values, api.debug)
+ response, err := channelRequest(ctx, api.httpclient, "channels.create", values, api)
if err != nil {
return nil, err
}
@@ -133,7 +133,7 @@ func (api *Client) GetChannelHistoryContext(ctx context.Context, channelID strin
}
}
- response, err := channelRequest(ctx, api.httpclient, "channels.history", values, api.debug)
+ response, err := channelRequest(ctx, api.httpclient, "channels.history", values, api)
if err != nil {
return nil, err
}
@@ -154,7 +154,7 @@ func (api *Client) GetChannelInfoContext(ctx context.Context, channelID string)
"channel": {channelID},
}
- response, err := channelRequest(ctx, api.httpclient, "channels.info", values, api.debug)
+ response, err := channelRequest(ctx, api.httpclient, "channels.info", values, api)
if err != nil {
return nil, err
}
@@ -167,7 +167,7 @@ func (api *Client) InviteUserToChannel(channelID, user string) (*Channel, error)
return api.InviteUserToChannelContext(context.Background(), channelID, user)
}
-// InviteUserToChannelCustom invites a user to a given channel and returns a *Channel with a custom context
+// InviteUserToChannelContext invites a user to a given channel and returns a *Channel with a custom context
// see https://api.slack.com/methods/channels.invite
func (api *Client) InviteUserToChannelContext(ctx context.Context, channelID, user string) (*Channel, error) {
values := url.Values{
@@ -176,7 +176,7 @@ func (api *Client) InviteUserToChannelContext(ctx context.Context, channelID, us
"user": {user},
}
- response, err := channelRequest(ctx, api.httpclient, "channels.invite", values, api.debug)
+ response, err := channelRequest(ctx, api.httpclient, "channels.invite", values, api)
if err != nil {
return nil, err
}
@@ -197,7 +197,7 @@ func (api *Client) JoinChannelContext(ctx context.Context, channelName string) (
"name": {channelName},
}
- response, err := channelRequest(ctx, api.httpclient, "channels.join", values, api.debug)
+ response, err := channelRequest(ctx, api.httpclient, "channels.join", values, api)
if err != nil {
return nil, err
}
@@ -218,7 +218,7 @@ func (api *Client) LeaveChannelContext(ctx context.Context, channelID string) (b
"channel": {channelID},
}
- response, err := channelRequest(ctx, api.httpclient, "channels.leave", values, api.debug)
+ response, err := channelRequest(ctx, api.httpclient, "channels.leave", values, api)
if err != nil {
return false, err
}
@@ -241,7 +241,7 @@ func (api *Client) KickUserFromChannelContext(ctx context.Context, channelID, us
"user": {user},
}
- _, err = channelRequest(ctx, api.httpclient, "channels.kick", values, api.debug)
+ _, err = channelRequest(ctx, api.httpclient, "channels.kick", values, api)
return err
}
@@ -261,7 +261,7 @@ func (api *Client) GetChannelsContext(ctx context.Context, excludeArchived bool)
values.Add("exclude_archived", "1")
}
- response, err := channelRequest(ctx, api.httpclient, "channels.list", values, api.debug)
+ response, err := channelRequest(ctx, api.httpclient, "channels.list", values, api)
if err != nil {
return nil, err
}
@@ -288,7 +288,7 @@ func (api *Client) SetChannelReadMarkContext(ctx context.Context, channelID, ts
"ts": {ts},
}
- _, err = channelRequest(ctx, api.httpclient, "channels.mark", values, api.debug)
+ _, err = channelRequest(ctx, api.httpclient, "channels.mark", values, api)
return err
}
@@ -309,7 +309,7 @@ func (api *Client) RenameChannelContext(ctx context.Context, channelID, name str
// XXX: the created entry in this call returns a string instead of a number
// so I may have to do some workaround to solve it.
- response, err := channelRequest(ctx, api.httpclient, "channels.rename", values, api.debug)
+ response, err := channelRequest(ctx, api.httpclient, "channels.rename", values, api)
if err != nil {
return nil, err
}
@@ -331,7 +331,7 @@ func (api *Client) SetChannelPurposeContext(ctx context.Context, channelID, purp
"purpose": {purpose},
}
- response, err := channelRequest(ctx, api.httpclient, "channels.setPurpose", values, api.debug)
+ response, err := channelRequest(ctx, api.httpclient, "channels.setPurpose", values, api)
if err != nil {
return "", err
}
@@ -353,7 +353,7 @@ func (api *Client) SetChannelTopicContext(ctx context.Context, channelID, topic
"topic": {topic},
}
- response, err := channelRequest(ctx, api.httpclient, "channels.setTopic", values, api.debug)
+ response, err := channelRequest(ctx, api.httpclient, "channels.setTopic", values, api)
if err != nil {
return "", err
}
@@ -374,7 +374,7 @@ func (api *Client) GetChannelRepliesContext(ctx context.Context, channelID, thre
"channel": {channelID},
"thread_ts": {thread_ts},
}
- response, err := channelRequest(ctx, api.httpclient, "channels.replies", values, api.debug)
+ response, err := channelRequest(ctx, api.httpclient, "channels.replies", values, api)
if err != nil {
return nil, err
}
diff --git a/vendor/github.com/nlopes/slack/chat.go b/vendor/github.com/nlopes/slack/chat.go
index 2b89a44c..eb8fd6c8 100644
--- a/vendor/github.com/nlopes/slack/chat.go
+++ b/vendor/github.com/nlopes/slack/chat.go
@@ -4,7 +4,8 @@ import (
"context"
"encoding/json"
"net/url"
- "strings"
+
+ "github.com/nlopes/slack/slackutilsx"
)
const (
@@ -95,26 +96,24 @@ func (api *Client) DeleteMessageContext(ctx context.Context, channel, messageTim
// PostMessage sends a message to a channel.
// Message is escaped by default according to https://api.slack.com/docs/formatting
// Use http://davestevens.github.io/slack-message-builder/ to help crafting your message.
-func (api *Client) PostMessage(channel, text string, params PostMessageParameters) (string, string, error) {
+func (api *Client) PostMessage(channelID string, options ...MsgOption) (string, string, error) {
respChannel, respTimestamp, _, err := api.SendMessageContext(
context.Background(),
- channel,
- MsgOptionText(text, params.EscapeText),
- MsgOptionAttachments(params.Attachments...),
- MsgOptionPostMessageParameters(params),
+ channelID,
+ MsgOptionPost(),
+ MsgOptionCompose(options...),
)
return respChannel, respTimestamp, err
}
// PostMessageContext sends a message to a channel with a custom context
-// For more details, see PostMessage documentation
-func (api *Client) PostMessageContext(ctx context.Context, channel, text string, params PostMessageParameters) (string, string, error) {
+// For more details, see PostMessage documentation.
+func (api *Client) PostMessageContext(ctx context.Context, channelID string, options ...MsgOption) (string, string, error) {
respChannel, respTimestamp, _, err := api.SendMessageContext(
ctx,
- channel,
- MsgOptionText(text, params.EscapeText),
- MsgOptionAttachments(params.Attachments...),
- MsgOptionPostMessageParameters(params),
+ channelID,
+ MsgOptionPost(),
+ MsgOptionCompose(options...),
)
return respChannel, respTimestamp, err
}
@@ -134,18 +133,23 @@ func (api *Client) PostEphemeral(channelID, userID string, options ...MsgOption)
// PostEphemeralContext sends an ephemeal message to a user in a channel with a custom context
// For more details, see PostEphemeral documentation
func (api *Client) PostEphemeralContext(ctx context.Context, channelID, userID string, options ...MsgOption) (timestamp string, err error) {
- _, timestamp, _, err = api.SendMessageContext(ctx, channelID, append(options, MsgOptionPostEphemeral2(userID))...)
+ _, timestamp, _, err = api.SendMessageContext(ctx, channelID, MsgOptionPostEphemeral(userID), MsgOptionCompose(options...))
return timestamp, err
}
// UpdateMessage updates a message in a channel
-func (api *Client) UpdateMessage(channelID, timestamp, text string) (string, string, string, error) {
- return api.UpdateMessageContext(context.Background(), channelID, timestamp, text)
+func (api *Client) UpdateMessage(channelID, timestamp string, options ...MsgOption) (string, string, string, error) {
+ return api.SendMessageContext(context.Background(), channelID, MsgOptionUpdate(timestamp), MsgOptionCompose(options...))
}
// UpdateMessageContext updates a message in a channel
-func (api *Client) UpdateMessageContext(ctx context.Context, channelID, timestamp, text string) (string, string, string, error) {
- return api.SendMessageContext(ctx, channelID, MsgOptionUpdate(timestamp), MsgOptionText(text, true))
+func (api *Client) UpdateMessageContext(ctx context.Context, channelID, timestamp string, options ...MsgOption) (string, string, string, error) {
+ return api.SendMessageContext(ctx, channelID, MsgOptionUpdate(timestamp), MsgOptionCompose(options...))
+}
+
+// UnfurlMessage unfurls a message in a channel
+func (api *Client) UnfurlMessage(channelID, timestamp string, unfurls map[string]Attachment, options ...MsgOption) (string, string, string, error) {
+ return api.SendMessageContext(context.Background(), channelID, MsgOptionUnfurl(timestamp, unfurls), MsgOptionCompose(options...))
}
// SendMessage more flexible method for configuring messages.
@@ -164,22 +168,24 @@ func (api *Client) SendMessageContext(ctx context.Context, channelID string, opt
return "", "", "", err
}
- if err = postSlackMethod(ctx, api.httpclient, string(config.mode), config.values, &response, api.debug); err != nil {
+ if err = postForm(ctx, api.httpclient, config.endpoint, config.values, &response, api); err != nil {
return "", "", "", err
}
return response.Channel, response.getMessageTimestamp(), response.Text, response.Err()
}
-// ApplyMsgOptions utility function for debugging/testing chat requests.
-func ApplyMsgOptions(token, channel string, options ...MsgOption) (string, url.Values, error) {
+// UnsafeApplyMsgOptions utility function for debugging/testing chat requests.
+// NOTE: USE AT YOUR OWN RISK: No issues relating to the use of this function
+// will be supported by the library.
+func UnsafeApplyMsgOptions(token, channel string, options ...MsgOption) (string, url.Values, error) {
config, err := applyMsgOptions(token, channel, options...)
- return string(config.mode), config.values, err
+ return config.endpoint, config.values, err
}
func applyMsgOptions(token, channel string, options ...MsgOption) (sendConfig, error) {
config := sendConfig{
- mode: chatPostMessage,
+ endpoint: APIURL + string(chatPostMessage),
values: url.Values{
"token": {token},
"channel": {channel},
@@ -195,11 +201,6 @@ func applyMsgOptions(token, channel string, options ...MsgOption) (sendConfig, e
return config, nil
}
-func escapeMessage(message string) string {
- replacer := strings.NewReplacer("&", "&", "<", "<", ">", ">")
- return replacer.Replace(message)
-}
-
type sendMode string
const (
@@ -208,11 +209,12 @@ const (
chatDelete sendMode = "chat.delete"
chatPostEphemeral sendMode = "chat.postEphemeral"
chatMeMessage sendMode = "chat.meMessage"
+ chatUnfurl sendMode = "chat.unfurl"
)
type sendConfig struct {
- mode sendMode
- values url.Values
+ endpoint string
+ values url.Values
}
// MsgOption option provided when sending a message.
@@ -221,26 +223,16 @@ type MsgOption func(*sendConfig) error
// MsgOptionPost posts a messages, this is the default.
func MsgOptionPost() MsgOption {
return func(config *sendConfig) error {
- config.mode = chatPostMessage
+ config.endpoint = APIURL + string(chatPostMessage)
config.values.Del("ts")
return nil
}
}
-// MsgOptionPostEphemeral - DEPRECATED: use MsgOptionPostEphemeral2
-// posts an ephemeral message.
-func MsgOptionPostEphemeral() MsgOption {
+// MsgOptionPostEphemeral - posts an ephemeral message to the provided user.
+func MsgOptionPostEphemeral(userID string) MsgOption {
return func(config *sendConfig) error {
- config.mode = chatPostEphemeral
- config.values.Del("ts")
- return nil
- }
-}
-
-// MsgOptionPostEphemeral2 - posts an ephemeral message to the provided user.
-func MsgOptionPostEphemeral2(userID string) MsgOption {
- return func(config *sendConfig) error {
- config.mode = chatPostEphemeral
+ config.endpoint = APIURL + string(chatPostEphemeral)
MsgOptionUser(userID)(config)
config.values.Del("ts")
@@ -251,7 +243,7 @@ func MsgOptionPostEphemeral2(userID string) MsgOption {
// MsgOptionMeMessage posts a "me message" type from the calling user
func MsgOptionMeMessage() MsgOption {
return func(config *sendConfig) error {
- config.mode = chatMeMessage
+ config.endpoint = APIURL + string(chatMeMessage)
return nil
}
}
@@ -259,7 +251,7 @@ func MsgOptionMeMessage() MsgOption {
// MsgOptionUpdate updates a message based on the timestamp.
func MsgOptionUpdate(timestamp string) MsgOption {
return func(config *sendConfig) error {
- config.mode = chatUpdate
+ config.endpoint = APIURL + string(chatUpdate)
config.values.Add("ts", timestamp)
return nil
}
@@ -268,12 +260,25 @@ func MsgOptionUpdate(timestamp string) MsgOption {
// MsgOptionDelete deletes a message based on the timestamp.
func MsgOptionDelete(timestamp string) MsgOption {
return func(config *sendConfig) error {
- config.mode = chatDelete
+ config.endpoint = APIURL + string(chatDelete)
config.values.Add("ts", timestamp)
return nil
}
}
+// MsgOptionUnfurl unfurls a message based on the timestamp.
+func MsgOptionUnfurl(timestamp string, unfurls map[string]Attachment) MsgOption {
+ return func(config *sendConfig) error {
+ config.endpoint = APIURL + string(chatUnfurl)
+ config.values.Add("ts", timestamp)
+ unfurlsStr, err := json.Marshal(unfurls)
+ if err == nil {
+ config.values.Add("unfurls", string(unfurlsStr))
+ }
+ return err
+ }
+}
+
// MsgOptionAsUser whether or not to send the message as the user.
func MsgOptionAsUser(b bool) MsgOption {
return func(config *sendConfig) error {
@@ -292,12 +297,20 @@ func MsgOptionUser(userID string) MsgOption {
}
}
+// MsgOptionUsername set the username for the message.
+func MsgOptionUsername(username string) MsgOption {
+ return func(config *sendConfig) error {
+ config.values.Set("username", username)
+ return nil
+ }
+}
+
// MsgOptionText provide the text for the message, optionally escape the provided
// text.
func MsgOptionText(text string, escape bool) MsgOption {
return func(config *sendConfig) error {
if escape {
- text = escapeMessage(text)
+ text = slackutilsx.EscapeMessage(text)
}
config.values.Add("text", text)
return nil
@@ -367,7 +380,7 @@ func MsgOptionBroadcast() MsgOption {
}
}
-// this function combines multiple options into a single option.
+// MsgOptionCompose combines multiple options into a single option.
func MsgOptionCompose(options ...MsgOption) MsgOption {
return func(c *sendConfig) error {
for _, opt := range options {
@@ -379,6 +392,7 @@ func MsgOptionCompose(options ...MsgOption) MsgOption {
}
}
+// MsgOptionParse set parse option.
func MsgOptionParse(b bool) MsgOption {
return func(c *sendConfig) error {
var v string
@@ -392,6 +406,18 @@ func MsgOptionParse(b bool) MsgOption {
}
}
+// UnsafeMsgOptionEndpoint deliver the message to the specified endpoint.
+// NOTE: USE AT YOUR OWN RISK: No issues relating to the use of this Option
+// will be supported by the library, it is subject to change without notice that
+// may result in compilation errors or runtime behaviour changes.
+func UnsafeMsgOptionEndpoint(endpoint string, update func(url.Values)) MsgOption {
+ return func(config *sendConfig) error {
+ config.endpoint = endpoint
+ update(config.values)
+ return nil
+ }
+}
+
// MsgOptionPostMessageParameters maintain backwards compatibility.
func MsgOptionPostMessageParameters(params PostMessageParameters) MsgOption {
return func(config *sendConfig) error {
@@ -446,3 +472,38 @@ func MsgOptionPostMessageParameters(params PostMessageParameters) MsgOption {
return nil
}
}
+
+// PermalinkParameters are the parameters required to get a permalink to a
+// message. Slack documentation can be found here:
+// https://api.slack.com/methods/chat.getPermalink
+type PermalinkParameters struct {
+ Channel string
+ Ts string
+}
+
+// GetPermalink returns the permalink for a message. It takes
+// PermalinkParameters and returns a string containing the permalink. It
+// returns an error if unable to retrieve the permalink.
+func (api *Client) GetPermalink(params *PermalinkParameters) (string, error) {
+ return api.GetPermalinkContext(context.Background(), params)
+}
+
+// GetPermalinkContext returns the permalink for a message using a custom context.
+func (api *Client) GetPermalinkContext(ctx context.Context, params *PermalinkParameters) (string, error) {
+ values := url.Values{
+ "token": {api.token},
+ "channel": {params.Channel},
+ "message_ts": {params.Ts},
+ }
+
+ response := struct {
+ Channel string `json:"channel"`
+ Permalink string `json:"permalink"`
+ SlackResponse
+ }{}
+ err := getSlackMethod(ctx, api.httpclient, "chat.getPermalink", values, &response, api)
+ if err != nil {
+ return "", err
+ }
+ return response.Permalink, response.Err()
+}
diff --git a/vendor/github.com/nlopes/slack/conversation.go b/vendor/github.com/nlopes/slack/conversation.go
index edde87a2..ccd38f88 100644
--- a/vendor/github.com/nlopes/slack/conversation.go
+++ b/vendor/github.com/nlopes/slack/conversation.go
@@ -29,6 +29,8 @@ type conversation struct {
NameNormalized string `json:"name_normalized"`
NumMembers int `json:"num_members"`
Priority float64 `json:"priority"`
+ User string `json:"user"`
+
// TODO support pending_shared
// TODO support previous_names
}
@@ -64,6 +66,13 @@ type GetUsersInConversationParameters struct {
Limit int
}
+type GetConversationsForUserParameters struct {
+ UserID string
+ Cursor string
+ Types []string
+ Limit int
+}
+
type responseMetaData struct {
NextCursor string `json:"next_cursor"`
}
@@ -90,7 +99,7 @@ func (api *Client) GetUsersInConversationContext(ctx context.Context, params *Ge
ResponseMetaData responseMetaData `json:"response_metadata"`
SlackResponse
}{}
- err := postSlackMethod(ctx, api.httpclient, "conversations.members", values, &response, api.debug)
+ err := postSlackMethod(ctx, api.httpclient, "conversations.members", values, &response, api)
if err != nil {
return nil, "", err
}
@@ -100,6 +109,41 @@ func (api *Client) GetUsersInConversationContext(ctx context.Context, params *Ge
return response.Members, response.ResponseMetaData.NextCursor, nil
}
+// GetConversationsForUser returns the list conversations for a given user
+func (api *Client) GetConversationsForUser(params *GetConversationsForUserParameters) (channels []Channel, nextCursor string, err error) {
+ return api.GetConversationsForUserContext(context.Background(), params)
+}
+
+// GetConversationsForUserContext returns the list conversations for a given user with a custom context
+func (api *Client) GetConversationsForUserContext(ctx context.Context, params *GetConversationsForUserParameters) (channels []Channel, nextCursor string, err error) {
+ values := url.Values{
+ "token": {api.token},
+ }
+ if params.UserID != "" {
+ values.Add("user", params.UserID)
+ }
+ if params.Cursor != "" {
+ values.Add("cursor", params.Cursor)
+ }
+ if params.Limit != 0 {
+ values.Add("limit", strconv.Itoa(params.Limit))
+ }
+ if params.Types != nil {
+ values.Add("types", strings.Join(params.Types, ","))
+ }
+ response := struct {
+ Channels []Channel `json:"channels"`
+ ResponseMetaData responseMetaData `json:"response_metadata"`
+ SlackResponse
+ }{}
+ err = postSlackMethod(ctx, api.httpclient, "users.conversations", values, &response, api)
+ if err != nil {
+ return nil, "", err
+ }
+
+ return response.Channels, response.ResponseMetaData.NextCursor, response.Err()
+}
+
// ArchiveConversation archives a conversation
func (api *Client) ArchiveConversation(channelID string) error {
return api.ArchiveConversationContext(context.Background(), channelID)
@@ -112,7 +156,7 @@ func (api *Client) ArchiveConversationContext(ctx context.Context, channelID str
"channel": {channelID},
}
response := SlackResponse{}
- err := postSlackMethod(ctx, api.httpclient, "conversations.archive", values, &response, api.debug)
+ err := postSlackMethod(ctx, api.httpclient, "conversations.archive", values, &response, api)
if err != nil {
return err
}
@@ -132,7 +176,7 @@ func (api *Client) UnArchiveConversationContext(ctx context.Context, channelID s
"channel": {channelID},
}
response := SlackResponse{}
- err := postSlackMethod(ctx, api.httpclient, "conversations.unarchive", values, &response, api.debug)
+ err := postSlackMethod(ctx, api.httpclient, "conversations.unarchive", values, &response, api)
if err != nil {
return err
}
@@ -156,7 +200,7 @@ func (api *Client) SetTopicOfConversationContext(ctx context.Context, channelID,
SlackResponse
Channel *Channel `json:"channel"`
}{}
- err := postSlackMethod(ctx, api.httpclient, "conversations.setTopic", values, &response, api.debug)
+ err := postSlackMethod(ctx, api.httpclient, "conversations.setTopic", values, &response, api)
if err != nil {
return nil, err
}
@@ -180,7 +224,7 @@ func (api *Client) SetPurposeOfConversationContext(ctx context.Context, channelI
SlackResponse
Channel *Channel `json:"channel"`
}{}
- err := postSlackMethod(ctx, api.httpclient, "conversations.setPurpose", values, &response, api.debug)
+ err := postSlackMethod(ctx, api.httpclient, "conversations.setPurpose", values, &response, api)
if err != nil {
return nil, err
}
@@ -204,7 +248,7 @@ func (api *Client) RenameConversationContext(ctx context.Context, channelID, cha
SlackResponse
Channel *Channel `json:"channel"`
}{}
- err := postSlackMethod(ctx, api.httpclient, "conversations.rename", values, &response, api.debug)
+ err := postSlackMethod(ctx, api.httpclient, "conversations.rename", values, &response, api)
if err != nil {
return nil, err
}
@@ -228,7 +272,7 @@ func (api *Client) InviteUsersToConversationContext(ctx context.Context, channel
SlackResponse
Channel *Channel `json:"channel"`
}{}
- err := postSlackMethod(ctx, api.httpclient, "conversations.invite", values, &response, api.debug)
+ err := postSlackMethod(ctx, api.httpclient, "conversations.invite", values, &response, api)
if err != nil {
return nil, err
}
@@ -249,7 +293,7 @@ func (api *Client) KickUserFromConversationContext(ctx context.Context, channelI
"user": {user},
}
response := SlackResponse{}
- err := postSlackMethod(ctx, api.httpclient, "conversations.kick", values, &response, api.debug)
+ err := postSlackMethod(ctx, api.httpclient, "conversations.kick", values, &response, api)
if err != nil {
return err
}
@@ -274,7 +318,7 @@ func (api *Client) CloseConversationContext(ctx context.Context, channelID strin
AlreadyClosed bool `json:"already_closed"`
}{}
- err = postSlackMethod(ctx, api.httpclient, "conversations.close", values, &response, api.debug)
+ err = postSlackMethod(ctx, api.httpclient, "conversations.close", values, &response, api)
if err != nil {
return false, false, err
}
@@ -295,7 +339,7 @@ func (api *Client) CreateConversationContext(ctx context.Context, channelName st
"is_private": {strconv.FormatBool(isPrivate)},
}
response, err := channelRequest(
- ctx, api.httpclient, "conversations.create", values, api.debug)
+ ctx, api.httpclient, "conversations.create", values, api)
if err != nil {
return nil, err
}
@@ -316,7 +360,7 @@ func (api *Client) GetConversationInfoContext(ctx context.Context, channelID str
"include_locale": {strconv.FormatBool(includeLocale)},
}
response, err := channelRequest(
- ctx, api.httpclient, "conversations.info", values, api.debug)
+ ctx, api.httpclient, "conversations.info", values, api)
if err != nil {
return nil, err
}
@@ -336,7 +380,7 @@ func (api *Client) LeaveConversationContext(ctx context.Context, channelID strin
"channel": {channelID},
}
- response, err := channelRequest(ctx, api.httpclient, "conversations.leave", values, api.debug)
+ response, err := channelRequest(ctx, api.httpclient, "conversations.leave", values, api)
if err != nil {
return false, err
}
@@ -392,7 +436,7 @@ func (api *Client) GetConversationRepliesContext(ctx context.Context, params *Ge
Messages []Message `json:"messages"`
}{}
- err = postSlackMethod(ctx, api.httpclient, "conversations.replies", values, &response, api.debug)
+ err = postSlackMethod(ctx, api.httpclient, "conversations.replies", values, &response, api)
if err != nil {
return nil, false, "", err
}
@@ -432,7 +476,7 @@ func (api *Client) GetConversationsContext(ctx context.Context, params *GetConve
ResponseMetaData responseMetaData `json:"response_metadata"`
SlackResponse
}{}
- err = postSlackMethod(ctx, api.httpclient, "conversations.list", values, &response, api.debug)
+ err = postSlackMethod(ctx, api.httpclient, "conversations.list", values, &response, api)
if err != nil {
return nil, "", err
}
@@ -469,7 +513,7 @@ func (api *Client) OpenConversationContext(ctx context.Context, params *OpenConv
AlreadyOpen bool `json:"already_open"`
SlackResponse
}{}
- err := postSlackMethod(ctx, api.httpclient, "conversations.open", values, &response, api.debug)
+ err := postSlackMethod(ctx, api.httpclient, "conversations.open", values, &response, api)
if err != nil {
return nil, false, false, err
}
@@ -493,7 +537,7 @@ func (api *Client) JoinConversationContext(ctx context.Context, channelID string
} `json:"response_metadata"`
SlackResponse
}{}
- err := postSlackMethod(ctx, api.httpclient, "conversations.join", values, &response, api.debug)
+ err := postSlackMethod(ctx, api.httpclient, "conversations.join", values, &response, api)
if err != nil {
return nil, "", nil, err
}
@@ -555,12 +599,10 @@ func (api *Client) GetConversationHistoryContext(ctx context.Context, params *Ge
response := GetConversationHistoryResponse{}
- err := postSlackMethod(ctx, api.httpclient, "conversations.history", values, &response, api.debug)
+ err := postSlackMethod(ctx, api.httpclient, "conversations.history", values, &response, api)
if err != nil {
return nil, err
}
- if !response.Ok {
- return nil, errors.New(response.Error)
- }
- return &response, nil
+
+ return &response, response.Err()
}
diff --git a/vendor/github.com/nlopes/slack/dialog.go b/vendor/github.com/nlopes/slack/dialog.go
index a13e53da..2e45a06c 100644
--- a/vendor/github.com/nlopes/slack/dialog.go
+++ b/vendor/github.com/nlopes/slack/dialog.go
@@ -6,100 +6,94 @@ import (
"errors"
)
+// InputType is the type of the dialog input type
+type InputType string
+
+const (
+ // InputTypeText textfield input
+ InputTypeText InputType = "text"
+ // InputTypeTextArea textarea input
+ InputTypeTextArea InputType = "textarea"
+ // InputTypeSelect textfield input
+ InputTypeSelect InputType = "select"
+)
+
+// DialogInput for dialogs input type text or menu
+type DialogInput struct {
+ Type InputType `json:"type"`
+ Label string `json:"label"`
+ Name string `json:"name"`
+ Placeholder string `json:"placeholder"`
+ Optional bool `json:"optional"`
+}
+
+// DialogTrigger ...
type DialogTrigger struct {
- TriggerId string `json:"trigger_id"` //Required. Must respond within 3 seconds.
+ TriggerID string `json:"trigger_id"` //Required. Must respond within 3 seconds.
Dialog Dialog `json:"dialog"` //Required.
}
+// Dialog as in Slack dialogs
+// https://api.slack.com/dialogs#option_element_attributes#top-level_dialog_attributes
type Dialog struct {
- CallbackId string `json:"callback_id"` //Required.
- Title string `json:"title"` //Required.
- SubmitLabel string `json:"submit_label,omitempty"` //Optional. Default value is 'Submit'
- NotifyOnCancel bool `json:"notify_on_cancel,omitempty"` //Optional. Default value is false
- Elements []DialogElement `json:"elements"` //Required.
+ TriggerID string `json:"trigger_id"` // Required
+ CallbackID string `json:"callback_id"` // Required
+ State string `json:"state,omitempty"` // Optional
+ Title string `json:"title"`
+ SubmitLabel string `json:"submit_label,omitempty"`
+ NotifyOnCancel bool `json:"notify_on_cancel"`
+ Elements []DialogElement `json:"elements"`
}
+// DialogElement abstract type for dialogs.
type DialogElement interface{}
-type DialogTextElement struct {
- Label string `json:"label"` //Required.
- Name string `json:"name"` //Required.
- Type string `json:"type"` //Required. Allowed values: "text", "textarea", "select".
- Placeholder string `json:"placeholder,omitempty"` //Optional.
- Optional bool `json:"optional,omitempty"` //Optional. Default value is false
- Value string `json:"value,omitempty"` //Optional.
- MaxLength int `json:"max_length,omitempty"` //Optional.
- MinLength int `json:"min_length,omitempty"` //Optional,. Default value is 0
- Hint string `json:"hint,omitempty"` //Optional.
- Subtype string `json:"subtype,omitempty"` //Optional. Allowed values: "email", "number", "tel", "url".
+// DialogCallback DEPRECATED use InteractionCallback
+type DialogCallback InteractionCallback
+
+// DialogSubmissionCallback is sent from Slack when a user submits a form from within a dialog
+type DialogSubmissionCallback struct {
+ State string `json:"state,omitempty"`
+ Submission map[string]string `json:"submission"`
}
-type DialogSelectElement struct {
- Label string `json:"label"` //Required.
- Name string `json:"name"` //Required.
- Type string `json:"type"` //Required. Allowed values: "text", "textarea", "select".
- Placeholder string `json:"placeholder,omitempty"` //Optional.
- Optional bool `json:"optional,omitempty"` //Optional. Default value is false
- Value string `json:"value,omitempty"` //Optional.
- DataSource string `json:"data_source,omitempty"` //Optional. Allowed values: "users", "channels", "conversations", "external".
- SelectedOptions string `json:"selected_options,omitempty"` //Optional. Default value for "external" only
- Options []DialogElementOption `json:"options,omitempty"` //One of options or option_groups is required.
- OptionGroups []DialogElementOption `json:"option_groups,omitempty"` //Provide up to 100 options.
+// DialogOpenResponse response from `dialog.open`
+type DialogOpenResponse struct {
+ SlackResponse
+ DialogResponseMetadata DialogResponseMetadata `json:"response_metadata"`
}
-type DialogElementOption struct {
- Label string `json:"label"` //Required.
- Value string `json:"value"` //Required.
+// DialogResponseMetadata lists the error messages
+type DialogResponseMetadata struct {
+ Messages []string `json:"messages"`
}
-// DialogCallback is sent from Slack when a user submits a form from within a dialog
-type DialogCallback struct {
- Type string `json:"type"`
- CallbackID string `json:"callback_id"`
- Team Team `json:"team"`
- Channel Channel `json:"channel"`
- User User `json:"user"`
- ActionTs string `json:"action_ts"`
- Token string `json:"token"`
- ResponseURL string `json:"response_url"`
- Submission map[string]string `json:"submission"`
-}
-
-// DialogSuggestionCallback is sent from Slack when a user types in a select field with an external data source
-type DialogSuggestionCallback struct {
- Type string `json:"type"`
- Token string `json:"token"`
- ActionTs string `json:"action_ts"`
- Team Team `json:"team"`
- User User `json:"user"`
- Channel Channel `json:"channel"`
- ElementName string `json:"name"`
- Value string `json:"value"`
- CallbackID string `json:"callback_id"`
-}
-
-// OpenDialog opens a dialog window where the triggerId originated from
-func (api *Client) OpenDialog(triggerId string, dialog Dialog) (err error) {
- return api.OpenDialogContext(context.Background(), triggerId, dialog)
+// OpenDialog opens a dialog window where the triggerID originated from.
+// EXPERIMENTAL: dialog functionality is currently experimental, api is not considered stable.
+func (api *Client) OpenDialog(triggerID string, dialog Dialog) (err error) {
+ return api.OpenDialogContext(context.Background(), triggerID, dialog)
}
// OpenDialogContext opens a dialog window where the triggerId originated from with a custom context
-func (api *Client) OpenDialogContext(ctx context.Context, triggerId string, dialog Dialog) (err error) {
- if triggerId == "" {
+// EXPERIMENTAL: dialog functionality is currently experimental, api is not considered stable.
+func (api *Client) OpenDialogContext(ctx context.Context, triggerID string, dialog Dialog) (err error) {
+ if triggerID == "" {
return errors.New("received empty parameters")
}
- resp := DialogTrigger{
- TriggerId: triggerId,
+ req := DialogTrigger{
+ TriggerID: triggerID,
Dialog: dialog,
}
- jsonResp, err := json.Marshal(resp)
+
+ encoded, err := json.Marshal(req)
if err != nil {
return err
}
- response := &SlackResponse{}
- endpoint := SLACK_API + "dialog.open"
- if err := postJSON(ctx, api.httpclient, endpoint, api.token, jsonResp, response, api.debug); err != nil {
+
+ response := &DialogOpenResponse{}
+ endpoint := APIURL + "dialog.open"
+ if err := postJSON(ctx, api.httpclient, endpoint, api.token, encoded, response, api); err != nil {
return err
}
diff --git a/vendor/github.com/nlopes/slack/dialog_select.go b/vendor/github.com/nlopes/slack/dialog_select.go
new file mode 100644
index 00000000..ea95ccfa
--- /dev/null
+++ b/vendor/github.com/nlopes/slack/dialog_select.go
@@ -0,0 +1,126 @@
+package slack
+
+// SelectDataSource types of select datasource
+type SelectDataSource string
+
+const (
+ // DialogDataSourceStatic menu with static Options/OptionGroups
+ DialogDataSourceStatic SelectDataSource = "static"
+ // DialogDataSourceExternal dynamic datasource
+ DialogDataSourceExternal SelectDataSource = "external"
+ // DialogDataSourceConversations provides a list of conversations
+ DialogDataSourceConversations SelectDataSource = "conversations"
+ // DialogDataSourceChannels provides a list of channels
+ DialogDataSourceChannels SelectDataSource = "channels"
+ // DialogDataSourceUsers provides a list of users
+ DialogDataSourceUsers SelectDataSource = "users"
+)
+
+// DialogInputSelect dialog support for select boxes.
+type DialogInputSelect struct {
+ DialogInput
+ Value string `json:"value,omitempty"` //Optional.
+ DataSource SelectDataSource `json:"data_source,omitempty"` //Optional. Allowed values: "users", "channels", "conversations", "external".
+ SelectedOptions string `json:"selected_options,omitempty"` //Optional. Default value for "external" only
+ Options []DialogSelectOption `json:"options,omitempty"` //One of options or option_groups is required.
+ OptionGroups []DialogOptionGroup `json:"option_groups,omitempty"` //Provide up to 100 options.
+ MinQueryLength int `json:"min_query_length,omitempty"` //Optional. minimum characters before query is sent.
+}
+
+// DialogSelectOption is an option for the user to select from the menu
+type DialogSelectOption struct {
+ Label string `json:"label"`
+ Value string `json:"value"`
+}
+
+// DialogOptionGroup is a collection of options for creating a segmented table
+type DialogOptionGroup struct {
+ Label string `json:"label"`
+ Options []DialogSelectOption `json:"options"`
+}
+
+// NewStaticSelectDialogInput constructor for a `static` datasource menu input
+func NewStaticSelectDialogInput(name, label string, options []DialogSelectOption) *DialogInputSelect {
+ return &DialogInputSelect{
+ DialogInput: DialogInput{
+ Type: InputTypeSelect,
+ Name: name,
+ Label: label,
+ Optional: true,
+ },
+ DataSource: DialogDataSourceStatic,
+ Options: options,
+ }
+}
+
+// NewGroupedSelectDialogInput creates grouped options select input for Dialogs.
+func NewGroupedSelectDialogInput(name, label string, groups map[string]map[string]string) *DialogInputSelect {
+ optionGroups := []DialogOptionGroup{}
+ for groupName, options := range groups {
+ optionGroups = append(optionGroups, DialogOptionGroup{
+ Label: groupName,
+ Options: optionsFromMap(options),
+ })
+ }
+ return &DialogInputSelect{
+ DialogInput: DialogInput{
+ Type: InputTypeSelect,
+ Name: name,
+ Label: label,
+ },
+ DataSource: DialogDataSourceStatic,
+ OptionGroups: optionGroups,
+ }
+}
+
+func optionsFromArray(options []string) []DialogSelectOption {
+ selectOptions := make([]DialogSelectOption, len(options))
+ for idx, value := range options {
+ selectOptions[idx] = DialogSelectOption{
+ Label: value,
+ Value: value,
+ }
+ }
+ return selectOptions
+}
+
+func optionsFromMap(options map[string]string) []DialogSelectOption {
+ selectOptions := make([]DialogSelectOption, len(options))
+ idx := 0
+ var option DialogSelectOption
+ for key, value := range options {
+ option = DialogSelectOption{
+ Label: key,
+ Value: value,
+ }
+ selectOptions[idx] = option
+ idx++
+ }
+ return selectOptions
+}
+
+// NewConversationsSelect returns a `Conversations` select
+func NewConversationsSelect(name, label string) *DialogInputSelect {
+ return newPresetSelect(name, label, DialogDataSourceConversations)
+}
+
+// NewChannelsSelect returns a `Channels` select
+func NewChannelsSelect(name, label string) *DialogInputSelect {
+ return newPresetSelect(name, label, DialogDataSourceChannels)
+}
+
+// NewUsersSelect returns a `Users` select
+func NewUsersSelect(name, label string) *DialogInputSelect {
+ return newPresetSelect(name, label, DialogDataSourceUsers)
+}
+
+func newPresetSelect(name, label string, dataSourceType SelectDataSource) *DialogInputSelect {
+ return &DialogInputSelect{
+ DialogInput: DialogInput{
+ Type: InputTypeSelect,
+ Label: label,
+ Name: name,
+ },
+ DataSource: dataSourceType,
+ }
+}
diff --git a/vendor/github.com/nlopes/slack/dialog_text.go b/vendor/github.com/nlopes/slack/dialog_text.go
new file mode 100644
index 00000000..bf9602cc
--- /dev/null
+++ b/vendor/github.com/nlopes/slack/dialog_text.go
@@ -0,0 +1,50 @@
+package slack
+
+// TextInputSubtype Accepts email, number, tel, or url. In some form factors, optimized input is provided for this subtype.
+type TextInputSubtype string
+
+const (
+ // InputSubtypeEmail email keyboard
+ InputSubtypeEmail TextInputSubtype = "email"
+ // InputSubtypeNumber numeric keyboard
+ InputSubtypeNumber TextInputSubtype = "number"
+ // InputSubtypeTel Phone keyboard
+ InputSubtypeTel TextInputSubtype = "tel"
+ // InputSubtypeURL Phone keyboard
+ InputSubtypeURL TextInputSubtype = "url"
+)
+
+// TextInputElement subtype of DialogInput
+// https://api.slack.com/dialogs#option_element_attributes#text_element_attributes
+type TextInputElement struct {
+ DialogInput
+ MaxLength int `json:"max_length,omitempty"`
+ MinLength int `json:"min_length,omitempty"`
+ Hint string `json:"hint,omitempty"`
+ Subtype TextInputSubtype `json:"subtype"`
+ Value string `json:"value"`
+}
+
+// NewTextInput constructor for a `text` input
+func NewTextInput(name, label, text string) *TextInputElement {
+ return &TextInputElement{
+ DialogInput: DialogInput{
+ Type: InputTypeText,
+ Name: name,
+ Label: label,
+ },
+ Value: text,
+ }
+}
+
+// NewTextAreaInput constructor for a `textarea` input
+func NewTextAreaInput(name, label, text string) *TextInputElement {
+ return &TextInputElement{
+ DialogInput: DialogInput{
+ Type: InputTypeTextArea,
+ Name: name,
+ Label: label,
+ },
+ Value: text,
+ }
+}
diff --git a/vendor/github.com/nlopes/slack/dnd.go b/vendor/github.com/nlopes/slack/dnd.go
index 26d36d6a..da6e4a16 100644
--- a/vendor/github.com/nlopes/slack/dnd.go
+++ b/vendor/github.com/nlopes/slack/dnd.go
@@ -36,9 +36,9 @@ type dndTeamInfoResponse struct {
SlackResponse
}
-func dndRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*dndResponseFull, error) {
+func dndRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (*dndResponseFull, error) {
response := &dndResponseFull{}
- err := postSlackMethod(ctx, client, path, values, response, debug)
+ err := postSlackMethod(ctx, client, path, values, response, d)
if err != nil {
return nil, err
}
@@ -61,7 +61,7 @@ func (api *Client) EndDNDContext(ctx context.Context) error {
response := &SlackResponse{}
- if err := postSlackMethod(ctx, api.httpclient, "dnd.endDnd", values, response, api.debug); err != nil {
+ if err := postSlackMethod(ctx, api.httpclient, "dnd.endDnd", values, response, api); err != nil {
return err
}
@@ -79,7 +79,7 @@ func (api *Client) EndSnoozeContext(ctx context.Context) (*DNDStatus, error) {
"token": {api.token},
}
- response, err := dndRequest(ctx, api.httpclient, "dnd.endSnooze", values, api.debug)
+ response, err := dndRequest(ctx, api.httpclient, "dnd.endSnooze", values, api)
if err != nil {
return nil, err
}
@@ -100,7 +100,7 @@ func (api *Client) GetDNDInfoContext(ctx context.Context, user *string) (*DNDSta
values.Set("user", *user)
}
- response, err := dndRequest(ctx, api.httpclient, "dnd.info", values, api.debug)
+ response, err := dndRequest(ctx, api.httpclient, "dnd.info", values, api)
if err != nil {
return nil, err
}
@@ -120,11 +120,12 @@ func (api *Client) GetDNDTeamInfoContext(ctx context.Context, users []string) (m
}
response := &dndTeamInfoResponse{}
- if err := postSlackMethod(ctx, api.httpclient, "dnd.teamInfo", values, response, api.debug); err != nil {
+ if err := postSlackMethod(ctx, api.httpclient, "dnd.teamInfo", values, response, api); err != nil {
return nil, err
}
- if !response.Ok {
- return nil, errors.New(response.Error)
+
+ if response.Err() != nil {
+ return nil, response.Err()
}
return response.Users, nil
}
@@ -144,7 +145,7 @@ func (api *Client) SetSnoozeContext(ctx context.Context, minutes int) (*DNDStatu
"num_minutes": {strconv.Itoa(minutes)},
}
- response, err := dndRequest(ctx, api.httpclient, "dnd.setSnooze", values, api.debug)
+ response, err := dndRequest(ctx, api.httpclient, "dnd.setSnooze", values, api)
if err != nil {
return nil, err
}
diff --git a/vendor/github.com/nlopes/slack/emoji.go b/vendor/github.com/nlopes/slack/emoji.go
index fe2945c4..aed2129f 100644
--- a/vendor/github.com/nlopes/slack/emoji.go
+++ b/vendor/github.com/nlopes/slack/emoji.go
@@ -23,7 +23,7 @@ func (api *Client) GetEmojiContext(ctx context.Context) (map[string]string, erro
}
response := &emojiResponseFull{}
- err := postSlackMethod(ctx, api.httpclient, "emoji.list", values, response, api.debug)
+ err := postSlackMethod(ctx, api.httpclient, "emoji.list", values, response, api)
if err != nil {
return nil, err
}
diff --git a/vendor/github.com/nlopes/slack/files.go b/vendor/github.com/nlopes/slack/files.go
index 2381ec3c..13158582 100644
--- a/vendor/github.com/nlopes/slack/files.go
+++ b/vendor/github.com/nlopes/slack/files.go
@@ -93,14 +93,15 @@ type File struct {
// There are three ways to upload a file. You can either set Content if file is small, set Reader if file is large,
// or provide a local file path in File to upload it from your filesystem.
type FileUploadParameters struct {
- File string
- Content string
- Reader io.Reader
- Filetype string
- Filename string
- Title string
- InitialComment string
- Channels []string
+ File string
+ Content string
+ Reader io.Reader
+ Filetype string
+ Filename string
+ Title string
+ InitialComment string
+ Channels []string
+ ThreadTimestamp string
}
// GetFilesParameters contains all the parameters necessary (including the optional ones) for a GetFiles() request
@@ -136,16 +137,14 @@ func NewGetFilesParameters() GetFilesParameters {
}
}
-func fileRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*fileResponseFull, error) {
+func fileRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (*fileResponseFull, error) {
response := &fileResponseFull{}
- err := postForm(ctx, client, SLACK_API+path, values, response, debug)
+ err := postForm(ctx, client, APIURL+path, values, response, d)
if err != nil {
return nil, err
}
- if !response.Ok {
- return nil, errors.New(response.Error)
- }
- return response, nil
+
+ return response, response.Err()
}
// GetFileInfo retrieves a file and related comments
@@ -162,7 +161,7 @@ func (api *Client) GetFileInfoContext(ctx context.Context, fileID string, count,
"page": {strconv.Itoa(page)},
}
- response, err := fileRequest(ctx, api.httpclient, "files.info", values, api.debug)
+ response, err := fileRequest(ctx, api.httpclient, "files.info", values, api)
if err != nil {
return nil, nil, nil, err
}
@@ -201,7 +200,7 @@ func (api *Client) GetFilesContext(ctx context.Context, params GetFilesParameter
values.Add("page", strconv.Itoa(params.Page))
}
- response, err := fileRequest(ctx, api.httpclient, "files.list", values, api.debug)
+ response, err := fileRequest(ctx, api.httpclient, "files.list", values, api)
if err != nil {
return nil, nil, err
}
@@ -237,24 +236,25 @@ func (api *Client) UploadFileContext(ctx context.Context, params FileUploadParam
if params.InitialComment != "" {
values.Add("initial_comment", params.InitialComment)
}
+ if params.ThreadTimestamp != "" {
+ values.Add("thread_ts", params.ThreadTimestamp)
+ }
if len(params.Channels) != 0 {
values.Add("channels", strings.Join(params.Channels, ","))
}
if params.Content != "" {
values.Add("content", params.Content)
- err = postForm(ctx, api.httpclient, SLACK_API+"files.upload", values, response, api.debug)
+ err = postForm(ctx, api.httpclient, APIURL+"files.upload", values, response, api)
} else if params.File != "" {
- err = postLocalWithMultipartResponse(ctx, api.httpclient, "files.upload", params.File, "file", values, response, api.debug)
+ err = postLocalWithMultipartResponse(ctx, api.httpclient, "files.upload", params.File, "file", values, response, api)
} else if params.Reader != nil {
- err = postWithMultipartResponse(ctx, api.httpclient, "files.upload", params.Filename, "file", values, params.Reader, response, api.debug)
+ err = postWithMultipartResponse(ctx, api.httpclient, "files.upload", params.Filename, "file", values, params.Reader, response, api)
}
if err != nil {
return nil, err
}
- if !response.Ok {
- return nil, errors.New(response.Error)
- }
- return &response.File, nil
+
+ return &response.File, response.Err()
}
// DeleteFileComment deletes a file's comment
@@ -273,7 +273,7 @@ func (api *Client) DeleteFileCommentContext(ctx context.Context, fileID, comment
"file": {fileID},
"id": {commentID},
}
- _, err = fileRequest(ctx, api.httpclient, "files.comments.delete", values, api.debug)
+ _, err = fileRequest(ctx, api.httpclient, "files.comments.delete", values, api)
return err
}
@@ -289,7 +289,7 @@ func (api *Client) DeleteFileContext(ctx context.Context, fileID string) (err er
"file": {fileID},
}
- _, err = fileRequest(ctx, api.httpclient, "files.delete", values, api.debug)
+ _, err = fileRequest(ctx, api.httpclient, "files.delete", values, api)
return err
}
@@ -305,7 +305,7 @@ func (api *Client) RevokeFilePublicURLContext(ctx context.Context, fileID string
"file": {fileID},
}
- response, err := fileRequest(ctx, api.httpclient, "files.revokePublicURL", values, api.debug)
+ response, err := fileRequest(ctx, api.httpclient, "files.revokePublicURL", values, api)
if err != nil {
return nil, err
}
@@ -324,7 +324,7 @@ func (api *Client) ShareFilePublicURLContext(ctx context.Context, fileID string)
"file": {fileID},
}
- response, err := fileRequest(ctx, api.httpclient, "files.sharedPublicURL", values, api.debug)
+ response, err := fileRequest(ctx, api.httpclient, "files.sharedPublicURL", values, api)
if err != nil {
return nil, nil, nil, err
}
diff --git a/vendor/github.com/nlopes/slack/groups.go b/vendor/github.com/nlopes/slack/groups.go
index 67e78e99..a248f6fd 100644
--- a/vendor/github.com/nlopes/slack/groups.go
+++ b/vendor/github.com/nlopes/slack/groups.go
@@ -2,7 +2,6 @@ package slack
import (
"context"
- "errors"
"net/url"
"strconv"
)
@@ -28,16 +27,14 @@ type groupResponseFull struct {
SlackResponse
}
-func groupRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*groupResponseFull, error) {
+func groupRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (*groupResponseFull, error) {
response := &groupResponseFull{}
- err := postForm(ctx, client, SLACK_API+path, values, response, debug)
+ err := postForm(ctx, client, APIURL+path, values, response, d)
if err != nil {
return nil, err
}
- if !response.Ok {
- return nil, errors.New(response.Error)
- }
- return response, nil
+
+ return response, response.Err()
}
// ArchiveGroup archives a private group
@@ -52,7 +49,7 @@ func (api *Client) ArchiveGroupContext(ctx context.Context, group string) error
"channel": {group},
}
- _, err := groupRequest(ctx, api.httpclient, "groups.archive", values, api.debug)
+ _, err := groupRequest(ctx, api.httpclient, "groups.archive", values, api)
return err
}
@@ -68,7 +65,7 @@ func (api *Client) UnarchiveGroupContext(ctx context.Context, group string) erro
"channel": {group},
}
- _, err := groupRequest(ctx, api.httpclient, "groups.unarchive", values, api.debug)
+ _, err := groupRequest(ctx, api.httpclient, "groups.unarchive", values, api)
return err
}
@@ -84,7 +81,7 @@ func (api *Client) CreateGroupContext(ctx context.Context, group string) (*Group
"name": {group},
}
- response, err := groupRequest(ctx, api.httpclient, "groups.create", values, api.debug)
+ response, err := groupRequest(ctx, api.httpclient, "groups.create", values, api)
if err != nil {
return nil, err
}
@@ -109,7 +106,7 @@ func (api *Client) CreateChildGroupContext(ctx context.Context, group string) (*
"channel": {group},
}
- response, err := groupRequest(ctx, api.httpclient, "groups.createChild", values, api.debug)
+ response, err := groupRequest(ctx, api.httpclient, "groups.createChild", values, api)
if err != nil {
return nil, err
}
@@ -128,7 +125,7 @@ func (api *Client) CloseGroupContext(ctx context.Context, group string) (bool, b
"channel": {group},
}
- response, err := imRequest(ctx, api.httpclient, "groups.close", values, api.debug)
+ response, err := imRequest(ctx, api.httpclient, "groups.close", values, api)
if err != nil {
return false, false, err
}
@@ -170,7 +167,7 @@ func (api *Client) GetGroupHistoryContext(ctx context.Context, group string, par
}
}
- response, err := groupRequest(ctx, api.httpclient, "groups.history", values, api.debug)
+ response, err := groupRequest(ctx, api.httpclient, "groups.history", values, api)
if err != nil {
return nil, err
}
@@ -190,7 +187,7 @@ func (api *Client) InviteUserToGroupContext(ctx context.Context, group, user str
"user": {user},
}
- response, err := groupRequest(ctx, api.httpclient, "groups.invite", values, api.debug)
+ response, err := groupRequest(ctx, api.httpclient, "groups.invite", values, api)
if err != nil {
return nil, false, err
}
@@ -209,7 +206,7 @@ func (api *Client) LeaveGroupContext(ctx context.Context, group string) (err err
"channel": {group},
}
- _, err = groupRequest(ctx, api.httpclient, "groups.leave", values, api.debug)
+ _, err = groupRequest(ctx, api.httpclient, "groups.leave", values, api)
return err
}
@@ -226,7 +223,7 @@ func (api *Client) KickUserFromGroupContext(ctx context.Context, group, user str
"user": {user},
}
- _, err = groupRequest(ctx, api.httpclient, "groups.kick", values, api.debug)
+ _, err = groupRequest(ctx, api.httpclient, "groups.kick", values, api)
return err
}
@@ -244,7 +241,7 @@ func (api *Client) GetGroupsContext(ctx context.Context, excludeArchived bool) (
values.Add("exclude_archived", "1")
}
- response, err := groupRequest(ctx, api.httpclient, "groups.list", values, api.debug)
+ response, err := groupRequest(ctx, api.httpclient, "groups.list", values, api)
if err != nil {
return nil, err
}
@@ -263,7 +260,7 @@ func (api *Client) GetGroupInfoContext(ctx context.Context, group string) (*Grou
"channel": {group},
}
- response, err := groupRequest(ctx, api.httpclient, "groups.info", values, api.debug)
+ response, err := groupRequest(ctx, api.httpclient, "groups.info", values, api)
if err != nil {
return nil, err
}
@@ -288,7 +285,7 @@ func (api *Client) SetGroupReadMarkContext(ctx context.Context, group, ts string
"ts": {ts},
}
- _, err = groupRequest(ctx, api.httpclient, "groups.mark", values, api.debug)
+ _, err = groupRequest(ctx, api.httpclient, "groups.mark", values, api)
return err
}
@@ -304,7 +301,7 @@ func (api *Client) OpenGroupContext(ctx context.Context, group string) (bool, bo
"channel": {group},
}
- response, err := groupRequest(ctx, api.httpclient, "groups.open", values, api.debug)
+ response, err := groupRequest(ctx, api.httpclient, "groups.open", values, api)
if err != nil {
return false, false, err
}
@@ -328,7 +325,7 @@ func (api *Client) RenameGroupContext(ctx context.Context, group, name string) (
// XXX: the created entry in this call returns a string instead of a number
// so I may have to do some workaround to solve it.
- response, err := groupRequest(ctx, api.httpclient, "groups.rename", values, api.debug)
+ response, err := groupRequest(ctx, api.httpclient, "groups.rename", values, api)
if err != nil {
return nil, err
}
@@ -348,7 +345,7 @@ func (api *Client) SetGroupPurposeContext(ctx context.Context, group, purpose st
"purpose": {purpose},
}
- response, err := groupRequest(ctx, api.httpclient, "groups.setPurpose", values, api.debug)
+ response, err := groupRequest(ctx, api.httpclient, "groups.setPurpose", values, api)
if err != nil {
return "", err
}
@@ -368,7 +365,7 @@ func (api *Client) SetGroupTopicContext(ctx context.Context, group, topic string
"topic": {topic},
}
- response, err := groupRequest(ctx, api.httpclient, "groups.setTopic", values, api.debug)
+ response, err := groupRequest(ctx, api.httpclient, "groups.setTopic", values, api)
if err != nil {
return "", err
}
diff --git a/vendor/github.com/nlopes/slack/im.go b/vendor/github.com/nlopes/slack/im.go
index fa8b0959..10563d91 100644
--- a/vendor/github.com/nlopes/slack/im.go
+++ b/vendor/github.com/nlopes/slack/im.go
@@ -2,7 +2,6 @@ package slack
import (
"context"
- "errors"
"net/url"
"strconv"
)
@@ -29,16 +28,14 @@ type IM struct {
IsUserDeleted bool `json:"is_user_deleted"`
}
-func imRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*imResponseFull, error) {
+func imRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (*imResponseFull, error) {
response := &imResponseFull{}
- err := postSlackMethod(ctx, client, path, values, response, debug)
+ err := postSlackMethod(ctx, client, path, values, response, d)
if err != nil {
return nil, err
}
- if !response.Ok {
- return nil, errors.New(response.Error)
- }
- return response, nil
+
+ return response, response.Err()
}
// CloseIMChannel closes the direct message channel
@@ -53,7 +50,7 @@ func (api *Client) CloseIMChannelContext(ctx context.Context, channel string) (b
"channel": {channel},
}
- response, err := imRequest(ctx, api.httpclient, "im.close", values, api.debug)
+ response, err := imRequest(ctx, api.httpclient, "im.close", values, api)
if err != nil {
return false, false, err
}
@@ -74,7 +71,7 @@ func (api *Client) OpenIMChannelContext(ctx context.Context, user string) (bool,
"user": {user},
}
- response, err := imRequest(ctx, api.httpclient, "im.open", values, api.debug)
+ response, err := imRequest(ctx, api.httpclient, "im.open", values, api)
if err != nil {
return false, false, "", err
}
@@ -94,7 +91,7 @@ func (api *Client) MarkIMChannelContext(ctx context.Context, channel, ts string)
"ts": {ts},
}
- _, err := imRequest(ctx, api.httpclient, "im.mark", values, api.debug)
+ _, err := imRequest(ctx, api.httpclient, "im.mark", values, api)
return err
}
@@ -133,7 +130,7 @@ func (api *Client) GetIMHistoryContext(ctx context.Context, channel string, para
}
}
- response, err := imRequest(ctx, api.httpclient, "im.history", values, api.debug)
+ response, err := imRequest(ctx, api.httpclient, "im.history", values, api)
if err != nil {
return nil, err
}
@@ -151,7 +148,7 @@ func (api *Client) GetIMChannelsContext(ctx context.Context) ([]IM, error) {
"token": {api.token},
}
- response, err := imRequest(ctx, api.httpclient, "im.list", values, api.debug)
+ response, err := imRequest(ctx, api.httpclient, "im.list", values, api)
if err != nil {
return nil, err
}
diff --git a/vendor/github.com/nlopes/slack/interactions.go b/vendor/github.com/nlopes/slack/interactions.go
new file mode 100644
index 00000000..97e31a49
--- /dev/null
+++ b/vendor/github.com/nlopes/slack/interactions.go
@@ -0,0 +1,31 @@
+package slack
+
+// InteractionType type of interactions
+type InteractionType string
+
+// Types of interactions that can be received.
+const (
+ InteractionTypeDialogSubmission = InteractionType("dialog_submission")
+ InteractionTypeDialogSuggestion = InteractionType("dialog_suggestion")
+ InteractionTypeInteractionMessage = InteractionType("interactive_message")
+ InteractionTypeMessageAction = InteractionType("message_action")
+)
+
+// InteractionCallback is sent from slack when a user interactions with a button or dialog.
+type InteractionCallback struct {
+ Type InteractionType `json:"type"`
+ Token string `json:"token"`
+ CallbackID string `json:"callback_id"`
+ ResponseURL string `json:"response_url"`
+ TriggerID string `json:"trigger_id"`
+ ActionTs string `json:"action_ts"`
+ Team Team `json:"team"`
+ Channel Channel `json:"channel"`
+ User User `json:"user"`
+ OriginalMessage Message `json:"original_message"`
+ Message Message `json:"message"`
+ Name string `json:"name"`
+ Value string `json:"value"`
+ ActionCallback
+ DialogSubmissionCallback
+}
diff --git a/vendor/github.com/nlopes/slack/logger.go b/vendor/github.com/nlopes/slack/logger.go
index 501d1672..6a3533a9 100644
--- a/vendor/github.com/nlopes/slack/logger.go
+++ b/vendor/github.com/nlopes/slack/logger.go
@@ -2,52 +2,59 @@ package slack
import (
"fmt"
- "sync"
)
-// SetLogger let's library users supply a logger, so that api debugging
-// can be logged along with the application's debugging info.
-func SetLogger(l logProvider) {
- loggerMutex.Lock()
- logger = ilogger{logProvider: l}
- loggerMutex.Unlock()
-}
-
-var (
- loggerMutex = new(sync.Mutex)
- logger logInternal // A logger that can be set by consumers
-)
-
-// logProvider is a logger interface compatible with both stdlib and some
-// 3rd party loggers such as logrus.
-type logProvider interface {
+// logger is a logger interface compatible with both stdlib and some
+// 3rd party loggers.
+type logger interface {
Output(int, string) error
}
-// logInternal represents the internal logging api we use.
-type logInternal interface {
+// ilogger represents the internal logging api we use.
+type ilogger interface {
+ logger
Print(...interface{})
Printf(string, ...interface{})
Println(...interface{})
- Output(int, string) error
}
-// ilogger implements the additional methods used by our internal logging.
-type ilogger struct {
- logProvider
+type debug interface {
+ Debug() bool
+
+ // Debugf print a formatted debug line.
+ Debugf(format string, v ...interface{})
+ // Debugln print a debug line.
+ Debugln(v ...interface{})
+}
+
+// internalLog implements the additional methods used by our internal logging.
+type internalLog struct {
+ logger
}
// Println replicates the behaviour of the standard logger.
-func (t ilogger) Println(v ...interface{}) {
+func (t internalLog) Println(v ...interface{}) {
t.Output(2, fmt.Sprintln(v...))
}
// Printf replicates the behaviour of the standard logger.
-func (t ilogger) Printf(format string, v ...interface{}) {
+func (t internalLog) Printf(format string, v ...interface{}) {
t.Output(2, fmt.Sprintf(format, v...))
}
// Print replicates the behaviour of the standard logger.
-func (t ilogger) Print(v ...interface{}) {
+func (t internalLog) Print(v ...interface{}) {
t.Output(2, fmt.Sprint(v...))
}
+
+type discard struct{}
+
+func (t discard) Debug() bool {
+ return false
+}
+
+// Debugf print a formatted debug line.
+func (t discard) Debugf(format string, v ...interface{}) {}
+
+// Debugln print a debug line.
+func (t discard) Debugln(v ...interface{}) {}
diff --git a/vendor/github.com/nlopes/slack/messages.go b/vendor/github.com/nlopes/slack/messages.go
index 6551dd4f..bde9a37e 100644
--- a/vendor/github.com/nlopes/slack/messages.go
+++ b/vendor/github.com/nlopes/slack/messages.go
@@ -4,11 +4,12 @@ package slack
type OutgoingMessage struct {
ID int `json:"id"`
// channel ID
- Channel string `json:"channel,omitempty"`
- Text string `json:"text,omitempty"`
- Type string `json:"type,omitempty"`
- ThreadTimestamp string `json:"thread_ts,omitempty"`
- ThreadBroadcast bool `json:"reply_broadcast,omitempty"`
+ Channel string `json:"channel,omitempty"`
+ Text string `json:"text,omitempty"`
+ Type string `json:"type,omitempty"`
+ ThreadTimestamp string `json:"thread_ts,omitempty"`
+ ThreadBroadcast bool `json:"reply_broadcast,omitempty"`
+ IDs []string `json:"ids,omitempty"`
}
// Message is an auxiliary type to allow us to have a message containing sub messages
@@ -147,6 +148,15 @@ func (rtm *RTM) NewOutgoingMessage(text string, channelID string, options ...RTM
return &msg
}
+// NewSubscribeUserPresence prepares an OutgoingMessage that the user can
+// use to subscribe presence events for the specified users.
+func (rtm *RTM) NewSubscribeUserPresence(ids []string) *OutgoingMessage {
+ return &OutgoingMessage{
+ Type: "presence_sub",
+ IDs: ids,
+ }
+}
+
// NewTypingMessage prepares an OutgoingMessage that the user can
// use to send as a typing indicator. Use this function to properly set the
// messageID.
@@ -174,5 +184,4 @@ func RTMsgOptionBroadcast() RTMsgOption {
return func(msg *OutgoingMessage) {
msg.ThreadBroadcast = true
}
-
}
diff --git a/vendor/github.com/nlopes/slack/misc.go b/vendor/github.com/nlopes/slack/misc.go
index 69103384..30ae4628 100644
--- a/vendor/github.com/nlopes/slack/misc.go
+++ b/vendor/github.com/nlopes/slack/misc.go
@@ -19,6 +19,7 @@ import (
"time"
)
+// SlackResponse handles parsing out errors from the web api.
type SlackResponse struct {
Ok bool `json:"ok"`
Error string `json:"error"`
@@ -64,47 +65,31 @@ func (e *RateLimitedError) Error() string {
return fmt.Sprintf("Slack rate limit exceeded, retry after %s", e.RetryAfter)
}
-func fileUploadReq(ctx context.Context, path, fieldname, filename string, values url.Values, r io.Reader) (*http.Request, error) {
- body := &bytes.Buffer{}
- wr := multipart.NewWriter(body)
+func fileUploadReq(ctx context.Context, path string, values url.Values, r io.Reader) (*http.Request, error) {
+ req, err := http.NewRequest("POST", path, r)
- ioWriter, err := wr.CreateFormFile(fieldname, filename)
- if err != nil {
- wr.Close()
- return nil, err
- }
- _, err = io.Copy(ioWriter, r)
- if err != nil {
- wr.Close()
- return nil, err
- }
- // Close the multipart writer or the footer won't be written
- wr.Close()
- req, err := http.NewRequest("POST", path, body)
req = req.WithContext(ctx)
if err != nil {
return nil, err
}
- req.Header.Add("Content-Type", wr.FormDataContentType())
req.URL.RawQuery = (values).Encode()
return req, nil
}
-func parseResponseBody(body io.ReadCloser, intf interface{}, debug bool) error {
+func parseResponseBody(body io.ReadCloser, intf interface{}, d debug) error {
response, err := ioutil.ReadAll(body)
if err != nil {
return err
}
- // FIXME: will be api.Debugf
- if debug {
- logger.Printf("parseResponseBody: %s\n", string(response))
+ if d.Debug() {
+ d.Debugln("parseResponseBody", string(response))
}
return json.Unmarshal(response, intf)
}
-func postLocalWithMultipartResponse(ctx context.Context, client HTTPRequester, path, fpath, fieldname string, values url.Values, intf interface{}, debug bool) error {
+func postLocalWithMultipartResponse(ctx context.Context, client httpClient, path, fpath, fieldname string, values url.Values, intf interface{}, d debug) error {
fullpath, err := filepath.Abs(fpath)
if err != nil {
return err
@@ -114,14 +99,65 @@ func postLocalWithMultipartResponse(ctx context.Context, client HTTPRequester, p
return err
}
defer file.Close()
- return postWithMultipartResponse(ctx, client, path, filepath.Base(fpath), fieldname, values, file, intf, debug)
+ return postWithMultipartResponse(ctx, client, path, filepath.Base(fpath), fieldname, values, file, intf, d)
}
-func postWithMultipartResponse(ctx context.Context, client HTTPRequester, path, name, fieldname string, values url.Values, r io.Reader, intf interface{}, debug bool) error {
- req, err := fileUploadReq(ctx, SLACK_API+path, fieldname, name, values, r)
+func postWithMultipartResponse(ctx context.Context, client httpClient, path, name, fieldname string, values url.Values, r io.Reader, intf interface{}, d debug) error {
+ pipeReader, pipeWriter := io.Pipe()
+ wr := multipart.NewWriter(pipeWriter)
+ errc := make(chan error)
+ go func() {
+ defer pipeWriter.Close()
+ ioWriter, err := wr.CreateFormFile(fieldname, name)
+ if err != nil {
+ errc <- err
+ return
+ }
+ _, err = io.Copy(ioWriter, r)
+ if err != nil {
+ errc <- err
+ return
+ }
+ if err = wr.Close(); err != nil {
+ errc <- err
+ return
+ }
+ }()
+ req, err := fileUploadReq(ctx, APIURL+path, values, pipeReader)
if err != nil {
return err
}
+ req.Header.Add("Content-Type", wr.FormDataContentType())
+ req = req.WithContext(ctx)
+ resp, err := client.Do(req)
+
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode == http.StatusTooManyRequests {
+ retry, err := strconv.ParseInt(resp.Header.Get("Retry-After"), 10, 64)
+ if err != nil {
+ return err
+ }
+ return &RateLimitedError{time.Duration(retry) * time.Second}
+ }
+
+ // Slack seems to send an HTML body along with 5xx error codes. Don't parse it.
+ if resp.StatusCode != http.StatusOK {
+ logResponse(resp, d)
+ return statusCodeError{Code: resp.StatusCode, Status: resp.Status}
+ }
+ select {
+ case err = <-errc:
+ return err
+ default:
+ return parseResponseBody(resp.Body, intf, d)
+ }
+}
+
+func doPost(ctx context.Context, client httpClient, req *http.Request, intf interface{}, d debug) error {
req = req.WithContext(ctx)
resp, err := client.Do(req)
if err != nil {
@@ -139,40 +175,15 @@ func postWithMultipartResponse(ctx context.Context, client HTTPRequester, path,
// Slack seems to send an HTML body along with 5xx error codes. Don't parse it.
if resp.StatusCode != http.StatusOK {
- logResponse(resp, debug)
+ logResponse(resp, d)
return statusCodeError{Code: resp.StatusCode, Status: resp.Status}
}
- return parseResponseBody(resp.Body, intf, debug)
-}
-
-func doPost(ctx context.Context, client HTTPRequester, req *http.Request, intf interface{}, debug bool) error {
- req = req.WithContext(ctx)
- resp, err := client.Do(req)
- if err != nil {
- return err
- }
- defer resp.Body.Close()
-
- if resp.StatusCode == http.StatusTooManyRequests {
- retry, err := strconv.ParseInt(resp.Header.Get("Retry-After"), 10, 64)
- if err != nil {
- return err
- }
- return &RateLimitedError{time.Duration(retry) * time.Second}
- }
-
- // Slack seems to send an HTML body along with 5xx error codes. Don't parse it.
- if resp.StatusCode != http.StatusOK {
- logResponse(resp, debug)
- return statusCodeError{Code: resp.StatusCode, Status: resp.Status}
- }
-
- return parseResponseBody(resp.Body, intf, debug)
+ return parseResponseBody(resp.Body, intf, d)
}
// post JSON.
-func postJSON(ctx context.Context, client HTTPRequester, endpoint, token string, json []byte, intf interface{}, debug bool) error {
+func postJSON(ctx context.Context, client httpClient, endpoint, token string, json []byte, intf interface{}, d debug) error {
reqBody := bytes.NewBuffer(json)
req, err := http.NewRequest("POST", endpoint, reqBody)
if err != nil {
@@ -180,38 +191,53 @@ func postJSON(ctx context.Context, client HTTPRequester, endpoint, token string,
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
- return doPost(ctx, client, req, intf, debug)
+ return doPost(ctx, client, req, intf, d)
}
// post a url encoded form.
-func postForm(ctx context.Context, client HTTPRequester, endpoint string, values url.Values, intf interface{}, debug bool) error {
+func postForm(ctx context.Context, client httpClient, endpoint string, values url.Values, intf interface{}, d debug) error {
reqBody := strings.NewReader(values.Encode())
req, err := http.NewRequest("POST", endpoint, reqBody)
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
- return doPost(ctx, client, req, intf, debug)
+ return doPost(ctx, client, req, intf, d)
}
// post to a slack web method.
-func postSlackMethod(ctx context.Context, client HTTPRequester, path string, values url.Values, intf interface{}, debug bool) error {
- return postForm(ctx, client, SLACK_API+path, values, intf, debug)
+func postSlackMethod(ctx context.Context, client httpClient, path string, values url.Values, intf interface{}, d debug) error {
+ return postForm(ctx, client, APIURL+path, values, intf, d)
}
-func parseAdminResponse(ctx context.Context, client HTTPRequester, method string, teamName string, values url.Values, intf interface{}, debug bool) error {
- endpoint := fmt.Sprintf(SLACK_WEB_API_FORMAT, teamName, method, time.Now().Unix())
- return postForm(ctx, client, endpoint, values, intf, debug)
+// get a slack web method.
+func getSlackMethod(ctx context.Context, client httpClient, path string, values url.Values, intf interface{}, d debug) error {
+ return getResource(ctx, client, APIURL+path, values, intf, d)
}
-func logResponse(resp *http.Response, debug bool) error {
- if debug {
+func getResource(ctx context.Context, client httpClient, endpoint string, values url.Values, intf interface{}, d debug) error {
+ req, err := http.NewRequest("GET", endpoint, nil)
+ if err != nil {
+ return err
+ }
+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+ req.URL.RawQuery = values.Encode()
+
+ return doPost(ctx, client, req, intf, d)
+}
+
+func parseAdminResponse(ctx context.Context, client httpClient, method string, teamName string, values url.Values, intf interface{}, d debug) error {
+ endpoint := fmt.Sprintf(WEBAPIURLFormat, teamName, method, time.Now().Unix())
+ return postForm(ctx, client, endpoint, values, intf, d)
+}
+
+func logResponse(resp *http.Response, d debug) error {
+ if d.Debug() {
text, err := httputil.DumpResponse(resp, true)
if err != nil {
return err
}
-
- logger.Print(string(text))
+ d.Debugln(string(text))
}
return nil
diff --git a/vendor/github.com/nlopes/slack/oauth.go b/vendor/github.com/nlopes/slack/oauth.go
index 378af4a5..8a8194cb 100644
--- a/vendor/github.com/nlopes/slack/oauth.go
+++ b/vendor/github.com/nlopes/slack/oauth.go
@@ -2,10 +2,10 @@ package slack
import (
"context"
- "errors"
"net/url"
)
+// OAuthResponseIncomingWebhook ...
type OAuthResponseIncomingWebhook struct {
URL string `json:"url"`
Channel string `json:"channel"`
@@ -13,11 +13,13 @@ type OAuthResponseIncomingWebhook struct {
ConfigurationURL string `json:"configuration_url"`
}
+// OAuthResponseBot ...
type OAuthResponseBot struct {
BotUserID string `json:"bot_user_id"`
BotAccessToken string `json:"bot_access_token"`
}
+// OAuthResponse ...
type OAuthResponse struct {
AccessToken string `json:"access_token"`
Scope string `json:"scope"`
@@ -30,24 +32,24 @@ type OAuthResponse struct {
}
// GetOAuthToken retrieves an AccessToken
-func GetOAuthToken(clientID, clientSecret, code, redirectURI string, debug bool) (accessToken string, scope string, err error) {
- return GetOAuthTokenContext(context.Background(), clientID, clientSecret, code, redirectURI, debug)
+func GetOAuthToken(client httpClient, clientID, clientSecret, code, redirectURI string) (accessToken string, scope string, err error) {
+ return GetOAuthTokenContext(context.Background(), client, clientID, clientSecret, code, redirectURI)
}
// GetOAuthTokenContext retrieves an AccessToken with a custom context
-func GetOAuthTokenContext(ctx context.Context, clientID, clientSecret, code, redirectURI string, debug bool) (accessToken string, scope string, err error) {
- response, err := GetOAuthResponseContext(ctx, clientID, clientSecret, code, redirectURI, debug)
+func GetOAuthTokenContext(ctx context.Context, client httpClient, clientID, clientSecret, code, redirectURI string) (accessToken string, scope string, err error) {
+ response, err := GetOAuthResponseContext(ctx, client, clientID, clientSecret, code, redirectURI)
if err != nil {
return "", "", err
}
return response.AccessToken, response.Scope, nil
}
-func GetOAuthResponse(clientID, clientSecret, code, redirectURI string, debug bool) (resp *OAuthResponse, err error) {
- return GetOAuthResponseContext(context.Background(), clientID, clientSecret, code, redirectURI, debug)
+func GetOAuthResponse(client httpClient, clientID, clientSecret, code, redirectURI string) (resp *OAuthResponse, err error) {
+ return GetOAuthResponseContext(context.Background(), client, clientID, clientSecret, code, redirectURI)
}
-func GetOAuthResponseContext(ctx context.Context, clientID, clientSecret, code, redirectURI string, debug bool) (resp *OAuthResponse, err error) {
+func GetOAuthResponseContext(ctx context.Context, client httpClient, clientID, clientSecret, code, redirectURI string) (resp *OAuthResponse, err error) {
values := url.Values{
"client_id": {clientID},
"client_secret": {clientSecret},
@@ -55,12 +57,8 @@ func GetOAuthResponseContext(ctx context.Context, clientID, clientSecret, code,
"redirect_uri": {redirectURI},
}
response := &OAuthResponse{}
- err = postSlackMethod(ctx, customHTTPClient, "oauth.access", values, response, debug)
- if err != nil {
+ if err = postSlackMethod(ctx, client, "oauth.access", values, response, discard{}); err != nil {
return nil, err
}
- if !response.Ok {
- return nil, errors.New(response.Error)
- }
- return response, nil
+ return response, response.Err()
}
diff --git a/vendor/github.com/nlopes/slack/pins.go b/vendor/github.com/nlopes/slack/pins.go
index 34863f17..c1d525df 100644
--- a/vendor/github.com/nlopes/slack/pins.go
+++ b/vendor/github.com/nlopes/slack/pins.go
@@ -34,7 +34,7 @@ func (api *Client) AddPinContext(ctx context.Context, channel string, item ItemR
}
response := &SlackResponse{}
- if err := postSlackMethod(ctx, api.httpclient, "pins.add", values, response, api.debug); err != nil {
+ if err := postSlackMethod(ctx, api.httpclient, "pins.add", values, response, api); err != nil {
return err
}
@@ -63,7 +63,7 @@ func (api *Client) RemovePinContext(ctx context.Context, channel string, item It
}
response := &SlackResponse{}
- if err := postSlackMethod(ctx, api.httpclient, "pins.remove", values, response, api.debug); err != nil {
+ if err := postSlackMethod(ctx, api.httpclient, "pins.remove", values, response, api); err != nil {
return err
}
@@ -83,7 +83,7 @@ func (api *Client) ListPinsContext(ctx context.Context, channel string) ([]Item,
}
response := &listPinsResponseFull{}
- err := postSlackMethod(ctx, api.httpclient, "pins.list", values, response, api.debug)
+ err := postSlackMethod(ctx, api.httpclient, "pins.list", values, response, api)
if err != nil {
return nil, nil, err
}
diff --git a/vendor/github.com/nlopes/slack/reactions.go b/vendor/github.com/nlopes/slack/reactions.go
index 5eabde63..abe1e72a 100644
--- a/vendor/github.com/nlopes/slack/reactions.go
+++ b/vendor/github.com/nlopes/slack/reactions.go
@@ -155,7 +155,7 @@ func (api *Client) AddReactionContext(ctx context.Context, name string, item Ite
}
response := &SlackResponse{}
- if err := postSlackMethod(ctx, api.httpclient, "reactions.add", values, response, api.debug); err != nil {
+ if err := postSlackMethod(ctx, api.httpclient, "reactions.add", values, response, api); err != nil {
return err
}
@@ -189,7 +189,7 @@ func (api *Client) RemoveReactionContext(ctx context.Context, name string, item
}
response := &SlackResponse{}
- if err := postSlackMethod(ctx, api.httpclient, "reactions.remove", values, response, api.debug); err != nil {
+ if err := postSlackMethod(ctx, api.httpclient, "reactions.remove", values, response, api); err != nil {
return err
}
@@ -223,7 +223,7 @@ func (api *Client) GetReactionsContext(ctx context.Context, item ItemRef, params
}
response := &getReactionsResponseFull{}
- if err := postSlackMethod(ctx, api.httpclient, "reactions.get", values, response, api.debug); err != nil {
+ if err := postSlackMethod(ctx, api.httpclient, "reactions.get", values, response, api); err != nil {
return nil, err
}
if !response.Ok {
@@ -256,7 +256,7 @@ func (api *Client) ListReactionsContext(ctx context.Context, params ListReaction
}
response := &listReactionsResponseFull{}
- err := postSlackMethod(ctx, api.httpclient, "reactions.list", values, response, api.debug)
+ err := postSlackMethod(ctx, api.httpclient, "reactions.list", values, response, api)
if err != nil {
return nil, nil, err
}
diff --git a/vendor/github.com/nlopes/slack/rtm.go b/vendor/github.com/nlopes/slack/rtm.go
index 41a136eb..e7fa83f7 100644
--- a/vendor/github.com/nlopes/slack/rtm.go
+++ b/vendor/github.com/nlopes/slack/rtm.go
@@ -38,7 +38,7 @@ func (api *Client) StartRTM() (info *Info, websocketURL string, err error) {
// To have a fully managed Websocket connection, use `NewRTM`, and call `ManageConnection()` on it.
func (api *Client) StartRTMContext(ctx context.Context) (info *Info, websocketURL string, err error) {
response := &infoResponseFull{}
- err = postSlackMethod(ctx, api.httpclient, "rtm.start", url.Values{"token": {api.token}}, response, api.debug)
+ err = postSlackMethod(ctx, api.httpclient, "rtm.start", url.Values{"token": {api.token}}, response, api)
if err != nil {
return nil, "", err
}
@@ -63,7 +63,7 @@ func (api *Client) ConnectRTM() (info *Info, websocketURL string, err error) {
// To have a fully managed Websocket connection, use `NewRTM`, and call `ManageConnection()` on it.
func (api *Client) ConnectRTMContext(ctx context.Context) (info *Info, websocketURL string, err error) {
response := &infoResponseFull{}
- err = postSlackMethod(ctx, api.httpclient, "rtm.connect", url.Values{"token": {api.token}}, response, api.debug)
+ err = postSlackMethod(ctx, api.httpclient, "rtm.connect", url.Values{"token": {api.token}}, response, api)
if err != nil {
api.Debugf("Failed to connect to RTM: %s", err)
return nil, "", err
@@ -100,17 +100,24 @@ func RTMOptionPingInterval(d time.Duration) RTMOption {
}
}
+// RTMOptionConnParams installs parameters to embed into the connection URL.
+func RTMOptionConnParams(connParams url.Values) RTMOption {
+ return func(rtm *RTM) {
+ rtm.connParams = connParams
+ }
+}
+
// NewRTM returns a RTM, which provides a fully managed connection to
// Slack's websocket-based Real-Time Messaging protocol.
func (api *Client) NewRTM(options ...RTMOption) *RTM {
result := &RTM{
Client: *api,
+ wasIntentional: true,
+ isConnected: false,
IncomingEvents: make(chan RTMEvent, 50),
outgoingMessages: make(chan OutgoingMessage, 20),
pingInterval: defaultPingInterval,
pingDeadman: time.NewTimer(deadmanDuration(defaultPingInterval)),
- isConnected: false,
- wasIntentional: true,
killChannel: make(chan bool),
disconnected: make(chan struct{}, 1),
forcePing: make(chan bool),
@@ -125,14 +132,3 @@ func (api *Client) NewRTM(options ...RTMOption) *RTM {
return result
}
-
-// NewRTMWithOptions Deprecated just use NewRTM(RTMOptionsUseStart(true))
-// returns a RTM, which provides a fully managed connection to
-// Slack's websocket-based Real-Time Messaging protocol.
-// This also allows to configure various options available for RTM API.
-func (api *Client) NewRTMWithOptions(options *RTMOptions) *RTM {
- if options != nil {
- return api.NewRTM(RTMOptionUseStart(options.UseRTMStart))
- }
- return api.NewRTM()
-}
diff --git a/vendor/github.com/nlopes/slack/search.go b/vendor/github.com/nlopes/slack/search.go
index e858952f..2d018fcc 100644
--- a/vendor/github.com/nlopes/slack/search.go
+++ b/vendor/github.com/nlopes/slack/search.go
@@ -2,7 +2,6 @@ package slack
import (
"context"
- "errors"
"net/url"
"strconv"
)
@@ -104,14 +103,12 @@ func (api *Client) _search(ctx context.Context, path, query string, params Searc
}
response = &searchResponseFull{}
- err := postSlackMethod(ctx, api.httpclient, path, values, response, api.debug)
+ err := postSlackMethod(ctx, api.httpclient, path, values, response, api)
if err != nil {
return nil, err
}
- if !response.Ok {
- return nil, errors.New(response.Error)
- }
- return response, nil
+
+ return response, response.Err()
}
diff --git a/vendor/github.com/nlopes/slack/security.go b/vendor/github.com/nlopes/slack/security.go
new file mode 100644
index 00000000..6ab3c698
--- /dev/null
+++ b/vendor/github.com/nlopes/slack/security.go
@@ -0,0 +1,99 @@
+package slack
+
+import (
+ "crypto/hmac"
+ "crypto/sha256"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "hash"
+ "net/http"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// Signature headers
+const (
+ hSignature = "X-Slack-Signature"
+ hTimestamp = "X-Slack-Request-Timestamp"
+)
+
+// SecretsVerifier contains the information needed to verify that the request comes from Slack
+type SecretsVerifier struct {
+ signature []byte
+ hmac hash.Hash
+}
+
+func unsafeSignatureVerifier(header http.Header, secret string) (_ SecretsVerifier, err error) {
+ var (
+ bsignature []byte
+ )
+
+ signature := header.Get(hSignature)
+ stimestamp := header.Get(hTimestamp)
+
+ if signature == "" || stimestamp == "" {
+ return SecretsVerifier{}, errors.New("missing headers")
+ }
+
+ if bsignature, err = hex.DecodeString(strings.TrimPrefix(signature, "v0=")); err != nil {
+ return SecretsVerifier{}, err
+ }
+
+ hash := hmac.New(sha256.New, []byte(secret))
+ hash.Write([]byte(fmt.Sprintf("v0:%s:", stimestamp)))
+
+ return SecretsVerifier{
+ signature: bsignature,
+ hmac: hash,
+ }, nil
+}
+
+// NewSecretsVerifier returns a SecretsVerifier object in exchange for an http.Header object and signing secret
+func NewSecretsVerifier(header http.Header, secret string) (sv SecretsVerifier, err error) {
+ var (
+ timestamp int64
+ )
+
+ stimestamp := header.Get(hTimestamp)
+
+ if sv, err = unsafeSignatureVerifier(header, secret); err != nil {
+ return SecretsVerifier{}, err
+ }
+
+ if timestamp, err = strconv.ParseInt(stimestamp, 10, 64); err != nil {
+ return SecretsVerifier{}, err
+ }
+
+ diff := absDuration(time.Now().Sub(time.Unix(timestamp, 0)))
+ if diff > 5*time.Minute {
+ return SecretsVerifier{}, fmt.Errorf("timestamp is too old")
+ }
+
+ return sv, err
+}
+
+func (v *SecretsVerifier) Write(body []byte) (n int, err error) {
+ return v.hmac.Write(body)
+}
+
+// Ensure compares the signature sent from Slack with the actual computed hash to judge validity
+func (v SecretsVerifier) Ensure() error {
+ computed := v.hmac.Sum(nil)
+ // use hmac.Equal prevent leaking timing information.
+ if hmac.Equal(computed, v.signature) {
+ return nil
+ }
+
+ return fmt.Errorf("Expected signing signature: %s, but computed: %s", v.signature, computed)
+}
+
+func abs64(n int64) int64 {
+ y := n >> 63
+ return (n ^ y) - y
+}
+
+func absDuration(n time.Duration) time.Duration {
+ return time.Duration(abs64(int64(n)))
+}
diff --git a/vendor/github.com/nlopes/slack/slack.go b/vendor/github.com/nlopes/slack/slack.go
index 6d1e7de9..1e75142d 100644
--- a/vendor/github.com/nlopes/slack/slack.go
+++ b/vendor/github.com/nlopes/slack/slack.go
@@ -2,7 +2,6 @@ package slack
import (
"context"
- "errors"
"fmt"
"log"
"net/http"
@@ -10,31 +9,17 @@ import (
"os"
)
-// Added as a var so that we can change this for testing purposes
-var SLACK_API string = "https://slack.com/api/"
-var SLACK_WEB_API_FORMAT string = "https://%s.slack.com/api/users.admin.%s?t=%s"
+// APIURL a dded as a var so that we can change this for testing purposes
+var APIURL = "https://slack.com/api/"
-// HTTPClient sets a custom http.Client
-// deprecated: in favor of SetHTTPClient()
-var HTTPClient = &http.Client{}
+// WEBAPIURLFormat ...
+const WEBAPIURLFormat = "https://%s.slack.com/api/users.admin.%s?t=%d"
-var customHTTPClient HTTPRequester = HTTPClient
-
-// HTTPRequester defines the minimal interface needed for an http.Client to be implemented.
-//
-// Use it in conjunction with the SetHTTPClient function to allow for other capabilities
-// like a tracing http.Client
-type HTTPRequester interface {
+// httpClient defines the minimal interface needed for an http.Client to be implemented.
+type httpClient interface {
Do(*http.Request) (*http.Response, error)
}
-// SetHTTPClient allows you to specify a custom http.Client
-// Use this instead of the package level HTTPClient variable if you want to use a custom client like the
-// Stackdriver Trace HTTPClient https://godoc.org/cloud.google.com/go/trace#HTTPClient
-func SetHTTPClient(client HTTPRequester) {
- customHTTPClient = client
-}
-
// ResponseMetadata holds pagination metadata
type ResponseMetadata struct {
Cursor string `json:"next_cursor"`
@@ -48,6 +33,7 @@ func (t *ResponseMetadata) initialize() *ResponseMetadata {
return &ResponseMetadata{}
}
+// AuthTestResponse ...
type AuthTestResponse struct {
URL string `json:"url"`
Team string `json:"team"`
@@ -61,20 +47,36 @@ type authTestResponseFull struct {
AuthTestResponse
}
+// Client for the slack api.
type Client struct {
token string
info Info
debug bool
- httpclient HTTPRequester
+ log ilogger
+ httpclient httpClient
}
// Option defines an option for a Client
type Option func(*Client)
// OptionHTTPClient - provide a custom http client to the slack client.
-func OptionHTTPClient(c HTTPRequester) func(*Client) {
- return func(s *Client) {
- s.httpclient = c
+func OptionHTTPClient(client httpClient) func(*Client) {
+ return func(c *Client) {
+ c.httpclient = client
+ }
+}
+
+// OptionDebug enable debugging for the client
+func OptionDebug(b bool) func(*Client) {
+ return func(c *Client) {
+ c.debug = b
+ }
+}
+
+// OptionLog set logging for client.
+func OptionLog(l logger) func(*Client) {
+ return func(c *Client) {
+ c.log = internalLog{logger: l}
}
}
@@ -82,7 +84,8 @@ func OptionHTTPClient(c HTTPRequester) func(*Client) {
func New(token string, options ...Option) *Client {
s := &Client{
token: token,
- httpclient: customHTTPClient,
+ httpclient: &http.Client{},
+ log: log.New(os.Stderr, "nlopes/slack", log.LstdFlags|log.Lshortfile),
}
for _, opt := range options {
@@ -98,43 +101,32 @@ func (api *Client) AuthTest() (response *AuthTestResponse, error error) {
}
// AuthTestContext tests if the user is able to do authenticated requests or not with a custom context
-func (api *Client) AuthTestContext(ctx context.Context) (response *AuthTestResponse, error error) {
+func (api *Client) AuthTestContext(ctx context.Context) (response *AuthTestResponse, err error) {
api.Debugf("Challenging auth...")
responseFull := &authTestResponseFull{}
- err := postSlackMethod(ctx, api.httpclient, "auth.test", url.Values{"token": {api.token}}, responseFull, api.debug)
+ err = postSlackMethod(ctx, api.httpclient, "auth.test", url.Values{"token": {api.token}}, responseFull, api)
if err != nil {
- api.Debugf("failed to test for auth: %s", err)
return nil, err
}
- if !responseFull.Ok {
- api.Debugf("auth response was not Ok: %s", responseFull.Error)
- return nil, errors.New(responseFull.Error)
- }
- api.Debugf("Auth challenge was successful with response %+v", responseFull.AuthTestResponse)
- return &responseFull.AuthTestResponse, nil
-}
-
-// SetDebug switches the api into debug mode
-// When in debug mode, it logs various info about what its doing
-// If you ever use this in production, don't call SetDebug(true)
-func (api *Client) SetDebug(debug bool) {
- api.debug = debug
- if debug && logger == nil {
- SetLogger(log.New(os.Stdout, "nlopes/slack", log.LstdFlags|log.Lshortfile))
- }
+ return &responseFull.AuthTestResponse, responseFull.Err()
}
// Debugf print a formatted debug line.
func (api *Client) Debugf(format string, v ...interface{}) {
if api.debug {
- logger.Output(2, fmt.Sprintf(format, v...))
+ api.log.Output(2, fmt.Sprintf(format, v...))
}
}
// Debugln print a debug line.
func (api *Client) Debugln(v ...interface{}) {
if api.debug {
- logger.Output(2, fmt.Sprintln(v...))
+ api.log.Output(2, fmt.Sprintln(v...))
}
}
+
+// Debug returns if debug is enabled.
+func (api *Client) Debug() bool {
+ return api.debug
+}
diff --git a/vendor/github.com/nlopes/slack/slackutilsx/slackutilsx.go b/vendor/github.com/nlopes/slack/slackutilsx/slackutilsx.go
new file mode 100644
index 00000000..ccf5372b
--- /dev/null
+++ b/vendor/github.com/nlopes/slack/slackutilsx/slackutilsx.go
@@ -0,0 +1,57 @@
+// Package slackutilsx is a utility package that doesn't promise API stability.
+// its for experimental functionality and utilities.
+package slackutilsx
+
+import (
+ "strings"
+ "unicode/utf8"
+)
+
+// ChannelType the type of channel based on the channelID
+type ChannelType int
+
+func (t ChannelType) String() string {
+ switch t {
+ case CTypeDM:
+ return "Direct"
+ case CTypeGroup:
+ return "Group"
+ case CTypeChannel:
+ return "Channel"
+ default:
+ return "Unknown"
+ }
+}
+
+const (
+ // CTypeUnknown represents channels we cannot properly detect.
+ CTypeUnknown ChannelType = iota
+ // CTypeDM is a private channel between two slack users.
+ CTypeDM
+ // CTypeGroup is a group channel.
+ CTypeGroup
+ // CTypeChannel is a public channel.
+ CTypeChannel
+)
+
+// DetectChannelType converts a channelID to a ChannelType.
+// channelID must not be empty. However, if it is empty, the channel type will default to Unknown.
+func DetectChannelType(channelID string) ChannelType {
+ // intentionally ignore the error and just default to CTypeUnknown
+ switch r, _ := utf8.DecodeRuneInString(channelID); r {
+ case 'C':
+ return CTypeChannel
+ case 'G':
+ return CTypeGroup
+ case 'D':
+ return CTypeDM
+ default:
+ return CTypeUnknown
+ }
+}
+
+// EscapeMessage text
+func EscapeMessage(message string) string {
+ replacer := strings.NewReplacer("&", "&", "<", "<", ">", ">")
+ return replacer.Replace(message)
+}
diff --git a/vendor/github.com/nlopes/slack/stars.go b/vendor/github.com/nlopes/slack/stars.go
index c1e2f6cb..7e1e621d 100644
--- a/vendor/github.com/nlopes/slack/stars.go
+++ b/vendor/github.com/nlopes/slack/stars.go
@@ -58,7 +58,7 @@ func (api *Client) AddStarContext(ctx context.Context, channel string, item Item
}
response := &SlackResponse{}
- if err := postSlackMethod(ctx, api.httpclient, "stars.add", values, response, api.debug); err != nil {
+ if err := postSlackMethod(ctx, api.httpclient, "stars.add", values, response, api); err != nil {
return err
}
@@ -87,7 +87,7 @@ func (api *Client) RemoveStarContext(ctx context.Context, channel string, item I
}
response := &SlackResponse{}
- if err := postSlackMethod(ctx, api.httpclient, "stars.remove", values, response, api.debug); err != nil {
+ if err := postSlackMethod(ctx, api.httpclient, "stars.remove", values, response, api); err != nil {
return err
}
@@ -115,7 +115,7 @@ func (api *Client) ListStarsContext(ctx context.Context, params StarsParameters)
}
response := &listResponseFull{}
- err := postSlackMethod(ctx, api.httpclient, "stars.list", values, response, api.debug)
+ err := postSlackMethod(ctx, api.httpclient, "stars.list", values, response, api)
if err != nil {
return nil, nil, err
}
diff --git a/vendor/github.com/nlopes/slack/team.go b/vendor/github.com/nlopes/slack/team.go
index b6e341eb..1892cf5f 100644
--- a/vendor/github.com/nlopes/slack/team.go
+++ b/vendor/github.com/nlopes/slack/team.go
@@ -2,7 +2,6 @@ package slack
import (
"context"
- "errors"
"net/url"
"strconv"
)
@@ -67,44 +66,33 @@ func NewAccessLogParameters() AccessLogParameters {
}
}
-func teamRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*TeamResponse, error) {
+func teamRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (*TeamResponse, error) {
response := &TeamResponse{}
- err := postSlackMethod(ctx, client, path, values, response, debug)
+ err := postSlackMethod(ctx, client, path, values, response, d)
if err != nil {
return nil, err
}
- if !response.Ok {
- return nil, errors.New(response.Error)
- }
-
- return response, nil
+ return response, response.Err()
}
-func billableInfoRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (map[string]BillingActive, error) {
+func billableInfoRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (map[string]BillingActive, error) {
response := &BillableInfoResponse{}
- err := postSlackMethod(ctx, client, path, values, response, debug)
+ err := postSlackMethod(ctx, client, path, values, response, d)
if err != nil {
return nil, err
}
- if !response.Ok {
- return nil, errors.New(response.Error)
- }
-
- return response.BillableInfo, nil
+ return response.BillableInfo, response.Err()
}
-func accessLogsRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*LoginResponse, error) {
+func accessLogsRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (*LoginResponse, error) {
response := &LoginResponse{}
- err := postSlackMethod(ctx, client, path, values, response, debug)
+ err := postSlackMethod(ctx, client, path, values, response, d)
if err != nil {
return nil, err
}
- if !response.Ok {
- return nil, errors.New(response.Error)
- }
- return response, nil
+ return response, response.Err()
}
// GetTeamInfo gets the Team Information of the user
@@ -118,7 +106,7 @@ func (api *Client) GetTeamInfoContext(ctx context.Context) (*TeamInfo, error) {
"token": {api.token},
}
- response, err := teamRequest(ctx, api.httpclient, "team.info", values, api.debug)
+ response, err := teamRequest(ctx, api.httpclient, "team.info", values, api)
if err != nil {
return nil, err
}
@@ -142,7 +130,7 @@ func (api *Client) GetAccessLogsContext(ctx context.Context, params AccessLogPar
values.Add("page", strconv.Itoa(params.Page))
}
- response, err := accessLogsRequest(ctx, api.httpclient, "team.accessLogs", values, api.debug)
+ response, err := accessLogsRequest(ctx, api.httpclient, "team.accessLogs", values, api)
if err != nil {
return nil, nil, err
}
@@ -159,7 +147,7 @@ func (api *Client) GetBillableInfoContext(ctx context.Context, user string) (map
"user": {user},
}
- return billableInfoRequest(ctx, api.httpclient, "team.billableInfo", values, api.debug)
+ return billableInfoRequest(ctx, api.httpclient, "team.billableInfo", values, api)
}
// GetBillableInfoForTeam returns the billing_active status of all users on the team.
@@ -173,5 +161,5 @@ func (api *Client) GetBillableInfoForTeamContext(ctx context.Context) (map[strin
"token": {api.token},
}
- return billableInfoRequest(ctx, api.httpclient, "team.billableInfo", values, api.debug)
+ return billableInfoRequest(ctx, api.httpclient, "team.billableInfo", values, api)
}
diff --git a/vendor/github.com/nlopes/slack/usergroups.go b/vendor/github.com/nlopes/slack/usergroups.go
index 1e2b6442..9e145272 100644
--- a/vendor/github.com/nlopes/slack/usergroups.go
+++ b/vendor/github.com/nlopes/slack/usergroups.go
@@ -2,7 +2,6 @@ package slack
import (
"context"
- "errors"
"net/url"
"strings"
)
@@ -25,6 +24,7 @@ type UserGroup struct {
DeletedBy string `json:"deleted_by"`
Prefs UserGroupPrefs `json:"prefs"`
UserCount int `json:"user_count"`
+ Users []string `json:"users"`
}
// UserGroupPrefs contains default channels and groups (private channels)
@@ -40,16 +40,14 @@ type userGroupResponseFull struct {
SlackResponse
}
-func userGroupRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*userGroupResponseFull, error) {
+func userGroupRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (*userGroupResponseFull, error) {
response := &userGroupResponseFull{}
- err := postSlackMethod(ctx, client, path, values, response, debug)
+ err := postSlackMethod(ctx, client, path, values, response, d)
if err != nil {
return nil, err
}
- if !response.Ok {
- return nil, errors.New(response.Error)
- }
- return response, nil
+
+ return response, response.Err()
}
// CreateUserGroup creates a new user group
@@ -76,7 +74,7 @@ func (api *Client) CreateUserGroupContext(ctx context.Context, userGroup UserGro
values["channels"] = []string{strings.Join(userGroup.Prefs.Channels, ",")}
}
- response, err := userGroupRequest(ctx, api.httpclient, "usergroups.create", values, api.debug)
+ response, err := userGroupRequest(ctx, api.httpclient, "usergroups.create", values, api)
if err != nil {
return UserGroup{}, err
}
@@ -95,7 +93,7 @@ func (api *Client) DisableUserGroupContext(ctx context.Context, userGroup string
"usergroup": {userGroup},
}
- response, err := userGroupRequest(ctx, api.httpclient, "usergroups.disable", values, api.debug)
+ response, err := userGroupRequest(ctx, api.httpclient, "usergroups.disable", values, api)
if err != nil {
return UserGroup{}, err
}
@@ -114,25 +112,71 @@ func (api *Client) EnableUserGroupContext(ctx context.Context, userGroup string)
"usergroup": {userGroup},
}
- response, err := userGroupRequest(ctx, api.httpclient, "usergroups.enable", values, api.debug)
+ response, err := userGroupRequest(ctx, api.httpclient, "usergroups.enable", values, api)
if err != nil {
return UserGroup{}, err
}
return response.UserGroup, nil
}
+// GetUserGroupsOption options for the GetUserGroups method call.
+type GetUserGroupsOption func(*GetUserGroupsParams)
+
+// GetUserGroupsOptionIncludeCount include the number of users in each User Group (default: false)
+func GetUserGroupsOptionIncludeCount(b bool) GetUserGroupsOption {
+ return func(params *GetUserGroupsParams) {
+ params.IncludeCount = b
+ }
+}
+
+// GetUserGroupsOptionIncludeDisabled include disabled User Groups (default: false)
+func GetUserGroupsOptionIncludeDisabled(b bool) GetUserGroupsOption {
+ return func(params *GetUserGroupsParams) {
+ params.IncludeDisabled = b
+ }
+}
+
+// GetUserGroupsOptionIncludeUsers include the list of users for each User Group (default: false)
+func GetUserGroupsOptionIncludeUsers(b bool) GetUserGroupsOption {
+ return func(params *GetUserGroupsParams) {
+ params.IncludeUsers = b
+ }
+}
+
+// GetUserGroupsParams contains arguments for GetUserGroups method call
+type GetUserGroupsParams struct {
+ IncludeCount bool
+ IncludeDisabled bool
+ IncludeUsers bool
+}
+
// GetUserGroups returns a list of user groups for the team
-func (api *Client) GetUserGroups() ([]UserGroup, error) {
- return api.GetUserGroupsContext(context.Background())
+func (api *Client) GetUserGroups(options ...GetUserGroupsOption) ([]UserGroup, error) {
+ return api.GetUserGroupsContext(context.Background(), options...)
}
// GetUserGroupsContext returns a list of user groups for the team with a custom context
-func (api *Client) GetUserGroupsContext(ctx context.Context) ([]UserGroup, error) {
+func (api *Client) GetUserGroupsContext(ctx context.Context, options ...GetUserGroupsOption) ([]UserGroup, error) {
+ params := GetUserGroupsParams{}
+
+ for _, opt := range options {
+ opt(¶ms)
+ }
+
values := url.Values{
"token": {api.token},
}
+ if params.IncludeCount {
+ values.Add("include_count", "true")
+ }
+ if params.IncludeDisabled {
+ values.Add("include_disabled", "true")
+ }
+ if params.IncludeUsers {
+ values.Add("include_users", "true")
+ }
- response, err := userGroupRequest(ctx, api.httpclient, "usergroups.list", values, api.debug)
+ response, err := userGroupRequest(ctx, api.httpclient, "usergroups.list", values, api)
if err != nil {
return nil, err
}
@@ -163,7 +207,7 @@ func (api *Client) UpdateUserGroupContext(ctx context.Context, userGroup UserGro
values["description"] = []string{userGroup.Description}
}
- response, err := userGroupRequest(ctx, api.httpclient, "usergroups.update", values, api.debug)
+ response, err := userGroupRequest(ctx, api.httpclient, "usergroups.update", values, api)
if err != nil {
return UserGroup{}, err
}
@@ -182,7 +226,7 @@ func (api *Client) GetUserGroupMembersContext(ctx context.Context, userGroup str
"usergroup": {userGroup},
}
- response, err := userGroupRequest(ctx, api.httpclient, "usergroups.users.list", values, api.debug)
+ response, err := userGroupRequest(ctx, api.httpclient, "usergroups.users.list", values, api)
if err != nil {
return []string{}, err
}
@@ -202,7 +246,7 @@ func (api *Client) UpdateUserGroupMembersContext(ctx context.Context, userGroup
"users": {members},
}
- response, err := userGroupRequest(ctx, api.httpclient, "usergroups.users.update", values, api.debug)
+ response, err := userGroupRequest(ctx, api.httpclient, "usergroups.users.update", values, api)
if err != nil {
return UserGroup{}, err
}
diff --git a/vendor/github.com/nlopes/slack/users.go b/vendor/github.com/nlopes/slack/users.go
index 0dd20db5..aa941a74 100644
--- a/vendor/github.com/nlopes/slack/users.go
+++ b/vendor/github.com/nlopes/slack/users.go
@@ -189,16 +189,14 @@ func NewUserSetPhotoParams() UserSetPhotoParams {
}
}
-func userRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*userResponseFull, error) {
+func userRequest(ctx context.Context, client httpClient, path string, values url.Values, d debug) (*userResponseFull, error) {
response := &userResponseFull{}
- err := postForm(ctx, client, SLACK_API+path, values, response, debug)
+ err := postForm(ctx, client, APIURL+path, values, response, d)
if err != nil {
return nil, err
}
- if !response.Ok {
- return nil, errors.New(response.Error)
- }
- return response, nil
+
+ return response, response.Err()
}
// GetUserPresence will retrieve the current presence status of given user.
@@ -213,7 +211,7 @@ func (api *Client) GetUserPresenceContext(ctx context.Context, user string) (*Us
"user": {user},
}
- response, err := userRequest(ctx, api.httpclient, "users.getPresence", values, api.debug)
+ response, err := userRequest(ctx, api.httpclient, "users.getPresence", values, api)
if err != nil {
return nil, err
}
@@ -232,7 +230,7 @@ func (api *Client) GetUserInfoContext(ctx context.Context, user string) (*User,
"user": {user},
}
- response, err := userRequest(ctx, api.httpclient, "users.info", values, api.debug)
+ response, err := userRequest(ctx, api.httpclient, "users.info", values, api)
if err != nil {
return nil, err
}
@@ -310,7 +308,7 @@ func (t UserPagination) Next(ctx context.Context) (_ UserPagination, err error)
"cursor": {t.previousResp.Cursor},
}
- if resp, err = userRequest(ctx, t.c.httpclient, "users.list", values, t.c.debug); err != nil {
+ if resp, err = userRequest(ctx, t.c.httpclient, "users.list", values, t.c); err != nil {
return t, err
}
@@ -355,7 +353,7 @@ func (api *Client) GetUserByEmailContext(ctx context.Context, email string) (*Us
"token": {api.token},
"email": {email},
}
- response, err := userRequest(ctx, api.httpclient, "users.lookupByEmail", values, api.debug)
+ response, err := userRequest(ctx, api.httpclient, "users.lookupByEmail", values, api)
if err != nil {
return nil, err
}
@@ -373,7 +371,7 @@ func (api *Client) SetUserAsActiveContext(ctx context.Context) (err error) {
"token": {api.token},
}
- _, err = userRequest(ctx, api.httpclient, "users.setActive", values, api.debug)
+ _, err = userRequest(ctx, api.httpclient, "users.setActive", values, api)
return err
}
@@ -389,7 +387,7 @@ func (api *Client) SetUserPresenceContext(ctx context.Context, presence string)
"presence": {presence},
}
- _, err := userRequest(ctx, api.httpclient, "users.setPresence", values, api.debug)
+ _, err := userRequest(ctx, api.httpclient, "users.setPresence", values, api)
return err
}
@@ -405,7 +403,7 @@ func (api *Client) GetUserIdentityContext(ctx context.Context) (*UserIdentityRes
}
response := &UserIdentityResponse{}
- err := postForm(ctx, api.httpclient, SLACK_API+"users.identity", values, response, api.debug)
+ err := postForm(ctx, api.httpclient, APIURL+"users.identity", values, response, api)
if err != nil {
return nil, err
}
@@ -436,7 +434,7 @@ func (api *Client) SetUserPhotoContext(ctx context.Context, image string, params
values.Add("crop_w", strconv.Itoa(params.CropW))
}
- err := postLocalWithMultipartResponse(ctx, api.httpclient, "users.setPhoto", image, "image", values, response, api.debug)
+ err := postLocalWithMultipartResponse(ctx, api.httpclient, "users.setPhoto", image, "image", values, response, api)
if err != nil {
return err
}
@@ -456,7 +454,7 @@ func (api *Client) DeleteUserPhotoContext(ctx context.Context) error {
"token": {api.token},
}
- err := postForm(ctx, api.httpclient, SLACK_API+"users.deletePhoto", values, response, api.debug)
+ err := postForm(ctx, api.httpclient, APIURL+"users.deletePhoto", values, response, api)
if err != nil {
return err
}
@@ -506,7 +504,7 @@ func (api *Client) SetUserCustomStatusContext(ctx context.Context, statusText, s
}
response := &userResponseFull{}
- if err = postForm(ctx, api.httpclient, SLACK_API+"users.profile.set", values, response, api.debug); err != nil {
+ if err = postForm(ctx, api.httpclient, APIURL+"users.profile.set", values, response, api); err != nil {
return err
}
@@ -547,7 +545,7 @@ func (api *Client) GetUserProfileContext(ctx context.Context, userID string, inc
}
resp := &getUserProfileResponse{}
- err := postSlackMethod(ctx, api.httpclient, "users.profile.get", values, &resp, api.debug)
+ err := postSlackMethod(ctx, api.httpclient, "users.profile.get", values, &resp, api)
if err != nil {
return nil, err
}
diff --git a/vendor/github.com/nlopes/slack/webhooks.go b/vendor/github.com/nlopes/slack/webhooks.go
index 870a8d8b..3ea69ffe 100644
--- a/vendor/github.com/nlopes/slack/webhooks.go
+++ b/vendor/github.com/nlopes/slack/webhooks.go
@@ -1,15 +1,16 @@
package slack
import (
- "github.com/pkg/errors"
- "net/http"
"bytes"
"encoding/json"
+ "net/http"
+
+ "github.com/pkg/errors"
)
type WebhookMessage struct {
- Text string `json:"text,omitempty"`
- Attachments []Attachment `json:"attachments,omitempty"`
+ Text string `json:"text,omitempty"`
+ Attachments []Attachment `json:"attachments,omitempty"`
}
func PostWebhook(url string, msg *WebhookMessage) error {
@@ -19,7 +20,7 @@ func PostWebhook(url string, msg *WebhookMessage) error {
return errors.Wrap(err, "marshal failed")
}
- response, err := http.Post(url, "application/json", bytes.NewReader(raw));
+ response, err := http.Post(url, "application/json", bytes.NewReader(raw))
if err != nil {
return errors.Wrap(err, "failed to post webhook")
diff --git a/vendor/github.com/nlopes/slack/websocket.go b/vendor/github.com/nlopes/slack/websocket.go
index 242acf40..ec810a9b 100644
--- a/vendor/github.com/nlopes/slack/websocket.go
+++ b/vendor/github.com/nlopes/slack/websocket.go
@@ -3,6 +3,7 @@ package slack
import (
"encoding/json"
"errors"
+ "net/url"
"sync"
"time"
@@ -20,6 +21,9 @@ const (
//
// Create this element with Client's NewRTM() or NewRTMWithOptions(*RTMOptions)
type RTM struct {
+ // Client is the main API, embedded
+ Client
+
idGen IDGenerator
pingInterval time.Duration
pingDeadman *time.Timer
@@ -35,8 +39,6 @@ type RTM struct {
wasIntentional bool
isConnected bool
- // Client is the main API, embedded
- Client
websocketURL string
// UserDetails upon connection
@@ -53,18 +55,9 @@ type RTM struct {
// mu is mutex used to prevent RTM connection race conditions
mu *sync.Mutex
-}
-// RTMOptions allows configuration of various options available for RTM messaging
-//
-// This structure will evolve in time so please make sure you are always using the
-// named keys for every entry available as per Go 1 compatibility promise adding fields
-// to this structure should not be considered a breaking change.
-type RTMOptions struct {
- // UseRTMStart set to true in order to use rtm.start or false to use rtm.connect
- // As of 11th July 2017 you should prefer setting this to false, see:
- // https://api.slack.com/changelog/2017-04-start-using-rtm-connect-and-stop-using-rtm-start
- UseRTMStart bool
+ // connParams is a map of flags for connection parameters.
+ connParams url.Values
}
// Disconnect and wait, blocking until a successful disconnection.
diff --git a/vendor/github.com/nlopes/slack/websocket_managed_conn.go b/vendor/github.com/nlopes/slack/websocket_managed_conn.go
index b6d1bfc8..62157910 100644
--- a/vendor/github.com/nlopes/slack/websocket_managed_conn.go
+++ b/vendor/github.com/nlopes/slack/websocket_managed_conn.go
@@ -5,6 +5,7 @@ import (
"fmt"
"io"
"net/http"
+ stdurl "net/url"
"reflect"
"time"
@@ -157,6 +158,14 @@ func (rtm *RTM) startRTMAndDial(useRTMStart bool) (info *Info, _ *websocket.Conn
return nil, nil, err
}
+ // install connection parameters
+ u, err := stdurl.Parse(url)
+ if err != nil {
+ return nil, nil, err
+ }
+ u.RawQuery = rtm.connParams.Encode()
+ url = u.String()
+
rtm.Debugf("Dialing to websocket on url %s", url)
// Only use HTTPS for connections to prevent MITM attacks on the connection.
upgradeHeader := http.Header{}
@@ -274,7 +283,7 @@ func (rtm *RTM) sendWithDeadline(msg interface{}) error {
// and instead lets a future failed 'PING' detect the failed connection.
func (rtm *RTM) sendOutgoingMessage(msg OutgoingMessage) {
rtm.Debugln("Sending message:", msg)
- if len(msg.Text) > MaxMessageTextLength {
+ if len([]rune(msg.Text)) > MaxMessageTextLength {
rtm.IncomingEvents <- RTMEvent{"outgoing_error", &MessageTooLongEvent{
Message: msg,
MaxLength: MaxMessageTextLength,
@@ -405,8 +414,7 @@ func (rtm *RTM) handlePong(event json.RawMessage) {
rtm.resetDeadman()
if err := json.Unmarshal(event, &p); err != nil {
- logger.Println("RTM Error unmarshalling 'pong' event:", err)
- rtm.Debugln(" -> Erroneous 'ping' event:", string(event))
+ rtm.Client.log.Println("RTM Error unmarshalling 'pong' event:", err)
return
}
@@ -423,8 +431,8 @@ func (rtm *RTM) handlePong(event json.RawMessage) {
func (rtm *RTM) handleEvent(typeStr string, event json.RawMessage) {
v, exists := EventMapping[typeStr]
if !exists {
- rtm.Debugf("RTM Error, received unmapped event %q: %s\n", typeStr, string(event))
- err := fmt.Errorf("RTM Error: Received unmapped event %q: %s\n", typeStr, string(event))
+ rtm.Debugf("RTM Error - received unmapped event %q: %s\n", typeStr, string(event))
+ err := fmt.Errorf("RTM Error: Received unmapped event %q: %s", typeStr, string(event))
rtm.IncomingEvents <- RTMEvent{"unmarshalling_error", &UnmarshallingErrorEvent{err}}
return
}
@@ -433,7 +441,7 @@ func (rtm *RTM) handleEvent(typeStr string, event json.RawMessage) {
err := json.Unmarshal(event, recvEvent)
if err != nil {
rtm.Debugf("RTM Error, could not unmarshall event %q: %s\n", typeStr, string(event))
- err := fmt.Errorf("RTM Error: Could not unmarshall event %q: %s\n", typeStr, string(event))
+ err := fmt.Errorf("RTM Error: Could not unmarshall event %q: %s", typeStr, string(event))
rtm.IncomingEvents <- RTMEvent{"unmarshalling_error", &UnmarshallingErrorEvent{err}}
return
}
@@ -524,4 +532,9 @@ var EventMapping = map[string]interface{}{
"member_joined_channel": MemberJoinedChannelEvent{},
"member_left_channel": MemberLeftChannelEvent{},
+
+ "subteam_created": SubteamCreatedEvent{},
+ "subteam_self_added": SubteamSelfAddedEvent{},
+ "subteam_self_removed": SubteamSelfRemovedEvent{},
+ "subteam_updated": SubteamUpdatedEvent{},
}
diff --git a/vendor/github.com/nlopes/slack/websocket_misc.go b/vendor/github.com/nlopes/slack/websocket_misc.go
index 16f48c74..bfcc805e 100644
--- a/vendor/github.com/nlopes/slack/websocket_misc.go
+++ b/vendor/github.com/nlopes/slack/websocket_misc.go
@@ -43,9 +43,10 @@ type HelloEvent struct{}
// PresenceChangeEvent represents the presence change event
type PresenceChangeEvent struct {
- Type string `json:"type"`
- Presence string `json:"presence"`
- User string `json:"user"`
+ Type string `json:"type"`
+ Presence string `json:"presence"`
+ User string `json:"user"`
+ Users []string `json:"users"`
}
// UserTypingEvent represents the user typing event
diff --git a/vendor/github.com/nlopes/slack/websocket_subteam.go b/vendor/github.com/nlopes/slack/websocket_subteam.go
new file mode 100644
index 00000000..a23b274c
--- /dev/null
+++ b/vendor/github.com/nlopes/slack/websocket_subteam.go
@@ -0,0 +1,35 @@
+package slack
+
+// SubteamCreatedEvent represents the Subteam created event
+type SubteamCreatedEvent struct {
+ Type string `json:"type"`
+ Subteam UserGroup `json:"subteam"`
+}
+
+// SubteamCreatedEvent represents the membership of an existing User Group has changed event
+type SubteamMembersChangedEvent struct {
+ Type string `json:"type"`
+ SubteamID string `json:"subteam_id"`
+ TeamID string `json:"team_id"`
+ DatePreviousUpdate JSONTime `json:"date_previous_update"`
+ DateUpdate JSONTime `json:"date_update"`
+ AddedUsers []string `json:"added_users"`
+ AddedUsersCount string `json:"added_users_count"`
+ RemovedUsers []string `json:"removed_users"`
+ RemovedUsersCount string `json:"removed_users_count"`
+}
+
+// SubteamSelfAddedEvent represents an event of you have been added to a User Group
+type SubteamSelfAddedEvent struct {
+ Type string `json:"type"`
+ SubteamID string `json:"subteam_id"`
+}
+
+// SubteamSelfRemovedEvent represents an event of you have been removed from a User Group
+type SubteamSelfRemovedEvent SubteamSelfAddedEvent
+
+// SubteamUpdatedEvent represents an event of an existing User Group has been updated or its members changed
+type SubteamUpdatedEvent struct {
+ Type string `json:"type"`
+ Subteam UserGroup `json:"subteam"`
+}
diff --git a/vendor/github.com/pelletier/go-toml/.travis.yml b/vendor/github.com/pelletier/go-toml/.travis.yml
index ab2775d7..c9fbf304 100644
--- a/vendor/github.com/pelletier/go-toml/.travis.yml
+++ b/vendor/github.com/pelletier/go-toml/.travis.yml
@@ -1,8 +1,9 @@
sudo: false
language: go
go:
- - 1.8.5
- - 1.9.2
+ - 1.8.x
+ - 1.9.x
+ - 1.10.x
- tip
matrix:
allow_failures:
diff --git a/vendor/github.com/pelletier/go-toml/benchmark.toml b/vendor/github.com/pelletier/go-toml/benchmark.toml
new file mode 100644
index 00000000..dfd77e09
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/benchmark.toml
@@ -0,0 +1,244 @@
+################################################################################
+## Comment
+
+# Speak your mind with the hash symbol. They go from the symbol to the end of
+# the line.
+
+
+################################################################################
+## Table
+
+# Tables (also known as hash tables or dictionaries) are collections of
+# key/value pairs. They appear in square brackets on a line by themselves.
+
+[table]
+
+key = "value" # Yeah, you can do this.
+
+# Nested tables are denoted by table names with dots in them. Name your tables
+# whatever crap you please, just don't use #, ., [ or ].
+
+[table.subtable]
+
+key = "another value"
+
+# You don't need to specify all the super-tables if you don't want to. TOML
+# knows how to do it for you.
+
+# [x] you
+# [x.y] don't
+# [x.y.z] need these
+[x.y.z.w] # for this to work
+
+
+################################################################################
+## Inline Table
+
+# Inline tables provide a more compact syntax for expressing tables. They are
+# especially useful for grouped data that can otherwise quickly become verbose.
+# Inline tables are enclosed in curly braces `{` and `}`. No newlines are
+# allowed between the curly braces unless they are valid within a value.
+
+[table.inline]
+
+name = { first = "Tom", last = "Preston-Werner" }
+point = { x = 1, y = 2 }
+
+
+################################################################################
+## String
+
+# There are four ways to express strings: basic, multi-line basic, literal, and
+# multi-line literal. All strings must contain only valid UTF-8 characters.
+
+[string.basic]
+
+basic = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."
+
+[string.multiline]
+
+# The following strings are byte-for-byte equivalent:
+key1 = "One\nTwo"
+key2 = """One\nTwo"""
+key3 = """
+One
+Two"""
+
+[string.multiline.continued]
+
+# The following strings are byte-for-byte equivalent:
+key1 = "The quick brown fox jumps over the lazy dog."
+
+key2 = """
+The quick brown \
+
+
+ fox jumps over \
+ the lazy dog."""
+
+key3 = """\
+ The quick brown \
+ fox jumps over \
+ the lazy dog.\
+ """
+
+[string.literal]
+
+# What you see is what you get.
+winpath = 'C:\Users\nodejs\templates'
+winpath2 = '\\ServerX\admin$\system32\'
+quoted = 'Tom "Dubs" Preston-Werner'
+regex = '<\i\c*\s*>'
+
+
+[string.literal.multiline]
+
+regex2 = '''I [dw]on't need \d{2} apples'''
+lines = '''
+The first newline is
+trimmed in raw strings.
+ All other whitespace
+ is preserved.
+'''
+
+
+################################################################################
+## Integer
+
+# Integers are whole numbers. Positive numbers may be prefixed with a plus sign.
+# Negative numbers are prefixed with a minus sign.
+
+[integer]
+
+key1 = +99
+key2 = 42
+key3 = 0
+key4 = -17
+
+[integer.underscores]
+
+# For large numbers, you may use underscores to enhance readability. Each
+# underscore must be surrounded by at least one digit.
+key1 = 1_000
+key2 = 5_349_221
+key3 = 1_2_3_4_5 # valid but inadvisable
+
+
+################################################################################
+## Float
+
+# A float consists of an integer part (which may be prefixed with a plus or
+# minus sign) followed by a fractional part and/or an exponent part.
+
+[float.fractional]
+
+key1 = +1.0
+key2 = 3.1415
+key3 = -0.01
+
+[float.exponent]
+
+key1 = 5e+22
+key2 = 1e6
+key3 = -2E-2
+
+[float.both]
+
+key = 6.626e-34
+
+[float.underscores]
+
+key1 = 9_224_617.445_991_228_313
+key2 = 1e1_00
+
+
+################################################################################
+## Boolean
+
+# Booleans are just the tokens you're used to. Always lowercase.
+
+[boolean]
+
+True = true
+False = false
+
+
+################################################################################
+## Datetime
+
+# Datetimes are RFC 3339 dates.
+
+[datetime]
+
+key1 = 1979-05-27T07:32:00Z
+key2 = 1979-05-27T00:32:00-07:00
+key3 = 1979-05-27T00:32:00.999999-07:00
+
+
+################################################################################
+## Array
+
+# Arrays are square brackets with other primitives inside. Whitespace is
+# ignored. Elements are separated by commas. Data types may not be mixed.
+
+[array]
+
+key1 = [ 1, 2, 3 ]
+key2 = [ "red", "yellow", "green" ]
+key3 = [ [ 1, 2 ], [3, 4, 5] ]
+#key4 = [ [ 1, 2 ], ["a", "b", "c"] ] # this is ok
+
+# Arrays can also be multiline. So in addition to ignoring whitespace, arrays
+# also ignore newlines between the brackets. Terminating commas are ok before
+# the closing bracket.
+
+key5 = [
+ 1, 2, 3
+]
+key6 = [
+ 1,
+ 2, # this is ok
+]
+
+
+################################################################################
+## Array of Tables
+
+# These can be expressed by using a table name in double brackets. Each table
+# with the same double bracketed name will be an element in the array. The
+# tables are inserted in the order encountered.
+
+[[products]]
+
+name = "Hammer"
+sku = 738594937
+
+[[products]]
+
+[[products]]
+
+name = "Nail"
+sku = 284758393
+color = "gray"
+
+
+# You can create nested arrays of tables as well.
+
+[[fruit]]
+ name = "apple"
+
+ [fruit.physical]
+ color = "red"
+ shape = "round"
+
+ [[fruit.variety]]
+ name = "red delicious"
+
+ [[fruit.variety]]
+ name = "granny smith"
+
+[[fruit]]
+ name = "banana"
+
+ [[fruit.variety]]
+ name = "plantain"
diff --git a/vendor/github.com/pelletier/go-toml/example-crlf.toml b/vendor/github.com/pelletier/go-toml/example-crlf.toml
new file mode 100644
index 00000000..12950a16
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/example-crlf.toml
@@ -0,0 +1,29 @@
+# This is a TOML document. Boom.
+
+title = "TOML Example"
+
+[owner]
+name = "Tom Preston-Werner"
+organization = "GitHub"
+bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
+dob = 1979-05-27T07:32:00Z # First class dates? Why not?
+
+[database]
+server = "192.168.1.1"
+ports = [ 8001, 8001, 8002 ]
+connection_max = 5000
+enabled = true
+
+[servers]
+
+ # You can indent as you please. Tabs or spaces. TOML don't care.
+ [servers.alpha]
+ ip = "10.0.0.1"
+ dc = "eqdc10"
+
+ [servers.beta]
+ ip = "10.0.0.2"
+ dc = "eqdc10"
+
+[clients]
+data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
diff --git a/vendor/github.com/pelletier/go-toml/example.toml b/vendor/github.com/pelletier/go-toml/example.toml
new file mode 100644
index 00000000..3d902f28
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/example.toml
@@ -0,0 +1,29 @@
+# This is a TOML document. Boom.
+
+title = "TOML Example"
+
+[owner]
+name = "Tom Preston-Werner"
+organization = "GitHub"
+bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
+dob = 1979-05-27T07:32:00Z # First class dates? Why not?
+
+[database]
+server = "192.168.1.1"
+ports = [ 8001, 8001, 8002 ]
+connection_max = 5000
+enabled = true
+
+[servers]
+
+ # You can indent as you please. Tabs or spaces. TOML don't care.
+ [servers.alpha]
+ ip = "10.0.0.1"
+ dc = "eqdc10"
+
+ [servers.beta]
+ ip = "10.0.0.2"
+ dc = "eqdc10"
+
+[clients]
+data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
diff --git a/vendor/github.com/pelletier/go-toml/marshal.go b/vendor/github.com/pelletier/go-toml/marshal.go
index f584ba4e..671da556 100644
--- a/vendor/github.com/pelletier/go-toml/marshal.go
+++ b/vendor/github.com/pelletier/go-toml/marshal.go
@@ -11,10 +11,13 @@ import (
"time"
)
+const tagKeyMultiline = "multiline"
+
type tomlOpts struct {
name string
comment string
commented bool
+ multiline bool
include bool
omitempty bool
}
@@ -230,7 +233,12 @@ func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, er
if err != nil {
return nil, err
}
- tval.SetWithComment(opts.name, opts.comment, opts.commented, val)
+
+ tval.SetWithOptions(opts.name, SetOptions{
+ Comment: opts.comment,
+ Commented: opts.commented,
+ Multiline: opts.multiline,
+ }, val)
}
}
case reflect.Map:
@@ -559,7 +567,8 @@ func tomlOptions(vf reflect.StructField) tomlOpts {
comment = c
}
commented, _ := strconv.ParseBool(vf.Tag.Get("commented"))
- result := tomlOpts{name: vf.Name, comment: comment, commented: commented, include: true, omitempty: false}
+ multiline, _ := strconv.ParseBool(vf.Tag.Get(tagKeyMultiline))
+ result := tomlOpts{name: vf.Name, comment: comment, commented: commented, multiline: multiline, include: true, omitempty: false}
if parse[0] != "" {
if parse[0] == "-" && len(parse) == 1 {
result.include = false
diff --git a/vendor/github.com/pelletier/go-toml/marshal_test.toml b/vendor/github.com/pelletier/go-toml/marshal_test.toml
new file mode 100644
index 00000000..1c5f98e7
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/marshal_test.toml
@@ -0,0 +1,38 @@
+title = "TOML Marshal Testing"
+
+[basic]
+ bool = true
+ date = 1979-05-27T07:32:00Z
+ float = 123.4
+ int = 5000
+ string = "Bite me"
+ uint = 5001
+
+[basic_lists]
+ bools = [true,false,true]
+ dates = [1979-05-27T07:32:00Z,1980-05-27T07:32:00Z]
+ floats = [12.3,45.6,78.9]
+ ints = [8001,8001,8002]
+ strings = ["One","Two","Three"]
+ uints = [5002,5003]
+
+[basic_map]
+ one = "one"
+ two = "two"
+
+[subdoc]
+
+ [subdoc.first]
+ name = "First"
+
+ [subdoc.second]
+ name = "Second"
+
+[[subdoclist]]
+ name = "List.First"
+
+[[subdoclist]]
+ name = "List.Second"
+
+[[subdocptrs]]
+ name = "Second"
diff --git a/vendor/github.com/pelletier/go-toml/test.sh b/vendor/github.com/pelletier/go-toml/test.sh
index a70a8b02..ba6adf3f 100644
--- a/vendor/github.com/pelletier/go-toml/test.sh
+++ b/vendor/github.com/pelletier/go-toml/test.sh
@@ -23,9 +23,6 @@ function git_clone() {
# Remove potential previous runs
rm -rf src test_program_bin toml-test
-# Run go vet
-go vet ./...
-
go get github.com/pelletier/go-buffruneio
go get github.com/davecgh/go-spew/spew
go get gopkg.in/yaml.v2
diff --git a/vendor/github.com/pelletier/go-toml/toml.go b/vendor/github.com/pelletier/go-toml/toml.go
index 05493a44..98c185ad 100644
--- a/vendor/github.com/pelletier/go-toml/toml.go
+++ b/vendor/github.com/pelletier/go-toml/toml.go
@@ -14,6 +14,7 @@ type tomlValue struct {
value interface{} // string, int64, uint64, float64, bool, time.Time, [] of any of this list
comment string
commented bool
+ multiline bool
position Position
}
@@ -175,6 +176,63 @@ func (t *Tree) GetDefault(key string, def interface{}) interface{} {
return val
}
+// SetOptions arguments are supplied to the SetWithOptions and SetPathWithOptions functions to modify marshalling behaviour.
+// The default values within the struct are valid default options.
+type SetOptions struct {
+ Comment string
+ Commented bool
+ Multiline bool
+}
+
+// SetWithOptions is the same as Set, but allows you to provide formatting
+// instructions to the key, that will be used by Marshal().
+func (t *Tree) SetWithOptions(key string, opts SetOptions, value interface{}) {
+ t.SetPathWithOptions(strings.Split(key, "."), opts, value)
+}
+
+// SetPathWithOptions is the same as SetPath, but allows you to provide
+// formatting instructions to the key, that will be reused by Marshal().
+func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interface{}) {
+ subtree := t
+ for _, intermediateKey := range keys[:len(keys)-1] {
+ nextTree, exists := subtree.values[intermediateKey]
+ if !exists {
+ nextTree = newTree()
+ subtree.values[intermediateKey] = nextTree // add new element here
+ }
+ switch node := nextTree.(type) {
+ case *Tree:
+ subtree = node
+ case []*Tree:
+ // go to most recent element
+ if len(node) == 0 {
+ // create element if it does not exist
+ subtree.values[intermediateKey] = append(node, newTree())
+ }
+ subtree = node[len(node)-1]
+ }
+ }
+
+ var toInsert interface{}
+
+ switch value.(type) {
+ case *Tree:
+ tt := value.(*Tree)
+ tt.comment = opts.Comment
+ toInsert = value
+ case []*Tree:
+ toInsert = value
+ case *tomlValue:
+ tt := value.(*tomlValue)
+ tt.comment = opts.Comment
+ toInsert = tt
+ default:
+ toInsert = &tomlValue{value: value, comment: opts.Comment, commented: opts.Commented, multiline: opts.Multiline}
+ }
+
+ subtree.values[keys[len(keys)-1]] = toInsert
+}
+
// Set an element in the tree.
// Key is a dot-separated path (e.g. a.b.c).
// Creates all necessary intermediate trees, if needed.
diff --git a/vendor/github.com/pelletier/go-toml/tomltree_write.go b/vendor/github.com/pelletier/go-toml/tomltree_write.go
index b5600a58..e4049e29 100644
--- a/vendor/github.com/pelletier/go-toml/tomltree_write.go
+++ b/vendor/github.com/pelletier/go-toml/tomltree_write.go
@@ -12,7 +12,41 @@ import (
"time"
)
-// encodes a string to a TOML-compliant string value
+// Encodes a string to a TOML-compliant multi-line string value
+// This function is a clone of the existing encodeTomlString function, except that whitespace characters
+// are preserved. Quotation marks and backslashes are also not escaped.
+func encodeMultilineTomlString(value string) string {
+ var b bytes.Buffer
+
+ for _, rr := range value {
+ switch rr {
+ case '\b':
+ b.WriteString(`\b`)
+ case '\t':
+ b.WriteString("\t")
+ case '\n':
+ b.WriteString("\n")
+ case '\f':
+ b.WriteString(`\f`)
+ case '\r':
+ b.WriteString("\r")
+ case '"':
+ b.WriteString(`"`)
+ case '\\':
+ b.WriteString(`\`)
+ default:
+ intRr := uint16(rr)
+ if intRr < 0x001F {
+ b.WriteString(fmt.Sprintf("\\u%0.4X", intRr))
+ } else {
+ b.WriteRune(rr)
+ }
+ }
+ }
+ return b.String()
+}
+
+// Encodes a string to a TOML-compliant string value
func encodeTomlString(value string) string {
var b bytes.Buffer
@@ -45,6 +79,15 @@ func encodeTomlString(value string) string {
}
func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElementPerLine bool) (string, error) {
+ // this interface check is added to dereference the change made in the writeTo function.
+ // That change was made to allow this function to see formatting options.
+ tv, ok := v.(*tomlValue)
+ if ok {
+ v = tv.value
+ } else {
+ tv = &tomlValue{}
+ }
+
switch value := v.(type) {
case uint64:
return strconv.FormatUint(value, 10), nil
@@ -58,6 +101,9 @@ func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElemen
}
return strings.ToLower(strconv.FormatFloat(value, 'f', -1, 32)), nil
case string:
+ if tv.multiline {
+ return "\"\"\"\n" + encodeMultilineTomlString(value) + "\"\"\"", nil
+ }
return "\"" + encodeTomlString(value) + "\"", nil
case []byte:
b, _ := v.([]byte)
@@ -130,7 +176,7 @@ func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64, a
return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k])
}
- repr, err := tomlValueStringRepresentation(v.value, indent, arraysOneElementPerLine)
+ repr, err := tomlValueStringRepresentation(v, indent, arraysOneElementPerLine)
if err != nil {
return bytesCount, err
}
diff --git a/vendor/github.com/peterhellberg/emojilib/.travis.yml b/vendor/github.com/peterhellberg/emojilib/.travis.yml
index 15cfa8b6..99c59c46 100644
--- a/vendor/github.com/peterhellberg/emojilib/.travis.yml
+++ b/vendor/github.com/peterhellberg/emojilib/.travis.yml
@@ -1,8 +1,11 @@
language: go
go:
- - 1.8
- - 1.7.5
- - 1.6.4
+ - "1.10.3"
+ - "1.9.7"
sudo: false
+
+script:
+ - go vet ./...
+ - go test ./...
diff --git a/vendor/github.com/peterhellberg/emojilib/LICENSE b/vendor/github.com/peterhellberg/emojilib/LICENSE
index 782fe09a..e1df4843 100644
--- a/vendor/github.com/peterhellberg/emojilib/LICENSE
+++ b/vendor/github.com/peterhellberg/emojilib/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2015-2017 Peter Hellberg https://c7.se/
+Copyright (c) 2015-2018 Peter Hellberg https://c7.se/
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
diff --git a/vendor/github.com/peterhellberg/emojilib/README.md b/vendor/github.com/peterhellberg/emojilib/README.md
index 3175a43d..2a8ab690 100644
--- a/vendor/github.com/peterhellberg/emojilib/README.md
+++ b/vendor/github.com/peterhellberg/emojilib/README.md
@@ -39,7 +39,7 @@ _You’ll need to have the [golang.org/x/tools/imports](https://golang.org/x/too
## License (MIT)
-Copyright (c) 2015-2017 [Peter Hellberg](http://c7.se/)
+Copyright (c) 2015-2018 [Peter Hellberg](http://c7.se/)
> Permission is hereby granted, free of charge, to any person obtaining
> a copy of this software and associated documentation files (the
diff --git a/vendor/github.com/peterhellberg/emojilib/generated.go b/vendor/github.com/peterhellberg/emojilib/generated.go
index a5976471..9bc3ea3b 100644
--- a/vendor/github.com/peterhellberg/emojilib/generated.go
+++ b/vendor/github.com/peterhellberg/emojilib/generated.go
@@ -2,7 +2,7 @@
//
// Update it by running: go generate
//
-// Generated at: 2017-06-16 16:34:02 +0000
+// Generated at: 2018-08-20 09:01:38 +0000
package emojilib
@@ -74,6 +74,11 @@ var emojis = Emojis{
Char: "🉑",
Category: "symbols",
},
+ "adult": Emoji{
+ Keywords: []string{"gender-neutral", "person"},
+ Char: "🧑",
+ Category: "people",
+ },
"aerial_tramway": Emoji{
Keywords: []string{"transportation", "vehicle", "ski"},
Char: "🚡",
@@ -105,7 +110,7 @@ var emojis = Emojis{
Category: "flags",
},
"alembic": Emoji{
- Keywords: []string{"distilling", "science", "experiment"},
+ Keywords: []string{"distilling", "science", "experiment", "chemistry"},
Char: "⚗",
Category: "objects",
},
@@ -355,7 +360,7 @@ var emojis = Emojis{
Category: "symbols",
},
"atom_symbol": Emoji{
- Keywords: []string{"science"},
+ Keywords: []string{"science", "physics", "chemistry"},
Char: "⚛",
Category: "symbols",
},
@@ -549,6 +554,11 @@ var emojis = Emojis{
Char: "🐻",
Category: "animals_and_nature",
},
+ "bearded_person": Emoji{
+ Keywords: []string{"person", "bewhiskered"},
+ Char: "🧔",
+ Category: "people",
+ },
"bed": Emoji{
Keywords: []string{"sleep", "rest"},
Char: "🛏",
@@ -639,6 +649,11 @@ var emojis = Emojis{
Char: "👙",
Category: "people",
},
+ "billed_hat": Emoji{
+ Keywords: []string{"cap", "baseball"},
+ Char: "🧢",
+ Category: "people",
+ },
"biohazard": Emoji{
Keywords: []string{"danger"},
Char: "☣",
@@ -814,16 +829,16 @@ var emojis = Emojis{
Char: "🙇♀️",
Category: "people",
},
+ "bowl_with_spoon": Emoji{
+ Keywords: []string{"food", "breakfast", "cereal", "oatmeal", "porridge"},
+ Char: "🥣",
+ Category: "food_and_drink",
+ },
"bowling": Emoji{
Keywords: []string{"sports", "fun", "play"},
Char: "🎳",
Category: "activity",
},
- "bowtie": Emoji{
- Keywords: []string{"face", "formal", "fashion", "suit", "classy", "magic", "circus"},
- Char: "",
- Category: "_custom",
- },
"boxing_glove": Emoji{
Keywords: []string{"sports", "fighting"},
Char: "🥊",
@@ -834,6 +849,11 @@ var emojis = Emojis{
Char: "👦",
Category: "people",
},
+ "brain": Emoji{
+ Keywords: []string{"smart", "intelligent"},
+ Char: "🧠",
+ Category: "people",
+ },
"brazil": Emoji{
Keywords: []string{"br", "flag", "nation", "country", "banner"},
Char: "🇧🇷",
@@ -844,6 +864,11 @@ var emojis = Emojis{
Char: "🍞",
Category: "food_and_drink",
},
+ "breastfeeding": Emoji{
+ Keywords: []string{"nursing", "baby"},
+ Char: "🤱",
+ Category: "people",
+ },
"bride_with_veil": Emoji{
Keywords: []string{"couple", "marriage", "wedding", "woman", "bride"},
Char: "👰",
@@ -855,7 +880,7 @@ var emojis = Emojis{
Category: "travel_and_places",
},
"briefcase": Emoji{
- Keywords: []string{"business", "documents", "work", "law", "legal"},
+ Keywords: []string{"business", "documents", "work", "law", "legal", "job", "career"},
Char: "💼",
Category: "people",
},
@@ -869,6 +894,11 @@ var emojis = Emojis{
Char: "🇻🇬",
Category: "flags",
},
+ "broccoli": Emoji{
+ Keywords: []string{"fruit", "food", "vegetable"},
+ Char: "🥦",
+ Category: "food_and_drink",
+ },
"broken_heart": Emoji{
Keywords: []string{"sad", "sorry", "break", "heart", "heartbreak"},
Char: "💔",
@@ -1034,6 +1064,11 @@ var emojis = Emojis{
Char: "🍬",
Category: "food_and_drink",
},
+ "canned_food": Emoji{
+ Keywords: []string{"food", "soup"},
+ Char: "🥫",
+ Category: "food_and_drink",
+ },
"canoe": Emoji{
Keywords: []string{"boat", "paddle", "water", "ship"},
Char: "🛶",
@@ -1169,6 +1204,11 @@ var emojis = Emojis{
Char: "🐔",
Category: "animals_and_nature",
},
+ "child": Emoji{
+ Keywords: []string{"gender-neutral", "young"},
+ Char: "🧒",
+ Category: "people",
+ },
"children_crossing": Emoji{
Keywords: []string{"school", "warning", "danger", "sign", "driving", "yellow-diamond"},
Char: "🚸",
@@ -1189,6 +1229,11 @@ var emojis = Emojis{
Char: "🍫",
Category: "food_and_drink",
},
+ "chopsticks": Emoji{
+ Keywords: []string{"food"},
+ Char: "🥢",
+ Category: "food_and_drink",
+ },
"christmas_island": Emoji{
Keywords: []string{"christmas", "island", "flag", "nation", "country", "banner"},
Char: "🇨🇽",
@@ -1254,8 +1299,18 @@ var emojis = Emojis{
Char: "🏛",
Category: "travel_and_places",
},
+ "climbing_man": Emoji{
+ Keywords: []string{"sports", "hobby", "man", "male", "rock"},
+ Char: "🧗♂️",
+ Category: "activity",
+ },
+ "climbing_woman": Emoji{
+ Keywords: []string{"sports", "hobby", "woman", "female", "rock"},
+ Char: "🧗♀️",
+ Category: "activity",
+ },
"clinking_glasses": Emoji{
- Keywords: []string{"beverage", "drink", "party", "alcohol", "celebrate", "cheers"},
+ Keywords: []string{"beverage", "drink", "party", "alcohol", "celebrate", "cheers", "wine", "champagne", "toast"},
Char: "🥂",
Category: "food_and_drink",
},
@@ -1439,11 +1494,21 @@ var emojis = Emojis{
Char: "🇨🇳",
Category: "flags",
},
+ "coat": Emoji{
+ Keywords: []string{"jacket"},
+ Char: "🧥",
+ Category: "people",
+ },
"cocktail": Emoji{
Keywords: []string{"drink", "drunk", "alcohol", "beverage", "booze", "mojito"},
Char: "🍸",
Category: "food_and_drink",
},
+ "coconut": Emoji{
+ Keywords: []string{"fruit", "nature", "food", "palm"},
+ Char: "🥥",
+ Category: "food_and_drink",
+ },
"cocos_islands": Emoji{
Keywords: []string{"cocos", "keeling", "islands", "flag", "nation", "country", "banner"},
Char: "🇨🇨",
@@ -1455,7 +1520,7 @@ var emojis = Emojis{
Category: "food_and_drink",
},
"coffin": Emoji{
- Keywords: []string{"vampire", "dead", "die", "death", "rip", "graveyard", "cemetery"},
+ Keywords: []string{"vampire", "dead", "die", "death", "rip", "graveyard", "cemetery", "casket", "funeral", "box"},
Char: "⚰",
Category: "objects",
},
@@ -1719,6 +1784,11 @@ var emojis = Emojis{
Char: "🥒",
Category: "food_and_drink",
},
+ "cup_with_straw": Emoji{
+ Keywords: []string{"drink", "soda"},
+ Char: "🥤",
+ Category: "food_and_drink",
+ },
"cupid": Emoji{
Keywords: []string{"love", "like", "heart", "affection", "valentines"},
Char: "💘",
@@ -1729,6 +1799,11 @@ var emojis = Emojis{
Char: "🇨🇼",
Category: "flags",
},
+ "curling_stone": Emoji{
+ Keywords: []string{"sports"},
+ Char: "🥌",
+ Category: "activity",
+ },
"curly_loop": Emoji{
Keywords: []string{"scribble", "draw", "shape", "squiggle"},
Char: "➰",
@@ -1755,7 +1830,7 @@ var emojis = Emojis{
Category: "symbols",
},
"cyclone": Emoji{
- Keywords: []string{"weather", "swirl", "blue", "cloud", "vortex", "spiral", "whirlpool", "spin"},
+ Keywords: []string{"weather", "swirl", "blue", "cloud", "vortex", "spiral", "whirlpool", "spin", "tornado", "hurricane", "typhoon"},
Char: "🌀",
Category: "symbols",
},
@@ -1800,7 +1875,7 @@ var emojis = Emojis{
Category: "people",
},
"dart": Emoji{
- Keywords: []string{"game", "play", "bar"},
+ Keywords: []string{"game", "play", "bar", "target", "bullseye"},
Char: "🎯",
Category: "activity",
},
@@ -1980,7 +2055,7 @@ var emojis = Emojis{
Category: "animals_and_nature",
},
"drum": Emoji{
- Keywords: []string{"music", "instrument", "drumsticks"},
+ Keywords: []string{"music", "instrument", "drumsticks", "snare"},
Char: "🥁",
Category: "activity",
},
@@ -1989,6 +2064,11 @@ var emojis = Emojis{
Char: "🦆",
Category: "animals_and_nature",
},
+ "dumpling": Emoji{
+ Keywords: []string{"food", "empanada", "pierogi", "potsticker"},
+ Char: "🥟",
+ Category: "food_and_drink",
+ },
"dvd": Emoji{
Keywords: []string{"cd", "disk", "disc"},
Char: "📀",
@@ -2064,6 +2144,11 @@ var emojis = Emojis{
Char: "✳️",
Category: "symbols",
},
+ "eject_button": Emoji{
+ Keywords: []string{"blue-square"},
+ Char: "⏏️",
+ Category: "symbols",
+ },
"el_salvador": Emoji{
Keywords: []string{"el", "salvador", "flag", "nation", "country", "banner"},
Char: "🇸🇻",
@@ -2089,6 +2174,11 @@ var emojis = Emojis{
Char: "🔚",
Category: "symbols",
},
+ "england": Emoji{
+ Keywords: []string{"flag", "english"},
+ Char: "🏴",
+ Category: "flags",
+ },
"envelope_with_arrow": Emoji{
Keywords: []string{"email", "communication"},
Char: "📩",
@@ -2149,6 +2239,11 @@ var emojis = Emojis{
Char: "❗",
Category: "symbols",
},
+ "exploding_head": Emoji{
+ Keywords: []string{"face", "shocked", "mind", "blown"},
+ Char: "🤯",
+ Category: "people",
+ },
"expressionless": Emoji{
Keywords: []string{"face", "indifferent", "-_-", "meh", "deadpan"},
Char: "😑",
@@ -2344,11 +2439,6 @@ var emojis = Emojis{
Char: "😨",
Category: "people",
},
- "feelsgood": Emoji{
- Keywords: []string{"doom", "oldschool"},
- Char: "",
- Category: "_custom",
- },
"female_detective": Emoji{
Keywords: []string{"human", "spy", "detective", "female", "woman"},
Char: "🕵️♀️",
@@ -2399,11 +2489,6 @@ var emojis = Emojis{
Char: "🇫🇮",
Category: "flags",
},
- "finnadie": Emoji{
- Keywords: []string{"doom", "oldschool"},
- Char: "",
- Category: "_custom",
- },
"fire": Emoji{
Keywords: []string{"hot", "cook", "flame"},
Char: "🔥",
@@ -2435,7 +2520,7 @@ var emojis = Emojis{
Category: "animals_and_nature",
},
"fish_cake": Emoji{
- Keywords: []string{"food", "japan", "sea", "beach"},
+ Keywords: []string{"food", "japan", "sea", "beach", "narutomaki", "pink", "swirl", "kamaboko", "surimi", "ramen"},
Char: "🍥",
Category: "food_and_drink",
},
@@ -2504,6 +2589,11 @@ var emojis = Emojis{
Char: "😳",
Category: "people",
},
+ "flying_saucer": Emoji{
+ Keywords: []string{"transportation", "vehicle", "ufo"},
+ Char: "🛸",
+ Category: "travel_and_places",
+ },
"fog": Emoji{
Keywords: []string{"weather"},
Char: "🌫",
@@ -2529,6 +2619,11 @@ var emojis = Emojis{
Char: "🍴",
Category: "food_and_drink",
},
+ "fortune_cookie": Emoji{
+ Keywords: []string{"food", "prophecy"},
+ Char: "🥠",
+ Category: "food_and_drink",
+ },
"fountain": Emoji{
Keywords: []string{"photo", "summer", "water", "fresh"},
Char: "⛲",
@@ -2709,6 +2804,11 @@ var emojis = Emojis{
Char: "💝",
Category: "symbols",
},
+ "giraffe": Emoji{
+ Keywords: []string{"animal", "nature", "spots", "safari"},
+ Char: "🦒",
+ Category: "animals_and_nature",
+ },
"girl": Emoji{
Keywords: []string{"female", "woman", "teenager"},
Char: "👧",
@@ -2719,6 +2819,11 @@ var emojis = Emojis{
Char: "🌐",
Category: "symbols",
},
+ "gloves": Emoji{
+ Keywords: []string{"hands", "winter", "clothes"},
+ Char: "🧤",
+ Category: "people",
+ },
"goal_net": Emoji{
Keywords: []string{"sports"},
Char: "🥅",
@@ -2729,16 +2834,6 @@ var emojis = Emojis{
Char: "🐐",
Category: "animals_and_nature",
},
- "goberserk": Emoji{
- Keywords: []string{"doom", "rage", "bloody", "hurt"},
- Char: "",
- Category: "_custom",
- },
- "godmode": Emoji{
- Keywords: []string{"doom", "oldschool"},
- Char: "",
- Category: "_custom",
- },
"golf": Emoji{
Keywords: []string{"sports", "business", "flag", "hole", "summer"},
Char: "⛳",
@@ -2764,6 +2859,11 @@ var emojis = Emojis{
Char: "🍇",
Category: "food_and_drink",
},
+ "grasshopper": Emoji{
+ Keywords: []string{"animal", "cricket", "chirp"},
+ Char: "🦗",
+ Category: "animals_and_nature",
+ },
"greece": Emoji{
Keywords: []string{"gr", "flag", "nation", "country", "banner"},
Char: "🇬🇷",
@@ -2919,6 +3019,11 @@ var emojis = Emojis{
Char: "🐹",
Category: "animals_and_nature",
},
+ "hand_over_mouth": Emoji{
+ Keywords: []string{"face", "whoops", "shock", "surprise"},
+ Char: "🤭",
+ Category: "people",
+ },
"handbag": Emoji{
Keywords: []string{"fashion", "accessory", "accessories", "shopping"},
Char: "👜",
@@ -3024,6 +3129,11 @@ var emojis = Emojis{
Char: "➕",
Category: "symbols",
},
+ "hedgehog": Emoji{
+ Keywords: []string{"animal", "nature", "spiny"},
+ Char: "🦔",
+ Category: "animals_and_nature",
+ },
"helicopter": Emoji{
Keywords: []string{"transportation", "vehicle", "fly"},
Char: "🚁",
@@ -3149,11 +3259,6 @@ var emojis = Emojis{
Char: "🇭🇺",
Category: "flags",
},
- "hurtrealbad": Emoji{
- Keywords: []string{"mad", "injured", "doom", "oldschool", "custom_"},
- Char: "",
- Category: "_custom",
- },
"hushed": Emoji{
Keywords: []string{"face", "woo", "shh"},
Char: "😯",
@@ -3654,6 +3759,11 @@ var emojis = Emojis{
Char: "💌",
Category: "objects",
},
+ "love_you": Emoji{
+ Keywords: []string{"hand", "fingers", "gesture"},
+ Char: "🤟",
+ Category: "people",
+ },
"low_brightness": Emoji{
Keywords: []string{"sun", "afternoon", "warm", "summer"},
Char: "🔅",
@@ -3784,6 +3894,11 @@ var emojis = Emojis{
Char: "🕺",
Category: "people",
},
+ "man_elf": Emoji{
+ Keywords: []string{"man", "male"},
+ Char: "🧝♂️",
+ Category: "people",
+ },
"man_facepalming": Emoji{
Keywords: []string{"man", "male", "boy", "disbelief"},
Char: "🤦",
@@ -3794,6 +3909,11 @@ var emojis = Emojis{
Char: "👨🏭",
Category: "people",
},
+ "man_fairy": Emoji{
+ Keywords: []string{"man", "male"},
+ Char: "🧚♂️",
+ Category: "people",
+ },
"man_farmer": Emoji{
Keywords: []string{"rancher", "gardener", "man", "human"},
Char: "👨🌾",
@@ -3804,11 +3924,26 @@ var emojis = Emojis{
Char: "👨🚒",
Category: "people",
},
+ "man_genie": Emoji{
+ Keywords: []string{"man", "male"},
+ Char: "🧞♂️",
+ Category: "people",
+ },
"man_health_worker": Emoji{
Keywords: []string{"doctor", "nurse", "therapist", "healthcare", "man", "human"},
Char: "👨⚕️",
Category: "people",
},
+ "man_in_lotus_position": Emoji{
+ Keywords: []string{"man", "male", "meditation", "yoga", "serenity", "zen", "mindfulness"},
+ Char: "🧘♂️",
+ Category: "activity",
+ },
+ "man_in_steamy_room": Emoji{
+ Keywords: []string{"male", "man", "spa", "steamroom", "sauna"},
+ Char: "🧖♂️",
+ Category: "people",
+ },
"man_in_tuxedo": Emoji{
Keywords: []string{"couple", "marriage", "wedding", "groom"},
Char: "🤵",
@@ -3825,7 +3960,7 @@ var emojis = Emojis{
Category: "activity",
},
"man_mechanic": Emoji{
- Keywords: []string{"plumber", "man", "human"},
+ Keywords: []string{"plumber", "man", "human", "wrench"},
Char: "👨🔧",
Category: "people",
},
@@ -3875,10 +4010,15 @@ var emojis = Emojis{
Category: "people",
},
"man_technologist": Emoji{
- Keywords: []string{"coder", "developer", "engineer", "programmer", "software", "man", "human"},
+ Keywords: []string{"coder", "developer", "engineer", "programmer", "software", "man", "human", "laptop", "computer"},
Char: "👨💻",
Category: "people",
},
+ "man_vampire": Emoji{
+ Keywords: []string{"man", "male", "dracula"},
+ Char: "🧛♂️",
+ Category: "people",
+ },
"man_with_gua_pi_mao": Emoji{
Keywords: []string{"male", "boy", "chinese"},
Char: "👲",
@@ -3889,6 +4029,11 @@ var emojis = Emojis{
Char: "👳",
Category: "people",
},
+ "man_zombie": Emoji{
+ Keywords: []string{"man", "male", "dracula", "undead", "walking dead"},
+ Char: "🧟♂️",
+ Category: "people",
+ },
"mans_shoe": Emoji{
Keywords: []string{"fashion", "male"},
Char: "👞",
@@ -3994,6 +4139,16 @@ var emojis = Emojis{
Char: "🚹",
Category: "symbols",
},
+ "mermaid": Emoji{
+ Keywords: []string{"woman", "female", "merwoman", "ariel"},
+ Char: "🧜♀️",
+ Category: "people",
+ },
+ "merman": Emoji{
+ Keywords: []string{"man", "male", "triton"},
+ Char: "🧜♂️",
+ Category: "people",
+ },
"metal": Emoji{
Keywords: []string{"hand", "fingers", "evil_eye", "sign_of_horns", "rock_on"},
Char: "🤘",
@@ -4089,6 +4244,11 @@ var emojis = Emojis{
Char: "🐵",
Category: "animals_and_nature",
},
+ "monocle": Emoji{
+ Keywords: []string{"face", "stuffy", "wealthy"},
+ Char: "🧐",
+ Category: "people",
+ },
"monorail": Emoji{
Keywords: []string{"transportation", "vehicle"},
Char: "🚝",
@@ -4269,11 +4429,6 @@ var emojis = Emojis{
Char: "🤢",
Category: "people",
},
- "neckbeard": Emoji{
- Keywords: []string{"nerdy", "face", "custom_"},
- Char: "",
- Category: "_custom",
- },
"necktie": Emoji{
Keywords: []string{"shirt", "suitup", "formal", "fashion", "cloth", "business"},
Char: "👔",
@@ -4494,11 +4649,6 @@ var emojis = Emojis{
Char: "🌊",
Category: "animals_and_nature",
},
- "octocat": Emoji{
- Keywords: []string{"animal", "octopus", "github", "custom_"},
- Char: "",
- Category: "_custom",
- },
"octopus": Emoji{
Keywords: []string{"animal", "creature", "ocean", "sea", "nature", "beach"},
Char: "🐙",
@@ -4525,7 +4675,7 @@ var emojis = Emojis{
Category: "symbols",
},
"ok_hand": Emoji{
- Keywords: []string{"fingers", "limbs", "perfect", "ok"},
+ Keywords: []string{"fingers", "limbs", "perfect", "ok", "okay"},
Char: "👌",
Category: "people",
},
@@ -4544,6 +4694,11 @@ var emojis = Emojis{
Char: "🗝",
Category: "objects",
},
+ "older_adult": Emoji{
+ Keywords: []string{"human", "elder", "senior", "gender-neutral"},
+ Char: "🧓",
+ Category: "people",
+ },
"older_man": Emoji{
Keywords: []string{"human", "male", "men", "old", "elder", "senior"},
Char: "👴",
@@ -4629,6 +4784,11 @@ var emojis = Emojis{
Char: "📙",
Category: "objects",
},
+ "orange_heart": Emoji{
+ Keywords: []string{"love", "like", "affection", "valentines"},
+ Char: "🧡",
+ Category: "symbols",
+ },
"orthodox_cross": Emoji{
Keywords: []string{"suppedaneum", "religion"},
Char: "☦",
@@ -4694,6 +4854,11 @@ var emojis = Emojis{
Char: "🌴",
Category: "animals_and_nature",
},
+ "palms_up": Emoji{
+ Keywords: []string{"hands", "gesture", "cupped", "prayer"},
+ Char: "🤲",
+ Category: "people",
+ },
"panama": Emoji{
Keywords: []string{"pa", "flag", "nation", "country", "banner"},
Char: "🇵🇦",
@@ -4805,7 +4970,7 @@ var emojis = Emojis{
Category: "animals_and_nature",
},
"pensive": Emoji{
- Keywords: []string{"face", "sad", "depressed", "okay", "upset"},
+ Keywords: []string{"face", "sad", "depressed", "upset"},
Char: "😔",
Category: "people",
},
@@ -4844,6 +5009,11 @@ var emojis = Emojis{
Char: "⛏",
Category: "objects",
},
+ "pie": Emoji{
+ Keywords: []string{"food", "dessert", "pastry"},
+ Char: "🥧",
+ Category: "food_and_drink",
+ },
"pig": Emoji{
Keywords: []string{"animal", "oink", "nature"},
Char: "🐷",
@@ -5039,6 +5209,11 @@ var emojis = Emojis{
Char: "🤰",
Category: "people",
},
+ "pretzel": Emoji{
+ Keywords: []string{"food", "bread", "twisted"},
+ Char: "🥨",
+ Category: "food_and_drink",
+ },
"previous_track_button": Emoji{
Keywords: []string{"backward"},
Char: "⏮",
@@ -5134,26 +5309,6 @@ var emojis = Emojis{
Char: "😡",
Category: "people",
},
- "rage1": Emoji{
- Keywords: []string{"angry", "mad", "hate", "despise"},
- Char: "",
- Category: "_custom",
- },
- "rage2": Emoji{
- Keywords: []string{"angry", "mad", "hate", "despise"},
- Char: "",
- Category: "_custom",
- },
- "rage3": Emoji{
- Keywords: []string{"angry", "mad", "hate", "despise"},
- Char: "",
- Category: "_custom",
- },
- "rage4": Emoji{
- Keywords: []string{"angry", "mad", "hate", "despise"},
- Char: "",
- Category: "_custom",
- },
"railway_car": Emoji{
Keywords: []string{"transportation", "vehicle"},
Char: "🚃",
@@ -5179,6 +5334,11 @@ var emojis = Emojis{
Char: "🤚",
Category: "people",
},
+ "raised_eyebrow": Emoji{
+ Keywords: []string{"face", "distrust", "scepticism", "disapproval", "disbelief", "surprise"},
+ Char: "🤨",
+ Category: "people",
+ },
"raised_hand": Emoji{
Keywords: []string{"fingers", "stop", "highfive", "palm", "ban"},
Char: "✋",
@@ -5464,6 +5624,11 @@ var emojis = Emojis{
Char: "👡",
Category: "people",
},
+ "sandwich": Emoji{
+ Keywords: []string{"food", "lunch", "bread"},
+ Char: "🥪",
+ Category: "food_and_drink",
+ },
"santa": Emoji{
Keywords: []string{"festival", "man", "male", "xmas", "father christmas"},
Char: "🎅",
@@ -5484,11 +5649,21 @@ var emojis = Emojis{
Char: "🇸🇦",
Category: "flags",
},
+ "sauropod": Emoji{
+ Keywords: []string{"animal", "nature", "dinosaur", "brachiosaurus", "brontosaurus", "diplodocus", "extinct"},
+ Char: "🦕",
+ Category: "animals_and_nature",
+ },
"saxophone": Emoji{
Keywords: []string{"music", "instrument", "jazz", "blues"},
Char: "🎷",
Category: "activity",
},
+ "scarf": Emoji{
+ Keywords: []string{"neck", "winter", "clothes"},
+ Char: "🧣",
+ Category: "people",
+ },
"school": Emoji{
Keywords: []string{"building", "student", "education", "learn", "teach"},
Char: "🏫",
@@ -5514,6 +5689,11 @@ var emojis = Emojis{
Char: "♏",
Category: "symbols",
},
+ "scotland": Emoji{
+ Keywords: []string{"flag", "scottish"},
+ Char: "🏴",
+ Category: "flags",
+ },
"scream": Emoji{
Keywords: []string{"face", "munch", "scared", "omg"},
Char: "😱",
@@ -5619,11 +5799,6 @@ var emojis = Emojis{
Char: "🚢",
Category: "travel_and_places",
},
- "shipit": Emoji{
- Keywords: []string{"squirrel", "detective", "animal", "sherlock", "inspector", "custom_"},
- Char: "",
- Category: "_custom",
- },
"shopping": Emoji{
Keywords: []string{"mall", "buy", "purchase"},
Char: "🛍",
@@ -5644,6 +5819,11 @@ var emojis = Emojis{
Char: "🦐",
Category: "animals_and_nature",
},
+ "shushing": Emoji{
+ Keywords: []string{"face", "quiet", "shhh"},
+ Char: "🤫",
+ Category: "people",
+ },
"sierra_leone": Emoji{
Keywords: []string{"sierra", "leone", "flag", "nation", "country", "banner"},
Char: "🇸🇱",
@@ -5685,15 +5865,20 @@ var emojis = Emojis{
Category: "activity",
},
"skull": Emoji{
- Keywords: []string{"dead", "skeleton", "creepy"},
+ Keywords: []string{"dead", "skeleton", "creepy", "death"},
Char: "💀",
Category: "people",
},
"skull_and_crossbones": Emoji{
- Keywords: []string{"poison", "danger", "deadly", "scary"},
+ Keywords: []string{"poison", "danger", "deadly", "scary", "death", "pirate", "evil"},
Char: "☠",
Category: "objects",
},
+ "sled": Emoji{
+ Keywords: []string{"sleigh", "luge", "toboggan"},
+ Char: "🛷",
+ Category: "activity",
+ },
"sleeping": Emoji{
Keywords: []string{"face", "tired", "sleepy", "night", "zzz"},
Char: "😴",
@@ -5844,6 +6029,11 @@ var emojis = Emojis{
Char: "⚽",
Category: "activity",
},
+ "socks": Emoji{
+ Keywords: []string{"stockings", "clothes"},
+ Char: "🧦",
+ Category: "people",
+ },
"solomon_islands": Emoji{
Keywords: []string{"solomon", "islands", "flag", "nation", "country", "banner"},
Char: "🇸🇧",
@@ -5859,6 +6049,11 @@ var emojis = Emojis{
Char: "🔜",
Category: "symbols",
},
+ "sorceress": Emoji{
+ Keywords: []string{"woman", "female", "mage", "witch"},
+ Char: "🧙♀️",
+ Category: "people",
+ },
"sos": Emoji{
Keywords: []string{"help", "red-square", "words", "emergency", "911"},
Char: "🆘",
@@ -6034,6 +6229,11 @@ var emojis = Emojis{
Char: "✡",
Category: "symbols",
},
+ "star_struck": Emoji{
+ Keywords: []string{"face", "smile", "starry", "eyes", "grinning"},
+ Char: "🤩",
+ Category: "people",
+ },
"stars": Emoji{
Keywords: []string{"night", "photo"},
Char: "🌠",
@@ -6049,6 +6249,11 @@ var emojis = Emojis{
Char: "🗽",
Category: "travel_and_places",
},
+ "steak": Emoji{
+ Keywords: []string{"food", "cow", "meat", "cut", "chop", "lambchop", "porkchop"},
+ Char: "🥩",
+ Category: "food_and_drink",
+ },
"steam_locomotive": Emoji{
Keywords: []string{"transportation", "vehicle", "train"},
Char: "🚂",
@@ -6179,11 +6384,6 @@ var emojis = Emojis{
Char: "🍣",
Category: "food_and_drink",
},
- "suspect": Emoji{
- Keywords: []string{"mad", "custom_"},
- Char: "",
- Category: "_custom",
- },
"suspension_railway": Emoji{
Keywords: []string{"vehicle", "transportation"},
Char: "🚟",
@@ -6239,6 +6439,11 @@ var emojis = Emojis{
Char: "🔣",
Category: "symbols",
},
+ "symbols_over_mouth": Emoji{
+ Keywords: []string{"face", "swearing", "cursing", "cussing", "profanity", "expletive"},
+ Char: "🤬",
+ Category: "people",
+ },
"synagogue": Emoji{
Keywords: []string{"judaism", "worship", "temple", "jewish"},
Char: "🕍",
@@ -6254,6 +6459,11 @@ var emojis = Emojis{
Char: "💉",
Category: "objects",
},
+ "t-rex": Emoji{
+ Keywords: []string{"animal", "nature", "dinosaur", "tyrannosaurus", "extinct"},
+ Char: "🦖",
+ Category: "animals_and_nature",
+ },
"taco": Emoji{
Keywords: []string{"food", "mexican"},
Char: "🌮",
@@ -6274,6 +6484,11 @@ var emojis = Emojis{
Char: "🇹🇯",
Category: "flags",
},
+ "takeout_box": Emoji{
+ Keywords: []string{"food", "leftovers"},
+ Char: "🥡",
+ Category: "food_and_drink",
+ },
"tanabata_tree": Emoji{
Keywords: []string{"plant", "nature", "branch", "summer"},
Char: "🎋",
@@ -6310,7 +6525,7 @@ var emojis = Emojis{
Category: "objects",
},
"telescope": Emoji{
- Keywords: []string{"stars", "space", "zoom"},
+ Keywords: []string{"stars", "space", "zoom", "science", "astronomy"},
Char: "🔭",
Category: "objects",
},
@@ -6514,11 +6729,6 @@ var emojis = Emojis{
Char: "🚎",
Category: "travel_and_places",
},
- "trollface": Emoji{
- Keywords: []string{"internet", "meme", "custom_"},
- Char: "",
- Category: "_custom",
- },
"trophy": Emoji{
Keywords: []string{"win", "award", "contest", "place", "ftw", "ceremony"},
Char: "🏆",
@@ -6695,7 +6905,7 @@ var emojis = Emojis{
Category: "animals_and_nature",
},
"unamused": Emoji{
- Keywords: []string{"indifference", "bored", "straight face", "serious", "sarcasm"},
+ Keywords: []string{"indifference", "bored", "straight face", "serious", "sarcasm", "unimpressed", "skeptical", "dubious", "side_eye"},
Char: "😒",
Category: "people",
},
@@ -6819,6 +7029,11 @@ var emojis = Emojis{
Char: "🏐",
Category: "activity",
},
+ "vomiting": Emoji{
+ Keywords: []string{"face", "sick"},
+ Char: "🤮",
+ Category: "people",
+ },
"vs": Emoji{
Keywords: []string{"words", "orange-square"},
Char: "🆚",
@@ -6829,6 +7044,11 @@ var emojis = Emojis{
Char: "🖖",
Category: "people",
},
+ "wales": Emoji{
+ Keywords: []string{"flag", "welsh"},
+ Char: "🏴",
+ Category: "flags",
+ },
"walking_man": Emoji{
Keywords: []string{"human", "feet", "steps"},
Char: "🚶",
@@ -7019,6 +7239,11 @@ var emojis = Emojis{
Char: "😉",
Category: "people",
},
+ "wizard": Emoji{
+ Keywords: []string{"man", "male", "mage", "sorcerer"},
+ Char: "🧙♂️",
+ Category: "people",
+ },
"wolf": Emoji{
Keywords: []string{"animal", "nature", "wild"},
Char: "🐺",
@@ -7049,6 +7274,11 @@ var emojis = Emojis{
Char: "👩🍳",
Category: "people",
},
+ "woman_elf": Emoji{
+ Keywords: []string{"woman", "female"},
+ Char: "🧝♀️",
+ Category: "people",
+ },
"woman_facepalming": Emoji{
Keywords: []string{"woman", "female", "girl", "disbelief"},
Char: "🤦♀️",
@@ -7059,6 +7289,11 @@ var emojis = Emojis{
Char: "👩🏭",
Category: "people",
},
+ "woman_fairy": Emoji{
+ Keywords: []string{"woman", "female"},
+ Char: "🧚♀️",
+ Category: "people",
+ },
"woman_farmer": Emoji{
Keywords: []string{"rancher", "gardener", "woman", "human"},
Char: "👩🌾",
@@ -7069,11 +7304,26 @@ var emojis = Emojis{
Char: "👩🚒",
Category: "people",
},
+ "woman_genie": Emoji{
+ Keywords: []string{"woman", "female"},
+ Char: "🧞♀️",
+ Category: "people",
+ },
"woman_health_worker": Emoji{
Keywords: []string{"doctor", "nurse", "therapist", "healthcare", "woman", "human"},
Char: "👩⚕️",
Category: "people",
},
+ "woman_in_lotus_position": Emoji{
+ Keywords: []string{"woman", "female", "meditation", "yoga", "serenity", "zen", "mindfulness"},
+ Char: "🧘♀️",
+ Category: "activity",
+ },
+ "woman_in_steamy_room": Emoji{
+ Keywords: []string{"female", "woman", "spa", "steamroom", "sauna"},
+ Char: "🧖♀️",
+ Category: "people",
+ },
"woman_judge": Emoji{
Keywords: []string{"justice", "court", "woman", "human"},
Char: "👩⚖️",
@@ -7085,7 +7335,7 @@ var emojis = Emojis{
Category: "activity",
},
"woman_mechanic": Emoji{
- Keywords: []string{"plumber", "woman", "human"},
+ Keywords: []string{"plumber", "woman", "human", "wrench"},
Char: "👩🔧",
Category: "people",
},
@@ -7135,15 +7385,30 @@ var emojis = Emojis{
Category: "people",
},
"woman_technologist": Emoji{
- Keywords: []string{"coder", "developer", "engineer", "programmer", "software", "woman", "human"},
+ Keywords: []string{"coder", "developer", "engineer", "programmer", "software", "woman", "human", "laptop", "computer"},
Char: "👩💻",
Category: "people",
},
+ "woman_vampire": Emoji{
+ Keywords: []string{"woman", "female"},
+ Char: "🧛♀️",
+ Category: "people",
+ },
+ "woman_with_headscarf": Emoji{
+ Keywords: []string{"female", "hijab", "mantilla", "tichel"},
+ Char: "🧕",
+ Category: "people",
+ },
"woman_with_turban": Emoji{
Keywords: []string{"female", "indian", "hinduism", "arabs", "woman"},
Char: "👳♀️",
Category: "people",
},
+ "woman_zombie": Emoji{
+ Keywords: []string{"woman", "female", "undead", "walking dead"},
+ Char: "🧟♀️",
+ Category: "people",
+ },
"womans_clothes": Emoji{
Keywords: []string{"fashion", "shopping_bags", "female"},
Char: "👚",
@@ -7219,11 +7484,21 @@ var emojis = Emojis{
Char: "🇿🇲",
Category: "flags",
},
+ "zany": Emoji{
+ Keywords: []string{"face", "goofy", "crazy"},
+ Char: "🤪",
+ Category: "people",
+ },
"zap": Emoji{
Keywords: []string{"thunder", "weather", "lightning bolt", "fast"},
Char: "⚡",
Category: "animals_and_nature",
},
+ "zebra": Emoji{
+ Keywords: []string{"animal", "nature", "stripes", "safari"},
+ Char: "🦓",
+ Category: "animals_and_nature",
+ },
"zero": Emoji{
Keywords: []string{"0", "numbers", "blue-square", "null"},
Char: "0️⃣",
@@ -7431,6 +7706,7 @@ var keywordLookup = map[string][]string{
"kissing_smiling_eyes",
"love_hotel",
"love_letter",
+ "orange_heart",
"purple_heart",
"revolving_hearts",
"sparkling_heart",
@@ -7542,10 +7818,6 @@ var keywordLookup = map[string][]string{
"facepunch",
"imp",
"rage",
- "rage1",
- "rage2",
- "rage3",
- "rage4",
},
"animal": []string{
"ant",
@@ -7583,13 +7855,16 @@ var keywordLookup = map[string][]string{
"fox_face",
"fried_shrimp",
"frog",
+ "giraffe",
"goat",
"gorilla",
+ "grasshopper",
"hamster",
"hatched_chick",
"hatching_chick",
"hear_no_evil",
"heart_eyes_cat",
+ "hedgehog",
"honeybee",
"horse",
"horse_racing",
@@ -7603,7 +7878,6 @@ var keywordLookup = map[string][]string{
"monkey_face",
"mouse",
"mouse2",
- "octocat",
"octopus",
"owl",
"ox",
@@ -7622,12 +7896,12 @@ var keywordLookup = map[string][]string{
"rat",
"rhinoceros",
"rooster",
+ "sauropod",
"scorpion",
"scream_cat",
"see_no_evil",
"shark",
"sheep",
- "shipit",
"shrimp",
"smile_cat",
"smiley_cat",
@@ -7638,6 +7912,7 @@ var keywordLookup = map[string][]string{
"spider",
"spider_web",
"squid",
+ "t-rex",
"tiger",
"tiger2",
"tropical_fish",
@@ -7648,6 +7923,7 @@ var keywordLookup = map[string][]string{
"whale",
"whale2",
"wolf",
+ "zebra",
},
"annoyed": []string{
"angry",
@@ -7697,6 +7973,9 @@ var keywordLookup = map[string][]string{
"straight_ruler",
"triangular_ruler",
},
+ "ariel": []string{
+ "mermaid",
+ },
"arm": []string{
"muscle",
},
@@ -7754,6 +8033,9 @@ var keywordLookup = map[string][]string{
"taurus",
"virgo",
},
+ "astronomy": []string{
+ "telescope",
+ },
"at": []string{
"austria",
},
@@ -7799,6 +8081,7 @@ var keywordLookup = map[string][]string{
"azerbaijan",
},
"baby": []string{
+ "breastfeeding",
"hatched_chick",
"hatching_chick",
"pregnant_woman",
@@ -8123,6 +8406,9 @@ var keywordLookup = map[string][]string{
"barthélemy": []string{
"st_barthelemy",
},
+ "baseball": []string{
+ "billed_hat",
+ },
"bath": []string{
"hotsprings",
},
@@ -8197,6 +8483,9 @@ var keywordLookup = map[string][]string{
"tumbler_glass",
"wine_glass",
},
+ "bewhiskered": []string{
+ "bearded_person",
+ },
"bg": []string{
"bulgaria",
},
@@ -8280,11 +8569,9 @@ var keywordLookup = map[string][]string{
"blood": []string{
"syringe",
},
- "bloody": []string{
- "goberserk",
- },
"blown": []string{
"boom",
+ "exploding_head",
},
"blue": []string{
"cyclone",
@@ -8325,6 +8612,7 @@ var keywordLookup = map[string][]string{
"cool",
"customs",
"eight",
+ "eject_button",
"fast_forward",
"five",
"four",
@@ -8458,6 +8746,7 @@ var keywordLookup = map[string][]string{
},
"box": []string{
"bento",
+ "coffin",
"package",
},
"boy": []string{
@@ -8484,6 +8773,9 @@ var keywordLookup = map[string][]string{
"br": []string{
"brazil",
},
+ "brachiosaurus": []string{
+ "sauropod",
+ },
"branch": []string{
"tanabata_tree",
},
@@ -8496,12 +8788,15 @@ var keywordLookup = map[string][]string{
"bread": []string{
"baguette_bread",
"croissant",
+ "pretzel",
+ "sandwich",
},
"break": []string{
"broken_heart",
},
"breakfast": []string{
"bacon",
+ "bowl_with_spoon",
"bread",
"egg",
"fried_egg",
@@ -8538,6 +8833,9 @@ var keywordLookup = map[string][]string{
"bromance": []string{
"two_men_holding_hands",
},
+ "brontosaurus": []string{
+ "sauropod",
+ },
"brown": []string{
"horse",
},
@@ -8591,6 +8889,9 @@ var keywordLookup = map[string][]string{
"city_sunset",
"houses",
},
+ "bullseye": []string{
+ "dart",
+ },
"bunny": []string{
"dancing_men",
"dancing_women",
@@ -8704,6 +9005,7 @@ var keywordLookup = map[string][]string{
"lollipop",
},
"cap": []string{
+ "billed_hat",
"mortar_board",
},
"caption": []string{
@@ -8725,6 +9027,9 @@ var keywordLookup = map[string][]string{
"hearts",
"spades",
},
+ "career": []string{
+ "briefcase",
+ },
"carnival": []string{
"carousel_horse",
"circus_tent",
@@ -8754,6 +9059,9 @@ var keywordLookup = map[string][]string{
"casino": []string{
"slot_machine",
},
+ "casket": []string{
+ "coffin",
+ },
"casserole": []string{
"shallow_pan_of_food",
},
@@ -8813,6 +9121,9 @@ var keywordLookup = map[string][]string{
"century": []string{
"100",
},
+ "cereal": []string{
+ "bowl_with_spoon",
+ },
"ceremony": []string{
"trophy",
},
@@ -8822,6 +9133,9 @@ var keywordLookup = map[string][]string{
"chadder": []string{
"cheese",
},
+ "champagne": []string{
+ "clinking_glasses",
+ },
"characters": []string{
"symbols",
},
@@ -8852,6 +9166,10 @@ var keywordLookup = map[string][]string{
"man_scientist",
"woman_scientist",
},
+ "chemistry": []string{
+ "alembic",
+ "atom_symbol",
+ },
"chicken": []string{
"baby_chick",
"egg",
@@ -8935,9 +9253,15 @@ var keywordLookup = map[string][]string{
"chips": []string{
"fries",
},
+ "chirp": []string{
+ "grasshopper",
+ },
"chocolate": []string{
"cookie",
},
+ "chop": []string{
+ "steak",
+ },
"chopsticks": []string{
"ramen",
},
@@ -8979,7 +9303,6 @@ var keywordLookup = map[string][]string{
},
"circus": []string{
"balloon",
- "bowtie",
"confetti_ball",
"crystal_ball",
"elephant",
@@ -8997,7 +9320,6 @@ var keywordLookup = map[string][]string{
"notebook_with_decorative_cover",
},
"classy": []string{
- "bowtie",
"man",
"tophat",
},
@@ -9024,6 +9346,9 @@ var keywordLookup = map[string][]string{
},
"clothes": []string{
"dress",
+ "gloves",
+ "scarf",
+ "socks",
},
"cloud": []string{
"cyclone",
@@ -9110,7 +9435,9 @@ var keywordLookup = map[string][]string{
},
"computer": []string{
"keyboard",
+ "man_technologist",
"robot",
+ "woman_technologist",
},
"computing": []string{
"desktop_computer",
@@ -9458,11 +9785,15 @@ var keywordLookup = map[string][]string{
"cow": []string{
"milk_glass",
"ox",
+ "steak",
"water_buffalo",
},
"cowgirl": []string{
"cowboy_hat_face",
},
+ "crazy": []string{
+ "zany",
+ },
"create": []string{
"hammer",
"hammer_and_pick",
@@ -9481,6 +9812,9 @@ var keywordLookup = map[string][]string{
"japanese_ogre",
"skull",
},
+ "cricket": []string{
+ "grasshopper",
+ },
"croak": []string{
"frog",
},
@@ -9521,6 +9855,9 @@ var keywordLookup = map[string][]string{
"cupertino": []string{
"motorway",
},
+ "cupped": []string{
+ "palms_up",
+ },
"curaçao": []string{
"curacao",
},
@@ -9531,22 +9868,21 @@ var keywordLookup = map[string][]string{
"pound",
"yen",
},
+ "cursing": []string{
+ "symbols_over_mouth",
+ },
"curtain": []string{
"cinema",
},
+ "cussing": []string{
+ "symbols_over_mouth",
+ },
"custom": []string{
"passport_control",
},
- "custom_": []string{
- "hurtrealbad",
- "neckbeard",
- "octocat",
- "shipit",
- "suspect",
- "trollface",
- },
"cut": []string{
"scissors",
+ "steak",
"u5272",
},
"cutlery": []string{
@@ -9631,6 +9967,8 @@ var keywordLookup = map[string][]string{
"death": []string{
"coffin",
"funeral_urn",
+ "skull",
+ "skull_and_crossbones",
},
"december": []string{
"christmas_tree",
@@ -9686,10 +10024,6 @@ var keywordLookup = map[string][]string{
},
"despise": []string{
"rage",
- "rage1",
- "rage2",
- "rage3",
- "rage4",
},
"dessert": []string{
"birthday",
@@ -9702,6 +10036,7 @@ var keywordLookup = map[string][]string{
"doughnut",
"ice_cream",
"icecream",
+ "pie",
"shaved_ice",
},
"destination": []string{
@@ -9712,7 +10047,6 @@ var keywordLookup = map[string][]string{
"mag",
"mag_right",
"male_detective",
- "shipit",
},
"developer": []string{
"man_technologist",
@@ -9758,6 +10092,13 @@ var keywordLookup = map[string][]string{
"dinner": []string{
"plate_with_cutlery",
},
+ "dinosaur": []string{
+ "sauropod",
+ "t-rex",
+ },
+ "diplodocus": []string{
+ "sauropod",
+ },
"direction": []string{
"arrow_backward",
"arrow_double_down",
@@ -9794,12 +10135,16 @@ var keywordLookup = map[string][]string{
"disappointed": []string{
"slightly_frowning_face",
},
+ "disapproval": []string{
+ "raised_eyebrow",
+ },
"disaster": []string{
"ocean",
"volcano",
},
"disbelief": []string{
"man_facepalming",
+ "raised_eyebrow",
"woman_facepalming",
},
"disc": []string{
@@ -9830,6 +10175,9 @@ var keywordLookup = map[string][]string{
"distilling": []string{
"alembic",
},
+ "distrust": []string{
+ "raised_eyebrow",
+ },
"divide": []string{
"heavy_division_sign",
"u5272",
@@ -9891,13 +10239,6 @@ var keywordLookup = map[string][]string{
"donut": []string{
"doughnut",
},
- "doom": []string{
- "feelsgood",
- "finnadie",
- "goberserk",
- "godmode",
- "hurtrealbad",
- },
"door": []string{
"key",
"old_key",
@@ -9920,6 +10261,10 @@ var keywordLookup = map[string][]string{
"downtown": []string{
"night_with_stars",
},
+ "dracula": []string{
+ "man_vampire",
+ "man_zombie",
+ },
"drama": []string{
"performing_arts",
},
@@ -9946,6 +10291,7 @@ var keywordLookup = map[string][]string{
"champagne",
"clinking_glasses",
"cocktail",
+ "cup_with_straw",
"milk_glass",
"non-potable_water",
"sake",
@@ -9986,6 +10332,9 @@ var keywordLookup = map[string][]string{
"tumbler_glass",
"wine_glass",
},
+ "dubious": []string{
+ "unamused",
+ },
"dusk": []string{
"clock6",
},
@@ -10063,6 +10412,7 @@ var keywordLookup = map[string][]string{
"el_salvador",
},
"elder": []string{
+ "older_adult",
"older_man",
"older_woman",
},
@@ -10100,6 +10450,9 @@ var keywordLookup = map[string][]string{
"emirates": []string{
"united_arab_emirates",
},
+ "empanada": []string{
+ "dumpling",
+ },
"empty": []string{
"u7a7a",
},
@@ -10126,6 +10479,7 @@ var keywordLookup = map[string][]string{
"uk",
},
"english": []string{
+ "england",
"uk",
},
"enter": []string{
@@ -10201,6 +10555,7 @@ var keywordLookup = map[string][]string{
"evil": []string{
"black_heart",
"japanese_goblin",
+ "skull_and_crossbones",
"snake",
},
"evil_eye": []string{
@@ -10234,6 +10589,9 @@ var keywordLookup = map[string][]string{
"alembic",
"microscope",
},
+ "expletive": []string{
+ "symbols_over_mouth",
+ },
"explode": []string{
"bomb",
"boom",
@@ -10245,12 +10603,19 @@ var keywordLookup = map[string][]string{
"express": []string{
"articulated_lorry",
},
+ "extinct": []string{
+ "sauropod",
+ "t-rex",
+ },
"eye": []string{
"wink",
},
"eyeroll": []string{
"roll_eyes",
},
+ "eyes": []string{
+ "star_struck",
+ },
"eyesight": []string{
"eyeglasses",
},
@@ -10262,7 +10627,6 @@ var keywordLookup = map[string][]string{
"anguished",
"astonished",
"blush",
- "bowtie",
"clown_face",
"cold_sweat",
"confounded",
@@ -10274,6 +10638,7 @@ var keywordLookup = map[string][]string{
"disappointed_relieved",
"drooling_face",
"ear",
+ "exploding_head",
"expressionless",
"eye",
"fearful",
@@ -10284,6 +10649,7 @@ var keywordLookup = map[string][]string{
"grimacing",
"grin",
"grinning",
+ "hand_over_mouth",
"heart_eyes",
"hugs",
"hushed",
@@ -10298,18 +10664,20 @@ var keywordLookup = map[string][]string{
"lying_face",
"mask",
"money_mouth_face",
+ "monocle",
"nauseated_face",
- "neckbeard",
"nerd_face",
"no_mouth",
"open_mouth",
"pensive",
"persevere",
+ "raised_eyebrow",
"relaxed",
"relieved",
"rofl",
"roll_eyes",
"scream",
+ "shushing",
"sleeping",
"sleepy",
"slightly_frowning_face",
@@ -10319,19 +10687,23 @@ var keywordLookup = map[string][]string{
"smirk",
"sneezing_face",
"sob",
+ "star_struck",
"stuck_out_tongue",
"stuck_out_tongue_closed_eyes",
"stuck_out_tongue_winking_eye",
"sunglasses",
"sweat",
"sweat_smile",
+ "symbols_over_mouth",
"thinking",
"triumph",
"upside_down_face",
+ "vomiting",
"weary",
"wink",
"worried",
"yum",
+ "zany",
"zipper_mouth_face",
},
"fail": []string{
@@ -10375,7 +10747,6 @@ var keywordLookup = map[string][]string{
"fashion": []string{
"bikini",
"boot",
- "bowtie",
"dress",
"eyeglasses",
"handbag",
@@ -10441,6 +10812,7 @@ var keywordLookup = map[string][]string{
"bikini",
"blonde_woman",
"bowing_woman",
+ "climbing_woman",
"construction_worker_woman",
"dancer",
"dancing_women",
@@ -10454,6 +10826,7 @@ var keywordLookup = map[string][]string{
"kimono",
"lipstick",
"massage_woman",
+ "mermaid",
"mountain_biking_woman",
"mrs_claus",
"no_good_woman",
@@ -10465,6 +10838,7 @@ var keywordLookup = map[string][]string{
"raising_hand_woman",
"rowing_woman",
"running_woman",
+ "sorceress",
"surfing_woman",
"swimming_woman",
"tipping_hand_woman",
@@ -10472,9 +10846,17 @@ var keywordLookup = map[string][]string{
"walking_woman",
"weight_lifting_woman",
"woman",
+ "woman_elf",
"woman_facepalming",
+ "woman_fairy",
+ "woman_genie",
+ "woman_in_lotus_position",
+ "woman_in_steamy_room",
"woman_shrugging",
+ "woman_vampire",
+ "woman_with_headscarf",
"woman_with_turban",
+ "woman_zombie",
"womans_clothes",
"womans_hat",
"womens",
@@ -10524,6 +10906,7 @@ var keywordLookup = map[string][]string{
"fingers": []string{
"fist",
"fu",
+ "love_you",
"metal",
"ok_hand",
"open_hands",
@@ -10649,6 +11032,7 @@ var keywordLookup = map[string][]string{
"ecuador",
"egypt",
"el_salvador",
+ "england",
"equatorial_guinea",
"eritrea",
"es",
@@ -10772,6 +11156,7 @@ var keywordLookup = map[string][]string{
"san_marino",
"sao_tome_principe",
"saudi_arabia",
+ "scotland",
"senegal",
"serbia",
"seychelles",
@@ -10824,6 +11209,7 @@ var keywordLookup = map[string][]string{
"vatican_city",
"venezuela",
"vietnam",
+ "wales",
"wallis_futuna",
"western_sahara",
"yemen",
@@ -10899,14 +11285,19 @@ var keywordLookup = map[string][]string{
"bento",
"birthday",
"blowfish",
+ "bowl_with_spoon",
"bread",
+ "broccoli",
"burrito",
"cake",
+ "canned_food",
"carrot",
"cheese",
"cherries",
"chestnut",
"chocolate_bar",
+ "chopsticks",
+ "coconut",
"cookie",
"corn",
"croissant",
@@ -10915,11 +11306,13 @@ var keywordLookup = map[string][]string{
"custard",
"dango",
"doughnut",
+ "dumpling",
"egg",
"eggplant",
"fish",
"fish_cake",
"fishing_pole_and_fish",
+ "fortune_cookie",
"fried_egg",
"fried_shrimp",
"grapes",
@@ -10937,24 +11330,29 @@ var keywordLookup = map[string][]string{
"peach",
"peanuts",
"pear",
+ "pie",
"pineapple",
"pizza",
"plate_with_cutlery",
"popcorn",
"potato",
"poultry_leg",
+ "pretzel",
"ramen",
"rice",
"rice_ball",
"rice_cracker",
+ "sandwich",
"shallow_pan_of_food",
"spaghetti",
+ "steak",
"stew",
"strawberry",
"stuffed_flatbread",
"sushi",
"sweet_potato",
"taco",
+ "takeout_box",
"tangerine",
"tomato",
"watermelon",
@@ -10973,7 +11371,6 @@ var keywordLookup = map[string][]string{
"u7981",
},
"formal": []string{
- "bowtie",
"necktie",
},
"formula": []string{
@@ -11028,7 +11425,9 @@ var keywordLookup = map[string][]string{
"apple",
"avocado",
"banana",
+ "broccoli",
"cherries",
+ "coconut",
"cucumber",
"grapes",
"green_apple",
@@ -11064,6 +11463,9 @@ var keywordLookup = map[string][]string{
"man_dancing",
"roller_coaster",
},
+ "funeral": []string{
+ "coffin",
+ },
"funny": []string{
"smile",
"smiley",
@@ -11144,6 +11546,11 @@ var keywordLookup = map[string][]string{
"restroom",
"womens",
},
+ "gender-neutral": []string{
+ "adult",
+ "child",
+ "older_adult",
+ },
"gentleman": []string{
"tophat",
},
@@ -11155,6 +11562,8 @@ var keywordLookup = map[string][]string{
},
"gesture": []string{
"call_me_hand",
+ "love_you",
+ "palms_up",
"raised_hands",
"wave",
},
@@ -11200,9 +11609,6 @@ var keywordLookup = map[string][]string{
"dancing_women",
"woman",
},
- "github": []string{
- "octocat",
- },
"give up": []string{
"white_flag",
},
@@ -11256,6 +11662,9 @@ var keywordLookup = map[string][]string{
"goodbye": []string{
"wave",
},
+ "goofy": []string{
+ "zany",
+ },
"gp": []string{
"guadeloupe",
},
@@ -11322,6 +11731,9 @@ var keywordLookup = map[string][]string{
"grin": []string{
"grinning",
},
+ "grinning": []string{
+ "star_struck",
+ },
"groceries": []string{
"convenience_store",
},
@@ -11400,6 +11812,7 @@ var keywordLookup = map[string][]string{
"fist_left",
"fist_right",
"fu",
+ "love_you",
"metal",
"muscle",
"point_down",
@@ -11414,7 +11827,9 @@ var keywordLookup = map[string][]string{
"hands": []string{
"call_me_hand",
"clap",
+ "gloves",
"open_hands",
+ "palms_up",
"raised_hands",
"wave",
},
@@ -11455,10 +11870,6 @@ var keywordLookup = map[string][]string{
},
"hate": []string{
"rage",
- "rage1",
- "rage2",
- "rage3",
- "rage4",
},
"have": []string{
"u6709",
@@ -11541,6 +11952,9 @@ var keywordLookup = map[string][]string{
"highway": []string{
"motorway",
},
+ "hijab": []string{
+ "woman_with_headscarf",
+ },
"hinduism": []string{
"man_with_turban",
"om",
@@ -11575,6 +11989,8 @@ var keywordLookup = map[string][]string{
},
"hobby": []string{
"8ball",
+ "climbing_man",
+ "climbing_woman",
"fishing_pole_and_fish",
"rowing_man",
"rowing_woman",
@@ -11740,6 +12156,7 @@ var keywordLookup = map[string][]string{
"mountain_biking_woman",
"ok_man",
"ok_woman",
+ "older_adult",
"older_man",
"older_woman",
"put_litter_in_its_place",
@@ -11776,9 +12193,11 @@ var keywordLookup = map[string][]string{
"hundred": []string{
"100",
},
+ "hurricane": []string{
+ "cyclone",
+ },
"hurt": []string{
"face_with_head_bandage",
- "goberserk",
},
"i18n": []string{
"globe_with_meridians",
@@ -11873,7 +12292,6 @@ var keywordLookup = map[string][]string{
},
"injured": []string{
"face_with_head_bandage",
- "hurtrealbad",
},
"ink": []string{
"printer",
@@ -11892,9 +12310,6 @@ var keywordLookup = map[string][]string{
"honeybee",
"spider_web",
},
- "inspector": []string{
- "shipit",
- },
"instructor": []string{
"man_teacher",
"woman_teacher",
@@ -11907,6 +12322,9 @@ var keywordLookup = map[string][]string{
"saxophone",
"violin",
},
+ "intelligent": []string{
+ "brain",
+ },
"international": []string{
"earth_africa",
"earth_americas",
@@ -11916,7 +12334,6 @@ var keywordLookup = map[string][]string{
"internet": []string{
"globe_with_meridians",
"signal_strength",
- "trollface",
},
"interstate": []string{
"motorway",
@@ -11990,6 +12407,9 @@ var keywordLookup = map[string][]string{
"ivory": []string{
"cote_divoire",
},
+ "jacket": []string{
+ "coat",
+ },
"jainism": []string{
"om",
"wheel_of_dharma",
@@ -12066,6 +12486,9 @@ var keywordLookup = map[string][]string{
"jo": []string{
"jordan",
},
+ "job": []string{
+ "briefcase",
+ },
"join": []string{
"u5408",
},
@@ -12099,6 +12522,9 @@ var keywordLookup = map[string][]string{
"man_judge",
"woman_judge",
},
+ "kamaboko": []string{
+ "fish_cake",
+ },
"kanji": []string{
"accept",
"congratulations",
@@ -12234,6 +12660,9 @@ var keywordLookup = map[string][]string{
"ladybug": []string{
"beetle",
},
+ "lambchop": []string{
+ "steak",
+ },
"landing": []string{
"flight_departure",
},
@@ -12245,6 +12674,8 @@ var keywordLookup = map[string][]string{
},
"laptop": []string{
"computer",
+ "man_technologist",
+ "woman_technologist",
},
"late": []string{
"clock1",
@@ -12322,6 +12753,9 @@ var keywordLookup = map[string][]string{
"arrow_backward",
"point_left",
},
+ "leftovers": []string{
+ "takeout_box",
+ },
"legal": []string{
"briefcase",
"copyright",
@@ -12421,6 +12855,7 @@ var keywordLookup = map[string][]string{
"kissing_heart",
"love_hotel",
"love_letter",
+ "orange_heart",
"purple_heart",
"revolving_hearts",
"smile",
@@ -12530,6 +12965,7 @@ var keywordLookup = map[string][]string{
"kissing",
"kissing_closed_eyes",
"kissing_heart",
+ "orange_heart",
"purple_heart",
"revolving_hearts",
"rose",
@@ -12569,8 +13005,12 @@ var keywordLookup = map[string][]string{
"crossed_fingers",
"four_leaf_clover",
},
+ "luge": []string{
+ "sled",
+ },
"lunch": []string{
"plate_with_cutlery",
+ "sandwich",
},
"lv": []string{
"latvia",
@@ -12599,19 +13039,16 @@ var keywordLookup = map[string][]string{
"mad": []string{
"anger",
"angry",
- "hurtrealbad",
"rage",
- "rage1",
- "rage2",
- "rage3",
- "rage4",
"right_anger_bubble",
- "suspect",
+ },
+ "mage": []string{
+ "sorceress",
+ "wizard",
},
"magic": []string{
"8ball",
"black_joker",
- "bowtie",
"clubs",
"crescent_moon",
"crystal_ball",
@@ -12636,19 +13073,28 @@ var keywordLookup = map[string][]string{
"blonde_man",
"bowing_man",
"boy",
+ "climbing_man",
"construction_worker_man",
"dancing_men",
"frowning_man",
"guardsman",
"haircut_man",
"man_dancing",
+ "man_elf",
"man_facepalming",
+ "man_fairy",
+ "man_genie",
+ "man_in_lotus_position",
+ "man_in_steamy_room",
"man_shrugging",
+ "man_vampire",
"man_with_gua_pi_mao",
"man_with_turban",
+ "man_zombie",
"mans_shoe",
"massage_man",
"mens",
+ "merman",
"no_good_man",
"ok_man",
"older_man",
@@ -12657,6 +13103,7 @@ var keywordLookup = map[string][]string{
"raising_hand_man",
"santa",
"tipping_hand_man",
+ "wizard",
},
"mall": []string{
"department_store",
@@ -12672,17 +13119,23 @@ var keywordLookup = map[string][]string{
"blonde_man",
"bowing_man",
"boy",
+ "climbing_man",
"frowning_man",
"haircut_man",
"isle_of_man",
"man_artist",
"man_astronaut",
"man_cook",
+ "man_elf",
"man_facepalming",
"man_factory_worker",
+ "man_fairy",
"man_farmer",
"man_firefighter",
+ "man_genie",
"man_health_worker",
+ "man_in_lotus_position",
+ "man_in_steamy_room",
"man_judge",
"man_mechanic",
"man_office_worker",
@@ -12693,7 +13146,10 @@ var keywordLookup = map[string][]string{
"man_student",
"man_teacher",
"man_technologist",
+ "man_vampire",
+ "man_zombie",
"massage_man",
+ "merman",
"no_good_man",
"ok_man",
"policeman",
@@ -12703,6 +13159,7 @@ var keywordLookup = map[string][]string{
"running_man",
"santa",
"tipping_hand_man",
+ "wizard",
},
"manager": []string{
"man_office_worker",
@@ -12711,6 +13168,9 @@ var keywordLookup = map[string][]string{
"manicure": []string{
"nail_care",
},
+ "mantilla": []string{
+ "woman_with_headscarf",
+ },
"map": []string{
"round_pushpin",
},
@@ -12776,6 +13236,7 @@ var keywordLookup = map[string][]string{
"dango",
"hamburger",
"poultry_leg",
+ "steak",
"stew",
},
"mecca": []string{
@@ -12786,13 +13247,14 @@ var keywordLookup = map[string][]string{
"pill",
"syringe",
},
+ "meditation": []string{
+ "man_in_lotus_position",
+ "woman_in_lotus_position",
+ },
"meh": []string{
"expressionless",
"neutral_face",
},
- "meme": []string{
- "trollface",
- },
"memo": []string{
"spiral_notepad",
},
@@ -12805,6 +13267,9 @@ var keywordLookup = map[string][]string{
"cat",
"cat2",
},
+ "merwoman": []string{
+ "mermaid",
+ },
"message": []string{
"left_speech_bubble",
"speech_balloon",
@@ -12842,6 +13307,13 @@ var keywordLookup = map[string][]string{
"minaret": []string{
"mosque",
},
+ "mind": []string{
+ "exploding_head",
+ },
+ "mindfulness": []string{
+ "man_in_lotus_position",
+ "woman_in_lotus_position",
+ },
"minor": []string{
"underage",
},
@@ -13048,6 +13520,9 @@ var keywordLookup = map[string][]string{
"nap": []string{
"sleepy",
},
+ "narutomaki": []string{
+ "fish_cake",
+ },
"nation": []string{
"afghanistan",
"aland_islands",
@@ -13317,6 +13792,7 @@ var keywordLookup = map[string][]string{
"cherry_blossom",
"chicken",
"chipmunk",
+ "coconut",
"cow",
"cow2",
"crocodile",
@@ -13342,11 +13818,13 @@ var keywordLookup = map[string][]string{
"frog",
"full_moon",
"full_moon_with_face",
+ "giraffe",
"goat",
"gorilla",
"green_apple",
"hamster",
"hear_no_evil",
+ "hedgehog",
"honeybee",
"horse",
"house_with_garden",
@@ -13389,6 +13867,7 @@ var keywordLookup = map[string][]string{
"ram",
"rhinoceros",
"rooster",
+ "sauropod",
"see_no_evil",
"seedling",
"shamrock",
@@ -13404,6 +13883,7 @@ var keywordLookup = map[string][]string{
"sunflower",
"sunny",
"sweet_potato",
+ "t-rex",
"tanabata_tree",
"tangerine",
"tiger",
@@ -13423,10 +13903,14 @@ var keywordLookup = map[string][]string{
"wilted_flower",
"wind_chime",
"wolf",
+ "zebra",
},
"ne": []string{
"niger",
},
+ "neck": []string{
+ "scarf",
+ },
"needle": []string{
"syringe",
},
@@ -13435,7 +13919,6 @@ var keywordLookup = map[string][]string{
},
"nerdy": []string{
"eyeglasses",
- "neckbeard",
"nerd_face",
},
"nervous": []string{
@@ -13592,9 +14075,15 @@ var keywordLookup = map[string][]string{
"syringe",
"woman_health_worker",
},
+ "nursing": []string{
+ "breastfeeding",
+ },
"nut": []string{
"peanuts",
},
+ "oatmeal": []string{
+ "bowl_with_spoon",
+ },
"obtain": []string{
"ideograph_advantage",
},
@@ -13612,9 +14101,6 @@ var keywordLookup = map[string][]string{
"whale",
"whale2",
},
- "octopus": []string{
- "octocat",
- },
"office": []string{
"file_folder",
"page_facing_up",
@@ -13638,7 +14124,7 @@ var keywordLookup = map[string][]string{
"white_check_mark",
},
"okay": []string{
- "pensive",
+ "ok_hand",
},
"old": []string{
"older_man",
@@ -13646,13 +14132,9 @@ var keywordLookup = map[string][]string{
"radio_button",
},
"oldschool": []string{
- "feelsgood",
- "finnadie",
"floppy_disk",
- "godmode",
"hourglass",
"hourglass_flowing_sand",
- "hurtrealbad",
"pager",
"tv",
"vhs",
@@ -13767,6 +14249,7 @@ var keywordLookup = map[string][]string{
"palestinian_territories",
},
"palm": []string{
+ "coconut",
"raised_hand",
"raised_hand_with_fingers_splayed",
"wave",
@@ -13842,6 +14325,9 @@ var keywordLookup = map[string][]string{
"lock",
"old_key",
},
+ "pastry": []string{
+ "pie",
+ },
"paul": []string{
"alien",
},
@@ -13909,6 +14395,8 @@ var keywordLookup = map[string][]string{
"ok_hand",
},
"person": []string{
+ "adult",
+ "bearded_person",
"blonde_man",
"blonde_woman",
"bust_in_silhouette",
@@ -13984,6 +14472,9 @@ var keywordLookup = map[string][]string{
"man_scientist",
"woman_scientist",
},
+ "physics": []string{
+ "atom_symbol",
+ },
"piano": []string{
"musical_keyboard",
},
@@ -13993,6 +14484,9 @@ var keywordLookup = map[string][]string{
"picnic": []string{
"watermelon",
},
+ "pierogi": []string{
+ "dumpling",
+ },
"pierre": []string{
"st_pierre_miquelon",
},
@@ -14009,6 +14503,7 @@ var keywordLookup = map[string][]string{
"ping_pong",
},
"pink": []string{
+ "fish_cake",
"heartbeat",
"heartpulse",
"ok_woman",
@@ -14022,6 +14517,7 @@ var keywordLookup = map[string][]string{
},
"pirate": []string{
"black_flag",
+ "skull_and_crossbones",
},
"pistol": []string{
"gun",
@@ -14161,9 +14657,18 @@ var keywordLookup = map[string][]string{
"pork": []string{
"bacon",
},
+ "porkchop": []string{
+ "steak",
+ },
+ "porridge": []string{
+ "bowl_with_spoon",
+ },
"postal": []string{
"email",
},
+ "potsticker": []string{
+ "dumpling",
+ },
"potty": []string{
"toilet",
},
@@ -14181,6 +14686,7 @@ var keywordLookup = map[string][]string{
"stuck_out_tongue_winking_eye",
},
"prayer": []string{
+ "palms_up",
"place_of_worship",
},
"prc": []string{
@@ -14225,6 +14731,9 @@ var keywordLookup = map[string][]string{
"problem": []string{
"warning",
},
+ "profanity": []string{
+ "symbols_over_mouth",
+ },
"professor": []string{
"man_teacher",
"woman_teacher",
@@ -14244,6 +14753,9 @@ var keywordLookup = map[string][]string{
"prohibited": []string{
"no_bicycles",
},
+ "prophecy": []string{
+ "fortune_cookie",
+ },
"propose": []string{
"ring",
},
@@ -14330,6 +14842,7 @@ var keywordLookup = map[string][]string{
"mobile_phone_off",
"mute",
"no_bell",
+ "shushing",
},
"quiz": []string{
"100",
@@ -14348,9 +14861,6 @@ var keywordLookup = map[string][]string{
"radio": []string{
"satellite",
},
- "rage": []string{
- "goberserk",
- },
"rain": []string{
"closed_umbrella",
},
@@ -14363,6 +14873,9 @@ var keywordLookup = map[string][]string{
"raised": []string{
"raised_back_of_hand",
},
+ "ramen": []string{
+ "fish_cake",
+ },
"rancher": []string{
"man_farmer",
"woman_farmer",
@@ -14531,6 +15044,8 @@ var keywordLookup = map[string][]string{
"tiger2",
},
"rock": []string{
+ "climbing_man",
+ "climbing_woman",
"moyai",
},
"rock_on": []string{
@@ -14614,6 +15129,10 @@ var keywordLookup = map[string][]string{
"sweat",
"weary",
},
+ "safari": []string{
+ "giraffe",
+ "zebra",
+ },
"sahara": []string{
"western_sahara",
},
@@ -14681,6 +15200,10 @@ var keywordLookup = map[string][]string{
"satisfied": []string{
"laughing",
},
+ "sauna": []string{
+ "man_in_steamy_room",
+ "woman_in_steamy_room",
+ },
"save": []string{
"bookmark",
"bookmark_tabs",
@@ -14709,6 +15232,9 @@ var keywordLookup = map[string][]string{
"japanese_ogre",
"skull_and_crossbones",
},
+ "scepticism": []string{
+ "raised_eyebrow",
+ },
"schedule": []string{
"calendar",
"clock1",
@@ -14749,6 +15275,7 @@ var keywordLookup = map[string][]string{
"alembic",
"atom_symbol",
"microscope",
+ "telescope",
},
"score": []string{
"100",
@@ -14762,6 +15289,9 @@ var keywordLookup = map[string][]string{
"scotch": []string{
"tumbler_glass",
},
+ "scottish": []string{
+ "scotland",
+ },
"scout": []string{
"fleur_de_lis",
},
@@ -14834,9 +15364,14 @@ var keywordLookup = map[string][]string{
"eyes",
},
"senior": []string{
+ "older_adult",
"older_man",
"older_woman",
},
+ "serenity": []string{
+ "man_in_lotus_position",
+ "woman_in_lotus_position",
+ },
"serious": []string{
"unamused",
},
@@ -14881,12 +15416,12 @@ var keywordLookup = map[string][]string{
"shell": []string{
"snail",
},
- "sherlock": []string{
- "shipit",
- },
"shh": []string{
"hushed",
},
+ "shhh": []string{
+ "shushing",
+ },
"shield": []string{
"beginner",
},
@@ -14921,6 +15456,12 @@ var keywordLookup = map[string][]string{
"shitface": []string{
"poop",
},
+ "shock": []string{
+ "hand_over_mouth",
+ },
+ "shocked": []string{
+ "exploding_head",
+ },
"shoes": []string{
"athletic_shoe",
"boot",
@@ -14974,6 +15515,10 @@ var keywordLookup = map[string][]string{
"persevere",
"sneezing_face",
"tired_face",
+ "vomiting",
+ },
+ "side_eye": []string{
+ "unamused",
},
"sideways": []string{
"left_right_arrow",
@@ -15046,6 +15591,9 @@ var keywordLookup = map[string][]string{
"skeleton": []string{
"skull",
},
+ "skeptical": []string{
+ "unamused",
+ },
"sketch": []string{
"straight_ruler",
"triangular_ruler",
@@ -15088,10 +15636,16 @@ var keywordLookup = map[string][]string{
"weary",
"zzz",
},
+ "sleigh": []string{
+ "sled",
+ },
"slow": []string{
"snail",
"turtle",
},
+ "smart": []string{
+ "brain",
+ },
"smell": []string{
"no_smoking",
"nose",
@@ -15106,6 +15660,7 @@ var keywordLookup = map[string][]string{
"smiley",
"smiley_cat",
"smirk",
+ "star_struck",
"stuck_out_tongue",
"stuck_out_tongue_closed_eyes",
"stuck_out_tongue_winking_eye",
@@ -15139,6 +15694,9 @@ var keywordLookup = map[string][]string{
"lollipop",
"popcorn",
},
+ "snare": []string{
+ "drum",
+ },
"sneakers": []string{
"athletic_shoe",
},
@@ -15155,6 +15713,9 @@ var keywordLookup = map[string][]string{
"so": []string{
"somalia",
},
+ "soda": []string{
+ "cup_with_straw",
+ },
"software": []string{
"man_technologist",
"woman_technologist",
@@ -15165,6 +15726,9 @@ var keywordLookup = map[string][]string{
"solong": []string{
"wave",
},
+ "sorcerer": []string{
+ "wizard",
+ },
"sorry": []string{
"broken_heart",
},
@@ -15180,6 +15744,7 @@ var keywordLookup = map[string][]string{
"speaker",
},
"soup": []string{
+ "canned_food",
"stew",
},
"south": []string{
@@ -15197,6 +15762,10 @@ var keywordLookup = map[string][]string{
"southwest": []string{
"arrow_lower_left",
},
+ "spa": []string{
+ "man_in_steamy_room",
+ "woman_in_steamy_room",
+ },
"space": []string{
"comet",
"first_quarter_moon",
@@ -15254,6 +15823,9 @@ var keywordLookup = map[string][]string{
"spin": []string{
"cyclone",
},
+ "spiny": []string{
+ "hedgehog",
+ },
"spiral": []string{
"cyclone",
},
@@ -15277,7 +15849,10 @@ var keywordLookup = map[string][]string{
"bow_and_arrow",
"bowling",
"boxing_glove",
+ "climbing_man",
+ "climbing_woman",
"cricket",
+ "curling_stone",
"field_hockey",
"football",
"goal_net",
@@ -15317,6 +15892,9 @@ var keywordLookup = map[string][]string{
"woman_playing_water_polo",
"women_wrestling",
},
+ "spots": []string{
+ "giraffe",
+ },
"spring": []string{
"bird",
"bouquet",
@@ -15349,7 +15927,6 @@ var keywordLookup = map[string][]string{
"squirrel": []string{
"chestnut",
"chipmunk",
- "shipit",
},
"sr": []string{
"suriname",
@@ -15383,6 +15960,9 @@ var keywordLookup = map[string][]string{
"stare": []string{
"eye",
},
+ "starry": []string{
+ "star_struck",
+ },
"stars": []string{
"milky_way",
"sparkle",
@@ -15425,6 +16005,10 @@ var keywordLookup = map[string][]string{
"chart_with_upwards_trend",
"part_alternation_mark",
},
+ "steamroom": []string{
+ "man_in_steamy_room",
+ "woman_in_steamy_room",
+ },
"steps": []string{
"walking_man",
"walking_woman",
@@ -15435,6 +16019,9 @@ var keywordLookup = map[string][]string{
"stiletto": []string{
"high_heel",
},
+ "stockings": []string{
+ "socks",
+ },
"stone": []string{
"white_large_square",
"white_medium_small_square",
@@ -15449,6 +16036,9 @@ var keywordLookup = map[string][]string{
"straight face": []string{
"unamused",
},
+ "stripes": []string{
+ "zebra",
+ },
"strong": []string{
"muscle",
},
@@ -15471,6 +16061,9 @@ var keywordLookup = map[string][]string{
"stuffed": []string{
"stuffed_flatbread",
},
+ "stuffy": []string{
+ "monocle",
+ },
"stunned": []string{
"anguished",
},
@@ -15484,7 +16077,6 @@ var keywordLookup = map[string][]string{
"chart_with_upwards_trend",
},
"suit": []string{
- "bowtie",
"business_suit_levitating",
},
"suits": []string{
@@ -15546,12 +16138,17 @@ var keywordLookup = map[string][]string{
"surgery": []string{
"hospital",
},
+ "surimi": []string{
+ "fish_cake",
+ },
"surprise": []string{
"bangbang",
"exclamation",
"grey_exclamation",
+ "hand_over_mouth",
"interrobang",
"open_mouth",
+ "raised_eyebrow",
},
"surprised": []string{
"astonished",
@@ -15562,6 +16159,9 @@ var keywordLookup = map[string][]string{
"sustain": []string{
"battery",
},
+ "swearing": []string{
+ "symbols_over_mouth",
+ },
"sweat": []string{
"cold_sweat",
"disappointed_relieved",
@@ -15584,6 +16184,7 @@ var keywordLookup = map[string][]string{
},
"swirl": []string{
"cyclone",
+ "fish_cake",
},
"sword": []string{
"person_fencing",
@@ -15635,6 +16236,9 @@ var keywordLookup = map[string][]string{
"film_projector",
"loop",
},
+ "target": []string{
+ "dart",
+ },
"td": []string{
"chad",
},
@@ -15760,6 +16364,9 @@ var keywordLookup = map[string][]string{
"cloud_with_lightning",
"zap",
},
+ "tichel": []string{
+ "woman_with_headscarf",
+ },
"tick": []string{
"ballot_box_with_check",
"heavy_check_mark",
@@ -15830,6 +16437,7 @@ var keywordLookup = map[string][]string{
},
"toast": []string{
"bread",
+ "clinking_glasses",
},
"tobacco": []string{
"smoking",
@@ -15837,6 +16445,9 @@ var keywordLookup = map[string][]string{
"tobago": []string{
"trinidad_tobago",
},
+ "toboggan": []string{
+ "sled",
+ },
"toddler": []string{
"baby",
},
@@ -15876,6 +16487,9 @@ var keywordLookup = map[string][]string{
"arrow_up_small",
"small_red_triangle",
},
+ "tornado": []string{
+ "cyclone",
+ },
"tortoise": []string{
"turtle",
},
@@ -15920,6 +16534,7 @@ var keywordLookup = map[string][]string{
"bus",
"busstop",
"fire_engine",
+ "flying_saucer",
"helicopter",
"light_rail",
"metro",
@@ -15977,6 +16592,9 @@ var keywordLookup = map[string][]string{
"tristan": []string{
"st_helena",
},
+ "triton": []string{
+ "merman",
+ },
"trolley": []string{
"shopping_cart",
},
@@ -16026,6 +16644,9 @@ var keywordLookup = map[string][]string{
"waxing_crescent_moon",
"waxing_gibbous_moon",
},
+ "twisted": []string{
+ "pretzel",
+ },
"twister": []string{
"tornado",
},
@@ -16038,6 +16659,12 @@ var keywordLookup = map[string][]string{
"type": []string{
"keyboard",
},
+ "typhoon": []string{
+ "cyclone",
+ },
+ "tyrannosaurus": []string{
+ "t-rex",
+ },
"ua": []string{
"ukraine",
},
@@ -16045,6 +16672,9 @@ var keywordLookup = map[string][]string{
"oncoming_taxi",
"taxi",
},
+ "ufo": []string{
+ "flying_saucer",
+ },
"ug": []string{
"uganda",
},
@@ -16056,6 +16686,10 @@ var keywordLookup = map[string][]string{
"unconscious": []string{
"dizzy_face",
},
+ "undead": []string{
+ "man_zombie",
+ "woman_zombie",
+ },
"underground": []string{
"metro",
},
@@ -16069,6 +16703,9 @@ var keywordLookup = map[string][]string{
"unicorn_face": []string{
"rainbow",
},
+ "unimpressed": []string{
+ "unamused",
+ },
"union": []string{
"eu",
},
@@ -16152,6 +16789,7 @@ var keywordLookup = map[string][]string{
"kissing_heart",
"kissing_smiling_eyes",
"love_letter",
+ "orange_heart",
"purple_heart",
"revolving_hearts",
"ring",
@@ -16181,6 +16819,7 @@ var keywordLookup = map[string][]string{
},
"vegetable": []string{
"bamboo",
+ "broccoli",
"cactus",
"carrot",
"corn",
@@ -16205,6 +16844,7 @@ var keywordLookup = map[string][]string{
"bullettrain_side",
"bus",
"fire_engine",
+ "flying_saucer",
"helicopter",
"kick_scooter",
"light_rail",
@@ -16307,6 +16947,10 @@ var keywordLookup = map[string][]string{
"running_man",
"running_woman",
},
+ "walking dead": []string{
+ "man_zombie",
+ "woman_zombie",
+ },
"wallis": []string{
"wallis_futuna",
},
@@ -16361,6 +17005,9 @@ var keywordLookup = map[string][]string{
"restroom",
"toilet",
},
+ "wealthy": []string{
+ "monocle",
+ },
"weapon": []string{
"crossed_swords",
"dagger",
@@ -16412,6 +17059,9 @@ var keywordLookup = map[string][]string{
"alien",
"confused",
},
+ "welsh": []string{
+ "wales",
+ },
"western": []string{
"western_sahara",
},
@@ -16433,6 +17083,9 @@ var keywordLookup = map[string][]string{
"whoa": []string{
"open_mouth",
},
+ "whoops": []string{
+ "hand_over_mouth",
+ },
"wifi": []string{
"signal_strength",
},
@@ -16449,6 +17102,7 @@ var keywordLookup = map[string][]string{
},
"wine": []string{
"champagne",
+ "clinking_glasses",
"grapes",
"sake",
},
@@ -16464,7 +17118,9 @@ var keywordLookup = map[string][]string{
"medal_sports",
},
"winter": []string{
+ "gloves",
"mountain_snow",
+ "scarf",
"ski",
"skier",
"snowboarder",
@@ -16482,6 +17138,9 @@ var keywordLookup = map[string][]string{
"wish": []string{
"pray",
},
+ "witch": []string{
+ "sorceress",
+ },
"without_snow": []string{
"snowman",
},
@@ -16492,6 +17151,7 @@ var keywordLookup = map[string][]string{
"blonde_woman",
"bowing_woman",
"bride_with_veil",
+ "climbing_woman",
"construction_worker_woman",
"dancer",
"female_detective",
@@ -16502,6 +17162,7 @@ var keywordLookup = map[string][]string{
"haircut_woman",
"lipstick",
"massage_woman",
+ "mermaid",
"mountain_biking_woman",
"mrs_claus",
"no_good_woman",
@@ -16512,6 +17173,7 @@ var keywordLookup = map[string][]string{
"raising_hand_woman",
"rowing_woman",
"running_woman",
+ "sorceress",
"surfing_woman",
"swimming_woman",
"tipping_hand_woman",
@@ -16520,11 +17182,16 @@ var keywordLookup = map[string][]string{
"woman_artist",
"woman_astronaut",
"woman_cook",
+ "woman_elf",
"woman_facepalming",
"woman_factory_worker",
+ "woman_fairy",
"woman_farmer",
"woman_firefighter",
+ "woman_genie",
"woman_health_worker",
+ "woman_in_lotus_position",
+ "woman_in_steamy_room",
"woman_judge",
"woman_mechanic",
"woman_office_worker",
@@ -16535,7 +17202,9 @@ var keywordLookup = map[string][]string{
"woman_student",
"woman_teacher",
"woman_technologist",
+ "woman_vampire",
"woman_with_turban",
+ "woman_zombie",
"womens",
},
"women": []string{
@@ -16600,6 +17269,10 @@ var keywordLookup = map[string][]string{
"grey_exclamation",
"open_mouth",
},
+ "wrench": []string{
+ "man_mechanic",
+ "woman_mechanic",
+ },
"wrestlers": []string{
"men_wrestling",
"women_wrestling",
@@ -16671,6 +17344,13 @@ var keywordLookup = map[string][]string{
"heavy_check_mark",
"ok",
},
+ "yoga": []string{
+ "man_in_lotus_position",
+ "woman_in_lotus_position",
+ },
+ "young": []string{
+ "child",
+ },
"yt": []string{
"mayotte",
},
@@ -16680,6 +17360,10 @@ var keywordLookup = map[string][]string{
"zealand": []string{
"new_zealand",
},
+ "zen": []string{
+ "man_in_lotus_position",
+ "woman_in_lotus_position",
+ },
"zipper": []string{
"zipper_mouth_face",
},
@@ -16733,6 +17417,7 @@ var emojiReplacer = strings.NewReplacer(
":abc:", "🔤",
":abcd:", "🔡",
":accept:", "🉑",
+ ":adult:", "🧑",
":aerial_tramway:", "🚡",
":afghanistan:", "🇦🇫",
":airplane:", "✈️",
@@ -16828,6 +17513,7 @@ var emojiReplacer = strings.NewReplacer(
":battery:", "🔋",
":beach_umbrella:", "🏖",
":bear:", "🐻",
+ ":bearded_person:", "🧔",
":bed:", "🛏",
":beer:", "🍺",
":beers:", "🍻",
@@ -16846,6 +17532,7 @@ var emojiReplacer = strings.NewReplacer(
":biking_man:", "🚴",
":biking_woman:", "🚴♀️",
":bikini:", "👙",
+ ":billed_hat:", "🧢",
":biohazard:", "☣",
":bird:", "🐦",
":birthday:", "🎂",
@@ -16881,17 +17568,20 @@ var emojiReplacer = strings.NewReplacer(
":bow_and_arrow:", "🏹",
":bowing_man:", "🙇",
":bowing_woman:", "🙇♀️",
+ ":bowl_with_spoon:", "🥣",
":bowling:", "🎳",
- ":bowtie:", "",
":boxing_glove:", "🥊",
":boy:", "👦",
+ ":brain:", "🧠",
":brazil:", "🇧🇷",
":bread:", "🍞",
+ ":breastfeeding:", "🤱",
":bride_with_veil:", "👰",
":bridge_at_night:", "🌉",
":briefcase:", "💼",
":british_indian_ocean_territory:", "🇮🇴",
":british_virgin_islands:", "🇻🇬",
+ ":broccoli:", "🥦",
":broken_heart:", "💔",
":brunei:", "🇧🇳",
":bug:", "🐛",
@@ -16925,6 +17615,7 @@ var emojiReplacer = strings.NewReplacer(
":cancer:", "♋",
":candle:", "🕯",
":candy:", "🍬",
+ ":canned_food:", "🥫",
":canoe:", "🛶",
":cape_verde:", "🇨🇻",
":capital_abcd:", "🔠",
@@ -16952,10 +17643,12 @@ var emojiReplacer = strings.NewReplacer(
":cherry_blossom:", "🌸",
":chestnut:", "🌰",
":chicken:", "🐔",
+ ":child:", "🧒",
":children_crossing:", "🚸",
":chile:", "🇨🇱",
":chipmunk:", "🐿",
":chocolate_bar:", "🍫",
+ ":chopsticks:", "🥢",
":christmas_island:", "🇨🇽",
":christmas_tree:", "🎄",
":church:", "⛪",
@@ -16969,6 +17662,8 @@ var emojiReplacer = strings.NewReplacer(
":clap:", "👏",
":clapper:", "🎬",
":classical_building:", "🏛",
+ ":climbing_man:", "🧗♂️",
+ ":climbing_woman:", "🧗♀️",
":clinking_glasses:", "🥂",
":clipboard:", "📋",
":clock1:", "🕐",
@@ -17006,7 +17701,9 @@ var emojiReplacer = strings.NewReplacer(
":clown_face:", "🤡",
":clubs:", "♣️",
":cn:", "🇨🇳",
+ ":coat:", "🧥",
":cocktail:", "🍸",
+ ":coconut:", "🥥",
":cocos_islands:", "🇨🇨",
":coffee:", "☕",
":coffin:", "⚰",
@@ -17062,8 +17759,10 @@ var emojiReplacer = strings.NewReplacer(
":crystal_ball:", "🔮",
":cuba:", "🇨🇺",
":cucumber:", "🥒",
+ ":cup_with_straw:", "🥤",
":cupid:", "💘",
":curacao:", "🇨🇼",
+ ":curling_stone:", "🥌",
":curly_loop:", "➰",
":currency_exchange:", "💱",
":curry:", "🍛",
@@ -17116,6 +17815,7 @@ var emojiReplacer = strings.NewReplacer(
":droplet:", "💧",
":drum:", "🥁",
":duck:", "🦆",
+ ":dumpling:", "🥟",
":dvd:", "📀",
":e-mail:", "📧",
":eagle:", "🦅",
@@ -17131,11 +17831,13 @@ var emojiReplacer = strings.NewReplacer(
":eight:", "8️⃣",
":eight_pointed_black_star:", "✴️",
":eight_spoked_asterisk:", "✳️",
+ ":eject_button:", "⏏️",
":el_salvador:", "🇸🇻",
":electric_plug:", "🔌",
":elephant:", "🐘",
":email:", "✉️",
":end:", "🔚",
+ ":england:", "🏴",
":envelope_with_arrow:", "📩",
":equatorial_guinea:", "🇬🇶",
":eritrea:", "🇪🇷",
@@ -17148,6 +17850,7 @@ var emojiReplacer = strings.NewReplacer(
":european_post_office:", "🏤",
":evergreen_tree:", "🌲",
":exclamation:", "❗",
+ ":exploding_head:", "🤯",
":expressionless:", "😑",
":eye:", "👁",
":eyeglasses:", "👓",
@@ -17187,7 +17890,6 @@ var emojiReplacer = strings.NewReplacer(
":fast_forward:", "⏩",
":fax:", "📠",
":fearful:", "😨",
- ":feelsgood:", "",
":female_detective:", "🕵️♀️",
":ferris_wheel:", "🎡",
":ferry:", "⛴",
@@ -17198,7 +17900,6 @@ var emojiReplacer = strings.NewReplacer(
":film_projector:", "📽",
":film_strip:", "🎞",
":finland:", "🇫🇮",
- ":finnadie:", "",
":fire:", "🔥",
":fire_engine:", "🚒",
":fireworks:", "🎆",
@@ -17219,11 +17920,13 @@ var emojiReplacer = strings.NewReplacer(
":floppy_disk:", "💾",
":flower_playing_cards:", "🎴",
":flushed:", "😳",
+ ":flying_saucer:", "🛸",
":fog:", "🌫",
":foggy:", "🌁",
":football:", "🏈",
":footprints:", "👣",
":fork_and_knife:", "🍴",
+ ":fortune_cookie:", "🥠",
":fountain:", "⛲",
":fountain_pen:", "🖋",
":four:", "4️⃣",
@@ -17260,17 +17963,18 @@ var emojiReplacer = strings.NewReplacer(
":gibraltar:", "🇬🇮",
":gift:", "🎁",
":gift_heart:", "💝",
+ ":giraffe:", "🦒",
":girl:", "👧",
":globe_with_meridians:", "🌐",
+ ":gloves:", "🧤",
":goal_net:", "🥅",
":goat:", "🐐",
- ":goberserk:", "",
- ":godmode:", "",
":golf:", "⛳",
":golfing_man:", "🏌",
":golfing_woman:", "🏌️♀️",
":gorilla:", "🦍",
":grapes:", "🍇",
+ ":grasshopper:", "🦗",
":greece:", "🇬🇷",
":green_apple:", "🍏",
":green_book:", "📗",
@@ -17302,6 +18006,7 @@ var emojiReplacer = strings.NewReplacer(
":hammer_and_pick:", "⚒",
":hammer_and_wrench:", "🛠",
":hamster:", "🐹",
+ ":hand_over_mouth:", "🤭",
":handbag:", "👜",
":handshake:", "🤝",
":hash:", "#️⃣",
@@ -17323,6 +18028,7 @@ var emojiReplacer = strings.NewReplacer(
":heavy_minus_sign:", "➖",
":heavy_multiplication_x:", "✖️",
":heavy_plus_sign:", "➕",
+ ":hedgehog:", "🦔",
":helicopter:", "🚁",
":herb:", "🌿",
":hibiscus:", "🌺",
@@ -17348,7 +18054,6 @@ var emojiReplacer = strings.NewReplacer(
":houses:", "🏘",
":hugs:", "🤗",
":hungary:", "🇭🇺",
- ":hurtrealbad:", "",
":hushed:", "😯",
":ice_cream:", "🍨",
":ice_hockey:", "🏒",
@@ -17449,6 +18154,7 @@ var emojiReplacer = strings.NewReplacer(
":loudspeaker:", "📢",
":love_hotel:", "🏩",
":love_letter:", "💌",
+ ":love_you:", "🤟",
":low_brightness:", "🔅",
":luxembourg:", "🇱🇺",
":lying_face:", "🤥",
@@ -17475,11 +18181,16 @@ var emojiReplacer = strings.NewReplacer(
":man_cartwheeling:", "🤸♂️",
":man_cook:", "👨🍳",
":man_dancing:", "🕺",
+ ":man_elf:", "🧝♂️",
":man_facepalming:", "🤦",
":man_factory_worker:", "👨🏭",
+ ":man_fairy:", "🧚♂️",
":man_farmer:", "👨🌾",
":man_firefighter:", "👨🚒",
+ ":man_genie:", "🧞♂️",
":man_health_worker:", "👨⚕️",
+ ":man_in_lotus_position:", "🧘♂️",
+ ":man_in_steamy_room:", "🧖♂️",
":man_in_tuxedo:", "🤵",
":man_judge:", "👨⚖️",
":man_juggling:", "🤹♂️",
@@ -17494,8 +18205,10 @@ var emojiReplacer = strings.NewReplacer(
":man_student:", "👨🎓",
":man_teacher:", "👨🏫",
":man_technologist:", "👨💻",
+ ":man_vampire:", "🧛♂️",
":man_with_gua_pi_mao:", "👲",
":man_with_turban:", "👳",
+ ":man_zombie:", "🧟♂️",
":mans_shoe:", "👞",
":mantelpiece_clock:", "🕰",
":maple_leaf:", "🍁",
@@ -17517,6 +18230,8 @@ var emojiReplacer = strings.NewReplacer(
":men_wrestling:", "🤼♂️",
":menorah:", "🕎",
":mens:", "🚹",
+ ":mermaid:", "🧜♀️",
+ ":merman:", "🧜♂️",
":metal:", "🤘",
":metro:", "🚇",
":mexico:", "🇲🇽",
@@ -17536,6 +18251,7 @@ var emojiReplacer = strings.NewReplacer(
":mongolia:", "🇲🇳",
":monkey:", "🐒",
":monkey_face:", "🐵",
+ ":monocle:", "🧐",
":monorail:", "🚝",
":montenegro:", "🇲🇪",
":montserrat:", "🇲🇸",
@@ -17572,7 +18288,6 @@ var emojiReplacer = strings.NewReplacer(
":national_park:", "🏞",
":nauru:", "🇳🇷",
":nauseated_face:", "🤢",
- ":neckbeard:", "",
":necktie:", "👔",
":negative_squared_cross_mark:", "❎",
":nepal:", "🇳🇵",
@@ -17617,7 +18332,6 @@ var emojiReplacer = strings.NewReplacer(
":o:", "⭕",
":o2:", "🅾️",
":ocean:", "🌊",
- ":octocat:", "",
":octopus:", "🐙",
":oden:", "🍢",
":office:", "🏢",
@@ -17627,6 +18341,7 @@ var emojiReplacer = strings.NewReplacer(
":ok_man:", "🙆♂️",
":ok_woman:", "🙆",
":old_key:", "🗝",
+ ":older_adult:", "🧓",
":older_man:", "👴",
":older_woman:", "👵",
":om:", "🕉",
@@ -17644,6 +18359,7 @@ var emojiReplacer = strings.NewReplacer(
":open_umbrella:", "☂",
":ophiuchus:", "⛎",
":orange_book:", "📙",
+ ":orange_heart:", "🧡",
":orthodox_cross:", "☦",
":outbox_tray:", "📤",
":owl:", "🦉",
@@ -17657,6 +18373,7 @@ var emojiReplacer = strings.NewReplacer(
":palau:", "🇵🇼",
":palestinian_territories:", "🇵🇸",
":palm_tree:", "🌴",
+ ":palms_up:", "🤲",
":panama:", "🇵🇦",
":pancakes:", "🥞",
":panda_face:", "🐼",
@@ -17687,6 +18404,7 @@ var emojiReplacer = strings.NewReplacer(
":philippines:", "🇵🇭",
":phone:", "☎️",
":pick:", "⛏",
+ ":pie:", "🥧",
":pig:", "🐷",
":pig2:", "🐖",
":pig_nose:", "🐽",
@@ -17726,6 +18444,7 @@ var emojiReplacer = strings.NewReplacer(
":pray:", "🙏",
":prayer_beads:", "📿",
":pregnant_woman:", "🤰",
+ ":pretzel:", "🥨",
":previous_track_button:", "⏮",
":prince:", "🤴",
":princess:", "👸",
@@ -17745,15 +18464,12 @@ var emojiReplacer = strings.NewReplacer(
":radio_button:", "🔘",
":radioactive:", "☢",
":rage:", "😡",
- ":rage1:", "",
- ":rage2:", "",
- ":rage3:", "",
- ":rage4:", "",
":railway_car:", "🚃",
":railway_track:", "🛤",
":rainbow:", "🌈",
":rainbow_flag:", "🏳️🌈",
":raised_back_of_hand:", "🤚",
+ ":raised_eyebrow:", "🤨",
":raised_hand:", "✋",
":raised_hand_with_fingers_splayed:", "🖐",
":raised_hands:", "🙌",
@@ -17811,16 +18527,20 @@ var emojiReplacer = strings.NewReplacer(
":samoa:", "🇼🇸",
":san_marino:", "🇸🇲",
":sandal:", "👡",
+ ":sandwich:", "🥪",
":santa:", "🎅",
":sao_tome_principe:", "🇸🇹",
":satellite:", "📡",
":saudi_arabia:", "🇸🇦",
+ ":sauropod:", "🦕",
":saxophone:", "🎷",
+ ":scarf:", "🧣",
":school:", "🏫",
":school_satchel:", "🎒",
":scissors:", "✂️",
":scorpion:", "🦂",
":scorpius:", "♏",
+ ":scotland:", "🏴",
":scream:", "😱",
":scream_cat:", "🙀",
":scroll:", "📜",
@@ -17842,11 +18562,11 @@ var emojiReplacer = strings.NewReplacer(
":shield:", "🛡",
":shinto_shrine:", "⛩",
":ship:", "🚢",
- ":shipit:", "",
":shopping:", "🛍",
":shopping_cart:", "🛒",
":shower:", "🚿",
":shrimp:", "🦐",
+ ":shushing:", "🤫",
":sierra_leone:", "🇸🇱",
":signal_strength:", "📶",
":singapore:", "🇸🇬",
@@ -17857,6 +18577,7 @@ var emojiReplacer = strings.NewReplacer(
":skier:", "⛷",
":skull:", "💀",
":skull_and_crossbones:", "☠",
+ ":sled:", "🛷",
":sleeping:", "😴",
":sleeping_bed:", "🛌",
":sleepy:", "😪",
@@ -17887,9 +18608,11 @@ var emojiReplacer = strings.NewReplacer(
":snowman_with_snow:", "☃",
":sob:", "😭",
":soccer:", "⚽",
+ ":socks:", "🧦",
":solomon_islands:", "🇸🇧",
":somalia:", "🇸🇴",
":soon:", "🔜",
+ ":sorceress:", "🧙♀️",
":sos:", "🆘",
":sound:", "🔉",
":south_africa:", "🇿🇦",
@@ -17925,9 +18648,11 @@ var emojiReplacer = strings.NewReplacer(
":star2:", "🌟",
":star_and_crescent:", "☪",
":star_of_david:", "✡",
+ ":star_struck:", "🤩",
":stars:", "🌠",
":station:", "🚉",
":statue_of_liberty:", "🗽",
+ ":steak:", "🥩",
":steam_locomotive:", "🚂",
":stew:", "🍲",
":stop_button:", "⏹",
@@ -17954,7 +18679,6 @@ var emojiReplacer = strings.NewReplacer(
":surfing_woman:", "🏄♀️",
":suriname:", "🇸🇷",
":sushi:", "🍣",
- ":suspect:", "",
":suspension_railway:", "🚟",
":swaziland:", "🇸🇿",
":sweat:", "😓",
@@ -17966,13 +18690,16 @@ var emojiReplacer = strings.NewReplacer(
":swimming_woman:", "🏊♀️",
":switzerland:", "🇨🇭",
":symbols:", "🔣",
+ ":symbols_over_mouth:", "🤬",
":synagogue:", "🕍",
":syria:", "🇸🇾",
":syringe:", "💉",
+ ":t-rex:", "🦖",
":taco:", "🌮",
":tada:", "🎉",
":taiwan:", "🇹🇼",
":tajikistan:", "🇹🇯",
+ ":takeout_box:", "🥡",
":tanabata_tree:", "🎋",
":tangerine:", "🍊",
":tanzania:", "🇹🇿",
@@ -18021,7 +18748,6 @@ var emojiReplacer = strings.NewReplacer(
":trinidad_tobago:", "🇹🇹",
":triumph:", "😤",
":trolleybus:", "🚎",
- ":trollface:", "",
":trophy:", "🏆",
":tropical_drink:", "🍹",
":tropical_fish:", "🐠",
@@ -18082,8 +18808,10 @@ var emojiReplacer = strings.NewReplacer(
":virgo:", "♍",
":volcano:", "🌋",
":volleyball:", "🏐",
+ ":vomiting:", "🤮",
":vs:", "🆚",
":vulcan_salute:", "🖖",
+ ":wales:", "🏴",
":walking_man:", "🚶",
":walking_woman:", "🚶♀️",
":wallis_futuna:", "🇼🇫",
@@ -18122,17 +18850,23 @@ var emojiReplacer = strings.NewReplacer(
":wind_face:", "🌬",
":wine_glass:", "🍷",
":wink:", "😉",
+ ":wizard:", "🧙♂️",
":wolf:", "🐺",
":woman:", "👩",
":woman_artist:", "👩🎨",
":woman_astronaut:", "👩🚀",
":woman_cartwheeling:", "🤸♀️",
":woman_cook:", "👩🍳",
+ ":woman_elf:", "🧝♀️",
":woman_facepalming:", "🤦♀️",
":woman_factory_worker:", "👩🏭",
+ ":woman_fairy:", "🧚♀️",
":woman_farmer:", "👩🌾",
":woman_firefighter:", "👩🚒",
+ ":woman_genie:", "🧞♀️",
":woman_health_worker:", "👩⚕️",
+ ":woman_in_lotus_position:", "🧘♀️",
+ ":woman_in_steamy_room:", "🧖♀️",
":woman_judge:", "👩⚖️",
":woman_juggling:", "🤹♀️",
":woman_mechanic:", "👩🔧",
@@ -18146,7 +18880,10 @@ var emojiReplacer = strings.NewReplacer(
":woman_student:", "👩🎓",
":woman_teacher:", "👩🏫",
":woman_technologist:", "👩💻",
+ ":woman_vampire:", "🧛♀️",
+ ":woman_with_headscarf:", "🧕",
":woman_with_turban:", "👳♀️",
+ ":woman_zombie:", "🧟♀️",
":womans_clothes:", "👚",
":womans_hat:", "👒",
":women_wrestling:", "🤼♀️",
@@ -18162,7 +18899,9 @@ var emojiReplacer = strings.NewReplacer(
":yin_yang:", "☯",
":yum:", "😋",
":zambia:", "🇿🇲",
+ ":zany:", "🤪",
":zap:", "⚡",
+ ":zebra:", "🦓",
":zero:", "0️⃣",
":zimbabwe:", "🇿🇼",
":zipper_mouth_face:", "🤐",
@@ -18183,6 +18922,7 @@ var emojiPaddedReplacer = strings.NewReplacer(
":abc:", "🔤 ",
":abcd:", "🔡 ",
":accept:", "🉑 ",
+ ":adult:", "🧑 ",
":aerial_tramway:", "🚡 ",
":afghanistan:", "🇦🇫 ",
":airplane:", "✈️ ",
@@ -18278,6 +19018,7 @@ var emojiPaddedReplacer = strings.NewReplacer(
":battery:", "🔋 ",
":beach_umbrella:", "🏖 ",
":bear:", "🐻 ",
+ ":bearded_person:", "🧔 ",
":bed:", "🛏 ",
":beer:", "🍺 ",
":beers:", "🍻 ",
@@ -18296,6 +19037,7 @@ var emojiPaddedReplacer = strings.NewReplacer(
":biking_man:", "🚴 ",
":biking_woman:", "🚴♀️ ",
":bikini:", "👙 ",
+ ":billed_hat:", "🧢 ",
":biohazard:", "☣ ",
":bird:", "🐦 ",
":birthday:", "🎂 ",
@@ -18331,17 +19073,20 @@ var emojiPaddedReplacer = strings.NewReplacer(
":bow_and_arrow:", "🏹 ",
":bowing_man:", "🙇 ",
":bowing_woman:", "🙇♀️ ",
+ ":bowl_with_spoon:", "🥣 ",
":bowling:", "🎳 ",
- ":bowtie:", " ",
":boxing_glove:", "🥊 ",
":boy:", "👦 ",
+ ":brain:", "🧠 ",
":brazil:", "🇧🇷 ",
":bread:", "🍞 ",
+ ":breastfeeding:", "🤱 ",
":bride_with_veil:", "👰 ",
":bridge_at_night:", "🌉 ",
":briefcase:", "💼 ",
":british_indian_ocean_territory:", "🇮🇴 ",
":british_virgin_islands:", "🇻🇬 ",
+ ":broccoli:", "🥦 ",
":broken_heart:", "💔 ",
":brunei:", "🇧🇳 ",
":bug:", "🐛 ",
@@ -18375,6 +19120,7 @@ var emojiPaddedReplacer = strings.NewReplacer(
":cancer:", "♋ ",
":candle:", "🕯 ",
":candy:", "🍬 ",
+ ":canned_food:", "🥫 ",
":canoe:", "🛶 ",
":cape_verde:", "🇨🇻 ",
":capital_abcd:", "🔠 ",
@@ -18402,10 +19148,12 @@ var emojiPaddedReplacer = strings.NewReplacer(
":cherry_blossom:", "🌸 ",
":chestnut:", "🌰 ",
":chicken:", "🐔 ",
+ ":child:", "🧒 ",
":children_crossing:", "🚸 ",
":chile:", "🇨🇱 ",
":chipmunk:", "🐿 ",
":chocolate_bar:", "🍫 ",
+ ":chopsticks:", "🥢 ",
":christmas_island:", "🇨🇽 ",
":christmas_tree:", "🎄 ",
":church:", "⛪ ",
@@ -18419,6 +19167,8 @@ var emojiPaddedReplacer = strings.NewReplacer(
":clap:", "👏 ",
":clapper:", "🎬 ",
":classical_building:", "🏛 ",
+ ":climbing_man:", "🧗♂️ ",
+ ":climbing_woman:", "🧗♀️ ",
":clinking_glasses:", "🥂 ",
":clipboard:", "📋 ",
":clock1:", "🕐 ",
@@ -18456,7 +19206,9 @@ var emojiPaddedReplacer = strings.NewReplacer(
":clown_face:", "🤡 ",
":clubs:", "♣️ ",
":cn:", "🇨🇳 ",
+ ":coat:", "🧥 ",
":cocktail:", "🍸 ",
+ ":coconut:", "🥥 ",
":cocos_islands:", "🇨🇨 ",
":coffee:", "☕ ",
":coffin:", "⚰ ",
@@ -18512,8 +19264,10 @@ var emojiPaddedReplacer = strings.NewReplacer(
":crystal_ball:", "🔮 ",
":cuba:", "🇨🇺 ",
":cucumber:", "🥒 ",
+ ":cup_with_straw:", "🥤 ",
":cupid:", "💘 ",
":curacao:", "🇨🇼 ",
+ ":curling_stone:", "🥌 ",
":curly_loop:", "➰ ",
":currency_exchange:", "💱 ",
":curry:", "🍛 ",
@@ -18566,6 +19320,7 @@ var emojiPaddedReplacer = strings.NewReplacer(
":droplet:", "💧 ",
":drum:", "🥁 ",
":duck:", "🦆 ",
+ ":dumpling:", "🥟 ",
":dvd:", "📀 ",
":e-mail:", "📧 ",
":eagle:", "🦅 ",
@@ -18581,11 +19336,13 @@ var emojiPaddedReplacer = strings.NewReplacer(
":eight:", "8️⃣ ",
":eight_pointed_black_star:", "✴️ ",
":eight_spoked_asterisk:", "✳️ ",
+ ":eject_button:", "⏏️ ",
":el_salvador:", "🇸🇻 ",
":electric_plug:", "🔌 ",
":elephant:", "🐘 ",
":email:", "✉️ ",
":end:", "🔚 ",
+ ":england:", "🏴 ",
":envelope_with_arrow:", "📩 ",
":equatorial_guinea:", "🇬🇶 ",
":eritrea:", "🇪🇷 ",
@@ -18598,6 +19355,7 @@ var emojiPaddedReplacer = strings.NewReplacer(
":european_post_office:", "🏤 ",
":evergreen_tree:", "🌲 ",
":exclamation:", "❗ ",
+ ":exploding_head:", "🤯 ",
":expressionless:", "😑 ",
":eye:", "👁 ",
":eyeglasses:", "👓 ",
@@ -18637,7 +19395,6 @@ var emojiPaddedReplacer = strings.NewReplacer(
":fast_forward:", "⏩ ",
":fax:", "📠 ",
":fearful:", "😨 ",
- ":feelsgood:", " ",
":female_detective:", "🕵️♀️ ",
":ferris_wheel:", "🎡 ",
":ferry:", "⛴ ",
@@ -18648,7 +19405,6 @@ var emojiPaddedReplacer = strings.NewReplacer(
":film_projector:", "📽 ",
":film_strip:", "🎞 ",
":finland:", "🇫🇮 ",
- ":finnadie:", " ",
":fire:", "🔥 ",
":fire_engine:", "🚒 ",
":fireworks:", "🎆 ",
@@ -18669,11 +19425,13 @@ var emojiPaddedReplacer = strings.NewReplacer(
":floppy_disk:", "💾 ",
":flower_playing_cards:", "🎴 ",
":flushed:", "😳 ",
+ ":flying_saucer:", "🛸 ",
":fog:", "🌫 ",
":foggy:", "🌁 ",
":football:", "🏈 ",
":footprints:", "👣 ",
":fork_and_knife:", "🍴 ",
+ ":fortune_cookie:", "🥠 ",
":fountain:", "⛲ ",
":fountain_pen:", "🖋 ",
":four:", "4️⃣ ",
@@ -18710,17 +19468,18 @@ var emojiPaddedReplacer = strings.NewReplacer(
":gibraltar:", "🇬🇮 ",
":gift:", "🎁 ",
":gift_heart:", "💝 ",
+ ":giraffe:", "🦒 ",
":girl:", "👧 ",
":globe_with_meridians:", "🌐 ",
+ ":gloves:", "🧤 ",
":goal_net:", "🥅 ",
":goat:", "🐐 ",
- ":goberserk:", " ",
- ":godmode:", " ",
":golf:", "⛳ ",
":golfing_man:", "🏌 ",
":golfing_woman:", "🏌️♀️ ",
":gorilla:", "🦍 ",
":grapes:", "🍇 ",
+ ":grasshopper:", "🦗 ",
":greece:", "🇬🇷 ",
":green_apple:", "🍏 ",
":green_book:", "📗 ",
@@ -18752,6 +19511,7 @@ var emojiPaddedReplacer = strings.NewReplacer(
":hammer_and_pick:", "⚒ ",
":hammer_and_wrench:", "🛠 ",
":hamster:", "🐹 ",
+ ":hand_over_mouth:", "🤭 ",
":handbag:", "👜 ",
":handshake:", "🤝 ",
":hash:", "#️⃣ ",
@@ -18773,6 +19533,7 @@ var emojiPaddedReplacer = strings.NewReplacer(
":heavy_minus_sign:", "➖ ",
":heavy_multiplication_x:", "✖️ ",
":heavy_plus_sign:", "➕ ",
+ ":hedgehog:", "🦔 ",
":helicopter:", "🚁 ",
":herb:", "🌿 ",
":hibiscus:", "🌺 ",
@@ -18798,7 +19559,6 @@ var emojiPaddedReplacer = strings.NewReplacer(
":houses:", "🏘 ",
":hugs:", "🤗 ",
":hungary:", "🇭🇺 ",
- ":hurtrealbad:", " ",
":hushed:", "😯 ",
":ice_cream:", "🍨 ",
":ice_hockey:", "🏒 ",
@@ -18899,6 +19659,7 @@ var emojiPaddedReplacer = strings.NewReplacer(
":loudspeaker:", "📢 ",
":love_hotel:", "🏩 ",
":love_letter:", "💌 ",
+ ":love_you:", "🤟 ",
":low_brightness:", "🔅 ",
":luxembourg:", "🇱🇺 ",
":lying_face:", "🤥 ",
@@ -18925,11 +19686,16 @@ var emojiPaddedReplacer = strings.NewReplacer(
":man_cartwheeling:", "🤸♂️ ",
":man_cook:", "👨🍳 ",
":man_dancing:", "🕺 ",
+ ":man_elf:", "🧝♂️ ",
":man_facepalming:", "🤦 ",
":man_factory_worker:", "👨🏭 ",
+ ":man_fairy:", "🧚♂️ ",
":man_farmer:", "👨🌾 ",
":man_firefighter:", "👨🚒 ",
+ ":man_genie:", "🧞♂️ ",
":man_health_worker:", "👨⚕️ ",
+ ":man_in_lotus_position:", "🧘♂️ ",
+ ":man_in_steamy_room:", "🧖♂️ ",
":man_in_tuxedo:", "🤵 ",
":man_judge:", "👨⚖️ ",
":man_juggling:", "🤹♂️ ",
@@ -18944,8 +19710,10 @@ var emojiPaddedReplacer = strings.NewReplacer(
":man_student:", "👨🎓 ",
":man_teacher:", "👨🏫 ",
":man_technologist:", "👨💻 ",
+ ":man_vampire:", "🧛♂️ ",
":man_with_gua_pi_mao:", "👲 ",
":man_with_turban:", "👳 ",
+ ":man_zombie:", "🧟♂️ ",
":mans_shoe:", "👞 ",
":mantelpiece_clock:", "🕰 ",
":maple_leaf:", "🍁 ",
@@ -18967,6 +19735,8 @@ var emojiPaddedReplacer = strings.NewReplacer(
":men_wrestling:", "🤼♂️ ",
":menorah:", "🕎 ",
":mens:", "🚹 ",
+ ":mermaid:", "🧜♀️ ",
+ ":merman:", "🧜♂️ ",
":metal:", "🤘 ",
":metro:", "🚇 ",
":mexico:", "🇲🇽 ",
@@ -18986,6 +19756,7 @@ var emojiPaddedReplacer = strings.NewReplacer(
":mongolia:", "🇲🇳 ",
":monkey:", "🐒 ",
":monkey_face:", "🐵 ",
+ ":monocle:", "🧐 ",
":monorail:", "🚝 ",
":montenegro:", "🇲🇪 ",
":montserrat:", "🇲🇸 ",
@@ -19022,7 +19793,6 @@ var emojiPaddedReplacer = strings.NewReplacer(
":national_park:", "🏞 ",
":nauru:", "🇳🇷 ",
":nauseated_face:", "🤢 ",
- ":neckbeard:", " ",
":necktie:", "👔 ",
":negative_squared_cross_mark:", "❎ ",
":nepal:", "🇳🇵 ",
@@ -19067,7 +19837,6 @@ var emojiPaddedReplacer = strings.NewReplacer(
":o:", "⭕ ",
":o2:", "🅾️ ",
":ocean:", "🌊 ",
- ":octocat:", " ",
":octopus:", "🐙 ",
":oden:", "🍢 ",
":office:", "🏢 ",
@@ -19077,6 +19846,7 @@ var emojiPaddedReplacer = strings.NewReplacer(
":ok_man:", "🙆♂️ ",
":ok_woman:", "🙆 ",
":old_key:", "🗝 ",
+ ":older_adult:", "🧓 ",
":older_man:", "👴 ",
":older_woman:", "👵 ",
":om:", "🕉 ",
@@ -19094,6 +19864,7 @@ var emojiPaddedReplacer = strings.NewReplacer(
":open_umbrella:", "☂ ",
":ophiuchus:", "⛎ ",
":orange_book:", "📙 ",
+ ":orange_heart:", "🧡 ",
":orthodox_cross:", "☦ ",
":outbox_tray:", "📤 ",
":owl:", "🦉 ",
@@ -19107,6 +19878,7 @@ var emojiPaddedReplacer = strings.NewReplacer(
":palau:", "🇵🇼 ",
":palestinian_territories:", "🇵🇸 ",
":palm_tree:", "🌴 ",
+ ":palms_up:", "🤲 ",
":panama:", "🇵🇦 ",
":pancakes:", "🥞 ",
":panda_face:", "🐼 ",
@@ -19137,6 +19909,7 @@ var emojiPaddedReplacer = strings.NewReplacer(
":philippines:", "🇵🇭 ",
":phone:", "☎️ ",
":pick:", "⛏ ",
+ ":pie:", "🥧 ",
":pig:", "🐷 ",
":pig2:", "🐖 ",
":pig_nose:", "🐽 ",
@@ -19176,6 +19949,7 @@ var emojiPaddedReplacer = strings.NewReplacer(
":pray:", "🙏 ",
":prayer_beads:", "📿 ",
":pregnant_woman:", "🤰 ",
+ ":pretzel:", "🥨 ",
":previous_track_button:", "⏮ ",
":prince:", "🤴 ",
":princess:", "👸 ",
@@ -19195,15 +19969,12 @@ var emojiPaddedReplacer = strings.NewReplacer(
":radio_button:", "🔘 ",
":radioactive:", "☢ ",
":rage:", "😡 ",
- ":rage1:", " ",
- ":rage2:", " ",
- ":rage3:", " ",
- ":rage4:", " ",
":railway_car:", "🚃 ",
":railway_track:", "🛤 ",
":rainbow:", "🌈 ",
":rainbow_flag:", "🏳️🌈 ",
":raised_back_of_hand:", "🤚 ",
+ ":raised_eyebrow:", "🤨 ",
":raised_hand:", "✋ ",
":raised_hand_with_fingers_splayed:", "🖐 ",
":raised_hands:", "🙌 ",
@@ -19261,16 +20032,20 @@ var emojiPaddedReplacer = strings.NewReplacer(
":samoa:", "🇼🇸 ",
":san_marino:", "🇸🇲 ",
":sandal:", "👡 ",
+ ":sandwich:", "🥪 ",
":santa:", "🎅 ",
":sao_tome_principe:", "🇸🇹 ",
":satellite:", "📡 ",
":saudi_arabia:", "🇸🇦 ",
+ ":sauropod:", "🦕 ",
":saxophone:", "🎷 ",
+ ":scarf:", "🧣 ",
":school:", "🏫 ",
":school_satchel:", "🎒 ",
":scissors:", "✂️ ",
":scorpion:", "🦂 ",
":scorpius:", "♏ ",
+ ":scotland:", "🏴 ",
":scream:", "😱 ",
":scream_cat:", "🙀 ",
":scroll:", "📜 ",
@@ -19292,11 +20067,11 @@ var emojiPaddedReplacer = strings.NewReplacer(
":shield:", "🛡 ",
":shinto_shrine:", "⛩ ",
":ship:", "🚢 ",
- ":shipit:", " ",
":shopping:", "🛍 ",
":shopping_cart:", "🛒 ",
":shower:", "🚿 ",
":shrimp:", "🦐 ",
+ ":shushing:", "🤫 ",
":sierra_leone:", "🇸🇱 ",
":signal_strength:", "📶 ",
":singapore:", "🇸🇬 ",
@@ -19307,6 +20082,7 @@ var emojiPaddedReplacer = strings.NewReplacer(
":skier:", "⛷ ",
":skull:", "💀 ",
":skull_and_crossbones:", "☠ ",
+ ":sled:", "🛷 ",
":sleeping:", "😴 ",
":sleeping_bed:", "🛌 ",
":sleepy:", "😪 ",
@@ -19337,9 +20113,11 @@ var emojiPaddedReplacer = strings.NewReplacer(
":snowman_with_snow:", "☃ ",
":sob:", "😭 ",
":soccer:", "⚽ ",
+ ":socks:", "🧦 ",
":solomon_islands:", "🇸🇧 ",
":somalia:", "🇸🇴 ",
":soon:", "🔜 ",
+ ":sorceress:", "🧙♀️ ",
":sos:", "🆘 ",
":sound:", "🔉 ",
":south_africa:", "🇿🇦 ",
@@ -19375,9 +20153,11 @@ var emojiPaddedReplacer = strings.NewReplacer(
":star2:", "🌟 ",
":star_and_crescent:", "☪ ",
":star_of_david:", "✡ ",
+ ":star_struck:", "🤩 ",
":stars:", "🌠 ",
":station:", "🚉 ",
":statue_of_liberty:", "🗽 ",
+ ":steak:", "🥩 ",
":steam_locomotive:", "🚂 ",
":stew:", "🍲 ",
":stop_button:", "⏹ ",
@@ -19404,7 +20184,6 @@ var emojiPaddedReplacer = strings.NewReplacer(
":surfing_woman:", "🏄♀️ ",
":suriname:", "🇸🇷 ",
":sushi:", "🍣 ",
- ":suspect:", " ",
":suspension_railway:", "🚟 ",
":swaziland:", "🇸🇿 ",
":sweat:", "😓 ",
@@ -19416,13 +20195,16 @@ var emojiPaddedReplacer = strings.NewReplacer(
":swimming_woman:", "🏊♀️ ",
":switzerland:", "🇨🇭 ",
":symbols:", "🔣 ",
+ ":symbols_over_mouth:", "🤬 ",
":synagogue:", "🕍 ",
":syria:", "🇸🇾 ",
":syringe:", "💉 ",
+ ":t-rex:", "🦖 ",
":taco:", "🌮 ",
":tada:", "🎉 ",
":taiwan:", "🇹🇼 ",
":tajikistan:", "🇹🇯 ",
+ ":takeout_box:", "🥡 ",
":tanabata_tree:", "🎋 ",
":tangerine:", "🍊 ",
":tanzania:", "🇹🇿 ",
@@ -19471,7 +20253,6 @@ var emojiPaddedReplacer = strings.NewReplacer(
":trinidad_tobago:", "🇹🇹 ",
":triumph:", "😤 ",
":trolleybus:", "🚎 ",
- ":trollface:", " ",
":trophy:", "🏆 ",
":tropical_drink:", "🍹 ",
":tropical_fish:", "🐠 ",
@@ -19532,8 +20313,10 @@ var emojiPaddedReplacer = strings.NewReplacer(
":virgo:", "♍ ",
":volcano:", "🌋 ",
":volleyball:", "🏐 ",
+ ":vomiting:", "🤮 ",
":vs:", "🆚 ",
":vulcan_salute:", "🖖 ",
+ ":wales:", "🏴 ",
":walking_man:", "🚶 ",
":walking_woman:", "🚶♀️ ",
":wallis_futuna:", "🇼🇫 ",
@@ -19572,17 +20355,23 @@ var emojiPaddedReplacer = strings.NewReplacer(
":wind_face:", "🌬 ",
":wine_glass:", "🍷 ",
":wink:", "😉 ",
+ ":wizard:", "🧙♂️ ",
":wolf:", "🐺 ",
":woman:", "👩 ",
":woman_artist:", "👩🎨 ",
":woman_astronaut:", "👩🚀 ",
":woman_cartwheeling:", "🤸♀️ ",
":woman_cook:", "👩🍳 ",
+ ":woman_elf:", "🧝♀️ ",
":woman_facepalming:", "🤦♀️ ",
":woman_factory_worker:", "👩🏭 ",
+ ":woman_fairy:", "🧚♀️ ",
":woman_farmer:", "👩🌾 ",
":woman_firefighter:", "👩🚒 ",
+ ":woman_genie:", "🧞♀️ ",
":woman_health_worker:", "👩⚕️ ",
+ ":woman_in_lotus_position:", "🧘♀️ ",
+ ":woman_in_steamy_room:", "🧖♀️ ",
":woman_judge:", "👩⚖️ ",
":woman_juggling:", "🤹♀️ ",
":woman_mechanic:", "👩🔧 ",
@@ -19596,7 +20385,10 @@ var emojiPaddedReplacer = strings.NewReplacer(
":woman_student:", "👩🎓 ",
":woman_teacher:", "👩🏫 ",
":woman_technologist:", "👩💻 ",
+ ":woman_vampire:", "🧛♀️ ",
+ ":woman_with_headscarf:", "🧕 ",
":woman_with_turban:", "👳♀️ ",
+ ":woman_zombie:", "🧟♀️ ",
":womans_clothes:", "👚 ",
":womans_hat:", "👒 ",
":women_wrestling:", "🤼♀️ ",
@@ -19612,7 +20404,9 @@ var emojiPaddedReplacer = strings.NewReplacer(
":yin_yang:", "☯ ",
":yum:", "😋 ",
":zambia:", "🇿🇲 ",
+ ":zany:", "🤪 ",
":zap:", "⚡ ",
+ ":zebra:", "🦓 ",
":zero:", "0️⃣ ",
":zimbabwe:", "🇿🇼 ",
":zipper_mouth_face:", "🤐 ",
diff --git a/vendor/github.com/rs/xid/hostid_darwin.go b/vendor/github.com/rs/xid/hostid_darwin.go
index abd06840..08351ff7 100644
--- a/vendor/github.com/rs/xid/hostid_darwin.go
+++ b/vendor/github.com/rs/xid/hostid_darwin.go
@@ -2,8 +2,8 @@
package xid
-import "golang.org/x/sys/unix"
+import "syscall"
func readPlatformMachineID() (string, error) {
- return unix.Sysctl("kern.uuid")
+ return syscall.Sysctl("kern.uuid")
}
diff --git a/vendor/github.com/rs/xid/hostid_freebsd.go b/vendor/github.com/rs/xid/hostid_freebsd.go
index df2bd13c..be25a039 100644
--- a/vendor/github.com/rs/xid/hostid_freebsd.go
+++ b/vendor/github.com/rs/xid/hostid_freebsd.go
@@ -2,8 +2,8 @@
package xid
-import "golang.org/x/sys/unix"
+import "syscall"
func readPlatformMachineID() (string, error) {
- return unix.Sysctl("kern.hostuuid")
+ return syscall.Sysctl("kern.hostuuid")
}
diff --git a/vendor/github.com/rs/xid/hostid_windows.go b/vendor/github.com/rs/xid/hostid_windows.go
index b8e1c2cb..ec2593ee 100644
--- a/vendor/github.com/rs/xid/hostid_windows.go
+++ b/vendor/github.com/rs/xid/hostid_windows.go
@@ -4,32 +4,31 @@ package xid
import (
"fmt"
+ "syscall"
"unsafe"
-
- "golang.org/x/sys/windows"
)
func readPlatformMachineID() (string, error) {
- // source: https://github.com/shirou/gopsutil/blob/master/host/host_windows.go
- var h windows.Handle
- err := windows.RegOpenKeyEx(windows.HKEY_LOCAL_MACHINE, windows.StringToUTF16Ptr(`SOFTWARE\Microsoft\Cryptography`), 0, windows.KEY_READ|windows.KEY_WOW64_64KEY, &h)
+ // source: https://github.com/shirou/gopsutil/blob/master/host/host_syscall.go
+ var h syscall.Handle
+ err := syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE, syscall.StringToUTF16Ptr(`SOFTWARE\Microsoft\Cryptography`), 0, syscall.KEY_READ|syscall.KEY_WOW64_64KEY, &h)
if err != nil {
return "", err
}
- defer windows.RegCloseKey(h)
+ defer syscall.RegCloseKey(h)
- const windowsRegBufLen = 74 // len(`{`) + len(`abcdefgh-1234-456789012-123345456671` * 2) + len(`}`) // 2 == bytes/UTF16
+ const syscallRegBufLen = 74 // len(`{`) + len(`abcdefgh-1234-456789012-123345456671` * 2) + len(`}`) // 2 == bytes/UTF16
const uuidLen = 36
- var regBuf [windowsRegBufLen]uint16
- bufLen := uint32(windowsRegBufLen)
+ var regBuf [syscallRegBufLen]uint16
+ bufLen := uint32(syscallRegBufLen)
var valType uint32
- err = windows.RegQueryValueEx(h, windows.StringToUTF16Ptr(`MachineGuid`), nil, &valType, (*byte)(unsafe.Pointer(®Buf[0])), &bufLen)
+ err = syscall.RegQueryValueEx(h, syscall.StringToUTF16Ptr(`MachineGuid`), nil, &valType, (*byte)(unsafe.Pointer(®Buf[0])), &bufLen)
if err != nil {
return "", err
}
- hostID := windows.UTF16ToString(regBuf[:])
+ hostID := syscall.UTF16ToString(regBuf[:])
hostIDLen := len(hostID)
if hostIDLen != uuidLen {
return "", fmt.Errorf("HostID incorrect: %q\n", hostID)
diff --git a/vendor/github.com/rs/xid/id.go b/vendor/github.com/rs/xid/id.go
index 51b8d7fa..466faf26 100644
--- a/vendor/github.com/rs/xid/id.go
+++ b/vendor/github.com/rs/xid/id.go
@@ -42,6 +42,7 @@
package xid
import (
+ "bytes"
"crypto/md5"
"crypto/rand"
"database/sql/driver"
@@ -51,10 +52,9 @@ import (
"hash/crc32"
"io/ioutil"
"os"
+ "sort"
"sync/atomic"
"time"
- "bytes"
- "sort"
)
// Code inspired from mgo/bson ObjectId
@@ -143,9 +143,14 @@ func randInt() uint32 {
// New generates a globally unique ID
func New() ID {
+ return NewWithTime(time.Now())
+}
+
+// NewWithTime generates a globally unique ID with the passed in time
+func NewWithTime(t time.Time) ID {
var id ID
// Timestamp, 4 bytes, big endian
- binary.BigEndian.PutUint32(id[:], uint32(time.Now().Unix()))
+ binary.BigEndian.PutUint32(id[:], uint32(t.Unix()))
// Machine, first 3 bytes of md5(hostname)
id[4] = machineID[0]
id[5] = machineID[1]
@@ -339,7 +344,6 @@ func (id ID) Compare(other ID) int {
return bytes.Compare(id[:], other[:])
}
-
type sorter []ID
func (s sorter) Len() int {
diff --git a/vendor/github.com/russross/blackfriday/.travis.yml b/vendor/github.com/russross/blackfriday/.travis.yml
index a1687f17..a4eb2577 100644
--- a/vendor/github.com/russross/blackfriday/.travis.yml
+++ b/vendor/github.com/russross/blackfriday/.travis.yml
@@ -1,30 +1,18 @@
-sudo: false
+# Travis CI (http://travis-ci.org/) is a continuous integration service for
+# open source projects. This file configures it to run unit tests for
+# blackfriday.
+
language: go
+
go:
- - 1.5.4
- - 1.6.2
- - tip
-matrix:
- include:
- - go: 1.2.2
- script:
- - go get -t -v ./...
- - go test -v -race ./...
- - go: 1.3.3
- script:
- - go get -t -v ./...
- - go test -v -race ./...
- - go: 1.4.3
- script:
- - go get -t -v ./...
- - go test -v -race ./...
- allow_failures:
- - go: tip
- fast_finish: true
+ - 1.5
+ - 1.6
+ - 1.7
+
install:
- - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step).
+ - go get -d -t -v ./...
+ - go build -v ./...
+
script:
- - go get -t -v ./...
- - diff -u <(echo -n) <(gofmt -d -s .)
- - go tool vet .
- - go test -v -race ./...
+ - go test -v ./...
+ - go test -run=^$ -bench=BenchmarkReference -benchmem
diff --git a/vendor/github.com/russross/blackfriday/README.md b/vendor/github.com/russross/blackfriday/README.md
index a6c94b79..2e0db355 100644
--- a/vendor/github.com/russross/blackfriday/README.md
+++ b/vendor/github.com/russross/blackfriday/README.md
@@ -1,6 +1,4 @@
-Blackfriday
-[![Build Status][BuildSVG]][BuildURL]
-[![Godoc][GodocV2SVG]][GodocV2URL]
+Blackfriday [](https://travis-ci.org/russross/blackfriday)
===========
Blackfriday is a [Markdown][1] processor implemented in [Go][2]. It
@@ -18,27 +16,27 @@ It started as a translation from C of [Sundown][3].
Installation
------------
-Blackfriday is compatible with any modern Go release. With Go and git installed:
+Blackfriday is compatible with any modern Go release. With Go 1.7 and git
+installed:
- go get -u gopkg.in/russross/blackfriday.v2
+ go get gopkg.in/russross/blackfriday.v2
-will download, compile, and install the package into your `$GOPATH` directory
-hierarchy.
+will download, compile, and install the package into your `$GOPATH`
+directory hierarchy. Alternatively, you can achieve the same if you
+import it into a project:
+
+ import "gopkg.in/russross/blackfriday.v2"
+
+and `go get` without parameters.
Versions
--------
Currently maintained and recommended version of Blackfriday is `v2`. It's being
-developed on its own branch: https://github.com/russross/blackfriday/tree/v2 and the
-documentation is available at
-https://godoc.org/gopkg.in/russross/blackfriday.v2.
-
-It is `go get`-able via via [gopkg.in][6] at `gopkg.in/russross/blackfriday.v2`,
-but we highly recommend using package management tool like [dep][7] or
-[Glide][8] and make use of semantic versioning. With package management you
-should import `github.com/russross/blackfriday` and specify that you're using
-version 2.0.0.
+developed on its own branch: https://github.com/russross/blackfriday/v2. You
+should install and import it via [gopkg.in][6] at
+`gopkg.in/russross/blackfriday.v2`.
Version 2 offers a number of improvements over v1:
@@ -58,43 +56,9 @@ Potential drawbacks:
v2. See issue [#348](https://github.com/russross/blackfriday/issues/348) for
tracking.
-If you are still interested in the legacy `v1`, you can import it from
-`github.com/russross/blackfriday`. Documentation for the legacy v1 can be found
-here: https://godoc.org/github.com/russross/blackfriday
-
-### Known issue with `dep`
-
-There is a known problem with using Blackfriday v1 _transitively_ and `dep`.
-Currently `dep` prioritizes semver versions over anything else, and picks the
-latest one, plus it does not apply a `[[constraint]]` specifier to transitively
-pulled in packages. So if you're using something that uses Blackfriday v1, but
-that something does not use `dep` yet, you will get Blackfriday v2 pulled in and
-your first dependency will fail to build.
-
-There are couple of fixes for it, documented here:
-https://github.com/golang/dep/blob/master/docs/FAQ.md#how-do-i-constrain-a-transitive-dependencys-version
-
-Meanwhile, `dep` team is working on a more general solution to the constraints
-on transitive dependencies problem: https://github.com/golang/dep/issues/1124.
-
-
Usage
-----
-### v1
-
-For basic usage, it is as simple as getting your input into a byte
-slice and calling:
-
- output := blackfriday.MarkdownBasic(input)
-
-This renders it with no extensions enabled. To get a more useful
-feature set, use this instead:
-
- output := blackfriday.MarkdownCommon(input)
-
-### v2
-
For the most sensible markdown processing, it is as simple as getting your input
into a byte slice and calling:
@@ -121,7 +85,7 @@ Here's an example of simple usage of Blackfriday together with Bluemonday:
```go
import (
"github.com/microcosm-cc/bluemonday"
- "gopkg.in/russross/blackfriday.v2"
+ "github.com/russross/blackfriday"
)
// ...
@@ -129,21 +93,11 @@ unsafe := blackfriday.Run(input)
html := bluemonday.UGCPolicy().SanitizeBytes(unsafe)
```
-### Custom options, v1
-
-If you want to customize the set of options, first get a renderer
-(currently only the HTML output engine), then use it to
-call the more general `Markdown` function. For examples, see the
-implementations of `MarkdownBasic` and `MarkdownCommon` in
-`markdown.go`.
-
-### Custom options, v2
+### Custom options
If you want to customize the set of options, use `blackfriday.WithExtensions`,
`blackfriday.WithRenderer` and `blackfriday.WithRefOverride`.
-### `blackfriday-tool`
-
You can also check out `blackfriday-tool` for a more complete example
of how to use it. Download and install it using:
@@ -163,22 +117,6 @@ installed in `$GOPATH/bin`. This is a statically-linked binary that
can be copied to wherever you need it without worrying about
dependencies and library versions.
-### Sanitized anchor names
-
-Blackfriday includes an algorithm for creating sanitized anchor names
-corresponding to a given input text. This algorithm is used to create
-anchors for headings when `EXTENSION_AUTO_HEADER_IDS` is enabled. The
-algorithm has a specification, so that other packages can create
-compatible anchor names and links to those anchors.
-
-The specification is located at https://godoc.org/github.com/russross/blackfriday#hdr-Sanitized_Anchor_Names.
-
-[`SanitizedAnchorName`](https://godoc.org/github.com/russross/blackfriday#SanitizedAnchorName) exposes this functionality, and can be used to
-create compatible links to the anchor names generated by blackfriday.
-This algorithm is also implemented in a small standalone package at
-[`github.com/shurcooL/sanitized_anchor_name`](https://godoc.org/github.com/shurcooL/sanitized_anchor_name). It can be useful for clients
-that want a small package and don't need full functionality of blackfriday.
-
Features
--------
@@ -246,7 +184,7 @@ implements the following extensions:
and supply a language (to make syntax highlighting simple). Just
mark it like this:
- ``` go
+ ```go
func getTrue() bool {
return true
}
@@ -255,15 +193,6 @@ implements the following extensions:
You can use 3 or more backticks to mark the beginning of the
block, and the same number to mark the end of the block.
- To preserve classes of fenced code blocks while using the bluemonday
- HTML sanitizer, use the following policy:
-
- ``` go
- p := bluemonday.UGCPolicy()
- p.AllowAttrs("class").Matching(regexp.MustCompile("^language-[a-zA-Z0-9]+$")).OnElements("code")
- html := p.SanitizeBytes(unsafe)
- ```
-
* **Definition lists**. A simple definition list is made of a single-line
term followed by a colon and the definition for that term.
@@ -289,10 +218,8 @@ implements the following extensions:
* **Strikethrough**. Use two tildes (`~~`) to mark text that
should be crossed out.
-* **Hard line breaks**. With this extension enabled (it is off by
- default in the `MarkdownBasic` and `MarkdownCommon` convenience
- functions), newlines in the input translate into line breaks in
- the output.
+* **Hard line breaks**. With this extension enabled newlines in the input
+ translate into line breaks in the output. This extension is off by default.
* **Smart quotes**. Smartypants-style punctuation substitution is
supported, turning normal double- and single-quote marks into
@@ -332,14 +259,14 @@ are a few of note:
renders output as LaTeX.
-TODO
+Todo
----
* More unit testing
-* Improve Unicode support. It does not understand all Unicode
+* Improve unicode support. It does not understand all unicode
rules (about what constitutes a letter, a punctuation symbol,
etc.), so it may fail to detect word boundaries correctly in
- some instances. It is safe on all UTF-8 input.
+ some instances. It is safe on all utf-8 input.
License
@@ -354,10 +281,3 @@ License
[4]: https://godoc.org/gopkg.in/russross/blackfriday.v2#Parse "Parse func"
[5]: https://github.com/microcosm-cc/bluemonday "Bluemonday"
[6]: https://labix.org/gopkg.in "gopkg.in"
- [7]: https://github.com/golang/dep/ "dep"
- [8]: https://github.com/Masterminds/glide "Glide"
-
- [BuildSVG]: https://travis-ci.org/russross/blackfriday.svg?branch=master
- [BuildURL]: https://travis-ci.org/russross/blackfriday
- [GodocV2SVG]: https://godoc.org/gopkg.in/russross/blackfriday.v2?status.svg
- [GodocV2URL]: https://godoc.org/gopkg.in/russross/blackfriday.v2
diff --git a/vendor/github.com/russross/blackfriday/block.go b/vendor/github.com/russross/blackfriday/block.go
index 7fc731d5..d7da33f2 100644
--- a/vendor/github.com/russross/blackfriday/block.go
+++ b/vendor/github.com/russross/blackfriday/block.go
@@ -15,17 +15,26 @@ package blackfriday
import (
"bytes"
- "unicode"
+ "html"
+ "regexp"
+
+ "github.com/shurcooL/sanitized_anchor_name"
+)
+
+const (
+ charEntity = "&(?:#x[a-f0-9]{1,8}|#[0-9]{1,8}|[a-z][a-z0-9]{1,31});"
+ escapable = "[!\"#$%&'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]"
+)
+
+var (
+ reBackslashOrAmp = regexp.MustCompile("[\\&]")
+ reEntityOrEscapedChar = regexp.MustCompile("(?i)\\\\" + escapable + "|" + charEntity)
)
// Parse block-level data.
// Note: this function and many that it calls assume that
// the input buffer ends with a newline.
-func (p *parser) block(out *bytes.Buffer, data []byte) {
- if len(data) == 0 || data[len(data)-1] != '\n' {
- panic("block input is missing terminating newline")
- }
-
+func (p *Markdown) block(data []byte) {
// this is called recursively: enforce a maximum depth
if p.nesting >= p.maxNesting {
return
@@ -34,14 +43,14 @@ func (p *parser) block(out *bytes.Buffer, data []byte) {
// parse out one block-level construct at a time
for len(data) > 0 {
- // prefixed header:
+ // prefixed heading:
//
- // # Header 1
- // ## Header 2
+ // # Heading 1
+ // ## Heading 2
// ...
- // ###### Header 6
- if p.isPrefixHeader(data) {
- data = data[p.prefixHeader(out, data):]
+ // ###### Heading 6
+ if p.isPrefixHeading(data) {
+ data = data[p.prefixHeading(data):]
continue
}
@@ -51,7 +60,7 @@ func (p *parser) block(out *bytes.Buffer, data []byte) {
// ...
//
if data[0] == '<' {
- if i := p.html(out, data, true); i > 0 {
+ if i := p.html(data, true); i > 0 {
data = data[i:]
continue
}
@@ -62,9 +71,9 @@ func (p *parser) block(out *bytes.Buffer, data []byte) {
// % stuff
// % more stuff
// % even more stuff
- if p.flags&EXTENSION_TITLEBLOCK != 0 {
+ if p.extensions&Titleblock != 0 {
if data[0] == '%' {
- if i := p.titleBlock(out, data, true); i > 0 {
+ if i := p.titleBlock(data, true); i > 0 {
data = data[i:]
continue
}
@@ -86,7 +95,7 @@ func (p *parser) block(out *bytes.Buffer, data []byte) {
// return b
// }
if p.codePrefix(data) > 0 {
- data = data[p.code(out, data):]
+ data = data[p.code(data):]
continue
}
@@ -100,8 +109,8 @@ func (p *parser) block(out *bytes.Buffer, data []byte) {
// return n * fact(n-1)
// }
// ```
- if p.flags&EXTENSION_FENCED_CODE != 0 {
- if i := p.fencedCodeBlock(out, data, true); i > 0 {
+ if p.extensions&FencedCode != 0 {
+ if i := p.fencedCodeBlock(data, true); i > 0 {
data = data[i:]
continue
}
@@ -115,9 +124,9 @@ func (p *parser) block(out *bytes.Buffer, data []byte) {
// or
// ______
if p.isHRule(data) {
- p.r.HRule(out)
+ p.addBlock(HorizontalRule, nil)
var i int
- for i = 0; data[i] != '\n'; i++ {
+ for i = 0; i < len(data) && data[i] != '\n'; i++ {
}
data = data[i:]
continue
@@ -128,7 +137,7 @@ func (p *parser) block(out *bytes.Buffer, data []byte) {
// > A big quote I found somewhere
// > on the web
if p.quotePrefix(data) > 0 {
- data = data[p.quote(out, data):]
+ data = data[p.quote(data):]
continue
}
@@ -138,8 +147,8 @@ func (p *parser) block(out *bytes.Buffer, data []byte) {
// ------|-----|---------
// Bob | 31 | 555-1234
// Alice | 27 | 555-4321
- if p.flags&EXTENSION_TABLES != 0 {
- if i := p.table(out, data); i > 0 {
+ if p.extensions&Tables != 0 {
+ if i := p.table(data); i > 0 {
data = data[i:]
continue
}
@@ -152,7 +161,7 @@ func (p *parser) block(out *bytes.Buffer, data []byte) {
//
// also works with + or -
if p.uliPrefix(data) > 0 {
- data = data[p.list(out, data, 0):]
+ data = data[p.list(data, 0):]
continue
}
@@ -161,7 +170,7 @@ func (p *parser) block(out *bytes.Buffer, data []byte) {
// 1. Item 1
// 2. Item 2
if p.oliPrefix(data) > 0 {
- data = data[p.list(out, data, LIST_TYPE_ORDERED):]
+ data = data[p.list(data, ListTypeOrdered):]
continue
}
@@ -173,55 +182,62 @@ func (p *parser) block(out *bytes.Buffer, data []byte) {
//
// Term 2
// : Definition c
- if p.flags&EXTENSION_DEFINITION_LISTS != 0 {
+ if p.extensions&DefinitionLists != 0 {
if p.dliPrefix(data) > 0 {
- data = data[p.list(out, data, LIST_TYPE_DEFINITION):]
+ data = data[p.list(data, ListTypeDefinition):]
continue
}
}
// anything else must look like a normal paragraph
- // note: this finds underlined headers, too
- data = data[p.paragraph(out, data):]
+ // note: this finds underlined headings, too
+ data = data[p.paragraph(data):]
}
p.nesting--
}
-func (p *parser) isPrefixHeader(data []byte) bool {
+func (p *Markdown) addBlock(typ NodeType, content []byte) *Node {
+ p.closeUnmatchedBlocks()
+ container := p.addChild(typ, 0)
+ container.content = content
+ return container
+}
+
+func (p *Markdown) isPrefixHeading(data []byte) bool {
if data[0] != '#' {
return false
}
- if p.flags&EXTENSION_SPACE_HEADERS != 0 {
+ if p.extensions&SpaceHeadings != 0 {
level := 0
- for level < 6 && data[level] == '#' {
+ for level < 6 && level < len(data) && data[level] == '#' {
level++
}
- if data[level] != ' ' {
+ if level == len(data) || data[level] != ' ' {
return false
}
}
return true
}
-func (p *parser) prefixHeader(out *bytes.Buffer, data []byte) int {
+func (p *Markdown) prefixHeading(data []byte) int {
level := 0
- for level < 6 && data[level] == '#' {
+ for level < 6 && level < len(data) && data[level] == '#' {
level++
}
i := skipChar(data, level, ' ')
end := skipUntilChar(data, i, '\n')
skip := end
id := ""
- if p.flags&EXTENSION_HEADER_IDS != 0 {
+ if p.extensions&HeadingIDs != 0 {
j, k := 0, 0
- // find start/end of header id
+ // find start/end of heading id
for j = i; j < end-1 && (data[j] != '{' || data[j+1] != '#'); j++ {
}
for k = j + 1; k < end && data[k] != '}'; k++ {
}
- // extract header id iff found
+ // extract heading id iff found
if j < end && k < end {
id = string(data[j+2 : k])
end = j
@@ -241,45 +257,41 @@ func (p *parser) prefixHeader(out *bytes.Buffer, data []byte) int {
end--
}
if end > i {
- if id == "" && p.flags&EXTENSION_AUTO_HEADER_IDS != 0 {
- id = SanitizedAnchorName(string(data[i:end]))
+ if id == "" && p.extensions&AutoHeadingIDs != 0 {
+ id = sanitized_anchor_name.Create(string(data[i:end]))
}
- work := func() bool {
- p.inline(out, data[i:end])
- return true
- }
- p.r.Header(out, work, level, id)
+ block := p.addBlock(Heading, data[i:end])
+ block.HeadingID = id
+ block.Level = level
}
return skip
}
-func (p *parser) isUnderlinedHeader(data []byte) int {
- // test of level 1 header
+func (p *Markdown) isUnderlinedHeading(data []byte) int {
+ // test of level 1 heading
if data[0] == '=' {
i := skipChar(data, 1, '=')
i = skipChar(data, i, ' ')
- if data[i] == '\n' {
+ if i < len(data) && data[i] == '\n' {
return 1
- } else {
- return 0
}
+ return 0
}
- // test of level 2 header
+ // test of level 2 heading
if data[0] == '-' {
i := skipChar(data, 1, '-')
i = skipChar(data, i, ' ')
- if data[i] == '\n' {
+ if i < len(data) && data[i] == '\n' {
return 2
- } else {
- return 0
}
+ return 0
}
return 0
}
-func (p *parser) titleBlock(out *bytes.Buffer, data []byte, doRender bool) int {
+func (p *Markdown) titleBlock(data []byte, doRender bool) int {
if data[0] != '%' {
return 0
}
@@ -293,12 +305,17 @@ func (p *parser) titleBlock(out *bytes.Buffer, data []byte, doRender bool) int {
}
data = bytes.Join(splitData[0:i], []byte("\n"))
- p.r.TitleBlock(out, data)
+ consumed := len(data)
+ data = bytes.TrimPrefix(data, []byte("% "))
+ data = bytes.Replace(data, []byte("\n% "), []byte("\n"), -1)
+ block := p.addBlock(Heading, data)
+ block.Level = 1
+ block.IsTitleblock = true
- return len(data)
+ return consumed
}
-func (p *parser) html(out *bytes.Buffer, data []byte, doRender bool) int {
+func (p *Markdown) html(data []byte, doRender bool) int {
var i, j int
// identify the opening tag
@@ -310,17 +327,12 @@ func (p *parser) html(out *bytes.Buffer, data []byte, doRender bool) int {
// handle special cases
if !tagfound {
// check for an HTML comment
- if size := p.htmlComment(out, data, doRender); size > 0 {
+ if size := p.htmlComment(data, doRender); size > 0 {
return size
}
// check for an
tag
- if size := p.htmlHr(out, data, doRender); size > 0 {
- return size
- }
-
- // check for HTML CDATA
- if size := p.htmlCDATA(out, data, doRender); size > 0 {
+ if size := p.htmlHr(data, doRender); size > 0 {
return size
}
@@ -395,60 +407,42 @@ func (p *parser) html(out *bytes.Buffer, data []byte, doRender bool) int {
for end > 0 && data[end-1] == '\n' {
end--
}
- p.r.BlockHtml(out, data[:end])
+ finalizeHTMLBlock(p.addBlock(HTMLBlock, data[:end]))
}
return i
}
-func (p *parser) renderHTMLBlock(out *bytes.Buffer, data []byte, start int, doRender bool) int {
- // html block needs to end with a blank line
- if i := p.isEmpty(data[start:]); i > 0 {
- size := start + i
+func finalizeHTMLBlock(block *Node) {
+ block.Literal = block.content
+ block.content = nil
+}
+
+// HTML comment, lax form
+func (p *Markdown) htmlComment(data []byte, doRender bool) int {
+ i := p.inlineHTMLComment(data)
+ // needs to end with a blank line
+ if j := p.isEmpty(data[i:]); j > 0 {
+ size := i + j
if doRender {
// trim trailing newlines
end := size
for end > 0 && data[end-1] == '\n' {
end--
}
- p.r.BlockHtml(out, data[:end])
+ block := p.addBlock(HTMLBlock, data[:end])
+ finalizeHTMLBlock(block)
}
return size
}
return 0
}
-// HTML comment, lax form
-func (p *parser) htmlComment(out *bytes.Buffer, data []byte, doRender bool) int {
- i := p.inlineHTMLComment(out, data)
- return p.renderHTMLBlock(out, data, i, doRender)
-}
-
-// HTML CDATA section
-func (p *parser) htmlCDATA(out *bytes.Buffer, data []byte, doRender bool) int {
- const cdataTag = "') {
- i++
- }
- i++
- // no end-of-comment marker
- if i >= len(data) {
- return 0
- }
- return p.renderHTMLBlock(out, data, i, doRender)
-}
-
// HR, which is the only self-closing block tag considered
-func (p *parser) htmlHr(out *bytes.Buffer, data []byte, doRender bool) int {
+func (p *Markdown) htmlHr(data []byte, doRender bool) int {
+ if len(data) < 4 {
+ return 0
+ }
if data[0] != '<' || (data[1] != 'h' && data[1] != 'H') || (data[2] != 'r' && data[2] != 'R') {
return 0
}
@@ -456,22 +450,31 @@ func (p *parser) htmlHr(out *bytes.Buffer, data []byte, doRender bool) int {
// not an
tag after all; at least not a valid one
return 0
}
-
i := 3
- for data[i] != '>' && data[i] != '\n' {
+ for i < len(data) && data[i] != '>' && data[i] != '\n' {
i++
}
-
- if data[i] == '>' {
- return p.renderHTMLBlock(out, data, i+1, doRender)
+ if i < len(data) && data[i] == '>' {
+ i++
+ if j := p.isEmpty(data[i:]); j > 0 {
+ size := i + j
+ if doRender {
+ // trim newlines
+ end := size
+ for end > 0 && data[end-1] == '\n' {
+ end--
+ }
+ finalizeHTMLBlock(p.addBlock(HTMLBlock, data[:end]))
+ }
+ return size
+ }
}
-
return 0
}
-func (p *parser) htmlFindTag(data []byte) (string, bool) {
+func (p *Markdown) htmlFindTag(data []byte) (string, bool) {
i := 0
- for isalnum(data[i]) {
+ for i < len(data) && isalnum(data[i]) {
i++
}
key := string(data[:i])
@@ -481,9 +484,11 @@ func (p *parser) htmlFindTag(data []byte) (string, bool) {
return "", false
}
-func (p *parser) htmlFindEnd(tag string, data []byte) int {
+func (p *Markdown) htmlFindEnd(tag string, data []byte) int {
// assume data[0] == '<' && data[1] == '/' already tested
-
+ if tag == "hr" {
+ return 2
+ }
// check if tag is a match
closetag := []byte("" + tag + ">")
if !bytes.HasPrefix(data, closetag) {
@@ -503,7 +508,7 @@ func (p *parser) htmlFindEnd(tag string, data []byte) int {
return i
}
- if p.flags&EXTENSION_LAX_HTML_BLOCKS != 0 {
+ if p.extensions&LaxHTMLBlocks != 0 {
return i
}
if skip = p.isEmpty(data[i:]); skip == 0 {
@@ -514,7 +519,7 @@ func (p *parser) htmlFindEnd(tag string, data []byte) int {
return i + skip
}
-func (*parser) isEmpty(data []byte) int {
+func (*Markdown) isEmpty(data []byte) int {
// it is okay to call isEmpty on an empty buffer
if len(data) == 0 {
return 0
@@ -526,10 +531,13 @@ func (*parser) isEmpty(data []byte) int {
return 0
}
}
- return i + 1
+ if i < len(data) && data[i] == '\n' {
+ i++
+ }
+ return i
}
-func (*parser) isHRule(data []byte) bool {
+func (*Markdown) isHRule(data []byte) bool {
i := 0
// skip up to three spaces
@@ -545,7 +553,7 @@ func (*parser) isHRule(data []byte) bool {
// the whole line must be the char or whitespace
n := 0
- for data[i] != '\n' {
+ for i < len(data) && data[i] != '\n' {
switch {
case data[i] == c:
n++
@@ -561,8 +569,7 @@ func (*parser) isHRule(data []byte) bool {
// isFenceLine checks if there's a fence line (e.g., ``` or ``` go) at the beginning of data,
// and returns the end index if so, or 0 otherwise. It also returns the marker found.
// If syntax is not nil, it gets set to the syntax specified in the fence line.
-// A final newline is mandatory to recognize the fence line, unless newlineOptional is true.
-func isFenceLine(data []byte, syntax *string, oldmarker string, newlineOptional bool) (end int, marker string) {
+func isFenceLine(data []byte, syntax *string, oldmarker string) (end int, marker string) {
i, size := 0, 0
// skip up to three spaces
@@ -604,7 +611,7 @@ func isFenceLine(data []byte, syntax *string, oldmarker string, newlineOptional
i = skipChar(data, i, ' ')
if i >= len(data) {
- if newlineOptional && i == len(data) {
+ if i == len(data) {
return i, marker
}
return 0, ""
@@ -649,33 +656,33 @@ func isFenceLine(data []byte, syntax *string, oldmarker string, newlineOptional
i = skipChar(data, i, ' ')
if i >= len(data) || data[i] != '\n' {
- if newlineOptional && i == len(data) {
+ if i == len(data) {
return i, marker
}
return 0, ""
}
-
return i + 1, marker // Take newline into account.
}
// fencedCodeBlock returns the end index if data contains a fenced code block at the beginning,
// or 0 otherwise. It writes to out if doRender is true, otherwise it has no side effects.
// If doRender is true, a final newline is mandatory to recognize the fenced code block.
-func (p *parser) fencedCodeBlock(out *bytes.Buffer, data []byte, doRender bool) int {
+func (p *Markdown) fencedCodeBlock(data []byte, doRender bool) int {
var syntax string
- beg, marker := isFenceLine(data, &syntax, "", false)
+ beg, marker := isFenceLine(data, &syntax, "")
if beg == 0 || beg >= len(data) {
return 0
}
var work bytes.Buffer
+ work.Write([]byte(syntax))
+ work.WriteByte('\n')
for {
// safe to assume beg < len(data)
// check for the end of the code block
- newlineOptional := !doRender
- fenceEnd, _ := isFenceLine(data[beg:], nil, marker, newlineOptional)
+ fenceEnd, _ := isFenceLine(data[beg:], nil, marker)
if fenceEnd != 0 {
beg += fenceEnd
break
@@ -697,24 +704,55 @@ func (p *parser) fencedCodeBlock(out *bytes.Buffer, data []byte, doRender bool)
}
if doRender {
- p.r.BlockCode(out, work.Bytes(), syntax)
+ block := p.addBlock(CodeBlock, work.Bytes()) // TODO: get rid of temp buffer
+ block.IsFenced = true
+ finalizeCodeBlock(block)
}
return beg
}
-func (p *parser) table(out *bytes.Buffer, data []byte) int {
- var header bytes.Buffer
- i, columns := p.tableHeader(&header, data)
+func unescapeChar(str []byte) []byte {
+ if str[0] == '\\' {
+ return []byte{str[1]}
+ }
+ return []byte(html.UnescapeString(string(str)))
+}
+
+func unescapeString(str []byte) []byte {
+ if reBackslashOrAmp.Match(str) {
+ return reEntityOrEscapedChar.ReplaceAllFunc(str, unescapeChar)
+ }
+ return str
+}
+
+func finalizeCodeBlock(block *Node) {
+ if block.IsFenced {
+ newlinePos := bytes.IndexByte(block.content, '\n')
+ firstLine := block.content[:newlinePos]
+ rest := block.content[newlinePos+1:]
+ block.Info = unescapeString(bytes.Trim(firstLine, "\n"))
+ block.Literal = rest
+ } else {
+ block.Literal = block.content
+ }
+ block.content = nil
+}
+
+func (p *Markdown) table(data []byte) int {
+ table := p.addBlock(Table, nil)
+ i, columns := p.tableHeader(data)
if i == 0 {
+ p.tip = table.Parent
+ table.Unlink()
return 0
}
- var body bytes.Buffer
+ p.addBlock(TableBody, nil)
for i < len(data) {
pipes, rowStart := 0, i
- for ; data[i] != '\n'; i++ {
+ for ; i < len(data) && data[i] != '\n'; i++ {
if data[i] == '|' {
pipes++
}
@@ -726,12 +764,12 @@ func (p *parser) table(out *bytes.Buffer, data []byte) int {
}
// include the newline in data sent to tableRow
- i++
- p.tableRow(&body, data[rowStart:i], columns, false)
+ if i < len(data) && data[i] == '\n' {
+ i++
+ }
+ p.tableRow(data[rowStart:i], columns, false)
}
- p.r.Table(out, header.Bytes(), body.Bytes(), columns)
-
return i
}
@@ -744,10 +782,10 @@ func isBackslashEscaped(data []byte, i int) bool {
return backslashes&1 == 1
}
-func (p *parser) tableHeader(out *bytes.Buffer, data []byte) (size int, columns []int) {
+func (p *Markdown) tableHeader(data []byte) (size int, columns []CellAlignFlags) {
i := 0
colCount := 1
- for i = 0; data[i] != '\n'; i++ {
+ for i = 0; i < len(data) && data[i] != '\n'; i++ {
if data[i] == '|' && !isBackslashEscaped(data, i) {
colCount++
}
@@ -759,7 +797,11 @@ func (p *parser) tableHeader(out *bytes.Buffer, data []byte) (size int, columns
}
// include the newline in the data sent to tableRow
- header := data[:i+1]
+ j := i
+ if j < len(data) && data[j] == '\n' {
+ j++
+ }
+ header := data[:j]
// column count ignores pipes at beginning or end of line
if data[0] == '|' {
@@ -769,7 +811,7 @@ func (p *parser) tableHeader(out *bytes.Buffer, data []byte) (size int, columns
colCount--
}
- columns = make([]int, colCount)
+ columns = make([]CellAlignFlags, colCount)
// move on to the header underline
i++
@@ -785,27 +827,29 @@ func (p *parser) tableHeader(out *bytes.Buffer, data []byte) (size int, columns
// each column header is of form: / *:?-+:? *|/ with # dashes + # colons >= 3
// and trailing | optional on last column
col := 0
- for data[i] != '\n' {
+ for i < len(data) && data[i] != '\n' {
dashes := 0
if data[i] == ':' {
i++
- columns[col] |= TABLE_ALIGNMENT_LEFT
+ columns[col] |= TableAlignmentLeft
dashes++
}
- for data[i] == '-' {
+ for i < len(data) && data[i] == '-' {
i++
dashes++
}
- if data[i] == ':' {
+ if i < len(data) && data[i] == ':' {
i++
- columns[col] |= TABLE_ALIGNMENT_RIGHT
+ columns[col] |= TableAlignmentRight
dashes++
}
- for data[i] == ' ' {
+ for i < len(data) && data[i] == ' ' {
i++
}
-
+ if i == len(data) {
+ return
+ }
// end of column test is messy
switch {
case dashes < 3:
@@ -816,12 +860,12 @@ func (p *parser) tableHeader(out *bytes.Buffer, data []byte) (size int, columns
// marker found, now skip past trailing whitespace
col++
i++
- for data[i] == ' ' {
+ for i < len(data) && data[i] == ' ' {
i++
}
// trailing junk found after last column
- if col >= colCount && data[i] != '\n' {
+ if col >= colCount && i < len(data) && data[i] != '\n' {
return
}
@@ -842,27 +886,31 @@ func (p *parser) tableHeader(out *bytes.Buffer, data []byte) (size int, columns
return
}
- p.tableRow(out, header, columns, true)
- size = i + 1
+ p.addBlock(TableHead, nil)
+ p.tableRow(header, columns, true)
+ size = i
+ if size < len(data) && data[size] == '\n' {
+ size++
+ }
return
}
-func (p *parser) tableRow(out *bytes.Buffer, data []byte, columns []int, header bool) {
+func (p *Markdown) tableRow(data []byte, columns []CellAlignFlags, header bool) {
+ p.addBlock(TableRow, nil)
i, col := 0, 0
- var rowWork bytes.Buffer
if data[i] == '|' && !isBackslashEscaped(data, i) {
i++
}
for col = 0; col < len(columns) && i < len(data); col++ {
- for data[i] == ' ' {
+ for i < len(data) && data[i] == ' ' {
i++
}
cellStart := i
- for (data[i] != '|' || isBackslashEscaped(data, i)) && data[i] != '\n' {
+ for i < len(data) && (data[i] != '|' || isBackslashEscaped(data, i)) && data[i] != '\n' {
i++
}
@@ -871,42 +919,33 @@ func (p *parser) tableRow(out *bytes.Buffer, data []byte, columns []int, header
// skip the end-of-cell marker, possibly taking us past end of buffer
i++
- for cellEnd > cellStart && data[cellEnd-1] == ' ' {
+ for cellEnd > cellStart && cellEnd-1 < len(data) && data[cellEnd-1] == ' ' {
cellEnd--
}
- var cellWork bytes.Buffer
- p.inline(&cellWork, data[cellStart:cellEnd])
-
- if header {
- p.r.TableHeaderCell(&rowWork, cellWork.Bytes(), columns[col])
- } else {
- p.r.TableCell(&rowWork, cellWork.Bytes(), columns[col])
- }
+ cell := p.addBlock(TableCell, data[cellStart:cellEnd])
+ cell.IsHeader = header
+ cell.Align = columns[col]
}
// pad it out with empty columns to get the right number
for ; col < len(columns); col++ {
- if header {
- p.r.TableHeaderCell(&rowWork, nil, columns[col])
- } else {
- p.r.TableCell(&rowWork, nil, columns[col])
- }
+ cell := p.addBlock(TableCell, nil)
+ cell.IsHeader = header
+ cell.Align = columns[col]
}
// silently ignore rows with too many cells
-
- p.r.TableRow(out, rowWork.Bytes())
}
// returns blockquote prefix length
-func (p *parser) quotePrefix(data []byte) int {
+func (p *Markdown) quotePrefix(data []byte) int {
i := 0
- for i < 3 && data[i] == ' ' {
+ for i < 3 && i < len(data) && data[i] == ' ' {
i++
}
- if data[i] == '>' {
- if data[i+1] == ' ' {
+ if i < len(data) && data[i] == '>' {
+ if i+1 < len(data) && data[i+1] == ' ' {
return i + 2
}
return i + 1
@@ -916,7 +955,7 @@ func (p *parser) quotePrefix(data []byte) int {
// blockquote ends with at least one blank line
// followed by something without a blockquote prefix
-func (p *parser) terminateBlockquote(data []byte, beg, end int) bool {
+func (p *Markdown) terminateBlockquote(data []byte, beg, end int) bool {
if p.isEmpty(data[beg:]) <= 0 {
return false
}
@@ -927,7 +966,8 @@ func (p *parser) terminateBlockquote(data []byte, beg, end int) bool {
}
// parse a blockquote fragment
-func (p *parser) quote(out *bytes.Buffer, data []byte) int {
+func (p *Markdown) quote(data []byte) int {
+ block := p.addBlock(BlockQuote, nil)
var raw bytes.Buffer
beg, end := 0, 0
for beg < len(data) {
@@ -935,9 +975,9 @@ func (p *parser) quote(out *bytes.Buffer, data []byte) int {
// Step over whole lines, collecting them. While doing that, check for
// fenced code and if one's found, incorporate it altogether,
// irregardless of any contents inside it
- for data[end] != '\n' {
- if p.flags&EXTENSION_FENCED_CODE != 0 {
- if i := p.fencedCodeBlock(out, data[end:], false); i > 0 {
+ for end < len(data) && data[end] != '\n' {
+ if p.extensions&FencedCode != 0 {
+ if i := p.fencedCodeBlock(data[end:], false); i > 0 {
// -1 to compensate for the extra end++ after the loop:
end += i - 1
break
@@ -945,44 +985,47 @@ func (p *parser) quote(out *bytes.Buffer, data []byte) int {
}
end++
}
- end++
-
+ if end < len(data) && data[end] == '\n' {
+ end++
+ }
if pre := p.quotePrefix(data[beg:]); pre > 0 {
// skip the prefix
beg += pre
} else if p.terminateBlockquote(data, beg, end) {
break
}
-
// this line is part of the blockquote
raw.Write(data[beg:end])
beg = end
}
-
- var cooked bytes.Buffer
- p.block(&cooked, raw.Bytes())
- p.r.BlockQuote(out, cooked.Bytes())
+ p.block(raw.Bytes())
+ p.finalize(block)
return end
}
// returns prefix length for block code
-func (p *parser) codePrefix(data []byte) int {
- if data[0] == ' ' && data[1] == ' ' && data[2] == ' ' && data[3] == ' ' {
+func (p *Markdown) codePrefix(data []byte) int {
+ if len(data) >= 1 && data[0] == '\t' {
+ return 1
+ }
+ if len(data) >= 4 && data[0] == ' ' && data[1] == ' ' && data[2] == ' ' && data[3] == ' ' {
return 4
}
return 0
}
-func (p *parser) code(out *bytes.Buffer, data []byte) int {
+func (p *Markdown) code(data []byte) int {
var work bytes.Buffer
i := 0
for i < len(data) {
beg := i
- for data[i] != '\n' {
+ for i < len(data) && data[i] != '\n' {
+ i++
+ }
+ if i < len(data) && data[i] == '\n' {
i++
}
- i++
blankline := p.isEmpty(data[beg:i]) > 0
if pre := p.codePrefix(data[beg:i]); pre > 0 {
@@ -993,7 +1036,7 @@ func (p *parser) code(out *bytes.Buffer, data []byte) int {
break
}
- // verbatim copy to the working buffeu
+ // verbatim copy to the working buffer
if blankline {
work.WriteByte('\n')
} else {
@@ -1013,122 +1056,183 @@ func (p *parser) code(out *bytes.Buffer, data []byte) int {
work.WriteByte('\n')
- p.r.BlockCode(out, work.Bytes(), "")
+ block := p.addBlock(CodeBlock, work.Bytes()) // TODO: get rid of temp buffer
+ block.IsFenced = false
+ finalizeCodeBlock(block)
return i
}
// returns unordered list item prefix
-func (p *parser) uliPrefix(data []byte) int {
+func (p *Markdown) uliPrefix(data []byte) int {
i := 0
-
// start with up to 3 spaces
- for i < 3 && data[i] == ' ' {
+ for i < len(data) && i < 3 && data[i] == ' ' {
i++
}
-
- // need a *, +, or - followed by a space
+ if i >= len(data)-1 {
+ return 0
+ }
+ // need one of {'*', '+', '-'} followed by a space or a tab
if (data[i] != '*' && data[i] != '+' && data[i] != '-') ||
- data[i+1] != ' ' {
+ (data[i+1] != ' ' && data[i+1] != '\t') {
return 0
}
return i + 2
}
// returns ordered list item prefix
-func (p *parser) oliPrefix(data []byte) int {
+func (p *Markdown) oliPrefix(data []byte) int {
i := 0
// start with up to 3 spaces
- for i < 3 && data[i] == ' ' {
+ for i < 3 && i < len(data) && data[i] == ' ' {
i++
}
// count the digits
start := i
- for data[i] >= '0' && data[i] <= '9' {
+ for i < len(data) && data[i] >= '0' && data[i] <= '9' {
i++
}
+ if start == i || i >= len(data)-1 {
+ return 0
+ }
- // we need >= 1 digits followed by a dot and a space
- if start == i || data[i] != '.' || data[i+1] != ' ' {
+ // we need >= 1 digits followed by a dot and a space or a tab
+ if data[i] != '.' || !(data[i+1] == ' ' || data[i+1] == '\t') {
return 0
}
return i + 2
}
// returns definition list item prefix
-func (p *parser) dliPrefix(data []byte) int {
- i := 0
-
- // need a : followed by a spaces
- if data[i] != ':' || data[i+1] != ' ' {
+func (p *Markdown) dliPrefix(data []byte) int {
+ if len(data) < 2 {
return 0
}
- for data[i] == ' ' {
+ i := 0
+ // need a ':' followed by a space or a tab
+ if data[i] != ':' || !(data[i+1] == ' ' || data[i+1] == '\t') {
+ return 0
+ }
+ for i < len(data) && data[i] == ' ' {
i++
}
return i + 2
}
// parse ordered or unordered list block
-func (p *parser) list(out *bytes.Buffer, data []byte, flags int) int {
+func (p *Markdown) list(data []byte, flags ListType) int {
i := 0
- flags |= LIST_ITEM_BEGINNING_OF_LIST
- work := func() bool {
- for i < len(data) {
- skip := p.listItem(out, data[i:], &flags)
- i += skip
+ flags |= ListItemBeginningOfList
+ block := p.addBlock(List, nil)
+ block.ListFlags = flags
+ block.Tight = true
- if skip == 0 || flags&LIST_ITEM_END_OF_LIST != 0 {
- break
- }
- flags &= ^LIST_ITEM_BEGINNING_OF_LIST
+ for i < len(data) {
+ skip := p.listItem(data[i:], &flags)
+ if flags&ListItemContainsBlock != 0 {
+ block.ListData.Tight = false
}
- return true
+ i += skip
+ if skip == 0 || flags&ListItemEndOfList != 0 {
+ break
+ }
+ flags &= ^ListItemBeginningOfList
}
- p.r.List(out, work, flags)
+ above := block.Parent
+ finalizeList(block)
+ p.tip = above
return i
}
+// Returns true if block ends with a blank line, descending if needed
+// into lists and sublists.
+func endsWithBlankLine(block *Node) bool {
+ // TODO: figure this out. Always false now.
+ for block != nil {
+ //if block.lastLineBlank {
+ //return true
+ //}
+ t := block.Type
+ if t == List || t == Item {
+ block = block.LastChild
+ } else {
+ break
+ }
+ }
+ return false
+}
+
+func finalizeList(block *Node) {
+ block.open = false
+ item := block.FirstChild
+ for item != nil {
+ // check for non-final list item ending with blank line:
+ if endsWithBlankLine(item) && item.Next != nil {
+ block.ListData.Tight = false
+ break
+ }
+ // recurse into children of list item, to see if there are spaces
+ // between any of them:
+ subItem := item.FirstChild
+ for subItem != nil {
+ if endsWithBlankLine(subItem) && (item.Next != nil || subItem.Next != nil) {
+ block.ListData.Tight = false
+ break
+ }
+ subItem = subItem.Next
+ }
+ item = item.Next
+ }
+}
+
// Parse a single list item.
// Assumes initial prefix is already removed if this is a sublist.
-func (p *parser) listItem(out *bytes.Buffer, data []byte, flags *int) int {
+func (p *Markdown) listItem(data []byte, flags *ListType) int {
// keep track of the indentation of the first line
itemIndent := 0
- for itemIndent < 3 && data[itemIndent] == ' ' {
- itemIndent++
+ if data[0] == '\t' {
+ itemIndent += 4
+ } else {
+ for itemIndent < 3 && data[itemIndent] == ' ' {
+ itemIndent++
+ }
}
+ var bulletChar byte = '*'
i := p.uliPrefix(data)
if i == 0 {
i = p.oliPrefix(data)
+ } else {
+ bulletChar = data[i-2]
}
if i == 0 {
i = p.dliPrefix(data)
// reset definition term flag
if i > 0 {
- *flags &= ^LIST_TYPE_TERM
+ *flags &= ^ListTypeTerm
}
}
if i == 0 {
- // if in defnition list, set term flag and continue
- if *flags&LIST_TYPE_DEFINITION != 0 {
- *flags |= LIST_TYPE_TERM
+ // if in definition list, set term flag and continue
+ if *flags&ListTypeDefinition != 0 {
+ *flags |= ListTypeTerm
} else {
return 0
}
}
// skip leading whitespace on first line
- for data[i] == ' ' {
+ for i < len(data) && data[i] == ' ' {
i++
}
// find the end of the line
line := i
- for i > 0 && data[i-1] != '\n' {
+ for i > 0 && i < len(data) && data[i-1] != '\n' {
i++
}
@@ -1148,7 +1252,7 @@ gatherlines:
i++
// find the end of this line
- for data[i-1] != '\n' {
+ for i < len(data) && data[i-1] != '\n' {
i++
}
@@ -1156,18 +1260,24 @@ gatherlines:
// and move on to the next line
if p.isEmpty(data[line:i]) > 0 {
containsBlankLine = true
- raw.Write(data[line:i])
line = i
continue
}
// calculate the indentation
indent := 0
- for indent < 4 && line+indent < i && data[line+indent] == ' ' {
- indent++
+ indentIndex := 0
+ if data[line] == '\t' {
+ indentIndex++
+ indent += 4
+ } else {
+ for indent < 4 && line+indent < i && data[line+indent] == ' ' {
+ indent++
+ indentIndex++
+ }
}
- chunk := data[line+indent : i]
+ chunk := data[line+indentIndex : i]
// evaluate how this line fits in
switch {
@@ -1177,15 +1287,7 @@ gatherlines:
p.dliPrefix(chunk) > 0:
if containsBlankLine {
- // end the list if the type changed after a blank line
- if indent <= itemIndent &&
- ((*flags&LIST_TYPE_ORDERED != 0 && p.uliPrefix(chunk) > 0) ||
- (*flags&LIST_TYPE_ORDERED == 0 && p.oliPrefix(chunk) > 0)) {
-
- *flags |= LIST_ITEM_END_OF_LIST
- break gatherlines
- }
- *flags |= LIST_ITEM_CONTAINS_BLOCK
+ *flags |= ListItemContainsBlock
}
// to be a nested list, it must be indented more
@@ -1199,93 +1301,89 @@ gatherlines:
sublist = raw.Len()
}
- // is this a nested prefix header?
- case p.isPrefixHeader(chunk):
- // if the header is not indented, it is not nested in the list
+ // is this a nested prefix heading?
+ case p.isPrefixHeading(chunk):
+ // if the heading is not indented, it is not nested in the list
// and thus ends the list
if containsBlankLine && indent < 4 {
- *flags |= LIST_ITEM_END_OF_LIST
+ *flags |= ListItemEndOfList
break gatherlines
}
- *flags |= LIST_ITEM_CONTAINS_BLOCK
+ *flags |= ListItemContainsBlock
// anything following an empty line is only part
// of this item if it is indented 4 spaces
// (regardless of the indentation of the beginning of the item)
case containsBlankLine && indent < 4:
- if *flags&LIST_TYPE_DEFINITION != 0 && i < len(data)-1 {
+ if *flags&ListTypeDefinition != 0 && i < len(data)-1 {
// is the next item still a part of this list?
next := i
- for data[next] != '\n' {
+ for next < len(data) && data[next] != '\n' {
next++
}
for next < len(data)-1 && data[next] == '\n' {
next++
}
if i < len(data)-1 && data[i] != ':' && data[next] != ':' {
- *flags |= LIST_ITEM_END_OF_LIST
+ *flags |= ListItemEndOfList
}
} else {
- *flags |= LIST_ITEM_END_OF_LIST
+ *flags |= ListItemEndOfList
}
break gatherlines
// a blank line means this should be parsed as a block
case containsBlankLine:
- *flags |= LIST_ITEM_CONTAINS_BLOCK
+ raw.WriteByte('\n')
+ *flags |= ListItemContainsBlock
}
- containsBlankLine = false
+ // if this line was preceded by one or more blanks,
+ // re-introduce the blank into the buffer
+ if containsBlankLine {
+ containsBlankLine = false
+ raw.WriteByte('\n')
+ }
// add the line into the working buffer without prefix
- raw.Write(data[line+indent : i])
+ raw.Write(data[line+indentIndex : i])
line = i
}
- // If reached end of data, the Renderer.ListItem call we're going to make below
- // is definitely the last in the list.
- if line >= len(data) {
- *flags |= LIST_ITEM_END_OF_LIST
- }
-
rawBytes := raw.Bytes()
+ block := p.addBlock(Item, nil)
+ block.ListFlags = *flags
+ block.Tight = false
+ block.BulletChar = bulletChar
+ block.Delimiter = '.' // Only '.' is possible in Markdown, but ')' will also be possible in CommonMark
+
// render the contents of the list item
- var cooked bytes.Buffer
- if *flags&LIST_ITEM_CONTAINS_BLOCK != 0 && *flags&LIST_TYPE_TERM == 0 {
+ if *flags&ListItemContainsBlock != 0 && *flags&ListTypeTerm == 0 {
// intermediate render of block item, except for definition term
if sublist > 0 {
- p.block(&cooked, rawBytes[:sublist])
- p.block(&cooked, rawBytes[sublist:])
+ p.block(rawBytes[:sublist])
+ p.block(rawBytes[sublist:])
} else {
- p.block(&cooked, rawBytes)
+ p.block(rawBytes)
}
} else {
// intermediate render of inline item
if sublist > 0 {
- p.inline(&cooked, rawBytes[:sublist])
- p.block(&cooked, rawBytes[sublist:])
+ child := p.addChild(Paragraph, 0)
+ child.content = rawBytes[:sublist]
+ p.block(rawBytes[sublist:])
} else {
- p.inline(&cooked, rawBytes)
+ child := p.addChild(Paragraph, 0)
+ child.content = rawBytes
}
}
-
- // render the actual list item
- cookedBytes := cooked.Bytes()
- parsedEnd := len(cookedBytes)
-
- // strip trailing newlines
- for parsedEnd > 0 && cookedBytes[parsedEnd-1] == '\n' {
- parsedEnd--
- }
- p.r.ListItem(out, cookedBytes[:parsedEnd], *flags)
-
return line
}
// render a single paragraph that has already been parsed out
-func (p *parser) renderParagraph(out *bytes.Buffer, data []byte) {
+func (p *Markdown) renderParagraph(data []byte) {
if len(data) == 0 {
return
}
@@ -1296,27 +1394,29 @@ func (p *parser) renderParagraph(out *bytes.Buffer, data []byte) {
beg++
}
+ end := len(data)
// trim trailing newline
- end := len(data) - 1
+ if data[len(data)-1] == '\n' {
+ end--
+ }
// trim trailing spaces
for end > beg && data[end-1] == ' ' {
end--
}
- work := func() bool {
- p.inline(out, data[beg:end])
- return true
- }
- p.r.Paragraph(out, work)
+ p.addBlock(Paragraph, data[beg:end])
}
-func (p *parser) paragraph(out *bytes.Buffer, data []byte) int {
+func (p *Markdown) paragraph(data []byte) int {
// prev: index of 1st char of previous line
// line: index of 1st char of current line
// i: index of cursor/end of current line
var prev, line, i int
-
+ tabSize := TabSizeDefault
+ if p.extensions&TabSizeEight != 0 {
+ tabSize = TabSizeDouble
+ }
// keep going until we find something to mark the end of the paragraph
for i < len(data) {
// mark the beginning of the current line
@@ -1324,24 +1424,32 @@ func (p *parser) paragraph(out *bytes.Buffer, data []byte) int {
current := data[i:]
line = i
+ // did we find a reference or a footnote? If so, end a paragraph
+ // preceding it and report that we have consumed up to the end of that
+ // reference:
+ if refEnd := isReference(p, current, tabSize); refEnd > 0 {
+ p.renderParagraph(data[:i])
+ return i + refEnd
+ }
+
// did we find a blank line marking the end of the paragraph?
if n := p.isEmpty(current); n > 0 {
// did this blank line followed by a definition list item?
- if p.flags&EXTENSION_DEFINITION_LISTS != 0 {
+ if p.extensions&DefinitionLists != 0 {
if i < len(data)-1 && data[i+1] == ':' {
- return p.list(out, data[prev:], LIST_TYPE_DEFINITION)
+ return p.list(data[prev:], ListTypeDefinition)
}
}
- p.renderParagraph(out, data[:i])
+ p.renderParagraph(data[:i])
return i + n
}
- // an underline under some text marks a header, so our paragraph ended on prev line
+ // an underline under some text marks a heading, so our paragraph ended on prev line
if i > 0 {
- if level := p.isUnderlinedHeader(current); level > 0 {
+ if level := p.isUnderlinedHeading(current); level > 0 {
// render the paragraph
- p.renderParagraph(out, data[:prev])
+ p.renderParagraph(data[:prev])
// ignore leading and trailing whitespace
eol := i - 1
@@ -1352,24 +1460,17 @@ func (p *parser) paragraph(out *bytes.Buffer, data []byte) int {
eol--
}
- // render the header
- // this ugly double closure avoids forcing variables onto the heap
- work := func(o *bytes.Buffer, pp *parser, d []byte) func() bool {
- return func() bool {
- pp.inline(o, d)
- return true
- }
- }(out, p, data[prev:eol])
-
id := ""
- if p.flags&EXTENSION_AUTO_HEADER_IDS != 0 {
- id = SanitizedAnchorName(string(data[prev:eol]))
+ if p.extensions&AutoHeadingIDs != 0 {
+ id = sanitized_anchor_name.Create(string(data[prev:eol]))
}
- p.r.Header(out, work, level, id)
+ block := p.addBlock(Heading, data[prev:eol])
+ block.Level = level
+ block.HeadingID = id
// find the end of the underline
- for data[i] != '\n' {
+ for i < len(data) && data[i] != '\n' {
i++
}
return i
@@ -1377,74 +1478,72 @@ func (p *parser) paragraph(out *bytes.Buffer, data []byte) int {
}
// if the next line starts a block of HTML, then the paragraph ends here
- if p.flags&EXTENSION_LAX_HTML_BLOCKS != 0 {
- if data[i] == '<' && p.html(out, current, false) > 0 {
+ if p.extensions&LaxHTMLBlocks != 0 {
+ if data[i] == '<' && p.html(current, false) > 0 {
// rewind to before the HTML block
- p.renderParagraph(out, data[:i])
+ p.renderParagraph(data[:i])
return i
}
}
- // if there's a prefixed header or a horizontal rule after this, paragraph is over
- if p.isPrefixHeader(current) || p.isHRule(current) {
- p.renderParagraph(out, data[:i])
+ // if there's a prefixed heading or a horizontal rule after this, paragraph is over
+ if p.isPrefixHeading(current) || p.isHRule(current) {
+ p.renderParagraph(data[:i])
return i
}
// if there's a fenced code block, paragraph is over
- if p.flags&EXTENSION_FENCED_CODE != 0 {
- if p.fencedCodeBlock(out, current, false) > 0 {
- p.renderParagraph(out, data[:i])
+ if p.extensions&FencedCode != 0 {
+ if p.fencedCodeBlock(current, false) > 0 {
+ p.renderParagraph(data[:i])
return i
}
}
// if there's a definition list item, prev line is a definition term
- if p.flags&EXTENSION_DEFINITION_LISTS != 0 {
+ if p.extensions&DefinitionLists != 0 {
if p.dliPrefix(current) != 0 {
- return p.list(out, data[prev:], LIST_TYPE_DEFINITION)
+ ret := p.list(data[prev:], ListTypeDefinition)
+ return ret
}
}
// if there's a list after this, paragraph is over
- if p.flags&EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK != 0 {
+ if p.extensions&NoEmptyLineBeforeBlock != 0 {
if p.uliPrefix(current) != 0 ||
p.oliPrefix(current) != 0 ||
p.quotePrefix(current) != 0 ||
p.codePrefix(current) != 0 {
- p.renderParagraph(out, data[:i])
+ p.renderParagraph(data[:i])
return i
}
}
// otherwise, scan to the beginning of the next line
- for data[i] != '\n' {
- i++
+ nl := bytes.IndexByte(data[i:], '\n')
+ if nl >= 0 {
+ i += nl + 1
+ } else {
+ i += len(data[i:])
}
- i++
}
- p.renderParagraph(out, data[:i])
+ p.renderParagraph(data[:i])
return i
}
-// SanitizedAnchorName returns a sanitized anchor name for the given text.
-//
-// It implements the algorithm specified in the package comment.
-func SanitizedAnchorName(text string) string {
- var anchorName []rune
- futureDash := false
- for _, r := range text {
- switch {
- case unicode.IsLetter(r) || unicode.IsNumber(r):
- if futureDash && len(anchorName) > 0 {
- anchorName = append(anchorName, '-')
- }
- futureDash = false
- anchorName = append(anchorName, unicode.ToLower(r))
- default:
- futureDash = true
- }
+func skipChar(data []byte, start int, char byte) int {
+ i := start
+ for i < len(data) && data[i] == char {
+ i++
}
- return string(anchorName)
+ return i
+}
+
+func skipUntilChar(text []byte, start int, char byte) int {
+ i := start
+ for i < len(text) && text[i] != char {
+ i++
+ }
+ return i
}
diff --git a/vendor/github.com/russross/blackfriday/doc.go b/vendor/github.com/russross/blackfriday/doc.go
index 9656c42a..5b3fa987 100644
--- a/vendor/github.com/russross/blackfriday/doc.go
+++ b/vendor/github.com/russross/blackfriday/doc.go
@@ -1,32 +1,18 @@
-// Package blackfriday is a Markdown processor.
+// Package blackfriday is a markdown processor.
//
-// It translates plain text with simple formatting rules into HTML or LaTeX.
+// It translates plain text with simple formatting rules into an AST, which can
+// then be further processed to HTML (provided by Blackfriday itself) or other
+// formats (provided by the community).
//
-// Sanitized Anchor Names
+// The simplest way to invoke Blackfriday is to call the Run function. It will
+// take a text input and produce a text output in HTML (or other format).
//
-// Blackfriday includes an algorithm for creating sanitized anchor names
-// corresponding to a given input text. This algorithm is used to create
-// anchors for headings when EXTENSION_AUTO_HEADER_IDS is enabled. The
-// algorithm is specified below, so that other packages can create
-// compatible anchor names and links to those anchors.
+// A slightly more sophisticated way to use Blackfriday is to create a Markdown
+// processor and to call Parse, which returns a syntax tree for the input
+// document. You can leverage Blackfriday's parsing for content extraction from
+// markdown documents. You can assign a custom renderer and set various options
+// to the Markdown processor.
//
-// The algorithm iterates over the input text, interpreted as UTF-8,
-// one Unicode code point (rune) at a time. All runes that are letters (category L)
-// or numbers (category N) are considered valid characters. They are mapped to
-// lower case, and included in the output. All other runes are considered
-// invalid characters. Invalid characters that preceed the first valid character,
-// as well as invalid character that follow the last valid character
-// are dropped completely. All other sequences of invalid characters
-// between two valid characters are replaced with a single dash character '-'.
-//
-// SanitizedAnchorName exposes this functionality, and can be used to
-// create compatible links to the anchor names generated by blackfriday.
-// This algorithm is also implemented in a small standalone package at
-// github.com/shurcooL/sanitized_anchor_name. It can be useful for clients
-// that want a small package and don't need full functionality of blackfriday.
+// If you're interested in calling Blackfriday from command line, see
+// https://github.com/russross/blackfriday-tool.
package blackfriday
-
-// NOTE: Keep Sanitized Anchor Name algorithm in sync with package
-// github.com/shurcooL/sanitized_anchor_name.
-// Otherwise, users of sanitized_anchor_name will get anchor names
-// that are incompatible with those generated by blackfriday.
diff --git a/vendor/github.com/russross/blackfriday/esc.go b/vendor/github.com/russross/blackfriday/esc.go
new file mode 100644
index 00000000..6385f27c
--- /dev/null
+++ b/vendor/github.com/russross/blackfriday/esc.go
@@ -0,0 +1,34 @@
+package blackfriday
+
+import (
+ "html"
+ "io"
+)
+
+var htmlEscaper = [256][]byte{
+ '&': []byte("&"),
+ '<': []byte("<"),
+ '>': []byte(">"),
+ '"': []byte("""),
+}
+
+func escapeHTML(w io.Writer, s []byte) {
+ var start, end int
+ for end < len(s) {
+ escSeq := htmlEscaper[s[end]]
+ if escSeq != nil {
+ w.Write(s[start:end])
+ w.Write(escSeq)
+ start = end + 1
+ }
+ end++
+ }
+ if start < len(s) && end <= len(s) {
+ w.Write(s[start:end])
+ }
+}
+
+func escLink(w io.Writer, text []byte) {
+ unesc := html.UnescapeString(string(text))
+ escapeHTML(w, []byte(unesc))
+}
diff --git a/vendor/github.com/russross/blackfriday/html.go b/vendor/github.com/russross/blackfriday/html.go
index c917c7d3..25fb185e 100644
--- a/vendor/github.com/russross/blackfriday/html.go
+++ b/vendor/github.com/russross/blackfriday/html.go
@@ -18,46 +18,62 @@ package blackfriday
import (
"bytes"
"fmt"
+ "io"
"regexp"
- "strconv"
"strings"
)
-// Html renderer configuration options.
+// HTMLFlags control optional behavior of HTML renderer.
+type HTMLFlags int
+
+// HTML renderer configuration options.
const (
- HTML_SKIP_HTML = 1 << iota // skip preformatted HTML blocks
- HTML_SKIP_STYLE // skip embedded