5
0
mirror of https://github.com/cwinfo/matterbridge.git synced 2024-11-22 23:00:28 +00:00

Update vendor

This commit is contained in:
Wim 2017-01-28 22:45:32 +01:00
parent 1426ddec5f
commit 79e6c9fa6c
33 changed files with 2800 additions and 2027 deletions

View File

@ -1,9 +1,9 @@
package bgitter package bgitter
import ( import (
"github.com/42wim/go-gitter"
"github.com/42wim/matterbridge/bridge/config" "github.com/42wim/matterbridge/bridge/config"
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
"github.com/sromku/go-gitter"
"strings" "strings"
) )

View File

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,434 +0,0 @@
package bridge
import (
"crypto/tls"
"github.com/42wim/matterbridge-plus/matterclient"
"github.com/42wim/matterbridge/matterhook"
log "github.com/Sirupsen/logrus"
"github.com/peterhellberg/giphy"
ircm "github.com/sorcix/irc"
"github.com/thoj/go-ircevent"
"regexp"
"sort"
"strconv"
"strings"
"time"
)
//type Bridge struct {
type MMhook struct {
mh *matterhook.Client
}
type MMapi struct {
mc *matterclient.MMClient
mmMap map[string]string
mmIgnoreNicks []string
}
type MMirc struct {
i *irc.Connection
ircNick string
ircMap map[string]string
names map[string][]string
ircIgnoreNicks []string
}
type MMMessage struct {
Text string
Channel string
Username string
}
type Bridge struct {
MMhook
MMapi
MMirc
*Config
kind string
}
type FancyLog struct {
irc *log.Entry
mm *log.Entry
}
var flog FancyLog
const Legacy = "legacy"
func initFLog() {
flog.irc = log.WithFields(log.Fields{"module": "irc"})
flog.mm = log.WithFields(log.Fields{"module": "mattermost"})
}
func NewBridge(name string, config *Config, kind string) *Bridge {
initFLog()
b := &Bridge{}
b.Config = config
b.kind = kind
b.ircNick = b.Config.IRC.Nick
b.ircMap = make(map[string]string)
b.MMirc.names = make(map[string][]string)
b.ircIgnoreNicks = strings.Fields(b.Config.IRC.IgnoreNicks)
b.mmIgnoreNicks = strings.Fields(b.Config.Mattermost.IgnoreNicks)
if kind == Legacy {
if len(b.Config.Token) > 0 {
for _, val := range b.Config.Token {
b.ircMap[val.IRCChannel] = val.MMChannel
}
}
b.mh = matterhook.New(b.Config.Mattermost.URL,
matterhook.Config{Port: b.Config.Mattermost.Port, Token: b.Config.Mattermost.Token,
InsecureSkipVerify: b.Config.Mattermost.SkipTLSVerify,
BindAddress: b.Config.Mattermost.BindAddress})
} else {
b.mmMap = make(map[string]string)
if len(b.Config.Channel) > 0 {
for _, val := range b.Config.Channel {
b.ircMap[val.IRC] = val.Mattermost
b.mmMap[val.Mattermost] = val.IRC
}
}
b.mc = matterclient.New(b.Config.Mattermost.Login, b.Config.Mattermost.Password,
b.Config.Mattermost.Team, b.Config.Mattermost.Server)
b.mc.SkipTLSVerify = b.Config.Mattermost.SkipTLSVerify
b.mc.NoTLS = b.Config.Mattermost.NoTLS
flog.mm.Infof("Trying login %s (team: %s) on %s", b.Config.Mattermost.Login, b.Config.Mattermost.Team, b.Config.Mattermost.Server)
err := b.mc.Login()
if err != nil {
flog.mm.Fatal("Can not connect", err)
}
flog.mm.Info("Login ok")
b.mc.JoinChannel(b.Config.Mattermost.Channel)
if len(b.Config.Channel) > 0 {
for _, val := range b.Config.Channel {
b.mc.JoinChannel(val.Mattermost)
}
}
go b.mc.WsReceiver()
}
flog.irc.Info("Trying IRC connection")
b.i = b.createIRC(name)
flog.irc.Info("Connection succeeded")
go b.handleMatter()
return b
}
func (b *Bridge) createIRC(name string) *irc.Connection {
i := irc.IRC(b.Config.IRC.Nick, b.Config.IRC.Nick)
i.UseTLS = b.Config.IRC.UseTLS
i.TLSConfig = &tls.Config{InsecureSkipVerify: b.Config.IRC.SkipTLSVerify}
if b.Config.IRC.Password != "" {
i.Password = b.Config.IRC.Password
}
i.AddCallback(ircm.RPL_WELCOME, b.handleNewConnection)
i.Connect(b.Config.IRC.Server + ":" + strconv.Itoa(b.Config.IRC.Port))
return i
}
func (b *Bridge) handleNewConnection(event *irc.Event) {
flog.irc.Info("Registering callbacks")
i := b.i
b.ircNick = event.Arguments[0]
i.AddCallback("PRIVMSG", b.handlePrivMsg)
i.AddCallback("CTCP_ACTION", b.handlePrivMsg)
i.AddCallback(ircm.RPL_ENDOFNAMES, b.endNames)
i.AddCallback(ircm.RPL_NAMREPLY, b.storeNames)
i.AddCallback(ircm.RPL_TOPICWHOTIME, b.handleTopicWhoTime)
i.AddCallback(ircm.NOTICE, b.handleNotice)
i.AddCallback(ircm.RPL_MYINFO, func(e *irc.Event) { flog.irc.Infof("%s: %s", e.Code, strings.Join(e.Arguments[1:], " ")) })
i.AddCallback("PING", func(e *irc.Event) {
i.SendRaw("PONG :" + e.Message())
flog.irc.Debugf("PING/PONG")
})
if b.Config.Mattermost.ShowJoinPart {
i.AddCallback("JOIN", b.handleJoinPart)
i.AddCallback("PART", b.handleJoinPart)
}
i.AddCallback("*", b.handleOther)
b.setupChannels()
}
func (b *Bridge) setupChannels() {
i := b.i
if b.Config.IRC.Channel != "" {
flog.irc.Infof("Joining %s as %s", b.Config.IRC.Channel, b.ircNick)
i.Join(b.Config.IRC.Channel)
}
if b.kind == Legacy {
for _, val := range b.Config.Token {
flog.irc.Infof("Joining %s as %s", val.IRCChannel, b.ircNick)
i.Join(val.IRCChannel)
}
} else {
for _, val := range b.Config.Channel {
flog.irc.Infof("Joining %s as %s", val.IRC, b.ircNick)
i.Join(val.IRC)
}
}
}
func (b *Bridge) handleIrcBotCommand(event *irc.Event) bool {
parts := strings.Fields(event.Message())
exp, _ := regexp.Compile("[:,]+$")
channel := event.Arguments[0]
command := ""
if len(parts) == 2 {
command = parts[1]
}
if exp.ReplaceAllString(parts[0], "") == b.ircNick {
switch command {
case "users":
usernames := b.mc.UsernamesInChannel(b.getMMChannel(channel))
sort.Strings(usernames)
b.i.Privmsg(channel, "Users on Mattermost: "+strings.Join(usernames, ", "))
default:
b.i.Privmsg(channel, "Valid commands are: [users, help]")
}
return true
}
return false
}
func (b *Bridge) ircNickFormat(nick string) string {
if nick == b.ircNick {
return nick
}
if b.Config.Mattermost.RemoteNickFormat == nil {
return "irc-" + nick
}
return strings.Replace(*b.Config.Mattermost.RemoteNickFormat, "{NICK}", nick, -1)
}
func (b *Bridge) handlePrivMsg(event *irc.Event) {
if b.ignoreMessage(event.Nick, event.Message(), "irc") {
return
}
if b.handleIrcBotCommand(event) {
return
}
msg := ""
if event.Code == "CTCP_ACTION" {
msg = event.Nick + " "
}
msg += event.Message()
b.Send(b.ircNickFormat(event.Nick), msg, b.getMMChannel(event.Arguments[0]))
}
func (b *Bridge) handleJoinPart(event *irc.Event) {
b.Send(b.ircNick, b.ircNickFormat(event.Nick)+" "+strings.ToLower(event.Code)+"s "+event.Message(), b.getMMChannel(event.Arguments[0]))
}
func (b *Bridge) handleNotice(event *irc.Event) {
if strings.Contains(event.Message(), "This nickname is registered") {
b.i.Privmsg(b.Config.IRC.NickServNick, "IDENTIFY "+b.Config.IRC.NickServPassword)
}
}
func (b *Bridge) nicksPerRow() int {
if b.Config.Mattermost.NicksPerRow < 1 {
return 4
}
return b.Config.Mattermost.NicksPerRow
}
func (b *Bridge) formatnicks(nicks []string, continued bool) string {
switch b.Config.Mattermost.NickFormatter {
case "table":
return tableformatter(nicks, b.nicksPerRow(), continued)
default:
return plainformatter(nicks, b.nicksPerRow())
}
}
func (b *Bridge) storeNames(event *irc.Event) {
channel := event.Arguments[2]
b.MMirc.names[channel] = append(
b.MMirc.names[channel],
strings.Split(strings.TrimSpace(event.Message()), " ")...)
}
func (b *Bridge) endNames(event *irc.Event) {
channel := event.Arguments[1]
sort.Strings(b.MMirc.names[channel])
maxNamesPerPost := (300 / b.nicksPerRow()) * b.nicksPerRow()
continued := false
for len(b.MMirc.names[channel]) > maxNamesPerPost {
b.Send(
b.ircNick,
b.formatnicks(b.MMirc.names[channel][0:maxNamesPerPost], continued),
b.getMMChannel(channel))
b.MMirc.names[channel] = b.MMirc.names[channel][maxNamesPerPost:]
continued = true
}
b.Send(b.ircNick, b.formatnicks(b.MMirc.names[channel], continued), b.getMMChannel(channel))
b.MMirc.names[channel] = nil
}
func (b *Bridge) handleTopicWhoTime(event *irc.Event) {
parts := strings.Split(event.Arguments[2], "!")
t, err := strconv.ParseInt(event.Arguments[3], 10, 64)
if err != nil {
flog.irc.Errorf("Invalid time stamp: %s", event.Arguments[3])
}
user := parts[0]
if len(parts) > 1 {
user += " [" + parts[1] + "]"
}
flog.irc.Infof("%s: Topic set by %s [%s]", event.Code, user, time.Unix(t, 0))
}
func (b *Bridge) handleOther(event *irc.Event) {
flog.irc.Debugf("%#v", event)
}
func (b *Bridge) Send(nick string, message string, channel string) error {
return b.SendType(nick, message, channel, "")
}
func (b *Bridge) SendType(nick string, message string, channel string, mtype string) error {
if b.Config.Mattermost.PrefixMessagesWithNick {
if IsMarkup(message) {
message = nick + "\n\n" + message
} else {
message = nick + " " + message
}
}
if b.kind == Legacy {
matterMessage := matterhook.OMessage{IconURL: b.Config.Mattermost.IconURL}
matterMessage.Channel = channel
matterMessage.UserName = nick
matterMessage.Type = mtype
matterMessage.Text = message
err := b.mh.Send(matterMessage)
if err != nil {
flog.mm.Info(err)
return err
}
return nil
}
flog.mm.Debug("->mattermost channel: ", channel, " ", message)
b.mc.PostMessage(channel, message)
return nil
}
func (b *Bridge) handleMatterHook(mchan chan *MMMessage) {
for {
message := b.mh.Receive()
m := &MMMessage{}
m.Username = message.UserName
m.Text = message.Text
m.Channel = message.Token
mchan <- m
}
}
func (b *Bridge) handleMatterClient(mchan chan *MMMessage) {
for message := range b.mc.MessageChan {
// do not post our own messages back to irc
if message.Raw.Action == "posted" && b.mc.User.Username != message.Username {
m := &MMMessage{}
m.Username = message.Username
m.Channel = message.Channel
m.Text = message.Text
flog.mm.Debugf("<-mattermost channel: %s %#v %#v", message.Channel, message.Post, message.Raw)
mchan <- m
}
}
}
func (b *Bridge) handleMatter() {
flog.mm.Infof("Choosing Mattermost connection type %s", b.kind)
mchan := make(chan *MMMessage)
if b.kind == Legacy {
go b.handleMatterHook(mchan)
} else {
go b.handleMatterClient(mchan)
}
flog.mm.Info("Start listening for Mattermost messages")
for message := range mchan {
var username string
if b.ignoreMessage(message.Username, message.Text, "mattermost") {
continue
}
username = message.Username + ": "
if b.Config.IRC.RemoteNickFormat != "" {
username = strings.Replace(b.Config.IRC.RemoteNickFormat, "{NICK}", message.Username, -1)
} else if b.Config.IRC.UseSlackCircumfix {
username = "<" + message.Username + "> "
}
cmds := strings.Fields(message.Text)
// empty message
if len(cmds) == 0 {
continue
}
cmd := cmds[0]
switch cmd {
case "!users":
flog.mm.Info("Received !users from ", message.Username)
b.i.SendRaw("NAMES " + b.getIRCChannel(message.Channel))
continue
case "!gif":
message.Text = b.giphyRandom(strings.Fields(strings.Replace(message.Text, "!gif ", "", 1)))
b.Send(b.ircNick, message.Text, b.getIRCChannel(message.Channel))
continue
}
texts := strings.Split(message.Text, "\n")
for _, text := range texts {
flog.mm.Debug("Sending message from " + message.Username + " to " + message.Channel)
b.i.Privmsg(b.getIRCChannel(message.Channel), username+text)
}
}
}
func (b *Bridge) giphyRandom(query []string) string {
g := giphy.DefaultClient
if b.Config.General.GiphyAPIKey != "" {
g.APIKey = b.Config.General.GiphyAPIKey
}
res, err := g.Random(query)
if err != nil {
return "error"
}
return res.Data.FixedHeightDownsampledURL
}
func (b *Bridge) getMMChannel(ircChannel string) string {
mmchannel, ok := b.ircMap[ircChannel]
if !ok {
mmchannel = b.Config.Mattermost.Channel
}
return mmchannel
}
func (b *Bridge) getIRCChannel(channel string) string {
if b.kind == Legacy {
ircchannel := b.Config.IRC.Channel
_, ok := b.Config.Token[channel]
if ok {
ircchannel = b.Config.Token[channel].IRCChannel
}
return ircchannel
}
ircchannel, ok := b.mmMap[channel]
if !ok {
ircchannel = b.Config.IRC.Channel
}
return ircchannel
}
func (b *Bridge) ignoreMessage(nick string, message string, protocol string) bool {
var ignoreNicks = b.mmIgnoreNicks
if protocol == "irc" {
ignoreNicks = b.ircIgnoreNicks
}
// should we discard messages ?
for _, entry := range ignoreNicks {
if nick == entry {
return true
}
}
return false
}

View File

@ -1,68 +0,0 @@
package bridge
import (
"gopkg.in/gcfg.v1"
"io/ioutil"
"log"
)
type Config struct {
IRC struct {
UseTLS bool
SkipTLSVerify bool
Server string
Port int
Nick string
Password string
Channel string
UseSlackCircumfix bool
NickServNick string
NickServPassword string
RemoteNickFormat string
IgnoreNicks string
}
Mattermost struct {
URL string
Port int
ShowJoinPart bool
Token string
IconURL string
SkipTLSVerify bool
BindAddress string
Channel string
PrefixMessagesWithNick bool
NicksPerRow int
NickFormatter string
Server string
Team string
Login string
Password string
RemoteNickFormat *string
IgnoreNicks string
NoTLS bool
}
Token map[string]*struct {
IRCChannel string
MMChannel string
}
Channel map[string]*struct {
IRC string
Mattermost string
}
General struct {
GiphyAPIKey string
}
}
func NewConfig(cfgfile string) *Config {
var cfg Config
content, err := ioutil.ReadFile(cfgfile)
if err != nil {
log.Fatal(err)
}
err = gcfg.ReadStringInto(&cfg, string(content))
if err != nil {
log.Fatal("Failed to parse "+cfgfile+":", err)
}
return &cfg
}

View File

@ -1,59 +0,0 @@
package bridge
import (
"strings"
)
func tableformatter(nicks []string, nicksPerRow int, continued bool) string {
result := "|IRC users"
if continued {
result = "|(continued)"
}
for i := 0; i < 2; i++ {
for j := 1; j <= nicksPerRow && j <= len(nicks); j++ {
if i == 0 {
result += "|"
} else {
result += ":-|"
}
}
result += "\r\n|"
}
result += nicks[0] + "|"
for i := 1; i < len(nicks); i++ {
if i%nicksPerRow == 0 {
result += "\r\n|" + nicks[i] + "|"
} else {
result += nicks[i] + "|"
}
}
return result
}
func plainformatter(nicks []string, nicksPerRow int) string {
return strings.Join(nicks, ", ") + " currently on IRC"
}
func IsMarkup(message string) bool {
switch message[0] {
case '|':
fallthrough
case '#':
fallthrough
case '_':
fallthrough
case '*':
fallthrough
case '~':
fallthrough
case '-':
fallthrough
case ':':
fallthrough
case '>':
fallthrough
case '=':
return true
}
return false
}

View File

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,441 +0,0 @@
package matterclient
import (
"crypto/tls"
"errors"
log "github.com/Sirupsen/logrus"
"net/http"
"net/http/cookiejar"
"net/url"
"strings"
"time"
"github.com/gorilla/websocket"
"github.com/jpillora/backoff"
"github.com/mattermost/platform/model"
)
type Credentials struct {
Login string
Team string
Pass string
Server string
NoTLS bool
SkipTLSVerify bool
}
type Message struct {
Raw *model.Message
Post *model.Post
Team string
Channel string
Username string
Text string
}
type MMClient struct {
*Credentials
Client *model.Client
WsClient *websocket.Conn
WsQuit bool
WsAway bool
Channels *model.ChannelList
MoreChannels *model.ChannelList
User *model.User
Users map[string]*model.User
MessageChan chan *Message
Team *model.Team
log *log.Entry
}
func New(login, pass, team, server string) *MMClient {
cred := &Credentials{Login: login, Pass: pass, Team: team, Server: server}
mmclient := &MMClient{Credentials: cred, MessageChan: make(chan *Message, 100)}
mmclient.log = log.WithFields(log.Fields{"module": "matterclient"})
log.SetFormatter(&log.TextFormatter{FullTimestamp: true})
return mmclient
}
func (m *MMClient) SetLogLevel(level string) {
l, err := log.ParseLevel(level)
if err != nil {
log.SetLevel(log.InfoLevel)
return
}
log.SetLevel(l)
}
func (m *MMClient) Login() error {
if m.WsQuit {
return nil
}
b := &backoff.Backoff{
Min: time.Second,
Max: 5 * time.Minute,
Jitter: true,
}
uriScheme := "https://"
wsScheme := "wss://"
if m.NoTLS {
uriScheme = "http://"
wsScheme = "ws://"
}
// login to mattermost
m.Client = model.NewClient(uriScheme + m.Credentials.Server)
m.Client.HttpClient.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}}
var myinfo *model.Result
var appErr *model.AppError
var logmsg = "trying login"
for {
m.log.Debugf("%s %s %s %s", logmsg, m.Credentials.Team, m.Credentials.Login, m.Credentials.Server)
if strings.Contains(m.Credentials.Pass, model.SESSION_COOKIE_TOKEN) {
m.log.Debugf(logmsg+" with ", model.SESSION_COOKIE_TOKEN)
token := strings.Split(m.Credentials.Pass, model.SESSION_COOKIE_TOKEN+"=")
m.Client.HttpClient.Jar = m.createCookieJar(token[1])
m.Client.MockSession(token[1])
myinfo, appErr = m.Client.GetMe("")
if myinfo.Data.(*model.User) == nil {
m.log.Errorf("LOGIN TOKEN: %s is invalid", m.Credentials.Pass)
return errors.New("invalid " + model.SESSION_COOKIE_TOKEN)
}
} else {
myinfo, appErr = m.Client.Login(m.Credentials.Login, m.Credentials.Pass)
}
if appErr != nil {
d := b.Duration()
m.log.Debug(appErr.DetailedError)
if !strings.Contains(appErr.DetailedError, "connection refused") &&
!strings.Contains(appErr.DetailedError, "invalid character") {
if appErr.Message == "" {
return errors.New(appErr.DetailedError)
}
return errors.New(appErr.Message)
}
m.log.Debugf("LOGIN: %s, reconnecting in %s", appErr, d)
time.Sleep(d)
logmsg = "retrying login"
continue
}
break
}
// reset timer
b.Reset()
initLoad, _ := m.Client.GetInitialLoad()
initData := initLoad.Data.(*model.InitialLoad)
m.User = initData.User
for _, v := range initData.Teams {
m.log.Debugf("trying %s (id: %s)", v.Name, v.Id)
if v.Name == m.Credentials.Team {
m.Client.SetTeamId(v.Id)
m.Team = v
m.log.Debugf("GetallTeamListings: found id %s for team %s", v.Id, v.Name)
break
}
}
if m.Team == nil {
return errors.New("team not found")
}
// setup websocket connection
wsurl := wsScheme + m.Credentials.Server + "/api/v3/users/websocket"
header := http.Header{}
header.Set(model.HEADER_AUTH, "BEARER "+m.Client.AuthToken)
m.log.Debug("WsClient: making connection")
var err error
for {
wsDialer := &websocket.Dialer{Proxy: http.ProxyFromEnvironment, TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}}
m.WsClient, _, err = wsDialer.Dial(wsurl, header)
if err != nil {
d := b.Duration()
m.log.Debugf("WSS: %s, reconnecting in %s", err, d)
time.Sleep(d)
continue
}
break
}
b.Reset()
// populating users
m.UpdateUsers()
// populating channels
m.UpdateChannels()
return nil
}
func (m *MMClient) WsReceiver() {
var rmsg model.Message
for {
if m.WsQuit {
m.log.Debug("exiting WsReceiver")
return
}
if err := m.WsClient.ReadJSON(&rmsg); err != nil {
m.log.Error("error:", err)
// reconnect
m.Login()
}
if rmsg.Action == "ping" {
m.handleWsPing()
continue
}
msg := &Message{Raw: &rmsg, Team: m.Credentials.Team}
m.parseMessage(msg)
m.MessageChan <- msg
}
}
func (m *MMClient) handleWsPing() {
m.log.Debug("Ws PING")
if !m.WsQuit && !m.WsAway {
m.log.Debug("Ws PONG")
m.WsClient.WriteMessage(websocket.PongMessage, []byte{})
}
}
func (m *MMClient) parseMessage(rmsg *Message) {
switch rmsg.Raw.Action {
case model.ACTION_POSTED:
m.parseActionPost(rmsg)
/*
case model.ACTION_USER_REMOVED:
m.handleWsActionUserRemoved(&rmsg)
case model.ACTION_USER_ADDED:
m.handleWsActionUserAdded(&rmsg)
*/
}
}
func (m *MMClient) parseActionPost(rmsg *Message) {
data := model.PostFromJson(strings.NewReader(rmsg.Raw.Props["post"]))
// log.Println("receiving userid", data.UserId)
// we don't have the user, refresh the userlist
if m.Users[data.UserId] == nil {
m.UpdateUsers()
}
rmsg.Username = m.Users[data.UserId].Username
rmsg.Channel = m.GetChannelName(data.ChannelId)
// direct message
if strings.Contains(rmsg.Channel, "__") {
//log.Println("direct message")
rcvusers := strings.Split(rmsg.Channel, "__")
if rcvusers[0] != m.User.Id {
rmsg.Channel = m.Users[rcvusers[0]].Username
} else {
rmsg.Channel = m.Users[rcvusers[1]].Username
}
}
rmsg.Text = data.Message
rmsg.Post = data
return
}
func (m *MMClient) UpdateUsers() error {
mmusers, _ := m.Client.GetProfilesForDirectMessageList(m.Team.Id)
m.Users = mmusers.Data.(map[string]*model.User)
return nil
}
func (m *MMClient) UpdateChannels() error {
mmchannels, _ := m.Client.GetChannels("")
m.Channels = mmchannels.Data.(*model.ChannelList)
mmchannels, _ = m.Client.GetMoreChannels("")
m.MoreChannels = mmchannels.Data.(*model.ChannelList)
return nil
}
func (m *MMClient) GetChannelName(id string) string {
for _, channel := range append(m.Channels.Channels, m.MoreChannels.Channels...) {
if channel.Id == id {
return channel.Name
}
}
// not found? could be a new direct message from mattermost. Try to update and check again
m.UpdateChannels()
for _, channel := range append(m.Channels.Channels, m.MoreChannels.Channels...) {
if channel.Id == id {
return channel.Name
}
}
return ""
}
func (m *MMClient) GetChannelId(name string) string {
for _, channel := range append(m.Channels.Channels, m.MoreChannels.Channels...) {
if channel.Name == name {
return channel.Id
}
}
return ""
}
func (m *MMClient) GetChannelHeader(id string) string {
for _, channel := range append(m.Channels.Channels, m.MoreChannels.Channels...) {
if channel.Id == id {
return channel.Header
}
}
return ""
}
func (m *MMClient) PostMessage(channel string, text string) {
post := &model.Post{ChannelId: m.GetChannelId(channel), Message: text}
m.Client.CreatePost(post)
}
func (m *MMClient) JoinChannel(channel string) error {
cleanChan := strings.Replace(channel, "#", "", 1)
if m.GetChannelId(cleanChan) == "" {
return errors.New("failed to join")
}
for _, c := range m.Channels.Channels {
if c.Name == cleanChan {
m.log.Debug("Not joining ", cleanChan, " already joined.")
return nil
}
}
m.log.Debug("Joining ", cleanChan)
_, err := m.Client.JoinChannel(m.GetChannelId(cleanChan))
if err != nil {
return errors.New("failed to join")
}
// m.SyncChannel(m.getMMChannelId(strings.Replace(channel, "#", "", 1)), strings.Replace(channel, "#", "", 1))
return nil
}
func (m *MMClient) GetPostsSince(channelId string, time int64) *model.PostList {
res, err := m.Client.GetPostsSince(channelId, time)
if err != nil {
return nil
}
return res.Data.(*model.PostList)
}
func (m *MMClient) SearchPosts(query string) *model.PostList {
res, err := m.Client.SearchPosts(query, false)
if err != nil {
return nil
}
return res.Data.(*model.PostList)
}
func (m *MMClient) GetPosts(channelId string, limit int) *model.PostList {
res, err := m.Client.GetPosts(channelId, 0, limit, "")
if err != nil {
return nil
}
return res.Data.(*model.PostList)
}
func (m *MMClient) GetPublicLink(filename string) string {
res, err := m.Client.GetPublicLink(filename)
if err != nil {
return ""
}
return res.Data.(string)
}
func (m *MMClient) GetPublicLinks(filenames []string) []string {
var output []string
for _, f := range filenames {
res, err := m.Client.GetPublicLink(f)
if err != nil {
continue
}
output = append(output, res.Data.(string))
}
return output
}
func (m *MMClient) UpdateChannelHeader(channelId string, header string) {
data := make(map[string]string)
data["channel_id"] = channelId
data["channel_header"] = header
m.log.Debugf("updating channelheader %#v, %#v", channelId, header)
_, err := m.Client.UpdateChannelHeader(data)
if err != nil {
log.Error(err)
}
}
func (m *MMClient) UpdateLastViewed(channelId string) {
m.log.Debugf("posting lastview %#v", channelId)
_, err := m.Client.UpdateLastViewedAt(channelId)
if err != nil {
m.log.Error(err)
}
}
func (m *MMClient) UsernamesInChannel(channelName string) []string {
ceiRes, err := m.Client.GetChannelExtraInfo(m.GetChannelId(channelName), 5000, "")
if err != nil {
m.log.Errorf("UsernamesInChannel(%s) failed: %s", channelName, err)
return []string{}
}
extra := ceiRes.Data.(*model.ChannelExtra)
result := []string{}
for _, member := range extra.Members {
result = append(result, member.Username)
}
return result
}
func (m *MMClient) createCookieJar(token string) *cookiejar.Jar {
var cookies []*http.Cookie
jar, _ := cookiejar.New(nil)
firstCookie := &http.Cookie{
Name: "MMAUTHTOKEN",
Value: token,
Path: "/",
Domain: m.Credentials.Server,
}
cookies = append(cookies, firstCookie)
cookieURL, _ := url.Parse("https://" + m.Credentials.Server)
jar.SetCookies(cookieURL, cookies)
return jar
}
func (m *MMClient) GetOtherUserDM(channel string) *model.User {
m.UpdateUsers()
var rcvuser *model.User
if strings.Contains(channel, "__") {
rcvusers := strings.Split(channel, "__")
if rcvusers[0] != m.User.Id {
rcvuser = m.Users[rcvusers[0]]
} else {
rcvuser = m.Users[rcvusers[1]]
}
}
return rcvuser
}
func (m *MMClient) SendDirectMessage(toUserId string, msg string) {
m.log.Debugf("SendDirectMessage to %s, msg %s", toUserId, msg)
var channel string
// We don't have a DM with this user yet.
if m.GetChannelId(toUserId+"__"+m.User.Id) == "" && m.GetChannelId(m.User.Id+"__"+toUserId) == "" {
// create DM channel
_, err := m.Client.CreateDirectChannel(toUserId)
if err != nil {
m.log.Debugf("SendDirectMessage to %#v failed: %s", toUserId, err)
}
// update our channels
mmchannels, _ := m.Client.GetChannels("")
m.Channels = mmchannels.Data.(*model.ChannelList)
}
// build the channel name
if toUserId > m.User.Id {
channel = m.User.Id + "__" + toUserId
} else {
channel = toUserId + "__" + m.User.Id
}
// build & send the message
msg = strings.Replace(msg, "\r", "", -1)
post := &model.Post{ChannelId: m.GetChannelId(channel), Message: msg}
m.Client.CreatePost(post)
}

View File

@ -13,13 +13,10 @@
// Package discordgo provides Discord binding for Go // Package discordgo provides Discord binding for Go
package discordgo package discordgo
import ( import "fmt"
"fmt"
"reflect"
)
// VERSION of Discordgo, follows Symantic Versioning. (http://semver.org/) // VERSION of Discordgo, follows Symantic Versioning. (http://semver.org/)
const VERSION = "0.13.0" const VERSION = "0.15.0"
// New creates a new Discord session and will automate some startup // New creates a new Discord session and will automate some startup
// tasks if given enough information to do so. Currently you can pass zero // tasks if given enough information to do so. Currently you can pass zero
@ -27,6 +24,8 @@ const VERSION = "0.13.0"
// There are 3 ways to call New: // There are 3 ways to call New:
// With a single auth token - All requests will use the token blindly, // With a single auth token - All requests will use the token blindly,
// no verification of the token will be done and requests may fail. // no verification of the token will be done and requests may fail.
// IF THE TOKEN IS FOR A BOT, IT MUST BE PREFIXED WITH `BOT `
// eg: `"Bot <token>"`
// With an email and password - Discord will sign in with the provided // With an email and password - Discord will sign in with the provided
// credentials. // credentials.
// With an email, password and auth token - Discord will verify the auth // With an email, password and auth token - Discord will verify the auth
@ -37,11 +36,13 @@ func New(args ...interface{}) (s *Session, err error) {
// Create an empty Session interface. // Create an empty Session interface.
s = &Session{ s = &Session{
State: NewState(), State: NewState(),
ratelimiter: NewRatelimiter(),
StateEnabled: true, StateEnabled: true,
Compress: true, Compress: true,
ShouldReconnectOnError: true, ShouldReconnectOnError: true,
ShardID: 0, ShardID: 0,
ShardCount: 1, ShardCount: 1,
MaxRestRetries: 3,
} }
// If no arguments are passed return the empty Session interface. // If no arguments are passed return the empty Session interface.
@ -122,136 +123,3 @@ func New(args ...interface{}) (s *Session, err error) {
return return
} }
// validateHandler takes an event handler func, and returns the type of event.
// eg.
// Session.validateHandler(func (s *discordgo.Session, m *discordgo.MessageCreate))
// will return the reflect.Type of *discordgo.MessageCreate
func (s *Session) validateHandler(handler interface{}) reflect.Type {
handlerType := reflect.TypeOf(handler)
if handlerType.NumIn() != 2 {
panic("Unable to add event handler, handler must be of the type func(*discordgo.Session, *discordgo.EventType).")
}
if handlerType.In(0) != reflect.TypeOf(s) {
panic("Unable to add event handler, first argument must be of type *discordgo.Session.")
}
eventType := handlerType.In(1)
// Support handlers of type interface{}, this is a special handler, which is triggered on every event.
if eventType.Kind() == reflect.Interface {
eventType = nil
}
return eventType
}
// AddHandler allows you to add an event handler that will be fired anytime
// the Discord WSAPI event that matches the interface fires.
// eventToInterface in events.go has a list of all the Discord WSAPI events
// and their respective interface.
// eg:
// Session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
// })
//
// or:
// Session.AddHandler(func(s *discordgo.Session, m *discordgo.PresenceUpdate) {
// })
// The return value of this method is a function, that when called will remove the
// event handler.
func (s *Session) AddHandler(handler interface{}) func() {
s.initialize()
eventType := s.validateHandler(handler)
s.handlersMu.Lock()
defer s.handlersMu.Unlock()
h := reflect.ValueOf(handler)
s.handlers[eventType] = append(s.handlers[eventType], h)
// This must be done as we need a consistent reference to the
// reflected value, otherwise a RemoveHandler method would have
// been nice.
return func() {
s.handlersMu.Lock()
defer s.handlersMu.Unlock()
handlers := s.handlers[eventType]
for i, v := range handlers {
if h == v {
s.handlers[eventType] = append(handlers[:i], handlers[i+1:]...)
return
}
}
}
}
// handle calls any handlers that match the event type and any handlers of
// interface{}.
func (s *Session) handle(event interface{}) {
s.handlersMu.RLock()
defer s.handlersMu.RUnlock()
if s.handlers == nil {
return
}
handlerParameters := []reflect.Value{reflect.ValueOf(s), reflect.ValueOf(event)}
if handlers, ok := s.handlers[nil]; ok {
for _, handler := range handlers {
go handler.Call(handlerParameters)
}
}
if handlers, ok := s.handlers[reflect.TypeOf(event)]; ok {
for _, handler := range handlers {
go handler.Call(handlerParameters)
}
}
}
// initialize adds all internal handlers and state tracking handlers.
func (s *Session) initialize() {
s.log(LogInformational, "called")
s.handlersMu.Lock()
if s.handlers != nil {
s.handlersMu.Unlock()
return
}
s.handlers = map[interface{}][]reflect.Value{}
s.handlersMu.Unlock()
s.AddHandler(s.onReady)
s.AddHandler(s.onResumed)
s.AddHandler(s.onVoiceServerUpdate)
s.AddHandler(s.onVoiceStateUpdate)
s.AddHandler(s.State.onInterface)
}
// onReady handles the ready event.
func (s *Session) onReady(se *Session, r *Ready) {
// Store the SessionID within the Session struct.
s.sessionID = r.SessionID
// Start the heartbeat to keep the connection alive.
go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval)
}
// onResumed handles the resumed event.
func (s *Session) onResumed(se *Session, r *Resumed) {
// Start the heartbeat to keep the connection alive.
go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval)
}

View File

@ -24,6 +24,7 @@ var (
EndpointChannels = EndpointAPI + "channels/" EndpointChannels = EndpointAPI + "channels/"
EndpointUsers = EndpointAPI + "users/" EndpointUsers = EndpointAPI + "users/"
EndpointGateway = EndpointAPI + "gateway" EndpointGateway = EndpointAPI + "gateway"
EndpointWebhooks = EndpointAPI + "webhooks/"
EndpointAuth = EndpointAPI + "auth/" EndpointAuth = EndpointAPI + "auth/"
EndpointLogin = EndpointAuth + "login" EndpointLogin = EndpointAuth + "login"
@ -61,6 +62,7 @@ var (
EndpointGuildChannels = func(gID string) string { return EndpointGuilds + gID + "/channels" } EndpointGuildChannels = func(gID string) string { return EndpointGuilds + gID + "/channels" }
EndpointGuildMembers = func(gID string) string { return EndpointGuilds + gID + "/members" } EndpointGuildMembers = func(gID string) string { return EndpointGuilds + gID + "/members" }
EndpointGuildMember = func(gID, uID string) string { return EndpointGuilds + gID + "/members/" + uID } EndpointGuildMember = func(gID, uID string) string { return EndpointGuilds + gID + "/members/" + uID }
EndpointGuildMemberRole = func(gID, uID, rID string) string { return EndpointGuilds + gID + "/members/" + uID + "/roles/" + rID }
EndpointGuildBans = func(gID string) string { return EndpointGuilds + gID + "/bans" } EndpointGuildBans = func(gID string) string { return EndpointGuilds + gID + "/bans" }
EndpointGuildBan = func(gID, uID string) string { return EndpointGuilds + gID + "/bans/" + uID } EndpointGuildBan = func(gID, uID string) string { return EndpointGuilds + gID + "/bans/" + uID }
EndpointGuildIntegrations = func(gID string) string { return EndpointGuilds + gID + "/integrations" } EndpointGuildIntegrations = func(gID string) string { return EndpointGuilds + gID + "/integrations" }
@ -73,6 +75,7 @@ var (
EndpointGuildPrune = func(gID string) string { return EndpointGuilds + gID + "/prune" } EndpointGuildPrune = func(gID string) string { return EndpointGuilds + gID + "/prune" }
EndpointGuildIcon = func(gID, hash string) string { return EndpointGuilds + gID + "/icons/" + hash + ".jpg" } EndpointGuildIcon = func(gID, hash string) string { return EndpointGuilds + gID + "/icons/" + hash + ".jpg" }
EndpointGuildSplash = func(gID, hash string) string { return EndpointGuilds + gID + "/splashes/" + hash + ".jpg" } EndpointGuildSplash = func(gID, hash string) string { return EndpointGuilds + gID + "/splashes/" + hash + ".jpg" }
EndpointGuildWebhooks = func(gID string) string { return EndpointGuilds + gID + "/webhooks" }
EndpointChannel = func(cID string) string { return EndpointChannels + cID } EndpointChannel = func(cID string) string { return EndpointChannels + cID }
EndpointChannelPermissions = func(cID string) string { return EndpointChannels + cID + "/permissions" } EndpointChannelPermissions = func(cID string) string { return EndpointChannels + cID + "/permissions" }
@ -86,6 +89,21 @@ var (
EndpointChannelMessagesPins = func(cID string) string { return EndpointChannel(cID) + "/pins" } EndpointChannelMessagesPins = func(cID string) string { return EndpointChannel(cID) + "/pins" }
EndpointChannelMessagePin = func(cID, mID string) string { return EndpointChannel(cID) + "/pins/" + mID } EndpointChannelMessagePin = func(cID, mID string) string { return EndpointChannel(cID) + "/pins/" + mID }
EndpointChannelWebhooks = func(cID string) string { return EndpointChannel(cID) + "/webhooks" }
EndpointWebhook = func(wID string) string { return EndpointWebhooks + wID }
EndpointWebhookToken = func(wID, token string) string { return EndpointWebhooks + wID + "/" + token }
EndpointMessageReactions = func(cID, mID, eID string) string {
return EndpointChannelMessage(cID, mID) + "/reactions/" + eID
}
EndpointMessageReaction = func(cID, mID, eID, uID string) string {
return EndpointMessageReactions(cID, mID, eID) + "/" + uID
}
EndpointRelationships = func() string { return EndpointUsers + "@me" + "/relationships" }
EndpointRelationship = func(uID string) string { return EndpointRelationships() + "/" + uID }
EndpointRelationshipsMutual = func(uID string) string { return EndpointUsers + uID + "/relationships" }
EndpointInvite = func(iID string) string { return EndpointAPI + "invite/" + iID } EndpointInvite = func(iID string) string { return EndpointAPI + "invite/" + iID }
EndpointIntegrationsJoin = func(iID string) string { return EndpointAPI + "integrations/" + iID + "/join" } EndpointIntegrationsJoin = func(iID string) string { return EndpointAPI + "integrations/" + iID + "/join" }

238
vendor/github.com/bwmarrin/discordgo/event.go generated vendored Normal file
View File

@ -0,0 +1,238 @@
package discordgo
import "fmt"
// EventHandler is an interface for Discord events.
type EventHandler interface {
// Type returns the type of event this handler belongs to.
Type() string
// Handle is called whenever an event of Type() happens.
// It is the recievers responsibility to type assert that the interface
// is the expected struct.
Handle(*Session, interface{})
}
// EventInterfaceProvider is an interface for providing empty interfaces for
// Discord events.
type EventInterfaceProvider interface {
// Type is the type of event this handler belongs to.
Type() string
// New returns a new instance of the struct this event handler handles.
// This is called once per event.
// The struct is provided to all handlers of the same Type().
New() interface{}
}
// interfaceEventType is the event handler type for interface{} events.
const interfaceEventType = "__INTERFACE__"
// interfaceEventHandler is an event handler for interface{} events.
type interfaceEventHandler func(*Session, interface{})
// Type returns the event type for interface{} events.
func (eh interfaceEventHandler) Type() string {
return interfaceEventType
}
// Handle is the handler for an interface{} event.
func (eh interfaceEventHandler) Handle(s *Session, i interface{}) {
eh(s, i)
}
var registeredInterfaceProviders = map[string]EventInterfaceProvider{}
// registerInterfaceProvider registers a provider so that DiscordGo can
// access it's New() method.
func registerInterfaceProvider(eh EventInterfaceProvider) error {
if _, ok := registeredInterfaceProviders[eh.Type()]; ok {
return fmt.Errorf("event %s already registered", eh.Type())
}
registeredInterfaceProviders[eh.Type()] = eh
return nil
}
// eventHandlerInstance is a wrapper around an event handler, as functions
// cannot be compared directly.
type eventHandlerInstance struct {
eventHandler EventHandler
}
// addEventHandler adds an event handler that will be fired anytime
// the Discord WSAPI matching eventHandler.Type() fires.
func (s *Session) addEventHandler(eventHandler EventHandler) func() {
s.handlersMu.Lock()
defer s.handlersMu.Unlock()
if s.handlers == nil {
s.handlers = map[string][]*eventHandlerInstance{}
}
ehi := &eventHandlerInstance{eventHandler}
s.handlers[eventHandler.Type()] = append(s.handlers[eventHandler.Type()], ehi)
return func() {
s.removeEventHandlerInstance(eventHandler.Type(), ehi)
}
}
// addEventHandler adds an event handler that will be fired the next time
// the Discord WSAPI matching eventHandler.Type() fires.
func (s *Session) addEventHandlerOnce(eventHandler EventHandler) func() {
s.handlersMu.Lock()
defer s.handlersMu.Unlock()
if s.onceHandlers == nil {
s.onceHandlers = map[string][]*eventHandlerInstance{}
}
ehi := &eventHandlerInstance{eventHandler}
s.onceHandlers[eventHandler.Type()] = append(s.onceHandlers[eventHandler.Type()], ehi)
return func() {
s.removeEventHandlerInstance(eventHandler.Type(), ehi)
}
}
// AddHandler allows you to add an event handler that will be fired anytime
// the Discord WSAPI event that matches the function fires.
// events.go contains all the Discord WSAPI events that can be fired.
// eg:
// Session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
// })
//
// or:
// Session.AddHandler(func(s *discordgo.Session, m *discordgo.PresenceUpdate) {
// })
// The return value of this method is a function, that when called will remove the
// event handler.
func (s *Session) AddHandler(handler interface{}) func() {
eh := handlerForInterface(handler)
if eh == nil {
s.log(LogError, "Invalid handler type, handler will never be called")
return func() {}
}
return s.addEventHandler(eh)
}
// AddHandlerOnce allows you to add an event handler that will be fired the next time
// the Discord WSAPI event that matches the function fires.
// See AddHandler for more details.
func (s *Session) AddHandlerOnce(handler interface{}) func() {
eh := handlerForInterface(handler)
if eh == nil {
s.log(LogError, "Invalid handler type, handler will never be called")
return func() {}
}
return s.addEventHandlerOnce(eh)
}
// removeEventHandler instance removes an event handler instance.
func (s *Session) removeEventHandlerInstance(t string, ehi *eventHandlerInstance) {
s.handlersMu.Lock()
defer s.handlersMu.Unlock()
handlers := s.handlers[t]
for i := range handlers {
if handlers[i] == ehi {
s.handlers[t] = append(handlers[:i], handlers[i+1:]...)
}
}
onceHandlers := s.onceHandlers[t]
for i := range onceHandlers {
if onceHandlers[i] == ehi {
s.onceHandlers[t] = append(onceHandlers[:i], handlers[i+1:]...)
}
}
}
// Handles calling permanent and once handlers for an event type.
func (s *Session) handle(t string, i interface{}) {
for _, eh := range s.handlers[t] {
go eh.eventHandler.Handle(s, i)
}
if len(s.onceHandlers[t]) > 0 {
for _, eh := range s.onceHandlers[t] {
go eh.eventHandler.Handle(s, i)
}
s.onceHandlers[t] = nil
}
}
// Handles an event type by calling internal methods, firing handlers and firing the
// interface{} event.
func (s *Session) handleEvent(t string, i interface{}) {
s.handlersMu.RLock()
defer s.handlersMu.RUnlock()
// All events are dispatched internally first.
s.onInterface(i)
// Then they are dispatched to anyone handling interface{} events.
s.handle(interfaceEventType, i)
// Finally they are dispatched to any typed handlers.
s.handle(t, i)
}
// setGuildIds will set the GuildID on all the members of a guild.
// This is done as event data does not have it set.
func setGuildIds(g *Guild) {
for _, c := range g.Channels {
c.GuildID = g.ID
}
for _, m := range g.Members {
m.GuildID = g.ID
}
for _, vs := range g.VoiceStates {
vs.GuildID = g.ID
}
}
// onInterface handles all internal events and routes them to the appropriate internal handler.
func (s *Session) onInterface(i interface{}) {
switch t := i.(type) {
case *Ready:
for _, g := range t.Guilds {
setGuildIds(g)
}
s.onReady(t)
case *GuildCreate:
setGuildIds(t.Guild)
case *GuildUpdate:
setGuildIds(t.Guild)
case *Resumed:
s.onResumed(t)
case *VoiceServerUpdate:
go s.onVoiceServerUpdate(t)
case *VoiceStateUpdate:
go s.onVoiceStateUpdate(t)
}
s.State.onInterface(s, i)
}
// onReady handles the ready event.
func (s *Session) onReady(r *Ready) {
// Store the SessionID within the Session struct.
s.sessionID = r.SessionID
// Start the heartbeat to keep the connection alive.
go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval)
}
// onResumed handles the resumed event.
func (s *Session) onResumed(r *Resumed) {
// Start the heartbeat to keep the connection alive.
go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval)
}

977
vendor/github.com/bwmarrin/discordgo/eventhandlers.go generated vendored Normal file
View File

@ -0,0 +1,977 @@
// Code generated by \"eventhandlers\"; DO NOT EDIT
// See events.go
package discordgo
// Following are all the event types.
// Event type values are used to match the events returned by Discord.
// EventTypes surrounded by __ are synthetic and are internal to DiscordGo.
const (
channelCreateEventType = "CHANNEL_CREATE"
channelDeleteEventType = "CHANNEL_DELETE"
channelPinsUpdateEventType = "CHANNEL_PINS_UPDATE"
channelUpdateEventType = "CHANNEL_UPDATE"
connectEventType = "__CONNECT__"
disconnectEventType = "__DISCONNECT__"
eventEventType = "__EVENT__"
guildBanAddEventType = "GUILD_BAN_ADD"
guildBanRemoveEventType = "GUILD_BAN_REMOVE"
guildCreateEventType = "GUILD_CREATE"
guildDeleteEventType = "GUILD_DELETE"
guildEmojisUpdateEventType = "GUILD_EMOJIS_UPDATE"
guildIntegrationsUpdateEventType = "GUILD_INTEGRATIONS_UPDATE"
guildMemberAddEventType = "GUILD_MEMBER_ADD"
guildMemberRemoveEventType = "GUILD_MEMBER_REMOVE"
guildMemberUpdateEventType = "GUILD_MEMBER_UPDATE"
guildMembersChunkEventType = "GUILD_MEMBERS_CHUNK"
guildRoleCreateEventType = "GUILD_ROLE_CREATE"
guildRoleDeleteEventType = "GUILD_ROLE_DELETE"
guildRoleUpdateEventType = "GUILD_ROLE_UPDATE"
guildUpdateEventType = "GUILD_UPDATE"
messageAckEventType = "MESSAGE_ACK"
messageCreateEventType = "MESSAGE_CREATE"
messageDeleteEventType = "MESSAGE_DELETE"
messageReactionAddEventType = "MESSAGE_REACTION_ADD"
messageReactionRemoveEventType = "MESSAGE_REACTION_REMOVE"
messageUpdateEventType = "MESSAGE_UPDATE"
presenceUpdateEventType = "PRESENCE_UPDATE"
presencesReplaceEventType = "PRESENCES_REPLACE"
rateLimitEventType = "__RATE_LIMIT__"
readyEventType = "READY"
relationshipAddEventType = "RELATIONSHIP_ADD"
relationshipRemoveEventType = "RELATIONSHIP_REMOVE"
resumedEventType = "RESUMED"
typingStartEventType = "TYPING_START"
userGuildSettingsUpdateEventType = "USER_GUILD_SETTINGS_UPDATE"
userSettingsUpdateEventType = "USER_SETTINGS_UPDATE"
userUpdateEventType = "USER_UPDATE"
voiceServerUpdateEventType = "VOICE_SERVER_UPDATE"
voiceStateUpdateEventType = "VOICE_STATE_UPDATE"
)
// channelCreateEventHandler is an event handler for ChannelCreate events.
type channelCreateEventHandler func(*Session, *ChannelCreate)
// Type returns the event type for ChannelCreate events.
func (eh channelCreateEventHandler) Type() string {
return channelCreateEventType
}
// New returns a new instance of ChannelCreate.
func (eh channelCreateEventHandler) New() interface{} {
return &ChannelCreate{}
}
// Handle is the handler for ChannelCreate events.
func (eh channelCreateEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*ChannelCreate); ok {
eh(s, t)
}
}
// channelDeleteEventHandler is an event handler for ChannelDelete events.
type channelDeleteEventHandler func(*Session, *ChannelDelete)
// Type returns the event type for ChannelDelete events.
func (eh channelDeleteEventHandler) Type() string {
return channelDeleteEventType
}
// New returns a new instance of ChannelDelete.
func (eh channelDeleteEventHandler) New() interface{} {
return &ChannelDelete{}
}
// Handle is the handler for ChannelDelete events.
func (eh channelDeleteEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*ChannelDelete); ok {
eh(s, t)
}
}
// channelPinsUpdateEventHandler is an event handler for ChannelPinsUpdate events.
type channelPinsUpdateEventHandler func(*Session, *ChannelPinsUpdate)
// Type returns the event type for ChannelPinsUpdate events.
func (eh channelPinsUpdateEventHandler) Type() string {
return channelPinsUpdateEventType
}
// New returns a new instance of ChannelPinsUpdate.
func (eh channelPinsUpdateEventHandler) New() interface{} {
return &ChannelPinsUpdate{}
}
// Handle is the handler for ChannelPinsUpdate events.
func (eh channelPinsUpdateEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*ChannelPinsUpdate); ok {
eh(s, t)
}
}
// channelUpdateEventHandler is an event handler for ChannelUpdate events.
type channelUpdateEventHandler func(*Session, *ChannelUpdate)
// Type returns the event type for ChannelUpdate events.
func (eh channelUpdateEventHandler) Type() string {
return channelUpdateEventType
}
// New returns a new instance of ChannelUpdate.
func (eh channelUpdateEventHandler) New() interface{} {
return &ChannelUpdate{}
}
// Handle is the handler for ChannelUpdate events.
func (eh channelUpdateEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*ChannelUpdate); ok {
eh(s, t)
}
}
// connectEventHandler is an event handler for Connect events.
type connectEventHandler func(*Session, *Connect)
// Type returns the event type for Connect events.
func (eh connectEventHandler) Type() string {
return connectEventType
}
// New returns a new instance of Connect.
func (eh connectEventHandler) New() interface{} {
return &Connect{}
}
// Handle is the handler for Connect events.
func (eh connectEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*Connect); ok {
eh(s, t)
}
}
// disconnectEventHandler is an event handler for Disconnect events.
type disconnectEventHandler func(*Session, *Disconnect)
// Type returns the event type for Disconnect events.
func (eh disconnectEventHandler) Type() string {
return disconnectEventType
}
// New returns a new instance of Disconnect.
func (eh disconnectEventHandler) New() interface{} {
return &Disconnect{}
}
// Handle is the handler for Disconnect events.
func (eh disconnectEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*Disconnect); ok {
eh(s, t)
}
}
// eventEventHandler is an event handler for Event events.
type eventEventHandler func(*Session, *Event)
// Type returns the event type for Event events.
func (eh eventEventHandler) Type() string {
return eventEventType
}
// New returns a new instance of Event.
func (eh eventEventHandler) New() interface{} {
return &Event{}
}
// Handle is the handler for Event events.
func (eh eventEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*Event); ok {
eh(s, t)
}
}
// guildBanAddEventHandler is an event handler for GuildBanAdd events.
type guildBanAddEventHandler func(*Session, *GuildBanAdd)
// Type returns the event type for GuildBanAdd events.
func (eh guildBanAddEventHandler) Type() string {
return guildBanAddEventType
}
// New returns a new instance of GuildBanAdd.
func (eh guildBanAddEventHandler) New() interface{} {
return &GuildBanAdd{}
}
// Handle is the handler for GuildBanAdd events.
func (eh guildBanAddEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*GuildBanAdd); ok {
eh(s, t)
}
}
// guildBanRemoveEventHandler is an event handler for GuildBanRemove events.
type guildBanRemoveEventHandler func(*Session, *GuildBanRemove)
// Type returns the event type for GuildBanRemove events.
func (eh guildBanRemoveEventHandler) Type() string {
return guildBanRemoveEventType
}
// New returns a new instance of GuildBanRemove.
func (eh guildBanRemoveEventHandler) New() interface{} {
return &GuildBanRemove{}
}
// Handle is the handler for GuildBanRemove events.
func (eh guildBanRemoveEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*GuildBanRemove); ok {
eh(s, t)
}
}
// guildCreateEventHandler is an event handler for GuildCreate events.
type guildCreateEventHandler func(*Session, *GuildCreate)
// Type returns the event type for GuildCreate events.
func (eh guildCreateEventHandler) Type() string {
return guildCreateEventType
}
// New returns a new instance of GuildCreate.
func (eh guildCreateEventHandler) New() interface{} {
return &GuildCreate{}
}
// Handle is the handler for GuildCreate events.
func (eh guildCreateEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*GuildCreate); ok {
eh(s, t)
}
}
// guildDeleteEventHandler is an event handler for GuildDelete events.
type guildDeleteEventHandler func(*Session, *GuildDelete)
// Type returns the event type for GuildDelete events.
func (eh guildDeleteEventHandler) Type() string {
return guildDeleteEventType
}
// New returns a new instance of GuildDelete.
func (eh guildDeleteEventHandler) New() interface{} {
return &GuildDelete{}
}
// Handle is the handler for GuildDelete events.
func (eh guildDeleteEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*GuildDelete); ok {
eh(s, t)
}
}
// guildEmojisUpdateEventHandler is an event handler for GuildEmojisUpdate events.
type guildEmojisUpdateEventHandler func(*Session, *GuildEmojisUpdate)
// Type returns the event type for GuildEmojisUpdate events.
func (eh guildEmojisUpdateEventHandler) Type() string {
return guildEmojisUpdateEventType
}
// New returns a new instance of GuildEmojisUpdate.
func (eh guildEmojisUpdateEventHandler) New() interface{} {
return &GuildEmojisUpdate{}
}
// Handle is the handler for GuildEmojisUpdate events.
func (eh guildEmojisUpdateEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*GuildEmojisUpdate); ok {
eh(s, t)
}
}
// guildIntegrationsUpdateEventHandler is an event handler for GuildIntegrationsUpdate events.
type guildIntegrationsUpdateEventHandler func(*Session, *GuildIntegrationsUpdate)
// Type returns the event type for GuildIntegrationsUpdate events.
func (eh guildIntegrationsUpdateEventHandler) Type() string {
return guildIntegrationsUpdateEventType
}
// New returns a new instance of GuildIntegrationsUpdate.
func (eh guildIntegrationsUpdateEventHandler) New() interface{} {
return &GuildIntegrationsUpdate{}
}
// Handle is the handler for GuildIntegrationsUpdate events.
func (eh guildIntegrationsUpdateEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*GuildIntegrationsUpdate); ok {
eh(s, t)
}
}
// guildMemberAddEventHandler is an event handler for GuildMemberAdd events.
type guildMemberAddEventHandler func(*Session, *GuildMemberAdd)
// Type returns the event type for GuildMemberAdd events.
func (eh guildMemberAddEventHandler) Type() string {
return guildMemberAddEventType
}
// New returns a new instance of GuildMemberAdd.
func (eh guildMemberAddEventHandler) New() interface{} {
return &GuildMemberAdd{}
}
// Handle is the handler for GuildMemberAdd events.
func (eh guildMemberAddEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*GuildMemberAdd); ok {
eh(s, t)
}
}
// guildMemberRemoveEventHandler is an event handler for GuildMemberRemove events.
type guildMemberRemoveEventHandler func(*Session, *GuildMemberRemove)
// Type returns the event type for GuildMemberRemove events.
func (eh guildMemberRemoveEventHandler) Type() string {
return guildMemberRemoveEventType
}
// New returns a new instance of GuildMemberRemove.
func (eh guildMemberRemoveEventHandler) New() interface{} {
return &GuildMemberRemove{}
}
// Handle is the handler for GuildMemberRemove events.
func (eh guildMemberRemoveEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*GuildMemberRemove); ok {
eh(s, t)
}
}
// guildMemberUpdateEventHandler is an event handler for GuildMemberUpdate events.
type guildMemberUpdateEventHandler func(*Session, *GuildMemberUpdate)
// Type returns the event type for GuildMemberUpdate events.
func (eh guildMemberUpdateEventHandler) Type() string {
return guildMemberUpdateEventType
}
// New returns a new instance of GuildMemberUpdate.
func (eh guildMemberUpdateEventHandler) New() interface{} {
return &GuildMemberUpdate{}
}
// Handle is the handler for GuildMemberUpdate events.
func (eh guildMemberUpdateEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*GuildMemberUpdate); ok {
eh(s, t)
}
}
// guildMembersChunkEventHandler is an event handler for GuildMembersChunk events.
type guildMembersChunkEventHandler func(*Session, *GuildMembersChunk)
// Type returns the event type for GuildMembersChunk events.
func (eh guildMembersChunkEventHandler) Type() string {
return guildMembersChunkEventType
}
// New returns a new instance of GuildMembersChunk.
func (eh guildMembersChunkEventHandler) New() interface{} {
return &GuildMembersChunk{}
}
// Handle is the handler for GuildMembersChunk events.
func (eh guildMembersChunkEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*GuildMembersChunk); ok {
eh(s, t)
}
}
// guildRoleCreateEventHandler is an event handler for GuildRoleCreate events.
type guildRoleCreateEventHandler func(*Session, *GuildRoleCreate)
// Type returns the event type for GuildRoleCreate events.
func (eh guildRoleCreateEventHandler) Type() string {
return guildRoleCreateEventType
}
// New returns a new instance of GuildRoleCreate.
func (eh guildRoleCreateEventHandler) New() interface{} {
return &GuildRoleCreate{}
}
// Handle is the handler for GuildRoleCreate events.
func (eh guildRoleCreateEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*GuildRoleCreate); ok {
eh(s, t)
}
}
// guildRoleDeleteEventHandler is an event handler for GuildRoleDelete events.
type guildRoleDeleteEventHandler func(*Session, *GuildRoleDelete)
// Type returns the event type for GuildRoleDelete events.
func (eh guildRoleDeleteEventHandler) Type() string {
return guildRoleDeleteEventType
}
// New returns a new instance of GuildRoleDelete.
func (eh guildRoleDeleteEventHandler) New() interface{} {
return &GuildRoleDelete{}
}
// Handle is the handler for GuildRoleDelete events.
func (eh guildRoleDeleteEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*GuildRoleDelete); ok {
eh(s, t)
}
}
// guildRoleUpdateEventHandler is an event handler for GuildRoleUpdate events.
type guildRoleUpdateEventHandler func(*Session, *GuildRoleUpdate)
// Type returns the event type for GuildRoleUpdate events.
func (eh guildRoleUpdateEventHandler) Type() string {
return guildRoleUpdateEventType
}
// New returns a new instance of GuildRoleUpdate.
func (eh guildRoleUpdateEventHandler) New() interface{} {
return &GuildRoleUpdate{}
}
// Handle is the handler for GuildRoleUpdate events.
func (eh guildRoleUpdateEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*GuildRoleUpdate); ok {
eh(s, t)
}
}
// guildUpdateEventHandler is an event handler for GuildUpdate events.
type guildUpdateEventHandler func(*Session, *GuildUpdate)
// Type returns the event type for GuildUpdate events.
func (eh guildUpdateEventHandler) Type() string {
return guildUpdateEventType
}
// New returns a new instance of GuildUpdate.
func (eh guildUpdateEventHandler) New() interface{} {
return &GuildUpdate{}
}
// Handle is the handler for GuildUpdate events.
func (eh guildUpdateEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*GuildUpdate); ok {
eh(s, t)
}
}
// messageAckEventHandler is an event handler for MessageAck events.
type messageAckEventHandler func(*Session, *MessageAck)
// Type returns the event type for MessageAck events.
func (eh messageAckEventHandler) Type() string {
return messageAckEventType
}
// New returns a new instance of MessageAck.
func (eh messageAckEventHandler) New() interface{} {
return &MessageAck{}
}
// Handle is the handler for MessageAck events.
func (eh messageAckEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*MessageAck); ok {
eh(s, t)
}
}
// messageCreateEventHandler is an event handler for MessageCreate events.
type messageCreateEventHandler func(*Session, *MessageCreate)
// Type returns the event type for MessageCreate events.
func (eh messageCreateEventHandler) Type() string {
return messageCreateEventType
}
// New returns a new instance of MessageCreate.
func (eh messageCreateEventHandler) New() interface{} {
return &MessageCreate{}
}
// Handle is the handler for MessageCreate events.
func (eh messageCreateEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*MessageCreate); ok {
eh(s, t)
}
}
// messageDeleteEventHandler is an event handler for MessageDelete events.
type messageDeleteEventHandler func(*Session, *MessageDelete)
// Type returns the event type for MessageDelete events.
func (eh messageDeleteEventHandler) Type() string {
return messageDeleteEventType
}
// New returns a new instance of MessageDelete.
func (eh messageDeleteEventHandler) New() interface{} {
return &MessageDelete{}
}
// Handle is the handler for MessageDelete events.
func (eh messageDeleteEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*MessageDelete); ok {
eh(s, t)
}
}
// messageReactionAddEventHandler is an event handler for MessageReactionAdd events.
type messageReactionAddEventHandler func(*Session, *MessageReactionAdd)
// Type returns the event type for MessageReactionAdd events.
func (eh messageReactionAddEventHandler) Type() string {
return messageReactionAddEventType
}
// New returns a new instance of MessageReactionAdd.
func (eh messageReactionAddEventHandler) New() interface{} {
return &MessageReactionAdd{}
}
// Handle is the handler for MessageReactionAdd events.
func (eh messageReactionAddEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*MessageReactionAdd); ok {
eh(s, t)
}
}
// messageReactionRemoveEventHandler is an event handler for MessageReactionRemove events.
type messageReactionRemoveEventHandler func(*Session, *MessageReactionRemove)
// Type returns the event type for MessageReactionRemove events.
func (eh messageReactionRemoveEventHandler) Type() string {
return messageReactionRemoveEventType
}
// New returns a new instance of MessageReactionRemove.
func (eh messageReactionRemoveEventHandler) New() interface{} {
return &MessageReactionRemove{}
}
// Handle is the handler for MessageReactionRemove events.
func (eh messageReactionRemoveEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*MessageReactionRemove); ok {
eh(s, t)
}
}
// messageUpdateEventHandler is an event handler for MessageUpdate events.
type messageUpdateEventHandler func(*Session, *MessageUpdate)
// Type returns the event type for MessageUpdate events.
func (eh messageUpdateEventHandler) Type() string {
return messageUpdateEventType
}
// New returns a new instance of MessageUpdate.
func (eh messageUpdateEventHandler) New() interface{} {
return &MessageUpdate{}
}
// Handle is the handler for MessageUpdate events.
func (eh messageUpdateEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*MessageUpdate); ok {
eh(s, t)
}
}
// presenceUpdateEventHandler is an event handler for PresenceUpdate events.
type presenceUpdateEventHandler func(*Session, *PresenceUpdate)
// Type returns the event type for PresenceUpdate events.
func (eh presenceUpdateEventHandler) Type() string {
return presenceUpdateEventType
}
// New returns a new instance of PresenceUpdate.
func (eh presenceUpdateEventHandler) New() interface{} {
return &PresenceUpdate{}
}
// Handle is the handler for PresenceUpdate events.
func (eh presenceUpdateEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*PresenceUpdate); ok {
eh(s, t)
}
}
// presencesReplaceEventHandler is an event handler for PresencesReplace events.
type presencesReplaceEventHandler func(*Session, *PresencesReplace)
// Type returns the event type for PresencesReplace events.
func (eh presencesReplaceEventHandler) Type() string {
return presencesReplaceEventType
}
// New returns a new instance of PresencesReplace.
func (eh presencesReplaceEventHandler) New() interface{} {
return &PresencesReplace{}
}
// Handle is the handler for PresencesReplace events.
func (eh presencesReplaceEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*PresencesReplace); ok {
eh(s, t)
}
}
// rateLimitEventHandler is an event handler for RateLimit events.
type rateLimitEventHandler func(*Session, *RateLimit)
// Type returns the event type for RateLimit events.
func (eh rateLimitEventHandler) Type() string {
return rateLimitEventType
}
// New returns a new instance of RateLimit.
func (eh rateLimitEventHandler) New() interface{} {
return &RateLimit{}
}
// Handle is the handler for RateLimit events.
func (eh rateLimitEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*RateLimit); ok {
eh(s, t)
}
}
// readyEventHandler is an event handler for Ready events.
type readyEventHandler func(*Session, *Ready)
// Type returns the event type for Ready events.
func (eh readyEventHandler) Type() string {
return readyEventType
}
// New returns a new instance of Ready.
func (eh readyEventHandler) New() interface{} {
return &Ready{}
}
// Handle is the handler for Ready events.
func (eh readyEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*Ready); ok {
eh(s, t)
}
}
// relationshipAddEventHandler is an event handler for RelationshipAdd events.
type relationshipAddEventHandler func(*Session, *RelationshipAdd)
// Type returns the event type for RelationshipAdd events.
func (eh relationshipAddEventHandler) Type() string {
return relationshipAddEventType
}
// New returns a new instance of RelationshipAdd.
func (eh relationshipAddEventHandler) New() interface{} {
return &RelationshipAdd{}
}
// Handle is the handler for RelationshipAdd events.
func (eh relationshipAddEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*RelationshipAdd); ok {
eh(s, t)
}
}
// relationshipRemoveEventHandler is an event handler for RelationshipRemove events.
type relationshipRemoveEventHandler func(*Session, *RelationshipRemove)
// Type returns the event type for RelationshipRemove events.
func (eh relationshipRemoveEventHandler) Type() string {
return relationshipRemoveEventType
}
// New returns a new instance of RelationshipRemove.
func (eh relationshipRemoveEventHandler) New() interface{} {
return &RelationshipRemove{}
}
// Handle is the handler for RelationshipRemove events.
func (eh relationshipRemoveEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*RelationshipRemove); ok {
eh(s, t)
}
}
// resumedEventHandler is an event handler for Resumed events.
type resumedEventHandler func(*Session, *Resumed)
// Type returns the event type for Resumed events.
func (eh resumedEventHandler) Type() string {
return resumedEventType
}
// New returns a new instance of Resumed.
func (eh resumedEventHandler) New() interface{} {
return &Resumed{}
}
// Handle is the handler for Resumed events.
func (eh resumedEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*Resumed); ok {
eh(s, t)
}
}
// typingStartEventHandler is an event handler for TypingStart events.
type typingStartEventHandler func(*Session, *TypingStart)
// Type returns the event type for TypingStart events.
func (eh typingStartEventHandler) Type() string {
return typingStartEventType
}
// New returns a new instance of TypingStart.
func (eh typingStartEventHandler) New() interface{} {
return &TypingStart{}
}
// Handle is the handler for TypingStart events.
func (eh typingStartEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*TypingStart); ok {
eh(s, t)
}
}
// userGuildSettingsUpdateEventHandler is an event handler for UserGuildSettingsUpdate events.
type userGuildSettingsUpdateEventHandler func(*Session, *UserGuildSettingsUpdate)
// Type returns the event type for UserGuildSettingsUpdate events.
func (eh userGuildSettingsUpdateEventHandler) Type() string {
return userGuildSettingsUpdateEventType
}
// New returns a new instance of UserGuildSettingsUpdate.
func (eh userGuildSettingsUpdateEventHandler) New() interface{} {
return &UserGuildSettingsUpdate{}
}
// Handle is the handler for UserGuildSettingsUpdate events.
func (eh userGuildSettingsUpdateEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*UserGuildSettingsUpdate); ok {
eh(s, t)
}
}
// userSettingsUpdateEventHandler is an event handler for UserSettingsUpdate events.
type userSettingsUpdateEventHandler func(*Session, *UserSettingsUpdate)
// Type returns the event type for UserSettingsUpdate events.
func (eh userSettingsUpdateEventHandler) Type() string {
return userSettingsUpdateEventType
}
// New returns a new instance of UserSettingsUpdate.
func (eh userSettingsUpdateEventHandler) New() interface{} {
return &UserSettingsUpdate{}
}
// Handle is the handler for UserSettingsUpdate events.
func (eh userSettingsUpdateEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*UserSettingsUpdate); ok {
eh(s, t)
}
}
// userUpdateEventHandler is an event handler for UserUpdate events.
type userUpdateEventHandler func(*Session, *UserUpdate)
// Type returns the event type for UserUpdate events.
func (eh userUpdateEventHandler) Type() string {
return userUpdateEventType
}
// New returns a new instance of UserUpdate.
func (eh userUpdateEventHandler) New() interface{} {
return &UserUpdate{}
}
// Handle is the handler for UserUpdate events.
func (eh userUpdateEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*UserUpdate); ok {
eh(s, t)
}
}
// voiceServerUpdateEventHandler is an event handler for VoiceServerUpdate events.
type voiceServerUpdateEventHandler func(*Session, *VoiceServerUpdate)
// Type returns the event type for VoiceServerUpdate events.
func (eh voiceServerUpdateEventHandler) Type() string {
return voiceServerUpdateEventType
}
// New returns a new instance of VoiceServerUpdate.
func (eh voiceServerUpdateEventHandler) New() interface{} {
return &VoiceServerUpdate{}
}
// Handle is the handler for VoiceServerUpdate events.
func (eh voiceServerUpdateEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*VoiceServerUpdate); ok {
eh(s, t)
}
}
// voiceStateUpdateEventHandler is an event handler for VoiceStateUpdate events.
type voiceStateUpdateEventHandler func(*Session, *VoiceStateUpdate)
// Type returns the event type for VoiceStateUpdate events.
func (eh voiceStateUpdateEventHandler) Type() string {
return voiceStateUpdateEventType
}
// New returns a new instance of VoiceStateUpdate.
func (eh voiceStateUpdateEventHandler) New() interface{} {
return &VoiceStateUpdate{}
}
// Handle is the handler for VoiceStateUpdate events.
func (eh voiceStateUpdateEventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*VoiceStateUpdate); ok {
eh(s, t)
}
}
func handlerForInterface(handler interface{}) EventHandler {
switch v := handler.(type) {
case func(*Session, interface{}):
return interfaceEventHandler(v)
case func(*Session, *ChannelCreate):
return channelCreateEventHandler(v)
case func(*Session, *ChannelDelete):
return channelDeleteEventHandler(v)
case func(*Session, *ChannelPinsUpdate):
return channelPinsUpdateEventHandler(v)
case func(*Session, *ChannelUpdate):
return channelUpdateEventHandler(v)
case func(*Session, *Connect):
return connectEventHandler(v)
case func(*Session, *Disconnect):
return disconnectEventHandler(v)
case func(*Session, *Event):
return eventEventHandler(v)
case func(*Session, *GuildBanAdd):
return guildBanAddEventHandler(v)
case func(*Session, *GuildBanRemove):
return guildBanRemoveEventHandler(v)
case func(*Session, *GuildCreate):
return guildCreateEventHandler(v)
case func(*Session, *GuildDelete):
return guildDeleteEventHandler(v)
case func(*Session, *GuildEmojisUpdate):
return guildEmojisUpdateEventHandler(v)
case func(*Session, *GuildIntegrationsUpdate):
return guildIntegrationsUpdateEventHandler(v)
case func(*Session, *GuildMemberAdd):
return guildMemberAddEventHandler(v)
case func(*Session, *GuildMemberRemove):
return guildMemberRemoveEventHandler(v)
case func(*Session, *GuildMemberUpdate):
return guildMemberUpdateEventHandler(v)
case func(*Session, *GuildMembersChunk):
return guildMembersChunkEventHandler(v)
case func(*Session, *GuildRoleCreate):
return guildRoleCreateEventHandler(v)
case func(*Session, *GuildRoleDelete):
return guildRoleDeleteEventHandler(v)
case func(*Session, *GuildRoleUpdate):
return guildRoleUpdateEventHandler(v)
case func(*Session, *GuildUpdate):
return guildUpdateEventHandler(v)
case func(*Session, *MessageAck):
return messageAckEventHandler(v)
case func(*Session, *MessageCreate):
return messageCreateEventHandler(v)
case func(*Session, *MessageDelete):
return messageDeleteEventHandler(v)
case func(*Session, *MessageReactionAdd):
return messageReactionAddEventHandler(v)
case func(*Session, *MessageReactionRemove):
return messageReactionRemoveEventHandler(v)
case func(*Session, *MessageUpdate):
return messageUpdateEventHandler(v)
case func(*Session, *PresenceUpdate):
return presenceUpdateEventHandler(v)
case func(*Session, *PresencesReplace):
return presencesReplaceEventHandler(v)
case func(*Session, *RateLimit):
return rateLimitEventHandler(v)
case func(*Session, *Ready):
return readyEventHandler(v)
case func(*Session, *RelationshipAdd):
return relationshipAddEventHandler(v)
case func(*Session, *RelationshipRemove):
return relationshipRemoveEventHandler(v)
case func(*Session, *Resumed):
return resumedEventHandler(v)
case func(*Session, *TypingStart):
return typingStartEventHandler(v)
case func(*Session, *UserGuildSettingsUpdate):
return userGuildSettingsUpdateEventHandler(v)
case func(*Session, *UserSettingsUpdate):
return userSettingsUpdateEventHandler(v)
case func(*Session, *UserUpdate):
return userUpdateEventHandler(v)
case func(*Session, *VoiceServerUpdate):
return voiceServerUpdateEventHandler(v)
case func(*Session, *VoiceStateUpdate):
return voiceStateUpdateEventHandler(v)
}
return nil
}
func init() {
registerInterfaceProvider(channelCreateEventHandler(nil))
registerInterfaceProvider(channelDeleteEventHandler(nil))
registerInterfaceProvider(channelPinsUpdateEventHandler(nil))
registerInterfaceProvider(channelUpdateEventHandler(nil))
registerInterfaceProvider(guildBanAddEventHandler(nil))
registerInterfaceProvider(guildBanRemoveEventHandler(nil))
registerInterfaceProvider(guildCreateEventHandler(nil))
registerInterfaceProvider(guildDeleteEventHandler(nil))
registerInterfaceProvider(guildEmojisUpdateEventHandler(nil))
registerInterfaceProvider(guildIntegrationsUpdateEventHandler(nil))
registerInterfaceProvider(guildMemberAddEventHandler(nil))
registerInterfaceProvider(guildMemberRemoveEventHandler(nil))
registerInterfaceProvider(guildMemberUpdateEventHandler(nil))
registerInterfaceProvider(guildMembersChunkEventHandler(nil))
registerInterfaceProvider(guildRoleCreateEventHandler(nil))
registerInterfaceProvider(guildRoleDeleteEventHandler(nil))
registerInterfaceProvider(guildRoleUpdateEventHandler(nil))
registerInterfaceProvider(guildUpdateEventHandler(nil))
registerInterfaceProvider(messageAckEventHandler(nil))
registerInterfaceProvider(messageCreateEventHandler(nil))
registerInterfaceProvider(messageDeleteEventHandler(nil))
registerInterfaceProvider(messageReactionAddEventHandler(nil))
registerInterfaceProvider(messageReactionRemoveEventHandler(nil))
registerInterfaceProvider(messageUpdateEventHandler(nil))
registerInterfaceProvider(presenceUpdateEventHandler(nil))
registerInterfaceProvider(presencesReplaceEventHandler(nil))
registerInterfaceProvider(readyEventHandler(nil))
registerInterfaceProvider(relationshipAddEventHandler(nil))
registerInterfaceProvider(relationshipRemoveEventHandler(nil))
registerInterfaceProvider(resumedEventHandler(nil))
registerInterfaceProvider(typingStartEventHandler(nil))
registerInterfaceProvider(userGuildSettingsUpdateEventHandler(nil))
registerInterfaceProvider(userSettingsUpdateEventHandler(nil))
registerInterfaceProvider(userUpdateEventHandler(nil))
registerInterfaceProvider(voiceServerUpdateEventHandler(nil))
registerInterfaceProvider(voiceStateUpdateEventHandler(nil))
}

View File

@ -1,159 +1,238 @@
package discordgo package discordgo
// eventToInterface is a mapping of Discord WSAPI events to their import (
// DiscordGo event container. "encoding/json"
// Each Discord WSAPI event maps to a unique interface. "time"
// Use Session.AddHandler with one of these types to handle that )
// type of event.
// eg:
// Session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
// })
//
// or:
// Session.AddHandler(func(s *discordgo.Session, m *discordgo.PresenceUpdate) {
// })
var eventToInterface = map[string]interface{}{
"CHANNEL_CREATE": ChannelCreate{},
"CHANNEL_UPDATE": ChannelUpdate{},
"CHANNEL_DELETE": ChannelDelete{},
"GUILD_CREATE": GuildCreate{},
"GUILD_UPDATE": GuildUpdate{},
"GUILD_DELETE": GuildDelete{},
"GUILD_BAN_ADD": GuildBanAdd{},
"GUILD_BAN_REMOVE": GuildBanRemove{},
"GUILD_MEMBER_ADD": GuildMemberAdd{},
"GUILD_MEMBER_UPDATE": GuildMemberUpdate{},
"GUILD_MEMBER_REMOVE": GuildMemberRemove{},
"GUILD_ROLE_CREATE": GuildRoleCreate{},
"GUILD_ROLE_UPDATE": GuildRoleUpdate{},
"GUILD_ROLE_DELETE": GuildRoleDelete{},
"GUILD_INTEGRATIONS_UPDATE": GuildIntegrationsUpdate{},
"GUILD_EMOJIS_UPDATE": GuildEmojisUpdate{},
"MESSAGE_ACK": MessageAck{},
"MESSAGE_CREATE": MessageCreate{},
"MESSAGE_UPDATE": MessageUpdate{},
"MESSAGE_DELETE": MessageDelete{},
"PRESENCE_UPDATE": PresenceUpdate{},
"PRESENCES_REPLACE": PresencesReplace{},
"READY": Ready{},
"USER_UPDATE": UserUpdate{},
"USER_SETTINGS_UPDATE": UserSettingsUpdate{},
"USER_GUILD_SETTINGS_UPDATE": UserGuildSettingsUpdate{},
"TYPING_START": TypingStart{},
"VOICE_SERVER_UPDATE": VoiceServerUpdate{},
"VOICE_STATE_UPDATE": VoiceStateUpdate{},
"RESUMED": Resumed{},
}
// Connect is an empty struct for an event. // This file contains all the possible structs that can be
// handled by AddHandler/EventHandler.
// DO NOT ADD ANYTHING BUT EVENT HANDLER STRUCTS TO THIS FILE.
//go:generate go run tools/cmd/eventhandlers/main.go
// Connect is the data for a Connect event.
// This is a sythetic event and is not dispatched by Discord.
type Connect struct{} type Connect struct{}
// Disconnect is an empty struct for an event. // Disconnect is the data for a Disconnect event.
// This is a sythetic event and is not dispatched by Discord.
type Disconnect struct{} type Disconnect struct{}
// RateLimit is a struct for the RateLimited event // RateLimit is the data for a RateLimit event.
// This is a sythetic event and is not dispatched by Discord.
type RateLimit struct { type RateLimit struct {
*TooManyRequests *TooManyRequests
URL string URL string
} }
// MessageCreate is a wrapper struct for an event. // Event provides a basic initial struct for all websocket events.
type MessageCreate struct { type Event struct {
*Message Operation int `json:"op"`
Sequence int `json:"s"`
Type string `json:"t"`
RawData json.RawMessage `json:"d"`
// Struct contains one of the other types in this file.
Struct interface{} `json:"-"`
} }
// MessageUpdate is a wrapper struct for an event. // A Ready stores all data for the websocket READY event.
type MessageUpdate struct { type Ready struct {
*Message Version int `json:"v"`
SessionID string `json:"session_id"`
HeartbeatInterval time.Duration `json:"heartbeat_interval"`
User *User `json:"user"`
ReadState []*ReadState `json:"read_state"`
PrivateChannels []*Channel `json:"private_channels"`
Guilds []*Guild `json:"guilds"`
// Undocumented fields
Settings *Settings `json:"user_settings"`
UserGuildSettings []*UserGuildSettings `json:"user_guild_settings"`
Relationships []*Relationship `json:"relationships"`
Presences []*Presence `json:"presences"`
} }
// MessageDelete is a wrapper struct for an event. // ChannelCreate is the data for a ChannelCreate event.
type MessageDelete struct {
*Message
}
// ChannelCreate is a wrapper struct for an event.
type ChannelCreate struct { type ChannelCreate struct {
*Channel *Channel
} }
// ChannelUpdate is a wrapper struct for an event. // ChannelUpdate is the data for a ChannelUpdate event.
type ChannelUpdate struct { type ChannelUpdate struct {
*Channel *Channel
} }
// ChannelDelete is a wrapper struct for an event. // ChannelDelete is the data for a ChannelDelete event.
type ChannelDelete struct { type ChannelDelete struct {
*Channel *Channel
} }
// GuildCreate is a wrapper struct for an event. // ChannelPinsUpdate stores data for a ChannelPinsUpdate event.
type ChannelPinsUpdate struct {
LastPinTimestamp string `json:"last_pin_timestamp"`
ChannelID string `json:"channel_id"`
}
// GuildCreate is the data for a GuildCreate event.
type GuildCreate struct { type GuildCreate struct {
*Guild *Guild
} }
// GuildUpdate is a wrapper struct for an event. // GuildUpdate is the data for a GuildUpdate event.
type GuildUpdate struct { type GuildUpdate struct {
*Guild *Guild
} }
// GuildDelete is a wrapper struct for an event. // GuildDelete is the data for a GuildDelete event.
type GuildDelete struct { type GuildDelete struct {
*Guild *Guild
} }
// GuildBanAdd is a wrapper struct for an event. // GuildBanAdd is the data for a GuildBanAdd event.
type GuildBanAdd struct { type GuildBanAdd struct {
*GuildBan User *User `json:"user"`
GuildID string `json:"guild_id"`
} }
// GuildBanRemove is a wrapper struct for an event. // GuildBanRemove is the data for a GuildBanRemove event.
type GuildBanRemove struct { type GuildBanRemove struct {
*GuildBan User *User `json:"user"`
GuildID string `json:"guild_id"`
} }
// GuildMemberAdd is a wrapper struct for an event. // GuildMemberAdd is the data for a GuildMemberAdd event.
type GuildMemberAdd struct { type GuildMemberAdd struct {
*Member *Member
} }
// GuildMemberUpdate is a wrapper struct for an event. // GuildMemberUpdate is the data for a GuildMemberUpdate event.
type GuildMemberUpdate struct { type GuildMemberUpdate struct {
*Member *Member
} }
// GuildMemberRemove is a wrapper struct for an event. // GuildMemberRemove is the data for a GuildMemberRemove event.
type GuildMemberRemove struct { type GuildMemberRemove struct {
*Member *Member
} }
// GuildRoleCreate is a wrapper struct for an event. // GuildRoleCreate is the data for a GuildRoleCreate event.
type GuildRoleCreate struct { type GuildRoleCreate struct {
*GuildRole *GuildRole
} }
// GuildRoleUpdate is a wrapper struct for an event. // GuildRoleUpdate is the data for a GuildRoleUpdate event.
type GuildRoleUpdate struct { type GuildRoleUpdate struct {
*GuildRole *GuildRole
} }
// PresencesReplace is an array of Presences for an event. // A GuildRoleDelete is the data for a GuildRoleDelete event.
type PresencesReplace []*Presence type GuildRoleDelete struct {
RoleID string `json:"role_id"`
// VoiceStateUpdate is a wrapper struct for an event. GuildID string `json:"guild_id"`
type VoiceStateUpdate struct {
*VoiceState
} }
// UserUpdate is a wrapper struct for an event. // A GuildEmojisUpdate is the data for a guild emoji update event.
type GuildEmojisUpdate struct {
GuildID string `json:"guild_id"`
Emojis []*Emoji `json:"emojis"`
}
// A GuildMembersChunk is the data for a GuildMembersChunk event.
type GuildMembersChunk struct {
GuildID string `json:"guild_id"`
Members []*Member `json:"members"`
}
// GuildIntegrationsUpdate is the data for a GuildIntegrationsUpdate event.
type GuildIntegrationsUpdate struct {
GuildID string `json:"guild_id"`
}
// MessageAck is the data for a MessageAck event.
type MessageAck struct {
MessageID string `json:"message_id"`
ChannelID string `json:"channel_id"`
}
// MessageCreate is the data for a MessageCreate event.
type MessageCreate struct {
*Message
}
// MessageUpdate is the data for a MessageUpdate event.
type MessageUpdate struct {
*Message
}
// MessageDelete is the data for a MessageDelete event.
type MessageDelete struct {
*Message
}
// MessageReactionAdd is the data for a MessageReactionAdd event.
type MessageReactionAdd struct {
*MessageReaction
}
// MessageReactionRemove is the data for a MessageReactionRemove event.
type MessageReactionRemove struct {
*MessageReaction
}
// PresencesReplace is the data for a PresencesReplace event.
type PresencesReplace []*Presence
// PresenceUpdate is the data for a PresenceUpdate event.
type PresenceUpdate struct {
Presence
GuildID string `json:"guild_id"`
Roles []string `json:"roles"`
}
// Resumed is the data for a Resumed event.
type Resumed struct {
HeartbeatInterval time.Duration `json:"heartbeat_interval"`
Trace []string `json:"_trace"`
}
// RelationshipAdd is the data for a RelationshipAdd event.
type RelationshipAdd struct {
*Relationship
}
// RelationshipRemove is the data for a RelationshipRemove event.
type RelationshipRemove struct {
*Relationship
}
// TypingStart is the data for a TypingStart event.
type TypingStart struct {
UserID string `json:"user_id"`
ChannelID string `json:"channel_id"`
Timestamp int `json:"timestamp"`
}
// UserUpdate is the data for a UserUpdate event.
type UserUpdate struct { type UserUpdate struct {
*User *User
} }
// UserSettingsUpdate is a map for an event. // UserSettingsUpdate is the data for a UserSettingsUpdate event.
type UserSettingsUpdate map[string]interface{} type UserSettingsUpdate map[string]interface{}
// UserGuildSettingsUpdate is a map for an event. // UserGuildSettingsUpdate is the data for a UserGuildSettingsUpdate event.
type UserGuildSettingsUpdate struct { type UserGuildSettingsUpdate struct {
*UserGuildSettings *UserGuildSettings
} }
// VoiceServerUpdate is the data for a VoiceServerUpdate event.
type VoiceServerUpdate struct {
Token string `json:"token"`
GuildID string `json:"guild_id"`
Endpoint string `json:"endpoint"`
}
// VoiceStateUpdate is the data for a VoiceStateUpdate event.
type VoiceStateUpdate struct {
*VoiceState
}

View File

@ -13,7 +13,7 @@ import (
) )
func init() { func init() {
flag.StringVar(&token, "t", "", "Account Token") flag.StringVar(&token, "t", "", "Bot Token")
flag.Parse() flag.Parse()
} }
@ -34,8 +34,8 @@ func main() {
return return
} }
// Create a new Discord session using the provided token. // Create a new Discord session using the provided bot token.
dg, err := discordgo.New(token) dg, err := discordgo.New("Bot " + token)
if err != nil { if err != nil {
fmt.Println("Error creating Discord session: ", err) fmt.Println("Error creating Discord session: ", err)
return return
@ -102,7 +102,7 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
// This function will be called (due to AddHandler above) every time a new // This function will be called (due to AddHandler above) every time a new
// guild is joined. // guild is joined.
func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) { func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) {
if event.Guild.Unavailable != nil { if event.Guild.Unavailable {
return return
} }
@ -131,6 +131,10 @@ func loadSound() error {
// If this is the end of the file, just return. // If this is the end of the file, just return.
if err == io.EOF || err == io.ErrUnexpectedEOF { if err == io.EOF || err == io.ErrUnexpectedEOF {
file.Close()
if err != nil {
return err
}
return nil return nil
} }

View File

@ -10,24 +10,19 @@ import (
// Variables used for command line parameters // Variables used for command line parameters
var ( var (
Email string
Password string
Token string Token string
) )
func init() { func init() {
flag.StringVar(&Email, "e", "", "Account Email") flag.StringVar(&Token, "t", "", "Bot Token")
flag.StringVar(&Password, "p", "", "Account Password")
flag.StringVar(&Token, "t", "", "Account Token")
flag.Parse() flag.Parse()
} }
func main() { func main() {
// Create a new Discord session using the provided login information. // Create a new Discord session using the provided bot token.
// Use discordgo.New(Token) to just use a token for login. dg, err := discordgo.New("Bot " + Token)
dg, err := discordgo.New(Email, Password, Token)
if err != nil { if err != nil {
fmt.Println("error creating Discord session,", err) fmt.Println("error creating Discord session,", err)
return return

View File

@ -9,24 +9,20 @@ import (
// Variables used for command line parameters // Variables used for command line parameters
var ( var (
Email string
Password string
Token string Token string
BotID string BotID string
) )
func init() { func init() {
flag.StringVar(&Email, "e", "", "Account Email") flag.StringVar(&Token, "t", "", "Bot Token")
flag.StringVar(&Password, "p", "", "Account Password")
flag.StringVar(&Token, "t", "", "Account Token")
flag.Parse() flag.Parse()
} }
func main() { func main() {
// Create a new Discord session using the provided login information. // Create a new Discord session using the provided bot token.
dg, err := discordgo.New(Email, Password, Token) dg, err := discordgo.New("Bot " + Token)
if err != nil { if err != nil {
fmt.Println("error creating Discord session,", err) fmt.Println("error creating Discord session,", err)
return return

View File

@ -19,8 +19,8 @@ type Message struct {
ID string `json:"id"` ID string `json:"id"`
ChannelID string `json:"channel_id"` ChannelID string `json:"channel_id"`
Content string `json:"content"` Content string `json:"content"`
Timestamp string `json:"timestamp"` Timestamp Timestamp `json:"timestamp"`
EditedTimestamp string `json:"edited_timestamp"` EditedTimestamp Timestamp `json:"edited_timestamp"`
MentionRoles []string `json:"mention_roles"` MentionRoles []string `json:"mention_roles"`
Tts bool `json:"tts"` Tts bool `json:"tts"`
MentionEveryone bool `json:"mention_everyone"` MentionEveryone bool `json:"mention_everyone"`
@ -28,6 +28,7 @@ type Message struct {
Attachments []*MessageAttachment `json:"attachments"` Attachments []*MessageAttachment `json:"attachments"`
Embeds []*MessageEmbed `json:"embeds"` Embeds []*MessageEmbed `json:"embeds"`
Mentions []*User `json:"mentions"` Mentions []*User `json:"mentions"`
Reactions []*MessageReactions `json:"reactions"`
} }
// A MessageAttachment stores data for message attachments. // A MessageAttachment stores data for message attachments.
@ -41,31 +42,80 @@ type MessageAttachment struct {
Size int `json:"size"` Size int `json:"size"`
} }
// MessageEmbedFooter is a part of a MessageEmbed struct.
type MessageEmbedFooter struct {
Text string `json:"text,omitempty"`
IconURL string `json:"icon_url,omitempty"`
ProxyIconURL string `json:"proxy_icon_url,omitempty"`
}
// MessageEmbedImage is a part of a MessageEmbed struct.
type MessageEmbedImage struct {
URL string `json:"url,omitempty"`
ProxyURL string `json:"proxy_url,omitempty"`
Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"`
}
// MessageEmbedThumbnail is a part of a MessageEmbed struct.
type MessageEmbedThumbnail struct {
URL string `json:"url,omitempty"`
ProxyURL string `json:"proxy_url,omitempty"`
Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"`
}
// MessageEmbedVideo is a part of a MessageEmbed struct.
type MessageEmbedVideo struct {
URL string `json:"url,omitempty"`
ProxyURL string `json:"proxy_url,omitempty"`
Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"`
}
// MessageEmbedProvider is a part of a MessageEmbed struct.
type MessageEmbedProvider struct {
URL string `json:"url,omitempty"`
Name string `json:"name,omitempty"`
}
// MessageEmbedAuthor is a part of a MessageEmbed struct.
type MessageEmbedAuthor struct {
URL string `json:"url,omitempty"`
Name string `json:"name,omitempty"`
IconURL string `json:"icon_url,omitempty"`
ProxyIconURL string `json:"proxy_icon_url,omitempty"`
}
// MessageEmbedField is a part of a MessageEmbed struct.
type MessageEmbedField struct {
Name string `json:"name,omitempty"`
Value string `json:"value,omitempty"`
Inline bool `json:"inline,omitempty"`
}
// An MessageEmbed stores data for message embeds. // An MessageEmbed stores data for message embeds.
type MessageEmbed struct { type MessageEmbed struct {
URL string `json:"url"` URL string `json:"url,omitempty"`
Type string `json:"type"` Type string `json:"type,omitempty"`
Title string `json:"title"` Title string `json:"title,omitempty"`
Description string `json:"description"` Description string `json:"description,omitempty"`
Thumbnail *struct { Timestamp string `json:"timestamp,omitempty"`
URL string `json:"url"` Color int `json:"color,omitempty"`
ProxyURL string `json:"proxy_url"` Footer *MessageEmbedFooter `json:"footer,omitempty"`
Width int `json:"width"` Image *MessageEmbedImage `json:"image,omitempty"`
Height int `json:"height"` Thumbnail *MessageEmbedThumbnail `json:"thumbnail,omitempty"`
} `json:"thumbnail"` Video *MessageEmbedVideo `json:"video,omitempty"`
Provider *struct { Provider *MessageEmbedProvider `json:"provider,omitempty"`
URL string `json:"url"` Author *MessageEmbedAuthor `json:"author,omitempty"`
Name string `json:"name"` Fields []*MessageEmbedField `json:"fields,omitempty"`
} `json:"provider"` }
Author *struct {
URL string `json:"url"` // MessageReactions holds a reactions object for a message.
Name string `json:"name"` type MessageReactions struct {
} `json:"author"` Count int `json:"count"`
Video *struct { Me bool `json:"me"`
URL string `json:"url"` Emoji *Emoji `json:"emoji"`
Width int `json:"width"`
Height int `json:"height"`
} `json:"video"`
} }
// ContentWithMentionsReplaced will replace all @<id> mentions with the // ContentWithMentionsReplaced will replace all @<id> mentions with the

View File

@ -21,13 +21,14 @@ type Application struct {
Icon string `json:"icon,omitempty"` Icon string `json:"icon,omitempty"`
Secret string `json:"secret,omitempty"` Secret string `json:"secret,omitempty"`
RedirectURIs *[]string `json:"redirect_uris,omitempty"` RedirectURIs *[]string `json:"redirect_uris,omitempty"`
Owner *User `json:"owner"`
} }
// Application returns an Application structure of a specific Application // Application returns an Application structure of a specific Application
// appID : The ID of an Application // appID : The ID of an Application
func (s *Session) Application(appID string) (st *Application, err error) { func (s *Session) Application(appID string) (st *Application, err error) {
body, err := s.Request("GET", EndpointApplication(appID), nil) body, err := s.RequestWithBucketID("GET", EndpointApplication(appID), nil, EndpointApplication(""))
if err != nil { if err != nil {
return return
} }
@ -39,7 +40,7 @@ func (s *Session) Application(appID string) (st *Application, err error) {
// Applications returns all applications for the authenticated user // Applications returns all applications for the authenticated user
func (s *Session) Applications() (st []*Application, err error) { func (s *Session) Applications() (st []*Application, err error) {
body, err := s.Request("GET", EndpointApplications, nil) body, err := s.RequestWithBucketID("GET", EndpointApplications, nil, EndpointApplications)
if err != nil { if err != nil {
return return
} }
@ -59,7 +60,7 @@ func (s *Session) ApplicationCreate(ap *Application) (st *Application, err error
RedirectURIs *[]string `json:"redirect_uris,omitempty"` RedirectURIs *[]string `json:"redirect_uris,omitempty"`
}{ap.Name, ap.Description, ap.RedirectURIs} }{ap.Name, ap.Description, ap.RedirectURIs}
body, err := s.Request("POST", EndpointApplications, data) body, err := s.RequestWithBucketID("POST", EndpointApplications, data, EndpointApplications)
if err != nil { if err != nil {
return return
} }
@ -78,7 +79,7 @@ func (s *Session) ApplicationUpdate(appID string, ap *Application) (st *Applicat
RedirectURIs *[]string `json:"redirect_uris,omitempty"` RedirectURIs *[]string `json:"redirect_uris,omitempty"`
}{ap.Name, ap.Description, ap.RedirectURIs} }{ap.Name, ap.Description, ap.RedirectURIs}
body, err := s.Request("PUT", EndpointApplication(appID), data) body, err := s.RequestWithBucketID("PUT", EndpointApplication(appID), data, EndpointApplication(""))
if err != nil { if err != nil {
return return
} }
@ -91,7 +92,7 @@ func (s *Session) ApplicationUpdate(appID string, ap *Application) (st *Applicat
// appID : The ID of an Application // appID : The ID of an Application
func (s *Session) ApplicationDelete(appID string) (err error) { func (s *Session) ApplicationDelete(appID string) (err error) {
_, err = s.Request("DELETE", EndpointApplication(appID), nil) _, err = s.RequestWithBucketID("DELETE", EndpointApplication(appID), nil, EndpointApplication(""))
if err != nil { if err != nil {
return return
} }
@ -110,7 +111,7 @@ func (s *Session) ApplicationDelete(appID string) (err error) {
// NOTE: func name may change, if I can think up something better. // NOTE: func name may change, if I can think up something better.
func (s *Session) ApplicationBotCreate(appID string) (st *User, err error) { func (s *Session) ApplicationBotCreate(appID string) (st *User, err error) {
body, err := s.Request("POST", EndpointApplicationsBot(appID), nil) body, err := s.RequestWithBucketID("POST", EndpointApplicationsBot(appID), nil, EndpointApplicationsBot(""))
if err != nil { if err != nil {
return return
} }

157
vendor/github.com/bwmarrin/discordgo/ratelimit.go generated vendored Normal file
View File

@ -0,0 +1,157 @@
package discordgo
import (
"net/http"
"strconv"
"sync"
"time"
)
// RateLimiter holds all ratelimit buckets
type RateLimiter struct {
sync.Mutex
global *Bucket
buckets map[string]*Bucket
globalRateLimit time.Duration
}
// NewRatelimiter returns a new RateLimiter
func NewRatelimiter() *RateLimiter {
return &RateLimiter{
buckets: make(map[string]*Bucket),
global: &Bucket{Key: "global"},
}
}
// getBucket retrieves or creates a bucket
func (r *RateLimiter) getBucket(key string) *Bucket {
r.Lock()
defer r.Unlock()
if bucket, ok := r.buckets[key]; ok {
return bucket
}
b := &Bucket{
remaining: 1,
Key: key,
global: r.global,
}
r.buckets[key] = b
return b
}
// LockBucket Locks until a request can be made
func (r *RateLimiter) LockBucket(bucketID string) *Bucket {
b := r.getBucket(bucketID)
b.Lock()
// If we ran out of calls and the reset time is still ahead of us
// then we need to take it easy and relax a little
if b.remaining < 1 && b.reset.After(time.Now()) {
time.Sleep(b.reset.Sub(time.Now()))
}
// Check for global ratelimits
r.global.Lock()
r.global.Unlock()
b.remaining--
return b
}
// Bucket represents a ratelimit bucket, each bucket gets ratelimited individually (-global ratelimits)
type Bucket struct {
sync.Mutex
Key string
remaining int
limit int
reset time.Time
global *Bucket
}
// Release unlocks the bucket and reads the headers to update the buckets ratelimit info
// and locks up the whole thing in case if there's a global ratelimit.
func (b *Bucket) Release(headers http.Header) error {
defer b.Unlock()
if headers == nil {
return nil
}
remaining := headers.Get("X-RateLimit-Remaining")
reset := headers.Get("X-RateLimit-Reset")
global := headers.Get("X-RateLimit-Global")
retryAfter := headers.Get("Retry-After")
// If it's global just keep the main ratelimit mutex locked
if global != "" {
parsedAfter, err := strconv.Atoi(retryAfter)
if err != nil {
return err
}
// Lock it in a new goroutine so that this isn't a blocking call
go func() {
// Make sure if several requests were waiting we don't sleep for n * retry-after
// where n is the amount of requests that were going on
sleepTo := time.Now().Add(time.Duration(parsedAfter) * time.Millisecond)
b.global.Lock()
sleepDuration := sleepTo.Sub(time.Now())
if sleepDuration > 0 {
time.Sleep(sleepDuration)
}
b.global.Unlock()
}()
return nil
}
// Update reset time if either retry after or reset headers are present
// Prefer retryafter because it's more accurate with time sync and whatnot
if retryAfter != "" {
parsedAfter, err := strconv.ParseInt(retryAfter, 10, 64)
if err != nil {
return err
}
b.reset = time.Now().Add(time.Duration(parsedAfter) * time.Millisecond)
} else if reset != "" {
// Calculate the reset time by using the date header returned from discord
discordTime, err := http.ParseTime(headers.Get("Date"))
if err != nil {
return err
}
unix, err := strconv.ParseInt(reset, 10, 64)
if err != nil {
return err
}
// Calculate the time until reset and add it to the current local time
// some extra time is added because without it i still encountered 429's.
// The added amount is the lowest amount that gave no 429's
// in 1k requests
delta := time.Unix(unix, 0).Sub(discordTime) + time.Millisecond*250
b.reset = time.Now().Add(delta)
}
// Udpate remaining if header is present
if remaining != "" {
parsedRemaining, err := strconv.ParseInt(remaining, 10, 32)
if err != nil {
return err
}
b.remaining = int(parsedRemaining)
}
return nil
}

File diff suppressed because it is too large Load Diff

View File

@ -55,33 +55,6 @@ func NewState() *State {
} }
} }
// OnReady takes a Ready event and updates all internal state.
func (s *State) OnReady(r *Ready) error {
if s == nil {
return ErrNilState
}
s.Lock()
defer s.Unlock()
s.Ready = *r
for _, g := range s.Guilds {
s.guildMap[g.ID] = g
for _, c := range g.Channels {
c.GuildID = g.ID
s.channelMap[c.ID] = c
}
}
for _, c := range s.PrivateChannels {
s.channelMap[c.ID] = c
}
return nil
}
// GuildAdd adds a guild to the current world state, or // GuildAdd adds a guild to the current world state, or
// updates it if it already exists. // updates it if it already exists.
func (s *State) GuildAdd(guild *Guild) error { func (s *State) GuildAdd(guild *Guild) error {
@ -94,20 +67,30 @@ func (s *State) GuildAdd(guild *Guild) error {
// Update the channels to point to the right guild, adding them to the channelMap as we go // Update the channels to point to the right guild, adding them to the channelMap as we go
for _, c := range guild.Channels { for _, c := range guild.Channels {
c.GuildID = guild.ID
s.channelMap[c.ID] = c s.channelMap[c.ID] = c
} }
// If the guild exists, replace it.
if g, ok := s.guildMap[guild.ID]; ok { if g, ok := s.guildMap[guild.ID]; ok {
// If this guild already exists with data, don't stomp on props. // We are about to replace `g` in the state with `guild`, but first we need to
if g.Unavailable != nil && !*g.Unavailable { // make sure we preserve any fields that the `guild` doesn't contain from `g`.
if guild.Roles == nil {
guild.Roles = g.Roles
}
if guild.Emojis == nil {
guild.Emojis = g.Emojis
}
if guild.Members == nil {
guild.Members = g.Members guild.Members = g.Members
}
if guild.Presences == nil {
guild.Presences = g.Presences guild.Presences = g.Presences
}
if guild.Channels == nil {
guild.Channels = g.Channels guild.Channels = g.Channels
}
if guild.VoiceStates == nil {
guild.VoiceStates = g.VoiceStates guild.VoiceStates = g.VoiceStates
} }
*g = *guild *g = *guild
return nil return nil
} }
@ -325,8 +308,12 @@ func (s *State) ChannelAdd(channel *Channel) error {
// If the channel exists, replace it // If the channel exists, replace it
if c, ok := s.channelMap[channel.ID]; ok { if c, ok := s.channelMap[channel.ID]; ok {
if channel.Messages == nil {
channel.Messages = c.Messages channel.Messages = c.Messages
}
if channel.PermissionOverwrites == nil {
channel.PermissionOverwrites = c.PermissionOverwrites channel.PermissionOverwrites = c.PermissionOverwrites
}
*c = *channel *c = *channel
return nil return nil
@ -511,6 +498,12 @@ func (s *State) MessageAdd(message *Message) error {
if message.Attachments != nil { if message.Attachments != nil {
m.Attachments = message.Attachments m.Attachments = message.Attachments
} }
if message.Timestamp != "" {
m.Timestamp = message.Timestamp
}
if message.Author != nil {
m.Author = message.Author
}
return nil return nil
} }
@ -602,18 +595,63 @@ func (s *State) Message(channelID, messageID string) (*Message, error) {
return nil, errors.New("Message not found.") return nil, errors.New("Message not found.")
} }
// OnReady takes a Ready event and updates all internal state.
func (s *State) onReady(se *Session, r *Ready) (err error) {
if s == nil {
return ErrNilState
}
s.Lock()
defer s.Unlock()
// We must track at least the current user for Voice, even
// if state is disabled, store the bare essentials.
if !se.StateEnabled {
ready := Ready{
Version: r.Version,
SessionID: r.SessionID,
HeartbeatInterval: r.HeartbeatInterval,
User: r.User,
}
s.Ready = ready
return nil
}
s.Ready = *r
for _, g := range s.Guilds {
s.guildMap[g.ID] = g
for _, c := range g.Channels {
s.channelMap[c.ID] = c
}
}
for _, c := range s.PrivateChannels {
s.channelMap[c.ID] = c
}
return nil
}
// onInterface handles all events related to states. // onInterface handles all events related to states.
func (s *State) onInterface(se *Session, i interface{}) (err error) { func (s *State) onInterface(se *Session, i interface{}) (err error) {
if s == nil { if s == nil {
return ErrNilState return ErrNilState
} }
r, ok := i.(*Ready)
if ok {
return s.onReady(se, r)
}
if !se.StateEnabled { if !se.StateEnabled {
return nil return nil
} }
switch t := i.(type) { switch t := i.(type) {
case *Ready:
err = s.OnReady(t)
case *GuildCreate: case *GuildCreate:
err = s.GuildAdd(t.Guild) err = s.GuildAdd(t.Guild)
case *GuildUpdate: case *GuildUpdate:
@ -685,6 +723,9 @@ func (s *State) onInterface(se *Session, i interface{}) (err error) {
// userID : The ID of the user to calculate permissions for. // userID : The ID of the user to calculate permissions for.
// channelID : The ID of the channel to calculate permission for. // channelID : The ID of the channel to calculate permission for.
func (s *State) UserChannelPermissions(userID, channelID string) (apermissions int, err error) { func (s *State) UserChannelPermissions(userID, channelID string) (apermissions int, err error) {
if s == nil {
return 0, ErrNilState
}
channel, err := s.Channel(channelID) channel, err := s.Channel(channelID)
if err != nil { if err != nil {
@ -706,6 +747,13 @@ func (s *State) UserChannelPermissions(userID, channelID string) (apermissions i
return return
} }
for _, role := range guild.Roles {
if role.ID == guild.ID {
apermissions |= role.Permissions
break
}
}
for _, role := range guild.Roles { for _, role := range guild.Roles {
for _, roleID := range member.Roles { for _, roleID := range member.Roles {
if role.ID == roleID { if role.ID == roleID {
@ -715,7 +763,7 @@ func (s *State) UserChannelPermissions(userID, channelID string) (apermissions i
} }
} }
if apermissions&PermissionManageRoles > 0 { if apermissions&PermissionAdministrator > 0 {
apermissions |= PermissionAll apermissions |= PermissionAll
} }
@ -738,7 +786,7 @@ func (s *State) UserChannelPermissions(userID, channelID string) (apermissions i
} }
} }
if apermissions&PermissionManageRoles > 0 { if apermissions&PermissionAdministrator > 0 {
apermissions |= PermissionAllChannel apermissions |= PermissionAllChannel
} }

View File

@ -13,7 +13,7 @@ package discordgo
import ( import (
"encoding/json" "encoding/json"
"reflect" "strconv"
"sync" "sync"
"time" "time"
@ -53,6 +53,9 @@ type Session struct {
// Whether the Data Websocket is ready // Whether the Data Websocket is ready
DataReady bool // NOTE: Maye be deprecated soon DataReady bool // NOTE: Maye be deprecated soon
// Max number of REST API retries
MaxRestRetries int
// Status stores the currect status of the websocket connection // Status stores the currect status of the websocket connection
// this is being tested, may stay, may go away. // this is being tested, may stay, may go away.
status int32 status int32
@ -70,13 +73,10 @@ type Session struct {
// StateEnabled is true. // StateEnabled is true.
State *State State *State
// Event handlers
handlersMu sync.RWMutex handlersMu sync.RWMutex
// This is a mapping of event struct to a reflected value handlers map[string][]*eventHandlerInstance
// for event handlers. onceHandlers map[string][]*eventHandlerInstance
// We store the reflected value instead of the function
// reference as it is more performant, instead of re-reflecting
// the function each event.
handlers map[interface{}][]reflect.Value
// The websocket connection. // The websocket connection.
wsConn *websocket.Conn wsConn *websocket.Conn
@ -85,9 +85,7 @@ type Session struct {
listening chan interface{} listening chan interface{}
// used to deal with rate limits // used to deal with rate limits
// may switch to slices later ratelimiter *RateLimiter
// TODO: performance test map vs slices
rateLimit rateLimitMutex
// sequence tracks the current gateway api websocket sequence number // sequence tracks the current gateway api websocket sequence number
sequence int sequence int
@ -108,12 +106,6 @@ type rateLimitMutex struct {
// bucket map[string]*sync.Mutex // TODO :) // bucket map[string]*sync.Mutex // TODO :)
} }
// A Resumed struct holds the data received in a RESUMED event
type Resumed struct {
HeartbeatInterval time.Duration `json:"heartbeat_interval"`
Trace []string `json:"_trace"`
}
// A VoiceRegion stores data for a specific voice region server. // A VoiceRegion stores data for a specific voice region server.
type VoiceRegion struct { type VoiceRegion struct {
ID string `json:"id"` ID string `json:"id"`
@ -141,7 +133,7 @@ type Invite struct {
Channel *Channel `json:"channel"` Channel *Channel `json:"channel"`
Inviter *User `json:"inviter"` Inviter *User `json:"inviter"`
Code string `json:"code"` Code string `json:"code"`
CreatedAt string `json:"created_at"` // TODO make timestamp CreatedAt Timestamp `json:"created_at"`
MaxAge int `json:"max_age"` MaxAge int `json:"max_age"`
Uses int `json:"uses"` Uses int `json:"uses"`
MaxUses int `json:"max_uses"` MaxUses int `json:"max_uses"`
@ -183,6 +175,17 @@ type Emoji struct {
RequireColons bool `json:"require_colons"` RequireColons bool `json:"require_colons"`
} }
// APIName returns an correctly formatted API name for use in the MessageReactions endpoints.
func (e *Emoji) APIName() string {
if e.ID != "" && e.Name != "" {
return e.Name + ":" + e.ID
}
if e.Name != "" {
return e.Name
}
return e.ID
}
// VerificationLevel type defination // VerificationLevel type defination
type VerificationLevel int type VerificationLevel int
@ -204,9 +207,10 @@ type Guild struct {
AfkChannelID string `json:"afk_channel_id"` AfkChannelID string `json:"afk_channel_id"`
EmbedChannelID string `json:"embed_channel_id"` EmbedChannelID string `json:"embed_channel_id"`
OwnerID string `json:"owner_id"` OwnerID string `json:"owner_id"`
JoinedAt string `json:"joined_at"` // make this a timestamp JoinedAt Timestamp `json:"joined_at"`
Splash string `json:"splash"` Splash string `json:"splash"`
AfkTimeout int `json:"afk_timeout"` AfkTimeout int `json:"afk_timeout"`
MemberCount int `json:"member_count"`
VerificationLevel VerificationLevel `json:"verification_level"` VerificationLevel VerificationLevel `json:"verification_level"`
EmbedEnabled bool `json:"embed_enabled"` EmbedEnabled bool `json:"embed_enabled"`
Large bool `json:"large"` // ?? Large bool `json:"large"` // ??
@ -217,7 +221,16 @@ type Guild struct {
Presences []*Presence `json:"presences"` Presences []*Presence `json:"presences"`
Channels []*Channel `json:"channels"` Channels []*Channel `json:"channels"`
VoiceStates []*VoiceState `json:"voice_states"` VoiceStates []*VoiceState `json:"voice_states"`
Unavailable *bool `json:"unavailable"` Unavailable bool `json:"unavailable"`
}
// A UserGuild holds a brief version of a Guild
type UserGuild struct {
ID string `json:"id"`
Name string `json:"name"`
Icon string `json:"icon"`
Owner bool `json:"owner"`
Permissions int `json:"permissions"`
} }
// A GuildParams stores all the data needed to update discord guild settings // A GuildParams stores all the data needed to update discord guild settings
@ -232,6 +245,7 @@ type Role struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Managed bool `json:"managed"` Managed bool `json:"managed"`
Mentionable bool `json:"mentionable"`
Hoist bool `json:"hoist"` Hoist bool `json:"hoist"`
Color int `json:"color"` Color int `json:"color"`
Position int `json:"position"` Position int `json:"position"`
@ -254,8 +268,10 @@ type VoiceState struct {
// A Presence stores the online, offline, or idle and game status of Guild members. // A Presence stores the online, offline, or idle and game status of Guild members.
type Presence struct { type Presence struct {
User *User `json:"user"` User *User `json:"user"`
Status string `json:"status"` Status Status `json:"status"`
Game *Game `json:"game"` Game *Game `json:"game"`
Nick string `json:"nick"`
Roles []string `json:"roles"`
} }
// A Game struct holds the name of the "playing .." game for a user // A Game struct holds the name of the "playing .." game for a user
@ -265,6 +281,38 @@ type Game struct {
URL string `json:"url"` URL string `json:"url"`
} }
// UnmarshalJSON unmarshals json to Game struct
func (g *Game) UnmarshalJSON(bytes []byte) error {
temp := &struct {
Name string `json:"name"`
Type json.RawMessage `json:"type"`
URL string `json:"url"`
}{}
err := json.Unmarshal(bytes, temp)
if err != nil {
return err
}
g.Name = temp.Name
g.URL = temp.URL
if temp.Type != nil {
err = json.Unmarshal(temp.Type, &g.Type)
if err == nil {
return nil
}
s := ""
err = json.Unmarshal(temp.Type, &s)
if err == nil {
g.Type, err = strconv.Atoi(s)
}
return err
}
return nil
}
// A Member stores user information for Guild members. // A Member stores user information for Guild members.
type Member struct { type Member struct {
GuildID string `json:"guild_id"` GuildID string `json:"guild_id"`
@ -297,15 +345,29 @@ type Settings struct {
EnableTtsCommand bool `json:"enable_tts_command"` EnableTtsCommand bool `json:"enable_tts_command"`
MessageDisplayCompact bool `json:"message_display_compact"` MessageDisplayCompact bool `json:"message_display_compact"`
ShowCurrentGame bool `json:"show_current_game"` ShowCurrentGame bool `json:"show_current_game"`
AllowEmailFriendRequest bool `json:"allow_email_friend_request"`
ConvertEmoticons bool `json:"convert_emoticons"` ConvertEmoticons bool `json:"convert_emoticons"`
Locale string `json:"locale"` Locale string `json:"locale"`
Theme string `json:"theme"` Theme string `json:"theme"`
GuildPositions []string `json:"guild_positions"` GuildPositions []string `json:"guild_positions"`
RestrictedGuilds []string `json:"restricted_guilds"` RestrictedGuilds []string `json:"restricted_guilds"`
FriendSourceFlags *FriendSourceFlags `json:"friend_source_flags"` FriendSourceFlags *FriendSourceFlags `json:"friend_source_flags"`
Status Status `json:"status"`
DetectPlatformAccounts bool `json:"detect_platform_accounts"`
DeveloperMode bool `json:"developer_mode"`
} }
// Status type defination
type Status string
// Constants for Status with the different current available status
const (
StatusOnline Status = "online"
StatusIdle Status = "idle"
StatusDoNotDisturb Status = "dnd"
StatusInvisible Status = "invisible"
StatusOffline Status = "offline"
)
// FriendSourceFlags stores ... TODO :) // FriendSourceFlags stores ... TODO :)
type FriendSourceFlags struct { type FriendSourceFlags struct {
All bool `json:"all"` All bool `json:"all"`
@ -313,32 +375,6 @@ type FriendSourceFlags struct {
MutualFriends bool `json:"mutual_friends"` MutualFriends bool `json:"mutual_friends"`
} }
// An Event provides a basic initial struct for all websocket event.
type Event struct {
Operation int `json:"op"`
Sequence int `json:"s"`
Type string `json:"t"`
RawData json.RawMessage `json:"d"`
Struct interface{} `json:"-"`
}
// A Ready stores all data for the websocket READY event.
type Ready struct {
Version int `json:"v"`
SessionID string `json:"session_id"`
HeartbeatInterval time.Duration `json:"heartbeat_interval"`
User *User `json:"user"`
ReadState []*ReadState `json:"read_state"`
PrivateChannels []*Channel `json:"private_channels"`
Guilds []*Guild `json:"guilds"`
// Undocumented fields
Settings *Settings `json:"user_settings"`
UserGuildSettings []*UserGuildSettings `json:"user_guild_settings"`
Relationships []*Relationship `json:"relationships"`
Presences []*Presence `json:"presences"`
}
// A Relationship between the logged in user and Relationship.User // A Relationship between the logged in user and Relationship.User
type Relationship struct { type Relationship struct {
User *User `json:"user"` User *User `json:"user"`
@ -361,54 +397,21 @@ type ReadState struct {
ID string `json:"id"` ID string `json:"id"`
} }
// A TypingStart stores data for the typing start websocket event. // An Ack is used to ack messages
type TypingStart struct { type Ack struct {
UserID string `json:"user_id"` Token string `json:"token"`
ChannelID string `json:"channel_id"`
Timestamp int `json:"timestamp"`
} }
// A PresenceUpdate stores data for the presence update websocket event. // A GuildRole stores data for guild roles.
type PresenceUpdate struct {
Presence
GuildID string `json:"guild_id"`
Roles []string `json:"roles"`
}
// A MessageAck stores data for the message ack websocket event.
type MessageAck struct {
MessageID string `json:"message_id"`
ChannelID string `json:"channel_id"`
}
// A GuildIntegrationsUpdate stores data for the guild integrations update
// websocket event.
type GuildIntegrationsUpdate struct {
GuildID string `json:"guild_id"`
}
// A GuildRole stores data for guild role websocket events.
type GuildRole struct { type GuildRole struct {
Role *Role `json:"role"` Role *Role `json:"role"`
GuildID string `json:"guild_id"` GuildID string `json:"guild_id"`
} }
// A GuildRoleDelete stores data for the guild role delete websocket event.
type GuildRoleDelete struct {
RoleID string `json:"role_id"`
GuildID string `json:"guild_id"`
}
// A GuildBan stores data for a guild ban. // A GuildBan stores data for a guild ban.
type GuildBan struct { type GuildBan struct {
Reason string `json:"reason"`
User *User `json:"user"` User *User `json:"user"`
GuildID string `json:"guild_id"`
}
// A GuildEmojisUpdate stores data for a guild emoji update event.
type GuildEmojisUpdate struct {
GuildID string `json:"guild_id"`
Emojis []*Emoji `json:"emojis"`
} }
// A GuildIntegration stores data for a guild integration. // A GuildIntegration stores data for a guild integration.
@ -464,6 +467,41 @@ type UserGuildSettingsEdit struct {
ChannelOverrides map[string]*UserGuildSettingsChannelOverride `json:"channel_overrides"` ChannelOverrides map[string]*UserGuildSettingsChannelOverride `json:"channel_overrides"`
} }
// An APIErrorMessage is an api error message returned from discord
type APIErrorMessage struct {
Code int `json:"code"`
Message string `json:"message"`
}
// Webhook stores the data for a webhook.
type Webhook struct {
ID string `json:"id"`
GuildID string `json:"guild_id"`
ChannelID string `json:"channel_id"`
User *User `json:"user"`
Name string `json:"name"`
Avatar string `json:"avatar"`
Token string `json:"token"`
}
// WebhookParams is a struct for webhook params, used in the WebhookExecute command.
type WebhookParams struct {
Content string `json:"content,omitempty"`
Username string `json:"username,omitempty"`
AvatarURL string `json:"avatar_url,omitempty"`
TTS bool `json:"tts,omitempty"`
File string `json:"file,omitempty"`
Embeds []*MessageEmbed `json:"embeds,omitempty"`
}
// MessageReaction stores the data for a message reaction.
type MessageReaction struct {
UserID string `json:"user_id"`
MessageID string `json:"message_id"`
Emoji Emoji `json:"emoji"`
ChannelID string `json:"channel_id"`
}
// Constants for the different bit offsets of text channel permissions // Constants for the different bit offsets of text channel permissions
const ( const (
PermissionReadMessages = 1 << (iota + 10) PermissionReadMessages = 1 << (iota + 10)
@ -474,6 +512,7 @@ const (
PermissionAttachFiles PermissionAttachFiles
PermissionReadMessageHistory PermissionReadMessageHistory
PermissionMentionEveryone PermissionMentionEveryone
PermissionUseExternalEmojis
) )
// Constants for the different bit offsets of voice permissions // Constants for the different bit offsets of voice permissions
@ -486,12 +525,21 @@ const (
PermissionVoiceUseVAD PermissionVoiceUseVAD
) )
// Constants for general management.
const (
PermissionChangeNickname = 1 << (iota + 26)
PermissionManageNicknames
PermissionManageRoles
PermissionManageWebhooks
PermissionManageEmojis
)
// Constants for the different bit offsets of general permissions // Constants for the different bit offsets of general permissions
const ( const (
PermissionCreateInstantInvite = 1 << iota PermissionCreateInstantInvite = 1 << iota
PermissionKickMembers PermissionKickMembers
PermissionBanMembers PermissionBanMembers
PermissionManageRoles PermissionAdministrator
PermissionManageChannels PermissionManageChannels
PermissionManageServer PermissionManageServer

View File

@ -0,0 +1,123 @@
package main
import (
"bytes"
"go/format"
"go/parser"
"go/token"
"io/ioutil"
"log"
"path/filepath"
"regexp"
"sort"
"strings"
"text/template"
)
var eventHandlerTmpl = template.Must(template.New("eventHandler").Funcs(template.FuncMap{
"constName": constName,
"isDiscordEvent": isDiscordEvent,
"privateName": privateName,
}).Parse(`// Code generated by \"eventhandlers\"; DO NOT EDIT
// See events.go
package discordgo
// Following are all the event types.
// Event type values are used to match the events returned by Discord.
// EventTypes surrounded by __ are synthetic and are internal to DiscordGo.
const ({{range .}}
{{privateName .}}EventType = "{{constName .}}"{{end}}
)
{{range .}}
// {{privateName .}}EventHandler is an event handler for {{.}} events.
type {{privateName .}}EventHandler func(*Session, *{{.}})
// Type returns the event type for {{.}} events.
func (eh {{privateName .}}EventHandler) Type() string {
return {{privateName .}}EventType
}
// New returns a new instance of {{.}}.
func (eh {{privateName .}}EventHandler) New() interface{} {
return &{{.}}{}
}
// Handle is the handler for {{.}} events.
func (eh {{privateName .}}EventHandler) Handle(s *Session, i interface{}) {
if t, ok := i.(*{{.}}); ok {
eh(s, t)
}
}
{{end}}
func handlerForInterface(handler interface{}) EventHandler {
switch v := handler.(type) {
case func(*Session, interface{}):
return interfaceEventHandler(v){{range .}}
case func(*Session, *{{.}}):
return {{privateName .}}EventHandler(v){{end}}
}
return nil
}
func init() { {{range .}}{{if isDiscordEvent .}}
registerInterfaceProvider({{privateName .}}EventHandler(nil)){{end}}{{end}}
}
`))
func main() {
var buf bytes.Buffer
dir := filepath.Dir(".")
fs := token.NewFileSet()
parsedFile, err := parser.ParseFile(fs, "events.go", nil, 0)
if err != nil {
log.Fatalf("warning: internal error: could not parse events.go: %s", err)
return
}
names := []string{}
for object := range parsedFile.Scope.Objects {
names = append(names, object)
}
sort.Strings(names)
eventHandlerTmpl.Execute(&buf, names)
src, err := format.Source(buf.Bytes())
if err != nil {
log.Println("warning: internal error: invalid Go generated:", err)
src = buf.Bytes()
}
err = ioutil.WriteFile(filepath.Join(dir, strings.ToLower("eventhandlers.go")), src, 0644)
if err != nil {
log.Fatal(buf, "writing output: %s", err)
}
}
var constRegexp = regexp.MustCompile("([a-z])([A-Z])")
func constCase(name string) string {
return strings.ToUpper(constRegexp.ReplaceAllString(name, "${1}_${2}"))
}
func isDiscordEvent(name string) bool {
switch {
case name == "Connect", name == "Disconnect", name == "Event", name == "RateLimit", name == "Interface":
return false
default:
return true
}
}
func constName(name string) string {
if !isDiscordEvent(name) {
return "__" + constCase(name) + "__"
}
return constCase(name)
}
func privateName(name string) string {
return strings.ToLower(string(name[0])) + name[1:]
}

58
vendor/github.com/bwmarrin/discordgo/types.go generated vendored Normal file
View File

@ -0,0 +1,58 @@
// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo
// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file contains custom types, currently only a timestamp wrapper.
package discordgo
import (
"encoding/json"
"fmt"
"net/http"
"time"
)
// Timestamp stores a timestamp, as sent by the Discord API.
type Timestamp string
// Parse parses a timestamp string into a time.Time object.
// The only time this can fail is if Discord changes their timestamp format.
func (t Timestamp) Parse() (time.Time, error) {
return time.Parse(time.RFC3339, string(t))
}
// RESTError stores error information about a request with a bad response code.
// Message is not always present, there are cases where api calls can fail
// without returning a json message.
type RESTError struct {
Request *http.Request
Response *http.Response
ResponseBody []byte
Message *APIErrorMessage // Message may be nil.
}
func newRestError(req *http.Request, resp *http.Response, body []byte) *RESTError {
restErr := &RESTError{
Request: req,
Response: resp,
ResponseBody: body,
}
// Attempt to decode the error and assume no message was provided if it fails
var msg *APIErrorMessage
err := json.Unmarshal(body, &msg)
if err == nil {
restErr.Message = msg
}
return restErr
}
func (r RESTError) Error() string {
return fmt.Sprintf("HTTP %s, %s", r.Response.Status, r.ResponseBody)
}

View File

@ -441,7 +441,7 @@ func (v *VoiceConnection) onEvent(message []byte) {
} }
default: default:
v.log(LogError, "unknown voice operation, %d, %s", e.Operation, string(e.RawData)) v.log(LogDebug, "unknown voice operation, %d, %s", e.Operation, string(e.RawData))
} }
return return
@ -570,7 +570,7 @@ func (v *VoiceConnection) udpOpen() (err error) {
return fmt.Errorf("received udp packet too small") return fmt.Errorf("received udp packet too small")
} }
// Loop over position 4 though 20 to grab the IP address // Loop over position 4 through 20 to grab the IP address
// Should never be beyond position 20. // Should never be beyond position 20.
var ip string var ip string
for i := 4; i < 20; i++ { for i := 4; i < 20; i++ {

View File

@ -17,9 +17,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"log"
"net/http" "net/http"
"reflect"
"runtime" "runtime"
"time" "time"
@ -47,6 +45,17 @@ func (s *Session) Open() (err error) {
} }
}() }()
// A basic state is a hard requirement for Voice.
if s.State == nil {
state := NewState()
state.TrackChannels = false
state.TrackEmojis = false
state.TrackMembers = false
state.TrackRoles = false
state.TrackVoice = false
s.State = state
}
if s.wsConn != nil { if s.wsConn != nil {
err = errors.New("Web socket already opened.") err = errors.New("Web socket already opened.")
return return
@ -111,9 +120,8 @@ func (s *Session) Open() (err error) {
s.Unlock() s.Unlock()
s.initialize()
s.log(LogInformational, "emit connect event") s.log(LogInformational, "emit connect event")
s.handle(&Connect{}) s.handleEvent(connectEventType, &Connect{})
s.log(LogInformational, "exiting") s.log(LogInformational, "exiting")
return return
@ -269,6 +277,44 @@ func (s *Session) UpdateStatus(idle int, game string) (err error) {
return s.UpdateStreamingStatus(idle, game, "") return s.UpdateStreamingStatus(idle, game, "")
} }
type requestGuildMembersData struct {
GuildID string `json:"guild_id"`
Query string `json:"query"`
Limit int `json:"limit"`
}
type requestGuildMembersOp struct {
Op int `json:"op"`
Data requestGuildMembersData `json:"d"`
}
// RequestGuildMembers requests guild members from the gateway
// The gateway responds with GuildMembersChunk events
// guildID : The ID of the guild to request members of
// query : String that username starts with, leave empty to return all members
// limit : Max number of items to return, or 0 to request all members matched
func (s *Session) RequestGuildMembers(guildID, query string, limit int) (err error) {
s.log(LogInformational, "called")
s.RLock()
defer s.RUnlock()
if s.wsConn == nil {
return errors.New("no websocket connection exists")
}
data := requestGuildMembersData{
GuildID: guildID,
Query: query,
Limit: limit,
}
s.wsMutex.Lock()
err = s.wsConn.WriteJSON(requestGuildMembersOp{8, data})
s.wsMutex.Unlock()
return
}
// onEvent is the "event handler" for all messages received on the // onEvent is the "event handler" for all messages received on the
// Discord Gateway API websocket connection. // Discord Gateway API websocket connection.
// //
@ -361,16 +407,12 @@ func (s *Session) onEvent(messageType int, message []byte) {
// Store the message sequence // Store the message sequence
s.sequence = e.Sequence s.sequence = e.Sequence
// Map event to registered event handlers and pass it along // Map event to registered event handlers and pass it along to any registered handlers.
// to any registered functions if eh, ok := registeredInterfaceProviders[e.Type]; ok {
i := eventToInterface[e.Type] e.Struct = eh.New()
if i != nil {
// Create a new instance of the event type.
i = reflect.New(reflect.TypeOf(i)).Interface()
// Attempt to unmarshal our event. // Attempt to unmarshal our event.
if err = json.Unmarshal(e.RawData, i); err != nil { if err = json.Unmarshal(e.RawData, e.Struct); err != nil {
s.log(LogError, "error unmarshalling %s event, %s", e.Type, err) s.log(LogError, "error unmarshalling %s event, %s", e.Type, err)
} }
@ -381,30 +423,19 @@ func (s *Session) onEvent(messageType int, message []byte) {
// it's better to pass along what we received than nothing at all. // it's better to pass along what we received than nothing at all.
// TODO: Think about that decision :) // TODO: Think about that decision :)
// Either way, READY events must fire, even with errors. // Either way, READY events must fire, even with errors.
go s.handle(i) s.handleEvent(e.Type, e.Struct)
} else { } else {
s.log(LogWarning, "unknown event: Op: %d, Seq: %d, Type: %s, Data: %s", e.Operation, e.Sequence, e.Type, string(e.RawData)) s.log(LogWarning, "unknown event: Op: %d, Seq: %d, Type: %s, Data: %s", e.Operation, e.Sequence, e.Type, string(e.RawData))
} }
// Emit event to the OnEvent handler // For legacy reasons, we send the raw event also, this could be useful for handling unknown events.
e.Struct = i s.handleEvent(eventEventType, e)
go s.handle(e)
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Code related to voice connections that initiate over the data websocket // Code related to voice connections that initiate over the data websocket
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// A VoiceServerUpdate stores the data received during the Voice Server Update
// data websocket event. This data is used during the initial Voice Channel
// join handshaking.
type VoiceServerUpdate struct {
Token string `json:"token"`
GuildID string `json:"guild_id"`
Endpoint string `json:"endpoint"`
}
type voiceChannelJoinData struct { type voiceChannelJoinData struct {
GuildID *string `json:"guild_id"` GuildID *string `json:"guild_id"`
ChannelID *string `json:"channel_id"` ChannelID *string `json:"channel_id"`
@ -461,7 +492,7 @@ func (s *Session) ChannelVoiceJoin(gID, cID string, mute, deaf bool) (voice *Voi
} }
// onVoiceStateUpdate handles Voice State Update events on the data websocket. // onVoiceStateUpdate handles Voice State Update events on the data websocket.
func (s *Session) onVoiceStateUpdate(se *Session, st *VoiceStateUpdate) { func (s *Session) onVoiceStateUpdate(st *VoiceStateUpdate) {
// If we don't have a connection for the channel, don't bother // If we don't have a connection for the channel, don't bother
if st.ChannelID == "" { if st.ChannelID == "" {
@ -474,22 +505,13 @@ func (s *Session) onVoiceStateUpdate(se *Session, st *VoiceStateUpdate) {
return return
} }
// Need to have this happen at login and store it in the Session // We only care about events that are about us.
// TODO : This should be done upon connecting to Discord, or if s.State.User.ID != st.UserID {
// be moved to a small helper function
self, err := s.User("@me") // TODO: move to Login/New
if err != nil {
log.Println(err)
return
}
// We only care about events that are about us
if st.UserID != self.ID {
return return
} }
// Store the SessionID for later use. // Store the SessionID for later use.
voice.UserID = self.ID // TODO: Review voice.UserID = st.UserID
voice.sessionID = st.SessionID voice.sessionID = st.SessionID
} }
@ -498,7 +520,7 @@ func (s *Session) onVoiceStateUpdate(se *Session, st *VoiceStateUpdate) {
// This is also fired if the Guild's voice region changes while connected // This is also fired if the Guild's voice region changes while connected
// to a voice channel. In that case, need to re-establish connection to // to a voice channel. In that case, need to re-establish connection to
// the new region endpoint. // the new region endpoint.
func (s *Session) onVoiceServerUpdate(se *Session, st *VoiceServerUpdate) { func (s *Session) onVoiceServerUpdate(st *VoiceServerUpdate) {
s.log(LogInformational, "called") s.log(LogInformational, "called")
@ -655,7 +677,7 @@ func (s *Session) Close() (err error) {
// frame and wait for the server to close the connection. // frame and wait for the server to close the connection.
err := s.wsConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) err := s.wsConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil { if err != nil {
s.log(LogError, "error closing websocket, %s", err) s.log(LogInformational, "error closing websocket, %s", err)
} }
// TODO: Wait for Discord to actually close the connection. // TODO: Wait for Discord to actually close the connection.
@ -664,7 +686,7 @@ func (s *Session) Close() (err error) {
s.log(LogInformational, "closing gateway websocket") s.log(LogInformational, "closing gateway websocket")
err = s.wsConn.Close() err = s.wsConn.Close()
if err != nil { if err != nil {
s.log(LogError, "error closing websocket, %s", err) s.log(LogInformational, "error closing websocket, %s", err)
} }
s.wsConn = nil s.wsConn = nil
@ -673,7 +695,7 @@ func (s *Session) Close() (err error) {
s.Unlock() s.Unlock()
s.log(LogInformational, "emit disconnect event") s.log(LogInformational, "emit disconnect event")
s.handle(&Disconnect{}) s.handleEvent(disconnectEventType, &Disconnect{})
return return
} }

View File

@ -404,9 +404,13 @@ func (c *Client) init(o *Options) error {
switch v := val.(type) { switch v := val.(type) {
case *saslSuccess: case *saslSuccess:
case *saslFailure: case *saslFailure:
errorMessage := v.Text
if errorMessage == "" {
// v.Any is type of sub-element in failure, // v.Any is type of sub-element in failure,
// which gives a description of what failed. // which gives a description of what failed if there was no text element
return errors.New("auth failure: " + v.Any.Local) errorMessage = v.Any.Local
}
return errors.New("auth failure: " + errorMessage)
default: default:
return errors.New("expected <success> or <failure>, got <" + name.Local + "> in " + name.Space) return errors.New("expected <success> or <failure>, got <" + name.Local + "> in " + name.Space)
} }
@ -699,6 +703,7 @@ type saslSuccess struct {
type saslFailure struct { type saslFailure struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl failure"` XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl failure"`
Any xml.Name `xml:",any"` Any xml.Name `xml:",any"`
Text string `xml:"text"`
} }
// RFC 3920 C.5 Resource binding name space // RFC 3920 C.5 Resource binding name space

View File

@ -127,7 +127,6 @@ func (gitter *Gitter) GetRooms() ([]Room, error) {
// GetUsersInRoom returns the users in the room with the passed id // GetUsersInRoom returns the users in the room with the passed id
func (gitter *Gitter) GetUsersInRoom(roomID string) ([]User, error) { func (gitter *Gitter) GetUsersInRoom(roomID string) ([]User, error) {
var users []User var users []User
response, err := gitter.get(gitter.config.apiBaseURL + "rooms/" + roomID + "/users") response, err := gitter.get(gitter.config.apiBaseURL + "rooms/" + roomID + "/users")
if err != nil { if err != nil {
@ -259,6 +258,45 @@ func (gitter *Gitter) SetDebug(debug bool, logWriter io.Writer) {
gitter.logWriter = logWriter gitter.logWriter = logWriter
} }
// SearchRooms queries the Rooms resources of gitter API
func (gitter *Gitter) SearchRooms(room string) ([]Room, error) {
var rooms struct {
Results []Room `json:"results"`
}
response, err := gitter.get(gitter.config.apiBaseURL + "rooms?q=" + room )
if err != nil {
gitter.log(err)
return nil, err
}
err = json.Unmarshal(response, &rooms)
if err != nil {
gitter.log(err)
return nil, err
}
return rooms.Results, nil
}
// GetRoomId returns the room ID of a given URI
func (gitter *Gitter) GetRoomId(uri string) (string, error) {
rooms, err := gitter.SearchRooms(uri)
if err != nil {
gitter.log(err)
return "", err
}
for _, element := range rooms {
if element.URI == uri {
return element.ID, nil
}
}
return "", APIError{What: "Room not found."}
}
// Pagination params // Pagination params
type Pagination struct { type Pagination struct {

38
vendor/manifest vendored
View File

@ -1,32 +1,6 @@
{ {
"version": 0, "version": 0,
"dependencies": [ "dependencies": [
{
"importpath": "github.com/42wim/go-gitter",
"repository": "https://github.com/42wim/go-gitter",
"vcs": "git",
"revision": "ae777f740326ef6b4d910496022649859232ff38",
"branch": "fixloop",
"notests": true
},
{
"importpath": "github.com/42wim/matterbridge-plus/bridge",
"repository": "https://github.com/42wim/matterbridge-plus",
"vcs": "git",
"revision": "63c7caabf4b92fa47060cf37beb0ed814cefbaea",
"branch": "master",
"path": "/bridge",
"notests": true
},
{
"importpath": "github.com/42wim/matterbridge-plus/matterclient",
"repository": "https://github.com/42wim/matterbridge-plus",
"vcs": "git",
"revision": "63c7caabf4b92fa47060cf37beb0ed814cefbaea",
"branch": "master",
"path": "/matterclient",
"notests": true
},
{ {
"importpath": "github.com/BurntSushi/toml", "importpath": "github.com/BurntSushi/toml",
"repository": "https://github.com/BurntSushi/toml", "repository": "https://github.com/BurntSushi/toml",
@ -55,7 +29,7 @@
"importpath": "github.com/bwmarrin/discordgo", "importpath": "github.com/bwmarrin/discordgo",
"repository": "https://github.com/bwmarrin/discordgo", "repository": "https://github.com/bwmarrin/discordgo",
"vcs": "git", "vcs": "git",
"revision": "11fa9dc906c531a85cd969b7b6b77c2f452a61b3", "revision": "5835676872d7fa65ee37b32672079c4ede2c1855",
"branch": "master", "branch": "master",
"notests": true "notests": true
}, },
@ -113,7 +87,7 @@
"importpath": "github.com/mattn/go-xmpp", "importpath": "github.com/mattn/go-xmpp",
"repository": "https://github.com/mattn/go-xmpp", "repository": "https://github.com/mattn/go-xmpp",
"vcs": "git", "vcs": "git",
"revision": "f4550b5399387339df5ce4c3f88c1ef85333bdd5", "revision": "325c112042ffd34aa24365590d3a6fdd1a79c011",
"branch": "master", "branch": "master",
"notests": true "notests": true
}, },
@ -174,6 +148,14 @@
"branch": "master", "branch": "master",
"notests": true "notests": true
}, },
{
"importpath": "github.com/sromku/go-gitter",
"repository": "https://github.com/sromku/go-gitter",
"vcs": "git",
"revision": "792abbfcc172b35bbf5f8b260ef184e38bbd90bd",
"branch": "master",
"notests": true
},
{ {
"importpath": "github.com/technoweenie/multipartstreamer", "importpath": "github.com/technoweenie/multipartstreamer",
"repository": "https://github.com/technoweenie/multipartstreamer", "repository": "https://github.com/technoweenie/multipartstreamer",