parent
f30310efa6
commit
7d87ccef35
56
Gopkg.lock
generated
Normal file
56
Gopkg.lock
generated
Normal file
@ -0,0 +1,56 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/erroneousboat/termui"
|
||||
packages = ["."]
|
||||
revision = "24acd523c756fd9728824cdfac66aad9d8982fb7"
|
||||
version = "v2.2.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gorilla/websocket"
|
||||
packages = ["."]
|
||||
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/maruel/panicparse"
|
||||
packages = ["stack"]
|
||||
revision = "ad661195ed0e88491e0f14be6613304e3b1141d6"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-runewidth"
|
||||
packages = ["."]
|
||||
revision = "9e777a8366cce605130a531d2cd6363d07ad7317"
|
||||
version = "v0.0.2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/mitchellh/go-wordwrap"
|
||||
packages = ["."]
|
||||
revision = "ad45545899c7b13c020ea92b2072220eefad42b8"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/nlopes/slack"
|
||||
packages = ["."]
|
||||
revision = "8ab4d0b364ef1e9af5d102531da20d5ec902b6c4"
|
||||
version = "v0.2.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/nsf/termbox-go"
|
||||
packages = ["."]
|
||||
revision = "e2050e41c8847748ec5288741c0b19a8cb26d084"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/renstrom/fuzzysearch"
|
||||
packages = ["fuzzy"]
|
||||
revision = "d4ca9dfccd55dc6b076f9880d49c35315922c1f4"
|
||||
version = "v1.0.0"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "719f2440551009ce6f1e1a7f6e56db762acdadd9a0e05245ee427b7455e50233"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
50
Gopkg.toml
Normal file
50
Gopkg.toml
Normal file
@ -0,0 +1,50 @@
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
#
|
||||
# [prune]
|
||||
# non-go = false
|
||||
# go-tests = true
|
||||
# unused-packages = true
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/erroneousboat/termui"
|
||||
version = "2.2.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/mattn/go-runewidth"
|
||||
version = "0.0.2"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/nlopes/slack"
|
||||
version = "0.2.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/nsf/termbox-go"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/renstrom/fuzzysearch"
|
||||
version = "1.0.0"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
26
vendor/github.com/erroneousboat/termui/.gitignore
generated
vendored
Normal file
26
vendor/github.com/erroneousboat/termui/.gitignore
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
.DS_Store
|
||||
/vendor
|
6
vendor/github.com/erroneousboat/termui/.travis.yml
generated
vendored
Normal file
6
vendor/github.com/erroneousboat/termui/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- tip
|
||||
|
||||
script: go test -v ./
|
2
vendor/github.com/erroneousboat/termui/barchart.go
generated
vendored
2
vendor/github.com/erroneousboat/termui/barchart.go
generated
vendored
@ -62,7 +62,7 @@ func (bc *BarChart) layout() {
|
||||
}
|
||||
|
||||
//bc.max = bc.Data[0] // what if Data is nil? Sometimes when bar graph is nill it produces panic with panic: runtime error: index out of range
|
||||
// Assign a negative value to get maxvalue auto-populates
|
||||
// Asign a negative value to get maxvalue auto-populates
|
||||
if bc.max == 0 {
|
||||
bc.max = -1
|
||||
}
|
||||
|
11
vendor/github.com/erroneousboat/termui/events.go
generated
vendored
11
vendor/github.com/erroneousboat/termui/events.go
generated
vendored
@ -126,7 +126,7 @@ func hookTermboxEvt() {
|
||||
e := termbox.PollEvent()
|
||||
|
||||
for _, c := range sysEvtChs {
|
||||
func(ch chan Event) {
|
||||
go func(ch chan Event) {
|
||||
ch <- crtTermboxEvt(e)
|
||||
}(c)
|
||||
}
|
||||
@ -221,7 +221,6 @@ func findMatch(mux map[string]func(Event), path string) string {
|
||||
return pattern
|
||||
|
||||
}
|
||||
|
||||
// Remove all existing defined Handlers from the map
|
||||
func (es *EvtStream) ResetHandlers() {
|
||||
for Path, _ := range es.Handlers {
|
||||
@ -244,7 +243,7 @@ func (es *EvtStream) Loop() {
|
||||
case "/sig/stoploop":
|
||||
return
|
||||
}
|
||||
func(a Event) {
|
||||
go func(a Event) {
|
||||
es.RLock()
|
||||
defer es.RUnlock()
|
||||
if pattern := es.match(a.Path); pattern != "" {
|
||||
@ -274,10 +273,6 @@ func Handle(path string, handler func(Event)) {
|
||||
DefaultEvtStream.Handle(path, handler)
|
||||
}
|
||||
|
||||
func ResetHandlers() {
|
||||
DefaultEvtStream.ResetHandlers()
|
||||
}
|
||||
|
||||
func Loop() {
|
||||
DefaultEvtStream.Loop()
|
||||
}
|
||||
@ -314,7 +309,7 @@ func NewTimerCh(du time.Duration) chan Event {
|
||||
return t
|
||||
}
|
||||
|
||||
var DefaultHandler = func(e Event) {
|
||||
var DefualtHandler = func(e Event) {
|
||||
}
|
||||
|
||||
var usrEvtCh = make(chan Event)
|
||||
|
4
vendor/github.com/erroneousboat/termui/grid.go
generated
vendored
4
vendor/github.com/erroneousboat/termui/grid.go
generated
vendored
@ -48,7 +48,7 @@ func (r *Row) assignWidth(w int) {
|
||||
|
||||
accW := 0 // acc span and offset
|
||||
calcW := make([]int, len(r.Cols)) // calculated width
|
||||
calcOftX := make([]int, len(r.Cols)) // computed start position of x
|
||||
calcOftX := make([]int, len(r.Cols)) // computated start position of x
|
||||
|
||||
for i, c := range r.Cols {
|
||||
accW += c.Span + c.Offset
|
||||
@ -266,7 +266,7 @@ func (g *Grid) Align() {
|
||||
}
|
||||
}
|
||||
|
||||
// Buffer implements Bufferer interface.
|
||||
// Buffer implments Bufferer interface.
|
||||
func (g Grid) Buffer() Buffer {
|
||||
buf := NewBuffer()
|
||||
|
||||
|
2
vendor/github.com/erroneousboat/termui/helper.go
generated
vendored
2
vendor/github.com/erroneousboat/termui/helper.go
generated
vendored
@ -100,7 +100,7 @@ func charWidth(ch rune) int {
|
||||
|
||||
var whiteSpaceRegex = regexp.MustCompile(`\s`)
|
||||
|
||||
// StringToAttribute converts text to a termui attribute. You may specify more
|
||||
// StringToAttribute converts text to a termui attribute. You may specifiy more
|
||||
// then one attribute like that: "BLACK, BOLD, ...". All whitespaces
|
||||
// are ignored.
|
||||
func StringToAttribute(text string) Attribute {
|
||||
|
4
vendor/github.com/erroneousboat/termui/linechart.go
generated
vendored
4
vendor/github.com/erroneousboat/termui/linechart.go
generated
vendored
@ -35,7 +35,7 @@ var braillePatterns = map[[2]int]rune{
|
||||
var lSingleBraille = [4]rune{'\u2840', '⠄', '⠂', '⠁'}
|
||||
var rSingleBraille = [4]rune{'\u2880', '⠠', '⠐', '⠈'}
|
||||
|
||||
// LineChart has two modes: braille(default) and dot. Using braille gives 2x capacity as dot mode,
|
||||
// LineChart has two modes: braille(default) and dot. Using braille gives 2x capicity as dot mode,
|
||||
// because one braille char can represent two data points.
|
||||
/*
|
||||
lc := termui.NewLineChart()
|
||||
@ -87,7 +87,7 @@ func NewLineChart() *LineChart {
|
||||
}
|
||||
|
||||
// one cell contains two data points
|
||||
// so the capacity is 2x as dot-mode
|
||||
// so the capicity is 2x as dot-mode
|
||||
func (lc *LineChart) renderBraille() Buffer {
|
||||
buf := NewBuffer()
|
||||
|
||||
|
2
vendor/github.com/erroneousboat/termui/list.go
generated
vendored
2
vendor/github.com/erroneousboat/termui/list.go
generated
vendored
@ -14,7 +14,7 @@ import "strings"
|
||||
strs := []string{
|
||||
"[0] github.com/gizak/termui",
|
||||
"[1] editbox.go",
|
||||
"[2] interrupt.go",
|
||||
"[2] iterrupt.go",
|
||||
"[3] keyboard.go",
|
||||
"[4] output.go",
|
||||
"[5] random_out.go",
|
||||
|
8
vendor/github.com/erroneousboat/termui/mbarchart.go
generated
vendored
8
vendor/github.com/erroneousboat/termui/mbarchart.go
generated
vendored
@ -8,7 +8,7 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// This is the implementation of multi-colored or stacked bar graph. This is different from default barGraph which is implemented in bar.go
|
||||
// This is the implemetation of multi-colored or stacked bar graph. This is different from default barGraph which is implemented in bar.go
|
||||
// Multi-Colored-BarChart creates multiple bars in a widget:
|
||||
/*
|
||||
bc := termui.NewMBarChart()
|
||||
@ -72,7 +72,7 @@ func (bc *MBarChart) layout() {
|
||||
}
|
||||
bc.numStack = DataLen
|
||||
|
||||
//We need to know what is the minimum size of data array data[0] could have 10 elements data[1] could have only 5, so we plot only 5 bar graphs
|
||||
//We need to know what is the mimimum size of data array data[0] could have 10 elements data[1] could have only 5, so we plot only 5 bar graphs
|
||||
|
||||
for i := 0; i < DataLen; i++ {
|
||||
if bc.minDataLen > len(bc.Data[i]) {
|
||||
@ -90,13 +90,13 @@ func (bc *MBarChart) layout() {
|
||||
|
||||
for i := 0; i < bc.numStack; i++ {
|
||||
bc.dataNum[i] = make([][]rune, len(bc.Data[i]))
|
||||
//For each stack of bar calculate the rune
|
||||
//For each stack of bar calcualte the rune
|
||||
for j := 0; j < LabelLen && i < bc.numBar; j++ {
|
||||
n := bc.Data[i][j]
|
||||
s := fmt.Sprint(n)
|
||||
bc.dataNum[i][j] = trimStr2Runes(s, bc.BarWidth)
|
||||
}
|
||||
//If color is not defined by default then populate a color that is different from the previous bar
|
||||
//If color is not defined by default then populate a color that is different from the prevous bar
|
||||
if bc.BarColor[i] == ColorDefault && bc.NumColor[i] == ColorDefault {
|
||||
if i == 0 {
|
||||
bc.BarColor[i] = ColorBlack
|
||||
|
4
vendor/github.com/erroneousboat/termui/render.go
generated
vendored
4
vendor/github.com/erroneousboat/termui/render.go
generated
vendored
@ -35,7 +35,7 @@ func Init() error {
|
||||
}
|
||||
|
||||
sysEvtChs = make([]chan Event, 0)
|
||||
// go hookTermboxEvt()
|
||||
go hookTermboxEvt()
|
||||
|
||||
renderJobs = make(chan []Bufferer)
|
||||
//renderLock = new(sync.RWMutex)
|
||||
@ -51,7 +51,7 @@ func Init() error {
|
||||
DefaultEvtStream.Merge("timer", NewTimerCh(time.Second))
|
||||
DefaultEvtStream.Merge("custom", usrEvtCh)
|
||||
|
||||
DefaultEvtStream.Handle("/", DefaultHandler)
|
||||
DefaultEvtStream.Handle("/", DefualtHandler)
|
||||
DefaultEvtStream.Handle("/sys/wnd/resize", func(e Event) {
|
||||
w := e.Data.(EvtWnd)
|
||||
Body.Width = w.Width
|
||||
|
2
vendor/github.com/erroneousboat/termui/sparkline.go
generated
vendored
2
vendor/github.com/erroneousboat/termui/sparkline.go
generated
vendored
@ -51,7 +51,7 @@ func NewSparkline() Sparkline {
|
||||
LineColor: ThemeAttr("sparkline.line.fg")}
|
||||
}
|
||||
|
||||
// NewSparklines return a new *Sparklines with given Sparkline(s), you can always add a new Sparkline later.
|
||||
// NewSparklines return a new *Spaklines with given Sparkline(s), you can always add a new Sparkline later.
|
||||
func NewSparklines(ss ...Sparkline) *Sparklines {
|
||||
s := &Sparklines{Block: *NewBlock(), Lines: ss}
|
||||
return s
|
||||
|
25
vendor/github.com/gorilla/websocket/.gitignore
generated
vendored
Normal file
25
vendor/github.com/gorilla/websocket/.gitignore
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
|
||||
.idea/
|
||||
*.iml
|
19
vendor/github.com/gorilla/websocket/.travis.yml
generated
vendored
Normal file
19
vendor/github.com/gorilla/websocket/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
language: go
|
||||
sudo: false
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- go: 1.4
|
||||
- go: 1.5
|
||||
- go: 1.6
|
||||
- go: 1.7
|
||||
- go: 1.8
|
||||
- go: tip
|
||||
allow_failures:
|
||||
- go: tip
|
||||
|
||||
script:
|
||||
- go get -t -v ./...
|
||||
- diff -u <(echo -n) <(gofmt -d .)
|
||||
- go vet $(go list ./... | grep -v /vendor/)
|
||||
- go test -v -race ./...
|
8
vendor/github.com/gorilla/websocket/AUTHORS
generated
vendored
Normal file
8
vendor/github.com/gorilla/websocket/AUTHORS
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# This is the official list of Gorilla WebSocket authors for copyright
|
||||
# purposes.
|
||||
#
|
||||
# Please keep the list sorted.
|
||||
|
||||
Gary Burd <gary@beagledreams.com>
|
||||
Joachim Bauch <mail@joachim-bauch.de>
|
||||
|
22
vendor/github.com/gorilla/websocket/LICENSE
generated
vendored
Normal file
22
vendor/github.com/gorilla/websocket/LICENSE
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
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.
|
64
vendor/github.com/gorilla/websocket/README.md
generated
vendored
Normal file
64
vendor/github.com/gorilla/websocket/README.md
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
# Gorilla WebSocket
|
||||
|
||||
Gorilla WebSocket is a [Go](http://golang.org/) implementation of the
|
||||
[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol.
|
||||
|
||||
[![Build Status](https://travis-ci.org/gorilla/websocket.svg?branch=master)](https://travis-ci.org/gorilla/websocket)
|
||||
[![GoDoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket)
|
||||
|
||||
### Documentation
|
||||
|
||||
* [API Reference](http://godoc.org/github.com/gorilla/websocket)
|
||||
* [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat)
|
||||
* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command)
|
||||
* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo)
|
||||
* [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch)
|
||||
|
||||
### Status
|
||||
|
||||
The Gorilla WebSocket package provides a complete and tested implementation of
|
||||
the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The
|
||||
package API is stable.
|
||||
|
||||
### Installation
|
||||
|
||||
go get github.com/gorilla/websocket
|
||||
|
||||
### Protocol Compliance
|
||||
|
||||
The Gorilla WebSocket package passes the server tests in the [Autobahn Test
|
||||
Suite](http://autobahn.ws/testsuite) using the application in the [examples/autobahn
|
||||
subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn).
|
||||
|
||||
### Gorilla WebSocket compared with other packages
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th><a href="http://godoc.org/github.com/gorilla/websocket">github.com/gorilla</a></th>
|
||||
<th><a href="http://godoc.org/golang.org/x/net/websocket">golang.org/x/net</a></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr><td colspan="3"><a href="http://tools.ietf.org/html/rfc6455">RFC 6455</a> Features</td></tr>
|
||||
<tr><td>Passes <a href="http://autobahn.ws/testsuite/">Autobahn Test Suite</a></td><td><a href="https://github.com/gorilla/websocket/tree/master/examples/autobahn">Yes</a></td><td>No</td></tr>
|
||||
<tr><td>Receive <a href="https://tools.ietf.org/html/rfc6455#section-5.4">fragmented</a> message<td>Yes</td><td><a href="https://code.google.com/p/go/issues/detail?id=7632">No</a>, see note 1</td></tr>
|
||||
<tr><td>Send <a href="https://tools.ietf.org/html/rfc6455#section-5.5.1">close</a> message</td><td><a href="http://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages">Yes</a></td><td><a href="https://code.google.com/p/go/issues/detail?id=4588">No</a></td></tr>
|
||||
<tr><td>Send <a href="https://tools.ietf.org/html/rfc6455#section-5.5.2">pings</a> and receive <a href="https://tools.ietf.org/html/rfc6455#section-5.5.3">pongs</a></td><td><a href="http://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages">Yes</a></td><td>No</td></tr>
|
||||
<tr><td>Get the <a href="https://tools.ietf.org/html/rfc6455#section-5.6">type</a> of a received data message</td><td>Yes</td><td>Yes, see note 2</td></tr>
|
||||
<tr><td colspan="3">Other Features</tr></td>
|
||||
<tr><td><a href="https://tools.ietf.org/html/rfc7692">Compression Extensions</a></td><td>Experimental</td><td>No</td></tr>
|
||||
<tr><td>Read message using io.Reader</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.NextReader">Yes</a></td><td>No, see note 3</td></tr>
|
||||
<tr><td>Write message using io.WriteCloser</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.NextWriter">Yes</a></td><td>No, see note 3</td></tr>
|
||||
</table>
|
||||
|
||||
Notes:
|
||||
|
||||
1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html).
|
||||
2. The application can get the type of a received data message by implementing
|
||||
a [Codec marshal](http://godoc.org/golang.org/x/net/websocket#Codec.Marshal)
|
||||
function.
|
||||
3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries.
|
||||
Read returns when the input buffer is full or a frame boundary is
|
||||
encountered. Each call to Write sends a single frame message. The Gorilla
|
||||
io.Reader and io.WriteCloser operate on a single WebSocket message.
|
||||
|
392
vendor/github.com/gorilla/websocket/client.go
generated
vendored
Normal file
392
vendor/github.com/gorilla/websocket/client.go
generated
vendored
Normal file
@ -0,0 +1,392 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ErrBadHandshake is returned when the server response to opening handshake is
|
||||
// invalid.
|
||||
var ErrBadHandshake = errors.New("websocket: bad handshake")
|
||||
|
||||
var errInvalidCompression = errors.New("websocket: invalid compression negotiation")
|
||||
|
||||
// NewClient creates a new client connection using the given net connection.
|
||||
// The URL u specifies the host and request URI. Use requestHeader to specify
|
||||
// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies
|
||||
// (Cookie). Use the response.Header to get the selected subprotocol
|
||||
// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).
|
||||
//
|
||||
// If the WebSocket handshake fails, ErrBadHandshake is returned along with a
|
||||
// non-nil *http.Response so that callers can handle redirects, authentication,
|
||||
// etc.
|
||||
//
|
||||
// Deprecated: Use Dialer instead.
|
||||
func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) {
|
||||
d := Dialer{
|
||||
ReadBufferSize: readBufSize,
|
||||
WriteBufferSize: writeBufSize,
|
||||
NetDial: func(net, addr string) (net.Conn, error) {
|
||||
return netConn, nil
|
||||
},
|
||||
}
|
||||
return d.Dial(u.String(), requestHeader)
|
||||
}
|
||||
|
||||
// A Dialer contains options for connecting to WebSocket server.
|
||||
type Dialer struct {
|
||||
// NetDial specifies the dial function for creating TCP connections. If
|
||||
// NetDial is nil, net.Dial is used.
|
||||
NetDial func(network, addr string) (net.Conn, error)
|
||||
|
||||
// Proxy specifies a function to return a proxy for a given
|
||||
// Request. If the function returns a non-nil error, the
|
||||
// request is aborted with the provided error.
|
||||
// If Proxy is nil or returns a nil *URL, no proxy is used.
|
||||
Proxy func(*http.Request) (*url.URL, error)
|
||||
|
||||
// TLSClientConfig specifies the TLS configuration to use with tls.Client.
|
||||
// If nil, the default configuration is used.
|
||||
TLSClientConfig *tls.Config
|
||||
|
||||
// HandshakeTimeout specifies the duration for the handshake to complete.
|
||||
HandshakeTimeout time.Duration
|
||||
|
||||
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer
|
||||
// size is zero, then a useful default size is used. The I/O buffer sizes
|
||||
// do not limit the size of the messages that can be sent or received.
|
||||
ReadBufferSize, WriteBufferSize int
|
||||
|
||||
// Subprotocols specifies the client's requested subprotocols.
|
||||
Subprotocols []string
|
||||
|
||||
// EnableCompression specifies if the client should attempt to negotiate
|
||||
// per message compression (RFC 7692). Setting this value to true does not
|
||||
// guarantee that compression will be supported. Currently only "no context
|
||||
// takeover" modes are supported.
|
||||
EnableCompression bool
|
||||
|
||||
// Jar specifies the cookie jar.
|
||||
// If Jar is nil, cookies are not sent in requests and ignored
|
||||
// in responses.
|
||||
Jar http.CookieJar
|
||||
}
|
||||
|
||||
var errMalformedURL = errors.New("malformed ws or wss URL")
|
||||
|
||||
// parseURL parses the URL.
|
||||
//
|
||||
// This function is a replacement for the standard library url.Parse function.
|
||||
// In Go 1.4 and earlier, url.Parse loses information from the path.
|
||||
func parseURL(s string) (*url.URL, error) {
|
||||
// From the RFC:
|
||||
//
|
||||
// ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
|
||||
// wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]
|
||||
var u url.URL
|
||||
switch {
|
||||
case strings.HasPrefix(s, "ws://"):
|
||||
u.Scheme = "ws"
|
||||
s = s[len("ws://"):]
|
||||
case strings.HasPrefix(s, "wss://"):
|
||||
u.Scheme = "wss"
|
||||
s = s[len("wss://"):]
|
||||
default:
|
||||
return nil, errMalformedURL
|
||||
}
|
||||
|
||||
if i := strings.Index(s, "?"); i >= 0 {
|
||||
u.RawQuery = s[i+1:]
|
||||
s = s[:i]
|
||||
}
|
||||
|
||||
if i := strings.Index(s, "/"); i >= 0 {
|
||||
u.Opaque = s[i:]
|
||||
s = s[:i]
|
||||
} else {
|
||||
u.Opaque = "/"
|
||||
}
|
||||
|
||||
u.Host = s
|
||||
|
||||
if strings.Contains(u.Host, "@") {
|
||||
// Don't bother parsing user information because user information is
|
||||
// not allowed in websocket URIs.
|
||||
return nil, errMalformedURL
|
||||
}
|
||||
|
||||
return &u, nil
|
||||
}
|
||||
|
||||
func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {
|
||||
hostPort = u.Host
|
||||
hostNoPort = u.Host
|
||||
if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") {
|
||||
hostNoPort = hostNoPort[:i]
|
||||
} else {
|
||||
switch u.Scheme {
|
||||
case "wss":
|
||||
hostPort += ":443"
|
||||
case "https":
|
||||
hostPort += ":443"
|
||||
default:
|
||||
hostPort += ":80"
|
||||
}
|
||||
}
|
||||
return hostPort, hostNoPort
|
||||
}
|
||||
|
||||
// DefaultDialer is a dialer with all fields set to the default zero values.
|
||||
var DefaultDialer = &Dialer{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
}
|
||||
|
||||
// Dial creates a new client connection. Use requestHeader to specify the
|
||||
// origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie).
|
||||
// Use the response.Header to get the selected subprotocol
|
||||
// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).
|
||||
//
|
||||
// If the WebSocket handshake fails, ErrBadHandshake is returned along with a
|
||||
// non-nil *http.Response so that callers can handle redirects, authentication,
|
||||
// etcetera. The response body may not contain the entire response and does not
|
||||
// need to be closed by the application.
|
||||
func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {
|
||||
|
||||
if d == nil {
|
||||
d = &Dialer{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
}
|
||||
}
|
||||
|
||||
challengeKey, err := generateChallengeKey()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
u, err := parseURL(urlStr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "ws":
|
||||
u.Scheme = "http"
|
||||
case "wss":
|
||||
u.Scheme = "https"
|
||||
default:
|
||||
return nil, nil, errMalformedURL
|
||||
}
|
||||
|
||||
if u.User != nil {
|
||||
// User name and password are not allowed in websocket URIs.
|
||||
return nil, nil, errMalformedURL
|
||||
}
|
||||
|
||||
req := &http.Request{
|
||||
Method: "GET",
|
||||
URL: u,
|
||||
Proto: "HTTP/1.1",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Header: make(http.Header),
|
||||
Host: u.Host,
|
||||
}
|
||||
|
||||
// Set the cookies present in the cookie jar of the dialer
|
||||
if d.Jar != nil {
|
||||
for _, cookie := range d.Jar.Cookies(u) {
|
||||
req.AddCookie(cookie)
|
||||
}
|
||||
}
|
||||
|
||||
// Set the request headers using the capitalization for names and values in
|
||||
// RFC examples. Although the capitalization shouldn't matter, there are
|
||||
// servers that depend on it. The Header.Set method is not used because the
|
||||
// method canonicalizes the header names.
|
||||
req.Header["Upgrade"] = []string{"websocket"}
|
||||
req.Header["Connection"] = []string{"Upgrade"}
|
||||
req.Header["Sec-WebSocket-Key"] = []string{challengeKey}
|
||||
req.Header["Sec-WebSocket-Version"] = []string{"13"}
|
||||
if len(d.Subprotocols) > 0 {
|
||||
req.Header["Sec-WebSocket-Protocol"] = []string{strings.Join(d.Subprotocols, ", ")}
|
||||
}
|
||||
for k, vs := range requestHeader {
|
||||
switch {
|
||||
case k == "Host":
|
||||
if len(vs) > 0 {
|
||||
req.Host = vs[0]
|
||||
}
|
||||
case k == "Upgrade" ||
|
||||
k == "Connection" ||
|
||||
k == "Sec-Websocket-Key" ||
|
||||
k == "Sec-Websocket-Version" ||
|
||||
k == "Sec-Websocket-Extensions" ||
|
||||
(k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0):
|
||||
return nil, nil, errors.New("websocket: duplicate header not allowed: " + k)
|
||||
default:
|
||||
req.Header[k] = vs
|
||||
}
|
||||
}
|
||||
|
||||
if d.EnableCompression {
|
||||
req.Header.Set("Sec-Websocket-Extensions", "permessage-deflate; server_no_context_takeover; client_no_context_takeover")
|
||||
}
|
||||
|
||||
hostPort, hostNoPort := hostPortNoPort(u)
|
||||
|
||||
var proxyURL *url.URL
|
||||
// Check wether the proxy method has been configured
|
||||
if d.Proxy != nil {
|
||||
proxyURL, err = d.Proxy(req)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var targetHostPort string
|
||||
if proxyURL != nil {
|
||||
targetHostPort, _ = hostPortNoPort(proxyURL)
|
||||
} else {
|
||||
targetHostPort = hostPort
|
||||
}
|
||||
|
||||
var deadline time.Time
|
||||
if d.HandshakeTimeout != 0 {
|
||||
deadline = time.Now().Add(d.HandshakeTimeout)
|
||||
}
|
||||
|
||||
netDial := d.NetDial
|
||||
if netDial == nil {
|
||||
netDialer := &net.Dialer{Deadline: deadline}
|
||||
netDial = netDialer.Dial
|
||||
}
|
||||
|
||||
netConn, err := netDial("tcp", targetHostPort)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if netConn != nil {
|
||||
netConn.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
if err := netConn.SetDeadline(deadline); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if proxyURL != nil {
|
||||
connectHeader := make(http.Header)
|
||||
if user := proxyURL.User; user != nil {
|
||||
proxyUser := user.Username()
|
||||
if proxyPassword, passwordSet := user.Password(); passwordSet {
|
||||
credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword))
|
||||
connectHeader.Set("Proxy-Authorization", "Basic "+credential)
|
||||
}
|
||||
}
|
||||
connectReq := &http.Request{
|
||||
Method: "CONNECT",
|
||||
URL: &url.URL{Opaque: hostPort},
|
||||
Host: hostPort,
|
||||
Header: connectHeader,
|
||||
}
|
||||
|
||||
connectReq.Write(netConn)
|
||||
|
||||
// Read response.
|
||||
// Okay to use and discard buffered reader here, because
|
||||
// TLS server will not speak until spoken to.
|
||||
br := bufio.NewReader(netConn)
|
||||
resp, err := http.ReadResponse(br, connectReq)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
f := strings.SplitN(resp.Status, " ", 2)
|
||||
return nil, nil, errors.New(f[1])
|
||||
}
|
||||
}
|
||||
|
||||
if u.Scheme == "https" {
|
||||
cfg := cloneTLSConfig(d.TLSClientConfig)
|
||||
if cfg.ServerName == "" {
|
||||
cfg.ServerName = hostNoPort
|
||||
}
|
||||
tlsConn := tls.Client(netConn, cfg)
|
||||
netConn = tlsConn
|
||||
if err := tlsConn.Handshake(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if !cfg.InsecureSkipVerify {
|
||||
if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize)
|
||||
|
||||
if err := req.Write(netConn); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(conn.br, req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if d.Jar != nil {
|
||||
if rc := resp.Cookies(); len(rc) > 0 {
|
||||
d.Jar.SetCookies(u, rc)
|
||||
}
|
||||
}
|
||||
|
||||
if resp.StatusCode != 101 ||
|
||||
!strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") ||
|
||||
!strings.EqualFold(resp.Header.Get("Connection"), "upgrade") ||
|
||||
resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) {
|
||||
// Before closing the network connection on return from this
|
||||
// function, slurp up some of the response to aid application
|
||||
// debugging.
|
||||
buf := make([]byte, 1024)
|
||||
n, _ := io.ReadFull(resp.Body, buf)
|
||||
resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n]))
|
||||
return nil, resp, ErrBadHandshake
|
||||
}
|
||||
|
||||
for _, ext := range parseExtensions(resp.Header) {
|
||||
if ext[""] != "permessage-deflate" {
|
||||
continue
|
||||
}
|
||||
_, snct := ext["server_no_context_takeover"]
|
||||
_, cnct := ext["client_no_context_takeover"]
|
||||
if !snct || !cnct {
|
||||
return nil, resp, errInvalidCompression
|
||||
}
|
||||
conn.newCompressionWriter = compressNoContextTakeover
|
||||
conn.newDecompressionReader = decompressNoContextTakeover
|
||||
break
|
||||
}
|
||||
|
||||
resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
|
||||
conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol")
|
||||
|
||||
netConn.SetDeadline(time.Time{})
|
||||
netConn = nil // to avoid close in defer.
|
||||
return conn, resp, nil
|
||||
}
|
16
vendor/github.com/gorilla/websocket/client_clone.go
generated
vendored
Normal file
16
vendor/github.com/gorilla/websocket/client_clone.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.8
|
||||
|
||||
package websocket
|
||||
|
||||
import "crypto/tls"
|
||||
|
||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||
if cfg == nil {
|
||||
return &tls.Config{}
|
||||
}
|
||||
return cfg.Clone()
|
||||
}
|
38
vendor/github.com/gorilla/websocket/client_clone_legacy.go
generated
vendored
Normal file
38
vendor/github.com/gorilla/websocket/client_clone_legacy.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !go1.8
|
||||
|
||||
package websocket
|
||||
|
||||
import "crypto/tls"
|
||||
|
||||
// cloneTLSConfig clones all public fields except the fields
|
||||
// SessionTicketsDisabled and SessionTicketKey. This avoids copying the
|
||||
// sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a
|
||||
// config in active use.
|
||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||
if cfg == nil {
|
||||
return &tls.Config{}
|
||||
}
|
||||
return &tls.Config{
|
||||
Rand: cfg.Rand,
|
||||
Time: cfg.Time,
|
||||
Certificates: cfg.Certificates,
|
||||
NameToCertificate: cfg.NameToCertificate,
|
||||
GetCertificate: cfg.GetCertificate,
|
||||
RootCAs: cfg.RootCAs,
|
||||
NextProtos: cfg.NextProtos,
|
||||
ServerName: cfg.ServerName,
|
||||
ClientAuth: cfg.ClientAuth,
|
||||
ClientCAs: cfg.ClientCAs,
|
||||
InsecureSkipVerify: cfg.InsecureSkipVerify,
|
||||
CipherSuites: cfg.CipherSuites,
|
||||
PreferServerCipherSuites: cfg.PreferServerCipherSuites,
|
||||
ClientSessionCache: cfg.ClientSessionCache,
|
||||
MinVersion: cfg.MinVersion,
|
||||
MaxVersion: cfg.MaxVersion,
|
||||
CurvePreferences: cfg.CurvePreferences,
|
||||
}
|
||||
}
|
148
vendor/github.com/gorilla/websocket/compression.go
generated
vendored
Normal file
148
vendor/github.com/gorilla/websocket/compression.go
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"compress/flate"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
minCompressionLevel = -2 // flate.HuffmanOnly not defined in Go < 1.6
|
||||
maxCompressionLevel = flate.BestCompression
|
||||
defaultCompressionLevel = 1
|
||||
)
|
||||
|
||||
var (
|
||||
flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool
|
||||
flateReaderPool = sync.Pool{New: func() interface{} {
|
||||
return flate.NewReader(nil)
|
||||
}}
|
||||
)
|
||||
|
||||
func decompressNoContextTakeover(r io.Reader) io.ReadCloser {
|
||||
const tail =
|
||||
// Add four bytes as specified in RFC
|
||||
"\x00\x00\xff\xff" +
|
||||
// Add final block to squelch unexpected EOF error from flate reader.
|
||||
"\x01\x00\x00\xff\xff"
|
||||
|
||||
fr, _ := flateReaderPool.Get().(io.ReadCloser)
|
||||
fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil)
|
||||
return &flateReadWrapper{fr}
|
||||
}
|
||||
|
||||
func isValidCompressionLevel(level int) bool {
|
||||
return minCompressionLevel <= level && level <= maxCompressionLevel
|
||||
}
|
||||
|
||||
func compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser {
|
||||
p := &flateWriterPools[level-minCompressionLevel]
|
||||
tw := &truncWriter{w: w}
|
||||
fw, _ := p.Get().(*flate.Writer)
|
||||
if fw == nil {
|
||||
fw, _ = flate.NewWriter(tw, level)
|
||||
} else {
|
||||
fw.Reset(tw)
|
||||
}
|
||||
return &flateWriteWrapper{fw: fw, tw: tw, p: p}
|
||||
}
|
||||
|
||||
// truncWriter is an io.Writer that writes all but the last four bytes of the
|
||||
// stream to another io.Writer.
|
||||
type truncWriter struct {
|
||||
w io.WriteCloser
|
||||
n int
|
||||
p [4]byte
|
||||
}
|
||||
|
||||
func (w *truncWriter) Write(p []byte) (int, error) {
|
||||
n := 0
|
||||
|
||||
// fill buffer first for simplicity.
|
||||
if w.n < len(w.p) {
|
||||
n = copy(w.p[w.n:], p)
|
||||
p = p[n:]
|
||||
w.n += n
|
||||
if len(p) == 0 {
|
||||
return n, nil
|
||||
}
|
||||
}
|
||||
|
||||
m := len(p)
|
||||
if m > len(w.p) {
|
||||
m = len(w.p)
|
||||
}
|
||||
|
||||
if nn, err := w.w.Write(w.p[:m]); err != nil {
|
||||
return n + nn, err
|
||||
}
|
||||
|
||||
copy(w.p[:], w.p[m:])
|
||||
copy(w.p[len(w.p)-m:], p[len(p)-m:])
|
||||
nn, err := w.w.Write(p[:len(p)-m])
|
||||
return n + nn, err
|
||||
}
|
||||
|
||||
type flateWriteWrapper struct {
|
||||
fw *flate.Writer
|
||||
tw *truncWriter
|
||||
p *sync.Pool
|
||||
}
|
||||
|
||||
func (w *flateWriteWrapper) Write(p []byte) (int, error) {
|
||||
if w.fw == nil {
|
||||
return 0, errWriteClosed
|
||||
}
|
||||
return w.fw.Write(p)
|
||||
}
|
||||
|
||||
func (w *flateWriteWrapper) Close() error {
|
||||
if w.fw == nil {
|
||||
return errWriteClosed
|
||||
}
|
||||
err1 := w.fw.Flush()
|
||||
w.p.Put(w.fw)
|
||||
w.fw = nil
|
||||
if w.tw.p != [4]byte{0, 0, 0xff, 0xff} {
|
||||
return errors.New("websocket: internal error, unexpected bytes at end of flate stream")
|
||||
}
|
||||
err2 := w.tw.w.Close()
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
return err2
|
||||
}
|
||||
|
||||
type flateReadWrapper struct {
|
||||
fr io.ReadCloser
|
||||
}
|
||||
|
||||
func (r *flateReadWrapper) Read(p []byte) (int, error) {
|
||||
if r.fr == nil {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
n, err := r.fr.Read(p)
|
||||
if err == io.EOF {
|
||||
// Preemptively place the reader back in the pool. This helps with
|
||||
// scenarios where the application does not call NextReader() soon after
|
||||
// this final read.
|
||||
r.Close()
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (r *flateReadWrapper) Close() error {
|
||||
if r.fr == nil {
|
||||
return io.ErrClosedPipe
|
||||
}
|
||||
err := r.fr.Close()
|
||||
flateReaderPool.Put(r.fr)
|
||||
r.fr = nil
|
||||
return err
|
||||
}
|
1149
vendor/github.com/gorilla/websocket/conn.go
generated
vendored
Normal file
1149
vendor/github.com/gorilla/websocket/conn.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
18
vendor/github.com/gorilla/websocket/conn_read.go
generated
vendored
Normal file
18
vendor/github.com/gorilla/websocket/conn_read.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.5
|
||||
|
||||
package websocket
|
||||
|
||||
import "io"
|
||||
|
||||
func (c *Conn) read(n int) ([]byte, error) {
|
||||
p, err := c.br.Peek(n)
|
||||
if err == io.EOF {
|
||||
err = errUnexpectedEOF
|
||||
}
|
||||
c.br.Discard(len(p))
|
||||
return p, err
|
||||
}
|
21
vendor/github.com/gorilla/websocket/conn_read_legacy.go
generated
vendored
Normal file
21
vendor/github.com/gorilla/websocket/conn_read_legacy.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !go1.5
|
||||
|
||||
package websocket
|
||||
|
||||
import "io"
|
||||
|
||||
func (c *Conn) read(n int) ([]byte, error) {
|
||||
p, err := c.br.Peek(n)
|
||||
if err == io.EOF {
|
||||
err = errUnexpectedEOF
|
||||
}
|
||||
if len(p) > 0 {
|
||||
// advance over the bytes just read
|
||||
io.ReadFull(c.br, p)
|
||||
}
|
||||
return p, err
|
||||
}
|
180
vendor/github.com/gorilla/websocket/doc.go
generated
vendored
Normal file
180
vendor/github.com/gorilla/websocket/doc.go
generated
vendored
Normal file
@ -0,0 +1,180 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package websocket implements the WebSocket protocol defined in RFC 6455.
|
||||
//
|
||||
// Overview
|
||||
//
|
||||
// The Conn type represents a WebSocket connection. A server application uses
|
||||
// the Upgrade function from an Upgrader object with a HTTP request handler
|
||||
// to get a pointer to a Conn:
|
||||
//
|
||||
// var upgrader = websocket.Upgrader{
|
||||
// ReadBufferSize: 1024,
|
||||
// WriteBufferSize: 1024,
|
||||
// }
|
||||
//
|
||||
// func handler(w http.ResponseWriter, r *http.Request) {
|
||||
// conn, err := upgrader.Upgrade(w, r, nil)
|
||||
// if err != nil {
|
||||
// log.Println(err)
|
||||
// return
|
||||
// }
|
||||
// ... Use conn to send and receive messages.
|
||||
// }
|
||||
//
|
||||
// Call the connection's WriteMessage and ReadMessage methods to send and
|
||||
// receive messages as a slice of bytes. This snippet of code shows how to echo
|
||||
// messages using these methods:
|
||||
//
|
||||
// for {
|
||||
// messageType, p, err := conn.ReadMessage()
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// if err = conn.WriteMessage(messageType, p); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// In above snippet of code, p is a []byte and messageType is an int with value
|
||||
// websocket.BinaryMessage or websocket.TextMessage.
|
||||
//
|
||||
// An application can also send and receive messages using the io.WriteCloser
|
||||
// and io.Reader interfaces. To send a message, call the connection NextWriter
|
||||
// method to get an io.WriteCloser, write the message to the writer and close
|
||||
// the writer when done. To receive a message, call the connection NextReader
|
||||
// method to get an io.Reader and read until io.EOF is returned. This snippet
|
||||
// shows how to echo messages using the NextWriter and NextReader methods:
|
||||
//
|
||||
// for {
|
||||
// messageType, r, err := conn.NextReader()
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// w, err := conn.NextWriter(messageType)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if _, err := io.Copy(w, r); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if err := w.Close(); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Data Messages
|
||||
//
|
||||
// The WebSocket protocol distinguishes between text and binary data messages.
|
||||
// Text messages are interpreted as UTF-8 encoded text. The interpretation of
|
||||
// binary messages is left to the application.
|
||||
//
|
||||
// This package uses the TextMessage and BinaryMessage integer constants to
|
||||
// identify the two data message types. The ReadMessage and NextReader methods
|
||||
// return the type of the received message. The messageType argument to the
|
||||
// WriteMessage and NextWriter methods specifies the type of a sent message.
|
||||
//
|
||||
// It is the application's responsibility to ensure that text messages are
|
||||
// valid UTF-8 encoded text.
|
||||
//
|
||||
// Control Messages
|
||||
//
|
||||
// The WebSocket protocol defines three types of control messages: close, ping
|
||||
// and pong. Call the connection WriteControl, WriteMessage or NextWriter
|
||||
// methods to send a control message to the peer.
|
||||
//
|
||||
// Connections handle received close messages by sending a close message to the
|
||||
// peer and returning a *CloseError from the the NextReader, ReadMessage or the
|
||||
// message Read method.
|
||||
//
|
||||
// Connections handle received ping and pong messages by invoking callback
|
||||
// functions set with SetPingHandler and SetPongHandler methods. The callback
|
||||
// functions are called from the NextReader, ReadMessage and the message Read
|
||||
// methods.
|
||||
//
|
||||
// The default ping handler sends a pong to the peer. The application's reading
|
||||
// goroutine can block for a short time while the handler writes the pong data
|
||||
// to the connection.
|
||||
//
|
||||
// The application must read the connection to process ping, pong and close
|
||||
// messages sent from the peer. If the application is not otherwise interested
|
||||
// in messages from the peer, then the application should start a goroutine to
|
||||
// read and discard messages from the peer. A simple example is:
|
||||
//
|
||||
// func readLoop(c *websocket.Conn) {
|
||||
// for {
|
||||
// if _, _, err := c.NextReader(); err != nil {
|
||||
// c.Close()
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Concurrency
|
||||
//
|
||||
// Connections support one concurrent reader and one concurrent writer.
|
||||
//
|
||||
// Applications are responsible for ensuring that no more than one goroutine
|
||||
// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage,
|
||||
// WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and
|
||||
// that no more than one goroutine calls the read methods (NextReader,
|
||||
// SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler)
|
||||
// concurrently.
|
||||
//
|
||||
// The Close and WriteControl methods can be called concurrently with all other
|
||||
// methods.
|
||||
//
|
||||
// Origin Considerations
|
||||
//
|
||||
// Web browsers allow Javascript applications to open a WebSocket connection to
|
||||
// any host. It's up to the server to enforce an origin policy using the Origin
|
||||
// request header sent by the browser.
|
||||
//
|
||||
// The Upgrader calls the function specified in the CheckOrigin field to check
|
||||
// the origin. If the CheckOrigin function returns false, then the Upgrade
|
||||
// method fails the WebSocket handshake with HTTP status 403.
|
||||
//
|
||||
// If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail
|
||||
// the handshake if the Origin request header is present and not equal to the
|
||||
// Host request header.
|
||||
//
|
||||
// An application can allow connections from any origin by specifying a
|
||||
// function that always returns true:
|
||||
//
|
||||
// var upgrader = websocket.Upgrader{
|
||||
// CheckOrigin: func(r *http.Request) bool { return true },
|
||||
// }
|
||||
//
|
||||
// The deprecated Upgrade function does not enforce an origin policy. It's the
|
||||
// application's responsibility to check the Origin header before calling
|
||||
// Upgrade.
|
||||
//
|
||||
// Compression EXPERIMENTAL
|
||||
//
|
||||
// Per message compression extensions (RFC 7692) are experimentally supported
|
||||
// by this package in a limited capacity. Setting the EnableCompression option
|
||||
// to true in Dialer or Upgrader will attempt to negotiate per message deflate
|
||||
// support.
|
||||
//
|
||||
// var upgrader = websocket.Upgrader{
|
||||
// EnableCompression: true,
|
||||
// }
|
||||
//
|
||||
// If compression was successfully negotiated with the connection's peer, any
|
||||
// message received in compressed form will be automatically decompressed.
|
||||
// All Read methods will return uncompressed bytes.
|
||||
//
|
||||
// Per message compression of messages written to a connection can be enabled
|
||||
// or disabled by calling the corresponding Conn method:
|
||||
//
|
||||
// conn.EnableWriteCompression(false)
|
||||
//
|
||||
// Currently this package does not support compression with "context takeover".
|
||||
// This means that messages must be compressed and decompressed in isolation,
|
||||
// without retaining sliding window or dictionary state across messages. For
|
||||
// more details refer to RFC 7692.
|
||||
//
|
||||
// Use of compression is experimental and may result in decreased performance.
|
||||
package websocket
|
55
vendor/github.com/gorilla/websocket/json.go
generated
vendored
Normal file
55
vendor/github.com/gorilla/websocket/json.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
// WriteJSON is deprecated, use c.WriteJSON instead.
|
||||
func WriteJSON(c *Conn, v interface{}) error {
|
||||
return c.WriteJSON(v)
|
||||
}
|
||||
|
||||
// WriteJSON writes the JSON encoding of v to the connection.
|
||||
//
|
||||
// See the documentation for encoding/json Marshal for details about the
|
||||
// conversion of Go values to JSON.
|
||||
func (c *Conn) WriteJSON(v interface{}) error {
|
||||
w, err := c.NextWriter(TextMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err1 := json.NewEncoder(w).Encode(v)
|
||||
err2 := w.Close()
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
return err2
|
||||
}
|
||||
|
||||
// ReadJSON is deprecated, use c.ReadJSON instead.
|
||||
func ReadJSON(c *Conn, v interface{}) error {
|
||||
return c.ReadJSON(v)
|
||||
}
|
||||
|
||||
// ReadJSON reads the next JSON-encoded message from the connection and stores
|
||||
// it in the value pointed to by v.
|
||||
//
|
||||
// See the documentation for the encoding/json Unmarshal function for details
|
||||
// about the conversion of JSON to a Go value.
|
||||
func (c *Conn) ReadJSON(v interface{}) error {
|
||||
_, r, err := c.NextReader()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = json.NewDecoder(r).Decode(v)
|
||||
if err == io.EOF {
|
||||
// One value is expected in the message.
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return err
|
||||
}
|
55
vendor/github.com/gorilla/websocket/mask.go
generated
vendored
Normal file
55
vendor/github.com/gorilla/websocket/mask.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of
|
||||
// this source code is governed by a BSD-style license that can be found in the
|
||||
// LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
|
||||
package websocket
|
||||
|
||||
import "unsafe"
|
||||
|
||||
const wordSize = int(unsafe.Sizeof(uintptr(0)))
|
||||
|
||||
func maskBytes(key [4]byte, pos int, b []byte) int {
|
||||
|
||||
// Mask one byte at a time for small buffers.
|
||||
if len(b) < 2*wordSize {
|
||||
for i := range b {
|
||||
b[i] ^= key[pos&3]
|
||||
pos++
|
||||
}
|
||||
return pos & 3
|
||||
}
|
||||
|
||||
// Mask one byte at a time to word boundary.
|
||||
if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 {
|
||||
n = wordSize - n
|
||||
for i := range b[:n] {
|
||||
b[i] ^= key[pos&3]
|
||||
pos++
|
||||
}
|
||||
b = b[n:]
|
||||
}
|
||||
|
||||
// Create aligned word size key.
|
||||
var k [wordSize]byte
|
||||
for i := range k {
|
||||
k[i] = key[(pos+i)&3]
|
||||
}
|
||||
kw := *(*uintptr)(unsafe.Pointer(&k))
|
||||
|
||||
// Mask one word at a time.
|
||||
n := (len(b) / wordSize) * wordSize
|
||||
for i := 0; i < n; i += wordSize {
|
||||
*(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw
|
||||
}
|
||||
|
||||
// Mask one byte at a time for remaining bytes.
|
||||
b = b[n:]
|
||||
for i := range b {
|
||||
b[i] ^= key[pos&3]
|
||||
pos++
|
||||
}
|
||||
|
||||
return pos & 3
|
||||
}
|
15
vendor/github.com/gorilla/websocket/mask_safe.go
generated
vendored
Normal file
15
vendor/github.com/gorilla/websocket/mask_safe.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of
|
||||
// this source code is governed by a BSD-style license that can be found in the
|
||||
// LICENSE file.
|
||||
|
||||
// +build appengine
|
||||
|
||||
package websocket
|
||||
|
||||
func maskBytes(key [4]byte, pos int, b []byte) int {
|
||||
for i := range b {
|
||||
b[i] ^= key[pos&3]
|
||||
pos++
|
||||
}
|
||||
return pos & 3
|
||||
}
|
103
vendor/github.com/gorilla/websocket/prepared.go
generated
vendored
Normal file
103
vendor/github.com/gorilla/websocket/prepared.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PreparedMessage caches on the wire representations of a message payload.
|
||||
// Use PreparedMessage to efficiently send a message payload to multiple
|
||||
// connections. PreparedMessage is especially useful when compression is used
|
||||
// because the CPU and memory expensive compression operation can be executed
|
||||
// once for a given set of compression options.
|
||||
type PreparedMessage struct {
|
||||
messageType int
|
||||
data []byte
|
||||
err error
|
||||
mu sync.Mutex
|
||||
frames map[prepareKey]*preparedFrame
|
||||
}
|
||||
|
||||
// prepareKey defines a unique set of options to cache prepared frames in PreparedMessage.
|
||||
type prepareKey struct {
|
||||
isServer bool
|
||||
compress bool
|
||||
compressionLevel int
|
||||
}
|
||||
|
||||
// preparedFrame contains data in wire representation.
|
||||
type preparedFrame struct {
|
||||
once sync.Once
|
||||
data []byte
|
||||
}
|
||||
|
||||
// NewPreparedMessage returns an initialized PreparedMessage. You can then send
|
||||
// it to connection using WritePreparedMessage method. Valid wire
|
||||
// representation will be calculated lazily only once for a set of current
|
||||
// connection options.
|
||||
func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) {
|
||||
pm := &PreparedMessage{
|
||||
messageType: messageType,
|
||||
frames: make(map[prepareKey]*preparedFrame),
|
||||
data: data,
|
||||
}
|
||||
|
||||
// Prepare a plain server frame.
|
||||
_, frameData, err := pm.frame(prepareKey{isServer: true, compress: false})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// To protect against caller modifying the data argument, remember the data
|
||||
// copied to the plain server frame.
|
||||
pm.data = frameData[len(frameData)-len(data):]
|
||||
return pm, nil
|
||||
}
|
||||
|
||||
func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) {
|
||||
pm.mu.Lock()
|
||||
frame, ok := pm.frames[key]
|
||||
if !ok {
|
||||
frame = &preparedFrame{}
|
||||
pm.frames[key] = frame
|
||||
}
|
||||
pm.mu.Unlock()
|
||||
|
||||
var err error
|
||||
frame.once.Do(func() {
|
||||
// Prepare a frame using a 'fake' connection.
|
||||
// TODO: Refactor code in conn.go to allow more direct construction of
|
||||
// the frame.
|
||||
mu := make(chan bool, 1)
|
||||
mu <- true
|
||||
var nc prepareConn
|
||||
c := &Conn{
|
||||
conn: &nc,
|
||||
mu: mu,
|
||||
isServer: key.isServer,
|
||||
compressionLevel: key.compressionLevel,
|
||||
enableWriteCompression: true,
|
||||
writeBuf: make([]byte, defaultWriteBufferSize+maxFrameHeaderSize),
|
||||
}
|
||||
if key.compress {
|
||||
c.newCompressionWriter = compressNoContextTakeover
|
||||
}
|
||||
err = c.WriteMessage(pm.messageType, pm.data)
|
||||
frame.data = nc.buf.Bytes()
|
||||
})
|
||||
return pm.messageType, frame.data, err
|
||||
}
|
||||
|
||||
type prepareConn struct {
|
||||
buf bytes.Buffer
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func (pc *prepareConn) Write(p []byte) (int, error) { return pc.buf.Write(p) }
|
||||
func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil }
|
291
vendor/github.com/gorilla/websocket/server.go
generated
vendored
Normal file
291
vendor/github.com/gorilla/websocket/server.go
generated
vendored
Normal file
@ -0,0 +1,291 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// HandshakeError describes an error with the handshake from the peer.
|
||||
type HandshakeError struct {
|
||||
message string
|
||||
}
|
||||
|
||||
func (e HandshakeError) Error() string { return e.message }
|
||||
|
||||
// Upgrader specifies parameters for upgrading an HTTP connection to a
|
||||
// WebSocket connection.
|
||||
type Upgrader struct {
|
||||
// HandshakeTimeout specifies the duration for the handshake to complete.
|
||||
HandshakeTimeout time.Duration
|
||||
|
||||
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer
|
||||
// size is zero, then buffers allocated by the HTTP server are used. The
|
||||
// I/O buffer sizes do not limit the size of the messages that can be sent
|
||||
// or received.
|
||||
ReadBufferSize, WriteBufferSize int
|
||||
|
||||
// Subprotocols specifies the server's supported protocols in order of
|
||||
// preference. If this field is set, then the Upgrade method negotiates a
|
||||
// subprotocol by selecting the first match in this list with a protocol
|
||||
// requested by the client.
|
||||
Subprotocols []string
|
||||
|
||||
// Error specifies the function for generating HTTP error responses. If Error
|
||||
// is nil, then http.Error is used to generate the HTTP response.
|
||||
Error func(w http.ResponseWriter, r *http.Request, status int, reason error)
|
||||
|
||||
// CheckOrigin returns true if the request Origin header is acceptable. If
|
||||
// CheckOrigin is nil, the host in the Origin header must not be set or
|
||||
// must match the host of the request.
|
||||
CheckOrigin func(r *http.Request) bool
|
||||
|
||||
// EnableCompression specify if the server should attempt to negotiate per
|
||||
// message compression (RFC 7692). Setting this value to true does not
|
||||
// guarantee that compression will be supported. Currently only "no context
|
||||
// takeover" modes are supported.
|
||||
EnableCompression bool
|
||||
}
|
||||
|
||||
func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) {
|
||||
err := HandshakeError{reason}
|
||||
if u.Error != nil {
|
||||
u.Error(w, r, status, err)
|
||||
} else {
|
||||
w.Header().Set("Sec-Websocket-Version", "13")
|
||||
http.Error(w, http.StatusText(status), status)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// checkSameOrigin returns true if the origin is not set or is equal to the request host.
|
||||
func checkSameOrigin(r *http.Request) bool {
|
||||
origin := r.Header["Origin"]
|
||||
if len(origin) == 0 {
|
||||
return true
|
||||
}
|
||||
u, err := url.Parse(origin[0])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return u.Host == r.Host
|
||||
}
|
||||
|
||||
func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string {
|
||||
if u.Subprotocols != nil {
|
||||
clientProtocols := Subprotocols(r)
|
||||
for _, serverProtocol := range u.Subprotocols {
|
||||
for _, clientProtocol := range clientProtocols {
|
||||
if clientProtocol == serverProtocol {
|
||||
return clientProtocol
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if responseHeader != nil {
|
||||
return responseHeader.Get("Sec-Websocket-Protocol")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
|
||||
//
|
||||
// The responseHeader is included in the response to the client's upgrade
|
||||
// request. Use the responseHeader to specify cookies (Set-Cookie) and the
|
||||
// application negotiated subprotocol (Sec-Websocket-Protocol).
|
||||
//
|
||||
// If the upgrade fails, then Upgrade replies to the client with an HTTP error
|
||||
// response.
|
||||
func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {
|
||||
if r.Method != "GET" {
|
||||
return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: not a websocket handshake: request method is not GET")
|
||||
}
|
||||
|
||||
if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok {
|
||||
return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-Websocket-Extensions' headers are unsupported")
|
||||
}
|
||||
|
||||
if !tokenListContainsValue(r.Header, "Connection", "upgrade") {
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'upgrade' token not found in 'Connection' header")
|
||||
}
|
||||
|
||||
if !tokenListContainsValue(r.Header, "Upgrade", "websocket") {
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'websocket' token not found in 'Upgrade' header")
|
||||
}
|
||||
|
||||
if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") {
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header")
|
||||
}
|
||||
|
||||
checkOrigin := u.CheckOrigin
|
||||
if checkOrigin == nil {
|
||||
checkOrigin = checkSameOrigin
|
||||
}
|
||||
if !checkOrigin(r) {
|
||||
return u.returnError(w, r, http.StatusForbidden, "websocket: 'Origin' header value not allowed")
|
||||
}
|
||||
|
||||
challengeKey := r.Header.Get("Sec-Websocket-Key")
|
||||
if challengeKey == "" {
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: `Sec-Websocket-Key' header is missing or blank")
|
||||
}
|
||||
|
||||
subprotocol := u.selectSubprotocol(r, responseHeader)
|
||||
|
||||
// Negotiate PMCE
|
||||
var compress bool
|
||||
if u.EnableCompression {
|
||||
for _, ext := range parseExtensions(r.Header) {
|
||||
if ext[""] != "permessage-deflate" {
|
||||
continue
|
||||
}
|
||||
compress = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
netConn net.Conn
|
||||
err error
|
||||
)
|
||||
|
||||
h, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker")
|
||||
}
|
||||
var brw *bufio.ReadWriter
|
||||
netConn, brw, err = h.Hijack()
|
||||
if err != nil {
|
||||
return u.returnError(w, r, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
if brw.Reader.Buffered() > 0 {
|
||||
netConn.Close()
|
||||
return nil, errors.New("websocket: client sent data before handshake is complete")
|
||||
}
|
||||
|
||||
c := newConnBRW(netConn, true, u.ReadBufferSize, u.WriteBufferSize, brw)
|
||||
c.subprotocol = subprotocol
|
||||
|
||||
if compress {
|
||||
c.newCompressionWriter = compressNoContextTakeover
|
||||
c.newDecompressionReader = decompressNoContextTakeover
|
||||
}
|
||||
|
||||
p := c.writeBuf[:0]
|
||||
p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...)
|
||||
p = append(p, computeAcceptKey(challengeKey)...)
|
||||
p = append(p, "\r\n"...)
|
||||
if c.subprotocol != "" {
|
||||
p = append(p, "Sec-Websocket-Protocol: "...)
|
||||
p = append(p, c.subprotocol...)
|
||||
p = append(p, "\r\n"...)
|
||||
}
|
||||
if compress {
|
||||
p = append(p, "Sec-Websocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n"...)
|
||||
}
|
||||
for k, vs := range responseHeader {
|
||||
if k == "Sec-Websocket-Protocol" {
|
||||
continue
|
||||
}
|
||||
for _, v := range vs {
|
||||
p = append(p, k...)
|
||||
p = append(p, ": "...)
|
||||
for i := 0; i < len(v); i++ {
|
||||
b := v[i]
|
||||
if b <= 31 {
|
||||
// prevent response splitting.
|
||||
b = ' '
|
||||
}
|
||||
p = append(p, b)
|
||||
}
|
||||
p = append(p, "\r\n"...)
|
||||
}
|
||||
}
|
||||
p = append(p, "\r\n"...)
|
||||
|
||||
// Clear deadlines set by HTTP server.
|
||||
netConn.SetDeadline(time.Time{})
|
||||
|
||||
if u.HandshakeTimeout > 0 {
|
||||
netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout))
|
||||
}
|
||||
if _, err = netConn.Write(p); err != nil {
|
||||
netConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
if u.HandshakeTimeout > 0 {
|
||||
netConn.SetWriteDeadline(time.Time{})
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
|
||||
//
|
||||
// This function is deprecated, use websocket.Upgrader instead.
|
||||
//
|
||||
// The application is responsible for checking the request origin before
|
||||
// calling Upgrade. An example implementation of the same origin policy is:
|
||||
//
|
||||
// if req.Header.Get("Origin") != "http://"+req.Host {
|
||||
// http.Error(w, "Origin not allowed", 403)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// If the endpoint supports subprotocols, then the application is responsible
|
||||
// for negotiating the protocol used on the connection. Use the Subprotocols()
|
||||
// function to get the subprotocols requested by the client. Use the
|
||||
// Sec-Websocket-Protocol response header to specify the subprotocol selected
|
||||
// by the application.
|
||||
//
|
||||
// The responseHeader is included in the response to the client's upgrade
|
||||
// request. Use the responseHeader to specify cookies (Set-Cookie) and the
|
||||
// negotiated subprotocol (Sec-Websocket-Protocol).
|
||||
//
|
||||
// The connection buffers IO to the underlying network connection. The
|
||||
// readBufSize and writeBufSize parameters specify the size of the buffers to
|
||||
// use. Messages can be larger than the buffers.
|
||||
//
|
||||
// If the request is not a valid WebSocket handshake, then Upgrade returns an
|
||||
// error of type HandshakeError. Applications should handle this error by
|
||||
// replying to the client with an HTTP error response.
|
||||
func Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) {
|
||||
u := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize}
|
||||
u.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) {
|
||||
// don't return errors to maintain backwards compatibility
|
||||
}
|
||||
u.CheckOrigin = func(r *http.Request) bool {
|
||||
// allow all connections by default
|
||||
return true
|
||||
}
|
||||
return u.Upgrade(w, r, responseHeader)
|
||||
}
|
||||
|
||||
// Subprotocols returns the subprotocols requested by the client in the
|
||||
// Sec-Websocket-Protocol header.
|
||||
func Subprotocols(r *http.Request) []string {
|
||||
h := strings.TrimSpace(r.Header.Get("Sec-Websocket-Protocol"))
|
||||
if h == "" {
|
||||
return nil
|
||||
}
|
||||
protocols := strings.Split(h, ",")
|
||||
for i := range protocols {
|
||||
protocols[i] = strings.TrimSpace(protocols[i])
|
||||
}
|
||||
return protocols
|
||||
}
|
||||
|
||||
// IsWebSocketUpgrade returns true if the client requested upgrade to the
|
||||
// WebSocket protocol.
|
||||
func IsWebSocketUpgrade(r *http.Request) bool {
|
||||
return tokenListContainsValue(r.Header, "Connection", "upgrade") &&
|
||||
tokenListContainsValue(r.Header, "Upgrade", "websocket")
|
||||
}
|
214
vendor/github.com/gorilla/websocket/util.go
generated
vendored
Normal file
214
vendor/github.com/gorilla/websocket/util.go
generated
vendored
Normal file
@ -0,0 +1,214 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
||||
|
||||
func computeAcceptKey(challengeKey string) string {
|
||||
h := sha1.New()
|
||||
h.Write([]byte(challengeKey))
|
||||
h.Write(keyGUID)
|
||||
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func generateChallengeKey() (string, error) {
|
||||
p := make([]byte, 16)
|
||||
if _, err := io.ReadFull(rand.Reader, p); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(p), nil
|
||||
}
|
||||
|
||||
// Octet types from RFC 2616.
|
||||
var octetTypes [256]byte
|
||||
|
||||
const (
|
||||
isTokenOctet = 1 << iota
|
||||
isSpaceOctet
|
||||
)
|
||||
|
||||
func init() {
|
||||
// From RFC 2616
|
||||
//
|
||||
// OCTET = <any 8-bit sequence of data>
|
||||
// CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||
// CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
|
||||
// CR = <US-ASCII CR, carriage return (13)>
|
||||
// LF = <US-ASCII LF, linefeed (10)>
|
||||
// SP = <US-ASCII SP, space (32)>
|
||||
// HT = <US-ASCII HT, horizontal-tab (9)>
|
||||
// <"> = <US-ASCII double-quote mark (34)>
|
||||
// CRLF = CR LF
|
||||
// LWS = [CRLF] 1*( SP | HT )
|
||||
// TEXT = <any OCTET except CTLs, but including LWS>
|
||||
// separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <">
|
||||
// | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT
|
||||
// token = 1*<any CHAR except CTLs or separators>
|
||||
// qdtext = <any TEXT except <">>
|
||||
|
||||
for c := 0; c < 256; c++ {
|
||||
var t byte
|
||||
isCtl := c <= 31 || c == 127
|
||||
isChar := 0 <= c && c <= 127
|
||||
isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0
|
||||
if strings.IndexRune(" \t\r\n", rune(c)) >= 0 {
|
||||
t |= isSpaceOctet
|
||||
}
|
||||
if isChar && !isCtl && !isSeparator {
|
||||
t |= isTokenOctet
|
||||
}
|
||||
octetTypes[c] = t
|
||||
}
|
||||
}
|
||||
|
||||
func skipSpace(s string) (rest string) {
|
||||
i := 0
|
||||
for ; i < len(s); i++ {
|
||||
if octetTypes[s[i]]&isSpaceOctet == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return s[i:]
|
||||
}
|
||||
|
||||
func nextToken(s string) (token, rest string) {
|
||||
i := 0
|
||||
for ; i < len(s); i++ {
|
||||
if octetTypes[s[i]]&isTokenOctet == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return s[:i], s[i:]
|
||||
}
|
||||
|
||||
func nextTokenOrQuoted(s string) (value string, rest string) {
|
||||
if !strings.HasPrefix(s, "\"") {
|
||||
return nextToken(s)
|
||||
}
|
||||
s = s[1:]
|
||||
for i := 0; i < len(s); i++ {
|
||||
switch s[i] {
|
||||
case '"':
|
||||
return s[:i], s[i+1:]
|
||||
case '\\':
|
||||
p := make([]byte, len(s)-1)
|
||||
j := copy(p, s[:i])
|
||||
escape := true
|
||||
for i = i + 1; i < len(s); i++ {
|
||||
b := s[i]
|
||||
switch {
|
||||
case escape:
|
||||
escape = false
|
||||
p[j] = b
|
||||
j += 1
|
||||
case b == '\\':
|
||||
escape = true
|
||||
case b == '"':
|
||||
return string(p[:j]), s[i+1:]
|
||||
default:
|
||||
p[j] = b
|
||||
j += 1
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// tokenListContainsValue returns true if the 1#token header with the given
|
||||
// name contains token.
|
||||
func tokenListContainsValue(header http.Header, name string, value string) bool {
|
||||
headers:
|
||||
for _, s := range header[name] {
|
||||
for {
|
||||
var t string
|
||||
t, s = nextToken(skipSpace(s))
|
||||
if t == "" {
|
||||
continue headers
|
||||
}
|
||||
s = skipSpace(s)
|
||||
if s != "" && s[0] != ',' {
|
||||
continue headers
|
||||
}
|
||||
if strings.EqualFold(t, value) {
|
||||
return true
|
||||
}
|
||||
if s == "" {
|
||||
continue headers
|
||||
}
|
||||
s = s[1:]
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// parseExtensiosn parses WebSocket extensions from a header.
|
||||
func parseExtensions(header http.Header) []map[string]string {
|
||||
|
||||
// From RFC 6455:
|
||||
//
|
||||
// Sec-WebSocket-Extensions = extension-list
|
||||
// extension-list = 1#extension
|
||||
// extension = extension-token *( ";" extension-param )
|
||||
// extension-token = registered-token
|
||||
// registered-token = token
|
||||
// extension-param = token [ "=" (token | quoted-string) ]
|
||||
// ;When using the quoted-string syntax variant, the value
|
||||
// ;after quoted-string unescaping MUST conform to the
|
||||
// ;'token' ABNF.
|
||||
|
||||
var result []map[string]string
|
||||
headers:
|
||||
for _, s := range header["Sec-Websocket-Extensions"] {
|
||||
for {
|
||||
var t string
|
||||
t, s = nextToken(skipSpace(s))
|
||||
if t == "" {
|
||||
continue headers
|
||||
}
|
||||
ext := map[string]string{"": t}
|
||||
for {
|
||||
s = skipSpace(s)
|
||||
if !strings.HasPrefix(s, ";") {
|
||||
break
|
||||
}
|
||||
var k string
|
||||
k, s = nextToken(skipSpace(s[1:]))
|
||||
if k == "" {
|
||||
continue headers
|
||||
}
|
||||
s = skipSpace(s)
|
||||
var v string
|
||||
if strings.HasPrefix(s, "=") {
|
||||
v, s = nextTokenOrQuoted(skipSpace(s[1:]))
|
||||
s = skipSpace(s)
|
||||
}
|
||||
if s != "" && s[0] != ',' && s[0] != ';' {
|
||||
continue headers
|
||||
}
|
||||
ext[k] = v
|
||||
}
|
||||
if s != "" && s[0] != ',' {
|
||||
continue headers
|
||||
}
|
||||
result = append(result, ext)
|
||||
if s == "" {
|
||||
continue headers
|
||||
}
|
||||
s = s[1:]
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
37
vendor/github.com/maruel/panicparse/stack/source.go
generated
vendored
37
vendor/github.com/maruel/panicparse/stack/source.go
generated
vendored
@ -53,7 +53,7 @@ func (c *cache) augmentGoroutine(goroutine *Goroutine) {
|
||||
}
|
||||
|
||||
// Once all loaded, we can look at the next call when available.
|
||||
for i := 0; i < len(goroutine.Stack.Calls)-1; i++ {
|
||||
for i := 1; i < len(goroutine.Stack.Calls); i++ {
|
||||
// Get the AST from the previous call and process the call line with it.
|
||||
if f := c.getFuncAST(&goroutine.Stack.Calls[i]); f != nil {
|
||||
processCall(&goroutine.Stack.Calls[i], f)
|
||||
@ -115,15 +115,6 @@ type parsedFile struct {
|
||||
// getFuncAST gets the callee site function AST representation for the code
|
||||
// inside the function f at line l.
|
||||
func (p *parsedFile) getFuncAST(f string, l int) (d *ast.FuncDecl) {
|
||||
if len(p.lineToByteOffset) <= l {
|
||||
// The line number in the stack trace line does not exist in the file. That
|
||||
// can only mean that the sources on disk do not match the sources used to
|
||||
// build the binary.
|
||||
// TODO(maruel): This should be surfaced, so that source parsing is
|
||||
// completely ignored.
|
||||
return
|
||||
}
|
||||
|
||||
// Walk the AST to find the lineToByteOffset that fits the line number.
|
||||
var lastFunc *ast.FuncDecl
|
||||
var found ast.Node
|
||||
@ -164,18 +155,20 @@ func (p *parsedFile) getFuncAST(f string, l int) (d *ast.FuncDecl) {
|
||||
}
|
||||
|
||||
func name(n ast.Node) string {
|
||||
switch t := n.(type) {
|
||||
case *ast.InterfaceType:
|
||||
if _, ok := n.(*ast.InterfaceType); ok {
|
||||
return "interface{}"
|
||||
case *ast.Ident:
|
||||
return t.Name
|
||||
case *ast.SelectorExpr:
|
||||
return t.Sel.Name
|
||||
case *ast.StarExpr:
|
||||
return "*" + name(t.X)
|
||||
default:
|
||||
return "<unknown>"
|
||||
}
|
||||
if i, ok := n.(*ast.Ident); ok {
|
||||
return i.Name
|
||||
}
|
||||
if _, ok := n.(*ast.FuncType); ok {
|
||||
return "func"
|
||||
}
|
||||
if s, ok := n.(*ast.SelectorExpr); ok {
|
||||
return s.Sel.Name
|
||||
}
|
||||
// TODO(maruel): Implement anything missing.
|
||||
return "<unknown>"
|
||||
}
|
||||
|
||||
// fieldToType returns the type name and whether if it's an ellipsis.
|
||||
@ -196,10 +189,6 @@ func fieldToType(f *ast.Field) (string, bool) {
|
||||
return arg.Sel.Name, false
|
||||
case *ast.StarExpr:
|
||||
return "*" + name(arg.X), false
|
||||
case *ast.MapType:
|
||||
return fmt.Sprintf("map[%s]%s", name(arg.Key), name(arg.Value)), false
|
||||
case *ast.ChanType:
|
||||
return fmt.Sprintf("chan %s", name(arg.Value)), false
|
||||
default:
|
||||
// TODO(maruel): Implement anything missing.
|
||||
return "<unknown>", false
|
||||
|
12
vendor/github.com/maruel/panicparse/stack/stack.go
generated
vendored
12
vendor/github.com/maruel/panicparse/stack/stack.go
generated
vendored
@ -35,7 +35,7 @@ var (
|
||||
// - found next stack barrier at 0x123; expected
|
||||
// - runtime: unexpected return pc for FUNC_NAME called from 0x123
|
||||
|
||||
reRoutineHeader = regexp.MustCompile("^goroutine (\\d+) \\[([^\\]]+)\\]\\:\r?\n$")
|
||||
reRoutineHeader = regexp.MustCompile("^goroutine (\\d+) \\[([^\\]]+)\\]\\:\n$")
|
||||
reMinutes = regexp.MustCompile("^(\\d+) minutes$")
|
||||
reUnavail = regexp.MustCompile("^(?:\t| +)goroutine running on other thread; stack unavailable")
|
||||
// See gentraceback() in src/runtime/traceback.go for more information.
|
||||
@ -54,12 +54,12 @@ var (
|
||||
// when a signal is not correctly handled. It is printed with m.throwing>0.
|
||||
// These are discarded.
|
||||
// - For cgo, the source file may be "??".
|
||||
reFile = regexp.MustCompile("^(?:\t| +)(\\?\\?|\\<autogenerated\\>|.+\\.(?:c|go|s))\\:(\\d+)(?:| \\+0x[0-9a-f]+)(?:| fp=0x[0-9a-f]+ sp=0x[0-9a-f]+)\r?\n$")
|
||||
reFile = regexp.MustCompile("^(?:\t| +)(\\?\\?|\\<autogenerated\\>|.+\\.(?:c|go|s))\\:(\\d+)(?:| \\+0x[0-9a-f]+)(?:| fp=0x[0-9a-f]+ sp=0x[0-9a-f]+)\n$")
|
||||
// Sadly, it doesn't note the goroutine number so we could cascade them per
|
||||
// parenthood.
|
||||
reCreated = regexp.MustCompile("^created by (.+)\r?\n$")
|
||||
reFunc = regexp.MustCompile("^(.+)\\((.*)\\)\r?\n$")
|
||||
reElided = regexp.MustCompile("^\\.\\.\\.additional frames elided\\.\\.\\.\r?\n$")
|
||||
reCreated = regexp.MustCompile("^created by (.+)\n$")
|
||||
reFunc = regexp.MustCompile("^(.+)\\((.*)\\)\n$")
|
||||
reElided = regexp.MustCompile("^\\.\\.\\.additional frames elided\\.\\.\\.\n$")
|
||||
// Include frequent GOROOT value on Windows, distro provided and user
|
||||
// installed path. This simplifies the user's life when processing a trace
|
||||
// generated on another VM.
|
||||
@ -656,7 +656,7 @@ func ParseDump(r io.Reader, out io.Writer) ([]Goroutine, error) {
|
||||
firstLine := false
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if line == "\n" || line == "\r\n" {
|
||||
if line == "\n" {
|
||||
if goroutine != nil {
|
||||
goroutine = nil
|
||||
continue
|
||||
|
8
vendor/github.com/mattn/go-runewidth/.travis.yml
generated
vendored
Normal file
8
vendor/github.com/mattn/go-runewidth/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
language: go
|
||||
go:
|
||||
- tip
|
||||
before_install:
|
||||
- go get github.com/mattn/goveralls
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
script:
|
||||
- $HOME/gopath/bin/goveralls -repotoken lAKAWPzcGsD3A8yBX3BGGtRUdJ6CaGERL
|
1
vendor/github.com/mattn/go-runewidth/runewidth.go
generated
vendored
1
vendor/github.com/mattn/go-runewidth/runewidth.go
generated
vendored
@ -55,7 +55,6 @@ var private = table{
|
||||
var nonprint = table{
|
||||
{0x0000, 0x001F}, {0x007F, 0x009F}, {0x00AD, 0x00AD},
|
||||
{0x070F, 0x070F}, {0x180B, 0x180E}, {0x200B, 0x200F},
|
||||
{0x2028, 0x2029},
|
||||
{0x202A, 0x202E}, {0x206A, 0x206F}, {0xD800, 0xDFFF},
|
||||
{0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFE, 0xFFFF},
|
||||
}
|
||||
|
2
vendor/github.com/nlopes/slack/.gitignore
generated
vendored
Normal file
2
vendor/github.com/nlopes/slack/.gitignore
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.test
|
||||
*~
|
21
vendor/github.com/nlopes/slack/.travis.yml
generated
vendored
Normal file
21
vendor/github.com/nlopes/slack/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- tip
|
||||
|
||||
before_install:
|
||||
- export PATH=$HOME/gopath/bin:$PATH
|
||||
|
||||
script:
|
||||
- go test -race ./...
|
||||
- go test -cover ./...
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
|
||||
git:
|
||||
depth: 10
|
2
vendor/github.com/nlopes/slack/README.md
generated
vendored
2
vendor/github.com/nlopes/slack/README.md
generated
vendored
@ -1,6 +1,8 @@
|
||||
Slack API in Go [![GoDoc](https://godoc.org/github.com/nlopes/slack?status.svg)](https://godoc.org/github.com/nlopes/slack) [![Build Status](https://travis-ci.org/nlopes/slack.svg)](https://travis-ci.org/nlopes/slack)
|
||||
===============
|
||||
|
||||
[![Join the chat at https://gitter.im/go-slack/Lobby](https://badges.gitter.im/go-slack/Lobby.svg)](https://gitter.im/go-slack/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
This library supports most if not all of the `api.slack.com` REST
|
||||
calls, as well as the Real-Time Messaging protocol over websocket, in
|
||||
a fully managed way.
|
||||
|
36
vendor/github.com/nlopes/slack/admin.go
generated
vendored
36
vendor/github.com/nlopes/slack/admin.go
generated
vendored
@ -12,9 +12,9 @@ type adminResponse struct {
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
func adminRequest(ctx context.Context, method string, teamName string, values url.Values, debug bool) (*adminResponse, error) {
|
||||
func adminRequest(ctx context.Context, client HTTPRequester, method string, teamName string, values url.Values, debug bool) (*adminResponse, error) {
|
||||
adminResponse := &adminResponse{}
|
||||
err := parseAdminResponse(ctx, method, teamName, values, adminResponse, debug)
|
||||
err := parseAdminResponse(ctx, client, method, teamName, values, adminResponse, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -35,12 +35,12 @@ func (api *Client) DisableUser(teamName string, uid string) error {
|
||||
func (api *Client) DisableUserContext(ctx context.Context, teamName string, uid string) error {
|
||||
values := url.Values{
|
||||
"user": {uid},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, "setInactive", teamName, values, api.debug)
|
||||
_, err := adminRequest(ctx, api.httpclient, "setInactive", teamName, values, api.debug)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to disable user with id '%s': %s", uid, err)
|
||||
}
|
||||
@ -61,12 +61,12 @@ func (api *Client) InviteGuestContext(ctx context.Context, teamName, channel, fi
|
||||
"first_name": {firstName},
|
||||
"last_name": {lastName},
|
||||
"ultra_restricted": {"1"},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, "invite", teamName, values, api.debug)
|
||||
_, err := adminRequest(ctx, api.httpclient, "invite", teamName, values, api.debug)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to invite single-channel guest: %s", err)
|
||||
}
|
||||
@ -87,12 +87,12 @@ func (api *Client) InviteRestrictedContext(ctx context.Context, teamName, channe
|
||||
"first_name": {firstName},
|
||||
"last_name": {lastName},
|
||||
"restricted": {"1"},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, "invite", teamName, values, api.debug)
|
||||
_, err := adminRequest(ctx, api.httpclient, "invite", teamName, values, api.debug)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to restricted account: %s", err)
|
||||
}
|
||||
@ -111,12 +111,12 @@ func (api *Client) InviteToTeamContext(ctx context.Context, teamName, firstName,
|
||||
"email": {emailAddress},
|
||||
"first_name": {firstName},
|
||||
"last_name": {lastName},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, "invite", teamName, values, api.debug)
|
||||
_, err := adminRequest(ctx, api.httpclient, "invite", teamName, values, api.debug)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to invite to team: %s", err)
|
||||
}
|
||||
@ -133,12 +133,12 @@ func (api *Client) SetRegular(teamName, user string) error {
|
||||
func (api *Client) SetRegularContext(ctx context.Context, teamName, user string) error {
|
||||
values := url.Values{
|
||||
"user": {user},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, "setRegular", teamName, values, api.debug)
|
||||
_, err := adminRequest(ctx, api.httpclient, "setRegular", teamName, values, api.debug)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to change the user (%s) to a regular user: %s", user, err)
|
||||
}
|
||||
@ -155,12 +155,12 @@ func (api *Client) SendSSOBindingEmail(teamName, user string) error {
|
||||
func (api *Client) SendSSOBindingEmailContext(ctx context.Context, teamName, user string) error {
|
||||
values := url.Values{
|
||||
"user": {user},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, "sendSSOBind", teamName, values, api.debug)
|
||||
_, err := adminRequest(ctx, api.httpclient, "sendSSOBind", teamName, values, api.debug)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to send SSO binding email for user (%s): %s", user, err)
|
||||
}
|
||||
@ -178,12 +178,12 @@ func (api *Client) SetUltraRestrictedContext(ctx context.Context, teamName, uid,
|
||||
values := url.Values{
|
||||
"user": {uid},
|
||||
"channel": {channel},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, "setUltraRestricted", teamName, values, api.debug)
|
||||
_, err := adminRequest(ctx, api.httpclient, "setUltraRestricted", teamName, values, api.debug)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to ultra-restrict account: %s", err)
|
||||
}
|
||||
@ -200,12 +200,12 @@ func (api *Client) SetRestricted(teamName, uid string) error {
|
||||
func (api *Client) SetRestrictedContext(ctx context.Context, teamName, uid string) error {
|
||||
values := url.Values{
|
||||
"user": {uid},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"set_active": {"true"},
|
||||
"_attempts": {"1"},
|
||||
}
|
||||
|
||||
_, err := adminRequest(ctx, "setRestricted", teamName, values, api.debug)
|
||||
_, err := adminRequest(ctx, api.httpclient, "setRestricted", teamName, values, api.debug)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to restrict account: %s", err)
|
||||
}
|
||||
|
6
vendor/github.com/nlopes/slack/attachments.go
generated
vendored
6
vendor/github.com/nlopes/slack/attachments.go
generated
vendored
@ -25,6 +25,7 @@ type AttachmentAction struct {
|
||||
SelectedOptions []AttachmentActionOption `json:"selected_options,omitempty"` // Optional. The first element of this array will be set as the pre-selected option for this menu.
|
||||
OptionGroups []AttachmentActionOptionGroup `json:"option_groups,omitempty"` // Optional.
|
||||
Confirm *ConfirmationField `json:"confirm,omitempty"` // Optional.
|
||||
URL string `json:"url,omitempty"` // Optional.
|
||||
}
|
||||
|
||||
// AttachmentActionOption the individual option to appear in action menu.
|
||||
@ -48,6 +49,9 @@ type AttachmentActionCallback struct {
|
||||
Channel Channel `json:"channel"`
|
||||
User User `json:"user"`
|
||||
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
|
||||
OriginalMessage Message `json:"original_message"`
|
||||
|
||||
ActionTs string `json:"action_ts"`
|
||||
@ -55,6 +59,7 @@ type AttachmentActionCallback struct {
|
||||
AttachmentID string `json:"attachment_id"`
|
||||
Token string `json:"token"`
|
||||
ResponseURL string `json:"response_url"`
|
||||
TriggerID string `json:"trigger_id"`
|
||||
}
|
||||
|
||||
// ConfirmationField are used to ask users to confirm actions
|
||||
@ -71,6 +76,7 @@ type Attachment struct {
|
||||
Fallback string `json:"fallback"`
|
||||
|
||||
CallbackID string `json:"callback_id,omitempty"`
|
||||
ID int `json:"id,omitempty"`
|
||||
|
||||
AuthorName string `json:"author_name,omitempty"`
|
||||
AuthorSubname string `json:"author_subname,omitempty"`
|
||||
|
9
vendor/github.com/nlopes/slack/bots.go
generated
vendored
9
vendor/github.com/nlopes/slack/bots.go
generated
vendored
@ -19,9 +19,9 @@ type botResponseFull struct {
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
func botRequest(ctx context.Context, path string, values url.Values, debug bool) (*botResponseFull, error) {
|
||||
func botRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*botResponseFull, error) {
|
||||
response := &botResponseFull{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := post(ctx, client, path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -39,10 +39,11 @@ func (api *Client) GetBotInfo(bot string) (*Bot, error) {
|
||||
// GetBotInfoContext will retrieve the complete bot information using a custom context
|
||||
func (api *Client) GetBotInfoContext(ctx context.Context, bot string) (*Bot, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"bot": {bot},
|
||||
}
|
||||
response, err := botRequest(ctx, "bots.info", values, api.debug)
|
||||
|
||||
response, err := botRequest(ctx, api.httpclient, "bots.info", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
242
vendor/github.com/nlopes/slack/channels.go
generated
vendored
242
vendor/github.com/nlopes/slack/channels.go
generated
vendored
@ -20,14 +20,15 @@ type channelResponseFull struct {
|
||||
// Channel contains information about the channel
|
||||
type Channel struct {
|
||||
groupConversation
|
||||
IsChannel bool `json:"is_channel"`
|
||||
IsGeneral bool `json:"is_general"`
|
||||
IsMember bool `json:"is_member"`
|
||||
IsChannel bool `json:"is_channel"`
|
||||
IsGeneral bool `json:"is_general"`
|
||||
IsMember bool `json:"is_member"`
|
||||
Locale string `json:"locale"`
|
||||
}
|
||||
|
||||
func channelRequest(ctx context.Context, path string, values url.Values, debug bool) (*channelResponseFull, error) {
|
||||
func channelRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*channelResponseFull, error) {
|
||||
response := &channelResponseFull{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := postForm(ctx, client, SLACK_API+path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -38,53 +39,62 @@ func channelRequest(ctx context.Context, path string, values url.Values, debug b
|
||||
}
|
||||
|
||||
// ArchiveChannel archives the given channel
|
||||
func (api *Client) ArchiveChannel(channel string) error {
|
||||
return api.ArchiveChannelContext(context.Background(), channel)
|
||||
// see https://api.slack.com/methods/channels.archive
|
||||
func (api *Client) ArchiveChannel(channelID string) error {
|
||||
return api.ArchiveChannelContext(context.Background(), channelID)
|
||||
}
|
||||
|
||||
// ArchiveChannelContext archives the given channel with a custom context
|
||||
func (api *Client) ArchiveChannelContext(ctx context.Context, channel string) error {
|
||||
// see https://api.slack.com/methods/channels.archive
|
||||
func (api *Client) ArchiveChannelContext(ctx context.Context, channelID string) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"channel": {channel},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
}
|
||||
_, err := channelRequest(ctx, "channels.archive", values, api.debug)
|
||||
if err != nil {
|
||||
|
||||
if _, err = channelRequest(ctx, api.httpclient, "channels.archive", values, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnarchiveChannel unarchives the given channel
|
||||
func (api *Client) UnarchiveChannel(channel string) error {
|
||||
return api.UnarchiveChannelContext(context.Background(), channel)
|
||||
// see https://api.slack.com/methods/channels.unarchive
|
||||
func (api *Client) UnarchiveChannel(channelID string) error {
|
||||
return api.UnarchiveChannelContext(context.Background(), channelID)
|
||||
}
|
||||
|
||||
// UnarchiveChannelContext unarchives the given channel with a custom context
|
||||
func (api *Client) UnarchiveChannelContext(ctx context.Context, channel string) error {
|
||||
// see https://api.slack.com/methods/channels.unarchive
|
||||
func (api *Client) UnarchiveChannelContext(ctx context.Context, channelID string) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"channel": {channel},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
}
|
||||
_, err := channelRequest(ctx, "channels.unarchive", values, api.debug)
|
||||
if err != nil {
|
||||
|
||||
if _, err = channelRequest(ctx, api.httpclient, "channels.unarchive", values, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateChannel creates a channel with the given name and returns a *Channel
|
||||
func (api *Client) CreateChannel(channel string) (*Channel, error) {
|
||||
return api.CreateChannelContext(context.Background(), channel)
|
||||
// see https://api.slack.com/methods/channels.create
|
||||
func (api *Client) CreateChannel(channelName string) (*Channel, error) {
|
||||
return api.CreateChannelContext(context.Background(), channelName)
|
||||
}
|
||||
|
||||
// CreateChannelContext creates a channel with the given name and returns a *Channel with a custom context
|
||||
func (api *Client) CreateChannelContext(ctx context.Context, channel string) (*Channel, error) {
|
||||
// see https://api.slack.com/methods/channels.create
|
||||
func (api *Client) CreateChannelContext(ctx context.Context, channelName string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"name": {channel},
|
||||
"token": {api.token},
|
||||
"name": {channelName},
|
||||
}
|
||||
response, err := channelRequest(ctx, "channels.create", values, api.debug)
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.create", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -92,15 +102,17 @@ func (api *Client) CreateChannelContext(ctx context.Context, channel string) (*C
|
||||
}
|
||||
|
||||
// GetChannelHistory retrieves the channel history
|
||||
func (api *Client) GetChannelHistory(channel string, params HistoryParameters) (*History, error) {
|
||||
return api.GetChannelHistoryContext(context.Background(), channel, params)
|
||||
// see https://api.slack.com/methods/channels.history
|
||||
func (api *Client) GetChannelHistory(channelID string, params HistoryParameters) (*History, error) {
|
||||
return api.GetChannelHistoryContext(context.Background(), channelID, params)
|
||||
}
|
||||
|
||||
// GetChannelHistoryContext retrieves the channel history with a custom context
|
||||
func (api *Client) GetChannelHistoryContext(ctx context.Context, channel string, params HistoryParameters) (*History, error) {
|
||||
// see https://api.slack.com/methods/channels.history
|
||||
func (api *Client) GetChannelHistoryContext(ctx context.Context, channelID string, params HistoryParameters) (*History, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"channel": {channel},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
}
|
||||
if params.Latest != DEFAULT_HISTORY_LATEST {
|
||||
values.Add("latest", params.Latest)
|
||||
@ -118,6 +130,7 @@ func (api *Client) GetChannelHistoryContext(ctx context.Context, channel string,
|
||||
values.Add("inclusive", "0")
|
||||
}
|
||||
}
|
||||
|
||||
if params.Unreads != DEFAULT_HISTORY_UNREADS {
|
||||
if params.Unreads {
|
||||
values.Add("unreads", "1")
|
||||
@ -125,7 +138,8 @@ func (api *Client) GetChannelHistoryContext(ctx context.Context, channel string,
|
||||
values.Add("unreads", "0")
|
||||
}
|
||||
}
|
||||
response, err := channelRequest(ctx, "channels.history", values, api.debug)
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.history", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -133,17 +147,20 @@ func (api *Client) GetChannelHistoryContext(ctx context.Context, channel string,
|
||||
}
|
||||
|
||||
// GetChannelInfo retrieves the given channel
|
||||
func (api *Client) GetChannelInfo(channel string) (*Channel, error) {
|
||||
return api.GetChannelInfoContext(context.Background(), channel)
|
||||
// see https://api.slack.com/methods/channels.info
|
||||
func (api *Client) GetChannelInfo(channelID string) (*Channel, error) {
|
||||
return api.GetChannelInfoContext(context.Background(), channelID)
|
||||
}
|
||||
|
||||
// GetChannelInfoContext retrieves the given channel with a custom context
|
||||
func (api *Client) GetChannelInfoContext(ctx context.Context, channel string) (*Channel, error) {
|
||||
// see https://api.slack.com/methods/channels.info
|
||||
func (api *Client) GetChannelInfoContext(ctx context.Context, channelID string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"channel": {channel},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
}
|
||||
response, err := channelRequest(ctx, "channels.info", values, api.debug)
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.info", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -151,18 +168,21 @@ func (api *Client) GetChannelInfoContext(ctx context.Context, channel string) (*
|
||||
}
|
||||
|
||||
// InviteUserToChannel invites a user to a given channel and returns a *Channel
|
||||
func (api *Client) InviteUserToChannel(channel, user string) (*Channel, error) {
|
||||
return api.InviteUserToChannelContext(context.Background(), channel, user)
|
||||
// see https://api.slack.com/methods/channels.invite
|
||||
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
|
||||
func (api *Client) InviteUserToChannelContext(ctx context.Context, channel, user string) (*Channel, error) {
|
||||
// see https://api.slack.com/methods/channels.invite
|
||||
func (api *Client) InviteUserToChannelContext(ctx context.Context, channelID, user string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"channel": {channel},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"user": {user},
|
||||
}
|
||||
response, err := channelRequest(ctx, "channels.invite", values, api.debug)
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.invite", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -170,17 +190,20 @@ func (api *Client) InviteUserToChannelContext(ctx context.Context, channel, user
|
||||
}
|
||||
|
||||
// JoinChannel joins the currently authenticated user to a channel
|
||||
func (api *Client) JoinChannel(channel string) (*Channel, error) {
|
||||
return api.JoinChannelContext(context.Background(), channel)
|
||||
// see https://api.slack.com/methods/channels.join
|
||||
func (api *Client) JoinChannel(channelName string) (*Channel, error) {
|
||||
return api.JoinChannelContext(context.Background(), channelName)
|
||||
}
|
||||
|
||||
// JoinChannelContext joins the currently authenticated user to a channel with a custom context
|
||||
func (api *Client) JoinChannelContext(ctx context.Context, channel string) (*Channel, error) {
|
||||
// see https://api.slack.com/methods/channels.join
|
||||
func (api *Client) JoinChannelContext(ctx context.Context, channelName string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"name": {channel},
|
||||
"token": {api.token},
|
||||
"name": {channelName},
|
||||
}
|
||||
response, err := channelRequest(ctx, "channels.join", values, api.debug)
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.join", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -188,59 +211,66 @@ func (api *Client) JoinChannelContext(ctx context.Context, channel string) (*Cha
|
||||
}
|
||||
|
||||
// LeaveChannel makes the authenticated user leave the given channel
|
||||
func (api *Client) LeaveChannel(channel string) (bool, error) {
|
||||
return api.LeaveChannelContext(context.Background(), channel)
|
||||
// see https://api.slack.com/methods/channels.leave
|
||||
func (api *Client) LeaveChannel(channelID string) (bool, error) {
|
||||
return api.LeaveChannelContext(context.Background(), channelID)
|
||||
}
|
||||
|
||||
// LeaveChannelContext makes the authenticated user leave the given channel with a custom context
|
||||
func (api *Client) LeaveChannelContext(ctx context.Context, channel string) (bool, error) {
|
||||
// see https://api.slack.com/methods/channels.leave
|
||||
func (api *Client) LeaveChannelContext(ctx context.Context, channelID string) (bool, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"channel": {channel},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
}
|
||||
response, err := channelRequest(ctx, "channels.leave", values, api.debug)
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.leave", values, api.debug)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if response.NotInChannel {
|
||||
return response.NotInChannel, nil
|
||||
}
|
||||
return false, nil
|
||||
|
||||
return response.NotInChannel, nil
|
||||
}
|
||||
|
||||
// KickUserFromChannel kicks a user from a given channel
|
||||
func (api *Client) KickUserFromChannel(channel, user string) error {
|
||||
return api.KickUserFromChannelContext(context.Background(), channel, user)
|
||||
// see https://api.slack.com/methods/channels.kick
|
||||
func (api *Client) KickUserFromChannel(channelID, user string) error {
|
||||
return api.KickUserFromChannelContext(context.Background(), channelID, user)
|
||||
}
|
||||
|
||||
// KickUserFromChannelContext kicks a user from a given channel with a custom context
|
||||
func (api *Client) KickUserFromChannelContext(ctx context.Context, channel, user string) error {
|
||||
// see https://api.slack.com/methods/channels.kick
|
||||
func (api *Client) KickUserFromChannelContext(ctx context.Context, channelID, user string) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"channel": {channel},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"user": {user},
|
||||
}
|
||||
_, err := channelRequest(ctx, "channels.kick", values, api.debug)
|
||||
if err != nil {
|
||||
|
||||
if _, err = channelRequest(ctx, api.httpclient, "channels.kick", values, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetChannels retrieves all the channels
|
||||
// see https://api.slack.com/methods/channels.list
|
||||
func (api *Client) GetChannels(excludeArchived bool) ([]Channel, error) {
|
||||
return api.GetChannelsContext(context.Background(), excludeArchived)
|
||||
}
|
||||
|
||||
// GetChannelsContext retrieves all the channels with a custom context
|
||||
// see https://api.slack.com/methods/channels.list
|
||||
func (api *Client) GetChannelsContext(ctx context.Context, excludeArchived bool) ([]Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if excludeArchived {
|
||||
values.Add("exclude_archived", "1")
|
||||
}
|
||||
response, err := channelRequest(ctx, "channels.list", values, api.debug)
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.list", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -252,40 +282,46 @@ func (api *Client) GetChannelsContext(ctx context.Context, excludeArchived bool)
|
||||
// timer before making the call. In this way, any further updates needed during the timeout will not generate extra calls
|
||||
// (just one per channel). This is useful for when reading scroll-back history, or following a busy live channel. A
|
||||
// timeout of 5 seconds is a good starting point. Be sure to flush these calls on shutdown/logout.
|
||||
func (api *Client) SetChannelReadMark(channel, ts string) error {
|
||||
return api.SetChannelReadMarkContext(context.Background(), channel, ts)
|
||||
// see https://api.slack.com/methods/channels.mark
|
||||
func (api *Client) SetChannelReadMark(channelID, ts string) error {
|
||||
return api.SetChannelReadMarkContext(context.Background(), channelID, ts)
|
||||
}
|
||||
|
||||
// SetChannelReadMarkContext sets the read mark of a given channel to a specific point with a custom context
|
||||
// For more details see SetChannelReadMark documentation
|
||||
func (api *Client) SetChannelReadMarkContext(ctx context.Context, channel, ts string) error {
|
||||
// see https://api.slack.com/methods/channels.mark
|
||||
func (api *Client) SetChannelReadMarkContext(ctx context.Context, channelID, ts string) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"channel": {channel},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"ts": {ts},
|
||||
}
|
||||
_, err := channelRequest(ctx, "channels.mark", values, api.debug)
|
||||
if err != nil {
|
||||
|
||||
if _, err = channelRequest(ctx, api.httpclient, "channels.mark", values, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RenameChannel renames a given channel
|
||||
func (api *Client) RenameChannel(channel, name string) (*Channel, error) {
|
||||
return api.RenameChannelContext(context.Background(), channel, name)
|
||||
// see https://api.slack.com/methods/channels.rename
|
||||
func (api *Client) RenameChannel(channelID, name string) (*Channel, error) {
|
||||
return api.RenameChannelContext(context.Background(), channelID, name)
|
||||
}
|
||||
|
||||
// RenameChannelContext renames a given channel with a custom context
|
||||
func (api *Client) RenameChannelContext(ctx context.Context, channel, name string) (*Channel, error) {
|
||||
// see https://api.slack.com/methods/channels.rename
|
||||
func (api *Client) RenameChannelContext(ctx context.Context, channelID, name string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"channel": {channel},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"name": {name},
|
||||
}
|
||||
|
||||
// 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, "channels.rename", values, api.debug)
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.rename", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -293,18 +329,21 @@ func (api *Client) RenameChannelContext(ctx context.Context, channel, name strin
|
||||
}
|
||||
|
||||
// SetChannelPurpose sets the channel purpose and returns the purpose that was successfully set
|
||||
func (api *Client) SetChannelPurpose(channel, purpose string) (string, error) {
|
||||
return api.SetChannelPurposeContext(context.Background(), channel, purpose)
|
||||
// see https://api.slack.com/methods/channels.setPurpose
|
||||
func (api *Client) SetChannelPurpose(channelID, purpose string) (string, error) {
|
||||
return api.SetChannelPurposeContext(context.Background(), channelID, purpose)
|
||||
}
|
||||
|
||||
// SetChannelPurposeContext sets the channel purpose and returns the purpose that was successfully set with a custom context
|
||||
func (api *Client) SetChannelPurposeContext(ctx context.Context, channel, purpose string) (string, error) {
|
||||
// see https://api.slack.com/methods/channels.setPurpose
|
||||
func (api *Client) SetChannelPurposeContext(ctx context.Context, channelID, purpose string) (string, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"channel": {channel},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"purpose": {purpose},
|
||||
}
|
||||
response, err := channelRequest(ctx, "channels.setPurpose", values, api.debug)
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.setPurpose", values, api.debug)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -312,18 +351,21 @@ func (api *Client) SetChannelPurposeContext(ctx context.Context, channel, purpos
|
||||
}
|
||||
|
||||
// SetChannelTopic sets the channel topic and returns the topic that was successfully set
|
||||
func (api *Client) SetChannelTopic(channel, topic string) (string, error) {
|
||||
return api.SetChannelTopicContext(context.Background(), channel, topic)
|
||||
// see https://api.slack.com/methods/channels.setTopic
|
||||
func (api *Client) SetChannelTopic(channelID, topic string) (string, error) {
|
||||
return api.SetChannelTopicContext(context.Background(), channelID, topic)
|
||||
}
|
||||
|
||||
// SetChannelTopicContext sets the channel topic and returns the topic that was successfully set with a custom context
|
||||
func (api *Client) SetChannelTopicContext(ctx context.Context, channel, topic string) (string, error) {
|
||||
// see https://api.slack.com/methods/channels.setTopic
|
||||
func (api *Client) SetChannelTopicContext(ctx context.Context, channelID, topic string) (string, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"channel": {channel},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"topic": {topic},
|
||||
}
|
||||
response, err := channelRequest(ctx, "channels.setTopic", values, api.debug)
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.setTopic", values, api.debug)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -331,18 +373,20 @@ func (api *Client) SetChannelTopicContext(ctx context.Context, channel, topic st
|
||||
}
|
||||
|
||||
// GetChannelReplies gets an entire thread (a message plus all the messages in reply to it).
|
||||
func (api *Client) GetChannelReplies(channel, thread_ts string) ([]Message, error) {
|
||||
return api.GetChannelRepliesContext(context.Background(), channel, thread_ts)
|
||||
// see https://api.slack.com/methods/channels.replies
|
||||
func (api *Client) GetChannelReplies(channelID, thread_ts string) ([]Message, error) {
|
||||
return api.GetChannelRepliesContext(context.Background(), channelID, thread_ts)
|
||||
}
|
||||
|
||||
// GetChannelRepliesContext gets an entire thread (a message plus all the messages in reply to it) with a custom context
|
||||
func (api *Client) GetChannelRepliesContext(ctx context.Context, channel, thread_ts string) ([]Message, error) {
|
||||
// see https://api.slack.com/methods/channels.replies
|
||||
func (api *Client) GetChannelRepliesContext(ctx context.Context, channelID, thread_ts string) ([]Message, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"channel": {channel},
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"thread_ts": {thread_ts},
|
||||
}
|
||||
response, err := channelRequest(ctx, "channels.replies", values, api.debug)
|
||||
response, err := channelRequest(ctx, api.httpclient, "channels.replies", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
106
vendor/github.com/nlopes/slack/chat.go
generated
vendored
106
vendor/github.com/nlopes/slack/chat.go
generated
vendored
@ -10,9 +10,10 @@ import (
|
||||
|
||||
const (
|
||||
DEFAULT_MESSAGE_USERNAME = ""
|
||||
DEFAULT_MESSAGE_THREAD_TIMESTAMP = ""
|
||||
DEFAULT_MESSAGE_REPLY_BROADCAST = false
|
||||
DEFAULT_MESSAGE_ASUSER = false
|
||||
DEFAULT_MESSAGE_PARSE = ""
|
||||
DEFAULT_MESSAGE_THREAD_TIMESTAMP = ""
|
||||
DEFAULT_MESSAGE_LINK_NAMES = 0
|
||||
DEFAULT_MESSAGE_UNFURL_LINKS = false
|
||||
DEFAULT_MESSAGE_UNFURL_MEDIA = true
|
||||
@ -31,11 +32,11 @@ type chatResponseFull struct {
|
||||
|
||||
// PostMessageParameters contains all the parameters necessary (including the optional ones) for a PostMessage() request
|
||||
type PostMessageParameters struct {
|
||||
Text string `json:"text"`
|
||||
Username string `json:"user_name"`
|
||||
AsUser bool `json:"as_user"`
|
||||
Parse string `json:"parse"`
|
||||
ThreadTimestamp string `json:"thread_ts"`
|
||||
ReplyBroadcast bool `json:"reply_broadcast"`
|
||||
LinkNames int `json:"link_names"`
|
||||
Attachments []Attachment `json:"attachments"`
|
||||
UnfurlLinks bool `json:"unfurl_links"`
|
||||
@ -44,22 +45,28 @@ type PostMessageParameters struct {
|
||||
IconEmoji string `json:"icon_emoji"`
|
||||
Markdown bool `json:"mrkdwn,omitempty"`
|
||||
EscapeText bool `json:"escape_text"`
|
||||
|
||||
// chat.postEphemeral support
|
||||
Channel string `json:"channel"`
|
||||
User string `json:"user"`
|
||||
}
|
||||
|
||||
// NewPostMessageParameters provides an instance of PostMessageParameters with all the sane default values set
|
||||
func NewPostMessageParameters() PostMessageParameters {
|
||||
return PostMessageParameters{
|
||||
Username: DEFAULT_MESSAGE_USERNAME,
|
||||
AsUser: DEFAULT_MESSAGE_ASUSER,
|
||||
Parse: DEFAULT_MESSAGE_PARSE,
|
||||
LinkNames: DEFAULT_MESSAGE_LINK_NAMES,
|
||||
Attachments: nil,
|
||||
UnfurlLinks: DEFAULT_MESSAGE_UNFURL_LINKS,
|
||||
UnfurlMedia: DEFAULT_MESSAGE_UNFURL_MEDIA,
|
||||
IconURL: DEFAULT_MESSAGE_ICON_URL,
|
||||
IconEmoji: DEFAULT_MESSAGE_ICON_EMOJI,
|
||||
Markdown: DEFAULT_MESSAGE_MARKDOWN,
|
||||
EscapeText: DEFAULT_MESSAGE_ESCAPE_TEXT,
|
||||
Username: DEFAULT_MESSAGE_USERNAME,
|
||||
User: DEFAULT_MESSAGE_USERNAME,
|
||||
AsUser: DEFAULT_MESSAGE_ASUSER,
|
||||
Parse: DEFAULT_MESSAGE_PARSE,
|
||||
ThreadTimestamp: DEFAULT_MESSAGE_THREAD_TIMESTAMP,
|
||||
LinkNames: DEFAULT_MESSAGE_LINK_NAMES,
|
||||
Attachments: nil,
|
||||
UnfurlLinks: DEFAULT_MESSAGE_UNFURL_LINKS,
|
||||
UnfurlMedia: DEFAULT_MESSAGE_UNFURL_MEDIA,
|
||||
IconURL: DEFAULT_MESSAGE_ICON_URL,
|
||||
IconEmoji: DEFAULT_MESSAGE_ICON_EMOJI,
|
||||
Markdown: DEFAULT_MESSAGE_MARKDOWN,
|
||||
EscapeText: DEFAULT_MESSAGE_ESCAPE_TEXT,
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,12 +109,43 @@ func (api *Client) PostMessageContext(ctx context.Context, channel, text string,
|
||||
return respChannel, respTimestamp, err
|
||||
}
|
||||
|
||||
// PostEphemeral sends an ephemeral message to a user in 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) PostEphemeral(channel, userID string, options ...MsgOption) (string, error) {
|
||||
options = append(options, MsgOptionPostEphemeral())
|
||||
return api.PostEphemeralContext(
|
||||
context.Background(),
|
||||
channel,
|
||||
userID,
|
||||
options...,
|
||||
)
|
||||
}
|
||||
|
||||
// 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, channel, userID string, options ...MsgOption) (string, error) {
|
||||
path, values, err := ApplyMsgOptions(api.token, channel, options...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
values.Add("user", userID)
|
||||
|
||||
response, err := chatRequest(ctx, api.httpclient, path, values, api.debug)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return response.Timestamp, nil
|
||||
}
|
||||
|
||||
// UpdateMessage updates a message in a channel
|
||||
func (api *Client) UpdateMessage(channel, timestamp, text string) (string, string, string, error) {
|
||||
return api.UpdateMessageContext(context.Background(), channel, timestamp, text)
|
||||
}
|
||||
|
||||
// UpdateMessage updates a message in a channel
|
||||
// UpdateMessageContext updates a message in a channel
|
||||
func (api *Client) UpdateMessageContext(ctx context.Context, channel, timestamp, text string) (string, string, string, error) {
|
||||
return api.SendMessageContext(ctx, channel, MsgOptionUpdate(timestamp), MsgOptionText(text, true))
|
||||
}
|
||||
@ -119,12 +157,12 @@ func (api *Client) SendMessage(channel string, options ...MsgOption) (string, st
|
||||
|
||||
// SendMessageContext more flexible method for configuring messages with a custom context.
|
||||
func (api *Client) SendMessageContext(ctx context.Context, channel string, options ...MsgOption) (string, string, string, error) {
|
||||
channel, values, err := ApplyMsgOptions(api.config.token, channel, options...)
|
||||
channel, values, err := ApplyMsgOptions(api.token, channel, options...)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
response, err := chatRequest(ctx, channel, values, api.debug)
|
||||
response, err := chatRequest(ctx, api.httpclient, channel, values, api.debug)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
@ -156,9 +194,9 @@ func escapeMessage(message string) string {
|
||||
return replacer.Replace(message)
|
||||
}
|
||||
|
||||
func chatRequest(ctx context.Context, path string, values url.Values, debug bool) (*chatResponseFull, error) {
|
||||
func chatRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*chatResponseFull, error) {
|
||||
response := &chatResponseFull{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := post(ctx, client, path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -171,9 +209,10 @@ func chatRequest(ctx context.Context, path string, values url.Values, debug bool
|
||||
type sendMode string
|
||||
|
||||
const (
|
||||
chatUpdate sendMode = "chat.update"
|
||||
chatPostMessage sendMode = "chat.postMessage"
|
||||
chatDelete sendMode = "chat.delete"
|
||||
chatUpdate sendMode = "chat.update"
|
||||
chatPostMessage sendMode = "chat.postMessage"
|
||||
chatDelete sendMode = "chat.delete"
|
||||
chatPostEphemeral sendMode = "chat.postEphemeral"
|
||||
)
|
||||
|
||||
type sendConfig struct {
|
||||
@ -193,6 +232,15 @@ func MsgOptionPost() MsgOption {
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionPostEphemeral posts an ephemeral message
|
||||
func MsgOptionPostEphemeral() MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.mode = chatPostEphemeral
|
||||
config.values.Del("ts")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionUpdate updates a message based on the timestamp.
|
||||
func MsgOptionUpdate(timestamp string) MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
@ -256,6 +304,14 @@ func MsgOptionEnableLinkUnfurl() MsgOption {
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionDisableLinkUnfurl disables link unfurling
|
||||
func MsgOptionDisableLinkUnfurl() MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
config.values.Set("unfurl_links", "false")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MsgOptionDisableMediaUnfurl disables media unfurling.
|
||||
func MsgOptionDisableMediaUnfurl() MsgOption {
|
||||
return func(config *sendConfig) error {
|
||||
@ -279,6 +335,11 @@ func MsgOptionPostMessageParameters(params PostMessageParameters) MsgOption {
|
||||
config.values.Set("username", string(params.Username))
|
||||
}
|
||||
|
||||
// chat.postEphemeral support
|
||||
if params.User != DEFAULT_MESSAGE_USERNAME {
|
||||
config.values.Set("user", params.User)
|
||||
}
|
||||
|
||||
// never generates an error.
|
||||
MsgOptionAsUser(params.AsUser)(config)
|
||||
|
||||
@ -314,6 +375,9 @@ func MsgOptionPostMessageParameters(params PostMessageParameters) MsgOption {
|
||||
if params.ThreadTimestamp != DEFAULT_MESSAGE_THREAD_TIMESTAMP {
|
||||
config.values.Set("thread_ts", params.ThreadTimestamp)
|
||||
}
|
||||
if params.ReplyBroadcast != DEFAULT_MESSAGE_REPLY_BROADCAST {
|
||||
config.values.Set("reply_broadcast", "true")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
555
vendor/github.com/nlopes/slack/conversation.go
generated
vendored
555
vendor/github.com/nlopes/slack/conversation.go
generated
vendored
@ -1,5 +1,13 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Conversation is the foundation for IM and BaseGroupConversation
|
||||
type conversation struct {
|
||||
ID string `json:"id"`
|
||||
@ -9,6 +17,20 @@ type conversation struct {
|
||||
Latest *Message `json:"latest,omitempty"`
|
||||
UnreadCount int `json:"unread_count,omitempty"`
|
||||
UnreadCountDisplay int `json:"unread_count_display,omitempty"`
|
||||
IsGroup bool `json:"is_group"`
|
||||
IsShared bool `json:"is_shared"`
|
||||
IsIM bool `json:"is_im"`
|
||||
IsExtShared bool `json:"is_ext_shared"`
|
||||
IsOrgShared bool `json:"is_org_shared"`
|
||||
IsPendingExtShared bool `json:"is_pending_ext_shared"`
|
||||
IsPrivate bool `json:"is_private"`
|
||||
IsMpIM bool `json:"is_mpim"`
|
||||
Unlinked int `json:"unlinked"`
|
||||
NameNormalized string `json:"name_normalized"`
|
||||
NumMembers int `json:"num_members"`
|
||||
Priority float64 `json:"priority"`
|
||||
// TODO support pending_shared
|
||||
// TODO support previous_names
|
||||
}
|
||||
|
||||
// GroupConversation is the foundation for Group and Channel
|
||||
@ -35,3 +57,536 @@ type Purpose struct {
|
||||
Creator string `json:"creator"`
|
||||
LastSet JSONTime `json:"last_set"`
|
||||
}
|
||||
|
||||
type GetUsersInConversationParameters struct {
|
||||
ChannelID string
|
||||
Cursor string
|
||||
Limit int
|
||||
}
|
||||
|
||||
type responseMetaData struct {
|
||||
NextCursor string `json:"next_cursor"`
|
||||
}
|
||||
|
||||
// GetUsersInConversation returns the list of users in a conversation
|
||||
func (api *Client) GetUsersInConversation(params *GetUsersInConversationParameters) ([]string, string, error) {
|
||||
return api.GetUsersInConversationContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// GetUsersInConversationContext returns the list of users in a conversation with a custom context
|
||||
func (api *Client) GetUsersInConversationContext(ctx context.Context, params *GetUsersInConversationParameters) ([]string, string, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {params.ChannelID},
|
||||
}
|
||||
if params.Cursor != "" {
|
||||
values.Add("cursor", params.Cursor)
|
||||
}
|
||||
if params.Limit != 0 {
|
||||
values.Add("limit", string(params.Limit))
|
||||
}
|
||||
response := struct {
|
||||
Members []string `json:"members"`
|
||||
ResponseMetaData responseMetaData `json:"response_metadata"`
|
||||
SlackResponse
|
||||
}{}
|
||||
err := post(ctx, api.httpclient, "conversations.members", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, "", errors.New(response.Error)
|
||||
}
|
||||
return response.Members, response.ResponseMetaData.NextCursor, nil
|
||||
}
|
||||
|
||||
// ArchiveConversation archives a conversation
|
||||
func (api *Client) ArchiveConversation(channelID string) error {
|
||||
return api.ArchiveConversationContext(context.Background(), channelID)
|
||||
}
|
||||
|
||||
// ArchiveConversationContext archives a conversation with a custom context
|
||||
func (api *Client) ArchiveConversationContext(ctx context.Context, channelID string) error {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
}
|
||||
response := SlackResponse{}
|
||||
err := post(ctx, api.httpclient, "conversations.archive", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !response.Ok {
|
||||
return errors.New(response.Error)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnArchiveConversation reverses conversation archival
|
||||
func (api *Client) UnArchiveConversation(channelID string) error {
|
||||
return api.UnArchiveConversationContext(context.Background(), channelID)
|
||||
}
|
||||
|
||||
// UnArchiveConversationContext reverses conversation archival with a custom context
|
||||
func (api *Client) UnArchiveConversationContext(ctx context.Context, channelID string) error {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
}
|
||||
response := SlackResponse{}
|
||||
err := post(ctx, api.httpclient, "conversations.unarchive", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !response.Ok {
|
||||
return errors.New(response.Error)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetTopicOfConversation sets the topic for a conversation
|
||||
func (api *Client) SetTopicOfConversation(channelID, topic string) (*Channel, error) {
|
||||
return api.SetTopicOfConversationContext(context.Background(), channelID, topic)
|
||||
}
|
||||
|
||||
// SetTopicOfConversationContext sets the topic for a conversation with a custom context
|
||||
func (api *Client) SetTopicOfConversationContext(ctx context.Context, channelID, topic string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"topic": {topic},
|
||||
}
|
||||
response := struct {
|
||||
SlackResponse
|
||||
Channel *Channel `json:"channel"`
|
||||
}{}
|
||||
err := post(ctx, api.httpclient, "conversations.setTopic", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
}
|
||||
return response.Channel, nil
|
||||
}
|
||||
|
||||
// SetPurposeOfConversation sets the purpose for a conversation
|
||||
func (api *Client) SetPurposeOfConversation(channelID, purpose string) (*Channel, error) {
|
||||
return api.SetPurposeOfConversationContext(context.Background(), channelID, purpose)
|
||||
}
|
||||
|
||||
// SetPurposeOfConversationContext sets the purpose for a conversation with a custom context
|
||||
func (api *Client) SetPurposeOfConversationContext(ctx context.Context, channelID, purpose string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"purpose": {purpose},
|
||||
}
|
||||
response := struct {
|
||||
SlackResponse
|
||||
Channel *Channel `json:"channel"`
|
||||
}{}
|
||||
err := post(ctx, api.httpclient, "conversations.setPurpose", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
}
|
||||
return response.Channel, nil
|
||||
}
|
||||
|
||||
// RenameConversation renames a conversation
|
||||
func (api *Client) RenameConversation(channelID, channelName string) (*Channel, error) {
|
||||
return api.RenameConversationContext(context.Background(), channelID, channelName)
|
||||
}
|
||||
|
||||
// RenameConversationContext renames a conversation with a custom context
|
||||
func (api *Client) RenameConversationContext(ctx context.Context, channelID, channelName string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"name": {channelName},
|
||||
}
|
||||
response := struct {
|
||||
SlackResponse
|
||||
Channel *Channel `json:"channel"`
|
||||
}{}
|
||||
err := post(ctx, api.httpclient, "conversations.rename", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
}
|
||||
return response.Channel, nil
|
||||
}
|
||||
|
||||
// InviteUsersToConversation invites users to a channel
|
||||
func (api *Client) InviteUsersToConversation(channelID string, users ...string) (*Channel, error) {
|
||||
return api.InviteUsersToConversationContext(context.Background(), channelID, users...)
|
||||
}
|
||||
|
||||
// InviteUsersToConversationContext invites users to a channel with a custom context
|
||||
func (api *Client) InviteUsersToConversationContext(ctx context.Context, channelID string, users ...string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"users": {strings.Join(users, ",")},
|
||||
}
|
||||
response := struct {
|
||||
SlackResponse
|
||||
Channel *Channel `json:"channel"`
|
||||
}{}
|
||||
err := post(ctx, api.httpclient, "conversations.invite", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
}
|
||||
return response.Channel, nil
|
||||
}
|
||||
|
||||
// KickUserFromConversation removes a user from a conversation
|
||||
func (api *Client) KickUserFromConversation(channelID string, user string) error {
|
||||
return api.KickUserFromConversationContext(context.Background(), channelID, user)
|
||||
}
|
||||
|
||||
// KickUserFromConversationContext removes a user from a conversation with a custom context
|
||||
func (api *Client) KickUserFromConversationContext(ctx context.Context, channelID string, user string) error {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"user": {user},
|
||||
}
|
||||
response := SlackResponse{}
|
||||
err := post(ctx, api.httpclient, "conversations.kick", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !response.Ok {
|
||||
return errors.New(response.Error)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CloseConversation closes a direct message or multi-person direct message
|
||||
func (api *Client) CloseConversation(channelID string) (noOp bool, alreadyClosed bool, err error) {
|
||||
return api.CloseConversationContext(context.Background(), channelID)
|
||||
}
|
||||
|
||||
// CloseConversationContext closes a direct message or multi-person direct message with a custom context
|
||||
func (api *Client) CloseConversationContext(ctx context.Context, channelID string) (noOp bool, alreadyClosed bool, err error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
}
|
||||
response := struct {
|
||||
SlackResponse
|
||||
NoOp bool `json:"no_op"`
|
||||
AlreadyClosed bool `json:"already_closed"`
|
||||
}{}
|
||||
|
||||
err = post(ctx, api.httpclient, "conversations.close", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return false, false, errors.New(response.Error)
|
||||
}
|
||||
return response.NoOp, response.AlreadyClosed, nil
|
||||
}
|
||||
|
||||
// CreateConversation initiates a public or private channel-based conversation
|
||||
func (api *Client) CreateConversation(channelName string, isPrivate bool) (*Channel, error) {
|
||||
return api.CreateConversationContext(context.Background(), channelName, isPrivate)
|
||||
}
|
||||
|
||||
// CreateConversationContext initiates a public or private channel-based conversation with a custom context
|
||||
func (api *Client) CreateConversationContext(ctx context.Context, channelName string, isPrivate bool) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"name": {channelName},
|
||||
"is_private": {strconv.FormatBool(isPrivate)},
|
||||
}
|
||||
response, err := channelRequest(
|
||||
ctx, api.httpclient, "conversations.create", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
}
|
||||
return &response.Channel, nil
|
||||
}
|
||||
|
||||
// GetConversationInfo retrieves information about a conversation
|
||||
func (api *Client) GetConversationInfo(channelID string, includeLocale bool) (*Channel, error) {
|
||||
return api.GetConversationInfoContext(context.Background(), channelID, includeLocale)
|
||||
}
|
||||
|
||||
// GetConversationInfoContext retrieves information about a conversation with a custom context
|
||||
func (api *Client) GetConversationInfoContext(ctx context.Context, channelID string, includeLocale bool) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
"include_locale": {strconv.FormatBool(includeLocale)},
|
||||
}
|
||||
response, err := channelRequest(
|
||||
ctx, api.httpclient, "conversations.info", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
}
|
||||
return &response.Channel, nil
|
||||
}
|
||||
|
||||
// LeaveConversation leaves a conversation
|
||||
func (api *Client) LeaveConversation(channelID string) (bool, error) {
|
||||
return api.LeaveConversationContext(context.Background(), channelID)
|
||||
}
|
||||
|
||||
// LeaveConversationContext leaves a conversation with a custom context
|
||||
func (api *Client) LeaveConversationContext(ctx context.Context, channelID string) (bool, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {channelID},
|
||||
}
|
||||
|
||||
response, err := channelRequest(ctx, api.httpclient, "conversations.leave", values, api.debug)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return response.NotInChannel, nil
|
||||
}
|
||||
|
||||
type GetConversationRepliesParameters struct {
|
||||
ChannelID string
|
||||
Timestamp string
|
||||
Cursor string
|
||||
Inclusive bool
|
||||
Latest string
|
||||
Limit int
|
||||
Oldest string
|
||||
}
|
||||
|
||||
// GetConversationReplies retrieves a thread of messages posted to a conversation
|
||||
func (api *Client) GetConversationReplies(params *GetConversationRepliesParameters) (msgs []Message, hasMore bool, nextCursor string, err error) {
|
||||
return api.GetConversationRepliesContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// GetConversationRepliesContext retrieves a thread of messages posted to a conversation with a custom context
|
||||
func (api *Client) GetConversationRepliesContext(ctx context.Context, params *GetConversationRepliesParameters) (msgs []Message, hasMore bool, nextCursor string, err error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"channel": {params.ChannelID},
|
||||
"ts": {params.Timestamp},
|
||||
}
|
||||
if params.Cursor != "" {
|
||||
values.Add("cursor", params.Cursor)
|
||||
}
|
||||
if params.Latest != "" {
|
||||
values.Add("latest", params.Latest)
|
||||
}
|
||||
if params.Limit != 0 {
|
||||
values.Add("limit", string(params.Limit))
|
||||
}
|
||||
if params.Oldest != "" {
|
||||
values.Add("oldest", params.Oldest)
|
||||
}
|
||||
if params.Inclusive {
|
||||
values.Add("inclusive", "1")
|
||||
} else {
|
||||
values.Add("inclusive", "0")
|
||||
}
|
||||
response := struct {
|
||||
SlackResponse
|
||||
HasMore bool `json:"has_more"`
|
||||
ResponseMetaData struct {
|
||||
NextCursor string `json:"next_cursor"`
|
||||
} `json:"response_metadata"`
|
||||
Messages []Message `json:"messages"`
|
||||
}{}
|
||||
|
||||
err = post(ctx, api.httpclient, "conversations.replies", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return nil, false, "", err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, false, "", errors.New(response.Error)
|
||||
}
|
||||
return response.Messages, response.HasMore, response.ResponseMetaData.NextCursor, nil
|
||||
}
|
||||
|
||||
type GetConversationsParameters struct {
|
||||
Cursor string
|
||||
ExcludeArchived string
|
||||
Limit int
|
||||
Types []string
|
||||
}
|
||||
|
||||
// GetConversations returns the list of channels in a Slack team
|
||||
func (api *Client) GetConversations(params *GetConversationsParameters) (channels []Channel, nextCursor string, err error) {
|
||||
return api.GetConversationsContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// GetConversationsContext returns the list of channels in a Slack team with a custom context
|
||||
func (api *Client) GetConversationsContext(ctx context.Context, params *GetConversationsParameters) (channels []Channel, nextCursor string, err error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"exclude_archived": {params.ExcludeArchived},
|
||||
}
|
||||
if params.Cursor != "" {
|
||||
values.Add("cursor", params.Cursor)
|
||||
}
|
||||
if params.Limit != 0 {
|
||||
values.Add("limit", string(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 = post(ctx, api.httpclient, "conversations.list", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, "", errors.New(response.Error)
|
||||
}
|
||||
return response.Channels, response.ResponseMetaData.NextCursor, nil
|
||||
}
|
||||
|
||||
type OpenConversationParameters struct {
|
||||
ChannelID string
|
||||
ReturnIM bool
|
||||
Users []string
|
||||
}
|
||||
|
||||
// OpenConversation opens or resumes a direct message or multi-person direct message
|
||||
func (api *Client) OpenConversation(params *OpenConversationParameters) (*Channel, bool, bool, error) {
|
||||
return api.OpenConversationContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// OpenConversationContext opens or resumes a direct message or multi-person direct message with a custom context
|
||||
func (api *Client) OpenConversationContext(ctx context.Context, params *OpenConversationParameters) (*Channel, bool, bool, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"return_im": {strconv.FormatBool(params.ReturnIM)},
|
||||
}
|
||||
if params.ChannelID != "" {
|
||||
values.Add("channel", params.ChannelID)
|
||||
}
|
||||
if params.Users != nil {
|
||||
values.Add("users", strings.Join(params.Users, ","))
|
||||
}
|
||||
response := struct {
|
||||
Channel *Channel `json:"channel"`
|
||||
NoOp bool `json:"no_op"`
|
||||
AlreadyOpen bool `json:"already_open"`
|
||||
SlackResponse
|
||||
}{}
|
||||
err := post(ctx, api.httpclient, "conversations.open", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return nil, false, false, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, false, false, errors.New(response.Error)
|
||||
}
|
||||
return response.Channel, response.NoOp, response.AlreadyOpen, nil
|
||||
}
|
||||
|
||||
// JoinConversation joins an existing conversation
|
||||
func (api *Client) JoinConversation(channelID string) (*Channel, string, []string, error) {
|
||||
return api.JoinConversationContext(context.Background(), channelID)
|
||||
}
|
||||
|
||||
// JoinConversationContext joins an existing conversation with a custom context
|
||||
func (api *Client) JoinConversationContext(ctx context.Context, channelID string) (*Channel, string, []string, error) {
|
||||
values := url.Values{"token": {api.token}, "channel": {channelID}}
|
||||
response := struct {
|
||||
Channel *Channel `json:"channel"`
|
||||
Warning string `json:"warning"`
|
||||
ResponseMetaData *struct {
|
||||
Warnings []string `json:"warnings"`
|
||||
} `json:"response_metadata"`
|
||||
SlackResponse
|
||||
}{}
|
||||
err := post(ctx, api.httpclient, "conversations.join", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return nil, "", nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, "", nil, errors.New(response.Error)
|
||||
}
|
||||
var warnings []string
|
||||
if response.ResponseMetaData != nil {
|
||||
warnings = response.ResponseMetaData.Warnings
|
||||
}
|
||||
return response.Channel, response.Warning, warnings, nil
|
||||
}
|
||||
|
||||
type GetConversationHistoryParameters struct {
|
||||
ChannelID string
|
||||
Cursor string
|
||||
Inclusive bool
|
||||
Latest string
|
||||
Limit int
|
||||
Oldest string
|
||||
}
|
||||
|
||||
type GetConversationHistoryResponse struct {
|
||||
SlackResponse
|
||||
HasMore bool `json:"has_more"`
|
||||
PinCount int `json:"pin_count"`
|
||||
Latest string `json:"latest"`
|
||||
ResponseMetaData struct {
|
||||
NextCursor string `json:"next_cursor"`
|
||||
} `json:"response_metadata"`
|
||||
Messages []Message `json:"messages"`
|
||||
}
|
||||
|
||||
// GetConversationHistory joins an existing conversation
|
||||
func (api *Client) GetConversationHistory(params *GetConversationHistoryParameters) (*GetConversationHistoryResponse, error) {
|
||||
return api.GetConversationHistoryContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// GetConversationHistoryContext joins an existing conversation with a custom context
|
||||
func (api *Client) GetConversationHistoryContext(ctx context.Context, params *GetConversationHistoryParameters) (*GetConversationHistoryResponse, error) {
|
||||
values := url.Values{"token": {api.token}, "channel": {params.ChannelID}}
|
||||
if params.Cursor != "" {
|
||||
values.Add("cursor", params.Cursor)
|
||||
}
|
||||
if params.Inclusive {
|
||||
values.Add("inclusive", "1")
|
||||
} else {
|
||||
values.Add("inclusive", "0")
|
||||
}
|
||||
if params.Latest != "" {
|
||||
values.Add("latest", params.Latest)
|
||||
}
|
||||
if params.Limit != 0 {
|
||||
values.Add("limit", string(params.Limit))
|
||||
}
|
||||
if params.Oldest != "" {
|
||||
values.Add("oldest", params.Oldest)
|
||||
}
|
||||
|
||||
response := GetConversationHistoryResponse{}
|
||||
|
||||
err := post(ctx, api.httpclient, "conversations.history", values, &response, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, errors.New(response.Error)
|
||||
}
|
||||
return &response, nil
|
||||
}
|
||||
|
28
vendor/github.com/nlopes/slack/dnd.go
generated
vendored
28
vendor/github.com/nlopes/slack/dnd.go
generated
vendored
@ -36,9 +36,9 @@ type dndTeamInfoResponse struct {
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
func dndRequest(ctx context.Context, path string, values url.Values, debug bool) (*dndResponseFull, error) {
|
||||
func dndRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*dndResponseFull, error) {
|
||||
response := &dndResponseFull{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := post(ctx, client, path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -56,11 +56,12 @@ func (api *Client) EndDND() error {
|
||||
// EndDNDContext ends the user's scheduled Do Not Disturb session with a custom context
|
||||
func (api *Client) EndDNDContext(ctx context.Context) error {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := post(ctx, "dnd.endDnd", values, response, api.debug); err != nil {
|
||||
|
||||
if err := post(ctx, api.httpclient, "dnd.endDnd", values, response, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
if !response.Ok {
|
||||
@ -77,10 +78,10 @@ func (api *Client) EndSnooze() (*DNDStatus, error) {
|
||||
// EndSnoozeContext ends the current user's snooze mode with a custom context
|
||||
func (api *Client) EndSnoozeContext(ctx context.Context) (*DNDStatus, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
response, err := dndRequest(ctx, "dnd.endSnooze", values, api.debug)
|
||||
response, err := dndRequest(ctx, api.httpclient, "dnd.endSnooze", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -95,12 +96,13 @@ func (api *Client) GetDNDInfo(user *string) (*DNDStatus, error) {
|
||||
// GetDNDInfoContext provides information about a user's current Do Not Disturb settings with a custom context.
|
||||
func (api *Client) GetDNDInfoContext(ctx context.Context, user *string) (*DNDStatus, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if user != nil {
|
||||
values.Set("user", *user)
|
||||
}
|
||||
response, err := dndRequest(ctx, "dnd.info", values, api.debug)
|
||||
|
||||
response, err := dndRequest(ctx, api.httpclient, "dnd.info", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -115,11 +117,12 @@ func (api *Client) GetDNDTeamInfo(users []string) (map[string]DNDStatus, error)
|
||||
// GetDNDTeamInfoContext provides information about a user's current Do Not Disturb settings with a custom context.
|
||||
func (api *Client) GetDNDTeamInfoContext(ctx context.Context, users []string) (map[string]DNDStatus, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"users": {strings.Join(users, ",")},
|
||||
}
|
||||
response := &dndTeamInfoResponse{}
|
||||
if err := post(ctx, "dnd.teamInfo", values, response, api.debug); err != nil {
|
||||
|
||||
if err := post(ctx, api.httpclient, "dnd.teamInfo", values, response, api.debug); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
@ -139,10 +142,11 @@ func (api *Client) SetSnooze(minutes int) (*DNDStatus, error) {
|
||||
// For more information see the SetSnooze docs
|
||||
func (api *Client) SetSnoozeContext(ctx context.Context, minutes int) (*DNDStatus, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"num_minutes": {strconv.Itoa(minutes)},
|
||||
}
|
||||
response, err := dndRequest(ctx, "dnd.setSnooze", values, api.debug)
|
||||
|
||||
response, err := dndRequest(ctx, api.httpclient, "dnd.setSnooze", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
5
vendor/github.com/nlopes/slack/emoji.go
generated
vendored
5
vendor/github.com/nlopes/slack/emoji.go
generated
vendored
@ -19,10 +19,11 @@ func (api *Client) GetEmoji() (map[string]string, error) {
|
||||
// GetEmojiContext retrieves all the emojis with a custom context
|
||||
func (api *Client) GetEmojiContext(ctx context.Context) (map[string]string, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
response := &emojiResponseFull{}
|
||||
err := post(ctx, "emoji.list", values, response, api.debug)
|
||||
|
||||
err := post(ctx, api.httpclient, "emoji.list", values, response, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
65
vendor/github.com/nlopes/slack/files.go
generated
vendored
65
vendor/github.com/nlopes/slack/files.go
generated
vendored
@ -136,9 +136,9 @@ func NewGetFilesParameters() GetFilesParameters {
|
||||
}
|
||||
}
|
||||
|
||||
func fileRequest(ctx context.Context, path string, values url.Values, debug bool) (*fileResponseFull, error) {
|
||||
func fileRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*fileResponseFull, error) {
|
||||
response := &fileResponseFull{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := postForm(ctx, client, SLACK_API+path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -156,12 +156,13 @@ func (api *Client) GetFileInfo(fileID string, count, page int) (*File, []Comment
|
||||
// GetFileInfoContext retrieves a file and related comments with a custom context
|
||||
func (api *Client) GetFileInfoContext(ctx context.Context, fileID string, count, page int) (*File, []Comment, *Paging, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"file": {fileID},
|
||||
"count": {strconv.Itoa(count)},
|
||||
"page": {strconv.Itoa(page)},
|
||||
}
|
||||
response, err := fileRequest(ctx, "files.info", values, api.debug)
|
||||
|
||||
response, err := fileRequest(ctx, api.httpclient, "files.info", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
@ -176,7 +177,7 @@ func (api *Client) GetFiles(params GetFilesParameters) ([]File, *Paging, error)
|
||||
// GetFilesContext retrieves all files according to the parameters given with a custom context
|
||||
func (api *Client) GetFilesContext(ctx context.Context, params GetFilesParameters) ([]File, *Paging, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if params.User != DEFAULT_FILES_USER {
|
||||
values.Add("user", params.User)
|
||||
@ -199,7 +200,8 @@ func (api *Client) GetFilesContext(ctx context.Context, params GetFilesParameter
|
||||
if params.Page != DEFAULT_FILES_PAGE {
|
||||
values.Add("page", strconv.Itoa(params.Page))
|
||||
}
|
||||
response, err := fileRequest(ctx, "files.list", values, api.debug)
|
||||
|
||||
response, err := fileRequest(ctx, api.httpclient, "files.list", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -221,7 +223,7 @@ func (api *Client) UploadFileContext(ctx context.Context, params FileUploadParam
|
||||
}
|
||||
response := &fileResponseFull{}
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if params.Filetype != "" {
|
||||
values.Add("filetype", params.Filetype)
|
||||
@ -240,11 +242,11 @@ func (api *Client) UploadFileContext(ctx context.Context, params FileUploadParam
|
||||
}
|
||||
if params.Content != "" {
|
||||
values.Add("content", params.Content)
|
||||
err = post(ctx, "files.upload", values, response, api.debug)
|
||||
err = postForm(ctx, api.httpclient, SLACK_API+"files.upload", values, response, api.debug)
|
||||
} else if params.File != "" {
|
||||
err = postLocalWithMultipartResponse(ctx, "files.upload", params.File, "file", values, response, api.debug)
|
||||
err = postLocalWithMultipartResponse(ctx, api.httpclient, SLACK_API+"files.upload", params.File, "file", values, response, api.debug)
|
||||
} else if params.Reader != nil {
|
||||
err = postWithMultipartResponse(ctx, "files.upload", params.Filename, "file", values, params.Reader, response, api.debug)
|
||||
err = postWithMultipartResponse(ctx, api.httpclient, SLACK_API+"files.upload", params.Filename, "file", values, params.Reader, response, api.debug)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -255,23 +257,46 @@ func (api *Client) UploadFileContext(ctx context.Context, params FileUploadParam
|
||||
return &response.File, nil
|
||||
}
|
||||
|
||||
// DeleteFileComment deletes a file's comment
|
||||
func (api *Client) DeleteFileComment(commentID, fileID string) error {
|
||||
return api.DeleteFileCommentContext(context.Background(), fileID, commentID)
|
||||
}
|
||||
|
||||
// DeleteFileCommentContext deletes a file's comment with a custom context
|
||||
func (api *Client) DeleteFileCommentContext(ctx context.Context, fileID, commentID string) (err error) {
|
||||
if fileID == "" || commentID == "" {
|
||||
return errors.New("received empty parameters")
|
||||
}
|
||||
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"file": {fileID},
|
||||
"id": {commentID},
|
||||
}
|
||||
if _, err = fileRequest(ctx, api.httpclient, "files.comments.delete", values, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteFile deletes a file
|
||||
func (api *Client) DeleteFile(fileID string) error {
|
||||
return api.DeleteFileContext(context.Background(), fileID)
|
||||
}
|
||||
|
||||
// DeleteFileContext deletes a file with a custom context
|
||||
func (api *Client) DeleteFileContext(ctx context.Context, fileID string) error {
|
||||
func (api *Client) DeleteFileContext(ctx context.Context, fileID string) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"file": {fileID},
|
||||
}
|
||||
_, err := fileRequest(ctx, "files.delete", values, api.debug)
|
||||
if err != nil {
|
||||
|
||||
if _, err = fileRequest(ctx, api.httpclient, "files.delete", values, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RevokeFilePublicURL disables public/external sharing for a file
|
||||
@ -282,10 +307,11 @@ func (api *Client) RevokeFilePublicURL(fileID string) (*File, error) {
|
||||
// RevokeFilePublicURLContext disables public/external sharing for a file with a custom context
|
||||
func (api *Client) RevokeFilePublicURLContext(ctx context.Context, fileID string) (*File, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"file": {fileID},
|
||||
}
|
||||
response, err := fileRequest(ctx, "files.revokePublicURL", values, api.debug)
|
||||
|
||||
response, err := fileRequest(ctx, api.httpclient, "files.revokePublicURL", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -300,10 +326,11 @@ func (api *Client) ShareFilePublicURL(fileID string) (*File, []Comment, *Paging,
|
||||
// ShareFilePublicURLContext enabled public/external sharing for a file with a custom context
|
||||
func (api *Client) ShareFilePublicURLContext(ctx context.Context, fileID string) (*File, []Comment, *Paging, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"file": {fileID},
|
||||
}
|
||||
response, err := fileRequest(ctx, "files.sharedPublicURL", values, api.debug)
|
||||
|
||||
response, err := fileRequest(ctx, api.httpclient, "files.sharedPublicURL", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
106
vendor/github.com/nlopes/slack/groups.go
generated
vendored
106
vendor/github.com/nlopes/slack/groups.go
generated
vendored
@ -28,9 +28,9 @@ type groupResponseFull struct {
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
func groupRequest(ctx context.Context, path string, values url.Values, debug bool) (*groupResponseFull, error) {
|
||||
func groupRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*groupResponseFull, error) {
|
||||
response := &groupResponseFull{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := postForm(ctx, client, SLACK_API+path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -45,17 +45,18 @@ func (api *Client) ArchiveGroup(group string) error {
|
||||
return api.ArchiveGroupContext(context.Background(), group)
|
||||
}
|
||||
|
||||
// ArchiveGroup archives a private group
|
||||
// ArchiveGroupContext archives a private group
|
||||
func (api *Client) ArchiveGroupContext(ctx context.Context, group string) error {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
}
|
||||
_, err := groupRequest(ctx, "groups.archive", values, api.debug)
|
||||
|
||||
_, err := groupRequest(ctx, api.httpclient, "groups.archive", values, api.debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
// UnarchiveGroup unarchives a private group
|
||||
@ -63,13 +64,14 @@ func (api *Client) UnarchiveGroup(group string) error {
|
||||
return api.UnarchiveGroupContext(context.Background(), group)
|
||||
}
|
||||
|
||||
// UnarchiveGroup unarchives a private group
|
||||
// UnarchiveGroupContext unarchives a private group
|
||||
func (api *Client) UnarchiveGroupContext(ctx context.Context, group string) error {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
}
|
||||
_, err := groupRequest(ctx, "groups.unarchive", values, api.debug)
|
||||
|
||||
_, err := groupRequest(ctx, api.httpclient, "groups.unarchive", values, api.debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -81,13 +83,14 @@ func (api *Client) CreateGroup(group string) (*Group, error) {
|
||||
return api.CreateGroupContext(context.Background(), group)
|
||||
}
|
||||
|
||||
// CreateGroup creates a private group
|
||||
// CreateGroupContext creates a private group
|
||||
func (api *Client) CreateGroupContext(ctx context.Context, group string) (*Group, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"name": {group},
|
||||
}
|
||||
response, err := groupRequest(ctx, "groups.create", values, api.debug)
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.create", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -104,14 +107,15 @@ func (api *Client) CreateChildGroup(group string) (*Group, error) {
|
||||
return api.CreateChildGroupContext(context.Background(), group)
|
||||
}
|
||||
|
||||
// CreateChildGroup creates a new private group archiving the old one with a custom context
|
||||
// CreateChildGroupContext creates a new private group archiving the old one with a custom context
|
||||
// For more information see CreateChildGroup
|
||||
func (api *Client) CreateChildGroupContext(ctx context.Context, group string) (*Group, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
}
|
||||
response, err := groupRequest(ctx, "groups.createChild", values, api.debug)
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.createChild", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -126,10 +130,11 @@ func (api *Client) CloseGroup(group string) (bool, bool, error) {
|
||||
// CloseGroupContext closes a private group with a custom context
|
||||
func (api *Client) CloseGroupContext(ctx context.Context, group string) (bool, bool, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
}
|
||||
response, err := imRequest(ctx, "groups.close", values, api.debug)
|
||||
|
||||
response, err := imRequest(ctx, api.httpclient, "groups.close", values, api.debug)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
@ -144,7 +149,7 @@ func (api *Client) GetGroupHistory(group string, params HistoryParameters) (*His
|
||||
// GetGroupHistoryContext fetches all the history for a private group with a custom context
|
||||
func (api *Client) GetGroupHistoryContext(ctx context.Context, group string, params HistoryParameters) (*History, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
}
|
||||
if params.Latest != DEFAULT_HISTORY_LATEST {
|
||||
@ -170,7 +175,8 @@ func (api *Client) GetGroupHistoryContext(ctx context.Context, group string, par
|
||||
values.Add("unreads", "0")
|
||||
}
|
||||
}
|
||||
response, err := groupRequest(ctx, "groups.history", values, api.debug)
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.history", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -185,11 +191,12 @@ func (api *Client) InviteUserToGroup(group, user string) (*Group, bool, error) {
|
||||
// InviteUserToGroupContext invites a specific user to a private group with a custom context
|
||||
func (api *Client) InviteUserToGroupContext(ctx context.Context, group, user string) (*Group, bool, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
"user": {user},
|
||||
}
|
||||
response, err := groupRequest(ctx, "groups.invite", values, api.debug)
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.invite", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
@ -202,15 +209,16 @@ func (api *Client) LeaveGroup(group string) error {
|
||||
}
|
||||
|
||||
// LeaveGroupContext makes authenticated user leave the group with a custom context
|
||||
func (api *Client) LeaveGroupContext(ctx context.Context, group string) error {
|
||||
func (api *Client) LeaveGroupContext(ctx context.Context, group string) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
}
|
||||
_, err := groupRequest(ctx, "groups.leave", values, api.debug)
|
||||
if err != nil {
|
||||
|
||||
if _, err = groupRequest(ctx, api.httpclient, "groups.leave", values, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -220,16 +228,17 @@ func (api *Client) KickUserFromGroup(group, user string) error {
|
||||
}
|
||||
|
||||
// KickUserFromGroupContext kicks a user from a group with a custom context
|
||||
func (api *Client) KickUserFromGroupContext(ctx context.Context, group, user string) error {
|
||||
func (api *Client) KickUserFromGroupContext(ctx context.Context, group, user string) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
"user": {user},
|
||||
}
|
||||
_, err := groupRequest(ctx, "groups.kick", values, api.debug)
|
||||
if err != nil {
|
||||
|
||||
if _, err = groupRequest(ctx, api.httpclient, "groups.kick", values, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -241,12 +250,13 @@ func (api *Client) GetGroups(excludeArchived bool) ([]Group, error) {
|
||||
// GetGroupsContext retrieves all groups with a custom context
|
||||
func (api *Client) GetGroupsContext(ctx context.Context, excludeArchived bool) ([]Group, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if excludeArchived {
|
||||
values.Add("exclude_archived", "1")
|
||||
}
|
||||
response, err := groupRequest(ctx, "groups.list", values, api.debug)
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.list", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -261,10 +271,11 @@ func (api *Client) GetGroupInfo(group string) (*Group, error) {
|
||||
// GetGroupInfoContext retrieves the given group with a custom context
|
||||
func (api *Client) GetGroupInfoContext(ctx context.Context, group string) (*Group, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
}
|
||||
response, err := groupRequest(ctx, "groups.info", values, api.debug)
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.info", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -282,16 +293,17 @@ func (api *Client) SetGroupReadMark(group, ts string) error {
|
||||
|
||||
// SetGroupReadMarkContext sets the read mark on a private group with a custom context
|
||||
// For more details see SetGroupReadMark
|
||||
func (api *Client) SetGroupReadMarkContext(ctx context.Context, group, ts string) error {
|
||||
func (api *Client) SetGroupReadMarkContext(ctx context.Context, group, ts string) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
"ts": {ts},
|
||||
}
|
||||
_, err := groupRequest(ctx, "groups.mark", values, api.debug)
|
||||
if err != nil {
|
||||
|
||||
if _, err = groupRequest(ctx, api.httpclient, "groups.mark", values, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -303,10 +315,11 @@ func (api *Client) OpenGroup(group string) (bool, bool, error) {
|
||||
// OpenGroupContext opens a private group with a custom context
|
||||
func (api *Client) OpenGroupContext(ctx context.Context, group string) (bool, bool, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
}
|
||||
response, err := groupRequest(ctx, "groups.open", values, api.debug)
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.open", values, api.debug)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
@ -323,13 +336,14 @@ func (api *Client) RenameGroup(group, name string) (*Channel, error) {
|
||||
// RenameGroupContext renames a group with a custom context
|
||||
func (api *Client) RenameGroupContext(ctx context.Context, group, name string) (*Channel, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
"name": {name},
|
||||
}
|
||||
|
||||
// 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, "groups.rename", values, api.debug)
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.rename", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -344,11 +358,12 @@ func (api *Client) SetGroupPurpose(group, purpose string) (string, error) {
|
||||
// SetGroupPurposeContext sets the group purpose with a custom context
|
||||
func (api *Client) SetGroupPurposeContext(ctx context.Context, group, purpose string) (string, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
"purpose": {purpose},
|
||||
}
|
||||
response, err := groupRequest(ctx, "groups.setPurpose", values, api.debug)
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.setPurpose", values, api.debug)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -363,11 +378,12 @@ func (api *Client) SetGroupTopic(group, topic string) (string, error) {
|
||||
// SetGroupTopicContext sets the group topic with a custom context
|
||||
func (api *Client) SetGroupTopicContext(ctx context.Context, group, topic string) (string, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {group},
|
||||
"topic": {topic},
|
||||
}
|
||||
response, err := groupRequest(ctx, "groups.setTopic", values, api.debug)
|
||||
|
||||
response, err := groupRequest(ctx, api.httpclient, "groups.setTopic", values, api.debug)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
29
vendor/github.com/nlopes/slack/im.go
generated
vendored
29
vendor/github.com/nlopes/slack/im.go
generated
vendored
@ -29,9 +29,9 @@ type IM struct {
|
||||
IsUserDeleted bool `json:"is_user_deleted"`
|
||||
}
|
||||
|
||||
func imRequest(ctx context.Context, path string, values url.Values, debug bool) (*imResponseFull, error) {
|
||||
func imRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*imResponseFull, error) {
|
||||
response := &imResponseFull{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := post(ctx, client, path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -49,10 +49,11 @@ func (api *Client) CloseIMChannel(channel string) (bool, bool, error) {
|
||||
// CloseIMChannelContext closes the direct message channel with a custom context
|
||||
func (api *Client) CloseIMChannelContext(ctx context.Context, channel string) (bool, bool, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {channel},
|
||||
}
|
||||
response, err := imRequest(ctx, "im.close", values, api.debug)
|
||||
|
||||
response, err := imRequest(ctx, api.httpclient, "im.close", values, api.debug)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
@ -69,10 +70,11 @@ func (api *Client) OpenIMChannel(user string) (bool, bool, string, error) {
|
||||
// Returns some status and the channel ID
|
||||
func (api *Client) OpenIMChannelContext(ctx context.Context, user string) (bool, bool, string, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"user": {user},
|
||||
}
|
||||
response, err := imRequest(ctx, "im.open", values, api.debug)
|
||||
|
||||
response, err := imRequest(ctx, api.httpclient, "im.open", values, api.debug)
|
||||
if err != nil {
|
||||
return false, false, "", err
|
||||
}
|
||||
@ -87,11 +89,12 @@ func (api *Client) MarkIMChannel(channel, ts string) (err error) {
|
||||
// MarkIMChannelContext sets the read mark of a direct message channel to a specific point with a custom context
|
||||
func (api *Client) MarkIMChannelContext(ctx context.Context, channel, ts string) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {channel},
|
||||
"ts": {ts},
|
||||
}
|
||||
_, err = imRequest(ctx, "im.mark", values, api.debug)
|
||||
|
||||
_, err = imRequest(ctx, api.httpclient, "im.mark", values, api.debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -106,7 +109,7 @@ func (api *Client) GetIMHistory(channel string, params HistoryParameters) (*Hist
|
||||
// GetIMHistoryContext retrieves the direct message channel history with a custom context
|
||||
func (api *Client) GetIMHistoryContext(ctx context.Context, channel string, params HistoryParameters) (*History, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"channel": {channel},
|
||||
}
|
||||
if params.Latest != DEFAULT_HISTORY_LATEST {
|
||||
@ -132,7 +135,8 @@ func (api *Client) GetIMHistoryContext(ctx context.Context, channel string, para
|
||||
values.Add("unreads", "0")
|
||||
}
|
||||
}
|
||||
response, err := imRequest(ctx, "im.history", values, api.debug)
|
||||
|
||||
response, err := imRequest(ctx, api.httpclient, "im.history", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -147,9 +151,10 @@ func (api *Client) GetIMChannels() ([]IM, error) {
|
||||
// GetIMChannelsContext returns the list of direct message channels with a custom context
|
||||
func (api *Client) GetIMChannelsContext(ctx context.Context) ([]IM, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
response, err := imRequest(ctx, "im.list", values, api.debug)
|
||||
|
||||
response, err := imRequest(ctx, api.httpclient, "im.list", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
53
vendor/github.com/nlopes/slack/logger.go
generated
vendored
Normal file
53
vendor/github.com/nlopes/slack/logger.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
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 {
|
||||
Output(int, string) error
|
||||
}
|
||||
|
||||
// logInternal represents the internal logging api we use.
|
||||
type logInternal interface {
|
||||
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
|
||||
}
|
||||
|
||||
// Println replicates the behaviour of the standard logger.
|
||||
func (t ilogger) 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{}) {
|
||||
t.Output(2, fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
// Print replicates the behaviour of the standard logger.
|
||||
func (t ilogger) Print(v ...interface{}) {
|
||||
t.Output(2, fmt.Sprint(v...))
|
||||
}
|
18
vendor/github.com/nlopes/slack/messages.go
generated
vendored
18
vendor/github.com/nlopes/slack/messages.go
generated
vendored
@ -2,7 +2,8 @@ package slack
|
||||
|
||||
// OutgoingMessage is used for the realtime API, and seems incomplete.
|
||||
type OutgoingMessage struct {
|
||||
ID int `json:"id"`
|
||||
ID int `json:"id"`
|
||||
// channel ID
|
||||
Channel string `json:"channel,omitempty"`
|
||||
Text string `json:"text,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
@ -28,6 +29,9 @@ type Msg struct {
|
||||
PinnedTo []string `json:"pinned_to, omitempty"`
|
||||
Attachments []Attachment `json:"attachments,omitempty"`
|
||||
Edited *Edited `json:"edited,omitempty"`
|
||||
LastRead string `json:"last_read,omitempty"`
|
||||
Subscribed bool `json:"subscribed,omitempty"`
|
||||
UnreadCount int `json:"unread_count,omitempty"`
|
||||
|
||||
// Message Subtypes
|
||||
SubType string `json:"subtype,omitempty"`
|
||||
@ -81,6 +85,10 @@ type Msg struct {
|
||||
|
||||
// reactions
|
||||
Reactions []ItemReaction `json:"reactions,omitempty"`
|
||||
|
||||
// slash commands and interactive messages
|
||||
ResponseType string `json:"response_type,omitempty"`
|
||||
ReplaceOriginal bool `json:"replace_original,omitempty"`
|
||||
}
|
||||
|
||||
// Icon is used for bot messages
|
||||
@ -121,12 +129,12 @@ type Pong struct {
|
||||
// NewOutgoingMessage prepares an OutgoingMessage that the user can
|
||||
// use to send a message. Use this function to properly set the
|
||||
// messageID.
|
||||
func (rtm *RTM) NewOutgoingMessage(text string, channel string) *OutgoingMessage {
|
||||
func (rtm *RTM) NewOutgoingMessage(text string, channelID string) *OutgoingMessage {
|
||||
id := rtm.idGen.Next()
|
||||
return &OutgoingMessage{
|
||||
ID: id,
|
||||
Type: "message",
|
||||
Channel: channel,
|
||||
Channel: channelID,
|
||||
Text: text,
|
||||
}
|
||||
}
|
||||
@ -134,11 +142,11 @@ func (rtm *RTM) NewOutgoingMessage(text string, channel string) *OutgoingMessage
|
||||
// NewTypingMessage prepares an OutgoingMessage that the user can
|
||||
// use to send as a typing indicator. Use this function to properly set the
|
||||
// messageID.
|
||||
func (rtm *RTM) NewTypingMessage(channel string) *OutgoingMessage {
|
||||
func (rtm *RTM) NewTypingMessage(channelID string) *OutgoingMessage {
|
||||
id := rtm.idGen.Next()
|
||||
return &OutgoingMessage{
|
||||
ID: id,
|
||||
Type: "typing",
|
||||
Channel: channel,
|
||||
Channel: channelID,
|
||||
}
|
||||
}
|
||||
|
89
vendor/github.com/nlopes/slack/misc.go
generated
vendored
89
vendor/github.com/nlopes/slack/misc.go
generated
vendored
@ -13,24 +13,11 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
Do(*http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
var customHTTPClient HTTPRequester
|
||||
|
||||
// HTTPClient sets a custom http.Client
|
||||
// deprecated: in favor of SetHTTPClient()
|
||||
var HTTPClient = &http.Client{}
|
||||
|
||||
type WebResponse struct {
|
||||
Ok bool `json:"ok"`
|
||||
Error *WebError `json:"error"`
|
||||
@ -42,6 +29,14 @@ func (s WebError) Error() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
type RateLimitedError struct {
|
||||
RetryAfter time.Duration
|
||||
}
|
||||
|
||||
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)
|
||||
@ -79,15 +74,10 @@ func parseResponseBody(body io.ReadCloser, intf *interface{}, debug bool) error
|
||||
logger.Printf("parseResponseBody: %s\n", string(response))
|
||||
}
|
||||
|
||||
err = json.Unmarshal(response, &intf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return json.Unmarshal(response, &intf)
|
||||
}
|
||||
|
||||
func postLocalWithMultipartResponse(ctx context.Context, path, fpath, fieldname string, values url.Values, intf interface{}, debug bool) error {
|
||||
func postLocalWithMultipartResponse(ctx context.Context, client HTTPRequester, path, fpath, fieldname string, values url.Values, intf interface{}, debug bool) error {
|
||||
fullpath, err := filepath.Abs(fpath)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -97,23 +87,31 @@ func postLocalWithMultipartResponse(ctx context.Context, path, fpath, fieldname
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
return postWithMultipartResponse(ctx, path, filepath.Base(fpath), fieldname, values, file, intf, debug)
|
||||
return postWithMultipartResponse(ctx, client, path, filepath.Base(fpath), fieldname, values, file, intf, debug)
|
||||
}
|
||||
|
||||
func postWithMultipartResponse(ctx context.Context, path, name, fieldname string, values url.Values, r io.Reader, intf interface{}, debug bool) error {
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
resp, err := getHTTPClient().Do(req)
|
||||
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 != 200 {
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
logResponse(resp, debug)
|
||||
return fmt.Errorf("Slack server error: %s.", resp.Status)
|
||||
}
|
||||
@ -121,7 +119,7 @@ func postWithMultipartResponse(ctx context.Context, path, name, fieldname string
|
||||
return parseResponseBody(resp.Body, &intf, debug)
|
||||
}
|
||||
|
||||
func postForm(ctx context.Context, endpoint string, values url.Values, intf interface{}, debug bool) error {
|
||||
func postForm(ctx context.Context, client HTTPRequester, endpoint string, values url.Values, intf interface{}, debug bool) error {
|
||||
reqBody := strings.NewReader(values.Encode())
|
||||
req, err := http.NewRequest("POST", endpoint, reqBody)
|
||||
if err != nil {
|
||||
@ -130,14 +128,22 @@ func postForm(ctx context.Context, endpoint string, values url.Values, intf inte
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
req = req.WithContext(ctx)
|
||||
resp, err := getHTTPClient().Do(req)
|
||||
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 != 200 {
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
logResponse(resp, debug)
|
||||
return fmt.Errorf("Slack server error: %s.", resp.Status)
|
||||
}
|
||||
@ -145,13 +151,13 @@ func postForm(ctx context.Context, endpoint string, values url.Values, intf inte
|
||||
return parseResponseBody(resp.Body, &intf, debug)
|
||||
}
|
||||
|
||||
func post(ctx context.Context, path string, values url.Values, intf interface{}, debug bool) error {
|
||||
return postForm(ctx, SLACK_API+path, values, intf, debug)
|
||||
func post(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 parseAdminResponse(ctx context.Context, method string, teamName string, values url.Values, intf interface{}, debug bool) error {
|
||||
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, endpoint, values, intf, debug)
|
||||
return postForm(ctx, client, endpoint, values, intf, debug)
|
||||
}
|
||||
|
||||
func logResponse(resp *http.Response, debug bool) error {
|
||||
@ -167,17 +173,10 @@ func logResponse(resp *http.Response, debug bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getHTTPClient() HTTPRequester {
|
||||
if customHTTPClient != nil {
|
||||
return customHTTPClient
|
||||
}
|
||||
|
||||
return HTTPClient
|
||||
}
|
||||
|
||||
// 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
|
||||
func okJsonHandler(rw http.ResponseWriter, r *http.Request) {
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
response, _ := json.Marshal(SlackResponse{
|
||||
Ok: true,
|
||||
})
|
||||
rw.Write(response)
|
||||
}
|
||||
|
2
vendor/github.com/nlopes/slack/oauth.go
generated
vendored
2
vendor/github.com/nlopes/slack/oauth.go
generated
vendored
@ -55,7 +55,7 @@ func GetOAuthResponseContext(ctx context.Context, clientID, clientSecret, code,
|
||||
"redirect_uri": {redirectURI},
|
||||
}
|
||||
response := &OAuthResponse{}
|
||||
err = post(ctx, "oauth.access", values, response, debug)
|
||||
err = post(ctx, customHTTPClient, "oauth.access", values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
15
vendor/github.com/nlopes/slack/pins.go
generated
vendored
15
vendor/github.com/nlopes/slack/pins.go
generated
vendored
@ -21,7 +21,7 @@ func (api *Client) AddPin(channel string, item ItemRef) error {
|
||||
func (api *Client) AddPinContext(ctx context.Context, channel string, item ItemRef) error {
|
||||
values := url.Values{
|
||||
"channel": {channel},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if item.Timestamp != "" {
|
||||
values.Set("timestamp", string(item.Timestamp))
|
||||
@ -32,8 +32,9 @@ func (api *Client) AddPinContext(ctx context.Context, channel string, item ItemR
|
||||
if item.Comment != "" {
|
||||
values.Set("file_comment", string(item.Comment))
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := post(ctx, "pins.add", values, response, api.debug); err != nil {
|
||||
if err := post(ctx, api.httpclient, "pins.add", values, response, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
if !response.Ok {
|
||||
@ -51,7 +52,7 @@ func (api *Client) RemovePin(channel string, item ItemRef) error {
|
||||
func (api *Client) RemovePinContext(ctx context.Context, channel string, item ItemRef) error {
|
||||
values := url.Values{
|
||||
"channel": {channel},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if item.Timestamp != "" {
|
||||
values.Set("timestamp", string(item.Timestamp))
|
||||
@ -62,8 +63,9 @@ func (api *Client) RemovePinContext(ctx context.Context, channel string, item It
|
||||
if item.Comment != "" {
|
||||
values.Set("file_comment", string(item.Comment))
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := post(ctx, "pins.remove", values, response, api.debug); err != nil {
|
||||
if err := post(ctx, api.httpclient, "pins.remove", values, response, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
if !response.Ok {
|
||||
@ -81,10 +83,11 @@ func (api *Client) ListPins(channel string) ([]Item, *Paging, error) {
|
||||
func (api *Client) ListPinsContext(ctx context.Context, channel string) ([]Item, *Paging, error) {
|
||||
values := url.Values{
|
||||
"channel": {channel},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
response := &listPinsResponseFull{}
|
||||
err := post(ctx, "pins.list", values, response, api.debug)
|
||||
err := post(ctx, api.httpclient, "pins.list", values, response, api.debug)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
20
vendor/github.com/nlopes/slack/reactions.go
generated
vendored
20
vendor/github.com/nlopes/slack/reactions.go
generated
vendored
@ -136,7 +136,7 @@ func (api *Client) AddReaction(name string, item ItemRef) error {
|
||||
// AddReactionContext adds a reaction emoji to a message, file or file comment with a custom context.
|
||||
func (api *Client) AddReactionContext(ctx context.Context, name string, item ItemRef) error {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if name != "" {
|
||||
values.Set("name", name)
|
||||
@ -153,8 +153,9 @@ func (api *Client) AddReactionContext(ctx context.Context, name string, item Ite
|
||||
if item.Comment != "" {
|
||||
values.Set("file_comment", string(item.Comment))
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := post(ctx, "reactions.add", values, response, api.debug); err != nil {
|
||||
if err := post(ctx, api.httpclient, "reactions.add", values, response, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
if !response.Ok {
|
||||
@ -171,7 +172,7 @@ func (api *Client) RemoveReaction(name string, item ItemRef) error {
|
||||
// RemoveReactionContext removes a reaction emoji from a message, file or file comment with a custom context.
|
||||
func (api *Client) RemoveReactionContext(ctx context.Context, name string, item ItemRef) error {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if name != "" {
|
||||
values.Set("name", name)
|
||||
@ -188,8 +189,9 @@ func (api *Client) RemoveReactionContext(ctx context.Context, name string, item
|
||||
if item.Comment != "" {
|
||||
values.Set("file_comment", string(item.Comment))
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := post(ctx, "reactions.remove", values, response, api.debug); err != nil {
|
||||
if err := post(ctx, api.httpclient, "reactions.remove", values, response, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
if !response.Ok {
|
||||
@ -206,7 +208,7 @@ func (api *Client) GetReactions(item ItemRef, params GetReactionsParameters) ([]
|
||||
// GetReactionsContext returns details about the reactions on an item with a custom context
|
||||
func (api *Client) GetReactionsContext(ctx context.Context, item ItemRef, params GetReactionsParameters) ([]ItemReaction, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if item.Channel != "" {
|
||||
values.Set("channel", string(item.Channel))
|
||||
@ -223,8 +225,9 @@ func (api *Client) GetReactionsContext(ctx context.Context, item ItemRef, params
|
||||
if params.Full != DEFAULT_REACTIONS_FULL {
|
||||
values.Set("full", strconv.FormatBool(params.Full))
|
||||
}
|
||||
|
||||
response := &getReactionsResponseFull{}
|
||||
if err := post(ctx, "reactions.get", values, response, api.debug); err != nil {
|
||||
if err := post(ctx, api.httpclient, "reactions.get", values, response, api.debug); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.Ok {
|
||||
@ -241,7 +244,7 @@ func (api *Client) ListReactions(params ListReactionsParameters) ([]ReactedItem,
|
||||
// ListReactionsContext returns information about the items a user reacted to with a custom context.
|
||||
func (api *Client) ListReactionsContext(ctx context.Context, params ListReactionsParameters) ([]ReactedItem, *Paging, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if params.User != DEFAULT_REACTIONS_USER {
|
||||
values.Add("user", params.User)
|
||||
@ -255,8 +258,9 @@ func (api *Client) ListReactionsContext(ctx context.Context, params ListReaction
|
||||
if params.Full != DEFAULT_REACTIONS_FULL {
|
||||
values.Add("full", strconv.FormatBool(params.Full))
|
||||
}
|
||||
|
||||
response := &listReactionsResponseFull{}
|
||||
err := post(ctx, "reactions.list", values, response, api.debug)
|
||||
err := post(ctx, api.httpclient, "reactions.list", values, response, api.debug)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
42
vendor/github.com/nlopes/slack/rtm.go
generated
vendored
42
vendor/github.com/nlopes/slack/rtm.go
generated
vendored
@ -8,11 +8,18 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
websocketDefaultTimeout = 10 * time.Second
|
||||
)
|
||||
|
||||
// StartRTM calls the "rtm.start" endpoint and returns the provided URL and the full Info block.
|
||||
//
|
||||
// To have a fully managed Websocket connection, use `NewRTM`, and call `ManageConnection()` on it.
|
||||
func (api *Client) StartRTM() (info *Info, websocketURL string, err error) {
|
||||
return api.StartRTMContext(context.Background())
|
||||
ctx, cancel := context.WithTimeout(context.Background(), websocketDefaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
return api.StartRTMContext(ctx)
|
||||
}
|
||||
|
||||
// StartRTMContext calls the "rtm.start" endpoint and returns the provided URL and the full Info block with a custom context.
|
||||
@ -20,31 +27,25 @@ 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 = post(ctx, "rtm.start", url.Values{"token": {api.config.token}}, response, api.debug)
|
||||
err = post(ctx, api.httpclient, "rtm.start", url.Values{"token": {api.token}}, response, api.debug)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("post: %s", err)
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, "", response.Error
|
||||
}
|
||||
|
||||
// websocket.Dial does not accept url without the port (yet)
|
||||
// Fixed by: https://github.com/golang/net/commit/5058c78c3627b31e484a81463acd51c7cecc06f3
|
||||
// but slack returns the address with no port, so we have to fix it
|
||||
api.Debugln("Using URL:", response.Info.URL)
|
||||
websocketURL, err = websocketizeURLPort(response.Info.URL)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("parsing response URL: %s", err)
|
||||
}
|
||||
|
||||
return &response.Info, websocketURL, nil
|
||||
return &response.Info, response.Info.URL, nil
|
||||
}
|
||||
|
||||
// ConnectRTM calls the "rtm.connect" endpoint and returns the provided URL and the compact Info block.
|
||||
//
|
||||
// To have a fully managed Websocket connection, use `NewRTM`, and call `ManageConnection()` on it.
|
||||
func (api *Client) ConnectRTM() (info *Info, websocketURL string, err error) {
|
||||
return api.ConnectRTMContext(context.Background())
|
||||
ctx, cancel := context.WithTimeout(context.Background(), websocketDefaultTimeout)
|
||||
defer cancel()
|
||||
|
||||
return api.ConnectRTMContext(ctx)
|
||||
}
|
||||
|
||||
// ConnectRTM calls the "rtm.connect" endpoint and returns the provided URL and the compact Info block with a custom context.
|
||||
@ -52,24 +53,16 @@ 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 = post(ctx, "rtm.connect", url.Values{"token": {api.config.token}}, response, api.debug)
|
||||
err = post(ctx, api.httpclient, "rtm.connect", url.Values{"token": {api.token}}, response, api.debug)
|
||||
if err != nil {
|
||||
api.Debugf("Failed to connect to RTM: %s", err)
|
||||
return nil, "", fmt.Errorf("post: %s", err)
|
||||
}
|
||||
if !response.Ok {
|
||||
return nil, "", response.Error
|
||||
}
|
||||
|
||||
// websocket.Dial does not accept url without the port (yet)
|
||||
// Fixed by: https://github.com/golang/net/commit/5058c78c3627b31e484a81463acd51c7cecc06f3
|
||||
// but slack returns the address with no port, so we have to fix it
|
||||
api.Debugln("Using URL:", response.Info.URL)
|
||||
websocketURL, err = websocketizeURLPort(response.Info.URL)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("parsing response URL: %s", err)
|
||||
}
|
||||
|
||||
return &response.Info, websocketURL, nil
|
||||
return &response.Info, response.Info.URL, nil
|
||||
}
|
||||
|
||||
// NewRTM returns a RTM, which provides a fully managed connection to
|
||||
@ -90,6 +83,7 @@ func (api *Client) NewRTMWithOptions(options *RTMOptions) *RTM {
|
||||
isConnected: false,
|
||||
wasIntentional: true,
|
||||
killChannel: make(chan bool),
|
||||
disconnected: make(chan struct{}),
|
||||
forcePing: make(chan bool),
|
||||
rawEvents: make(chan json.RawMessage),
|
||||
idGen: NewSafeID(1),
|
||||
|
5
vendor/github.com/nlopes/slack/search.go
generated
vendored
5
vendor/github.com/nlopes/slack/search.go
generated
vendored
@ -83,7 +83,7 @@ func NewSearchParameters() SearchParameters {
|
||||
|
||||
func (api *Client) _search(ctx context.Context, path, query string, params SearchParameters, files, messages bool) (response *searchResponseFull, error error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"query": {query},
|
||||
}
|
||||
if params.Sort != DEFAULT_SEARCH_SORT {
|
||||
@ -101,8 +101,9 @@ func (api *Client) _search(ctx context.Context, path, query string, params Searc
|
||||
if params.Page != DEFAULT_SEARCH_PAGE {
|
||||
values.Add("page", strconv.Itoa(params.Page))
|
||||
}
|
||||
|
||||
response = &searchResponseFull{}
|
||||
err := post(ctx, path, values, response, api.debug)
|
||||
err := post(ctx, api.httpclient, path, values, response, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
78
vendor/github.com/nlopes/slack/slack.go
generated
vendored
78
vendor/github.com/nlopes/slack/slack.go
generated
vendored
@ -3,18 +3,38 @@ package slack
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
)
|
||||
|
||||
var logger *log.Logger // A logger that can be set by consumers
|
||||
/*
|
||||
Added as a var so that we can change this for testing purposes
|
||||
*/
|
||||
// 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"
|
||||
|
||||
// HTTPClient sets a custom http.Client
|
||||
// deprecated: in favor of SetHTTPClient()
|
||||
var HTTPClient = &http.Client{}
|
||||
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
type SlackResponse struct {
|
||||
Ok bool `json:"ok"`
|
||||
Error string `json:"error"`
|
||||
@ -34,22 +54,33 @@ type authTestResponseFull struct {
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
config struct {
|
||||
token string
|
||||
token string
|
||||
info Info
|
||||
debug bool
|
||||
httpclient HTTPRequester
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
info Info
|
||||
debug bool
|
||||
}
|
||||
|
||||
// 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 *log.Logger) {
|
||||
logger = l
|
||||
}
|
||||
// New builds a slack client from the provided token and options.
|
||||
func New(token string, options ...Option) *Client {
|
||||
s := &Client{
|
||||
token: token,
|
||||
httpclient: customHTTPClient,
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
opt(s)
|
||||
}
|
||||
|
||||
func New(token string) *Client {
|
||||
s := &Client{}
|
||||
s.config.token = token
|
||||
return s
|
||||
}
|
||||
|
||||
@ -60,14 +91,19 @@ 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) {
|
||||
api.Debugf("Challenging auth...")
|
||||
responseFull := &authTestResponseFull{}
|
||||
err := post(ctx, "auth.test", url.Values{"token": {api.config.token}}, responseFull, api.debug)
|
||||
err := post(ctx, api.httpclient, "auth.test", url.Values{"token": {api.token}}, responseFull, api.debug)
|
||||
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
|
||||
}
|
||||
|
||||
@ -77,18 +113,20 @@ func (api *Client) AuthTestContext(ctx context.Context) (response *AuthTestRespo
|
||||
func (api *Client) SetDebug(debug bool) {
|
||||
api.debug = debug
|
||||
if debug && logger == nil {
|
||||
logger = log.New(os.Stdout, "nlopes/slack", log.LstdFlags|log.Lshortfile)
|
||||
SetLogger(log.New(os.Stdout, "nlopes/slack", log.LstdFlags|log.Lshortfile))
|
||||
}
|
||||
}
|
||||
|
||||
// Debugf print a formatted debug line.
|
||||
func (api *Client) Debugf(format string, v ...interface{}) {
|
||||
if api.debug {
|
||||
logger.Printf(format, v...)
|
||||
logger.Output(2, fmt.Sprintf(format, v...))
|
||||
}
|
||||
}
|
||||
|
||||
// Debugln print a debug line.
|
||||
func (api *Client) Debugln(v ...interface{}) {
|
||||
if api.debug {
|
||||
logger.Println(v...)
|
||||
logger.Output(2, fmt.Sprintln(v...))
|
||||
}
|
||||
}
|
||||
|
49
vendor/github.com/nlopes/slack/slash.go
generated
vendored
Normal file
49
vendor/github.com/nlopes/slack/slash.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// SlashCommand contains information about a request of the slash command
|
||||
type SlashCommand struct {
|
||||
Token string `json:"token"`
|
||||
TeamID string `json:"team_id"`
|
||||
TeamDomain string `json:"team_domain"`
|
||||
ChannelID string `json:"channel_id"`
|
||||
ChannelName string `json:"channel_name"`
|
||||
UserID string `json:"user_id"`
|
||||
UserName string `json:"user_name"`
|
||||
Command string `json:"command"`
|
||||
Text string `json:"text"`
|
||||
ResponseURL string `json:"response_url"`
|
||||
TriggerID string `json:"trigger_id"`
|
||||
}
|
||||
|
||||
// SlashCommandParse will parse the request of the slash command
|
||||
func SlashCommandParse(r *http.Request) (s SlashCommand, err error) {
|
||||
if err = r.ParseForm(); err != nil {
|
||||
return s, err
|
||||
}
|
||||
s.Token = r.PostForm.Get("token")
|
||||
s.TeamID = r.PostForm.Get("team_id")
|
||||
s.TeamDomain = r.PostForm.Get("team_domain")
|
||||
s.ChannelID = r.PostForm.Get("channel_id")
|
||||
s.ChannelName = r.PostForm.Get("channel_name")
|
||||
s.UserID = r.PostForm.Get("user_id")
|
||||
s.UserName = r.PostForm.Get("user_name")
|
||||
s.Command = r.PostForm.Get("command")
|
||||
s.Text = r.PostForm.Get("text")
|
||||
s.ResponseURL = r.PostForm.Get("response_url")
|
||||
s.TriggerID = r.PostForm.Get("trigger_id")
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// ValidateToken validates verificationTokens
|
||||
func (s SlashCommand) ValidateToken(verificationTokens ...string) bool {
|
||||
for _, token := range verificationTokens {
|
||||
if s.Token == token {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
15
vendor/github.com/nlopes/slack/stars.go
generated
vendored
15
vendor/github.com/nlopes/slack/stars.go
generated
vendored
@ -45,7 +45,7 @@ func (api *Client) AddStar(channel string, item ItemRef) error {
|
||||
func (api *Client) AddStarContext(ctx context.Context, channel string, item ItemRef) error {
|
||||
values := url.Values{
|
||||
"channel": {channel},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if item.Timestamp != "" {
|
||||
values.Set("timestamp", string(item.Timestamp))
|
||||
@ -56,8 +56,9 @@ func (api *Client) AddStarContext(ctx context.Context, channel string, item Item
|
||||
if item.Comment != "" {
|
||||
values.Set("file_comment", string(item.Comment))
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := post(ctx, "stars.add", values, response, api.debug); err != nil {
|
||||
if err := post(ctx, api.httpclient, "stars.add", values, response, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
if !response.Ok {
|
||||
@ -75,7 +76,7 @@ func (api *Client) RemoveStar(channel string, item ItemRef) error {
|
||||
func (api *Client) RemoveStarContext(ctx context.Context, channel string, item ItemRef) error {
|
||||
values := url.Values{
|
||||
"channel": {channel},
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if item.Timestamp != "" {
|
||||
values.Set("timestamp", string(item.Timestamp))
|
||||
@ -86,8 +87,9 @@ func (api *Client) RemoveStarContext(ctx context.Context, channel string, item I
|
||||
if item.Comment != "" {
|
||||
values.Set("file_comment", string(item.Comment))
|
||||
}
|
||||
|
||||
response := &SlackResponse{}
|
||||
if err := post(ctx, "stars.remove", values, response, api.debug); err != nil {
|
||||
if err := post(ctx, api.httpclient, "stars.remove", values, response, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
if !response.Ok {
|
||||
@ -104,7 +106,7 @@ func (api *Client) ListStars(params StarsParameters) ([]Item, *Paging, error) {
|
||||
// ListStarsContext returns information about the stars a user added with a custom context
|
||||
func (api *Client) ListStarsContext(ctx context.Context, params StarsParameters) ([]Item, *Paging, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if params.User != DEFAULT_STARS_USER {
|
||||
values.Add("user", params.User)
|
||||
@ -115,8 +117,9 @@ func (api *Client) ListStarsContext(ctx context.Context, params StarsParameters)
|
||||
if params.Page != DEFAULT_STARS_PAGE {
|
||||
values.Add("page", strconv.Itoa(params.Page))
|
||||
}
|
||||
|
||||
response := &listResponseFull{}
|
||||
err := post(ctx, "stars.list", values, response, api.debug)
|
||||
err := post(ctx, api.httpclient, "stars.list", values, response, api.debug)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
29
vendor/github.com/nlopes/slack/team.go
generated
vendored
29
vendor/github.com/nlopes/slack/team.go
generated
vendored
@ -67,9 +67,9 @@ func NewAccessLogParameters() AccessLogParameters {
|
||||
}
|
||||
}
|
||||
|
||||
func teamRequest(ctx context.Context, path string, values url.Values, debug bool) (*TeamResponse, error) {
|
||||
func teamRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*TeamResponse, error) {
|
||||
response := &TeamResponse{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := post(ctx, client, path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -81,9 +81,9 @@ func teamRequest(ctx context.Context, path string, values url.Values, debug bool
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func billableInfoRequest(ctx context.Context, path string, values url.Values, debug bool) (map[string]BillingActive, error) {
|
||||
func billableInfoRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (map[string]BillingActive, error) {
|
||||
response := &BillableInfoResponse{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := post(ctx, client, path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -95,9 +95,9 @@ func billableInfoRequest(ctx context.Context, path string, values url.Values, de
|
||||
return response.BillableInfo, nil
|
||||
}
|
||||
|
||||
func accessLogsRequest(ctx context.Context, path string, values url.Values, debug bool) (*LoginResponse, error) {
|
||||
func accessLogsRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*LoginResponse, error) {
|
||||
response := &LoginResponse{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := post(ctx, client, path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -115,10 +115,10 @@ func (api *Client) GetTeamInfo() (*TeamInfo, error) {
|
||||
// GetTeamInfoContext gets the Team Information of the user with a custom context
|
||||
func (api *Client) GetTeamInfoContext(ctx context.Context) (*TeamInfo, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
response, err := teamRequest(ctx, "team.info", values, api.debug)
|
||||
response, err := teamRequest(ctx, api.httpclient, "team.info", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -133,7 +133,7 @@ func (api *Client) GetAccessLogs(params AccessLogParameters) ([]Login, *Paging,
|
||||
// GetAccessLogsContext retrieves a page of logins according to the parameters given with a custom context
|
||||
func (api *Client) GetAccessLogsContext(ctx context.Context, params AccessLogParameters) ([]Login, *Paging, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if params.Count != DEFAULT_LOGINS_COUNT {
|
||||
values.Add("count", strconv.Itoa(params.Count))
|
||||
@ -141,7 +141,8 @@ func (api *Client) GetAccessLogsContext(ctx context.Context, params AccessLogPar
|
||||
if params.Page != DEFAULT_LOGINS_PAGE {
|
||||
values.Add("page", strconv.Itoa(params.Page))
|
||||
}
|
||||
response, err := accessLogsRequest(ctx, "team.accessLogs", values, api.debug)
|
||||
|
||||
response, err := accessLogsRequest(ctx, api.httpclient, "team.accessLogs", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -154,11 +155,11 @@ func (api *Client) GetBillableInfo(user string) (map[string]BillingActive, error
|
||||
|
||||
func (api *Client) GetBillableInfoContext(ctx context.Context, user string) (map[string]BillingActive, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"user": {user},
|
||||
}
|
||||
|
||||
return billableInfoRequest(ctx, "team.billableInfo", values, api.debug)
|
||||
return billableInfoRequest(ctx, api.httpclient, "team.billableInfo", values, api.debug)
|
||||
}
|
||||
|
||||
// GetBillableInfoForTeam returns the billing_active status of all users on the team.
|
||||
@ -169,8 +170,8 @@ func (api *Client) GetBillableInfoForTeam() (map[string]BillingActive, error) {
|
||||
// GetBillableInfoForTeamContext returns the billing_active status of all users on the team with a custom context
|
||||
func (api *Client) GetBillableInfoForTeamContext(ctx context.Context) (map[string]BillingActive, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
return billableInfoRequest(ctx, "team.billableInfo", values, api.debug)
|
||||
return billableInfoRequest(ctx, api.httpclient, "team.billableInfo", values, api.debug)
|
||||
}
|
||||
|
32
vendor/github.com/nlopes/slack/usergroups.go
generated
vendored
32
vendor/github.com/nlopes/slack/usergroups.go
generated
vendored
@ -40,9 +40,9 @@ type userGroupResponseFull struct {
|
||||
SlackResponse
|
||||
}
|
||||
|
||||
func userGroupRequest(ctx context.Context, path string, values url.Values, debug bool) (*userGroupResponseFull, error) {
|
||||
func userGroupRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*userGroupResponseFull, error) {
|
||||
response := &userGroupResponseFull{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := post(ctx, client, path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -60,7 +60,7 @@ func (api *Client) CreateUserGroup(userGroup UserGroup) (UserGroup, error) {
|
||||
// CreateUserGroupContext creates a new user group with a custom context
|
||||
func (api *Client) CreateUserGroupContext(ctx context.Context, userGroup UserGroup) (UserGroup, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"name": {userGroup.Name},
|
||||
}
|
||||
|
||||
@ -76,7 +76,7 @@ func (api *Client) CreateUserGroupContext(ctx context.Context, userGroup UserGro
|
||||
values["channels"] = []string{strings.Join(userGroup.Prefs.Channels, ",")}
|
||||
}
|
||||
|
||||
response, err := userGroupRequest(ctx, "usergroups.create", values, api.debug)
|
||||
response, err := userGroupRequest(ctx, api.httpclient, "usergroups.create", values, api.debug)
|
||||
if err != nil {
|
||||
return UserGroup{}, err
|
||||
}
|
||||
@ -91,11 +91,11 @@ func (api *Client) DisableUserGroup(userGroup string) (UserGroup, error) {
|
||||
// DisableUserGroupContext disables an existing user group with a custom context
|
||||
func (api *Client) DisableUserGroupContext(ctx context.Context, userGroup string) (UserGroup, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"usergroup": {userGroup},
|
||||
}
|
||||
|
||||
response, err := userGroupRequest(ctx, "usergroups.disable", values, api.debug)
|
||||
response, err := userGroupRequest(ctx, api.httpclient, "usergroups.disable", values, api.debug)
|
||||
if err != nil {
|
||||
return UserGroup{}, err
|
||||
}
|
||||
@ -110,11 +110,11 @@ func (api *Client) EnableUserGroup(userGroup string) (UserGroup, error) {
|
||||
// EnableUserGroupContext enables an existing user group with a custom context
|
||||
func (api *Client) EnableUserGroupContext(ctx context.Context, userGroup string) (UserGroup, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"usergroup": {userGroup},
|
||||
}
|
||||
|
||||
response, err := userGroupRequest(ctx, "usergroups.enable", values, api.debug)
|
||||
response, err := userGroupRequest(ctx, api.httpclient, "usergroups.enable", values, api.debug)
|
||||
if err != nil {
|
||||
return UserGroup{}, err
|
||||
}
|
||||
@ -129,10 +129,10 @@ func (api *Client) GetUserGroups() ([]UserGroup, error) {
|
||||
// GetUserGroupsContext returns a list of user groups for the team with a custom context
|
||||
func (api *Client) GetUserGroupsContext(ctx context.Context) ([]UserGroup, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
|
||||
response, err := userGroupRequest(ctx, "usergroups.list", values, api.debug)
|
||||
response, err := userGroupRequest(ctx, api.httpclient, "usergroups.list", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -147,7 +147,7 @@ func (api *Client) UpdateUserGroup(userGroup UserGroup) (UserGroup, error) {
|
||||
// UpdateUserGroupContext will update an existing user group with a custom context
|
||||
func (api *Client) UpdateUserGroupContext(ctx context.Context, userGroup UserGroup) (UserGroup, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"usergroup": {userGroup.ID},
|
||||
}
|
||||
|
||||
@ -163,7 +163,7 @@ func (api *Client) UpdateUserGroupContext(ctx context.Context, userGroup UserGro
|
||||
values["description"] = []string{userGroup.Description}
|
||||
}
|
||||
|
||||
response, err := userGroupRequest(ctx, "usergroups.update", values, api.debug)
|
||||
response, err := userGroupRequest(ctx, api.httpclient, "usergroups.update", values, api.debug)
|
||||
if err != nil {
|
||||
return UserGroup{}, err
|
||||
}
|
||||
@ -178,11 +178,11 @@ func (api *Client) GetUserGroupMembers(userGroup string) ([]string, error) {
|
||||
// GetUserGroupMembersContext will retrieve the current list of users in a group with a custom context
|
||||
func (api *Client) GetUserGroupMembersContext(ctx context.Context, userGroup string) ([]string, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"usergroup": {userGroup},
|
||||
}
|
||||
|
||||
response, err := userGroupRequest(ctx, "usergroups.users.list", values, api.debug)
|
||||
response, err := userGroupRequest(ctx, api.httpclient, "usergroups.users.list", values, api.debug)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
@ -197,12 +197,12 @@ func (api *Client) UpdateUserGroupMembers(userGroup string, members string) (Use
|
||||
// UpdateUserGroupMembersContext will update the members of an existing user group with a custom context
|
||||
func (api *Client) UpdateUserGroupMembersContext(ctx context.Context, userGroup string, members string) (UserGroup, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"usergroup": {userGroup},
|
||||
"users": {members},
|
||||
}
|
||||
|
||||
response, err := userGroupRequest(ctx, "usergroups.users.update", values, api.debug)
|
||||
response, err := userGroupRequest(ctx, api.httpclient, "usergroups.users.update", values, api.debug)
|
||||
if err != nil {
|
||||
return UserGroup{}, err
|
||||
}
|
||||
|
118
vendor/github.com/nlopes/slack/users.go
generated
vendored
118
vendor/github.com/nlopes/slack/users.go
generated
vendored
@ -15,29 +15,33 @@ const (
|
||||
|
||||
// UserProfile contains all the information details of a given user
|
||||
type UserProfile struct {
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
RealName string `json:"real_name"`
|
||||
RealNameNormalized string `json:"real_name_normalized"`
|
||||
Email string `json:"email"`
|
||||
Skype string `json:"skype"`
|
||||
Phone string `json:"phone"`
|
||||
Image24 string `json:"image_24"`
|
||||
Image32 string `json:"image_32"`
|
||||
Image48 string `json:"image_48"`
|
||||
Image72 string `json:"image_72"`
|
||||
Image192 string `json:"image_192"`
|
||||
ImageOriginal string `json:"image_original"`
|
||||
Title string `json:"title"`
|
||||
BotID string `json:"bot_id,omitempty"`
|
||||
ApiAppID string `json:"api_app_id,omitempty"`
|
||||
StatusText string `json:"status_text,omitempty"`
|
||||
StatusEmoji string `json:"status_emoji,omitempty"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
RealName string `json:"real_name"`
|
||||
RealNameNormalized string `json:"real_name_normalized"`
|
||||
DisplayName string `json:"display_name"`
|
||||
DisplayNameNormalized string `json:"display_name_normalized"`
|
||||
Email string `json:"email"`
|
||||
Skype string `json:"skype"`
|
||||
Phone string `json:"phone"`
|
||||
Image24 string `json:"image_24"`
|
||||
Image32 string `json:"image_32"`
|
||||
Image48 string `json:"image_48"`
|
||||
Image72 string `json:"image_72"`
|
||||
Image192 string `json:"image_192"`
|
||||
ImageOriginal string `json:"image_original"`
|
||||
Title string `json:"title"`
|
||||
BotID string `json:"bot_id,omitempty"`
|
||||
ApiAppID string `json:"api_app_id,omitempty"`
|
||||
StatusText string `json:"status_text,omitempty"`
|
||||
StatusEmoji string `json:"status_emoji,omitempty"`
|
||||
Team string `json:"team"`
|
||||
}
|
||||
|
||||
// User contains all the information of a user
|
||||
type User struct {
|
||||
ID string `json:"id"`
|
||||
TeamID string `json:"team_id"`
|
||||
Name string `json:"name"`
|
||||
Deleted bool `json:"deleted"`
|
||||
Color string `json:"color"`
|
||||
@ -52,9 +56,12 @@ type User struct {
|
||||
IsPrimaryOwner bool `json:"is_primary_owner"`
|
||||
IsRestricted bool `json:"is_restricted"`
|
||||
IsUltraRestricted bool `json:"is_ultra_restricted"`
|
||||
IsStranger bool `json:"is_stranger"`
|
||||
IsAppUser bool `json:"is_app_user"`
|
||||
Has2FA bool `json:"has_2fa"`
|
||||
HasFiles bool `json:"has_files"`
|
||||
Presence string `json:"presence"`
|
||||
Locale string `json:"locale"`
|
||||
}
|
||||
|
||||
// UserPresence contains details about a user online status
|
||||
@ -121,9 +128,9 @@ func NewUserSetPhotoParams() UserSetPhotoParams {
|
||||
}
|
||||
}
|
||||
|
||||
func userRequest(ctx context.Context, path string, values url.Values, debug bool) (*userResponseFull, error) {
|
||||
func userRequest(ctx context.Context, client HTTPRequester, path string, values url.Values, debug bool) (*userResponseFull, error) {
|
||||
response := &userResponseFull{}
|
||||
err := post(ctx, path, values, response, debug)
|
||||
err := postForm(ctx, client, SLACK_API+path, values, response, debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -141,10 +148,11 @@ func (api *Client) GetUserPresence(user string) (*UserPresence, error) {
|
||||
// GetUserPresenceContext will retrieve the current presence status of given user with a custom context.
|
||||
func (api *Client) GetUserPresenceContext(ctx context.Context, user string) (*UserPresence, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"user": {user},
|
||||
}
|
||||
response, err := userRequest(ctx, "users.getPresence", values, api.debug)
|
||||
|
||||
response, err := userRequest(ctx, api.httpclient, "users.getPresence", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -159,10 +167,11 @@ func (api *Client) GetUserInfo(user string) (*User, error) {
|
||||
// GetUserInfoContext will retrieve the complete user information with a custom context
|
||||
func (api *Client) GetUserInfoContext(ctx context.Context, user string) (*User, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"user": {user},
|
||||
}
|
||||
response, err := userRequest(ctx, "users.info", values, api.debug)
|
||||
|
||||
response, err := userRequest(ctx, api.httpclient, "users.info", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -177,30 +186,50 @@ func (api *Client) GetUsers() ([]User, error) {
|
||||
// GetUsersContext returns the list of users (with their detailed information) with a custom context
|
||||
func (api *Client) GetUsersContext(ctx context.Context) ([]User, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"presence": {"1"},
|
||||
}
|
||||
response, err := userRequest(ctx, "users.list", values, api.debug)
|
||||
|
||||
response, err := userRequest(ctx, api.httpclient, "users.list", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response.Members, nil
|
||||
}
|
||||
|
||||
// GetUserByEmail will retrieve the complete user information by email
|
||||
func (api *Client) GetUserByEmail(email string) (*User, error) {
|
||||
return api.GetUserByEmailContext(context.Background(), email)
|
||||
}
|
||||
|
||||
// GetUserByEmailContext will retrieve the complete user information by email with a custom context
|
||||
func (api *Client) GetUserByEmailContext(ctx context.Context, email string) (*User, error) {
|
||||
values := url.Values{
|
||||
"token": {api.token},
|
||||
"email": {email},
|
||||
}
|
||||
response, err := userRequest(ctx, api.httpclient, "users.lookupByEmail", values, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response.User, nil
|
||||
}
|
||||
|
||||
// SetUserAsActive marks the currently authenticated user as active
|
||||
func (api *Client) SetUserAsActive() error {
|
||||
return api.SetUserAsActiveContext(context.Background())
|
||||
}
|
||||
|
||||
// SetUserAsActiveContext marks the currently authenticated user as active with a custom context
|
||||
func (api *Client) SetUserAsActiveContext(ctx context.Context) error {
|
||||
func (api *Client) SetUserAsActiveContext(ctx context.Context) (err error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
_, err := userRequest(ctx, "users.setActive", values, api.debug)
|
||||
if err != nil {
|
||||
|
||||
if _, err := userRequest(ctx, api.httpclient, "users.setActive", values, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -212,10 +241,11 @@ func (api *Client) SetUserPresence(presence string) error {
|
||||
// SetUserPresenceContext changes the currently authenticated user presence with a custom context
|
||||
func (api *Client) SetUserPresenceContext(ctx context.Context, presence string) error {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"presence": {presence},
|
||||
}
|
||||
_, err := userRequest(ctx, "users.setPresence", values, api.debug)
|
||||
|
||||
_, err := userRequest(ctx, api.httpclient, "users.setPresence", values, api.debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -231,10 +261,11 @@ func (api *Client) GetUserIdentity() (*UserIdentityResponse, error) {
|
||||
// GetUserIdentityContext will retrieve user info available per identity scopes with a custom context
|
||||
func (api *Client) GetUserIdentityContext(ctx context.Context) (*UserIdentityResponse, error) {
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
response := &UserIdentityResponse{}
|
||||
err := post(ctx, "users.identity", values, response, api.debug)
|
||||
|
||||
err := postForm(ctx, api.httpclient, SLACK_API+"users.identity", values, response, api.debug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -245,15 +276,15 @@ func (api *Client) GetUserIdentityContext(ctx context.Context) (*UserIdentityRes
|
||||
}
|
||||
|
||||
// SetUserPhoto changes the currently authenticated user's profile image
|
||||
func (api *Client) SetUserPhoto(ctx context.Context, image string, params UserSetPhotoParams) error {
|
||||
return api.SetUserPhoto(context.Background(), image, params)
|
||||
func (api *Client) SetUserPhoto(image string, params UserSetPhotoParams) error {
|
||||
return api.SetUserPhotoContext(context.Background(), image, params)
|
||||
}
|
||||
|
||||
// SetUserPhotoContext changes the currently authenticated user's profile image using a custom context
|
||||
func (api *Client) SetUserPhotoContext(ctx context.Context, image string, params UserSetPhotoParams) error {
|
||||
response := &SlackResponse{}
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
if params.CropX != DEFAULT_USER_PHOTO_CROP_X {
|
||||
values.Add("crop_x", string(params.CropX))
|
||||
@ -264,7 +295,8 @@ func (api *Client) SetUserPhotoContext(ctx context.Context, image string, params
|
||||
if params.CropW != DEFAULT_USER_PHOTO_CROP_W {
|
||||
values.Add("crop_w", string(params.CropW))
|
||||
}
|
||||
err := postLocalWithMultipartResponse(ctx, "users.setPhoto", image, "image", values, response, api.debug)
|
||||
|
||||
err := postLocalWithMultipartResponse(ctx, api.httpclient, SLACK_API+"users.setPhoto", image, "image", values, response, api.debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -283,9 +315,10 @@ func (api *Client) DeleteUserPhoto() error {
|
||||
func (api *Client) DeleteUserPhotoContext(ctx context.Context) error {
|
||||
response := &SlackResponse{}
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
}
|
||||
err := post(ctx, "users.deletePhoto", values, response, api.debug)
|
||||
|
||||
err := postForm(ctx, api.httpclient, SLACK_API+"users.deletePhoto", values, response, api.debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -332,13 +365,12 @@ func (api *Client) SetUserCustomStatusContext(ctx context.Context, statusText, s
|
||||
}
|
||||
|
||||
values := url.Values{
|
||||
"token": {api.config.token},
|
||||
"token": {api.token},
|
||||
"profile": {string(profile)},
|
||||
}
|
||||
|
||||
response := &userResponseFull{}
|
||||
|
||||
if err = post(ctx, "users.profile.set", values, response, api.debug); err != nil {
|
||||
if err = postForm(ctx, api.httpclient, SLACK_API+"users.profile.set", values, response, api.debug); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
8
vendor/github.com/nlopes/slack/websocket.go
generated
vendored
8
vendor/github.com/nlopes/slack/websocket.go
generated
vendored
@ -5,7 +5,7 @@ import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/websocket"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -27,6 +27,7 @@ type RTM struct {
|
||||
IncomingEvents chan RTMEvent
|
||||
outgoingMessages chan OutgoingMessage
|
||||
killChannel chan bool
|
||||
disconnected chan struct{} // disconnected is closed when Disconnect is invoked, regardless of connection state. Allows for ManagedConnection to not leak.
|
||||
forcePing chan bool
|
||||
rawEvents chan json.RawMessage
|
||||
wasIntentional bool
|
||||
@ -59,9 +60,14 @@ type RTMOptions struct {
|
||||
|
||||
// Disconnect and wait, blocking until a successful disconnection.
|
||||
func (rtm *RTM) Disconnect() error {
|
||||
// this channel is always closed on disconnect. lets the ManagedConnection() function
|
||||
// properly clean up.
|
||||
close(rtm.disconnected)
|
||||
|
||||
if !rtm.isConnected {
|
||||
return errors.New("Invalid call to Disconnect - Slack API is already disconnected")
|
||||
}
|
||||
|
||||
rtm.killChannel <- true
|
||||
return nil
|
||||
}
|
||||
|
7
vendor/github.com/nlopes/slack/websocket_internals.go
generated
vendored
7
vendor/github.com/nlopes/slack/websocket_internals.go
generated
vendored
@ -63,6 +63,13 @@ func (m *MessageTooLongEvent) Error() string {
|
||||
return fmt.Sprintf("Message too long (max %d characters)", m.MaxLength)
|
||||
}
|
||||
|
||||
// RateLimitEvent is used when Slack warns that rate-limits are being hit.
|
||||
type RateLimitEvent struct{}
|
||||
|
||||
func (e *RateLimitEvent) Error() string {
|
||||
return "Messages are being sent too fast."
|
||||
}
|
||||
|
||||
// OutgoingErrorEvent contains information in case there were errors sending messages
|
||||
type OutgoingErrorEvent struct {
|
||||
Message OutgoingMessage
|
||||
|
41
vendor/github.com/nlopes/slack/websocket_managed_conn.go
generated
vendored
41
vendor/github.com/nlopes/slack/websocket_managed_conn.go
generated
vendored
@ -4,10 +4,11 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/websocket"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
// ManageConnection can be called on a Slack RTM instance returned by the
|
||||
@ -33,6 +34,7 @@ func (rtm *RTM) ManageConnection() {
|
||||
// if err != nil then the connection is sucessful - otherwise it is
|
||||
// fatal
|
||||
if err != nil {
|
||||
rtm.Debugf("Failed to connect with RTM on try %d: %s", connectionCount, err)
|
||||
return
|
||||
}
|
||||
rtm.info = info
|
||||
@ -44,6 +46,8 @@ func (rtm *RTM) ManageConnection() {
|
||||
rtm.conn = conn
|
||||
rtm.isConnected = true
|
||||
|
||||
rtm.Debugf("RTM connection succeeded on try %d", connectionCount)
|
||||
|
||||
keepRunning := make(chan bool)
|
||||
// we're now connected (or have failed fatally) so we can set up
|
||||
// listeners
|
||||
@ -89,6 +93,7 @@ func (rtm *RTM) connect(connectionCount int, useRTMStart bool) (*Info, *websocke
|
||||
}
|
||||
// check for fatal errors - currently only invalid_auth
|
||||
if sErr, ok := err.(*WebError); ok && (sErr.Error() == "invalid_auth" || sErr.Error() == "account_inactive") {
|
||||
rtm.Debugf("Invalid auth when connecting with RTM: %s", err)
|
||||
rtm.IncomingEvents <- RTMEvent{"invalid_auth", &InvalidAuthEvent{}}
|
||||
return nil, nil, sErr
|
||||
}
|
||||
@ -99,6 +104,15 @@ func (rtm *RTM) connect(connectionCount int, useRTMStart bool) (*Info, *websocke
|
||||
Attempt: boff.attempts,
|
||||
ErrorObj: err,
|
||||
}}
|
||||
|
||||
// check if Disconnect() has been invoked.
|
||||
select {
|
||||
case _ = <-rtm.disconnected:
|
||||
rtm.IncomingEvents <- RTMEvent{"disconnected", &DisconnectedEvent{Intentional: true}}
|
||||
return nil, nil, fmt.Errorf("disconnect received while trying to connect")
|
||||
default:
|
||||
}
|
||||
|
||||
// get time we should wait before attempting to connect again
|
||||
dur := boff.Duration()
|
||||
rtm.Debugf("reconnection %d failed: %s", boff.attempts+1, err)
|
||||
@ -116,16 +130,24 @@ func (rtm *RTM) startRTMAndDial(useRTMStart bool) (*Info, *websocket.Conn, error
|
||||
var err error
|
||||
|
||||
if useRTMStart {
|
||||
rtm.Debugf("Starting RTM")
|
||||
info, url, err = rtm.StartRTM()
|
||||
} else {
|
||||
rtm.Debugf("Connecting to RTM")
|
||||
info, url, err = rtm.ConnectRTM()
|
||||
}
|
||||
if err != nil {
|
||||
rtm.Debugf("Failed to start or connect to RTM: %s", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
conn, err := websocketProxyDial(url, "http://api.slack.com")
|
||||
rtm.Debugf("Dialing to websocket on url %s", url)
|
||||
// Only use HTTPS for connections to prevent MITM attacks on the connection.
|
||||
upgradeHeader := http.Header{}
|
||||
upgradeHeader.Add("Origin", "https://api.slack.com")
|
||||
conn, _, err := websocket.DefaultDialer.Dial(url, upgradeHeader)
|
||||
if err != nil {
|
||||
rtm.Debugf("Failed to dial to the websocket: %s", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
return info, conn, err
|
||||
@ -208,7 +230,7 @@ func (rtm *RTM) sendWithDeadline(msg interface{}) error {
|
||||
if err := rtm.conn.SetWriteDeadline(time.Now().Add(10 * time.Second)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := websocket.JSON.Send(rtm.conn, msg); err != nil {
|
||||
if err := rtm.conn.WriteJSON(msg); err != nil {
|
||||
return err
|
||||
}
|
||||
// remove write deadline
|
||||
@ -263,7 +285,7 @@ func (rtm *RTM) ping() error {
|
||||
// This will block until a frame is available from the websocket.
|
||||
func (rtm *RTM) receiveIncomingEvent() {
|
||||
event := json.RawMessage{}
|
||||
err := websocket.JSON.Receive(rtm.conn, &event)
|
||||
err := rtm.conn.ReadJSON(&event)
|
||||
if err == io.EOF {
|
||||
// EOF's don't seem to signify a failed connection so instead we ignore
|
||||
// them here and detect a failed connection upon attempting to send a
|
||||
@ -317,10 +339,19 @@ func (rtm *RTM) handleAck(event json.RawMessage) {
|
||||
rtm.Debugln(" -> Erroneous 'ack' event:", string(event))
|
||||
return
|
||||
}
|
||||
|
||||
if ack.Ok {
|
||||
rtm.IncomingEvents <- RTMEvent{"ack", ack}
|
||||
} else if ack.RTMResponse.Error != nil {
|
||||
// As there is no documentation for RTM error-codes, this
|
||||
// identification of a rate-limit warning is very brittle.
|
||||
if ack.RTMResponse.Error.Code == -1 && ack.RTMResponse.Error.Msg == "slow down, too many messages..." {
|
||||
rtm.IncomingEvents <- RTMEvent{"ack_error", &RateLimitEvent{}}
|
||||
} else {
|
||||
rtm.IncomingEvents <- RTMEvent{"ack_error", &AckErrorEvent{ack.Error}}
|
||||
}
|
||||
} else {
|
||||
rtm.IncomingEvents <- RTMEvent{"ack_error", &AckErrorEvent{ack.Error}}
|
||||
rtm.IncomingEvents <- RTMEvent{"ack_error", &AckErrorEvent{fmt.Errorf("ack decode failure")}}
|
||||
}
|
||||
}
|
||||
|
||||
|
83
vendor/github.com/nlopes/slack/websocket_proxy.go
generated
vendored
83
vendor/github.com/nlopes/slack/websocket_proxy.go
generated
vendored
@ -1,83 +0,0 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
// Taken and reworked from: https://gist.github.com/madmo/8548738
|
||||
func websocketHTTPConnect(proxy, urlString string) (net.Conn, error) {
|
||||
p, err := net.Dial("tcp", proxy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
turl, err := url.Parse(urlString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := http.Request{
|
||||
Method: "CONNECT",
|
||||
URL: &url.URL{},
|
||||
Host: turl.Host,
|
||||
}
|
||||
|
||||
cc := httputil.NewProxyClientConn(p, nil)
|
||||
cc.Do(&req)
|
||||
if err != nil && err != httputil.ErrPersistEOF {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rwc, _ := cc.Hijack()
|
||||
|
||||
return rwc, nil
|
||||
}
|
||||
|
||||
func websocketProxyDial(urlString, origin string) (ws *websocket.Conn, err error) {
|
||||
if os.Getenv("HTTP_PROXY") == "" {
|
||||
return websocket.Dial(urlString, "", origin)
|
||||
}
|
||||
|
||||
purl, err := url.Parse(os.Getenv("HTTP_PROXY"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config, err := websocket.NewConfig(urlString, origin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := websocketHTTPConnect(purl.Host, urlString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch config.Location.Scheme {
|
||||
case "ws":
|
||||
case "wss":
|
||||
tlsClient := tls.Client(client, &tls.Config{
|
||||
ServerName: strings.Split(config.Location.Host, ":")[0],
|
||||
})
|
||||
err := tlsClient.Handshake()
|
||||
if err != nil {
|
||||
tlsClient.Close()
|
||||
return nil, err
|
||||
}
|
||||
client = tlsClient
|
||||
|
||||
default:
|
||||
return nil, errors.New("invalid websocket schema")
|
||||
}
|
||||
|
||||
return websocket.NewClient(config, client)
|
||||
}
|
20
vendor/github.com/nlopes/slack/websocket_utils.go
generated
vendored
20
vendor/github.com/nlopes/slack/websocket_utils.go
generated
vendored
@ -1,20 +0,0 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
var portMapping = map[string]string{"ws": "80", "wss": "443"}
|
||||
|
||||
func websocketizeURLPort(orig string) (string, error) {
|
||||
urlObj, err := url.ParseRequestURI(orig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_, _, err = net.SplitHostPort(urlObj.Host)
|
||||
if err != nil {
|
||||
return urlObj.Scheme + "://" + urlObj.Host + ":" + portMapping[urlObj.Scheme] + urlObj.Path, nil
|
||||
}
|
||||
return orig, nil
|
||||
}
|
6
vendor/github.com/nsf/termbox-go/README.md
generated
vendored
6
vendor/github.com/nsf/termbox-go/README.md
generated
vendored
@ -1,3 +1,5 @@
|
||||
[![GoDoc](https://godoc.org/github.com/nsf/termbox-go?status.svg)](http://godoc.org/github.com/nsf/termbox-go)
|
||||
|
||||
## Termbox
|
||||
Termbox is a library that provides a minimalistic API which allows the programmer to write text-based user interfaces. The library is crossplatform and has both terminal-based implementations on *nix operating systems and a winapi console based implementation for windows operating systems. The basic idea is an abstraction of the greatest common subset of features available on all major terminals and other terminal-like APIs in a minimalistic fashion. Small API means it is easy to implement, test, maintain and learn it, that's what makes the termbox a distinct library in its area.
|
||||
|
||||
@ -33,6 +35,4 @@ There are also some interesting projects using termbox-go:
|
||||
- [jv](https://github.com/maxzender/jv) helps you view JSON on the command-line.
|
||||
- [pinger](https://github.com/hirose31/pinger) helps you to monitor numerous hosts using ICMP ECHO_REQUEST.
|
||||
- [vixl44](https://github.com/sebashwa/vixl44) lets you create pixel art inside your terminal using vim movements
|
||||
|
||||
### API reference
|
||||
[godoc.org/github.com/nsf/termbox-go](http://godoc.org/github.com/nsf/termbox-go)
|
||||
- [zterm](https://github.com/varunrau/zterm) is a typing game inspired by http://zty.pe/
|
||||
|
44
vendor/github.com/nsf/termbox-go/api.go
generated
vendored
44
vendor/github.com/nsf/termbox-go/api.go
generated
vendored
@ -8,6 +8,7 @@ import "os"
|
||||
import "os/signal"
|
||||
import "syscall"
|
||||
import "runtime"
|
||||
import "time"
|
||||
|
||||
// public API
|
||||
|
||||
@ -253,8 +254,8 @@ func CellBuffer() []Cell {
|
||||
// NOTE: This API is experimental and may change in future.
|
||||
func ParseEvent(data []byte) Event {
|
||||
event := Event{Type: EventKey}
|
||||
ok := extract_event(data, &event)
|
||||
if !ok {
|
||||
status := extract_event(data, &event, false)
|
||||
if status != event_extracted {
|
||||
return Event{Type: EventNone, N: event.N}
|
||||
}
|
||||
return event
|
||||
@ -303,34 +304,65 @@ func PollRawEvent(data []byte) Event {
|
||||
|
||||
// Wait for an event and return it. This is a blocking function call.
|
||||
func PollEvent() Event {
|
||||
// Constant governing macOS specific behavior. See https://github.com/nsf/termbox-go/issues/132
|
||||
// This is an arbitrary delay which hopefully will be enough time for any lagging
|
||||
// partial escape sequences to come through.
|
||||
const esc_wait_delay = 100 * time.Millisecond
|
||||
|
||||
var event Event
|
||||
var esc_wait_timer *time.Timer
|
||||
var esc_timeout <-chan time.Time
|
||||
|
||||
// try to extract event from input buffer, return on success
|
||||
event.Type = EventKey
|
||||
ok := extract_event(inbuf, &event)
|
||||
status := extract_event(inbuf, &event, true)
|
||||
if event.N != 0 {
|
||||
copy(inbuf, inbuf[event.N:])
|
||||
inbuf = inbuf[:len(inbuf)-event.N]
|
||||
}
|
||||
if ok {
|
||||
if status == event_extracted {
|
||||
return event
|
||||
} else if status == esc_wait {
|
||||
esc_wait_timer = time.NewTimer(esc_wait_delay)
|
||||
esc_timeout = esc_wait_timer.C
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case ev := <-input_comm:
|
||||
if esc_wait_timer != nil {
|
||||
if !esc_wait_timer.Stop() {
|
||||
<-esc_wait_timer.C
|
||||
}
|
||||
esc_wait_timer = nil
|
||||
}
|
||||
|
||||
if ev.err != nil {
|
||||
return Event{Type: EventError, Err: ev.err}
|
||||
}
|
||||
|
||||
inbuf = append(inbuf, ev.data...)
|
||||
input_comm <- ev
|
||||
ok := extract_event(inbuf, &event)
|
||||
status := extract_event(inbuf, &event, true)
|
||||
if event.N != 0 {
|
||||
copy(inbuf, inbuf[event.N:])
|
||||
inbuf = inbuf[:len(inbuf)-event.N]
|
||||
}
|
||||
if ok {
|
||||
if status == event_extracted {
|
||||
return event
|
||||
} else if status == esc_wait {
|
||||
esc_wait_timer = time.NewTimer(esc_wait_delay)
|
||||
esc_timeout = esc_wait_timer.C
|
||||
}
|
||||
case <-esc_timeout:
|
||||
esc_wait_timer = nil
|
||||
|
||||
status := extract_event(inbuf, &event, false)
|
||||
if event.N != 0 {
|
||||
copy(inbuf, inbuf[event.N:])
|
||||
inbuf = inbuf[:len(inbuf)-event.N]
|
||||
}
|
||||
if status == event_extracted {
|
||||
return event
|
||||
}
|
||||
case <-interrupt_comm:
|
||||
|
2
vendor/github.com/nsf/termbox-go/api_common.go
generated
vendored
2
vendor/github.com/nsf/termbox-go/api_common.go
generated
vendored
@ -148,7 +148,7 @@ const (
|
||||
// using bitwise OR ('|'). Although, colors cannot be combined. But you can
|
||||
// combine attributes and a single color.
|
||||
//
|
||||
// It's worth mentioning that some platforms don't support certain attibutes.
|
||||
// It's worth mentioning that some platforms don't support certain attributes.
|
||||
// For example windows console doesn't support AttrUnderline. And on some
|
||||
// terminals applying AttrBold to background may result in blinking text. Use
|
||||
// them with caution and test your code on various terminals.
|
||||
|
11
vendor/github.com/nsf/termbox-go/escwait.go
generated
vendored
Normal file
11
vendor/github.com/nsf/termbox-go/escwait.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
// +build !darwin
|
||||
|
||||
package termbox
|
||||
|
||||
// On all systems other than macOS, disable behavior which will wait before
|
||||
// deciding that the escape key was pressed, to account for partially send
|
||||
// escape sequences, especially with regard to lengthy mouse sequences.
|
||||
// See https://github.com/nsf/termbox-go/issues/132
|
||||
func enable_wait_for_escape_sequence() bool {
|
||||
return false
|
||||
}
|
9
vendor/github.com/nsf/termbox-go/escwait_darwin.go
generated
vendored
Normal file
9
vendor/github.com/nsf/termbox-go/escwait_darwin.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
package termbox
|
||||
|
||||
// On macOS, enable behavior which will wait before deciding that the escape
|
||||
// key was pressed, to account for partially send escape sequences, especially
|
||||
// with regard to lengthy mouse sequences.
|
||||
// See https://github.com/nsf/termbox-go/issues/132
|
||||
func enable_wait_for_escape_sequence() bool {
|
||||
return true
|
||||
}
|
39
vendor/github.com/nsf/termbox-go/syscalls.go
generated
vendored
Normal file
39
vendor/github.com/nsf/termbox-go/syscalls.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
// +build ignore
|
||||
|
||||
package termbox
|
||||
|
||||
/*
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type syscall_Termios C.struct_termios
|
||||
|
||||
const (
|
||||
syscall_IGNBRK = C.IGNBRK
|
||||
syscall_BRKINT = C.BRKINT
|
||||
syscall_PARMRK = C.PARMRK
|
||||
syscall_ISTRIP = C.ISTRIP
|
||||
syscall_INLCR = C.INLCR
|
||||
syscall_IGNCR = C.IGNCR
|
||||
syscall_ICRNL = C.ICRNL
|
||||
syscall_IXON = C.IXON
|
||||
syscall_OPOST = C.OPOST
|
||||
syscall_ECHO = C.ECHO
|
||||
syscall_ECHONL = C.ECHONL
|
||||
syscall_ICANON = C.ICANON
|
||||
syscall_ISIG = C.ISIG
|
||||
syscall_IEXTEN = C.IEXTEN
|
||||
syscall_CSIZE = C.CSIZE
|
||||
syscall_PARENB = C.PARENB
|
||||
syscall_CS8 = C.CS8
|
||||
syscall_VMIN = C.VMIN
|
||||
syscall_VTIME = C.VTIME
|
||||
|
||||
// on darwin change these to (on *bsd too?):
|
||||
// C.TIOCGETA
|
||||
// C.TIOCSETA
|
||||
syscall_TCGETS = C.TCGETS
|
||||
syscall_TCSETS = C.TCSETS
|
||||
)
|
40
vendor/github.com/nsf/termbox-go/termbox.go
generated
vendored
40
vendor/github.com/nsf/termbox-go/termbox.go
generated
vendored
@ -41,6 +41,14 @@ type input_event struct {
|
||||
err error
|
||||
}
|
||||
|
||||
type extract_event_res int
|
||||
|
||||
const (
|
||||
event_not_extracted extract_event_res = iota
|
||||
event_extracted
|
||||
esc_wait
|
||||
)
|
||||
|
||||
var (
|
||||
// term specific sequences
|
||||
keys []string
|
||||
@ -417,7 +425,7 @@ func parse_escape_sequence(event *Event, buf []byte) (int, bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// if none of the keys match, let's try mouse seqences
|
||||
// if none of the keys match, let's try mouse sequences
|
||||
return parse_mouse_event(event, bufstr)
|
||||
}
|
||||
|
||||
@ -440,17 +448,27 @@ func extract_raw_event(data []byte, event *Event) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func extract_event(inbuf []byte, event *Event) bool {
|
||||
func extract_event(inbuf []byte, event *Event, allow_esc_wait bool) extract_event_res {
|
||||
if len(inbuf) == 0 {
|
||||
event.N = 0
|
||||
return false
|
||||
return event_not_extracted
|
||||
}
|
||||
|
||||
if inbuf[0] == '\033' {
|
||||
// possible escape sequence
|
||||
if n, ok := parse_escape_sequence(event, inbuf); n != 0 {
|
||||
event.N = n
|
||||
return ok
|
||||
if ok {
|
||||
return event_extracted
|
||||
} else {
|
||||
return event_not_extracted
|
||||
}
|
||||
}
|
||||
|
||||
// possible partially read escape sequence; trigger a wait if appropriate
|
||||
if enable_wait_for_escape_sequence() && allow_esc_wait {
|
||||
event.N = 0
|
||||
return esc_wait
|
||||
}
|
||||
|
||||
// it's not escape sequence, then it's Alt or Esc, check input_mode
|
||||
@ -461,17 +479,17 @@ func extract_event(inbuf []byte, event *Event) bool {
|
||||
event.Key = KeyEsc
|
||||
event.Mod = 0
|
||||
event.N = 1
|
||||
return true
|
||||
return event_extracted
|
||||
case input_mode&InputAlt != 0:
|
||||
// if we're in alt mode, set Alt modifier to event and redo parsing
|
||||
event.Mod = ModAlt
|
||||
ok := extract_event(inbuf[1:], event)
|
||||
if ok {
|
||||
status := extract_event(inbuf[1:], event, false)
|
||||
if status == event_extracted {
|
||||
event.N++
|
||||
} else {
|
||||
event.N = 0
|
||||
}
|
||||
return ok
|
||||
return status
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
@ -486,7 +504,7 @@ func extract_event(inbuf []byte, event *Event) bool {
|
||||
event.Ch = 0
|
||||
event.Key = Key(inbuf[0])
|
||||
event.N = 1
|
||||
return true
|
||||
return event_extracted
|
||||
}
|
||||
|
||||
// the only possible option is utf8 rune
|
||||
@ -494,10 +512,10 @@ func extract_event(inbuf []byte, event *Event) bool {
|
||||
event.Ch = r
|
||||
event.Key = 0
|
||||
event.N = n
|
||||
return true
|
||||
return event_extracted
|
||||
}
|
||||
|
||||
return false
|
||||
return event_not_extracted
|
||||
}
|
||||
|
||||
func fcntl(fd int, cmd int, arg int) (val int, err error) {
|
||||
|
7
vendor/github.com/nsf/termbox-go/terminfo.go
generated
vendored
7
vendor/github.com/nsf/termbox-go/terminfo.go
generated
vendored
@ -151,11 +151,16 @@ func setup_term() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
number_sec_len := int16(2)
|
||||
if header[0] == 542 { // doc says it should be octal 0542, but what I see it terminfo files is 542, learn to program please... thank you..
|
||||
number_sec_len = 4
|
||||
}
|
||||
|
||||
if (header[1]+header[2])%2 != 0 {
|
||||
// old quirk to align everything on word boundaries
|
||||
header[2] += 1
|
||||
}
|
||||
str_offset = ti_header_length + header[1] + header[2] + 2*header[3]
|
||||
str_offset = ti_header_length + header[1] + header[2] + number_sec_len*header[3]
|
||||
table_offset = str_offset + 2*header[4]
|
||||
|
||||
keys = make([]string, 0xFFFF-key_min)
|
||||
|
16
vendor/github.com/renstrom/fuzzysearch/fuzzy/fuzzy.go
generated
vendored
16
vendor/github.com/renstrom/fuzzysearch/fuzzy/fuzzy.go
generated
vendored
@ -122,8 +122,8 @@ Outer:
|
||||
|
||||
// RankFind is similar to Find, except it will also rank all matches using
|
||||
// Levenshtein distance.
|
||||
func RankFind(source string, targets []string) Ranks {
|
||||
var r Ranks
|
||||
func RankFind(source string, targets []string) ranks {
|
||||
var r ranks
|
||||
for _, target := range find(source, targets, noop) {
|
||||
distance := LevenshteinDistance(source, target)
|
||||
r = append(r, Rank{source, target, distance})
|
||||
@ -132,8 +132,8 @@ func RankFind(source string, targets []string) Ranks {
|
||||
}
|
||||
|
||||
// RankFindFold is a case-insensitive version of RankFind.
|
||||
func RankFindFold(source string, targets []string) Ranks {
|
||||
var r Ranks
|
||||
func RankFindFold(source string, targets []string) ranks {
|
||||
var r ranks
|
||||
for _, target := range find(source, targets, unicode.ToLower) {
|
||||
distance := LevenshteinDistance(source, target)
|
||||
r = append(r, Rank{source, target, distance})
|
||||
@ -152,16 +152,16 @@ type Rank struct {
|
||||
Distance int
|
||||
}
|
||||
|
||||
type Ranks []Rank
|
||||
type ranks []Rank
|
||||
|
||||
func (r Ranks) Len() int {
|
||||
func (r ranks) Len() int {
|
||||
return len(r)
|
||||
}
|
||||
|
||||
func (r Ranks) Swap(i, j int) {
|
||||
func (r ranks) Swap(i, j int) {
|
||||
r[i], r[j] = r[j], r[i]
|
||||
}
|
||||
|
||||
func (r Ranks) Less(i, j int) bool {
|
||||
func (r ranks) Less(i, j int) bool {
|
||||
return r[i].Distance < r[j].Distance
|
||||
}
|
||||
|
27
vendor/golang.org/x/net/LICENSE
generated
vendored
27
vendor/golang.org/x/net/LICENSE
generated
vendored
@ -1,27 +0,0 @@
|
||||
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.
|
22
vendor/golang.org/x/net/PATENTS
generated
vendored
22
vendor/golang.org/x/net/PATENTS
generated
vendored
@ -1,22 +0,0 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google 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,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
106
vendor/golang.org/x/net/websocket/client.go
generated
vendored
106
vendor/golang.org/x/net/websocket/client.go
generated
vendored
@ -1,106 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// DialError is an error that occurs while dialling a websocket server.
|
||||
type DialError struct {
|
||||
*Config
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *DialError) Error() string {
|
||||
return "websocket.Dial " + e.Config.Location.String() + ": " + e.Err.Error()
|
||||
}
|
||||
|
||||
// NewConfig creates a new WebSocket config for client connection.
|
||||
func NewConfig(server, origin string) (config *Config, err error) {
|
||||
config = new(Config)
|
||||
config.Version = ProtocolVersionHybi13
|
||||
config.Location, err = url.ParseRequestURI(server)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
config.Origin, err = url.ParseRequestURI(origin)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
config.Header = http.Header(make(map[string][]string))
|
||||
return
|
||||
}
|
||||
|
||||
// NewClient creates a new WebSocket client connection over rwc.
|
||||
func NewClient(config *Config, rwc io.ReadWriteCloser) (ws *Conn, err error) {
|
||||
br := bufio.NewReader(rwc)
|
||||
bw := bufio.NewWriter(rwc)
|
||||
err = hybiClientHandshake(config, br, bw)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
buf := bufio.NewReadWriter(br, bw)
|
||||
ws = newHybiClientConn(config, buf, rwc)
|
||||
return
|
||||
}
|
||||
|
||||
// Dial opens a new client connection to a WebSocket.
|
||||
func Dial(url_, protocol, origin string) (ws *Conn, err error) {
|
||||
config, err := NewConfig(url_, origin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if protocol != "" {
|
||||
config.Protocol = []string{protocol}
|
||||
}
|
||||
return DialConfig(config)
|
||||
}
|
||||
|
||||
var portMap = map[string]string{
|
||||
"ws": "80",
|
||||
"wss": "443",
|
||||
}
|
||||
|
||||
func parseAuthority(location *url.URL) string {
|
||||
if _, ok := portMap[location.Scheme]; ok {
|
||||
if _, _, err := net.SplitHostPort(location.Host); err != nil {
|
||||
return net.JoinHostPort(location.Host, portMap[location.Scheme])
|
||||
}
|
||||
}
|
||||
return location.Host
|
||||
}
|
||||
|
||||
// DialConfig opens a new client connection to a WebSocket with a config.
|
||||
func DialConfig(config *Config) (ws *Conn, err error) {
|
||||
var client net.Conn
|
||||
if config.Location == nil {
|
||||
return nil, &DialError{config, ErrBadWebSocketLocation}
|
||||
}
|
||||
if config.Origin == nil {
|
||||
return nil, &DialError{config, ErrBadWebSocketOrigin}
|
||||
}
|
||||
dialer := config.Dialer
|
||||
if dialer == nil {
|
||||
dialer = &net.Dialer{}
|
||||
}
|
||||
client, err = dialWithDialer(dialer, config)
|
||||
if err != nil {
|
||||
goto Error
|
||||
}
|
||||
ws, err = NewClient(config, client)
|
||||
if err != nil {
|
||||
client.Close()
|
||||
goto Error
|
||||
}
|
||||
return
|
||||
|
||||
Error:
|
||||
return nil, &DialError{config, err}
|
||||
}
|
24
vendor/golang.org/x/net/websocket/dial.go
generated
vendored
24
vendor/golang.org/x/net/websocket/dial.go
generated
vendored
@ -1,24 +0,0 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
)
|
||||
|
||||
func dialWithDialer(dialer *net.Dialer, config *Config) (conn net.Conn, err error) {
|
||||
switch config.Location.Scheme {
|
||||
case "ws":
|
||||
conn, err = dialer.Dial("tcp", parseAuthority(config.Location))
|
||||
|
||||
case "wss":
|
||||
conn, err = tls.DialWithDialer(dialer, "tcp", parseAuthority(config.Location), config.TlsConfig)
|
||||
|
||||
default:
|
||||
err = ErrBadScheme
|
||||
}
|
||||
return
|
||||
}
|
583
vendor/golang.org/x/net/websocket/hybi.go
generated
vendored
583
vendor/golang.org/x/net/websocket/hybi.go
generated
vendored
@ -1,583 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
// This file implements a protocol of hybi draft.
|
||||
// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
websocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
||||
|
||||
closeStatusNormal = 1000
|
||||
closeStatusGoingAway = 1001
|
||||
closeStatusProtocolError = 1002
|
||||
closeStatusUnsupportedData = 1003
|
||||
closeStatusFrameTooLarge = 1004
|
||||
closeStatusNoStatusRcvd = 1005
|
||||
closeStatusAbnormalClosure = 1006
|
||||
closeStatusBadMessageData = 1007
|
||||
closeStatusPolicyViolation = 1008
|
||||
closeStatusTooBigData = 1009
|
||||
closeStatusExtensionMismatch = 1010
|
||||
|
||||
maxControlFramePayloadLength = 125
|
||||
)
|
||||
|
||||
var (
|
||||
ErrBadMaskingKey = &ProtocolError{"bad masking key"}
|
||||
ErrBadPongMessage = &ProtocolError{"bad pong message"}
|
||||
ErrBadClosingStatus = &ProtocolError{"bad closing status"}
|
||||
ErrUnsupportedExtensions = &ProtocolError{"unsupported extensions"}
|
||||
ErrNotImplemented = &ProtocolError{"not implemented"}
|
||||
|
||||
handshakeHeader = map[string]bool{
|
||||
"Host": true,
|
||||
"Upgrade": true,
|
||||
"Connection": true,
|
||||
"Sec-Websocket-Key": true,
|
||||
"Sec-Websocket-Origin": true,
|
||||
"Sec-Websocket-Version": true,
|
||||
"Sec-Websocket-Protocol": true,
|
||||
"Sec-Websocket-Accept": true,
|
||||
}
|
||||
)
|
||||
|
||||
// A hybiFrameHeader is a frame header as defined in hybi draft.
|
||||
type hybiFrameHeader struct {
|
||||
Fin bool
|
||||
Rsv [3]bool
|
||||
OpCode byte
|
||||
Length int64
|
||||
MaskingKey []byte
|
||||
|
||||
data *bytes.Buffer
|
||||
}
|
||||
|
||||
// A hybiFrameReader is a reader for hybi frame.
|
||||
type hybiFrameReader struct {
|
||||
reader io.Reader
|
||||
|
||||
header hybiFrameHeader
|
||||
pos int64
|
||||
length int
|
||||
}
|
||||
|
||||
func (frame *hybiFrameReader) Read(msg []byte) (n int, err error) {
|
||||
n, err = frame.reader.Read(msg)
|
||||
if frame.header.MaskingKey != nil {
|
||||
for i := 0; i < n; i++ {
|
||||
msg[i] = msg[i] ^ frame.header.MaskingKey[frame.pos%4]
|
||||
frame.pos++
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (frame *hybiFrameReader) PayloadType() byte { return frame.header.OpCode }
|
||||
|
||||
func (frame *hybiFrameReader) HeaderReader() io.Reader {
|
||||
if frame.header.data == nil {
|
||||
return nil
|
||||
}
|
||||
if frame.header.data.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
return frame.header.data
|
||||
}
|
||||
|
||||
func (frame *hybiFrameReader) TrailerReader() io.Reader { return nil }
|
||||
|
||||
func (frame *hybiFrameReader) Len() (n int) { return frame.length }
|
||||
|
||||
// A hybiFrameReaderFactory creates new frame reader based on its frame type.
|
||||
type hybiFrameReaderFactory struct {
|
||||
*bufio.Reader
|
||||
}
|
||||
|
||||
// NewFrameReader reads a frame header from the connection, and creates new reader for the frame.
|
||||
// See Section 5.2 Base Framing protocol for detail.
|
||||
// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5.2
|
||||
func (buf hybiFrameReaderFactory) NewFrameReader() (frame frameReader, err error) {
|
||||
hybiFrame := new(hybiFrameReader)
|
||||
frame = hybiFrame
|
||||
var header []byte
|
||||
var b byte
|
||||
// First byte. FIN/RSV1/RSV2/RSV3/OpCode(4bits)
|
||||
b, err = buf.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
header = append(header, b)
|
||||
hybiFrame.header.Fin = ((header[0] >> 7) & 1) != 0
|
||||
for i := 0; i < 3; i++ {
|
||||
j := uint(6 - i)
|
||||
hybiFrame.header.Rsv[i] = ((header[0] >> j) & 1) != 0
|
||||
}
|
||||
hybiFrame.header.OpCode = header[0] & 0x0f
|
||||
|
||||
// Second byte. Mask/Payload len(7bits)
|
||||
b, err = buf.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
header = append(header, b)
|
||||
mask := (b & 0x80) != 0
|
||||
b &= 0x7f
|
||||
lengthFields := 0
|
||||
switch {
|
||||
case b <= 125: // Payload length 7bits.
|
||||
hybiFrame.header.Length = int64(b)
|
||||
case b == 126: // Payload length 7+16bits
|
||||
lengthFields = 2
|
||||
case b == 127: // Payload length 7+64bits
|
||||
lengthFields = 8
|
||||
}
|
||||
for i := 0; i < lengthFields; i++ {
|
||||
b, err = buf.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if lengthFields == 8 && i == 0 { // MSB must be zero when 7+64 bits
|
||||
b &= 0x7f
|
||||
}
|
||||
header = append(header, b)
|
||||
hybiFrame.header.Length = hybiFrame.header.Length*256 + int64(b)
|
||||
}
|
||||
if mask {
|
||||
// Masking key. 4 bytes.
|
||||
for i := 0; i < 4; i++ {
|
||||
b, err = buf.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
header = append(header, b)
|
||||
hybiFrame.header.MaskingKey = append(hybiFrame.header.MaskingKey, b)
|
||||
}
|
||||
}
|
||||
hybiFrame.reader = io.LimitReader(buf.Reader, hybiFrame.header.Length)
|
||||
hybiFrame.header.data = bytes.NewBuffer(header)
|
||||
hybiFrame.length = len(header) + int(hybiFrame.header.Length)
|
||||
return
|
||||
}
|
||||
|
||||
// A HybiFrameWriter is a writer for hybi frame.
|
||||
type hybiFrameWriter struct {
|
||||
writer *bufio.Writer
|
||||
|
||||
header *hybiFrameHeader
|
||||
}
|
||||
|
||||
func (frame *hybiFrameWriter) Write(msg []byte) (n int, err error) {
|
||||
var header []byte
|
||||
var b byte
|
||||
if frame.header.Fin {
|
||||
b |= 0x80
|
||||
}
|
||||
for i := 0; i < 3; i++ {
|
||||
if frame.header.Rsv[i] {
|
||||
j := uint(6 - i)
|
||||
b |= 1 << j
|
||||
}
|
||||
}
|
||||
b |= frame.header.OpCode
|
||||
header = append(header, b)
|
||||
if frame.header.MaskingKey != nil {
|
||||
b = 0x80
|
||||
} else {
|
||||
b = 0
|
||||
}
|
||||
lengthFields := 0
|
||||
length := len(msg)
|
||||
switch {
|
||||
case length <= 125:
|
||||
b |= byte(length)
|
||||
case length < 65536:
|
||||
b |= 126
|
||||
lengthFields = 2
|
||||
default:
|
||||
b |= 127
|
||||
lengthFields = 8
|
||||
}
|
||||
header = append(header, b)
|
||||
for i := 0; i < lengthFields; i++ {
|
||||
j := uint((lengthFields - i - 1) * 8)
|
||||
b = byte((length >> j) & 0xff)
|
||||
header = append(header, b)
|
||||
}
|
||||
if frame.header.MaskingKey != nil {
|
||||
if len(frame.header.MaskingKey) != 4 {
|
||||
return 0, ErrBadMaskingKey
|
||||
}
|
||||
header = append(header, frame.header.MaskingKey...)
|
||||
frame.writer.Write(header)
|
||||
data := make([]byte, length)
|
||||
for i := range data {
|
||||
data[i] = msg[i] ^ frame.header.MaskingKey[i%4]
|
||||
}
|
||||
frame.writer.Write(data)
|
||||
err = frame.writer.Flush()
|
||||
return length, err
|
||||
}
|
||||
frame.writer.Write(header)
|
||||
frame.writer.Write(msg)
|
||||
err = frame.writer.Flush()
|
||||
return length, err
|
||||
}
|
||||
|
||||
func (frame *hybiFrameWriter) Close() error { return nil }
|
||||
|
||||
type hybiFrameWriterFactory struct {
|
||||
*bufio.Writer
|
||||
needMaskingKey bool
|
||||
}
|
||||
|
||||
func (buf hybiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err error) {
|
||||
frameHeader := &hybiFrameHeader{Fin: true, OpCode: payloadType}
|
||||
if buf.needMaskingKey {
|
||||
frameHeader.MaskingKey, err = generateMaskingKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &hybiFrameWriter{writer: buf.Writer, header: frameHeader}, nil
|
||||
}
|
||||
|
||||
type hybiFrameHandler struct {
|
||||
conn *Conn
|
||||
payloadType byte
|
||||
}
|
||||
|
||||
func (handler *hybiFrameHandler) HandleFrame(frame frameReader) (frameReader, error) {
|
||||
if handler.conn.IsServerConn() {
|
||||
// The client MUST mask all frames sent to the server.
|
||||
if frame.(*hybiFrameReader).header.MaskingKey == nil {
|
||||
handler.WriteClose(closeStatusProtocolError)
|
||||
return nil, io.EOF
|
||||
}
|
||||
} else {
|
||||
// The server MUST NOT mask all frames.
|
||||
if frame.(*hybiFrameReader).header.MaskingKey != nil {
|
||||
handler.WriteClose(closeStatusProtocolError)
|
||||
return nil, io.EOF
|
||||
}
|
||||
}
|
||||
if header := frame.HeaderReader(); header != nil {
|
||||
io.Copy(ioutil.Discard, header)
|
||||
}
|
||||
switch frame.PayloadType() {
|
||||
case ContinuationFrame:
|
||||
frame.(*hybiFrameReader).header.OpCode = handler.payloadType
|
||||
case TextFrame, BinaryFrame:
|
||||
handler.payloadType = frame.PayloadType()
|
||||
case CloseFrame:
|
||||
return nil, io.EOF
|
||||
case PingFrame, PongFrame:
|
||||
b := make([]byte, maxControlFramePayloadLength)
|
||||
n, err := io.ReadFull(frame, b)
|
||||
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
|
||||
return nil, err
|
||||
}
|
||||
io.Copy(ioutil.Discard, frame)
|
||||
if frame.PayloadType() == PingFrame {
|
||||
if _, err := handler.WritePong(b[:n]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
return frame, nil
|
||||
}
|
||||
|
||||
func (handler *hybiFrameHandler) WriteClose(status int) (err error) {
|
||||
handler.conn.wio.Lock()
|
||||
defer handler.conn.wio.Unlock()
|
||||
w, err := handler.conn.frameWriterFactory.NewFrameWriter(CloseFrame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
msg := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(msg, uint16(status))
|
||||
_, err = w.Write(msg)
|
||||
w.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
func (handler *hybiFrameHandler) WritePong(msg []byte) (n int, err error) {
|
||||
handler.conn.wio.Lock()
|
||||
defer handler.conn.wio.Unlock()
|
||||
w, err := handler.conn.frameWriterFactory.NewFrameWriter(PongFrame)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n, err = w.Write(msg)
|
||||
w.Close()
|
||||
return n, err
|
||||
}
|
||||
|
||||
// newHybiConn creates a new WebSocket connection speaking hybi draft protocol.
|
||||
func newHybiConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
|
||||
if buf == nil {
|
||||
br := bufio.NewReader(rwc)
|
||||
bw := bufio.NewWriter(rwc)
|
||||
buf = bufio.NewReadWriter(br, bw)
|
||||
}
|
||||
ws := &Conn{config: config, request: request, buf: buf, rwc: rwc,
|
||||
frameReaderFactory: hybiFrameReaderFactory{buf.Reader},
|
||||
frameWriterFactory: hybiFrameWriterFactory{
|
||||
buf.Writer, request == nil},
|
||||
PayloadType: TextFrame,
|
||||
defaultCloseStatus: closeStatusNormal}
|
||||
ws.frameHandler = &hybiFrameHandler{conn: ws}
|
||||
return ws
|
||||
}
|
||||
|
||||
// generateMaskingKey generates a masking key for a frame.
|
||||
func generateMaskingKey() (maskingKey []byte, err error) {
|
||||
maskingKey = make([]byte, 4)
|
||||
if _, err = io.ReadFull(rand.Reader, maskingKey); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// generateNonce generates a nonce consisting of a randomly selected 16-byte
|
||||
// value that has been base64-encoded.
|
||||
func generateNonce() (nonce []byte) {
|
||||
key := make([]byte, 16)
|
||||
if _, err := io.ReadFull(rand.Reader, key); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
nonce = make([]byte, 24)
|
||||
base64.StdEncoding.Encode(nonce, key)
|
||||
return
|
||||
}
|
||||
|
||||
// removeZone removes IPv6 zone identifer from host.
|
||||
// E.g., "[fe80::1%en0]:8080" to "[fe80::1]:8080"
|
||||
func removeZone(host string) string {
|
||||
if !strings.HasPrefix(host, "[") {
|
||||
return host
|
||||
}
|
||||
i := strings.LastIndex(host, "]")
|
||||
if i < 0 {
|
||||
return host
|
||||
}
|
||||
j := strings.LastIndex(host[:i], "%")
|
||||
if j < 0 {
|
||||
return host
|
||||
}
|
||||
return host[:j] + host[i:]
|
||||
}
|
||||
|
||||
// getNonceAccept computes the base64-encoded SHA-1 of the concatenation of
|
||||
// the nonce ("Sec-WebSocket-Key" value) with the websocket GUID string.
|
||||
func getNonceAccept(nonce []byte) (expected []byte, err error) {
|
||||
h := sha1.New()
|
||||
if _, err = h.Write(nonce); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = h.Write([]byte(websocketGUID)); err != nil {
|
||||
return
|
||||
}
|
||||
expected = make([]byte, 28)
|
||||
base64.StdEncoding.Encode(expected, h.Sum(nil))
|
||||
return
|
||||
}
|
||||
|
||||
// Client handshake described in draft-ietf-hybi-thewebsocket-protocol-17
|
||||
func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) {
|
||||
bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n")
|
||||
|
||||
// According to RFC 6874, an HTTP client, proxy, or other
|
||||
// intermediary must remove any IPv6 zone identifier attached
|
||||
// to an outgoing URI.
|
||||
bw.WriteString("Host: " + removeZone(config.Location.Host) + "\r\n")
|
||||
bw.WriteString("Upgrade: websocket\r\n")
|
||||
bw.WriteString("Connection: Upgrade\r\n")
|
||||
nonce := generateNonce()
|
||||
if config.handshakeData != nil {
|
||||
nonce = []byte(config.handshakeData["key"])
|
||||
}
|
||||
bw.WriteString("Sec-WebSocket-Key: " + string(nonce) + "\r\n")
|
||||
bw.WriteString("Origin: " + strings.ToLower(config.Origin.String()) + "\r\n")
|
||||
|
||||
if config.Version != ProtocolVersionHybi13 {
|
||||
return ErrBadProtocolVersion
|
||||
}
|
||||
|
||||
bw.WriteString("Sec-WebSocket-Version: " + fmt.Sprintf("%d", config.Version) + "\r\n")
|
||||
if len(config.Protocol) > 0 {
|
||||
bw.WriteString("Sec-WebSocket-Protocol: " + strings.Join(config.Protocol, ", ") + "\r\n")
|
||||
}
|
||||
// TODO(ukai): send Sec-WebSocket-Extensions.
|
||||
err = config.Header.WriteSubset(bw, handshakeHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bw.WriteString("\r\n")
|
||||
if err = bw.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(br, &http.Request{Method: "GET"})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode != 101 {
|
||||
return ErrBadStatus
|
||||
}
|
||||
if strings.ToLower(resp.Header.Get("Upgrade")) != "websocket" ||
|
||||
strings.ToLower(resp.Header.Get("Connection")) != "upgrade" {
|
||||
return ErrBadUpgrade
|
||||
}
|
||||
expectedAccept, err := getNonceAccept(nonce)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.Header.Get("Sec-WebSocket-Accept") != string(expectedAccept) {
|
||||
return ErrChallengeResponse
|
||||
}
|
||||
if resp.Header.Get("Sec-WebSocket-Extensions") != "" {
|
||||
return ErrUnsupportedExtensions
|
||||
}
|
||||
offeredProtocol := resp.Header.Get("Sec-WebSocket-Protocol")
|
||||
if offeredProtocol != "" {
|
||||
protocolMatched := false
|
||||
for i := 0; i < len(config.Protocol); i++ {
|
||||
if config.Protocol[i] == offeredProtocol {
|
||||
protocolMatched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !protocolMatched {
|
||||
return ErrBadWebSocketProtocol
|
||||
}
|
||||
config.Protocol = []string{offeredProtocol}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// newHybiClientConn creates a client WebSocket connection after handshake.
|
||||
func newHybiClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn {
|
||||
return newHybiConn(config, buf, rwc, nil)
|
||||
}
|
||||
|
||||
// A HybiServerHandshaker performs a server handshake using hybi draft protocol.
|
||||
type hybiServerHandshaker struct {
|
||||
*Config
|
||||
accept []byte
|
||||
}
|
||||
|
||||
func (c *hybiServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) {
|
||||
c.Version = ProtocolVersionHybi13
|
||||
if req.Method != "GET" {
|
||||
return http.StatusMethodNotAllowed, ErrBadRequestMethod
|
||||
}
|
||||
// HTTP version can be safely ignored.
|
||||
|
||||
if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" ||
|
||||
!strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") {
|
||||
return http.StatusBadRequest, ErrNotWebSocket
|
||||
}
|
||||
|
||||
key := req.Header.Get("Sec-Websocket-Key")
|
||||
if key == "" {
|
||||
return http.StatusBadRequest, ErrChallengeResponse
|
||||
}
|
||||
version := req.Header.Get("Sec-Websocket-Version")
|
||||
switch version {
|
||||
case "13":
|
||||
c.Version = ProtocolVersionHybi13
|
||||
default:
|
||||
return http.StatusBadRequest, ErrBadWebSocketVersion
|
||||
}
|
||||
var scheme string
|
||||
if req.TLS != nil {
|
||||
scheme = "wss"
|
||||
} else {
|
||||
scheme = "ws"
|
||||
}
|
||||
c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI())
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, err
|
||||
}
|
||||
protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol"))
|
||||
if protocol != "" {
|
||||
protocols := strings.Split(protocol, ",")
|
||||
for i := 0; i < len(protocols); i++ {
|
||||
c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i]))
|
||||
}
|
||||
}
|
||||
c.accept, err = getNonceAccept([]byte(key))
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
return http.StatusSwitchingProtocols, nil
|
||||
}
|
||||
|
||||
// Origin parses the Origin header in req.
|
||||
// If the Origin header is not set, it returns nil and nil.
|
||||
func Origin(config *Config, req *http.Request) (*url.URL, error) {
|
||||
var origin string
|
||||
switch config.Version {
|
||||
case ProtocolVersionHybi13:
|
||||
origin = req.Header.Get("Origin")
|
||||
}
|
||||
if origin == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return url.ParseRequestURI(origin)
|
||||
}
|
||||
|
||||
func (c *hybiServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) {
|
||||
if len(c.Protocol) > 0 {
|
||||
if len(c.Protocol) != 1 {
|
||||
// You need choose a Protocol in Handshake func in Server.
|
||||
return ErrBadWebSocketProtocol
|
||||
}
|
||||
}
|
||||
buf.WriteString("HTTP/1.1 101 Switching Protocols\r\n")
|
||||
buf.WriteString("Upgrade: websocket\r\n")
|
||||
buf.WriteString("Connection: Upgrade\r\n")
|
||||
buf.WriteString("Sec-WebSocket-Accept: " + string(c.accept) + "\r\n")
|
||||
if len(c.Protocol) > 0 {
|
||||
buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n")
|
||||
}
|
||||
// TODO(ukai): send Sec-WebSocket-Extensions.
|
||||
if c.Header != nil {
|
||||
err := c.Header.WriteSubset(buf, handshakeHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
buf.WriteString("\r\n")
|
||||
return buf.Flush()
|
||||
}
|
||||
|
||||
func (c *hybiServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
|
||||
return newHybiServerConn(c.Config, buf, rwc, request)
|
||||
}
|
||||
|
||||
// newHybiServerConn returns a new WebSocket connection speaking hybi draft protocol.
|
||||
func newHybiServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
|
||||
return newHybiConn(config, buf, rwc, request)
|
||||
}
|
113
vendor/golang.org/x/net/websocket/server.go
generated
vendored
113
vendor/golang.org/x/net/websocket/server.go
generated
vendored
@ -1,113 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func newServerConn(rwc io.ReadWriteCloser, buf *bufio.ReadWriter, req *http.Request, config *Config, handshake func(*Config, *http.Request) error) (conn *Conn, err error) {
|
||||
var hs serverHandshaker = &hybiServerHandshaker{Config: config}
|
||||
code, err := hs.ReadHandshake(buf.Reader, req)
|
||||
if err == ErrBadWebSocketVersion {
|
||||
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
|
||||
fmt.Fprintf(buf, "Sec-WebSocket-Version: %s\r\n", SupportedProtocolVersion)
|
||||
buf.WriteString("\r\n")
|
||||
buf.WriteString(err.Error())
|
||||
buf.Flush()
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
|
||||
buf.WriteString("\r\n")
|
||||
buf.WriteString(err.Error())
|
||||
buf.Flush()
|
||||
return
|
||||
}
|
||||
if handshake != nil {
|
||||
err = handshake(config, req)
|
||||
if err != nil {
|
||||
code = http.StatusForbidden
|
||||
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
|
||||
buf.WriteString("\r\n")
|
||||
buf.Flush()
|
||||
return
|
||||
}
|
||||
}
|
||||
err = hs.AcceptHandshake(buf.Writer)
|
||||
if err != nil {
|
||||
code = http.StatusBadRequest
|
||||
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
|
||||
buf.WriteString("\r\n")
|
||||
buf.Flush()
|
||||
return
|
||||
}
|
||||
conn = hs.NewServerConn(buf, rwc, req)
|
||||
return
|
||||
}
|
||||
|
||||
// Server represents a server of a WebSocket.
|
||||
type Server struct {
|
||||
// Config is a WebSocket configuration for new WebSocket connection.
|
||||
Config
|
||||
|
||||
// Handshake is an optional function in WebSocket handshake.
|
||||
// For example, you can check, or don't check Origin header.
|
||||
// Another example, you can select config.Protocol.
|
||||
Handshake func(*Config, *http.Request) error
|
||||
|
||||
// Handler handles a WebSocket connection.
|
||||
Handler
|
||||
}
|
||||
|
||||
// ServeHTTP implements the http.Handler interface for a WebSocket
|
||||
func (s Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
s.serveWebSocket(w, req)
|
||||
}
|
||||
|
||||
func (s Server) serveWebSocket(w http.ResponseWriter, req *http.Request) {
|
||||
rwc, buf, err := w.(http.Hijacker).Hijack()
|
||||
if err != nil {
|
||||
panic("Hijack failed: " + err.Error())
|
||||
}
|
||||
// The server should abort the WebSocket connection if it finds
|
||||
// the client did not send a handshake that matches with protocol
|
||||
// specification.
|
||||
defer rwc.Close()
|
||||
conn, err := newServerConn(rwc, buf, req, &s.Config, s.Handshake)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if conn == nil {
|
||||
panic("unexpected nil conn")
|
||||
}
|
||||
s.Handler(conn)
|
||||
}
|
||||
|
||||
// Handler is a simple interface to a WebSocket browser client.
|
||||
// It checks if Origin header is valid URL by default.
|
||||
// You might want to verify websocket.Conn.Config().Origin in the func.
|
||||
// If you use Server instead of Handler, you could call websocket.Origin and
|
||||
// check the origin in your Handshake func. So, if you want to accept
|
||||
// non-browser clients, which do not send an Origin header, set a
|
||||
// Server.Handshake that does not check the origin.
|
||||
type Handler func(*Conn)
|
||||
|
||||
func checkOrigin(config *Config, req *http.Request) (err error) {
|
||||
config.Origin, err = Origin(config, req)
|
||||
if err == nil && config.Origin == nil {
|
||||
return fmt.Errorf("null origin")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ServeHTTP implements the http.Handler interface for a WebSocket
|
||||
func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
s := Server{Handler: h, Handshake: checkOrigin}
|
||||
s.serveWebSocket(w, req)
|
||||
}
|
448
vendor/golang.org/x/net/websocket/websocket.go
generated
vendored
448
vendor/golang.org/x/net/websocket/websocket.go
generated
vendored
@ -1,448 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package websocket implements a client and server for the WebSocket protocol
|
||||
// as specified in RFC 6455.
|
||||
//
|
||||
// This package currently lacks some features found in an alternative
|
||||
// and more actively maintained WebSocket package:
|
||||
//
|
||||
// https://godoc.org/github.com/gorilla/websocket
|
||||
//
|
||||
package websocket // import "golang.org/x/net/websocket"
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
ProtocolVersionHybi13 = 13
|
||||
ProtocolVersionHybi = ProtocolVersionHybi13
|
||||
SupportedProtocolVersion = "13"
|
||||
|
||||
ContinuationFrame = 0
|
||||
TextFrame = 1
|
||||
BinaryFrame = 2
|
||||
CloseFrame = 8
|
||||
PingFrame = 9
|
||||
PongFrame = 10
|
||||
UnknownFrame = 255
|
||||
|
||||
DefaultMaxPayloadBytes = 32 << 20 // 32MB
|
||||
)
|
||||
|
||||
// ProtocolError represents WebSocket protocol errors.
|
||||
type ProtocolError struct {
|
||||
ErrorString string
|
||||
}
|
||||
|
||||
func (err *ProtocolError) Error() string { return err.ErrorString }
|
||||
|
||||
var (
|
||||
ErrBadProtocolVersion = &ProtocolError{"bad protocol version"}
|
||||
ErrBadScheme = &ProtocolError{"bad scheme"}
|
||||
ErrBadStatus = &ProtocolError{"bad status"}
|
||||
ErrBadUpgrade = &ProtocolError{"missing or bad upgrade"}
|
||||
ErrBadWebSocketOrigin = &ProtocolError{"missing or bad WebSocket-Origin"}
|
||||
ErrBadWebSocketLocation = &ProtocolError{"missing or bad WebSocket-Location"}
|
||||
ErrBadWebSocketProtocol = &ProtocolError{"missing or bad WebSocket-Protocol"}
|
||||
ErrBadWebSocketVersion = &ProtocolError{"missing or bad WebSocket Version"}
|
||||
ErrChallengeResponse = &ProtocolError{"mismatch challenge/response"}
|
||||
ErrBadFrame = &ProtocolError{"bad frame"}
|
||||
ErrBadFrameBoundary = &ProtocolError{"not on frame boundary"}
|
||||
ErrNotWebSocket = &ProtocolError{"not websocket protocol"}
|
||||
ErrBadRequestMethod = &ProtocolError{"bad method"}
|
||||
ErrNotSupported = &ProtocolError{"not supported"}
|
||||
)
|
||||
|
||||
// ErrFrameTooLarge is returned by Codec's Receive method if payload size
|
||||
// exceeds limit set by Conn.MaxPayloadBytes
|
||||
var ErrFrameTooLarge = errors.New("websocket: frame payload size exceeds limit")
|
||||
|
||||
// Addr is an implementation of net.Addr for WebSocket.
|
||||
type Addr struct {
|
||||
*url.URL
|
||||
}
|
||||
|
||||
// Network returns the network type for a WebSocket, "websocket".
|
||||
func (addr *Addr) Network() string { return "websocket" }
|
||||
|
||||
// Config is a WebSocket configuration
|
||||
type Config struct {
|
||||
// A WebSocket server address.
|
||||
Location *url.URL
|
||||
|
||||
// A Websocket client origin.
|
||||
Origin *url.URL
|
||||
|
||||
// WebSocket subprotocols.
|
||||
Protocol []string
|
||||
|
||||
// WebSocket protocol version.
|
||||
Version int
|
||||
|
||||
// TLS config for secure WebSocket (wss).
|
||||
TlsConfig *tls.Config
|
||||
|
||||
// Additional header fields to be sent in WebSocket opening handshake.
|
||||
Header http.Header
|
||||
|
||||
// Dialer used when opening websocket connections.
|
||||
Dialer *net.Dialer
|
||||
|
||||
handshakeData map[string]string
|
||||
}
|
||||
|
||||
// serverHandshaker is an interface to handle WebSocket server side handshake.
|
||||
type serverHandshaker interface {
|
||||
// ReadHandshake reads handshake request message from client.
|
||||
// Returns http response code and error if any.
|
||||
ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error)
|
||||
|
||||
// AcceptHandshake accepts the client handshake request and sends
|
||||
// handshake response back to client.
|
||||
AcceptHandshake(buf *bufio.Writer) (err error)
|
||||
|
||||
// NewServerConn creates a new WebSocket connection.
|
||||
NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn)
|
||||
}
|
||||
|
||||
// frameReader is an interface to read a WebSocket frame.
|
||||
type frameReader interface {
|
||||
// Reader is to read payload of the frame.
|
||||
io.Reader
|
||||
|
||||
// PayloadType returns payload type.
|
||||
PayloadType() byte
|
||||
|
||||
// HeaderReader returns a reader to read header of the frame.
|
||||
HeaderReader() io.Reader
|
||||
|
||||
// TrailerReader returns a reader to read trailer of the frame.
|
||||
// If it returns nil, there is no trailer in the frame.
|
||||
TrailerReader() io.Reader
|
||||
|
||||
// Len returns total length of the frame, including header and trailer.
|
||||
Len() int
|
||||
}
|
||||
|
||||
// frameReaderFactory is an interface to creates new frame reader.
|
||||
type frameReaderFactory interface {
|
||||
NewFrameReader() (r frameReader, err error)
|
||||
}
|
||||
|
||||
// frameWriter is an interface to write a WebSocket frame.
|
||||
type frameWriter interface {
|
||||
// Writer is to write payload of the frame.
|
||||
io.WriteCloser
|
||||
}
|
||||
|
||||
// frameWriterFactory is an interface to create new frame writer.
|
||||
type frameWriterFactory interface {
|
||||
NewFrameWriter(payloadType byte) (w frameWriter, err error)
|
||||
}
|
||||
|
||||
type frameHandler interface {
|
||||
HandleFrame(frame frameReader) (r frameReader, err error)
|
||||
WriteClose(status int) (err error)
|
||||
}
|
||||
|
||||
// Conn represents a WebSocket connection.
|
||||
//
|
||||
// Multiple goroutines may invoke methods on a Conn simultaneously.
|
||||
type Conn struct {
|
||||
config *Config
|
||||
request *http.Request
|
||||
|
||||
buf *bufio.ReadWriter
|
||||
rwc io.ReadWriteCloser
|
||||
|
||||
rio sync.Mutex
|
||||
frameReaderFactory
|
||||
frameReader
|
||||
|
||||
wio sync.Mutex
|
||||
frameWriterFactory
|
||||
|
||||
frameHandler
|
||||
PayloadType byte
|
||||
defaultCloseStatus int
|
||||
|
||||
// MaxPayloadBytes limits the size of frame payload received over Conn
|
||||
// by Codec's Receive method. If zero, DefaultMaxPayloadBytes is used.
|
||||
MaxPayloadBytes int
|
||||
}
|
||||
|
||||
// Read implements the io.Reader interface:
|
||||
// it reads data of a frame from the WebSocket connection.
|
||||
// if msg is not large enough for the frame data, it fills the msg and next Read
|
||||
// will read the rest of the frame data.
|
||||
// it reads Text frame or Binary frame.
|
||||
func (ws *Conn) Read(msg []byte) (n int, err error) {
|
||||
ws.rio.Lock()
|
||||
defer ws.rio.Unlock()
|
||||
again:
|
||||
if ws.frameReader == nil {
|
||||
frame, err := ws.frameReaderFactory.NewFrameReader()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
ws.frameReader, err = ws.frameHandler.HandleFrame(frame)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if ws.frameReader == nil {
|
||||
goto again
|
||||
}
|
||||
}
|
||||
n, err = ws.frameReader.Read(msg)
|
||||
if err == io.EOF {
|
||||
if trailer := ws.frameReader.TrailerReader(); trailer != nil {
|
||||
io.Copy(ioutil.Discard, trailer)
|
||||
}
|
||||
ws.frameReader = nil
|
||||
goto again
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Write implements the io.Writer interface:
|
||||
// it writes data as a frame to the WebSocket connection.
|
||||
func (ws *Conn) Write(msg []byte) (n int, err error) {
|
||||
ws.wio.Lock()
|
||||
defer ws.wio.Unlock()
|
||||
w, err := ws.frameWriterFactory.NewFrameWriter(ws.PayloadType)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n, err = w.Write(msg)
|
||||
w.Close()
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Close implements the io.Closer interface.
|
||||
func (ws *Conn) Close() error {
|
||||
err := ws.frameHandler.WriteClose(ws.defaultCloseStatus)
|
||||
err1 := ws.rwc.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err1
|
||||
}
|
||||
|
||||
func (ws *Conn) IsClientConn() bool { return ws.request == nil }
|
||||
func (ws *Conn) IsServerConn() bool { return ws.request != nil }
|
||||
|
||||
// LocalAddr returns the WebSocket Origin for the connection for client, or
|
||||
// the WebSocket location for server.
|
||||
func (ws *Conn) LocalAddr() net.Addr {
|
||||
if ws.IsClientConn() {
|
||||
return &Addr{ws.config.Origin}
|
||||
}
|
||||
return &Addr{ws.config.Location}
|
||||
}
|
||||
|
||||
// RemoteAddr returns the WebSocket location for the connection for client, or
|
||||
// the Websocket Origin for server.
|
||||
func (ws *Conn) RemoteAddr() net.Addr {
|
||||
if ws.IsClientConn() {
|
||||
return &Addr{ws.config.Location}
|
||||
}
|
||||
return &Addr{ws.config.Origin}
|
||||
}
|
||||
|
||||
var errSetDeadline = errors.New("websocket: cannot set deadline: not using a net.Conn")
|
||||
|
||||
// SetDeadline sets the connection's network read & write deadlines.
|
||||
func (ws *Conn) SetDeadline(t time.Time) error {
|
||||
if conn, ok := ws.rwc.(net.Conn); ok {
|
||||
return conn.SetDeadline(t)
|
||||
}
|
||||
return errSetDeadline
|
||||
}
|
||||
|
||||
// SetReadDeadline sets the connection's network read deadline.
|
||||
func (ws *Conn) SetReadDeadline(t time.Time) error {
|
||||
if conn, ok := ws.rwc.(net.Conn); ok {
|
||||
return conn.SetReadDeadline(t)
|
||||
}
|
||||
return errSetDeadline
|
||||
}
|
||||
|
||||
// SetWriteDeadline sets the connection's network write deadline.
|
||||
func (ws *Conn) SetWriteDeadline(t time.Time) error {
|
||||
if conn, ok := ws.rwc.(net.Conn); ok {
|
||||
return conn.SetWriteDeadline(t)
|
||||
}
|
||||
return errSetDeadline
|
||||
}
|
||||
|
||||
// Config returns the WebSocket config.
|
||||
func (ws *Conn) Config() *Config { return ws.config }
|
||||
|
||||
// Request returns the http request upgraded to the WebSocket.
|
||||
// It is nil for client side.
|
||||
func (ws *Conn) Request() *http.Request { return ws.request }
|
||||
|
||||
// Codec represents a symmetric pair of functions that implement a codec.
|
||||
type Codec struct {
|
||||
Marshal func(v interface{}) (data []byte, payloadType byte, err error)
|
||||
Unmarshal func(data []byte, payloadType byte, v interface{}) (err error)
|
||||
}
|
||||
|
||||
// Send sends v marshaled by cd.Marshal as single frame to ws.
|
||||
func (cd Codec) Send(ws *Conn, v interface{}) (err error) {
|
||||
data, payloadType, err := cd.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ws.wio.Lock()
|
||||
defer ws.wio.Unlock()
|
||||
w, err := ws.frameWriterFactory.NewFrameWriter(payloadType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(data)
|
||||
w.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
// Receive receives single frame from ws, unmarshaled by cd.Unmarshal and stores
|
||||
// in v. The whole frame payload is read to an in-memory buffer; max size of
|
||||
// payload is defined by ws.MaxPayloadBytes. If frame payload size exceeds
|
||||
// limit, ErrFrameTooLarge is returned; in this case frame is not read off wire
|
||||
// completely. The next call to Receive would read and discard leftover data of
|
||||
// previous oversized frame before processing next frame.
|
||||
func (cd Codec) Receive(ws *Conn, v interface{}) (err error) {
|
||||
ws.rio.Lock()
|
||||
defer ws.rio.Unlock()
|
||||
if ws.frameReader != nil {
|
||||
_, err = io.Copy(ioutil.Discard, ws.frameReader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ws.frameReader = nil
|
||||
}
|
||||
again:
|
||||
frame, err := ws.frameReaderFactory.NewFrameReader()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
frame, err = ws.frameHandler.HandleFrame(frame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if frame == nil {
|
||||
goto again
|
||||
}
|
||||
maxPayloadBytes := ws.MaxPayloadBytes
|
||||
if maxPayloadBytes == 0 {
|
||||
maxPayloadBytes = DefaultMaxPayloadBytes
|
||||
}
|
||||
if hf, ok := frame.(*hybiFrameReader); ok && hf.header.Length > int64(maxPayloadBytes) {
|
||||
// payload size exceeds limit, no need to call Unmarshal
|
||||
//
|
||||
// set frameReader to current oversized frame so that
|
||||
// the next call to this function can drain leftover
|
||||
// data before processing the next frame
|
||||
ws.frameReader = frame
|
||||
return ErrFrameTooLarge
|
||||
}
|
||||
payloadType := frame.PayloadType()
|
||||
data, err := ioutil.ReadAll(frame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cd.Unmarshal(data, payloadType, v)
|
||||
}
|
||||
|
||||
func marshal(v interface{}) (msg []byte, payloadType byte, err error) {
|
||||
switch data := v.(type) {
|
||||
case string:
|
||||
return []byte(data), TextFrame, nil
|
||||
case []byte:
|
||||
return data, BinaryFrame, nil
|
||||
}
|
||||
return nil, UnknownFrame, ErrNotSupported
|
||||
}
|
||||
|
||||
func unmarshal(msg []byte, payloadType byte, v interface{}) (err error) {
|
||||
switch data := v.(type) {
|
||||
case *string:
|
||||
*data = string(msg)
|
||||
return nil
|
||||
case *[]byte:
|
||||
*data = msg
|
||||
return nil
|
||||
}
|
||||
return ErrNotSupported
|
||||
}
|
||||
|
||||
/*
|
||||
Message is a codec to send/receive text/binary data in a frame on WebSocket connection.
|
||||
To send/receive text frame, use string type.
|
||||
To send/receive binary frame, use []byte type.
|
||||
|
||||
Trivial usage:
|
||||
|
||||
import "websocket"
|
||||
|
||||
// receive text frame
|
||||
var message string
|
||||
websocket.Message.Receive(ws, &message)
|
||||
|
||||
// send text frame
|
||||
message = "hello"
|
||||
websocket.Message.Send(ws, message)
|
||||
|
||||
// receive binary frame
|
||||
var data []byte
|
||||
websocket.Message.Receive(ws, &data)
|
||||
|
||||
// send binary frame
|
||||
data = []byte{0, 1, 2}
|
||||
websocket.Message.Send(ws, data)
|
||||
|
||||
*/
|
||||
var Message = Codec{marshal, unmarshal}
|
||||
|
||||
func jsonMarshal(v interface{}) (msg []byte, payloadType byte, err error) {
|
||||
msg, err = json.Marshal(v)
|
||||
return msg, TextFrame, err
|
||||
}
|
||||
|
||||
func jsonUnmarshal(msg []byte, payloadType byte, v interface{}) (err error) {
|
||||
return json.Unmarshal(msg, v)
|
||||
}
|
||||
|
||||
/*
|
||||
JSON is a codec to send/receive JSON data in a frame from a WebSocket connection.
|
||||
|
||||
Trivial usage:
|
||||
|
||||
import "websocket"
|
||||
|
||||
type T struct {
|
||||
Msg string
|
||||
Count int
|
||||
}
|
||||
|
||||
// receive JSON type T
|
||||
var data T
|
||||
websocket.JSON.Receive(ws, &data)
|
||||
|
||||
// send JSON type T
|
||||
websocket.JSON.Send(ws, data)
|
||||
*/
|
||||
var JSON = Codec{jsonMarshal, jsonUnmarshal}
|
243
vendor/vendor.json
vendored
243
vendor/vendor.json
vendored
@ -1,243 +0,0 @@
|
||||
{
|
||||
"comment": "",
|
||||
"ignore": "test",
|
||||
"package": [
|
||||
{
|
||||
"path": "bufio",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "bytes",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "context",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "crypto/rand",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "crypto/sha1",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "crypto/tls",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "encoding/base64",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "encoding/binary",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "encoding/hex",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "encoding/json",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "errors",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "flag",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "fmt",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "CbpC2ha+GTTuROMyyLVd/L3O+8Y=",
|
||||
"path": "github.com/erroneousboat/termui",
|
||||
"revision": "80f245cdfa0488883a3e8602bf3f0c8a3c889a22",
|
||||
"revisionTime": "2017-09-23T11:51:41Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "zpFCi2nWiwR5F2INAJOvQqsj7lY=",
|
||||
"path": "github.com/maruel/panicparse/stack",
|
||||
"revision": "766956aceb8ff49664065ae50bef0ae8a0a83ec4",
|
||||
"revisionTime": "2017-11-29T15:16:18Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "cJE7dphDlam/i7PhnsyosNWtbd4=",
|
||||
"path": "github.com/mattn/go-runewidth",
|
||||
"revision": "97311d9f7767e3d6f422ea06661bc2c7a19e8a5d",
|
||||
"revisionTime": "2017-05-10T07:48:58Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "L3leymg2RT8hFl5uL+5KP/LpBkg=",
|
||||
"path": "github.com/mitchellh/go-wordwrap",
|
||||
"revision": "ad45545899c7b13c020ea92b2072220eefad42b8",
|
||||
"revisionTime": "2015-03-14T17:03:34Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "HYgTWn4FgVbvSBYVO4DxUPWfCz0=",
|
||||
"path": "github.com/nlopes/slack",
|
||||
"revision": "5cde21b8b96a43fc3435a1f514123d14fd7eabdc",
|
||||
"revisionTime": "2017-07-25T12:17:30Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Zi8hWUMkKtii1fc6YaGgoYAssIw=",
|
||||
"path": "github.com/nsf/termbox-go",
|
||||
"revision": "aa4a75b1c20a2b03751b1a9f7e41d58bd6f71c43",
|
||||
"revisionTime": "2017-11-04T16:23:16Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "DF3jZEw4lCq/SEaC7DIl/R+7S70=",
|
||||
"path": "github.com/renstrom/fuzzysearch/fuzzy",
|
||||
"revision": "2d205ac6ec17a839a94bdbfd16d2fa6c6dada2e0",
|
||||
"revisionTime": "2016-03-31T20:48:55Z"
|
||||
},
|
||||
{
|
||||
"path": "go/ast",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "go/parser",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "go/token",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "7EZyXN0EmZLgGxZxK01IJua4c8o=",
|
||||
"path": "golang.org/x/net/websocket",
|
||||
"revision": "a8b9294777976932365dabb6640cf1468d95c70f",
|
||||
"revisionTime": "2017-11-29T19:21:16Z"
|
||||
},
|
||||
{
|
||||
"path": "html",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "image",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "io",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "io/ioutil",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "log",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "math",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "math/rand",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "mime/multipart",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "net",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "net/http",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "net/http/httputil",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "net/url",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "os",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "os/signal",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "os/user",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "path",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "path/filepath",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "reflect",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "regexp",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "runtime",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "runtime/debug",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "sort",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "strconv",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "strings",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "sync",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "syscall",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "time",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "unicode",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "unicode/utf16",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "unicode/utf8",
|
||||
"revision": ""
|
||||
},
|
||||
{
|
||||
"path": "unsafe",
|
||||
"revision": ""
|
||||
}
|
||||
],
|
||||
"rootPath": "github.com/erroneousboat/slack-term"
|
||||
}
|
Loading…
Reference in New Issue
Block a user