Add Slack support
This commit is contained in:
		
							
								
								
									
										23
									
								
								vendor/github.com/nlopes/slack/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/nlopes/slack/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
Copyright (c) 2015, Norberto Lopes
 | 
			
		||||
All rights reserved.
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without modification,
 | 
			
		||||
are permitted provided that the following conditions are met:
 | 
			
		||||
 | 
			
		||||
1. Redistributions of source code must retain the above copyright notice, this
 | 
			
		||||
list of conditions and the following disclaimer.
 | 
			
		||||
 | 
			
		||||
2. Redistributions in binary form must reproduce the above copyright notice,
 | 
			
		||||
this list of conditions and the following disclaimer in the documentation and/or
 | 
			
		||||
other materials provided with the distribution.
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
							
								
								
									
										190
									
								
								vendor/github.com/nlopes/slack/admin.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								vendor/github.com/nlopes/slack/admin.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,190 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/url"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type adminResponse struct {
 | 
			
		||||
	OK    bool   `json:"ok"`
 | 
			
		||||
	Error string `json:"error"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func adminRequest(method string, teamName string, values url.Values, debug bool) (*adminResponse, error) {
 | 
			
		||||
	adminResponse := &adminResponse{}
 | 
			
		||||
	err := parseAdminResponse(method, teamName, values, adminResponse, debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !adminResponse.OK {
 | 
			
		||||
		return nil, errors.New(adminResponse.Error)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return adminResponse, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DisableUser disabled a user account, given a user ID
 | 
			
		||||
func (api *Client) DisableUser(teamName string, uid string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"user":       {uid},
 | 
			
		||||
		"token":      {api.config.token},
 | 
			
		||||
		"set_active": {"true"},
 | 
			
		||||
		"_attempts":  {"1"},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := adminRequest("setInactive", teamName, values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to disable user with id '%s': %s", uid, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InviteGuest invites a user to Slack as a single-channel guest
 | 
			
		||||
func (api *Client) InviteGuest(
 | 
			
		||||
	teamName string,
 | 
			
		||||
	channel string,
 | 
			
		||||
	firstName string,
 | 
			
		||||
	lastName string,
 | 
			
		||||
	emailAddress string,
 | 
			
		||||
) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"email":            {emailAddress},
 | 
			
		||||
		"channels":         {channel},
 | 
			
		||||
		"first_name":       {firstName},
 | 
			
		||||
		"last_name":        {lastName},
 | 
			
		||||
		"ultra_restricted": {"1"},
 | 
			
		||||
		"token":            {api.config.token},
 | 
			
		||||
		"set_active":       {"true"},
 | 
			
		||||
		"_attempts":        {"1"},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := adminRequest("invite", teamName, values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to invite single-channel guest: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InviteRestricted invites a user to Slack as a restricted account
 | 
			
		||||
func (api *Client) InviteRestricted(
 | 
			
		||||
	teamName string,
 | 
			
		||||
	channel string,
 | 
			
		||||
	firstName string,
 | 
			
		||||
	lastName string,
 | 
			
		||||
	emailAddress string,
 | 
			
		||||
) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"email":      {emailAddress},
 | 
			
		||||
		"channels":   {channel},
 | 
			
		||||
		"first_name": {firstName},
 | 
			
		||||
		"last_name":  {lastName},
 | 
			
		||||
		"restricted": {"1"},
 | 
			
		||||
		"token":      {api.config.token},
 | 
			
		||||
		"set_active": {"true"},
 | 
			
		||||
		"_attempts":  {"1"},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := adminRequest("invite", teamName, values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to restricted account: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InviteToTeam invites a user to a Slack team
 | 
			
		||||
func (api *Client) InviteToTeam(
 | 
			
		||||
	teamName string,
 | 
			
		||||
	firstName string,
 | 
			
		||||
	lastName string,
 | 
			
		||||
	emailAddress string,
 | 
			
		||||
) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"email":      {emailAddress},
 | 
			
		||||
		"first_name": {firstName},
 | 
			
		||||
		"last_name":  {lastName},
 | 
			
		||||
		"token":      {api.config.token},
 | 
			
		||||
		"set_active": {"true"},
 | 
			
		||||
		"_attempts":  {"1"},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := adminRequest("invite", teamName, values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to invite to team: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetRegular enables the specified user
 | 
			
		||||
func (api *Client) SetRegular(teamName string, user string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"user":       {user},
 | 
			
		||||
		"token":      {api.config.token},
 | 
			
		||||
		"set_active": {"true"},
 | 
			
		||||
		"_attempts":  {"1"},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := adminRequest("setRegular", teamName, values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to change the user (%s) to a regular user: %s", user, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendSSOBindingEmail sends an SSO binding email to the specified user
 | 
			
		||||
func (api *Client) SendSSOBindingEmail(teamName string, user string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"user":       {user},
 | 
			
		||||
		"token":      {api.config.token},
 | 
			
		||||
		"set_active": {"true"},
 | 
			
		||||
		"_attempts":  {"1"},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := adminRequest("sendSSOBind", teamName, values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to send SSO binding email for user (%s): %s", user, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetUltraRestricted converts a user into a single-channel guest
 | 
			
		||||
func (api *Client) SetUltraRestricted(teamName, uid, channel string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"user":       {uid},
 | 
			
		||||
		"channel":    {channel},
 | 
			
		||||
		"token":      {api.config.token},
 | 
			
		||||
		"set_active": {"true"},
 | 
			
		||||
		"_attempts":  {"1"},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := adminRequest("setUltraRestricted", teamName, values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to ultra-restrict account: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetRestricted converts a user into a restricted account
 | 
			
		||||
func (api *Client) SetRestricted(teamName, uid string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"user":       {uid},
 | 
			
		||||
		"token":      {api.config.token},
 | 
			
		||||
		"set_active": {"true"},
 | 
			
		||||
		"_attempts":  {"1"},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := adminRequest("setRestricted", teamName, values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to restrict account: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										76
									
								
								vendor/github.com/nlopes/slack/attachments.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								vendor/github.com/nlopes/slack/attachments.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
// AttachmentField contains information for an attachment field
 | 
			
		||||
// An Attachment can contain multiple of these
 | 
			
		||||
type AttachmentField struct {
 | 
			
		||||
	Title string `json:"title"`
 | 
			
		||||
	Value string `json:"value"`
 | 
			
		||||
	Short bool   `json:"short"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AttachmentAction is a button to be included in the attachment. Required when
 | 
			
		||||
// using message buttons and otherwise not useful. A maximum of 5 actions may be
 | 
			
		||||
// provided per attachment.
 | 
			
		||||
type AttachmentAction struct {
 | 
			
		||||
	Name    string              `json:"name"`              // Required.
 | 
			
		||||
	Text    string              `json:"text"`              // Required.
 | 
			
		||||
	Style   string              `json:"style,omitempty"`   // Optional. Allowed values: "default", "primary", "danger"
 | 
			
		||||
	Type    string              `json:"type"`              // Required. Must be set to "button"
 | 
			
		||||
	Value   string              `json:"value,omitempty"`   // Optional.
 | 
			
		||||
	Confirm []ConfirmationField `json:"confirm,omitempty"` // Optional.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AttachmentActionCallback is sent from Slack when a user clicks a button in an interactive message (aka AttachmentAction)
 | 
			
		||||
type AttachmentActionCallback struct {
 | 
			
		||||
	Actions    []AttachmentAction `json:"actions"`
 | 
			
		||||
	CallbackID string             `json:"callback_id"`
 | 
			
		||||
	Team       Team               `json:"team"`
 | 
			
		||||
	Channel    Channel            `json:"channel"`
 | 
			
		||||
	User       User               `json:"user"`
 | 
			
		||||
 | 
			
		||||
	OriginalMessage Message `json:"original_message"`
 | 
			
		||||
 | 
			
		||||
	ActionTs     string `json:"action_ts"`
 | 
			
		||||
	MessageTs    string `json:"message_ts"`
 | 
			
		||||
	AttachmentID string `json:"attachment_id"`
 | 
			
		||||
	Token        string `json:"token"`
 | 
			
		||||
	ResponseURL  string `json:"response_url"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConfirmationField are used to ask users to confirm actions
 | 
			
		||||
type ConfirmationField struct {
 | 
			
		||||
	Title       string `json:"title,omitempty"`        // Optional.
 | 
			
		||||
	Text        string `json:"text"`                   // Required.
 | 
			
		||||
	OkText      string `json:"ok_text,omitempty"`      // Optional. Defaults to "Okay"
 | 
			
		||||
	DismissText string `json:"dismiss_text,omitempty"` // Optional. Defaults to "Cancel"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Attachment contains all the information for an attachment
 | 
			
		||||
type Attachment struct {
 | 
			
		||||
	Color    string `json:"color,omitempty"`
 | 
			
		||||
	Fallback string `json:"fallback"`
 | 
			
		||||
 | 
			
		||||
	CallbackID string `json:"callback_id,omitempty"`
 | 
			
		||||
 | 
			
		||||
	AuthorName    string `json:"author_name,omitempty"`
 | 
			
		||||
	AuthorSubname string `json:"author_subname,omitempty"`
 | 
			
		||||
	AuthorLink    string `json:"author_link,omitempty"`
 | 
			
		||||
	AuthorIcon    string `json:"author_icon,omitempty"`
 | 
			
		||||
 | 
			
		||||
	Title     string `json:"title,omitempty"`
 | 
			
		||||
	TitleLink string `json:"title_link,omitempty"`
 | 
			
		||||
	Pretext   string `json:"pretext,omitempty"`
 | 
			
		||||
	Text      string `json:"text"`
 | 
			
		||||
 | 
			
		||||
	ImageURL string `json:"image_url,omitempty"`
 | 
			
		||||
	ThumbURL string `json:"thumb_url,omitempty"`
 | 
			
		||||
 | 
			
		||||
	Fields     []AttachmentField  `json:"fields,omitempty"`
 | 
			
		||||
	Actions    []AttachmentAction `json:"actions,omitempty"`
 | 
			
		||||
	MarkdownIn []string           `json:"mrkdwn_in,omitempty"`
 | 
			
		||||
 | 
			
		||||
	Footer     string `json:"footer,omitempty"`
 | 
			
		||||
	FooterIcon string `json:"footer_icon,omitempty"`
 | 
			
		||||
 | 
			
		||||
	Ts int64 `json:"ts,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										57
									
								
								vendor/github.com/nlopes/slack/backoff.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								vendor/github.com/nlopes/slack/backoff.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"math"
 | 
			
		||||
	"math/rand"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// This one was ripped from https://github.com/jpillora/backoff/blob/master/backoff.go
 | 
			
		||||
 | 
			
		||||
// Backoff is a time.Duration counter. It starts at Min.  After every
 | 
			
		||||
// call to Duration() it is multiplied by Factor.  It is capped at
 | 
			
		||||
// Max. It returns to Min on every call to Reset().  Used in
 | 
			
		||||
// conjunction with the time package.
 | 
			
		||||
type backoff struct {
 | 
			
		||||
	attempts int
 | 
			
		||||
	//Factor is the multiplying factor for each increment step
 | 
			
		||||
	Factor float64
 | 
			
		||||
	//Jitter eases contention by randomizing backoff steps
 | 
			
		||||
	Jitter bool
 | 
			
		||||
	//Min and Max are the minimum and maximum values of the counter
 | 
			
		||||
	Min, Max time.Duration
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns the current value of the counter and then multiplies it
 | 
			
		||||
// Factor
 | 
			
		||||
func (b *backoff) Duration() time.Duration {
 | 
			
		||||
	//Zero-values are nonsensical, so we use
 | 
			
		||||
	//them to apply defaults
 | 
			
		||||
	if b.Min == 0 {
 | 
			
		||||
		b.Min = 100 * time.Millisecond
 | 
			
		||||
	}
 | 
			
		||||
	if b.Max == 0 {
 | 
			
		||||
		b.Max = 10 * time.Second
 | 
			
		||||
	}
 | 
			
		||||
	if b.Factor == 0 {
 | 
			
		||||
		b.Factor = 2
 | 
			
		||||
	}
 | 
			
		||||
	//calculate this duration
 | 
			
		||||
	dur := float64(b.Min) * math.Pow(b.Factor, float64(b.attempts))
 | 
			
		||||
	if b.Jitter == true {
 | 
			
		||||
		dur = rand.Float64()*(dur-float64(b.Min)) + float64(b.Min)
 | 
			
		||||
	}
 | 
			
		||||
	//cap!
 | 
			
		||||
	if dur > float64(b.Max) {
 | 
			
		||||
		return b.Max
 | 
			
		||||
	}
 | 
			
		||||
	//bump attempts count
 | 
			
		||||
	b.attempts++
 | 
			
		||||
	//return as a time.Duration
 | 
			
		||||
	return time.Duration(dur)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//Resets the current value of the counter back to Min
 | 
			
		||||
func (b *backoff) Reset() {
 | 
			
		||||
	b.attempts = 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										261
									
								
								vendor/github.com/nlopes/slack/channels.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										261
									
								
								vendor/github.com/nlopes/slack/channels.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,261 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type channelResponseFull struct {
 | 
			
		||||
	Channel      Channel   `json:"channel"`
 | 
			
		||||
	Channels     []Channel `json:"channels"`
 | 
			
		||||
	Purpose      string    `json:"purpose"`
 | 
			
		||||
	Topic        string    `json:"topic"`
 | 
			
		||||
	NotInChannel bool      `json:"not_in_channel"`
 | 
			
		||||
	History
 | 
			
		||||
	SlackResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func channelRequest(path string, values url.Values, debug bool) (*channelResponseFull, error) {
 | 
			
		||||
	response := &channelResponseFull{}
 | 
			
		||||
	err := post(path, values, response, debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return response, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ArchiveChannel archives the given channel
 | 
			
		||||
func (api *Client) ArchiveChannel(channel string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := channelRequest("channels.archive", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnarchiveChannel unarchives the given channel
 | 
			
		||||
func (api *Client) UnarchiveChannel(channel string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := channelRequest("channels.unarchive", values, api.debug)
 | 
			
		||||
	if 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) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
		"name":  {channel},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest("channels.create", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.Channel, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannelHistory retrieves the channel history
 | 
			
		||||
func (api *Client) GetChannelHistory(channel string, params HistoryParameters) (*History, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
	}
 | 
			
		||||
	if params.Latest != DEFAULT_HISTORY_LATEST {
 | 
			
		||||
		values.Add("latest", params.Latest)
 | 
			
		||||
	}
 | 
			
		||||
	if params.Oldest != DEFAULT_HISTORY_OLDEST {
 | 
			
		||||
		values.Add("oldest", params.Oldest)
 | 
			
		||||
	}
 | 
			
		||||
	if params.Count != DEFAULT_HISTORY_COUNT {
 | 
			
		||||
		values.Add("count", strconv.Itoa(params.Count))
 | 
			
		||||
	}
 | 
			
		||||
	if params.Inclusive != DEFAULT_HISTORY_INCLUSIVE {
 | 
			
		||||
		if params.Inclusive {
 | 
			
		||||
			values.Add("inclusive", "1")
 | 
			
		||||
		} else {
 | 
			
		||||
			values.Add("inclusive", "0")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if params.Unreads != DEFAULT_HISTORY_UNREADS {
 | 
			
		||||
		if params.Unreads {
 | 
			
		||||
			values.Add("unreads", "1")
 | 
			
		||||
		} else {
 | 
			
		||||
			values.Add("unreads", "0")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest("channels.history", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.History, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannelInfo retrieves the given channel
 | 
			
		||||
func (api *Client) GetChannelInfo(channel string) (*Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest("channels.info", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.Channel, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InviteUserToChannel invites a user to a given channel and returns a *Channel
 | 
			
		||||
func (api *Client) InviteUserToChannel(channel, user string) (*Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"user":    {user},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest("channels.invite", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.Channel, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// JoinChannel joins the currently authenticated user to a channel
 | 
			
		||||
func (api *Client) JoinChannel(channel string) (*Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
		"name":  {channel},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest("channels.join", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.Channel, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LeaveChannel makes the authenticated user leave the given channel
 | 
			
		||||
func (api *Client) LeaveChannel(channel string) (bool, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest("channels.leave", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	if response.NotInChannel {
 | 
			
		||||
		return response.NotInChannel, nil
 | 
			
		||||
	}
 | 
			
		||||
	return false, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// KickUserFromChannel kicks a user from a given channel
 | 
			
		||||
func (api *Client) KickUserFromChannel(channel, user string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"user":    {user},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := channelRequest("channels.kick", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannels retrieves all the channels
 | 
			
		||||
func (api *Client) GetChannels(excludeArchived bool) ([]Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	if excludeArchived {
 | 
			
		||||
		values.Add("exclude_archived", "1")
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest("channels.list", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return response.Channels, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetChannelReadMark sets the read mark of a given channel to a specific point
 | 
			
		||||
// Clients should try to avoid making this call too often. When needing to mark a read position, a client should set a
 | 
			
		||||
// 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 {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"ts":      {ts},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := channelRequest("channels.mark", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RenameChannel renames a given channel
 | 
			
		||||
func (api *Client) RenameChannel(channel, name string) (*Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"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("channels.rename", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.Channel, nil
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetChannelPurpose sets the channel purpose and returns the purpose that was
 | 
			
		||||
// successfully set
 | 
			
		||||
func (api *Client) SetChannelPurpose(channel, purpose string) (string, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"purpose": {purpose},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest("channels.setPurpose", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return response.Purpose, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetChannelTopic sets the channel topic and returns the topic that was successfully set
 | 
			
		||||
func (api *Client) SetChannelTopic(channel, topic string) (string, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"topic":   {topic},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := channelRequest("channels.setTopic", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return response.Topic, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										166
									
								
								vendor/github.com/nlopes/slack/chat.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								vendor/github.com/nlopes/slack/chat.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,166 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	DEFAULT_MESSAGE_USERNAME     = ""
 | 
			
		||||
	DEFAULT_MESSAGE_ASUSER       = false
 | 
			
		||||
	DEFAULT_MESSAGE_PARSE        = ""
 | 
			
		||||
	DEFAULT_MESSAGE_LINK_NAMES   = 0
 | 
			
		||||
	DEFAULT_MESSAGE_UNFURL_LINKS = false
 | 
			
		||||
	DEFAULT_MESSAGE_UNFURL_MEDIA = true
 | 
			
		||||
	DEFAULT_MESSAGE_ICON_URL     = ""
 | 
			
		||||
	DEFAULT_MESSAGE_ICON_EMOJI   = ""
 | 
			
		||||
	DEFAULT_MESSAGE_MARKDOWN     = true
 | 
			
		||||
	DEFAULT_MESSAGE_ESCAPE_TEXT  = true
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type chatResponseFull struct {
 | 
			
		||||
	Channel   string `json:"channel"`
 | 
			
		||||
	Timestamp string `json:"ts"`
 | 
			
		||||
	Text      string `json:"text"`
 | 
			
		||||
	SlackResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PostMessageParameters contains all the parameters necessary (including the optional ones) for a PostMessage() request
 | 
			
		||||
type PostMessageParameters struct {
 | 
			
		||||
	Text        string
 | 
			
		||||
	Username    string
 | 
			
		||||
	AsUser      bool
 | 
			
		||||
	Parse       string
 | 
			
		||||
	LinkNames   int
 | 
			
		||||
	Attachments []Attachment
 | 
			
		||||
	UnfurlLinks bool
 | 
			
		||||
	UnfurlMedia bool
 | 
			
		||||
	IconURL     string
 | 
			
		||||
	IconEmoji   string
 | 
			
		||||
	Markdown    bool `json:"mrkdwn,omitempty"`
 | 
			
		||||
	EscapeText  bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func chatRequest(path string, values url.Values, debug bool) (*chatResponseFull, error) {
 | 
			
		||||
	response := &chatResponseFull{}
 | 
			
		||||
	err := post(path, values, response, debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return response, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteMessage deletes a message in a channel
 | 
			
		||||
func (api *Client) DeleteMessage(channel, messageTimestamp string) (string, string, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"ts":      {messageTimestamp},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := chatRequest("chat.delete", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", "", err
 | 
			
		||||
	}
 | 
			
		||||
	return response.Channel, response.Timestamp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func escapeMessage(message string) string {
 | 
			
		||||
	replacer := strings.NewReplacer("&", "&", "<", "<", ">", ">")
 | 
			
		||||
	return replacer.Replace(message)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PostMessage sends a message to a channel.
 | 
			
		||||
// Message is escaped by default according to https://api.slack.com/docs/formatting
 | 
			
		||||
// Use http://davestevens.github.io/slack-message-builder/ to help crafting your message.
 | 
			
		||||
func (api *Client) PostMessage(channel, text string, params PostMessageParameters) (string, string, error) {
 | 
			
		||||
	if params.EscapeText {
 | 
			
		||||
		text = escapeMessage(text)
 | 
			
		||||
	}
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"text":    {text},
 | 
			
		||||
	}
 | 
			
		||||
	if params.Username != DEFAULT_MESSAGE_USERNAME {
 | 
			
		||||
		values.Set("username", string(params.Username))
 | 
			
		||||
	}
 | 
			
		||||
	if params.AsUser != DEFAULT_MESSAGE_ASUSER {
 | 
			
		||||
		values.Set("as_user", "true")
 | 
			
		||||
	}
 | 
			
		||||
	if params.Parse != DEFAULT_MESSAGE_PARSE {
 | 
			
		||||
		values.Set("parse", string(params.Parse))
 | 
			
		||||
	}
 | 
			
		||||
	if params.LinkNames != DEFAULT_MESSAGE_LINK_NAMES {
 | 
			
		||||
		values.Set("link_names", "1")
 | 
			
		||||
	}
 | 
			
		||||
	if params.Attachments != nil {
 | 
			
		||||
		attachments, err := json.Marshal(params.Attachments)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", "", err
 | 
			
		||||
		}
 | 
			
		||||
		values.Set("attachments", string(attachments))
 | 
			
		||||
	}
 | 
			
		||||
	if params.UnfurlLinks != DEFAULT_MESSAGE_UNFURL_LINKS {
 | 
			
		||||
		values.Set("unfurl_links", "true")
 | 
			
		||||
	}
 | 
			
		||||
	// I want to send a message with explicit `as_user` `true` and `unfurl_links` `false` in request.
 | 
			
		||||
	// Because setting `as_user` to `true` will change the default value for `unfurl_links` to `true` on Slack API side.
 | 
			
		||||
	if params.AsUser != DEFAULT_MESSAGE_ASUSER && params.UnfurlLinks == DEFAULT_MESSAGE_UNFURL_LINKS {
 | 
			
		||||
		values.Set("unfurl_links", "false")
 | 
			
		||||
	}
 | 
			
		||||
	if params.UnfurlMedia != DEFAULT_MESSAGE_UNFURL_MEDIA {
 | 
			
		||||
		values.Set("unfurl_media", "false")
 | 
			
		||||
	}
 | 
			
		||||
	if params.IconURL != DEFAULT_MESSAGE_ICON_URL {
 | 
			
		||||
		values.Set("icon_url", params.IconURL)
 | 
			
		||||
	}
 | 
			
		||||
	if params.IconEmoji != DEFAULT_MESSAGE_ICON_EMOJI {
 | 
			
		||||
		values.Set("icon_emoji", params.IconEmoji)
 | 
			
		||||
	}
 | 
			
		||||
	if params.Markdown != DEFAULT_MESSAGE_MARKDOWN {
 | 
			
		||||
		values.Set("mrkdwn", "false")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	response, err := chatRequest("chat.postMessage", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", "", err
 | 
			
		||||
	}
 | 
			
		||||
	return response.Channel, response.Timestamp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateMessage updates a message in a channel
 | 
			
		||||
func (api *Client) UpdateMessage(channel, timestamp, text string) (string, string, string, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"text":    {escapeMessage(text)},
 | 
			
		||||
		"ts":      {timestamp},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := chatRequest("chat.update", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", "", "", err
 | 
			
		||||
	}
 | 
			
		||||
	return response.Channel, response.Timestamp, response.Text, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								vendor/github.com/nlopes/slack/comment.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/nlopes/slack/comment.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
// Comment contains all the information relative to a comment
 | 
			
		||||
type Comment struct {
 | 
			
		||||
	ID        string   `json:"id,omitempty"`
 | 
			
		||||
	Created   JSONTime `json:"created,omitempty"`
 | 
			
		||||
	Timestamp JSONTime `json:"timestamp,omitempty"`
 | 
			
		||||
	User      string   `json:"user,omitempty"`
 | 
			
		||||
	Comment   string   `json:"comment,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								vendor/github.com/nlopes/slack/conversation.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								vendor/github.com/nlopes/slack/conversation.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
// Conversation is the foundation for IM and BaseGroupConversation
 | 
			
		||||
type conversation struct {
 | 
			
		||||
	ID                 string   `json:"id"`
 | 
			
		||||
	Created            JSONTime `json:"created"`
 | 
			
		||||
	IsOpen             bool     `json:"is_open"`
 | 
			
		||||
	LastRead           string   `json:"last_read,omitempty"`
 | 
			
		||||
	Latest             *Message `json:"latest,omitempty"`
 | 
			
		||||
	UnreadCount        int      `json:"unread_count,omitempty"`
 | 
			
		||||
	UnreadCountDisplay int      `json:"unread_count_display,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GroupConversation is the foundation for Group and Channel
 | 
			
		||||
type groupConversation struct {
 | 
			
		||||
	conversation
 | 
			
		||||
	Name       string   `json:"name"`
 | 
			
		||||
	Creator    string   `json:"creator"`
 | 
			
		||||
	IsArchived bool     `json:"is_archived"`
 | 
			
		||||
	Members    []string `json:"members"`
 | 
			
		||||
	NumMembers int      `json:"num_members,omitempty"`
 | 
			
		||||
	Topic      Topic    `json:"topic"`
 | 
			
		||||
	Purpose    Purpose  `json:"purpose"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Topic contains information about the topic
 | 
			
		||||
type Topic struct {
 | 
			
		||||
	Value   string   `json:"value"`
 | 
			
		||||
	Creator string   `json:"creator"`
 | 
			
		||||
	LastSet JSONTime `json:"last_set"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Purpose contains information about the purpose
 | 
			
		||||
type Purpose struct {
 | 
			
		||||
	Value   string   `json:"value"`
 | 
			
		||||
	Creator string   `json:"creator"`
 | 
			
		||||
	LastSet JSONTime `json:"last_set"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										123
									
								
								vendor/github.com/nlopes/slack/dnd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								vendor/github.com/nlopes/slack/dnd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,123 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type SnoozeDebug struct {
 | 
			
		||||
	SnoozeEndDate string `json:"snooze_end_date"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SnoozeInfo struct {
 | 
			
		||||
	SnoozeEnabled   bool        `json:"snooze_enabled,omitempty"`
 | 
			
		||||
	SnoozeEndTime   int         `json:"snooze_endtime,omitempty"`
 | 
			
		||||
	SnoozeRemaining int         `json:"snooze_remaining,omitempty"`
 | 
			
		||||
	SnoozeDebug     SnoozeDebug `json:"snooze_debug,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type DNDStatus struct {
 | 
			
		||||
	Enabled            bool `json:"dnd_enabled"`
 | 
			
		||||
	NextStartTimestamp int  `json:"next_dnd_start_ts"`
 | 
			
		||||
	NextEndTimestamp   int  `json:"next_dnd_end_ts"`
 | 
			
		||||
	SnoozeInfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type dndResponseFull struct {
 | 
			
		||||
	DNDStatus
 | 
			
		||||
	SlackResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type dndTeamInfoResponse struct {
 | 
			
		||||
	Users map[string]DNDStatus `json:"users"`
 | 
			
		||||
	SlackResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func dndRequest(path string, values url.Values, debug bool) (*dndResponseFull, error) {
 | 
			
		||||
	response := &dndResponseFull{}
 | 
			
		||||
	err := post(path, values, response, debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return response, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EndDND ends the user's scheduled Do Not Disturb session
 | 
			
		||||
func (api *Client) EndDND() error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	response := &SlackResponse{}
 | 
			
		||||
	if err := post("dnd.endDnd", values, response, api.debug); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EndSnooze ends the current user's snooze mode
 | 
			
		||||
func (api *Client) EndSnooze() (*DNDStatus, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	response, err := dndRequest("dnd.endSnooze", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.DNDStatus, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetDNDInfo provides information about a user's current Do Not Disturb settings.
 | 
			
		||||
func (api *Client) GetDNDInfo(user *string) (*DNDStatus, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	if user != nil {
 | 
			
		||||
		values.Set("user", *user)
 | 
			
		||||
	}
 | 
			
		||||
	response, err := dndRequest("dnd.info", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.DNDStatus, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetDNDTeamInfo provides information about a user's current Do Not Disturb settings.
 | 
			
		||||
func (api *Client) GetDNDTeamInfo(users []string) (map[string]DNDStatus, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
		"users": {strings.Join(users, ",")},
 | 
			
		||||
	}
 | 
			
		||||
	response := &dndTeamInfoResponse{}
 | 
			
		||||
	if err := post("dnd.teamInfo", values, response, api.debug); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return response.Users, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetSnooze adjusts the snooze duration for a user's Do Not Disturb
 | 
			
		||||
// settings. If a snooze session is not already active for the user, invoking
 | 
			
		||||
// this method will begin one for the specified duration.
 | 
			
		||||
func (api *Client) SetSnooze(minutes int) (*DNDStatus, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":       {api.config.token},
 | 
			
		||||
		"num_minutes": {strconv.Itoa(minutes)},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := dndRequest("dnd.setSnooze", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.DNDStatus, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								vendor/github.com/nlopes/slack/emoji.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/nlopes/slack/emoji.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/url"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type emojiResponseFull struct {
 | 
			
		||||
	Emoji map[string]string `json:"emoji"`
 | 
			
		||||
	SlackResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetEmoji retrieves all the emojis
 | 
			
		||||
func (api *Client) GetEmoji() (map[string]string, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	response := &emojiResponseFull{}
 | 
			
		||||
	err := post("emoji.list", values, response, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return response.Emoji, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								vendor/github.com/nlopes/slack/examples/channels/channels.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								vendor/github.com/nlopes/slack/examples/channels/channels.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	api := slack.New("YOUR_TOKEN_HERE")
 | 
			
		||||
	channels, err := api.GetChannels(false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("%s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for _, channel := range channels {
 | 
			
		||||
		fmt.Println(channel.ID)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								vendor/github.com/nlopes/slack/examples/files/files.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/nlopes/slack/examples/files/files.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	api := slack.New("YOUR_TOKEN_HERE")
 | 
			
		||||
	params := slack.FileUploadParameters{
 | 
			
		||||
		Title: "Batman Example",
 | 
			
		||||
		//Filetype: "txt",
 | 
			
		||||
		File: "example.txt",
 | 
			
		||||
		//Content:  "Nan Nan Nan Nan Nan Nan Nan Nan Batman",
 | 
			
		||||
	}
 | 
			
		||||
	file, err := api.UploadFile(params)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("%s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("Name: %s, URL: %s\n", file.Name, file.URL)
 | 
			
		||||
 | 
			
		||||
	err = api.DeleteFile(file.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("%s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("File %s deleted successfully.\n", file.Name)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								vendor/github.com/nlopes/slack/examples/groups/groups.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/nlopes/slack/examples/groups/groups.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	api := slack.New("YOUR_TOKEN_HERE")
 | 
			
		||||
	// If you set debugging, it will log all requests to the console
 | 
			
		||||
	// Useful when encountering issues
 | 
			
		||||
	// api.SetDebug(true)
 | 
			
		||||
	groups, err := api.GetGroups(false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("%s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for _, group := range groups {
 | 
			
		||||
		fmt.Printf("ID: %s, Name: %s\n", group.ID, group.Name)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								vendor/github.com/nlopes/slack/examples/messages/messages.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								vendor/github.com/nlopes/slack/examples/messages/messages.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	api := slack.New("YOUR_TOKEN_HERE")
 | 
			
		||||
	params := slack.PostMessageParameters{}
 | 
			
		||||
	attachment := slack.Attachment{
 | 
			
		||||
		Pretext: "some pretext",
 | 
			
		||||
		Text:    "some text",
 | 
			
		||||
		// Uncomment the following part to send a field too
 | 
			
		||||
		/*
 | 
			
		||||
			Fields: []slack.AttachmentField{
 | 
			
		||||
				slack.AttachmentField{
 | 
			
		||||
					Title: "a",
 | 
			
		||||
					Value: "no",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		*/
 | 
			
		||||
	}
 | 
			
		||||
	params.Attachments = []slack.Attachment{attachment}
 | 
			
		||||
	channelID, timestamp, err := api.PostMessage("CHANNEL_ID", "Some text", params)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("%s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("Message successfully sent to channel %s at %s", channelID, timestamp)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										123
									
								
								vendor/github.com/nlopes/slack/examples/pins/pins.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								vendor/github.com/nlopes/slack/examples/pins/pins.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,123 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
   WARNING: This example is destructive in the sense that it create a channel called testpinning
 | 
			
		||||
*/
 | 
			
		||||
func main() {
 | 
			
		||||
	var (
 | 
			
		||||
		apiToken string
 | 
			
		||||
		debug    bool
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	flag.StringVar(&apiToken, "token", "YOUR_TOKEN_HERE", "Your Slack API Token")
 | 
			
		||||
	flag.BoolVar(&debug, "debug", false, "Show JSON output")
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	api := slack.New(apiToken)
 | 
			
		||||
	if debug {
 | 
			
		||||
		api.SetDebug(true)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		postAsUserName  string
 | 
			
		||||
		postAsUserID    string
 | 
			
		||||
		postToChannelID string
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// Find the user to post as.
 | 
			
		||||
	authTest, err := api.AuthTest()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error getting channels: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	channelName := "testpinning"
 | 
			
		||||
 | 
			
		||||
	// Post as the authenticated user.
 | 
			
		||||
	postAsUserName = authTest.User
 | 
			
		||||
	postAsUserID = authTest.UserID
 | 
			
		||||
 | 
			
		||||
	// Create a temporary channel
 | 
			
		||||
	channel, err := api.CreateChannel(channelName)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// If the channel exists, that means we just need to unarchive it
 | 
			
		||||
		if err.Error() == "name_taken" {
 | 
			
		||||
			err = nil
 | 
			
		||||
			channels, err := api.GetChannels(false)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				fmt.Println("Could not retrieve channels")
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			for _, archivedChannel := range channels {
 | 
			
		||||
				if archivedChannel.Name == channelName {
 | 
			
		||||
					if archivedChannel.IsArchived {
 | 
			
		||||
						err = api.UnarchiveChannel(archivedChannel.ID)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							fmt.Printf("Could not unarchive %s: %s\n", archivedChannel.ID, err)
 | 
			
		||||
							return
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					channel = &archivedChannel
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Printf("Error setting test channel for pinning: %s\n", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	postToChannelID = channel.ID
 | 
			
		||||
 | 
			
		||||
	fmt.Printf("Posting as %s (%s) in channel %s\n", postAsUserName, postAsUserID, postToChannelID)
 | 
			
		||||
 | 
			
		||||
	// Post a message.
 | 
			
		||||
	postParams := slack.PostMessageParameters{}
 | 
			
		||||
	channelID, timestamp, err := api.PostMessage(postToChannelID, "Is this any good?", postParams)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error posting message: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Grab a reference to the message.
 | 
			
		||||
	msgRef := slack.NewRefToMessage(channelID, timestamp)
 | 
			
		||||
 | 
			
		||||
	// Add message pin to channel
 | 
			
		||||
	if err := api.AddPin(channelID, msgRef); err != nil {
 | 
			
		||||
		fmt.Printf("Error adding pin: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// List all of the users pins.
 | 
			
		||||
	listPins, _, err := api.ListPins(channelID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error listing pins: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("\n")
 | 
			
		||||
	fmt.Printf("All pins by %s...\n", authTest.User)
 | 
			
		||||
	for _, item := range listPins {
 | 
			
		||||
		fmt.Printf(" > Item type: %s\n", item.Type)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Remove the pin.
 | 
			
		||||
	err = api.RemovePin(channelID, msgRef)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error remove pin: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = api.ArchiveChannel(channelID); err != nil {
 | 
			
		||||
		fmt.Printf("Error archiving channel: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										126
									
								
								vendor/github.com/nlopes/slack/examples/reactions/reactions.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								vendor/github.com/nlopes/slack/examples/reactions/reactions.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,126 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	var (
 | 
			
		||||
		apiToken string
 | 
			
		||||
		debug    bool
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	flag.StringVar(&apiToken, "token", "YOUR_TOKEN_HERE", "Your Slack API Token")
 | 
			
		||||
	flag.BoolVar(&debug, "debug", false, "Show JSON output")
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	api := slack.New(apiToken)
 | 
			
		||||
	if debug {
 | 
			
		||||
		api.SetDebug(true)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		postAsUserName  string
 | 
			
		||||
		postAsUserID    string
 | 
			
		||||
		postToUserName  string
 | 
			
		||||
		postToUserID    string
 | 
			
		||||
		postToChannelID string
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// Find the user to post as.
 | 
			
		||||
	authTest, err := api.AuthTest()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error getting channels: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Post as the authenticated user.
 | 
			
		||||
	postAsUserName = authTest.User
 | 
			
		||||
	postAsUserID = authTest.UserID
 | 
			
		||||
 | 
			
		||||
	// Posting to DM with self causes a conversation with slackbot.
 | 
			
		||||
	postToUserName = authTest.User
 | 
			
		||||
	postToUserID = authTest.UserID
 | 
			
		||||
 | 
			
		||||
	// Find the channel.
 | 
			
		||||
	_, _, chanID, err := api.OpenIMChannel(postToUserID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error opening IM: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	postToChannelID = chanID
 | 
			
		||||
 | 
			
		||||
	fmt.Printf("Posting as %s (%s) in DM with %s (%s), channel %s\n", postAsUserName, postAsUserID, postToUserName, postToUserID, postToChannelID)
 | 
			
		||||
 | 
			
		||||
	// Post a message.
 | 
			
		||||
	postParams := slack.PostMessageParameters{}
 | 
			
		||||
	channelID, timestamp, err := api.PostMessage(postToChannelID, "Is this any good?", postParams)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error posting message: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Grab a reference to the message.
 | 
			
		||||
	msgRef := slack.NewRefToMessage(channelID, timestamp)
 | 
			
		||||
 | 
			
		||||
	// React with :+1:
 | 
			
		||||
	if err := api.AddReaction("+1", msgRef); err != nil {
 | 
			
		||||
		fmt.Printf("Error adding reaction: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// React with :-1:
 | 
			
		||||
	if err := api.AddReaction("cry", msgRef); err != nil {
 | 
			
		||||
		fmt.Printf("Error adding reaction: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get all reactions on the message.
 | 
			
		||||
	msgReactions, err := api.GetReactions(msgRef, slack.NewGetReactionsParameters())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error getting reactions: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("\n")
 | 
			
		||||
	fmt.Printf("%d reactions to message...\n", len(msgReactions))
 | 
			
		||||
	for _, r := range msgReactions {
 | 
			
		||||
		fmt.Printf("  %d users say %s\n", r.Count, r.Name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// List all of the users reactions.
 | 
			
		||||
	listReactions, _, err := api.ListReactions(slack.NewListReactionsParameters())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error listing reactions: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("\n")
 | 
			
		||||
	fmt.Printf("All reactions by %s...\n", authTest.User)
 | 
			
		||||
	for _, item := range listReactions {
 | 
			
		||||
		fmt.Printf("%d on a %s...\n", len(item.Reactions), item.Type)
 | 
			
		||||
		for _, r := range item.Reactions {
 | 
			
		||||
			fmt.Printf("  %s (along with %d others)\n", r.Name, r.Count-1)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Remove the :cry: reaction.
 | 
			
		||||
	err = api.RemoveReaction("cry", msgRef)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error remove reaction: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get all reactions on the message.
 | 
			
		||||
	msgReactions, err = api.GetReactions(msgRef, slack.NewGetReactionsParameters())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error getting reactions: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("\n")
 | 
			
		||||
	fmt.Printf("%d reactions to message after removing cry...\n", len(msgReactions))
 | 
			
		||||
	for _, r := range msgReactions {
 | 
			
		||||
		fmt.Printf("  %d users say %s\n", r.Count, r.Name)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								vendor/github.com/nlopes/slack/examples/stars/stars.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								vendor/github.com/nlopes/slack/examples/stars/stars.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	var (
 | 
			
		||||
		apiToken string
 | 
			
		||||
		debug    bool
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	flag.StringVar(&apiToken, "token", "YOUR_TOKEN_HERE", "Your Slack API Token")
 | 
			
		||||
	flag.BoolVar(&debug, "debug", false, "Show JSON output")
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	api := slack.New(apiToken)
 | 
			
		||||
	if debug {
 | 
			
		||||
		api.SetDebug(true)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get all stars for the usr.
 | 
			
		||||
	params := slack.NewStarsParameters()
 | 
			
		||||
	starredItems, _, err := api.GetStarred(params)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error getting stars: %s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for _, s := range starredItems {
 | 
			
		||||
		var desc string
 | 
			
		||||
		switch s.Type {
 | 
			
		||||
		case slack.TYPE_MESSAGE:
 | 
			
		||||
			desc = s.Message.Text
 | 
			
		||||
		case slack.TYPE_FILE:
 | 
			
		||||
			desc = s.File.Name
 | 
			
		||||
		case slack.TYPE_FILE_COMMENT:
 | 
			
		||||
			desc = s.File.Name + " - " + s.Comment.Comment
 | 
			
		||||
		case slack.TYPE_CHANNEL, slack.TYPE_IM, slack.TYPE_GROUP:
 | 
			
		||||
			desc = s.Channel
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Printf("Starred %s: %s\n", s.Type, desc)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								vendor/github.com/nlopes/slack/examples/users/users.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								vendor/github.com/nlopes/slack/examples/users/users.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	api := slack.New("YOUR_TOKEN_HERE")
 | 
			
		||||
	user, err := api.GetUserInfo("U023BECGF")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("%s\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("ID: %s, Fullname: %s, Email: %s\n", user.ID, user.Profile.RealName, user.Profile.Email)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										58
									
								
								vendor/github.com/nlopes/slack/examples/websocket/websocket.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								vendor/github.com/nlopes/slack/examples/websocket/websocket.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/nlopes/slack"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	api := slack.New("YOUR TOKEN HERE")
 | 
			
		||||
	logger := log.New(os.Stdout, "slack-bot: ", log.Lshortfile|log.LstdFlags)
 | 
			
		||||
	slack.SetLogger(logger)
 | 
			
		||||
	api.SetDebug(true)
 | 
			
		||||
 | 
			
		||||
	rtm := api.NewRTM()
 | 
			
		||||
	go rtm.ManageConnection()
 | 
			
		||||
 | 
			
		||||
Loop:
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case msg := <-rtm.IncomingEvents:
 | 
			
		||||
			fmt.Print("Event Received: ")
 | 
			
		||||
			switch ev := msg.Data.(type) {
 | 
			
		||||
			case *slack.HelloEvent:
 | 
			
		||||
				// Ignore hello
 | 
			
		||||
 | 
			
		||||
			case *slack.ConnectedEvent:
 | 
			
		||||
				fmt.Println("Infos:", ev.Info)
 | 
			
		||||
				fmt.Println("Connection counter:", ev.ConnectionCount)
 | 
			
		||||
				// Replace #general with your Channel ID
 | 
			
		||||
				rtm.SendMessage(rtm.NewOutgoingMessage("Hello world", "#general"))
 | 
			
		||||
 | 
			
		||||
			case *slack.MessageEvent:
 | 
			
		||||
				fmt.Printf("Message: %v\n", ev)
 | 
			
		||||
 | 
			
		||||
			case *slack.PresenceChangeEvent:
 | 
			
		||||
				fmt.Printf("Presence Change: %v\n", ev)
 | 
			
		||||
 | 
			
		||||
			case *slack.LatencyReport:
 | 
			
		||||
				fmt.Printf("Current latency: %v\n", ev.Value)
 | 
			
		||||
 | 
			
		||||
			case *slack.RTMError:
 | 
			
		||||
				fmt.Printf("Error: %s\n", ev.Error())
 | 
			
		||||
 | 
			
		||||
			case *slack.InvalidAuthEvent:
 | 
			
		||||
				fmt.Printf("Invalid credentials")
 | 
			
		||||
				break Loop
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
 | 
			
		||||
				// Ignore other events..
 | 
			
		||||
				// fmt.Printf("Unexpected: %v\n", msg.Data)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										274
									
								
								vendor/github.com/nlopes/slack/files.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										274
									
								
								vendor/github.com/nlopes/slack/files.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,274 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// Add here the defaults in the siten
 | 
			
		||||
	DEFAULT_FILES_USER    = ""
 | 
			
		||||
	DEFAULT_FILES_CHANNEL = ""
 | 
			
		||||
	DEFAULT_FILES_TS_FROM = 0
 | 
			
		||||
	DEFAULT_FILES_TS_TO   = -1
 | 
			
		||||
	DEFAULT_FILES_TYPES   = "all"
 | 
			
		||||
	DEFAULT_FILES_COUNT   = 100
 | 
			
		||||
	DEFAULT_FILES_PAGE    = 1
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// File contains all the information for a file
 | 
			
		||||
type File struct {
 | 
			
		||||
	ID        string   `json:"id"`
 | 
			
		||||
	Created   JSONTime `json:"created"`
 | 
			
		||||
	Timestamp JSONTime `json:"timestamp"`
 | 
			
		||||
 | 
			
		||||
	Name              string `json:"name"`
 | 
			
		||||
	Title             string `json:"title"`
 | 
			
		||||
	Mimetype          string `json:"mimetype"`
 | 
			
		||||
	ImageExifRotation int    `json:"image_exif_rotation"`
 | 
			
		||||
	Filetype          string `json:"filetype"`
 | 
			
		||||
	PrettyType        string `json:"pretty_type"`
 | 
			
		||||
	User              string `json:"user"`
 | 
			
		||||
 | 
			
		||||
	Mode         string `json:"mode"`
 | 
			
		||||
	Editable     bool   `json:"editable"`
 | 
			
		||||
	IsExternal   bool   `json:"is_external"`
 | 
			
		||||
	ExternalType string `json:"external_type"`
 | 
			
		||||
 | 
			
		||||
	Size int `json:"size"`
 | 
			
		||||
 | 
			
		||||
	URL                string `json:"url"`          // Deprecated - never set
 | 
			
		||||
	URLDownload        string `json:"url_download"` // Deprecated - never set
 | 
			
		||||
	URLPrivate         string `json:"url_private"`
 | 
			
		||||
	URLPrivateDownload string `json:"url_private_download"`
 | 
			
		||||
 | 
			
		||||
	OriginalH   int    `json:"original_h"`
 | 
			
		||||
	OriginalW   int    `json:"original_w"`
 | 
			
		||||
	Thumb64     string `json:"thumb_64"`
 | 
			
		||||
	Thumb80     string `json:"thumb_80"`
 | 
			
		||||
	Thumb160    string `json:"thumb_160"`
 | 
			
		||||
	Thumb360    string `json:"thumb_360"`
 | 
			
		||||
	Thumb360Gif string `json:"thumb_360_gif"`
 | 
			
		||||
	Thumb360W   int    `json:"thumb_360_w"`
 | 
			
		||||
	Thumb360H   int    `json:"thumb_360_h"`
 | 
			
		||||
	Thumb480    string `json:"thumb_480"`
 | 
			
		||||
	Thumb480W   int    `json:"thumb_480_w"`
 | 
			
		||||
	Thumb480H   int    `json:"thumb_480_h"`
 | 
			
		||||
	Thumb720    string `json:"thumb_720"`
 | 
			
		||||
	Thumb720W   int    `json:"thumb_720_w"`
 | 
			
		||||
	Thumb720H   int    `json:"thumb_720_h"`
 | 
			
		||||
	Thumb960    string `json:"thumb_960"`
 | 
			
		||||
	Thumb960W   int    `json:"thumb_960_w"`
 | 
			
		||||
	Thumb960H   int    `json:"thumb_960_h"`
 | 
			
		||||
	Thumb1024   string `json:"thumb_1024"`
 | 
			
		||||
	Thumb1024W  int    `json:"thumb_1024_w"`
 | 
			
		||||
	Thumb1024H  int    `json:"thumb_1024_h"`
 | 
			
		||||
 | 
			
		||||
	Permalink       string `json:"permalink"`
 | 
			
		||||
	PermalinkPublic string `json:"permalink_public"`
 | 
			
		||||
 | 
			
		||||
	EditLink         string `json:"edit_link"`
 | 
			
		||||
	Preview          string `json:"preview"`
 | 
			
		||||
	PreviewHighlight string `json:"preview_highlight"`
 | 
			
		||||
	Lines            int    `json:"lines"`
 | 
			
		||||
	LinesMore        int    `json:"lines_more"`
 | 
			
		||||
 | 
			
		||||
	IsPublic        bool     `json:"is_public"`
 | 
			
		||||
	PublicURLShared bool     `json:"public_url_shared"`
 | 
			
		||||
	Channels        []string `json:"channels"`
 | 
			
		||||
	Groups          []string `json:"groups"`
 | 
			
		||||
	IMs             []string `json:"ims"`
 | 
			
		||||
	InitialComment  Comment  `json:"initial_comment"`
 | 
			
		||||
	CommentsCount   int      `json:"comments_count"`
 | 
			
		||||
	NumStars        int      `json:"num_stars"`
 | 
			
		||||
	IsStarred       bool     `json:"is_starred"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FileUploadParameters contains all the parameters necessary (including the optional ones) for an UploadFile() request
 | 
			
		||||
type FileUploadParameters struct {
 | 
			
		||||
	File           string
 | 
			
		||||
	Content        string
 | 
			
		||||
	Filetype       string
 | 
			
		||||
	Filename       string
 | 
			
		||||
	Title          string
 | 
			
		||||
	InitialComment string
 | 
			
		||||
	Channels       []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetFilesParameters contains all the parameters necessary (including the optional ones) for a GetFiles() request
 | 
			
		||||
type GetFilesParameters struct {
 | 
			
		||||
	User          string
 | 
			
		||||
	Channel       string
 | 
			
		||||
	TimestampFrom JSONTime
 | 
			
		||||
	TimestampTo   JSONTime
 | 
			
		||||
	Types         string
 | 
			
		||||
	Count         int
 | 
			
		||||
	Page          int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type fileResponseFull struct {
 | 
			
		||||
	File     `json:"file"`
 | 
			
		||||
	Paging   `json:"paging"`
 | 
			
		||||
	Comments []Comment `json:"comments"`
 | 
			
		||||
	Files    []File    `json:"files"`
 | 
			
		||||
 | 
			
		||||
	SlackResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewGetFilesParameters provides an instance of GetFilesParameters with all the sane default values set
 | 
			
		||||
func NewGetFilesParameters() GetFilesParameters {
 | 
			
		||||
	return GetFilesParameters{
 | 
			
		||||
		User:          DEFAULT_FILES_USER,
 | 
			
		||||
		Channel:       DEFAULT_FILES_CHANNEL,
 | 
			
		||||
		TimestampFrom: DEFAULT_FILES_TS_FROM,
 | 
			
		||||
		TimestampTo:   DEFAULT_FILES_TS_TO,
 | 
			
		||||
		Types:         DEFAULT_FILES_TYPES,
 | 
			
		||||
		Count:         DEFAULT_FILES_COUNT,
 | 
			
		||||
		Page:          DEFAULT_FILES_PAGE,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fileRequest(path string, values url.Values, debug bool) (*fileResponseFull, error) {
 | 
			
		||||
	response := &fileResponseFull{}
 | 
			
		||||
	err := post(path, values, response, debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return response, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetFileInfo retrieves a file and related comments
 | 
			
		||||
func (api *Client) GetFileInfo(fileID string, count, page int) (*File, []Comment, *Paging, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
		"file":  {fileID},
 | 
			
		||||
		"count": {strconv.Itoa(count)},
 | 
			
		||||
		"page":  {strconv.Itoa(page)},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := fileRequest("files.info", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.File, response.Comments, &response.Paging, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetFiles retrieves all files according to the parameters given
 | 
			
		||||
func (api *Client) GetFiles(params GetFilesParameters) ([]File, *Paging, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	if params.User != DEFAULT_FILES_USER {
 | 
			
		||||
		values.Add("user", params.User)
 | 
			
		||||
	}
 | 
			
		||||
	if params.Channel != DEFAULT_FILES_CHANNEL {
 | 
			
		||||
		values.Add("channel", params.Channel)
 | 
			
		||||
	}
 | 
			
		||||
	// XXX: this is broken. fix it with a proper unix timestamp
 | 
			
		||||
	if params.TimestampFrom != DEFAULT_FILES_TS_FROM {
 | 
			
		||||
		values.Add("ts_from", params.TimestampFrom.String())
 | 
			
		||||
	}
 | 
			
		||||
	if params.TimestampTo != DEFAULT_FILES_TS_TO {
 | 
			
		||||
		values.Add("ts_to", params.TimestampTo.String())
 | 
			
		||||
	}
 | 
			
		||||
	if params.Types != DEFAULT_FILES_TYPES {
 | 
			
		||||
		values.Add("types", params.Types)
 | 
			
		||||
	}
 | 
			
		||||
	if params.Count != DEFAULT_FILES_COUNT {
 | 
			
		||||
		values.Add("count", strconv.Itoa(params.Count))
 | 
			
		||||
	}
 | 
			
		||||
	if params.Page != DEFAULT_FILES_PAGE {
 | 
			
		||||
		values.Add("page", strconv.Itoa(params.Page))
 | 
			
		||||
	}
 | 
			
		||||
	response, err := fileRequest("files.list", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return response.Files, &response.Paging, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UploadFile uploads a file
 | 
			
		||||
func (api *Client) UploadFile(params FileUploadParameters) (file *File, err error) {
 | 
			
		||||
	// Test if user token is valid. This helps because client.Do doesn't like this for some reason. XXX: More
 | 
			
		||||
	// investigation needed, but for now this will do.
 | 
			
		||||
	_, err = api.AuthTest()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	response := &fileResponseFull{}
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	if params.Filetype != "" {
 | 
			
		||||
		values.Add("filetype", params.Filetype)
 | 
			
		||||
	}
 | 
			
		||||
	if params.Filename != "" {
 | 
			
		||||
		values.Add("filename", params.Filename)
 | 
			
		||||
	}
 | 
			
		||||
	if params.Title != "" {
 | 
			
		||||
		values.Add("title", params.Title)
 | 
			
		||||
	}
 | 
			
		||||
	if params.InitialComment != "" {
 | 
			
		||||
		values.Add("initial_comment", params.InitialComment)
 | 
			
		||||
	}
 | 
			
		||||
	if len(params.Channels) != 0 {
 | 
			
		||||
		values.Add("channels", strings.Join(params.Channels, ","))
 | 
			
		||||
	}
 | 
			
		||||
	if params.Content != "" {
 | 
			
		||||
		values.Add("content", params.Content)
 | 
			
		||||
		err = post("files.upload", values, response, api.debug)
 | 
			
		||||
	} else if params.File != "" {
 | 
			
		||||
		err = postWithMultipartResponse("files.upload", params.File, values, response, api.debug)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return &response.File, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteFile deletes a file
 | 
			
		||||
func (api *Client) DeleteFile(fileID string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
		"file":  {fileID},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := fileRequest("files.delete", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RevokeFilePublicURL disables public/external sharing for a file
 | 
			
		||||
func (api *Client) RevokeFilePublicURL(fileID string) (*File, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
		"file":  {fileID},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := fileRequest("files.revokePublicURL", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.File, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ShareFilePublicURL enabled public/external sharing for a file
 | 
			
		||||
func (api *Client) ShareFilePublicURL(fileID string) (*File, []Comment, *Paging, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
		"file":  {fileID},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := fileRequest("files.sharedPublicURL", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.File, response.Comments, &response.Paging, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										293
									
								
								vendor/github.com/nlopes/slack/groups.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										293
									
								
								vendor/github.com/nlopes/slack/groups.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,293 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Group contains all the information for a group
 | 
			
		||||
type Group struct {
 | 
			
		||||
	groupConversation
 | 
			
		||||
	IsGroup bool `json:"is_group"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type groupResponseFull struct {
 | 
			
		||||
	Group          Group   `json:"group"`
 | 
			
		||||
	Groups         []Group `json:"groups"`
 | 
			
		||||
	Purpose        string  `json:"purpose"`
 | 
			
		||||
	Topic          string  `json:"topic"`
 | 
			
		||||
	NotInGroup     bool    `json:"not_in_group"`
 | 
			
		||||
	NoOp           bool    `json:"no_op"`
 | 
			
		||||
	AlreadyClosed  bool    `json:"already_closed"`
 | 
			
		||||
	AlreadyOpen    bool    `json:"already_open"`
 | 
			
		||||
	AlreadyInGroup bool    `json:"already_in_group"`
 | 
			
		||||
	Channel        Channel `json:"channel"`
 | 
			
		||||
	History
 | 
			
		||||
	SlackResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func groupRequest(path string, values url.Values, debug bool) (*groupResponseFull, error) {
 | 
			
		||||
	response := &groupResponseFull{}
 | 
			
		||||
	err := post(path, values, response, debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return response, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ArchiveGroup archives a private group
 | 
			
		||||
func (api *Client) ArchiveGroup(group string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {group},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := groupRequest("groups.archive", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnarchiveGroup unarchives a private group
 | 
			
		||||
func (api *Client) UnarchiveGroup(group string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {group},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := groupRequest("groups.unarchive", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateGroup creates a private group
 | 
			
		||||
func (api *Client) CreateGroup(group string) (*Group, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
		"name":  {group},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := groupRequest("groups.create", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.Group, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateChildGroup creates a new private group archiving the old one
 | 
			
		||||
// This method takes an existing private group and performs the following steps:
 | 
			
		||||
//   1. Renames the existing group (from "example" to "example-archived").
 | 
			
		||||
//   2. Archives the existing group.
 | 
			
		||||
//   3. Creates a new group with the name of the existing group.
 | 
			
		||||
//   4. Adds all members of the existing group to the new group.
 | 
			
		||||
func (api *Client) CreateChildGroup(group string) (*Group, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {group},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := groupRequest("groups.createChild", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.Group, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CloseGroup closes a private group
 | 
			
		||||
func (api *Client) CloseGroup(group string) (bool, bool, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {group},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := imRequest("groups.close", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, false, err
 | 
			
		||||
	}
 | 
			
		||||
	return response.NoOp, response.AlreadyClosed, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetGroupHistory fetches all the history for a private group
 | 
			
		||||
func (api *Client) GetGroupHistory(group string, params HistoryParameters) (*History, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {group},
 | 
			
		||||
	}
 | 
			
		||||
	if params.Latest != DEFAULT_HISTORY_LATEST {
 | 
			
		||||
		values.Add("latest", params.Latest)
 | 
			
		||||
	}
 | 
			
		||||
	if params.Oldest != DEFAULT_HISTORY_OLDEST {
 | 
			
		||||
		values.Add("oldest", params.Oldest)
 | 
			
		||||
	}
 | 
			
		||||
	if params.Count != DEFAULT_HISTORY_COUNT {
 | 
			
		||||
		values.Add("count", strconv.Itoa(params.Count))
 | 
			
		||||
	}
 | 
			
		||||
	if params.Inclusive != DEFAULT_HISTORY_INCLUSIVE {
 | 
			
		||||
		if params.Inclusive {
 | 
			
		||||
			values.Add("inclusive", "1")
 | 
			
		||||
		} else {
 | 
			
		||||
			values.Add("inclusive", "0")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if params.Unreads != DEFAULT_HISTORY_UNREADS {
 | 
			
		||||
		if params.Unreads {
 | 
			
		||||
			values.Add("unreads", "1")
 | 
			
		||||
		} else {
 | 
			
		||||
			values.Add("unreads", "0")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	response, err := groupRequest("groups.history", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.History, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InviteUserToGroup invites a specific user to a private group
 | 
			
		||||
func (api *Client) InviteUserToGroup(group, user string) (*Group, bool, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {group},
 | 
			
		||||
		"user":    {user},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := groupRequest("groups.invite", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, false, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.Group, response.AlreadyInGroup, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LeaveGroup makes authenticated user leave the group
 | 
			
		||||
func (api *Client) LeaveGroup(group string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {group},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := groupRequest("groups.leave", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// KickUserFromGroup kicks a user from a group
 | 
			
		||||
func (api *Client) KickUserFromGroup(group, user string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {group},
 | 
			
		||||
		"user":    {user},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := groupRequest("groups.kick", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetGroups retrieves all groups
 | 
			
		||||
func (api *Client) GetGroups(excludeArchived bool) ([]Group, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	if excludeArchived {
 | 
			
		||||
		values.Add("exclude_archived", "1")
 | 
			
		||||
	}
 | 
			
		||||
	response, err := groupRequest("groups.list", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return response.Groups, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetGroupInfo retrieves the given group
 | 
			
		||||
func (api *Client) GetGroupInfo(group string) (*Group, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {group},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := groupRequest("groups.info", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.Group, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetGroupReadMark sets the read mark on a private group
 | 
			
		||||
// Clients should try to avoid making this call too often. When needing to mark a read position, a client should set a
 | 
			
		||||
// 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) SetGroupReadMark(group, ts string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {group},
 | 
			
		||||
		"ts":      {ts},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := groupRequest("groups.mark", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OpenGroup opens a private group
 | 
			
		||||
func (api *Client) OpenGroup(group string) (bool, bool, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {group},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := groupRequest("groups.open", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, false, err
 | 
			
		||||
	}
 | 
			
		||||
	return response.NoOp, response.AlreadyOpen, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RenameGroup renames a group
 | 
			
		||||
// XXX: They return a channel, not a group. What is this crap? :(
 | 
			
		||||
// Inconsistent api it seems.
 | 
			
		||||
func (api *Client) RenameGroup(group, name string) (*Channel, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.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("groups.rename", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.Channel, nil
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetGroupPurpose sets the group purpose
 | 
			
		||||
func (api *Client) SetGroupPurpose(group, purpose string) (string, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {group},
 | 
			
		||||
		"purpose": {purpose},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := groupRequest("groups.setPurpose", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return response.Purpose, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetGroupTopic sets the group topic
 | 
			
		||||
func (api *Client) SetGroupTopic(group, topic string) (string, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {group},
 | 
			
		||||
		"topic":   {topic},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := groupRequest("groups.setTopic", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return response.Topic, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										36
									
								
								vendor/github.com/nlopes/slack/history.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								vendor/github.com/nlopes/slack/history.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	DEFAULT_HISTORY_LATEST    = ""
 | 
			
		||||
	DEFAULT_HISTORY_OLDEST    = "0"
 | 
			
		||||
	DEFAULT_HISTORY_COUNT     = 100
 | 
			
		||||
	DEFAULT_HISTORY_INCLUSIVE = false
 | 
			
		||||
	DEFAULT_HISTORY_UNREADS   = false
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// HistoryParameters contains all the necessary information to help in the retrieval of history for Channels/Groups/DMs
 | 
			
		||||
type HistoryParameters struct {
 | 
			
		||||
	Latest    string
 | 
			
		||||
	Oldest    string
 | 
			
		||||
	Count     int
 | 
			
		||||
	Inclusive bool
 | 
			
		||||
	Unreads   bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// History contains message history information needed to navigate a Channel / Group / DM history
 | 
			
		||||
type History struct {
 | 
			
		||||
	Latest   string    `json:"latest"`
 | 
			
		||||
	Messages []Message `json:"messages"`
 | 
			
		||||
	HasMore  bool      `json:"has_more"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewHistoryParameters provides an instance of HistoryParameters with all the sane default values set
 | 
			
		||||
func NewHistoryParameters() HistoryParameters {
 | 
			
		||||
	return HistoryParameters{
 | 
			
		||||
		Latest:    DEFAULT_HISTORY_LATEST,
 | 
			
		||||
		Oldest:    DEFAULT_HISTORY_OLDEST,
 | 
			
		||||
		Count:     DEFAULT_HISTORY_COUNT,
 | 
			
		||||
		Inclusive: DEFAULT_HISTORY_INCLUSIVE,
 | 
			
		||||
		Unreads:   DEFAULT_HISTORY_UNREADS,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										130
									
								
								vendor/github.com/nlopes/slack/im.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								vendor/github.com/nlopes/slack/im.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,130 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type imChannel struct {
 | 
			
		||||
	ID string `json:"id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type imResponseFull struct {
 | 
			
		||||
	NoOp          bool      `json:"no_op"`
 | 
			
		||||
	AlreadyClosed bool      `json:"already_closed"`
 | 
			
		||||
	AlreadyOpen   bool      `json:"already_open"`
 | 
			
		||||
	Channel       imChannel `json:"channel"`
 | 
			
		||||
	IMs           []IM      `json:"ims"`
 | 
			
		||||
	History
 | 
			
		||||
	SlackResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IM contains information related to the Direct Message channel
 | 
			
		||||
type IM struct {
 | 
			
		||||
	conversation
 | 
			
		||||
	IsIM          bool   `json:"is_im"`
 | 
			
		||||
	User          string `json:"user"`
 | 
			
		||||
	IsUserDeleted bool   `json:"is_user_deleted"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func imRequest(path string, values url.Values, debug bool) (*imResponseFull, error) {
 | 
			
		||||
	response := &imResponseFull{}
 | 
			
		||||
	err := post(path, values, response, debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return response, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CloseIMChannel closes the direct message channel
 | 
			
		||||
func (api *Client) CloseIMChannel(channel string) (bool, bool, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := imRequest("im.close", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, false, err
 | 
			
		||||
	}
 | 
			
		||||
	return response.NoOp, response.AlreadyClosed, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OpenIMChannel opens a direct message channel to the user provided as argument
 | 
			
		||||
// Returns some status and the channel ID
 | 
			
		||||
func (api *Client) OpenIMChannel(user string) (bool, bool, string, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
		"user":  {user},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := imRequest("im.open", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, false, "", err
 | 
			
		||||
	}
 | 
			
		||||
	return response.NoOp, response.AlreadyOpen, response.Channel.ID, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MarkIMChannel sets the read mark of a direct message channel to a specific point
 | 
			
		||||
func (api *Client) MarkIMChannel(channel, ts string) (err error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"ts":      {ts},
 | 
			
		||||
	}
 | 
			
		||||
	_, err = imRequest("im.mark", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetIMHistory retrieves the direct message channel history
 | 
			
		||||
func (api *Client) GetIMHistory(channel string, params HistoryParameters) (*History, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
	}
 | 
			
		||||
	if params.Latest != DEFAULT_HISTORY_LATEST {
 | 
			
		||||
		values.Add("latest", params.Latest)
 | 
			
		||||
	}
 | 
			
		||||
	if params.Oldest != DEFAULT_HISTORY_OLDEST {
 | 
			
		||||
		values.Add("oldest", params.Oldest)
 | 
			
		||||
	}
 | 
			
		||||
	if params.Count != DEFAULT_HISTORY_COUNT {
 | 
			
		||||
		values.Add("count", strconv.Itoa(params.Count))
 | 
			
		||||
	}
 | 
			
		||||
	if params.Inclusive != DEFAULT_HISTORY_INCLUSIVE {
 | 
			
		||||
		if params.Inclusive {
 | 
			
		||||
			values.Add("inclusive", "1")
 | 
			
		||||
		} else {
 | 
			
		||||
			values.Add("inclusive", "0")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if params.Unreads != DEFAULT_HISTORY_UNREADS {
 | 
			
		||||
		if params.Unreads {
 | 
			
		||||
			values.Add("unreads", "1")
 | 
			
		||||
		} else {
 | 
			
		||||
			values.Add("unreads", "0")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	response, err := imRequest("im.history", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.History, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetIMChannels returns the list of direct message channels
 | 
			
		||||
func (api *Client) GetIMChannels() ([]IM, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := imRequest("im.list", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return response.IMs, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										206
									
								
								vendor/github.com/nlopes/slack/info.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								vendor/github.com/nlopes/slack/info.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,206 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// UserPrefs needs to be implemented
 | 
			
		||||
type UserPrefs struct {
 | 
			
		||||
	// "highlight_words":"",
 | 
			
		||||
	// "user_colors":"",
 | 
			
		||||
	// "color_names_in_list":true,
 | 
			
		||||
	// "growls_enabled":true,
 | 
			
		||||
	// "tz":"Europe\/London",
 | 
			
		||||
	// "push_dm_alert":true,
 | 
			
		||||
	// "push_mention_alert":true,
 | 
			
		||||
	// "push_everything":true,
 | 
			
		||||
	// "push_idle_wait":2,
 | 
			
		||||
	// "push_sound":"b2.mp3",
 | 
			
		||||
	// "push_loud_channels":"",
 | 
			
		||||
	// "push_mention_channels":"",
 | 
			
		||||
	// "push_loud_channels_set":"",
 | 
			
		||||
	// "email_alerts":"instant",
 | 
			
		||||
	// "email_alerts_sleep_until":0,
 | 
			
		||||
	// "email_misc":false,
 | 
			
		||||
	// "email_weekly":true,
 | 
			
		||||
	// "welcome_message_hidden":false,
 | 
			
		||||
	// "all_channels_loud":true,
 | 
			
		||||
	// "loud_channels":"",
 | 
			
		||||
	// "never_channels":"",
 | 
			
		||||
	// "loud_channels_set":"",
 | 
			
		||||
	// "show_member_presence":true,
 | 
			
		||||
	// "search_sort":"timestamp",
 | 
			
		||||
	// "expand_inline_imgs":true,
 | 
			
		||||
	// "expand_internal_inline_imgs":true,
 | 
			
		||||
	// "expand_snippets":false,
 | 
			
		||||
	// "posts_formatting_guide":true,
 | 
			
		||||
	// "seen_welcome_2":true,
 | 
			
		||||
	// "seen_ssb_prompt":false,
 | 
			
		||||
	// "search_only_my_channels":false,
 | 
			
		||||
	// "emoji_mode":"default",
 | 
			
		||||
	// "has_invited":true,
 | 
			
		||||
	// "has_uploaded":false,
 | 
			
		||||
	// "has_created_channel":true,
 | 
			
		||||
	// "search_exclude_channels":"",
 | 
			
		||||
	// "messages_theme":"default",
 | 
			
		||||
	// "webapp_spellcheck":true,
 | 
			
		||||
	// "no_joined_overlays":false,
 | 
			
		||||
	// "no_created_overlays":true,
 | 
			
		||||
	// "dropbox_enabled":false,
 | 
			
		||||
	// "seen_user_menu_tip_card":true,
 | 
			
		||||
	// "seen_team_menu_tip_card":true,
 | 
			
		||||
	// "seen_channel_menu_tip_card":true,
 | 
			
		||||
	// "seen_message_input_tip_card":true,
 | 
			
		||||
	// "seen_channels_tip_card":true,
 | 
			
		||||
	// "seen_domain_invite_reminder":false,
 | 
			
		||||
	// "seen_member_invite_reminder":false,
 | 
			
		||||
	// "seen_flexpane_tip_card":true,
 | 
			
		||||
	// "seen_search_input_tip_card":true,
 | 
			
		||||
	// "mute_sounds":false,
 | 
			
		||||
	// "arrow_history":false,
 | 
			
		||||
	// "tab_ui_return_selects":true,
 | 
			
		||||
	// "obey_inline_img_limit":true,
 | 
			
		||||
	// "new_msg_snd":"knock_brush.mp3",
 | 
			
		||||
	// "collapsible":false,
 | 
			
		||||
	// "collapsible_by_click":true,
 | 
			
		||||
	// "require_at":false,
 | 
			
		||||
	// "mac_ssb_bounce":"",
 | 
			
		||||
	// "mac_ssb_bullet":true,
 | 
			
		||||
	// "win_ssb_bullet":true,
 | 
			
		||||
	// "expand_non_media_attachments":true,
 | 
			
		||||
	// "show_typing":true,
 | 
			
		||||
	// "pagekeys_handled":true,
 | 
			
		||||
	// "last_snippet_type":"",
 | 
			
		||||
	// "display_real_names_override":0,
 | 
			
		||||
	// "time24":false,
 | 
			
		||||
	// "enter_is_special_in_tbt":false,
 | 
			
		||||
	// "graphic_emoticons":false,
 | 
			
		||||
	// "convert_emoticons":true,
 | 
			
		||||
	// "autoplay_chat_sounds":true,
 | 
			
		||||
	// "ss_emojis":true,
 | 
			
		||||
	// "sidebar_behavior":"",
 | 
			
		||||
	// "mark_msgs_read_immediately":true,
 | 
			
		||||
	// "start_scroll_at_oldest":true,
 | 
			
		||||
	// "snippet_editor_wrap_long_lines":false,
 | 
			
		||||
	// "ls_disabled":false,
 | 
			
		||||
	// "sidebar_theme":"default",
 | 
			
		||||
	// "sidebar_theme_custom_values":"",
 | 
			
		||||
	// "f_key_search":false,
 | 
			
		||||
	// "k_key_omnibox":true,
 | 
			
		||||
	// "speak_growls":false,
 | 
			
		||||
	// "mac_speak_voice":"com.apple.speech.synthesis.voice.Alex",
 | 
			
		||||
	// "mac_speak_speed":250,
 | 
			
		||||
	// "comma_key_prefs":false,
 | 
			
		||||
	// "at_channel_suppressed_channels":"",
 | 
			
		||||
	// "push_at_channel_suppressed_channels":"",
 | 
			
		||||
	// "prompted_for_email_disabling":false,
 | 
			
		||||
	// "full_text_extracts":false,
 | 
			
		||||
	// "no_text_in_notifications":false,
 | 
			
		||||
	// "muted_channels":"",
 | 
			
		||||
	// "no_macssb1_banner":false,
 | 
			
		||||
	// "privacy_policy_seen":true,
 | 
			
		||||
	// "search_exclude_bots":false,
 | 
			
		||||
	// "fuzzy_matching":false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UserDetails contains user details coming in the initial response from StartRTM
 | 
			
		||||
type UserDetails struct {
 | 
			
		||||
	ID             string    `json:"id"`
 | 
			
		||||
	Name           string    `json:"name"`
 | 
			
		||||
	Created        JSONTime  `json:"created"`
 | 
			
		||||
	ManualPresence string    `json:"manual_presence"`
 | 
			
		||||
	Prefs          UserPrefs `json:"prefs"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// JSONTime exists so that we can have a String method converting the date
 | 
			
		||||
type JSONTime int64
 | 
			
		||||
 | 
			
		||||
// String converts the unix timestamp into a string
 | 
			
		||||
func (t JSONTime) String() string {
 | 
			
		||||
	tm := t.Time()
 | 
			
		||||
	return fmt.Sprintf("\"%s\"", tm.Format("Mon Jan _2"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Time returns a `time.Time` representation of this value.
 | 
			
		||||
func (t JSONTime) Time() time.Time {
 | 
			
		||||
	return time.Unix(int64(t), 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Team contains details about a team
 | 
			
		||||
type Team struct {
 | 
			
		||||
	ID     string `json:"id"`
 | 
			
		||||
	Name   string `json:"name"`
 | 
			
		||||
	Domain string `json:"domain"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Icons XXX: needs further investigation
 | 
			
		||||
type Icons struct {
 | 
			
		||||
	Image48 string `json:"image_48"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Bot contains information about a bot
 | 
			
		||||
type Bot struct {
 | 
			
		||||
	ID      string `json:"id"`
 | 
			
		||||
	Name    string `json:"name"`
 | 
			
		||||
	Deleted bool   `json:"deleted"`
 | 
			
		||||
	Icons   Icons  `json:"icons"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Info contains various details about Users, Channels, Bots and the authenticated user.
 | 
			
		||||
// It is returned by StartRTM or included in the "ConnectedEvent" RTM event.
 | 
			
		||||
type Info struct {
 | 
			
		||||
	URL      string       `json:"url,omitempty"`
 | 
			
		||||
	User     *UserDetails `json:"self,omitempty"`
 | 
			
		||||
	Team     *Team        `json:"team,omitempty"`
 | 
			
		||||
	Users    []User       `json:"users,omitempty"`
 | 
			
		||||
	Channels []Channel    `json:"channels,omitempty"`
 | 
			
		||||
	Groups   []Group      `json:"groups,omitempty"`
 | 
			
		||||
	Bots     []Bot        `json:"bots,omitempty"`
 | 
			
		||||
	IMs      []IM         `json:"ims,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type infoResponseFull struct {
 | 
			
		||||
	Info
 | 
			
		||||
	WebResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetBotByID returns a bot given a bot id
 | 
			
		||||
func (info Info) GetBotByID(botID string) *Bot {
 | 
			
		||||
	for _, bot := range info.Bots {
 | 
			
		||||
		if bot.ID == botID {
 | 
			
		||||
			return &bot
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetUserByID returns a user given a user id
 | 
			
		||||
func (info Info) GetUserByID(userID string) *User {
 | 
			
		||||
	for _, user := range info.Users {
 | 
			
		||||
		if user.ID == userID {
 | 
			
		||||
			return &user
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannelByID returns a channel given a channel id
 | 
			
		||||
func (info Info) GetChannelByID(channelID string) *Channel {
 | 
			
		||||
	for _, channel := range info.Channels {
 | 
			
		||||
		if channel.ID == channelID {
 | 
			
		||||
			return &channel
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetGroupByID returns a group given a group id
 | 
			
		||||
func (info Info) GetGroupByID(groupID string) *Group {
 | 
			
		||||
	for _, group := range info.Groups {
 | 
			
		||||
		if group.ID == groupID {
 | 
			
		||||
			return &group
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										75
									
								
								vendor/github.com/nlopes/slack/item.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								vendor/github.com/nlopes/slack/item.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	TYPE_MESSAGE      = "message"
 | 
			
		||||
	TYPE_FILE         = "file"
 | 
			
		||||
	TYPE_FILE_COMMENT = "file_comment"
 | 
			
		||||
	TYPE_CHANNEL      = "channel"
 | 
			
		||||
	TYPE_IM           = "im"
 | 
			
		||||
	TYPE_GROUP        = "group"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Item is any type of slack message - message, file, or file comment.
 | 
			
		||||
type Item struct {
 | 
			
		||||
	Type      string   `json:"type"`
 | 
			
		||||
	Channel   string   `json:"channel,omitempty"`
 | 
			
		||||
	Message   *Message `json:"message,omitempty"`
 | 
			
		||||
	File      *File    `json:"file,omitempty"`
 | 
			
		||||
	Comment   *Comment `json:"comment,omitempty"`
 | 
			
		||||
	Timestamp string   `json:"ts,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMessageItem turns a message on a channel into a typed message struct.
 | 
			
		||||
func NewMessageItem(ch string, m *Message) Item {
 | 
			
		||||
	return Item{Type: TYPE_MESSAGE, Channel: ch, Message: m}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewFileItem turns a file into a typed file struct.
 | 
			
		||||
func NewFileItem(f *File) Item {
 | 
			
		||||
	return Item{Type: TYPE_FILE, File: f}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewFileCommentItem turns a file and comment into a typed file_comment struct.
 | 
			
		||||
func NewFileCommentItem(f *File, c *Comment) Item {
 | 
			
		||||
	return Item{Type: TYPE_FILE_COMMENT, File: f, Comment: c}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewChannelItem turns a channel id into a typed channel struct.
 | 
			
		||||
func NewChannelItem(ch string) Item {
 | 
			
		||||
	return Item{Type: TYPE_CHANNEL, Channel: ch}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewIMItem turns a channel id into a typed im struct.
 | 
			
		||||
func NewIMItem(ch string) Item {
 | 
			
		||||
	return Item{Type: TYPE_IM, Channel: ch}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewGroupItem turns a channel id into a typed group struct.
 | 
			
		||||
func NewGroupItem(ch string) Item {
 | 
			
		||||
	return Item{Type: TYPE_GROUP, Channel: ch}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ItemRef is a reference to a message of any type. One of FileID,
 | 
			
		||||
// CommentId, or the combination of ChannelId and Timestamp must be
 | 
			
		||||
// specified.
 | 
			
		||||
type ItemRef struct {
 | 
			
		||||
	Channel   string `json:"channel"`
 | 
			
		||||
	Timestamp string `json:"timestamp"`
 | 
			
		||||
	File      string `json:"file"`
 | 
			
		||||
	Comment   string `json:"file_comment"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRefToMessage initializes a reference to to a message.
 | 
			
		||||
func NewRefToMessage(channel, timestamp string) ItemRef {
 | 
			
		||||
	return ItemRef{Channel: channel, Timestamp: timestamp}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRefToFile initializes a reference to a file.
 | 
			
		||||
func NewRefToFile(file string) ItemRef {
 | 
			
		||||
	return ItemRef{File: file}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRefToComment initializes a reference to a file comment.
 | 
			
		||||
func NewRefToComment(comment string) ItemRef {
 | 
			
		||||
	return ItemRef{Comment: comment}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								vendor/github.com/nlopes/slack/messageID.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/nlopes/slack/messageID.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import "sync"
 | 
			
		||||
 | 
			
		||||
// IDGenerator provides an interface for generating integer ID values.
 | 
			
		||||
type IDGenerator interface {
 | 
			
		||||
	Next() int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewSafeID returns a new instance of an IDGenerator which is safe for
 | 
			
		||||
// concurrent use by multiple goroutines.
 | 
			
		||||
func NewSafeID(startID int) IDGenerator {
 | 
			
		||||
	return &safeID{
 | 
			
		||||
		nextID: startID,
 | 
			
		||||
		mutex:  &sync.Mutex{},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type safeID struct {
 | 
			
		||||
	nextID int
 | 
			
		||||
	mutex  *sync.Mutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *safeID) Next() int {
 | 
			
		||||
	s.mutex.Lock()
 | 
			
		||||
	defer s.mutex.Unlock()
 | 
			
		||||
	id := s.nextID
 | 
			
		||||
	s.nextID++
 | 
			
		||||
	return id
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										131
									
								
								vendor/github.com/nlopes/slack/messages.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								vendor/github.com/nlopes/slack/messages.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,131 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
// OutgoingMessage is used for the realtime API, and seems incomplete.
 | 
			
		||||
type OutgoingMessage struct {
 | 
			
		||||
	ID      int    `json:"id"`
 | 
			
		||||
	Channel string `json:"channel,omitempty"`
 | 
			
		||||
	Text    string `json:"text,omitempty"`
 | 
			
		||||
	Type    string `json:"type,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Message is an auxiliary type to allow us to have a message containing sub messages
 | 
			
		||||
type Message struct {
 | 
			
		||||
	Msg
 | 
			
		||||
	SubMessage *Msg `json:"message,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Msg contains information about a slack message
 | 
			
		||||
type Msg struct {
 | 
			
		||||
	// Basic Message
 | 
			
		||||
	Type        string       `json:"type,omitempty"`
 | 
			
		||||
	Channel     string       `json:"channel,omitempty"`
 | 
			
		||||
	User        string       `json:"user,omitempty"`
 | 
			
		||||
	Text        string       `json:"text,omitempty"`
 | 
			
		||||
	Timestamp   string       `json:"ts,omitempty"`
 | 
			
		||||
	IsStarred   bool         `json:"is_starred,omitempty"`
 | 
			
		||||
	PinnedTo    []string     `json:"pinned_to, omitempty"`
 | 
			
		||||
	Attachments []Attachment `json:"attachments,omitempty"`
 | 
			
		||||
	Edited      *Edited      `json:"edited,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// Message Subtypes
 | 
			
		||||
	SubType string `json:"subtype,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// Hidden Subtypes
 | 
			
		||||
	Hidden           bool   `json:"hidden,omitempty"`     // message_changed, message_deleted, unpinned_item
 | 
			
		||||
	DeletedTimestamp string `json:"deleted_ts,omitempty"` // message_deleted
 | 
			
		||||
	EventTimestamp   string `json:"event_ts,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// bot_message (https://api.slack.com/events/message/bot_message)
 | 
			
		||||
	BotID    string `json:"bot_id,omitempty"`
 | 
			
		||||
	Username string `json:"username,omitempty"`
 | 
			
		||||
	Icons    *Icon  `json:"icons,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// channel_join, group_join
 | 
			
		||||
	Inviter string `json:"inviter,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// channel_topic, group_topic
 | 
			
		||||
	Topic string `json:"topic,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// channel_purpose, group_purpose
 | 
			
		||||
	Purpose string `json:"purpose,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// channel_name, group_name
 | 
			
		||||
	Name    string `json:"name,omitempty"`
 | 
			
		||||
	OldName string `json:"old_name,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// channel_archive, group_archive
 | 
			
		||||
	Members []string `json:"members,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// file_share, file_comment, file_mention
 | 
			
		||||
	File *File `json:"file,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// file_share
 | 
			
		||||
	Upload bool `json:"upload,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// file_comment
 | 
			
		||||
	Comment *Comment `json:"comment,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// pinned_item
 | 
			
		||||
	ItemType string `json:"item_type,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// https://api.slack.com/rtm
 | 
			
		||||
	ReplyTo int    `json:"reply_to,omitempty"`
 | 
			
		||||
	Team    string `json:"team,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// reactions
 | 
			
		||||
	Reactions []ItemReaction `json:"reactions,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Icon is used for bot messages
 | 
			
		||||
type Icon struct {
 | 
			
		||||
	IconURL   string `json:"icon_url,omitempty"`
 | 
			
		||||
	IconEmoji string `json:"icon_emoji,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Edited indicates that a message has been edited.
 | 
			
		||||
type Edited struct {
 | 
			
		||||
	User      string `json:"user,omitempty"`
 | 
			
		||||
	Timestamp string `json:"ts,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Event contains the event type
 | 
			
		||||
type Event struct {
 | 
			
		||||
	Type string `json:"type,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Ping contains information about a Ping Event
 | 
			
		||||
type Ping struct {
 | 
			
		||||
	ID   int    `json:"id"`
 | 
			
		||||
	Type string `json:"type"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Pong contains information about a Pong Event
 | 
			
		||||
type Pong struct {
 | 
			
		||||
	Type    string `json:"type"`
 | 
			
		||||
	ReplyTo int    `json:"reply_to"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 {
 | 
			
		||||
	id := rtm.idGen.Next()
 | 
			
		||||
	return &OutgoingMessage{
 | 
			
		||||
		ID:      id,
 | 
			
		||||
		Type:    "message",
 | 
			
		||||
		Channel: channel,
 | 
			
		||||
		Text:    text,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 {
 | 
			
		||||
	id := rtm.idGen.Next()
 | 
			
		||||
	return &OutgoingMessage{
 | 
			
		||||
		ID:      id,
 | 
			
		||||
		Type:    "typing",
 | 
			
		||||
		Channel: channel,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										119
									
								
								vendor/github.com/nlopes/slack/misc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								vendor/github.com/nlopes/slack/misc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,119 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"mime/multipart"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var HTTPClient = &http.Client{}
 | 
			
		||||
 | 
			
		||||
type WebResponse struct {
 | 
			
		||||
	Ok    bool      `json:"ok"`
 | 
			
		||||
	Error *WebError `json:"error"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type WebError string
 | 
			
		||||
 | 
			
		||||
func (s WebError) Error() string {
 | 
			
		||||
	return string(s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fileUploadReq(path, fpath string, values url.Values) (*http.Request, error) {
 | 
			
		||||
	fullpath, err := filepath.Abs(fpath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	file, err := os.Open(fullpath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	defer file.Close()
 | 
			
		||||
 | 
			
		||||
	body := &bytes.Buffer{}
 | 
			
		||||
	wr := multipart.NewWriter(body)
 | 
			
		||||
 | 
			
		||||
	ioWriter, err := wr.CreateFormFile("file", filepath.Base(fullpath))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		wr.Close()
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	bytes, err := io.Copy(ioWriter, file)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		wr.Close()
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	// Close the multipart writer or the footer won't be written
 | 
			
		||||
	wr.Close()
 | 
			
		||||
	stat, err := file.Stat()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if bytes != stat.Size() {
 | 
			
		||||
		return nil, errors.New("could not read the whole file")
 | 
			
		||||
	}
 | 
			
		||||
	req, err := http.NewRequest("POST", path, body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	req.Header.Add("Content-Type", wr.FormDataContentType())
 | 
			
		||||
	req.URL.RawQuery = (values).Encode()
 | 
			
		||||
	return req, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseResponseBody(body io.ReadCloser, intf *interface{}, debug bool) error {
 | 
			
		||||
	response, err := ioutil.ReadAll(body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// FIXME: will be api.Debugf
 | 
			
		||||
	if debug {
 | 
			
		||||
		logger.Printf("parseResponseBody: %s\n", string(response))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = json.Unmarshal(response, &intf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func postWithMultipartResponse(path string, filepath string, values url.Values, intf interface{}, debug bool) error {
 | 
			
		||||
	req, err := fileUploadReq(SLACK_API+path, filepath, values)
 | 
			
		||||
	resp, err := HTTPClient.Do(req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
	return parseResponseBody(resp.Body, &intf, debug)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func postForm(endpoint string, values url.Values, intf interface{}, debug bool) error {
 | 
			
		||||
	resp, err := HTTPClient.PostForm(endpoint, values)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
	return parseResponseBody(resp.Body, &intf, debug)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func post(path string, values url.Values, intf interface{}, debug bool) error {
 | 
			
		||||
	return postForm(SLACK_API+path, values, intf, debug)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseAdminResponse(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(endpoint, values, intf, debug)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										54
									
								
								vendor/github.com/nlopes/slack/oauth.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								vendor/github.com/nlopes/slack/oauth.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/url"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type OAuthResponseIncomingWebhook struct {
 | 
			
		||||
	URL              string `json:"url"`
 | 
			
		||||
	Channel          string `json:"channel"`
 | 
			
		||||
	ConfigurationURL string `json:"configuration_url"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type OAuthResponseBot struct {
 | 
			
		||||
	BotUserID      string `json:"bot_user_id"`
 | 
			
		||||
	BotAccessToken string `json:"bot_access_token"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type OAuthResponse struct {
 | 
			
		||||
	AccessToken     string                       `json:"access_token"`
 | 
			
		||||
	Scope           string                       `json:"scope"`
 | 
			
		||||
	TeamName        string                       `json:"team_name"`
 | 
			
		||||
	TeamID          string                       `json:"team_id"`
 | 
			
		||||
	IncomingWebhook OAuthResponseIncomingWebhook `json:"incoming_webhook"`
 | 
			
		||||
	Bot             OAuthResponseBot             `json:"bot"`
 | 
			
		||||
	SlackResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetOAuthToken retrieves an AccessToken
 | 
			
		||||
func GetOAuthToken(clientID, clientSecret, code, redirectURI string, debug bool) (accessToken string, scope string, err error) {
 | 
			
		||||
	response, err := GetOAuthResponse(clientID, clientSecret, code, redirectURI, debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", "", err
 | 
			
		||||
	}
 | 
			
		||||
	return response.AccessToken, response.Scope, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetOAuthResponse(clientID, clientSecret, code, redirectURI string, debug bool) (resp *OAuthResponse, err error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"client_id":     {clientID},
 | 
			
		||||
		"client_secret": {clientSecret},
 | 
			
		||||
		"code":          {code},
 | 
			
		||||
		"redirect_uri":  {redirectURI},
 | 
			
		||||
	}
 | 
			
		||||
	response := &OAuthResponse{}
 | 
			
		||||
	err = post("oauth.access", values, response, debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return response, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								vendor/github.com/nlopes/slack/pagination.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/nlopes/slack/pagination.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
// Paging contains paging information
 | 
			
		||||
type Paging struct {
 | 
			
		||||
	Count int `json:"count"`
 | 
			
		||||
	Total int `json:"total"`
 | 
			
		||||
	Page  int `json:"page"`
 | 
			
		||||
	Pages int `json:"pages"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Pagination contains pagination information
 | 
			
		||||
// This is different from Paging in that it contains additional details
 | 
			
		||||
type Pagination struct {
 | 
			
		||||
	TotalCount int `json:"total_count"`
 | 
			
		||||
	Page       int `json:"page"`
 | 
			
		||||
	PerPage    int `json:"per_page"`
 | 
			
		||||
	PageCount  int `json:"page_count"`
 | 
			
		||||
	First      int `json:"first"`
 | 
			
		||||
	Last       int `json:"last"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										79
									
								
								vendor/github.com/nlopes/slack/pins.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								vendor/github.com/nlopes/slack/pins.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/url"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type listPinsResponseFull struct {
 | 
			
		||||
	Items  []Item
 | 
			
		||||
	Paging `json:"paging"`
 | 
			
		||||
	SlackResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddPin pins an item in a channel
 | 
			
		||||
func (api *Client) AddPin(channel string, item ItemRef) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	if item.Timestamp != "" {
 | 
			
		||||
		values.Set("timestamp", string(item.Timestamp))
 | 
			
		||||
	}
 | 
			
		||||
	if item.File != "" {
 | 
			
		||||
		values.Set("file", string(item.File))
 | 
			
		||||
	}
 | 
			
		||||
	if item.Comment != "" {
 | 
			
		||||
		values.Set("file_comment", string(item.Comment))
 | 
			
		||||
	}
 | 
			
		||||
	response := &SlackResponse{}
 | 
			
		||||
	if err := post("pins.add", values, response, api.debug); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RemovePin un-pins an item from a channel
 | 
			
		||||
func (api *Client) RemovePin(channel string, item ItemRef) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	if item.Timestamp != "" {
 | 
			
		||||
		values.Set("timestamp", string(item.Timestamp))
 | 
			
		||||
	}
 | 
			
		||||
	if item.File != "" {
 | 
			
		||||
		values.Set("file", string(item.File))
 | 
			
		||||
	}
 | 
			
		||||
	if item.Comment != "" {
 | 
			
		||||
		values.Set("file_comment", string(item.Comment))
 | 
			
		||||
	}
 | 
			
		||||
	response := &SlackResponse{}
 | 
			
		||||
	if err := post("pins.remove", values, response, api.debug); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListPins returns information about the items a user reacted to.
 | 
			
		||||
func (api *Client) ListPins(channel string) ([]Item, *Paging, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	response := &listPinsResponseFull{}
 | 
			
		||||
	err := post("pins.list", values, response, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, nil, errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return response.Items, &response.Paging, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										246
									
								
								vendor/github.com/nlopes/slack/reactions.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										246
									
								
								vendor/github.com/nlopes/slack/reactions.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,246 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ItemReaction is the reactions that have happened on an item.
 | 
			
		||||
type ItemReaction struct {
 | 
			
		||||
	Name  string   `json:"name"`
 | 
			
		||||
	Count int      `json:"count"`
 | 
			
		||||
	Users []string `json:"users"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReactedItem is an item that was reacted to, and the details of the
 | 
			
		||||
// reactions.
 | 
			
		||||
type ReactedItem struct {
 | 
			
		||||
	Item
 | 
			
		||||
	Reactions []ItemReaction
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetReactionsParameters is the inputs to get reactions to an item.
 | 
			
		||||
type GetReactionsParameters struct {
 | 
			
		||||
	Full bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewGetReactionsParameters initializes the inputs to get reactions to an item.
 | 
			
		||||
func NewGetReactionsParameters() GetReactionsParameters {
 | 
			
		||||
	return GetReactionsParameters{
 | 
			
		||||
		Full: false,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type getReactionsResponseFull struct {
 | 
			
		||||
	Type string
 | 
			
		||||
	M    struct {
 | 
			
		||||
		Reactions []ItemReaction
 | 
			
		||||
	} `json:"message"`
 | 
			
		||||
	F struct {
 | 
			
		||||
		Reactions []ItemReaction
 | 
			
		||||
	} `json:"file"`
 | 
			
		||||
	FC struct {
 | 
			
		||||
		Reactions []ItemReaction
 | 
			
		||||
	} `json:"comment"`
 | 
			
		||||
	SlackResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (res getReactionsResponseFull) extractReactions() []ItemReaction {
 | 
			
		||||
	switch res.Type {
 | 
			
		||||
	case "message":
 | 
			
		||||
		return res.M.Reactions
 | 
			
		||||
	case "file":
 | 
			
		||||
		return res.F.Reactions
 | 
			
		||||
	case "file_comment":
 | 
			
		||||
		return res.FC.Reactions
 | 
			
		||||
	}
 | 
			
		||||
	return []ItemReaction{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	DEFAULT_REACTIONS_USER  = ""
 | 
			
		||||
	DEFAULT_REACTIONS_COUNT = 100
 | 
			
		||||
	DEFAULT_REACTIONS_PAGE  = 1
 | 
			
		||||
	DEFAULT_REACTIONS_FULL  = false
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ListReactionsParameters is the inputs to find all reactions by a user.
 | 
			
		||||
type ListReactionsParameters struct {
 | 
			
		||||
	User  string
 | 
			
		||||
	Count int
 | 
			
		||||
	Page  int
 | 
			
		||||
	Full  bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewListReactionsParameters initializes the inputs to find all reactions
 | 
			
		||||
// performed by a user.
 | 
			
		||||
func NewListReactionsParameters() ListReactionsParameters {
 | 
			
		||||
	return ListReactionsParameters{
 | 
			
		||||
		User:  DEFAULT_REACTIONS_USER,
 | 
			
		||||
		Count: DEFAULT_REACTIONS_COUNT,
 | 
			
		||||
		Page:  DEFAULT_REACTIONS_PAGE,
 | 
			
		||||
		Full:  DEFAULT_REACTIONS_FULL,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type listReactionsResponseFull struct {
 | 
			
		||||
	Items []struct {
 | 
			
		||||
		Type    string
 | 
			
		||||
		Channel string
 | 
			
		||||
		M       struct {
 | 
			
		||||
			*Message
 | 
			
		||||
		} `json:"message"`
 | 
			
		||||
		F struct {
 | 
			
		||||
			*File
 | 
			
		||||
			Reactions []ItemReaction
 | 
			
		||||
		} `json:"file"`
 | 
			
		||||
		FC struct {
 | 
			
		||||
			*Comment
 | 
			
		||||
			Reactions []ItemReaction
 | 
			
		||||
		} `json:"comment"`
 | 
			
		||||
	}
 | 
			
		||||
	Paging `json:"paging"`
 | 
			
		||||
	SlackResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (res listReactionsResponseFull) extractReactedItems() []ReactedItem {
 | 
			
		||||
	items := make([]ReactedItem, len(res.Items))
 | 
			
		||||
	for i, input := range res.Items {
 | 
			
		||||
		item := ReactedItem{}
 | 
			
		||||
		item.Type = input.Type
 | 
			
		||||
		switch input.Type {
 | 
			
		||||
		case "message":
 | 
			
		||||
			item.Channel = input.Channel
 | 
			
		||||
			item.Message = input.M.Message
 | 
			
		||||
			item.Reactions = input.M.Reactions
 | 
			
		||||
		case "file":
 | 
			
		||||
			item.File = input.F.File
 | 
			
		||||
			item.Reactions = input.F.Reactions
 | 
			
		||||
		case "file_comment":
 | 
			
		||||
			item.File = input.F.File
 | 
			
		||||
			item.Comment = input.FC.Comment
 | 
			
		||||
			item.Reactions = input.FC.Reactions
 | 
			
		||||
		}
 | 
			
		||||
		items[i] = item
 | 
			
		||||
	}
 | 
			
		||||
	return items
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddReaction adds a reaction emoji to a message, file or file comment.
 | 
			
		||||
func (api *Client) AddReaction(name string, item ItemRef) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	if name != "" {
 | 
			
		||||
		values.Set("name", name)
 | 
			
		||||
	}
 | 
			
		||||
	if item.Channel != "" {
 | 
			
		||||
		values.Set("channel", string(item.Channel))
 | 
			
		||||
	}
 | 
			
		||||
	if item.Timestamp != "" {
 | 
			
		||||
		values.Set("timestamp", string(item.Timestamp))
 | 
			
		||||
	}
 | 
			
		||||
	if item.File != "" {
 | 
			
		||||
		values.Set("file", string(item.File))
 | 
			
		||||
	}
 | 
			
		||||
	if item.Comment != "" {
 | 
			
		||||
		values.Set("file_comment", string(item.Comment))
 | 
			
		||||
	}
 | 
			
		||||
	response := &SlackResponse{}
 | 
			
		||||
	if err := post("reactions.add", values, response, api.debug); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RemoveReaction removes a reaction emoji from a message, file or file comment.
 | 
			
		||||
func (api *Client) RemoveReaction(name string, item ItemRef) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	if name != "" {
 | 
			
		||||
		values.Set("name", name)
 | 
			
		||||
	}
 | 
			
		||||
	if item.Channel != "" {
 | 
			
		||||
		values.Set("channel", string(item.Channel))
 | 
			
		||||
	}
 | 
			
		||||
	if item.Timestamp != "" {
 | 
			
		||||
		values.Set("timestamp", string(item.Timestamp))
 | 
			
		||||
	}
 | 
			
		||||
	if item.File != "" {
 | 
			
		||||
		values.Set("file", string(item.File))
 | 
			
		||||
	}
 | 
			
		||||
	if item.Comment != "" {
 | 
			
		||||
		values.Set("file_comment", string(item.Comment))
 | 
			
		||||
	}
 | 
			
		||||
	response := &SlackResponse{}
 | 
			
		||||
	if err := post("reactions.remove", values, response, api.debug); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetReactions returns details about the reactions on an item.
 | 
			
		||||
func (api *Client) GetReactions(item ItemRef, params GetReactionsParameters) ([]ItemReaction, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	if item.Channel != "" {
 | 
			
		||||
		values.Set("channel", string(item.Channel))
 | 
			
		||||
	}
 | 
			
		||||
	if item.Timestamp != "" {
 | 
			
		||||
		values.Set("timestamp", string(item.Timestamp))
 | 
			
		||||
	}
 | 
			
		||||
	if item.File != "" {
 | 
			
		||||
		values.Set("file", string(item.File))
 | 
			
		||||
	}
 | 
			
		||||
	if item.Comment != "" {
 | 
			
		||||
		values.Set("file_comment", string(item.Comment))
 | 
			
		||||
	}
 | 
			
		||||
	if params.Full != DEFAULT_REACTIONS_FULL {
 | 
			
		||||
		values.Set("full", strconv.FormatBool(params.Full))
 | 
			
		||||
	}
 | 
			
		||||
	response := &getReactionsResponseFull{}
 | 
			
		||||
	if err := post("reactions.get", values, response, api.debug); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return response.extractReactions(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListReactions returns information about the items a user reacted to.
 | 
			
		||||
func (api *Client) ListReactions(params ListReactionsParameters) ([]ReactedItem, *Paging, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	if params.User != DEFAULT_REACTIONS_USER {
 | 
			
		||||
		values.Add("user", params.User)
 | 
			
		||||
	}
 | 
			
		||||
	if params.Count != DEFAULT_REACTIONS_COUNT {
 | 
			
		||||
		values.Add("count", strconv.Itoa(params.Count))
 | 
			
		||||
	}
 | 
			
		||||
	if params.Page != DEFAULT_REACTIONS_PAGE {
 | 
			
		||||
		values.Add("page", strconv.Itoa(params.Page))
 | 
			
		||||
	}
 | 
			
		||||
	if params.Full != DEFAULT_REACTIONS_FULL {
 | 
			
		||||
		values.Add("full", strconv.FormatBool(params.Full))
 | 
			
		||||
	}
 | 
			
		||||
	response := &listReactionsResponseFull{}
 | 
			
		||||
	err := post("reactions.list", values, response, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, nil, errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return response.extractReactedItems(), &response.Paging, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								vendor/github.com/nlopes/slack/rtm.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								vendor/github.com/nlopes/slack/rtm.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/url"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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) {
 | 
			
		||||
	response := &infoResponseFull{}
 | 
			
		||||
	err = post("rtm.start", url.Values{"token": {api.config.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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRTM returns a RTM, which provides a fully managed connection to
 | 
			
		||||
// Slack's websocket-based Real-Time Messaging protocol./
 | 
			
		||||
func (api *Client) NewRTM() *RTM {
 | 
			
		||||
	return newRTM(api)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										137
									
								
								vendor/github.com/nlopes/slack/search.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								vendor/github.com/nlopes/slack/search.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,137 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	DEFAULT_SEARCH_SORT      = "score"
 | 
			
		||||
	DEFAULT_SEARCH_SORT_DIR  = "desc"
 | 
			
		||||
	DEFAULT_SEARCH_HIGHLIGHT = false
 | 
			
		||||
	DEFAULT_SEARCH_COUNT     = 100
 | 
			
		||||
	DEFAULT_SEARCH_PAGE      = 1
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type SearchParameters struct {
 | 
			
		||||
	Sort          string
 | 
			
		||||
	SortDirection string
 | 
			
		||||
	Highlight     bool
 | 
			
		||||
	Count         int
 | 
			
		||||
	Page          int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type CtxChannel struct {
 | 
			
		||||
	ID   string `json:"id"`
 | 
			
		||||
	Name string `json:"name"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type CtxMessage struct {
 | 
			
		||||
	User      string `json:"user"`
 | 
			
		||||
	Username  string `json:"username"`
 | 
			
		||||
	Text      string `json:"text"`
 | 
			
		||||
	Timestamp string `json:"ts"`
 | 
			
		||||
	Type      string `json:"type"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SearchMessage struct {
 | 
			
		||||
	Type      string     `json:"type"`
 | 
			
		||||
	Channel   CtxChannel `json:"channel"`
 | 
			
		||||
	User      string     `json:"user"`
 | 
			
		||||
	Username  string     `json:"username"`
 | 
			
		||||
	Timestamp string     `json:"ts"`
 | 
			
		||||
	Text      string     `json:"text"`
 | 
			
		||||
	Permalink string     `json:"permalink"`
 | 
			
		||||
	Previous  CtxMessage `json:"previous"`
 | 
			
		||||
	Previous2 CtxMessage `json:"previous_2"`
 | 
			
		||||
	Next      CtxMessage `json:"next"`
 | 
			
		||||
	Next2     CtxMessage `json:"next_2"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SearchMessages struct {
 | 
			
		||||
	Matches    []SearchMessage `json:"matches"`
 | 
			
		||||
	Paging     `json:"paging"`
 | 
			
		||||
	Pagination `json:"pagination"`
 | 
			
		||||
	Total      int `json:"total"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SearchFiles struct {
 | 
			
		||||
	Matches    []File `json:"matches"`
 | 
			
		||||
	Paging     `json:"paging"`
 | 
			
		||||
	Pagination `json:"pagination"`
 | 
			
		||||
	Total      int `json:"total"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type searchResponseFull struct {
 | 
			
		||||
	Query          string `json:"query"`
 | 
			
		||||
	SearchMessages `json:"messages"`
 | 
			
		||||
	SearchFiles    `json:"files"`
 | 
			
		||||
	SlackResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewSearchParameters() SearchParameters {
 | 
			
		||||
	return SearchParameters{
 | 
			
		||||
		Sort:          DEFAULT_SEARCH_SORT,
 | 
			
		||||
		SortDirection: DEFAULT_SEARCH_SORT_DIR,
 | 
			
		||||
		Highlight:     DEFAULT_SEARCH_HIGHLIGHT,
 | 
			
		||||
		Count:         DEFAULT_SEARCH_COUNT,
 | 
			
		||||
		Page:          DEFAULT_SEARCH_PAGE,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api *Client) _search(path, query string, params SearchParameters, files, messages bool) (response *searchResponseFull, error error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
		"query": {query},
 | 
			
		||||
	}
 | 
			
		||||
	if params.Sort != DEFAULT_SEARCH_SORT {
 | 
			
		||||
		values.Add("sort", params.Sort)
 | 
			
		||||
	}
 | 
			
		||||
	if params.SortDirection != DEFAULT_SEARCH_SORT_DIR {
 | 
			
		||||
		values.Add("sort_dir", params.SortDirection)
 | 
			
		||||
	}
 | 
			
		||||
	if params.Highlight != DEFAULT_SEARCH_HIGHLIGHT {
 | 
			
		||||
		values.Add("highlight", strconv.Itoa(1))
 | 
			
		||||
	}
 | 
			
		||||
	if params.Count != DEFAULT_SEARCH_COUNT {
 | 
			
		||||
		values.Add("count", strconv.Itoa(params.Count))
 | 
			
		||||
	}
 | 
			
		||||
	if params.Page != DEFAULT_SEARCH_PAGE {
 | 
			
		||||
		values.Add("page", strconv.Itoa(params.Page))
 | 
			
		||||
	}
 | 
			
		||||
	response = &searchResponseFull{}
 | 
			
		||||
	err := post(path, values, response, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return response, nil
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api *Client) Search(query string, params SearchParameters) (*SearchMessages, *SearchFiles, error) {
 | 
			
		||||
	response, err := api._search("search.all", query, params, true, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.SearchMessages, &response.SearchFiles, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api *Client) SearchFiles(query string, params SearchParameters) (*SearchFiles, error) {
 | 
			
		||||
	response, err := api._search("search.files", query, params, true, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.SearchFiles, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api *Client) SearchMessages(query string, params SearchParameters) (*SearchMessages, error) {
 | 
			
		||||
	response, err := api._search("search.messages", query, params, false, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.SearchMessages, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										88
									
								
								vendor/github.com/nlopes/slack/slack.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								vendor/github.com/nlopes/slack/slack.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"log"
 | 
			
		||||
	"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
 | 
			
		||||
*/
 | 
			
		||||
var SLACK_API string = "https://slack.com/api/"
 | 
			
		||||
var SLACK_WEB_API_FORMAT string = "https://%s.slack.com/api/users.admin.%s?t=%s"
 | 
			
		||||
 | 
			
		||||
type SlackResponse struct {
 | 
			
		||||
	Ok    bool   `json:"ok"`
 | 
			
		||||
	Error string `json:"error"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AuthTestResponse struct {
 | 
			
		||||
	URL    string `json:"url"`
 | 
			
		||||
	Team   string `json:"team"`
 | 
			
		||||
	User   string `json:"user"`
 | 
			
		||||
	TeamID string `json:"team_id"`
 | 
			
		||||
	UserID string `json:"user_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type authTestResponseFull struct {
 | 
			
		||||
	SlackResponse
 | 
			
		||||
	AuthTestResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Client struct {
 | 
			
		||||
	config struct {
 | 
			
		||||
		token string
 | 
			
		||||
	}
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(token string) *Client {
 | 
			
		||||
	s := &Client{}
 | 
			
		||||
	s.config.token = token
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AuthTest tests if the user is able to do authenticated requests or not
 | 
			
		||||
func (api *Client) AuthTest() (response *AuthTestResponse, error error) {
 | 
			
		||||
	responseFull := &authTestResponseFull{}
 | 
			
		||||
	err := post("auth.test", url.Values{"token": {api.config.token}}, responseFull, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !responseFull.Ok {
 | 
			
		||||
		return nil, errors.New(responseFull.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return &responseFull.AuthTestResponse, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetDebug switches the api into debug mode
 | 
			
		||||
// When in debug mode, it logs various info about what its doing
 | 
			
		||||
// If you ever use this in production, don't call SetDebug(true)
 | 
			
		||||
func (api *Client) SetDebug(debug bool) {
 | 
			
		||||
	api.debug = debug
 | 
			
		||||
	if debug && logger == nil {
 | 
			
		||||
		logger = log.New(os.Stdout, "nlopes/slack", log.LstdFlags | log.Lshortfile)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api *Client) Debugf(format string, v ...interface{}) {
 | 
			
		||||
	if api.debug {
 | 
			
		||||
		logger.Printf(format, v...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api *Client) Debugln(v ...interface{}) {
 | 
			
		||||
	if api.debug {
 | 
			
		||||
		logger.Println(v...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										135
									
								
								vendor/github.com/nlopes/slack/stars.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								vendor/github.com/nlopes/slack/stars.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,135 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	DEFAULT_STARS_USER  = ""
 | 
			
		||||
	DEFAULT_STARS_COUNT = 100
 | 
			
		||||
	DEFAULT_STARS_PAGE  = 1
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type StarsParameters struct {
 | 
			
		||||
	User  string
 | 
			
		||||
	Count int
 | 
			
		||||
	Page  int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type StarredItem Item
 | 
			
		||||
 | 
			
		||||
type listResponseFull struct {
 | 
			
		||||
	Items  []Item `json:"items"`
 | 
			
		||||
	Paging `json:"paging"`
 | 
			
		||||
	SlackResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewStarsParameters initialises StarsParameters with default values
 | 
			
		||||
func NewStarsParameters() StarsParameters {
 | 
			
		||||
	return StarsParameters{
 | 
			
		||||
		User:  DEFAULT_STARS_USER,
 | 
			
		||||
		Count: DEFAULT_STARS_COUNT,
 | 
			
		||||
		Page:  DEFAULT_STARS_PAGE,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddStar stars an item in a channel
 | 
			
		||||
func (api *Client) AddStar(channel string, item ItemRef) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	if item.Timestamp != "" {
 | 
			
		||||
		values.Set("timestamp", string(item.Timestamp))
 | 
			
		||||
	}
 | 
			
		||||
	if item.File != "" {
 | 
			
		||||
		values.Set("file", string(item.File))
 | 
			
		||||
	}
 | 
			
		||||
	if item.Comment != "" {
 | 
			
		||||
		values.Set("file_comment", string(item.Comment))
 | 
			
		||||
	}
 | 
			
		||||
	response := &SlackResponse{}
 | 
			
		||||
	if err := post("stars.add", values, response, api.debug); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RemoveStar removes a starred item from a channel
 | 
			
		||||
func (api *Client) RemoveStar(channel string, item ItemRef) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"channel": {channel},
 | 
			
		||||
		"token":   {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	if item.Timestamp != "" {
 | 
			
		||||
		values.Set("timestamp", string(item.Timestamp))
 | 
			
		||||
	}
 | 
			
		||||
	if item.File != "" {
 | 
			
		||||
		values.Set("file", string(item.File))
 | 
			
		||||
	}
 | 
			
		||||
	if item.Comment != "" {
 | 
			
		||||
		values.Set("file_comment", string(item.Comment))
 | 
			
		||||
	}
 | 
			
		||||
	response := &SlackResponse{}
 | 
			
		||||
	if err := post("stars.remove", values, response, api.debug); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListStars returns information about the stars a user added
 | 
			
		||||
func (api *Client) ListStars(params StarsParameters) ([]Item, *Paging, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	if params.User != DEFAULT_STARS_USER {
 | 
			
		||||
		values.Add("user", params.User)
 | 
			
		||||
	}
 | 
			
		||||
	if params.Count != DEFAULT_STARS_COUNT {
 | 
			
		||||
		values.Add("count", strconv.Itoa(params.Count))
 | 
			
		||||
	}
 | 
			
		||||
	if params.Page != DEFAULT_STARS_PAGE {
 | 
			
		||||
		values.Add("page", strconv.Itoa(params.Page))
 | 
			
		||||
	}
 | 
			
		||||
	response := &listResponseFull{}
 | 
			
		||||
	err := post("stars.list", values, response, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, nil, errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return response.Items, &response.Paging, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetStarred returns a list of StarredItem items. The user then has to iterate over them and figure out what they should
 | 
			
		||||
// be looking at according to what is in the Type.
 | 
			
		||||
//    for _, item := range items {
 | 
			
		||||
//        switch c.Type {
 | 
			
		||||
//        case "file_comment":
 | 
			
		||||
//            log.Println(c.Comment)
 | 
			
		||||
//        case "file":
 | 
			
		||||
//             ...
 | 
			
		||||
//
 | 
			
		||||
//    }
 | 
			
		||||
// This function still exists to maintain backwards compatibility.
 | 
			
		||||
// I exposed it as returning []StarredItem, so it shall stay as StarredItem
 | 
			
		||||
func (api *Client) GetStarred(params StarsParameters) ([]StarredItem, *Paging, error) {
 | 
			
		||||
	items, paging, err := api.ListStars(params)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	starredItems := make([]StarredItem, len(items))
 | 
			
		||||
	for i, item := range items {
 | 
			
		||||
		starredItems[i] = StarredItem(item)
 | 
			
		||||
	}
 | 
			
		||||
	return starredItems, paging, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								vendor/github.com/nlopes/slack/team.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								vendor/github.com/nlopes/slack/team.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/url"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type TeamResponse struct {
 | 
			
		||||
	Team TeamInfo `json:"team"`
 | 
			
		||||
	SlackResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type TeamInfo struct {
 | 
			
		||||
	ID          string                 `json:"id"`
 | 
			
		||||
	Name        string                 `json:"name"`
 | 
			
		||||
	Domain      string                 `json:"domain"`
 | 
			
		||||
	EmailDomain string                 `json:"email_domain"`
 | 
			
		||||
	Icon        map[string]interface{} `json:"icon"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func teamRequest(path string, values url.Values, debug bool) (*TeamResponse, error) {
 | 
			
		||||
	response := &TeamResponse{}
 | 
			
		||||
	err := post(path, values, response, debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return response, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTeamInfo gets the Team Information of the user
 | 
			
		||||
func (api *Client) GetTeamInfo() (*TeamInfo, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	response, err := teamRequest("team.info", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.Team, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										140
									
								
								vendor/github.com/nlopes/slack/users.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								vendor/github.com/nlopes/slack/users.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,140 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/url"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// User contains all the information of a user
 | 
			
		||||
type User struct {
 | 
			
		||||
	ID                string      `json:"id"`
 | 
			
		||||
	Name              string      `json:"name"`
 | 
			
		||||
	Deleted           bool        `json:"deleted"`
 | 
			
		||||
	Color             string      `json:"color"`
 | 
			
		||||
	RealName          string      `json:"real_name"`
 | 
			
		||||
	TZ                string      `json:"tz,omitempty"`
 | 
			
		||||
	TZLabel           string      `json:"tz_label"`
 | 
			
		||||
	TZOffset          int         `json:"tz_offset"`
 | 
			
		||||
	Profile           UserProfile `json:"profile"`
 | 
			
		||||
	IsBot             bool        `json:"is_bot"`
 | 
			
		||||
	IsAdmin           bool        `json:"is_admin"`
 | 
			
		||||
	IsOwner           bool        `json:"is_owner"`
 | 
			
		||||
	IsPrimaryOwner    bool        `json:"is_primary_owner"`
 | 
			
		||||
	IsRestricted      bool        `json:"is_restricted"`
 | 
			
		||||
	IsUltraRestricted bool        `json:"is_ultra_restricted"`
 | 
			
		||||
	Has2FA            bool        `json:"has_2fa"`
 | 
			
		||||
	HasFiles          bool        `json:"has_files"`
 | 
			
		||||
	Presence          string      `json:"presence"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UserPresence contains details about a user online status
 | 
			
		||||
type UserPresence struct {
 | 
			
		||||
	Presence        string   `json:"presence,omitempty"`
 | 
			
		||||
	Online          bool     `json:"online,omitempty"`
 | 
			
		||||
	AutoAway        bool     `json:"auto_away,omitempty"`
 | 
			
		||||
	ManualAway      bool     `json:"manual_away,omitempty"`
 | 
			
		||||
	ConnectionCount int      `json:"connection_count,omitempty"`
 | 
			
		||||
	LastActivity    JSONTime `json:"last_activity,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type userResponseFull struct {
 | 
			
		||||
	Members      []User                  `json:"members,omitempty"` // ListUsers
 | 
			
		||||
	User         `json:"user,omitempty"` // GetUserInfo
 | 
			
		||||
	UserPresence                         // GetUserPresence
 | 
			
		||||
	SlackResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func userRequest(path string, values url.Values, debug bool) (*userResponseFull, error) {
 | 
			
		||||
	response := &userResponseFull{}
 | 
			
		||||
	err := post(path, values, response, debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if !response.Ok {
 | 
			
		||||
		return nil, errors.New(response.Error)
 | 
			
		||||
	}
 | 
			
		||||
	return response, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetUserPresence will retrieve the current presence status of given user.
 | 
			
		||||
func (api *Client) GetUserPresence(user string) (*UserPresence, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
		"user":  {user},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := userRequest("users.getPresence", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.UserPresence, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetUserInfo will retrive the complete user information
 | 
			
		||||
func (api *Client) GetUserInfo(user string) (*User, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
		"user":  {user},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := userRequest("users.info", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &response.User, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetUsers returns the list of users (with their detailed information)
 | 
			
		||||
func (api *Client) GetUsers() ([]User, error) {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":    {api.config.token},
 | 
			
		||||
		"presence": {"1"},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := userRequest("users.list", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return response.Members, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetUserAsActive marks the currently authenticated user as active
 | 
			
		||||
func (api *Client) SetUserAsActive() error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token": {api.config.token},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := userRequest("users.setActive", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetUserPresence changes the currently authenticated user presence
 | 
			
		||||
func (api *Client) SetUserPresence(presence string) error {
 | 
			
		||||
	values := url.Values{
 | 
			
		||||
		"token":    {api.config.token},
 | 
			
		||||
		"presence": {presence},
 | 
			
		||||
	}
 | 
			
		||||
	_, err := userRequest("users.setPresence", values, api.debug)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										93
									
								
								vendor/github.com/nlopes/slack/websocket.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								vendor/github.com/nlopes/slack/websocket.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/net/websocket"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// MaxMessageTextLength is the current maximum message length in number of characters as defined here
 | 
			
		||||
	// https://api.slack.com/rtm#limits
 | 
			
		||||
	MaxMessageTextLength = 4000
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RTM represents a managed websocket connection. It also supports
 | 
			
		||||
// all the methods of the `Client` type.
 | 
			
		||||
//
 | 
			
		||||
// Create this element with Client's NewRTM().
 | 
			
		||||
type RTM struct {
 | 
			
		||||
	idGen IDGenerator
 | 
			
		||||
	pings map[int]time.Time
 | 
			
		||||
 | 
			
		||||
	// Connection life-cycle
 | 
			
		||||
	conn             *websocket.Conn
 | 
			
		||||
	IncomingEvents   chan RTMEvent
 | 
			
		||||
	outgoingMessages chan OutgoingMessage
 | 
			
		||||
	killChannel      chan bool
 | 
			
		||||
	forcePing        chan bool
 | 
			
		||||
	rawEvents        chan json.RawMessage
 | 
			
		||||
	wasIntentional   bool
 | 
			
		||||
	isConnected      bool
 | 
			
		||||
 | 
			
		||||
	// Client is the main API, embedded
 | 
			
		||||
	Client
 | 
			
		||||
	websocketURL string
 | 
			
		||||
 | 
			
		||||
	// UserDetails upon connection
 | 
			
		||||
	info *Info
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRTM returns a RTM, which provides a fully managed connection to
 | 
			
		||||
// Slack's websocket-based Real-Time Messaging protocol.
 | 
			
		||||
func newRTM(api *Client) *RTM {
 | 
			
		||||
	return &RTM{
 | 
			
		||||
		Client:           *api,
 | 
			
		||||
		IncomingEvents:   make(chan RTMEvent, 50),
 | 
			
		||||
		outgoingMessages: make(chan OutgoingMessage, 20),
 | 
			
		||||
		pings:            make(map[int]time.Time),
 | 
			
		||||
		isConnected:      false,
 | 
			
		||||
		wasIntentional:   true,
 | 
			
		||||
		killChannel:      make(chan bool),
 | 
			
		||||
		forcePing:        make(chan bool),
 | 
			
		||||
		rawEvents:        make(chan json.RawMessage),
 | 
			
		||||
		idGen:            NewSafeID(1),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Disconnect and wait, blocking until a successful disconnection.
 | 
			
		||||
func (rtm *RTM) Disconnect() error {
 | 
			
		||||
	if !rtm.isConnected {
 | 
			
		||||
		return errors.New("Invalid call to Disconnect - Slack API is already disconnected")
 | 
			
		||||
	}
 | 
			
		||||
	rtm.killChannel <- true
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reconnect only makes sense if you've successfully disconnectd with Disconnect().
 | 
			
		||||
func (rtm *RTM) Reconnect() error {
 | 
			
		||||
	logger.Println("RTM::Reconnect not implemented!")
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetInfo returns the info structure received when calling
 | 
			
		||||
// "startrtm", holding all channels, groups and other metadata needed
 | 
			
		||||
// to implement a full chat client. It will be non-nil after a call to
 | 
			
		||||
// StartRTM().
 | 
			
		||||
func (rtm *RTM) GetInfo() *Info {
 | 
			
		||||
	return rtm.info
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendMessage submits a simple message through the websocket.  For
 | 
			
		||||
// more complicated messages, use `rtm.PostMessage` with a complete
 | 
			
		||||
// struct describing your attachments and all.
 | 
			
		||||
func (rtm *RTM) SendMessage(msg *OutgoingMessage) {
 | 
			
		||||
	if msg == nil {
 | 
			
		||||
		rtm.Debugln("Error: Attempted to SendMessage(nil)")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rtm.outgoingMessages <- *msg
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										72
									
								
								vendor/github.com/nlopes/slack/websocket_channels.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								vendor/github.com/nlopes/slack/websocket_channels.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
// ChannelCreatedEvent represents the Channel created event
 | 
			
		||||
type ChannelCreatedEvent struct {
 | 
			
		||||
	Type           string             `json:"type"`
 | 
			
		||||
	Channel        ChannelCreatedInfo `json:"channel"`
 | 
			
		||||
	EventTimestamp string             `json:"event_ts"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ChannelCreatedInfo represents the information associated with the Channel created event
 | 
			
		||||
type ChannelCreatedInfo struct {
 | 
			
		||||
	ID        string `json:"id"`
 | 
			
		||||
	IsChannel bool   `json:"is_channel"`
 | 
			
		||||
	Name      string `json:"name"`
 | 
			
		||||
	Created   int    `json:"created"`
 | 
			
		||||
	Creator   string `json:"creator"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ChannelJoinedEvent represents the Channel joined event
 | 
			
		||||
type ChannelJoinedEvent struct {
 | 
			
		||||
	Type    string  `json:"type"`
 | 
			
		||||
	Channel Channel `json:"channel"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ChannelInfoEvent represents the Channel info event
 | 
			
		||||
type ChannelInfoEvent struct {
 | 
			
		||||
	// channel_left
 | 
			
		||||
	// channel_deleted
 | 
			
		||||
	// channel_archive
 | 
			
		||||
	// channel_unarchive
 | 
			
		||||
	Type      string `json:"type"`
 | 
			
		||||
	Channel   string `json:"channel"`
 | 
			
		||||
	User      string `json:"user,omitempty"`
 | 
			
		||||
	Timestamp string `json:"ts,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ChannelRenameEvent represents the Channel rename event
 | 
			
		||||
type ChannelRenameEvent struct {
 | 
			
		||||
	Type      string            `json:"type"`
 | 
			
		||||
	Channel   ChannelRenameInfo `json:"channel"`
 | 
			
		||||
	Timestamp string            `json:"event_ts"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ChannelRenameInfo represents the information associated with a Channel rename event
 | 
			
		||||
type ChannelRenameInfo struct {
 | 
			
		||||
	ID      string `json:"id"`
 | 
			
		||||
	Name    string `json:"name"`
 | 
			
		||||
	Created string `json:"created"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ChannelHistoryChangedEvent represents the Channel history changed event
 | 
			
		||||
type ChannelHistoryChangedEvent struct {
 | 
			
		||||
	Type           string `json:"type"`
 | 
			
		||||
	Latest         string `json:"latest"`
 | 
			
		||||
	Timestamp      string `json:"ts"`
 | 
			
		||||
	EventTimestamp string `json:"event_ts"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ChannelMarkedEvent represents the Channel marked event
 | 
			
		||||
type ChannelMarkedEvent ChannelInfoEvent
 | 
			
		||||
 | 
			
		||||
// ChannelLeftEvent represents the Channel left event
 | 
			
		||||
type ChannelLeftEvent ChannelInfoEvent
 | 
			
		||||
 | 
			
		||||
// ChannelDeletedEvent represents the Channel deleted event
 | 
			
		||||
type ChannelDeletedEvent ChannelInfoEvent
 | 
			
		||||
 | 
			
		||||
// ChannelArchiveEvent represents the Channel archive event
 | 
			
		||||
type ChannelArchiveEvent ChannelInfoEvent
 | 
			
		||||
 | 
			
		||||
// ChannelUnarchiveEvent represents the Channel unarchive event
 | 
			
		||||
type ChannelUnarchiveEvent ChannelInfoEvent
 | 
			
		||||
							
								
								
									
										23
									
								
								vendor/github.com/nlopes/slack/websocket_dm.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/nlopes/slack/websocket_dm.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
// IMCreatedEvent represents the IM created event
 | 
			
		||||
type IMCreatedEvent struct {
 | 
			
		||||
	Type    string             `json:"type"`
 | 
			
		||||
	User    string             `json:"user"`
 | 
			
		||||
	Channel ChannelCreatedInfo `json:"channel"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IMHistoryChangedEvent represents the IM history changed event
 | 
			
		||||
type IMHistoryChangedEvent ChannelHistoryChangedEvent
 | 
			
		||||
 | 
			
		||||
// IMOpenEvent represents the IM open event
 | 
			
		||||
type IMOpenEvent ChannelInfoEvent
 | 
			
		||||
 | 
			
		||||
// IMCloseEvent represents the IM close event
 | 
			
		||||
type IMCloseEvent ChannelInfoEvent
 | 
			
		||||
 | 
			
		||||
// IMMarkedEvent represents the IM marked event
 | 
			
		||||
type IMMarkedEvent ChannelInfoEvent
 | 
			
		||||
 | 
			
		||||
// IMMarkedHistoryChanged represents the IM marked history changed event
 | 
			
		||||
type IMMarkedHistoryChanged ChannelInfoEvent
 | 
			
		||||
							
								
								
									
										8
									
								
								vendor/github.com/nlopes/slack/websocket_dnd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								vendor/github.com/nlopes/slack/websocket_dnd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
// DNDUpdatedEvent represents the update event for Do Not Disturb
 | 
			
		||||
type DNDUpdatedEvent struct {
 | 
			
		||||
	Type   string    `json:"type"`
 | 
			
		||||
	User   string    `json:"user"`
 | 
			
		||||
	Status DNDStatus `json:"dnd_status"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										49
									
								
								vendor/github.com/nlopes/slack/websocket_files.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								vendor/github.com/nlopes/slack/websocket_files.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
// FileActionEvent represents the File action event
 | 
			
		||||
type fileActionEvent struct {
 | 
			
		||||
	Type           string `json:"type"`
 | 
			
		||||
	EventTimestamp string `json:"event_ts"`
 | 
			
		||||
	File           File   `json:"file"`
 | 
			
		||||
	// FileID is used for FileDeletedEvent
 | 
			
		||||
	FileID string `json:"file_id,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FileCreatedEvent represents the File created event
 | 
			
		||||
type FileCreatedEvent fileActionEvent
 | 
			
		||||
 | 
			
		||||
// FileSharedEvent represents the File shared event
 | 
			
		||||
type FileSharedEvent fileActionEvent
 | 
			
		||||
 | 
			
		||||
// FilePublicEvent represents the File public event
 | 
			
		||||
type FilePublicEvent fileActionEvent
 | 
			
		||||
 | 
			
		||||
// FileUnsharedEvent represents the File unshared event
 | 
			
		||||
type FileUnsharedEvent fileActionEvent
 | 
			
		||||
 | 
			
		||||
// FileChangeEvent represents the File change event
 | 
			
		||||
type FileChangeEvent fileActionEvent
 | 
			
		||||
 | 
			
		||||
// FileDeletedEvent represents the File deleted event
 | 
			
		||||
type FileDeletedEvent fileActionEvent
 | 
			
		||||
 | 
			
		||||
// FilePrivateEvent represents the File private event
 | 
			
		||||
type FilePrivateEvent fileActionEvent
 | 
			
		||||
 | 
			
		||||
// FileCommentAddedEvent represents the File comment added event
 | 
			
		||||
type FileCommentAddedEvent struct {
 | 
			
		||||
	fileActionEvent
 | 
			
		||||
	Comment Comment `json:"comment"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FileCommentEditedEvent represents the File comment edited event
 | 
			
		||||
type FileCommentEditedEvent struct {
 | 
			
		||||
	fileActionEvent
 | 
			
		||||
	Comment Comment `json:"comment"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FileCommentDeletedEvent represents the File comment deleted event
 | 
			
		||||
type FileCommentDeletedEvent struct {
 | 
			
		||||
	fileActionEvent
 | 
			
		||||
	Comment string `json:"comment"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										49
									
								
								vendor/github.com/nlopes/slack/websocket_groups.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								vendor/github.com/nlopes/slack/websocket_groups.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
// GroupCreatedEvent represents the Group created event
 | 
			
		||||
type GroupCreatedEvent struct {
 | 
			
		||||
	Type    string             `json:"type"`
 | 
			
		||||
	User    string             `json:"user"`
 | 
			
		||||
	Channel ChannelCreatedInfo `json:"channel"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// XXX: Should we really do this? event.Group is probably nicer than event.Channel
 | 
			
		||||
// even though the api returns "channel"
 | 
			
		||||
 | 
			
		||||
// GroupMarkedEvent represents the Group marked event
 | 
			
		||||
type GroupMarkedEvent ChannelInfoEvent
 | 
			
		||||
 | 
			
		||||
// GroupOpenEvent represents the Group open event
 | 
			
		||||
type GroupOpenEvent ChannelInfoEvent
 | 
			
		||||
 | 
			
		||||
// GroupCloseEvent represents the Group close event
 | 
			
		||||
type GroupCloseEvent ChannelInfoEvent
 | 
			
		||||
 | 
			
		||||
// GroupArchiveEvent represents the Group archive event
 | 
			
		||||
type GroupArchiveEvent ChannelInfoEvent
 | 
			
		||||
 | 
			
		||||
// GroupUnarchiveEvent represents the Group unarchive event
 | 
			
		||||
type GroupUnarchiveEvent ChannelInfoEvent
 | 
			
		||||
 | 
			
		||||
// GroupLeftEvent represents the Group left event
 | 
			
		||||
type GroupLeftEvent ChannelInfoEvent
 | 
			
		||||
 | 
			
		||||
// GroupJoinedEvent represents the Group joined event
 | 
			
		||||
type GroupJoinedEvent ChannelJoinedEvent
 | 
			
		||||
 | 
			
		||||
// GroupRenameEvent represents the Group rename event
 | 
			
		||||
type GroupRenameEvent struct {
 | 
			
		||||
	Type      string          `json:"type"`
 | 
			
		||||
	Group     GroupRenameInfo `json:"channel"`
 | 
			
		||||
	Timestamp string          `json:"ts"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GroupRenameInfo represents the group info related to the renamed group
 | 
			
		||||
type GroupRenameInfo struct {
 | 
			
		||||
	ID      string `json:"id"`
 | 
			
		||||
	Name    string `json:"name"`
 | 
			
		||||
	Created string `json:"created"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GroupHistoryChangedEvent represents the Group history changed event
 | 
			
		||||
type GroupHistoryChangedEvent ChannelHistoryChangedEvent
 | 
			
		||||
							
								
								
									
										92
									
								
								vendor/github.com/nlopes/slack/websocket_internals.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								vendor/github.com/nlopes/slack/websocket_internals.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Internal events, created by this lib and not mapped to Slack APIs.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
// ConnectedEvent is used for when we connect to Slack
 | 
			
		||||
type ConnectedEvent struct {
 | 
			
		||||
	ConnectionCount int // 1 = first time, 2 = second time
 | 
			
		||||
	Info            *Info
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConnectionErrorEvent contains information about a connection error
 | 
			
		||||
type ConnectionErrorEvent struct {
 | 
			
		||||
	Attempt  int
 | 
			
		||||
	ErrorObj error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *ConnectionErrorEvent) Error() string {
 | 
			
		||||
	return c.ErrorObj.Error()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConnectingEvent contains information about our connection attempt
 | 
			
		||||
type ConnectingEvent struct {
 | 
			
		||||
	Attempt         int // 1 = first attempt, 2 = second attempt
 | 
			
		||||
	ConnectionCount int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DisconnectedEvent contains information about how we disconnected
 | 
			
		||||
type DisconnectedEvent struct {
 | 
			
		||||
	Intentional bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LatencyReport contains information about connection latency
 | 
			
		||||
type LatencyReport struct {
 | 
			
		||||
	Value time.Duration
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InvalidAuthEvent is used in case we can't even authenticate with the API
 | 
			
		||||
type InvalidAuthEvent struct{}
 | 
			
		||||
 | 
			
		||||
// UnmarshallingErrorEvent is used when there are issues deconstructing a response
 | 
			
		||||
type UnmarshallingErrorEvent struct {
 | 
			
		||||
	ErrorObj error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u UnmarshallingErrorEvent) Error() string {
 | 
			
		||||
	return u.ErrorObj.Error()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MessageTooLongEvent is used when sending a message that is too long
 | 
			
		||||
type MessageTooLongEvent struct {
 | 
			
		||||
	Message   OutgoingMessage
 | 
			
		||||
	MaxLength int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MessageTooLongEvent) Error() string {
 | 
			
		||||
	return fmt.Sprintf("Message too long (max %d characters)", m.MaxLength)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OutgoingErrorEvent contains information in case there were errors sending messages
 | 
			
		||||
type OutgoingErrorEvent struct {
 | 
			
		||||
	Message  OutgoingMessage
 | 
			
		||||
	ErrorObj error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o OutgoingErrorEvent) Error() string {
 | 
			
		||||
	return o.ErrorObj.Error()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IncomingEventError contains information about an unexpected error receiving a websocket event
 | 
			
		||||
type IncomingEventError struct {
 | 
			
		||||
	ErrorObj error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (i *IncomingEventError) Error() string {
 | 
			
		||||
	return i.ErrorObj.Error()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AckErrorEvent i
 | 
			
		||||
type AckErrorEvent struct {
 | 
			
		||||
	ErrorObj error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *AckErrorEvent) Error() string {
 | 
			
		||||
	return a.ErrorObj.Error()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										427
									
								
								vendor/github.com/nlopes/slack/websocket_managed_conn.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										427
									
								
								vendor/github.com/nlopes/slack/websocket_managed_conn.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,427 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/net/websocket"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ManageConnection can be called on a Slack RTM instance returned by the
 | 
			
		||||
// NewRTM method. It will connect to the slack RTM API and handle all incoming
 | 
			
		||||
// and outgoing events. If a connection fails then it will attempt to reconnect
 | 
			
		||||
// and will notify any listeners through an error event on the IncomingEvents
 | 
			
		||||
// channel.
 | 
			
		||||
//
 | 
			
		||||
// If the connection ends and the disconnect was unintentional then this will
 | 
			
		||||
// attempt to reconnect.
 | 
			
		||||
//
 | 
			
		||||
// This should only be called once per slack API! Otherwise expect undefined
 | 
			
		||||
// behavior.
 | 
			
		||||
//
 | 
			
		||||
// The defined error events are located in websocket_internals.go.
 | 
			
		||||
func (rtm *RTM) ManageConnection() {
 | 
			
		||||
	var connectionCount int
 | 
			
		||||
	for {
 | 
			
		||||
		connectionCount++
 | 
			
		||||
		// start trying to connect
 | 
			
		||||
		// the returned err is already passed onto the IncomingEvents channel
 | 
			
		||||
		info, conn, err := rtm.connect(connectionCount)
 | 
			
		||||
		// if err != nil then the connection is sucessful - otherwise it is
 | 
			
		||||
		// fatal
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		rtm.info = info
 | 
			
		||||
		rtm.IncomingEvents <- RTMEvent{"connected", &ConnectedEvent{
 | 
			
		||||
			ConnectionCount: connectionCount,
 | 
			
		||||
			Info:            info,
 | 
			
		||||
		}}
 | 
			
		||||
 | 
			
		||||
		rtm.conn = conn
 | 
			
		||||
		rtm.isConnected = true
 | 
			
		||||
 | 
			
		||||
		keepRunning := make(chan bool)
 | 
			
		||||
		// we're now connected (or have failed fatally) so we can set up
 | 
			
		||||
		// listeners
 | 
			
		||||
		go rtm.handleIncomingEvents(keepRunning)
 | 
			
		||||
 | 
			
		||||
		// this should be a blocking call until the connection has ended
 | 
			
		||||
		rtm.handleEvents(keepRunning, 30*time.Second)
 | 
			
		||||
 | 
			
		||||
		// after being disconnected we need to check if it was intentional
 | 
			
		||||
		// if not then we should try to reconnect
 | 
			
		||||
		if rtm.wasIntentional {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		// else continue and run the loop again to connect
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// connect attempts to connect to the slack websocket API. It handles any
 | 
			
		||||
// errors that occur while connecting and will return once a connection
 | 
			
		||||
// has been successfully opened.
 | 
			
		||||
func (rtm *RTM) connect(connectionCount int) (*Info, *websocket.Conn, error) {
 | 
			
		||||
	// used to provide exponential backoff wait time with jitter before trying
 | 
			
		||||
	// to connect to slack again
 | 
			
		||||
	boff := &backoff{
 | 
			
		||||
		Min:    100 * time.Millisecond,
 | 
			
		||||
		Max:    5 * time.Minute,
 | 
			
		||||
		Factor: 2,
 | 
			
		||||
		Jitter: true,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		// send connecting event
 | 
			
		||||
		rtm.IncomingEvents <- RTMEvent{"connecting", &ConnectingEvent{
 | 
			
		||||
			Attempt:         boff.attempts + 1,
 | 
			
		||||
			ConnectionCount: connectionCount,
 | 
			
		||||
		}}
 | 
			
		||||
		// attempt to start the connection
 | 
			
		||||
		info, conn, err := rtm.startRTMAndDial()
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			return info, conn, nil
 | 
			
		||||
		}
 | 
			
		||||
		// check for fatal errors - currently only invalid_auth
 | 
			
		||||
		if sErr, ok := err.(*WebError); ok && (sErr.Error() == "invalid_auth" || sErr.Error() == "account_inactive") {
 | 
			
		||||
			rtm.IncomingEvents <- RTMEvent{"invalid_auth", &InvalidAuthEvent{}}
 | 
			
		||||
			return nil, nil, sErr
 | 
			
		||||
		}
 | 
			
		||||
		// any other errors are treated as recoverable and we try again after
 | 
			
		||||
		// sending the event along the IncomingEvents channel
 | 
			
		||||
		rtm.IncomingEvents <- RTMEvent{"connection_error", &ConnectionErrorEvent{
 | 
			
		||||
			Attempt:  boff.attempts,
 | 
			
		||||
			ErrorObj: err,
 | 
			
		||||
		}}
 | 
			
		||||
		// get time we should wait before attempting to connect again
 | 
			
		||||
		dur := boff.Duration()
 | 
			
		||||
		rtm.Debugf("reconnection %d failed: %s", boff.attempts+1, err)
 | 
			
		||||
		rtm.Debugln(" -> reconnecting in", dur)
 | 
			
		||||
		time.Sleep(dur)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// startRTMAndDial attemps to connect to the slack websocket. It returns the
 | 
			
		||||
// full information returned by the "rtm.start" method on the slack API.
 | 
			
		||||
func (rtm *RTM) startRTMAndDial() (*Info, *websocket.Conn, error) {
 | 
			
		||||
	info, url, err := rtm.StartRTM()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn, err := websocketProxyDial(url, "http://api.slack.com")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return info, conn, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// killConnection stops the websocket connection and signals to all goroutines
 | 
			
		||||
// that they should cease listening to the connection for events.
 | 
			
		||||
//
 | 
			
		||||
// This should not be called directly! Instead a boolean value (true for
 | 
			
		||||
// intentional, false otherwise) should be sent to the killChannel on the RTM.
 | 
			
		||||
func (rtm *RTM) killConnection(keepRunning chan bool, intentional bool) error {
 | 
			
		||||
	rtm.Debugln("killing connection")
 | 
			
		||||
	if rtm.isConnected {
 | 
			
		||||
		close(keepRunning)
 | 
			
		||||
	}
 | 
			
		||||
	rtm.isConnected = false
 | 
			
		||||
	rtm.wasIntentional = intentional
 | 
			
		||||
	err := rtm.conn.Close()
 | 
			
		||||
	rtm.IncomingEvents <- RTMEvent{"disconnected", &DisconnectedEvent{intentional}}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handleEvents is a blocking function that handles all events. This sends
 | 
			
		||||
// pings when asked to (on rtm.forcePing) and upon every given elapsed
 | 
			
		||||
// interval. This also sends outgoing messages that are received from the RTM's
 | 
			
		||||
// outgoingMessages channel. This also handles incoming raw events from the RTM
 | 
			
		||||
// rawEvents channel.
 | 
			
		||||
func (rtm *RTM) handleEvents(keepRunning chan bool, interval time.Duration) {
 | 
			
		||||
	ticker := time.NewTicker(interval)
 | 
			
		||||
	defer ticker.Stop()
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		// catch "stop" signal on channel close
 | 
			
		||||
		case intentional := <-rtm.killChannel:
 | 
			
		||||
			_ = rtm.killConnection(keepRunning, intentional)
 | 
			
		||||
			return
 | 
			
		||||
			// send pings on ticker interval
 | 
			
		||||
		case <-ticker.C:
 | 
			
		||||
			err := rtm.ping()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				_ = rtm.killConnection(keepRunning, false)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		case <-rtm.forcePing:
 | 
			
		||||
			err := rtm.ping()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				_ = rtm.killConnection(keepRunning, false)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		// listen for messages that need to be sent
 | 
			
		||||
		case msg := <-rtm.outgoingMessages:
 | 
			
		||||
			rtm.sendOutgoingMessage(msg)
 | 
			
		||||
		// listen for incoming messages that need to be parsed
 | 
			
		||||
		case rawEvent := <-rtm.rawEvents:
 | 
			
		||||
			rtm.handleRawEvent(rawEvent)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handleIncomingEvents monitors the RTM's opened websocket for any incoming
 | 
			
		||||
// events. It pushes the raw events onto the RTM channel rawEvents.
 | 
			
		||||
//
 | 
			
		||||
// This will stop executing once the RTM's keepRunning channel has been closed
 | 
			
		||||
// or has anything sent to it.
 | 
			
		||||
func (rtm *RTM) handleIncomingEvents(keepRunning <-chan bool) {
 | 
			
		||||
	for {
 | 
			
		||||
		// non-blocking listen to see if channel is closed
 | 
			
		||||
		select {
 | 
			
		||||
		// catch "stop" signal on channel close
 | 
			
		||||
		case <-keepRunning:
 | 
			
		||||
			return
 | 
			
		||||
		default:
 | 
			
		||||
			rtm.receiveIncomingEvent()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// sendOutgoingMessage sends the given OutgoingMessage to the slack websocket.
 | 
			
		||||
//
 | 
			
		||||
// It does not currently detect if a outgoing message fails due to a disconnect
 | 
			
		||||
// and instead lets a future failed 'PING' detect the failed connection.
 | 
			
		||||
func (rtm *RTM) sendOutgoingMessage(msg OutgoingMessage) {
 | 
			
		||||
	rtm.Debugln("Sending message:", msg)
 | 
			
		||||
	if len(msg.Text) > MaxMessageTextLength {
 | 
			
		||||
		rtm.IncomingEvents <- RTMEvent{"outgoing_error", &MessageTooLongEvent{
 | 
			
		||||
			Message:   msg,
 | 
			
		||||
			MaxLength: MaxMessageTextLength,
 | 
			
		||||
		}}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	err := websocket.JSON.Send(rtm.conn, msg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		rtm.IncomingEvents <- RTMEvent{"outgoing_error", &OutgoingErrorEvent{
 | 
			
		||||
			Message:  msg,
 | 
			
		||||
			ErrorObj: err,
 | 
			
		||||
		}}
 | 
			
		||||
		// TODO force ping?
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ping sends a 'PING' message to the RTM's websocket. If the 'PING' message
 | 
			
		||||
// fails to send then this returns an error signifying that the connection
 | 
			
		||||
// should be considered disconnected.
 | 
			
		||||
//
 | 
			
		||||
// This does not handle incoming 'PONG' responses but does store the time of
 | 
			
		||||
// each successful 'PING' send so latency can be detected upon a 'PONG'
 | 
			
		||||
// response.
 | 
			
		||||
func (rtm *RTM) ping() error {
 | 
			
		||||
	id := rtm.idGen.Next()
 | 
			
		||||
	rtm.Debugln("Sending PING ", id)
 | 
			
		||||
	rtm.pings[id] = time.Now()
 | 
			
		||||
 | 
			
		||||
	msg := &Ping{ID: id, Type: "ping"}
 | 
			
		||||
	err := websocket.JSON.Send(rtm.conn, msg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		rtm.Debugf("RTM Error sending 'PING %d': %s", id, err.Error())
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// receiveIncomingEvent attempts to receive an event from the RTM's websocket.
 | 
			
		||||
// 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)
 | 
			
		||||
	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
 | 
			
		||||
		// 'PING' message
 | 
			
		||||
 | 
			
		||||
		// trigger a 'PING' to detect pontential websocket disconnect
 | 
			
		||||
		rtm.forcePing <- true
 | 
			
		||||
		return
 | 
			
		||||
	} else if err != nil {
 | 
			
		||||
		rtm.IncomingEvents <- RTMEvent{"incoming_error", &IncomingEventError{
 | 
			
		||||
			ErrorObj: err,
 | 
			
		||||
		}}
 | 
			
		||||
		// force a ping here too?
 | 
			
		||||
		return
 | 
			
		||||
	} else if len(event) == 0 {
 | 
			
		||||
		rtm.Debugln("Received empty event")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	rtm.Debugln("Incoming Event:", string(event[:]))
 | 
			
		||||
	rtm.rawEvents <- event
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handleRawEvent takes a raw JSON message received from the slack websocket
 | 
			
		||||
// and handles the encoded event.
 | 
			
		||||
func (rtm *RTM) handleRawEvent(rawEvent json.RawMessage) {
 | 
			
		||||
	event := &Event{}
 | 
			
		||||
	err := json.Unmarshal(rawEvent, event)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		rtm.IncomingEvents <- RTMEvent{"unmarshalling_error", &UnmarshallingErrorEvent{err}}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	switch event.Type {
 | 
			
		||||
	case "":
 | 
			
		||||
		rtm.handleAck(rawEvent)
 | 
			
		||||
	case "hello":
 | 
			
		||||
		rtm.IncomingEvents <- RTMEvent{"hello", &HelloEvent{}}
 | 
			
		||||
	case "pong":
 | 
			
		||||
		rtm.handlePong(rawEvent)
 | 
			
		||||
	default:
 | 
			
		||||
		rtm.handleEvent(event.Type, rawEvent)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handleAck handles an incoming 'ACK' message.
 | 
			
		||||
func (rtm *RTM) handleAck(event json.RawMessage) {
 | 
			
		||||
	ack := &AckMessage{}
 | 
			
		||||
	if err := json.Unmarshal(event, ack); err != nil {
 | 
			
		||||
		rtm.Debugln("RTM Error unmarshalling 'ack' event:", err)
 | 
			
		||||
		rtm.Debugln(" -> Erroneous 'ack' event:", string(event))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if ack.Ok {
 | 
			
		||||
		rtm.IncomingEvents <- RTMEvent{"ack", ack}
 | 
			
		||||
	} else {
 | 
			
		||||
		rtm.IncomingEvents <- RTMEvent{"ack_error", &AckErrorEvent{ack.Error}}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handlePong handles an incoming 'PONG' message which should be in response to
 | 
			
		||||
// a previously sent 'PING' message. This is then used to compute the
 | 
			
		||||
// connection's latency.
 | 
			
		||||
func (rtm *RTM) handlePong(event json.RawMessage) {
 | 
			
		||||
	pong := &Pong{}
 | 
			
		||||
	if err := json.Unmarshal(event, pong); err != nil {
 | 
			
		||||
		rtm.Debugln("RTM Error unmarshalling 'pong' event:", err)
 | 
			
		||||
		rtm.Debugln(" -> Erroneous 'ping' event:", string(event))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if pingTime, exists := rtm.pings[pong.ReplyTo]; exists {
 | 
			
		||||
		latency := time.Since(pingTime)
 | 
			
		||||
		rtm.IncomingEvents <- RTMEvent{"latency_report", &LatencyReport{Value: latency}}
 | 
			
		||||
		delete(rtm.pings, pong.ReplyTo)
 | 
			
		||||
	} else {
 | 
			
		||||
		rtm.Debugln("RTM Error - unmatched 'pong' event:", string(event))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// handleEvent is the "default" response to an event that does not have a
 | 
			
		||||
// special case. It matches the command's name to a mapping of defined events
 | 
			
		||||
// and then sends the corresponding event struct to the IncomingEvents channel.
 | 
			
		||||
// If the event type is not found or the event cannot be unmarshalled into the
 | 
			
		||||
// correct struct then this sends an UnmarshallingErrorEvent to the
 | 
			
		||||
// IncomingEvents channel.
 | 
			
		||||
func (rtm *RTM) handleEvent(typeStr string, event json.RawMessage) {
 | 
			
		||||
	v, exists := eventMapping[typeStr]
 | 
			
		||||
	if !exists {
 | 
			
		||||
		rtm.Debugf("RTM Error, received unmapped event %q: %s\n", typeStr, string(event))
 | 
			
		||||
		err := fmt.Errorf("RTM Error: Received unmapped event %q: %s\n", typeStr, string(event))
 | 
			
		||||
		rtm.IncomingEvents <- RTMEvent{"unmarshalling_error", &UnmarshallingErrorEvent{err}}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	t := reflect.TypeOf(v)
 | 
			
		||||
	recvEvent := reflect.New(t).Interface()
 | 
			
		||||
	err := json.Unmarshal(event, recvEvent)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		rtm.Debugf("RTM Error, could not unmarshall event %q: %s\n", typeStr, string(event))
 | 
			
		||||
		err := fmt.Errorf("RTM Error: Could not unmarshall event %q: %s\n", typeStr, string(event))
 | 
			
		||||
		rtm.IncomingEvents <- RTMEvent{"unmarshalling_error", &UnmarshallingErrorEvent{err}}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	rtm.IncomingEvents <- RTMEvent{typeStr, recvEvent}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// eventMapping holds a mapping of event names to their corresponding struct
 | 
			
		||||
// implementations. The structs should be instances of the unmarshalling
 | 
			
		||||
// target for the matching event type.
 | 
			
		||||
var eventMapping = map[string]interface{}{
 | 
			
		||||
	"message":         MessageEvent{},
 | 
			
		||||
	"presence_change": PresenceChangeEvent{},
 | 
			
		||||
	"user_typing":     UserTypingEvent{},
 | 
			
		||||
 | 
			
		||||
	"channel_marked":          ChannelMarkedEvent{},
 | 
			
		||||
	"channel_created":         ChannelCreatedEvent{},
 | 
			
		||||
	"channel_joined":          ChannelJoinedEvent{},
 | 
			
		||||
	"channel_left":            ChannelLeftEvent{},
 | 
			
		||||
	"channel_deleted":         ChannelDeletedEvent{},
 | 
			
		||||
	"channel_rename":          ChannelRenameEvent{},
 | 
			
		||||
	"channel_archive":         ChannelArchiveEvent{},
 | 
			
		||||
	"channel_unarchive":       ChannelUnarchiveEvent{},
 | 
			
		||||
	"channel_history_changed": ChannelHistoryChangedEvent{},
 | 
			
		||||
 | 
			
		||||
	"dnd_updated":      DNDUpdatedEvent{},
 | 
			
		||||
	"dnd_updated_user": DNDUpdatedEvent{},
 | 
			
		||||
 | 
			
		||||
	"im_created":         IMCreatedEvent{},
 | 
			
		||||
	"im_open":            IMOpenEvent{},
 | 
			
		||||
	"im_close":           IMCloseEvent{},
 | 
			
		||||
	"im_marked":          IMMarkedEvent{},
 | 
			
		||||
	"im_history_changed": IMHistoryChangedEvent{},
 | 
			
		||||
 | 
			
		||||
	"group_marked":          GroupMarkedEvent{},
 | 
			
		||||
	"group_open":            GroupOpenEvent{},
 | 
			
		||||
	"group_joined":          GroupJoinedEvent{},
 | 
			
		||||
	"group_left":            GroupLeftEvent{},
 | 
			
		||||
	"group_close":           GroupCloseEvent{},
 | 
			
		||||
	"group_rename":          GroupRenameEvent{},
 | 
			
		||||
	"group_archive":         GroupArchiveEvent{},
 | 
			
		||||
	"group_unarchive":       GroupUnarchiveEvent{},
 | 
			
		||||
	"group_history_changed": GroupHistoryChangedEvent{},
 | 
			
		||||
 | 
			
		||||
	"file_created":         FileCreatedEvent{},
 | 
			
		||||
	"file_shared":          FileSharedEvent{},
 | 
			
		||||
	"file_unshared":        FileUnsharedEvent{},
 | 
			
		||||
	"file_public":          FilePublicEvent{},
 | 
			
		||||
	"file_private":         FilePrivateEvent{},
 | 
			
		||||
	"file_change":          FileChangeEvent{},
 | 
			
		||||
	"file_deleted":         FileDeletedEvent{},
 | 
			
		||||
	"file_comment_added":   FileCommentAddedEvent{},
 | 
			
		||||
	"file_comment_edited":  FileCommentEditedEvent{},
 | 
			
		||||
	"file_comment_deleted": FileCommentDeletedEvent{},
 | 
			
		||||
 | 
			
		||||
	"pin_added":   PinAddedEvent{},
 | 
			
		||||
	"pin_removed": PinRemovedEvent{},
 | 
			
		||||
 | 
			
		||||
	"star_added":   StarAddedEvent{},
 | 
			
		||||
	"star_removed": StarRemovedEvent{},
 | 
			
		||||
 | 
			
		||||
	"reaction_added":   ReactionAddedEvent{},
 | 
			
		||||
	"reaction_removed": ReactionRemovedEvent{},
 | 
			
		||||
 | 
			
		||||
	"pref_change": PrefChangeEvent{},
 | 
			
		||||
 | 
			
		||||
	"team_join":              TeamJoinEvent{},
 | 
			
		||||
	"team_rename":            TeamRenameEvent{},
 | 
			
		||||
	"team_pref_change":       TeamPrefChangeEvent{},
 | 
			
		||||
	"team_domain_change":     TeamDomainChangeEvent{},
 | 
			
		||||
	"team_migration_started": TeamMigrationStartedEvent{},
 | 
			
		||||
 | 
			
		||||
	"manual_presence_change": ManualPresenceChangeEvent{},
 | 
			
		||||
 | 
			
		||||
	"user_change": UserChangeEvent{},
 | 
			
		||||
 | 
			
		||||
	"emoji_changed": EmojiChangedEvent{},
 | 
			
		||||
 | 
			
		||||
	"commands_changed": CommandsChangedEvent{},
 | 
			
		||||
 | 
			
		||||
	"email_domain_changed": EmailDomainChangedEvent{},
 | 
			
		||||
 | 
			
		||||
	"bot_added":   BotAddedEvent{},
 | 
			
		||||
	"bot_changed": BotChangedEvent{},
 | 
			
		||||
 | 
			
		||||
	"accounts_changed": AccountsChangedEvent{},
 | 
			
		||||
 | 
			
		||||
	"reconnect_url": ReconnectUrlEvent{},
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										117
									
								
								vendor/github.com/nlopes/slack/websocket_misc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								vendor/github.com/nlopes/slack/websocket_misc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,117 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AckMessage is used for messages received in reply to other messages
 | 
			
		||||
type AckMessage struct {
 | 
			
		||||
	ReplyTo   int    `json:"reply_to"`
 | 
			
		||||
	Timestamp string `json:"ts"`
 | 
			
		||||
	Text      string `json:"text"`
 | 
			
		||||
	RTMResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RTMResponse encapsulates response details as returned by the Slack API
 | 
			
		||||
type RTMResponse struct {
 | 
			
		||||
	Ok    bool      `json:"ok"`
 | 
			
		||||
	Error *RTMError `json:"error"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RTMError encapsulates error information as returned by the Slack API
 | 
			
		||||
type RTMError struct {
 | 
			
		||||
	Code int
 | 
			
		||||
	Msg  string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s RTMError) Error() string {
 | 
			
		||||
	return fmt.Sprintf("Code %d - %s", s.Code, s.Msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MessageEvent represents a Slack Message (used as the event type for an incoming message)
 | 
			
		||||
type MessageEvent Message
 | 
			
		||||
 | 
			
		||||
// RTMEvent is the main wrapper. You will find all the other messages attached
 | 
			
		||||
type RTMEvent struct {
 | 
			
		||||
	Type string
 | 
			
		||||
	Data interface{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HelloEvent represents the hello event
 | 
			
		||||
type HelloEvent struct{}
 | 
			
		||||
 | 
			
		||||
// PresenceChangeEvent represents the presence change event
 | 
			
		||||
type PresenceChangeEvent struct {
 | 
			
		||||
	Type     string `json:"type"`
 | 
			
		||||
	Presence string `json:"presence"`
 | 
			
		||||
	User     string `json:"user"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UserTypingEvent represents the user typing event
 | 
			
		||||
type UserTypingEvent struct {
 | 
			
		||||
	Type    string `json:"type"`
 | 
			
		||||
	User    string `json:"user"`
 | 
			
		||||
	Channel string `json:"channel"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PrefChangeEvent represents a user preferences change event
 | 
			
		||||
type PrefChangeEvent struct {
 | 
			
		||||
	Type  string          `json:"type"`
 | 
			
		||||
	Name  string          `json:"name"`
 | 
			
		||||
	Value json.RawMessage `json:"value"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ManualPresenceChangeEvent represents the manual presence change event
 | 
			
		||||
type ManualPresenceChangeEvent struct {
 | 
			
		||||
	Type     string `json:"type"`
 | 
			
		||||
	Presence string `json:"presence"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UserChangeEvent represents the user change event
 | 
			
		||||
type UserChangeEvent struct {
 | 
			
		||||
	Type string `json:"type"`
 | 
			
		||||
	User User   `json:"user"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EmojiChangedEvent represents the emoji changed event
 | 
			
		||||
type EmojiChangedEvent struct {
 | 
			
		||||
	Type           string `json:"type"`
 | 
			
		||||
	EventTimestamp string `json:"event_ts"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CommandsChangedEvent represents the commands changed event
 | 
			
		||||
type CommandsChangedEvent struct {
 | 
			
		||||
	Type           string `json:"type"`
 | 
			
		||||
	EventTimestamp string `json:"event_ts"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EmailDomainChangedEvent represents the email domain changed event
 | 
			
		||||
type EmailDomainChangedEvent struct {
 | 
			
		||||
	Type           string `json:"type"`
 | 
			
		||||
	EventTimestamp string `json:"event_ts"`
 | 
			
		||||
	EmailDomain    string `json:"email_domain"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BotAddedEvent represents the bot added event
 | 
			
		||||
type BotAddedEvent struct {
 | 
			
		||||
	Type string `json:"type"`
 | 
			
		||||
	Bot  Bot    `json:"bot"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BotChangedEvent represents the bot changed event
 | 
			
		||||
type BotChangedEvent struct {
 | 
			
		||||
	Type string `json:"type"`
 | 
			
		||||
	Bot  Bot    `json:"bot"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AccountsChangedEvent represents the accounts changed event
 | 
			
		||||
type AccountsChangedEvent struct {
 | 
			
		||||
	Type string `json:"type"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReconnectUrlEvent represents the receiving reconnect url event
 | 
			
		||||
type ReconnectUrlEvent struct {
 | 
			
		||||
	Type string `json:"type"`
 | 
			
		||||
	URL  string `json:"url"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								vendor/github.com/nlopes/slack/websocket_pins.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								vendor/github.com/nlopes/slack/websocket_pins.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
type pinEvent struct {
 | 
			
		||||
	Type           string `json:"type"`
 | 
			
		||||
	User           string `json:"user"`
 | 
			
		||||
	Item           Item   `json:"item"`
 | 
			
		||||
	Channel        string `json:"channel_id"`
 | 
			
		||||
	EventTimestamp string `json:"event_ts"`
 | 
			
		||||
	HasPins        bool   `json:"has_pins,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PinAddedEvent represents the Pin added event
 | 
			
		||||
type PinAddedEvent pinEvent
 | 
			
		||||
 | 
			
		||||
// PinRemovedEvent represents the Pin removed event
 | 
			
		||||
type PinRemovedEvent pinEvent
 | 
			
		||||
							
								
								
									
										83
									
								
								vendor/github.com/nlopes/slack/websocket_proxy.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								vendor/github.com/nlopes/slack/websocket_proxy.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
			
		||||
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)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								vendor/github.com/nlopes/slack/websocket_reactions.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/nlopes/slack/websocket_reactions.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
// reactionItem is a lighter-weight item than is returned by the reactions list.
 | 
			
		||||
type reactionItem struct {
 | 
			
		||||
	Type        string `json:"type"`
 | 
			
		||||
	Channel     string `json:"channel,omitempty"`
 | 
			
		||||
	File        string `json:"file,omitempty"`
 | 
			
		||||
	FileComment string `json:"file_comment,omitempty"`
 | 
			
		||||
	Timestamp   string `json:"ts,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type reactionEvent struct {
 | 
			
		||||
	Type           string       `json:"type"`
 | 
			
		||||
	User           string       `json:"user"`
 | 
			
		||||
	ItemUser       string       `json:"item_user"`
 | 
			
		||||
	Item           reactionItem `json:"item"`
 | 
			
		||||
	Reaction       string       `json:"reaction"`
 | 
			
		||||
	EventTimestamp string       `json:"event_ts"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReactionAddedEvent represents the Reaction added event
 | 
			
		||||
type ReactionAddedEvent reactionEvent
 | 
			
		||||
 | 
			
		||||
// ReactionRemovedEvent represents the Reaction removed event
 | 
			
		||||
type ReactionRemovedEvent reactionEvent
 | 
			
		||||
							
								
								
									
										14
									
								
								vendor/github.com/nlopes/slack/websocket_stars.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/nlopes/slack/websocket_stars.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
type starEvent struct {
 | 
			
		||||
	Type           string      `json:"type"`
 | 
			
		||||
	User           string      `json:"user"`
 | 
			
		||||
	Item           StarredItem `json:"item"`
 | 
			
		||||
	EventTimestamp string      `json:"event_ts"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StarAddedEvent represents the Star added event
 | 
			
		||||
type StarAddedEvent starEvent
 | 
			
		||||
 | 
			
		||||
// StarRemovedEvent represents the Star removed event
 | 
			
		||||
type StarRemovedEvent starEvent
 | 
			
		||||
							
								
								
									
										33
									
								
								vendor/github.com/nlopes/slack/websocket_teams.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/nlopes/slack/websocket_teams.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
package slack
 | 
			
		||||
 | 
			
		||||
// TeamJoinEvent represents the Team join event
 | 
			
		||||
type TeamJoinEvent struct {
 | 
			
		||||
	Type string `json:"type"`
 | 
			
		||||
	User User   `json:"user"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TeamRenameEvent represents the Team rename event
 | 
			
		||||
type TeamRenameEvent struct {
 | 
			
		||||
	Type           string `json:"type"`
 | 
			
		||||
	Name           string `json:"name,omitempty"`
 | 
			
		||||
	EventTimestamp string `json:"event_ts,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TeamPrefChangeEvent represents the Team preference change event
 | 
			
		||||
type TeamPrefChangeEvent struct {
 | 
			
		||||
	Type  string   `json:"type"`
 | 
			
		||||
	Name  string   `json:"name,omitempty"`
 | 
			
		||||
	Value []string `json:"value,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TeamDomainChangeEvent represents the Team domain change event
 | 
			
		||||
type TeamDomainChangeEvent struct {
 | 
			
		||||
	Type   string `json:"type"`
 | 
			
		||||
	URL    string `json:"url"`
 | 
			
		||||
	Domain string `json:"domain"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TeamMigrationStartedEvent represents the Team migration started event
 | 
			
		||||
type TeamMigrationStartedEvent struct {
 | 
			
		||||
	Type string `json:"type"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								vendor/github.com/nlopes/slack/websocket_utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/nlopes/slack/websocket_utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								vendor/golang.org/x/net/websocket/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/golang.org/x/net/websocket/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
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.
 | 
			
		||||
							
								
								
									
										113
									
								
								vendor/golang.org/x/net/websocket/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								vendor/golang.org/x/net/websocket/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,113 @@
 | 
			
		||||
// 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"
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"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}
 | 
			
		||||
	}
 | 
			
		||||
	switch config.Location.Scheme {
 | 
			
		||||
	case "ws":
 | 
			
		||||
		client, err = net.Dial("tcp", parseAuthority(config.Location))
 | 
			
		||||
 | 
			
		||||
	case "wss":
 | 
			
		||||
		client, err = tls.Dial("tcp", parseAuthority(config.Location), config.TlsConfig)
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		err = ErrBadScheme
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		goto Error
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ws, err = NewClient(config, client)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		client.Close()
 | 
			
		||||
		goto Error
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
 | 
			
		||||
Error:
 | 
			
		||||
	return nil, &DialError{config, err}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										583
									
								
								vendor/golang.org/x/net/websocket/hybi.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										583
									
								
								vendor/golang.org/x/net/websocket/hybi.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,583 @@
 | 
			
		||||
// 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
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								vendor/golang.org/x/net/websocket/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,113 @@
 | 
			
		||||
// 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)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										411
									
								
								vendor/golang.org/x/net/websocket/websocket.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										411
									
								
								vendor/golang.org/x/net/websocket/websocket.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,411 @@
 | 
			
		||||
// 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.
 | 
			
		||||
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
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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"}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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.
 | 
			
		||||
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
 | 
			
		||||
	}
 | 
			
		||||
	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}
 | 
			
		||||
							
								
								
									
										17
									
								
								vendor/manifest
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								vendor/manifest
									
									
									
									
										vendored
									
									
								
							@@ -110,6 +110,14 @@
 | 
			
		||||
			"path": "/i18n",
 | 
			
		||||
			"notests": true
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"importpath": "github.com/nlopes/slack",
 | 
			
		||||
			"repository": "https://github.com/nlopes/slack",
 | 
			
		||||
			"vcs": "git",
 | 
			
		||||
			"revision": "4feee83bb2b31d790977ce727a028c6a542c72c7",
 | 
			
		||||
			"branch": "HEAD",
 | 
			
		||||
			"notests": true
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"importpath": "github.com/pborman/uuid",
 | 
			
		||||
			"repository": "https://github.com/pborman/uuid",
 | 
			
		||||
@@ -186,6 +194,15 @@
 | 
			
		||||
			"path": "/lex/httplex",
 | 
			
		||||
			"notests": true
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"importpath": "golang.org/x/net/websocket",
 | 
			
		||||
			"repository": "https://go.googlesource.com/net",
 | 
			
		||||
			"vcs": "git",
 | 
			
		||||
			"revision": "1358eff22f0dd0c54fc521042cc607f6ff4b531a",
 | 
			
		||||
			"branch": "master",
 | 
			
		||||
			"path": "/websocket",
 | 
			
		||||
			"notests": true
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"importpath": "gopkg.in/gcfg.v1",
 | 
			
		||||
			"repository": "https://gopkg.in/gcfg.v1",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user