mirror of
https://github.com/cwinfo/matterbridge.git
synced 2025-06-26 22:19:26 +00:00
Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
248d88c849 | |||
d19535fa21 | |||
49204cafcc | |||
812db2d267 | |||
14490bea9f | |||
0352970051 | |||
ed01820722 | |||
90a61f15cc | |||
86cd7f1ba6 | |||
d6ee55e35f | |||
aef64eec32 | |||
c4193d5ccd | |||
0c94186818 | |||
9039720013 | |||
a3470f8aec | |||
01badde21d | |||
a37b232dd9 | |||
579ee48385 | |||
dd985d1dad | |||
d2caea70a2 | |||
21143cf5ee |
@ -36,7 +36,7 @@ Has a REST API.
|
||||
|
||||
# Requirements
|
||||
Accounts to one of the supported bridges
|
||||
* [Mattermost](https://github.com/mattermost/platform/) 3.8.x - 3.10.x, 4.0.x - 4.1.x
|
||||
* [Mattermost](https://github.com/mattermost/platform/) 3.8.x - 3.10.x, 4.0.x - 4.2.x
|
||||
* [IRC](http://www.mirc.com/servers.html)
|
||||
* [XMPP](https://jabber.org)
|
||||
* [Gitter](https://gitter.im)
|
||||
@ -53,7 +53,7 @@ See https://github.com/42wim/matterbridge/wiki
|
||||
|
||||
# Installing
|
||||
## Binaries
|
||||
* Latest stable release [v1.1.0](https://github.com/42wim/matterbridge/releases/latest)
|
||||
* Latest stable release [v1.2.0](https://github.com/42wim/matterbridge/releases/latest)
|
||||
* Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)
|
||||
|
||||
## Building
|
||||
|
@ -69,6 +69,10 @@ func (b *Api) JoinChannel(channel config.ChannelInfo) error {
|
||||
func (b *Api) Send(msg config.Message) (string, error) {
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
// ignore delete messages
|
||||
if msg.Event == config.EVENT_MSG_DELETE {
|
||||
return "", nil
|
||||
}
|
||||
b.Messages.Enqueue(&msg)
|
||||
return "", nil
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ const (
|
||||
EVENT_FAILURE = "failure"
|
||||
EVENT_REJOIN_CHANNELS = "rejoin_channels"
|
||||
EVENT_USER_ACTION = "user_action"
|
||||
EVENT_MSG_DELETE = "msg_delete"
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
|
@ -66,6 +66,7 @@ func (b *bdiscord) Connect() error {
|
||||
b.c.AddHandler(b.messageCreate)
|
||||
b.c.AddHandler(b.memberUpdate)
|
||||
b.c.AddHandler(b.messageUpdate)
|
||||
b.c.AddHandler(b.messageDelete)
|
||||
err = b.c.Open()
|
||||
if err != nil {
|
||||
flog.Debugf("%#v", err)
|
||||
@ -129,6 +130,13 @@ func (b *bdiscord) Send(msg config.Message) (string, error) {
|
||||
|
||||
if wID == "" {
|
||||
flog.Debugf("Broadcasting using token (API)")
|
||||
if msg.Event == config.EVENT_MSG_DELETE {
|
||||
if msg.ID == "" {
|
||||
return "", nil
|
||||
}
|
||||
err := b.c.ChannelMessageDelete(channelID, msg.ID)
|
||||
return "", err
|
||||
}
|
||||
if msg.ID != "" {
|
||||
_, err := b.c.ChannelMessageEdit(channelID, msg.ID, msg.Username+msg.Text)
|
||||
return msg.ID, err
|
||||
@ -152,6 +160,17 @@ func (b *bdiscord) Send(msg config.Message) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
func (b *bdiscord) messageDelete(s *discordgo.Session, m *discordgo.MessageDelete) {
|
||||
rmsg := config.Message{Account: b.Account, ID: m.ID, Event: config.EVENT_MSG_DELETE, Text: config.EVENT_MSG_DELETE}
|
||||
rmsg.Channel = b.getChannelName(m.ChannelID)
|
||||
if b.UseChannelID {
|
||||
rmsg.Channel = "ID:" + m.ChannelID
|
||||
}
|
||||
flog.Debugf("Sending message from %s to gateway", b.Account)
|
||||
flog.Debugf("Message is %#v", rmsg)
|
||||
b.Remote <- rmsg
|
||||
}
|
||||
|
||||
func (b *bdiscord) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdate) {
|
||||
if b.Config.EditDisable {
|
||||
return
|
||||
@ -223,6 +242,7 @@ func (b *bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat
|
||||
|
||||
rmsg.Text = text
|
||||
flog.Debugf("Sending message from %s on %s to gateway", m.Author.Username, b.Account)
|
||||
flog.Debugf("Message is %#v", rmsg)
|
||||
b.Remote <- rmsg
|
||||
}
|
||||
|
||||
|
@ -88,6 +88,7 @@ func (b *Bgitter) JoinChannel(channel config.ChannelInfo) error {
|
||||
rmsg.Event = config.EVENT_USER_ACTION
|
||||
rmsg.Text = strings.Replace(rmsg.Text, "@"+ev.Message.From.Username+" ", "", -1)
|
||||
}
|
||||
flog.Debugf("Message is %#v", rmsg)
|
||||
b.Remote <- rmsg
|
||||
}
|
||||
case *gitter.GitterConnectionClosed:
|
||||
@ -105,6 +106,17 @@ func (b *Bgitter) Send(msg config.Message) (string, error) {
|
||||
flog.Errorf("Could not find roomID for %v", msg.Channel)
|
||||
return "", nil
|
||||
}
|
||||
if msg.Event == config.EVENT_MSG_DELETE {
|
||||
if msg.ID == "" {
|
||||
return "", nil
|
||||
}
|
||||
// gitter has no delete message api
|
||||
_, err := b.c.UpdateMessage(roomID, msg.ID, "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
if msg.ID != "" {
|
||||
flog.Debugf("updating message with id %s", msg.ID)
|
||||
_, err := b.c.UpdateMessage(roomID, msg.ID, msg.Username+msg.Text)
|
||||
|
@ -129,6 +129,10 @@ func (b *Birc) JoinChannel(channel config.ChannelInfo) error {
|
||||
}
|
||||
|
||||
func (b *Birc) Send(msg config.Message) (string, error) {
|
||||
// ignore delete messages
|
||||
if msg.Event == config.EVENT_MSG_DELETE {
|
||||
return "", nil
|
||||
}
|
||||
flog.Debugf("Receiving %#v", msg)
|
||||
if strings.HasPrefix(msg.Text, "!") {
|
||||
b.Command(&msg)
|
||||
|
@ -76,6 +76,10 @@ func (b *Bmatrix) JoinChannel(channel config.ChannelInfo) error {
|
||||
|
||||
func (b *Bmatrix) Send(msg config.Message) (string, error) {
|
||||
flog.Debugf("Receiving %#v", msg)
|
||||
// ignore delete messages
|
||||
if msg.Event == config.EVENT_MSG_DELETE {
|
||||
return "", nil
|
||||
}
|
||||
channel := b.getRoomID(msg.Channel)
|
||||
flog.Debugf("Sending to channel %s", channel)
|
||||
if msg.Event == config.EVENT_USER_ACTION {
|
||||
|
@ -25,6 +25,7 @@ type MMMessage struct {
|
||||
Username string
|
||||
UserID string
|
||||
ID string
|
||||
Event string
|
||||
}
|
||||
|
||||
type Bmattermost struct {
|
||||
@ -168,6 +169,12 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) {
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
if msg.Event == config.EVENT_MSG_DELETE {
|
||||
if msg.ID == "" {
|
||||
return "", nil
|
||||
}
|
||||
return msg.ID, b.mc.DeleteMessage(msg.ID)
|
||||
}
|
||||
if msg.ID != "" {
|
||||
return b.mc.EditMessage(msg.ID, message)
|
||||
}
|
||||
@ -188,13 +195,14 @@ func (b *Bmattermost) handleMatter() {
|
||||
go b.handleMatterClient(mchan)
|
||||
}
|
||||
for message := range mchan {
|
||||
rmsg := config.Message{Username: message.Username, Channel: message.Channel, Account: b.Account, UserID: message.UserID, ID: message.ID}
|
||||
rmsg := config.Message{Username: message.Username, Channel: message.Channel, Account: b.Account, UserID: message.UserID, ID: message.ID, Event: message.Event}
|
||||
text, ok := b.replaceAction(message.Text)
|
||||
if ok {
|
||||
rmsg.Event = config.EVENT_USER_ACTION
|
||||
}
|
||||
rmsg.Text = text
|
||||
flog.Debugf("Sending message from %s on %s to gateway", message.Username, b.Account)
|
||||
flog.Debugf("Message is %#v", rmsg)
|
||||
b.Remote <- rmsg
|
||||
}
|
||||
}
|
||||
@ -214,7 +222,7 @@ func (b *Bmattermost) handleMatterClient(mchan chan *MMMessage) {
|
||||
}
|
||||
// do not post our own messages back to irc
|
||||
// only listen to message from our team
|
||||
if (message.Raw.Event == "posted" || message.Raw.Event == "post_edited") &&
|
||||
if (message.Raw.Event == "posted" || message.Raw.Event == "post_edited" || message.Raw.Event == "post_deleted") &&
|
||||
b.mc.User.Username != message.Username && message.Raw.Data["team_id"].(string) == b.TeamId {
|
||||
// if the message has reactions don't repost it (for now, until we can correlate reaction with message)
|
||||
if message.Post.HasReactions {
|
||||
@ -230,6 +238,9 @@ func (b *Bmattermost) handleMatterClient(mchan chan *MMMessage) {
|
||||
if message.Raw.Event == "post_edited" && !b.Config.EditDisable {
|
||||
m.Text = message.Text + b.Config.EditSuffix
|
||||
}
|
||||
if message.Raw.Event == "post_deleted" {
|
||||
m.Event = config.EVENT_MSG_DELETE
|
||||
}
|
||||
if len(message.Post.FileIds) > 0 {
|
||||
for _, link := range b.mc.GetFileLinks(message.Post.FileIds) {
|
||||
m.Text = m.Text + "\n" + link
|
||||
|
@ -58,6 +58,10 @@ func (b *Brocketchat) JoinChannel(channel config.ChannelInfo) error {
|
||||
}
|
||||
|
||||
func (b *Brocketchat) Send(msg config.Message) (string, error) {
|
||||
// ignore delete messages
|
||||
if msg.Event == config.EVENT_MSG_DELETE {
|
||||
return "", nil
|
||||
}
|
||||
flog.Debugf("Receiving %#v", msg)
|
||||
matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL}
|
||||
matterMessage.Channel = msg.Channel
|
||||
|
@ -166,6 +166,16 @@ func (b *Bslack) Send(msg config.Message) (string, error) {
|
||||
// replace mentions
|
||||
np.LinkNames = 1
|
||||
|
||||
if msg.Event == config.EVENT_MSG_DELETE {
|
||||
// some protocols echo deletes, but with empty ID
|
||||
if msg.ID == "" {
|
||||
return "", nil
|
||||
}
|
||||
// we get a "slack <ID>", split it
|
||||
ts := strings.Fields(msg.ID)
|
||||
b.sc.DeleteMessage(schannel.ID, ts[1])
|
||||
return "", nil
|
||||
}
|
||||
// if we have no ID it means we're creating a new message, not updating an existing one
|
||||
if msg.ID != "" {
|
||||
ts := strings.Fields(msg.ID)
|
||||
@ -231,91 +241,90 @@ func (b *Bslack) handleSlack() {
|
||||
if b.Config.WebhookURL == "" && b.Config.WebhookBindAddress == "" && message.Username == b.si.User.Name {
|
||||
continue
|
||||
}
|
||||
if message.Text == "" || message.Username == "" {
|
||||
if (message.Text == "" || message.Username == "") && message.Raw.SubType != "message_deleted" {
|
||||
continue
|
||||
}
|
||||
texts := strings.Split(message.Text, "\n")
|
||||
for _, text := range texts {
|
||||
text = b.replaceURL(text)
|
||||
text = html.UnescapeString(text)
|
||||
flog.Debugf("Sending message from %s on %s to gateway", message.Username, b.Account)
|
||||
msg := config.Message{Text: text, Username: message.Username, Channel: message.Channel, Account: b.Account, Avatar: b.getAvatar(message.Username), UserID: message.UserID, ID: "slack " + message.Raw.Timestamp}
|
||||
if message.Raw.SubType == "me_message" {
|
||||
msg.Event = config.EVENT_USER_ACTION
|
||||
}
|
||||
if message.Raw.SubType == "channel_leave" || message.Raw.SubType == "channel_join" {
|
||||
msg.Username = "system"
|
||||
msg.Event = config.EVENT_JOIN_LEAVE
|
||||
}
|
||||
// edited messages have a submessage, use this timestamp
|
||||
if message.Raw.SubMessage != nil {
|
||||
msg.ID = "slack " + message.Raw.SubMessage.Timestamp
|
||||
}
|
||||
b.Remote <- msg
|
||||
text := message.Text
|
||||
text = b.replaceURL(text)
|
||||
text = html.UnescapeString(text)
|
||||
flog.Debugf("Sending message from %s on %s to gateway", message.Username, b.Account)
|
||||
msg := config.Message{Text: text, Username: message.Username, Channel: message.Channel, Account: b.Account, Avatar: b.getAvatar(message.Username), UserID: message.UserID, ID: "slack " + message.Raw.Timestamp}
|
||||
if message.Raw.SubType == "me_message" {
|
||||
msg.Event = config.EVENT_USER_ACTION
|
||||
}
|
||||
if message.Raw.SubType == "channel_leave" || message.Raw.SubType == "channel_join" {
|
||||
msg.Username = "system"
|
||||
msg.Event = config.EVENT_JOIN_LEAVE
|
||||
}
|
||||
// edited messages have a submessage, use this timestamp
|
||||
if message.Raw.SubMessage != nil {
|
||||
msg.ID = "slack " + message.Raw.SubMessage.Timestamp
|
||||
}
|
||||
if message.Raw.SubType == "message_deleted" {
|
||||
msg.Text = config.EVENT_MSG_DELETE
|
||||
msg.Event = config.EVENT_MSG_DELETE
|
||||
msg.ID = "slack " + message.Raw.DeletedTimestamp
|
||||
}
|
||||
flog.Debugf("Message is %#v", msg)
|
||||
b.Remote <- msg
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bslack) handleSlackClient(mchan chan *MMMessage) {
|
||||
count := 0
|
||||
for msg := range b.rtm.IncomingEvents {
|
||||
switch ev := msg.Data.(type) {
|
||||
case *slack.MessageEvent:
|
||||
// ignore first message
|
||||
if count > 0 {
|
||||
flog.Debugf("Receiving from slackclient %#v", ev)
|
||||
if len(ev.Attachments) > 0 {
|
||||
// skip messages we made ourselves
|
||||
if ev.Attachments[0].CallbackID == "matterbridge" {
|
||||
continue
|
||||
}
|
||||
flog.Debugf("Receiving from slackclient %#v", ev)
|
||||
if len(ev.Attachments) > 0 {
|
||||
// skip messages we made ourselves
|
||||
if ev.Attachments[0].CallbackID == "matterbridge" {
|
||||
continue
|
||||
}
|
||||
if !b.Config.EditDisable && ev.SubMessage != nil && ev.SubMessage.ThreadTimestamp != ev.SubMessage.Timestamp {
|
||||
flog.Debugf("SubMessage %#v", ev.SubMessage)
|
||||
ev.User = ev.SubMessage.User
|
||||
ev.Text = ev.SubMessage.Text + b.Config.EditSuffix
|
||||
}
|
||||
// use our own func because rtm.GetChannelInfo doesn't work for private channels
|
||||
channel, err := b.getChannelByID(ev.Channel)
|
||||
}
|
||||
if !b.Config.EditDisable && ev.SubMessage != nil && ev.SubMessage.ThreadTimestamp != ev.SubMessage.Timestamp {
|
||||
flog.Debugf("SubMessage %#v", ev.SubMessage)
|
||||
ev.User = ev.SubMessage.User
|
||||
ev.Text = ev.SubMessage.Text + b.Config.EditSuffix
|
||||
}
|
||||
// use our own func because rtm.GetChannelInfo doesn't work for private channels
|
||||
channel, err := b.getChannelByID(ev.Channel)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
m := &MMMessage{}
|
||||
if ev.BotID == "" && ev.SubType != "message_deleted" {
|
||||
user, err := b.rtm.GetUserInfo(ev.User)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
m := &MMMessage{}
|
||||
if ev.BotID == "" {
|
||||
user, err := b.rtm.GetUserInfo(ev.User)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
m.UserID = user.ID
|
||||
m.Username = user.Name
|
||||
}
|
||||
m.Channel = channel.Name
|
||||
m.Text = ev.Text
|
||||
if m.Text == "" {
|
||||
for _, attach := range ev.Attachments {
|
||||
if attach.Text != "" {
|
||||
m.Text = attach.Text
|
||||
} else {
|
||||
m.Text = attach.Fallback
|
||||
}
|
||||
}
|
||||
}
|
||||
m.Raw = ev
|
||||
m.Text = b.replaceMention(m.Text)
|
||||
// when using webhookURL we can't check if it's our webhook or not for now
|
||||
if ev.BotID != "" && b.Config.WebhookURL == "" {
|
||||
bot, err := b.rtm.GetBotInfo(ev.BotID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if bot.Name != "" {
|
||||
m.Username = bot.Name
|
||||
m.UserID = bot.ID
|
||||
}
|
||||
}
|
||||
mchan <- m
|
||||
m.UserID = user.ID
|
||||
m.Username = user.Name
|
||||
}
|
||||
count++
|
||||
m.Channel = channel.Name
|
||||
m.Text = ev.Text
|
||||
if m.Text == "" {
|
||||
for _, attach := range ev.Attachments {
|
||||
if attach.Text != "" {
|
||||
m.Text = attach.Text
|
||||
} else {
|
||||
m.Text = attach.Fallback
|
||||
}
|
||||
}
|
||||
}
|
||||
m.Raw = ev
|
||||
m.Text = b.replaceMention(m.Text)
|
||||
// when using webhookURL we can't check if it's our webhook or not for now
|
||||
if ev.BotID != "" && b.Config.WebhookURL == "" {
|
||||
bot, err := b.rtm.GetBotInfo(ev.BotID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if bot.Name != "" {
|
||||
m.Username = bot.Name
|
||||
m.UserID = bot.ID
|
||||
}
|
||||
}
|
||||
mchan <- m
|
||||
case *slack.OutgoingErrorEvent:
|
||||
flog.Debugf("%#v", ev.Error())
|
||||
case *slack.ChannelJoinedEvent:
|
||||
|
@ -70,6 +70,10 @@ func (b *Bsteam) JoinChannel(channel config.ChannelInfo) error {
|
||||
}
|
||||
|
||||
func (b *Bsteam) Send(msg config.Message) (string, error) {
|
||||
// ignore delete messages
|
||||
if msg.Event == config.EVENT_MSG_DELETE {
|
||||
return "", nil
|
||||
}
|
||||
id, err := steamid.NewId(msg.Channel)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -68,6 +68,18 @@ func (b *Btelegram) Send(msg config.Message) (string, error) {
|
||||
msg.Text = makeHTML(msg.Text)
|
||||
}
|
||||
|
||||
if msg.Event == config.EVENT_MSG_DELETE {
|
||||
if msg.ID == "" {
|
||||
return "", nil
|
||||
}
|
||||
msgid, err := strconv.Atoi(msg.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_, err = b.c.DeleteMessage(tgbotapi.DeleteMessageConfig{ChatID: chatid, MessageID: msgid})
|
||||
return "", err
|
||||
}
|
||||
|
||||
// edit the message if we have a msg ID
|
||||
if msg.ID != "" {
|
||||
msgid, err := strconv.Atoi(msg.ID)
|
||||
|
@ -80,6 +80,10 @@ func (b *Bxmpp) JoinChannel(channel config.ChannelInfo) error {
|
||||
}
|
||||
|
||||
func (b *Bxmpp) Send(msg config.Message) (string, error) {
|
||||
// ignore delete messages
|
||||
if msg.Event == config.EVENT_MSG_DELETE {
|
||||
return "", nil
|
||||
}
|
||||
flog.Debugf("Receiving %#v", msg)
|
||||
b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Config.Muc, Text: msg.Username + msg.Text})
|
||||
return "", nil
|
||||
|
27
changelog.md
27
changelog.md
@ -1,3 +1,30 @@
|
||||
# v1.2.0
|
||||
## Breaking changes
|
||||
* If you're running a discord bridge, update to this release before 16 october otherwise
|
||||
it will stop working. (see https://discordapp.com/developers/docs/reference)
|
||||
|
||||
## New features
|
||||
* general: Add delete support. (actually delete the messages on bridges that support it)
|
||||
(mattermost,discord,gitter,slack,telegram)
|
||||
|
||||
## Bugfix
|
||||
* Do not break messages on newline (slack). Closes #258
|
||||
* Update telegram library
|
||||
* Update discord library (supports v6 API now). Old API is deprecated on 16 October
|
||||
|
||||
# v1.1.2
|
||||
## New features
|
||||
* general: also build darwin binaries
|
||||
* mattermost: add support for mattermost 4.2.x
|
||||
|
||||
## Bugfix
|
||||
* mattermost: Send images when text is empty regression. (mattermost). Closes #254
|
||||
* slack: also send the first messsage after connect. #252
|
||||
|
||||
# v1.1.1
|
||||
## Bugfix
|
||||
* mattermost: fix public links
|
||||
|
||||
# v1.1.0
|
||||
## New features
|
||||
* general: Add better editing support. (actually edit the messages on bridges that support it)
|
||||
|
@ -2,9 +2,10 @@
|
||||
go version |grep go1.9 || exit
|
||||
VERSION=$(git describe --tags)
|
||||
mkdir ci/binaries
|
||||
GOOS=windows GOARCH=amd64 go build -ldflags "-s -w -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o ci/binaries/matterbridge-$VERSION-win64.exe
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags "-s -w -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o ci/binaries/matterbridge-$VERSION-linux64
|
||||
GOOS=windows GOARCH=amd64 go build -ldflags "-s -w -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o ci/binaries/matterbridge-$VERSION-windows-amd64.exe
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags "-s -w -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o ci/binaries/matterbridge-$VERSION-linux-amd64
|
||||
GOOS=linux GOARCH=arm go build -ldflags "-s -w -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o ci/binaries/matterbridge-$VERSION-linux-arm
|
||||
GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o ci/binaries/matterbridge-$VERSION-darwin-amd64
|
||||
cd ci
|
||||
cat > deploy.json <<EOF
|
||||
{
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
version = "1.1.0"
|
||||
version = "1.2.0"
|
||||
githash string
|
||||
)
|
||||
|
||||
|
@ -383,7 +383,6 @@ WebhookURL="https://hooks.slack.com/services/yourhook"
|
||||
#AND DEDICATED BOT USER WHEN POSSIBLE!
|
||||
#Address to listen on for outgoing webhook requests from slack
|
||||
#See account settings - integrations - outgoing webhooks on slack
|
||||
#This setting will not be used when useAPI is eanbled
|
||||
#webhooks
|
||||
#OPTIONAL
|
||||
WebhookBindAddress="0.0.0.0:9999"
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
[mattermost]
|
||||
[mattermost.work]
|
||||
useAPI=true
|
||||
#do not prefix it wit http:// or https://
|
||||
Server="yourmattermostserver.domain"
|
||||
Team="yourteam"
|
||||
|
@ -277,6 +277,13 @@ func (m *MMClient) WsReceiver() {
|
||||
// check if we didn't empty the message
|
||||
if msg.Text != "" {
|
||||
m.MessageChan <- msg
|
||||
continue
|
||||
}
|
||||
// if we have file attached but the message is empty, also send it
|
||||
if msg.Post != nil {
|
||||
if msg.Text != "" || len(msg.Post.FileIds) > 0 {
|
||||
m.MessageChan <- msg
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
@ -292,7 +299,7 @@ func (m *MMClient) WsReceiver() {
|
||||
|
||||
func (m *MMClient) parseMessage(rmsg *Message) {
|
||||
switch rmsg.Raw.Event {
|
||||
case model.WEBSOCKET_EVENT_POSTED, model.WEBSOCKET_EVENT_POST_EDITED:
|
||||
case model.WEBSOCKET_EVENT_POSTED, model.WEBSOCKET_EVENT_POST_EDITED, model.WEBSOCKET_EVENT_POST_DELETED:
|
||||
m.parseActionPost(rmsg)
|
||||
/*
|
||||
case model.ACTION_USER_REMOVED:
|
||||
@ -469,6 +476,14 @@ func (m *MMClient) EditMessage(postId string, text string) (string, error) {
|
||||
return res.Id, nil
|
||||
}
|
||||
|
||||
func (m *MMClient) DeleteMessage(postId string) error {
|
||||
_, resp := m.Client.DeletePost(postId)
|
||||
if resp.Error != nil {
|
||||
return resp.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MMClient) JoinChannel(channelId string) error {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
@ -511,8 +526,8 @@ func (m *MMClient) GetPosts(channelId string, limit int) *model.PostList {
|
||||
}
|
||||
|
||||
func (m *MMClient) GetPublicLink(filename string) string {
|
||||
res, err := m.Client.GetFileLink(filename)
|
||||
if err != nil {
|
||||
res, resp := m.Client.GetFileLink(filename)
|
||||
if resp.Error != nil {
|
||||
return ""
|
||||
}
|
||||
return res
|
||||
@ -521,8 +536,8 @@ func (m *MMClient) GetPublicLink(filename string) string {
|
||||
func (m *MMClient) GetPublicLinks(filenames []string) []string {
|
||||
var output []string
|
||||
for _, f := range filenames {
|
||||
res, err := m.Client.GetFileLink(f)
|
||||
if err != nil {
|
||||
res, resp := m.Client.GetFileLink(f)
|
||||
if resp.Error != nil {
|
||||
continue
|
||||
}
|
||||
output = append(output, res)
|
||||
@ -538,8 +553,8 @@ func (m *MMClient) GetFileLinks(filenames []string) []string {
|
||||
|
||||
var output []string
|
||||
for _, f := range filenames {
|
||||
res, err := m.Client.GetFileLink(f)
|
||||
if err != nil {
|
||||
res, resp := m.Client.GetFileLink(f)
|
||||
if resp.Error != nil {
|
||||
// public links is probably disabled, create the link ourselves
|
||||
output = append(output, uriScheme+m.Credentials.Server+model.API_URL_SUFFIX_V3+"/files/"+f+"/get")
|
||||
continue
|
||||
@ -567,6 +582,16 @@ func (m *MMClient) UpdateLastViewed(channelId string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MMClient) UpdateUserNick(nick string) error {
|
||||
user := m.User
|
||||
user.Nickname = nick
|
||||
_, resp := m.Client.UpdateUser(user)
|
||||
if resp.Error != nil {
|
||||
return resp.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MMClient) UsernamesInChannel(channelId string) []string {
|
||||
res, resp := m.Client.GetChannelMembers(channelId, 0, 50000, "")
|
||||
if resp.Error != nil {
|
||||
@ -600,9 +625,9 @@ func (m *MMClient) createCookieJar(token string) *cookiejar.Jar {
|
||||
func (m *MMClient) SendDirectMessage(toUserId string, msg string) {
|
||||
m.log.Debugf("SendDirectMessage to %s, msg %s", toUserId, msg)
|
||||
// create DM channel (only happens on first message)
|
||||
_, err := m.Client.CreateDirectChannel(m.User.Id, toUserId)
|
||||
if err != nil {
|
||||
m.log.Debugf("SendDirectMessage to %#v failed: %s", toUserId, err)
|
||||
_, resp := m.Client.CreateDirectChannel(m.User.Id, toUserId)
|
||||
if resp.Error != nil {
|
||||
m.log.Debugf("SendDirectMessage to %#v failed: %s", toUserId, resp.Error)
|
||||
return
|
||||
}
|
||||
channelName := model.GetDMNameFromIds(toUserId, m.User.Id)
|
||||
@ -854,7 +879,8 @@ func supportedVersion(version string) bool {
|
||||
strings.HasPrefix(version, "3.9.0") ||
|
||||
strings.HasPrefix(version, "3.10.0") ||
|
||||
strings.HasPrefix(version, "4.0") ||
|
||||
strings.HasPrefix(version, "4.1") {
|
||||
strings.HasPrefix(version, "4.1") ||
|
||||
strings.HasPrefix(version, "4.2") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
3
vendor/github.com/bwmarrin/discordgo/discord.go
generated
vendored
3
vendor/github.com/bwmarrin/discordgo/discord.go
generated
vendored
@ -21,7 +21,7 @@ import (
|
||||
)
|
||||
|
||||
// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
|
||||
const VERSION = "0.16.0"
|
||||
const VERSION = "0.17.0"
|
||||
|
||||
// ErrMFA will be risen by New when the user has 2FA.
|
||||
var ErrMFA = errors.New("account has 2FA enabled")
|
||||
@ -59,6 +59,7 @@ func New(args ...interface{}) (s *Session, err error) {
|
||||
MaxRestRetries: 3,
|
||||
Client: &http.Client{Timeout: (20 * time.Second)},
|
||||
sequence: new(int64),
|
||||
LastHeartbeatAck: time.Now().UTC(),
|
||||
}
|
||||
|
||||
// If no arguments are passed return the empty Session interface.
|
||||
|
42
vendor/github.com/bwmarrin/discordgo/endpoints.go
generated
vendored
42
vendor/github.com/bwmarrin/discordgo/endpoints.go
generated
vendored
@ -11,6 +11,9 @@
|
||||
|
||||
package discordgo
|
||||
|
||||
// APIVersion is the Discord API version used for the REST and Websocket API.
|
||||
var APIVersion = "6"
|
||||
|
||||
// Known Discord API Endpoints.
|
||||
var (
|
||||
EndpointStatus = "https://status.discordapp.com/api/v2/"
|
||||
@ -18,13 +21,14 @@ var (
|
||||
EndpointSmActive = EndpointSm + "active.json"
|
||||
EndpointSmUpcoming = EndpointSm + "upcoming.json"
|
||||
|
||||
EndpointDiscord = "https://discordapp.com/"
|
||||
EndpointAPI = EndpointDiscord + "api/"
|
||||
EndpointGuilds = EndpointAPI + "guilds/"
|
||||
EndpointChannels = EndpointAPI + "channels/"
|
||||
EndpointUsers = EndpointAPI + "users/"
|
||||
EndpointGateway = EndpointAPI + "gateway"
|
||||
EndpointWebhooks = EndpointAPI + "webhooks/"
|
||||
EndpointDiscord = "https://discordapp.com/"
|
||||
EndpointAPI = EndpointDiscord + "api/v" + APIVersion + "/"
|
||||
EndpointGuilds = EndpointAPI + "guilds/"
|
||||
EndpointChannels = EndpointAPI + "channels/"
|
||||
EndpointUsers = EndpointAPI + "users/"
|
||||
EndpointGateway = EndpointAPI + "gateway"
|
||||
EndpointGatewayBot = EndpointGateway + "/bot"
|
||||
EndpointWebhooks = EndpointAPI + "webhooks/"
|
||||
|
||||
EndpointCDN = "https://cdn.discordapp.com/"
|
||||
EndpointCDNAttachments = EndpointCDN + "attachments/"
|
||||
@ -54,16 +58,17 @@ var (
|
||||
EndpointReport = EndpointAPI + "report"
|
||||
EndpointIntegrations = EndpointAPI + "integrations"
|
||||
|
||||
EndpointUser = func(uID string) string { return EndpointUsers + uID }
|
||||
EndpointUserAvatar = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".png" }
|
||||
EndpointUserSettings = func(uID string) string { return EndpointUsers + uID + "/settings" }
|
||||
EndpointUserGuilds = func(uID string) string { return EndpointUsers + uID + "/guilds" }
|
||||
EndpointUserGuild = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID }
|
||||
EndpointUserGuildSettings = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID + "/settings" }
|
||||
EndpointUserChannels = func(uID string) string { return EndpointUsers + uID + "/channels" }
|
||||
EndpointUserDevices = func(uID string) string { return EndpointUsers + uID + "/devices" }
|
||||
EndpointUserConnections = func(uID string) string { return EndpointUsers + uID + "/connections" }
|
||||
EndpointUserNotes = func(uID string) string { return EndpointUsers + "@me/notes/" + uID }
|
||||
EndpointUser = func(uID string) string { return EndpointUsers + uID }
|
||||
EndpointUserAvatar = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".png" }
|
||||
EndpointUserAvatarAnimated = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".gif" }
|
||||
EndpointUserSettings = func(uID string) string { return EndpointUsers + uID + "/settings" }
|
||||
EndpointUserGuilds = func(uID string) string { return EndpointUsers + uID + "/guilds" }
|
||||
EndpointUserGuild = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID }
|
||||
EndpointUserGuildSettings = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID + "/settings" }
|
||||
EndpointUserChannels = func(uID string) string { return EndpointUsers + uID + "/channels" }
|
||||
EndpointUserDevices = func(uID string) string { return EndpointUsers + uID + "/devices" }
|
||||
EndpointUserConnections = func(uID string) string { return EndpointUsers + uID + "/connections" }
|
||||
EndpointUserNotes = func(uID string) string { return EndpointUsers + "@me/notes/" + uID }
|
||||
|
||||
EndpointGuild = func(gID string) string { return EndpointGuilds + gID }
|
||||
EndpointGuildInivtes = func(gID string) string { return EndpointGuilds + gID + "/invites" }
|
||||
@ -103,6 +108,9 @@ var (
|
||||
EndpointWebhook = func(wID string) string { return EndpointWebhooks + wID }
|
||||
EndpointWebhookToken = func(wID, token string) string { return EndpointWebhooks + wID + "/" + token }
|
||||
|
||||
EndpointMessageReactionsAll = func(cID, mID string) string {
|
||||
return EndpointChannelMessage(cID, mID) + "/reactions"
|
||||
}
|
||||
EndpointMessageReactions = func(cID, mID, eID string) string {
|
||||
return EndpointChannelMessage(cID, mID) + "/reactions/" + eID
|
||||
}
|
||||
|
14
vendor/github.com/bwmarrin/discordgo/event.go
generated
vendored
14
vendor/github.com/bwmarrin/discordgo/event.go
generated
vendored
@ -156,12 +156,20 @@ func (s *Session) removeEventHandlerInstance(t string, ehi *eventHandlerInstance
|
||||
// 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 s.SyncEvents {
|
||||
eh.eventHandler.Handle(s, i)
|
||||
} else {
|
||||
go eh.eventHandler.Handle(s, i)
|
||||
}
|
||||
}
|
||||
|
||||
if len(s.onceHandlers[t]) > 0 {
|
||||
for _, eh := range s.onceHandlers[t] {
|
||||
go eh.eventHandler.Handle(s, i)
|
||||
if s.SyncEvents {
|
||||
eh.eventHandler.Handle(s, i)
|
||||
} else {
|
||||
go eh.eventHandler.Handle(s, i)
|
||||
}
|
||||
}
|
||||
s.onceHandlers[t] = nil
|
||||
}
|
||||
@ -216,7 +224,7 @@ func (s *Session) onInterface(i interface{}) {
|
||||
case *VoiceStateUpdate:
|
||||
go s.onVoiceStateUpdate(t)
|
||||
}
|
||||
err := s.State.onInterface(s, i)
|
||||
err := s.State.OnInterface(s, i)
|
||||
if err != nil {
|
||||
s.log(LogDebug, "error dispatching internal event, %s", err)
|
||||
}
|
||||
|
94
vendor/github.com/bwmarrin/discordgo/message.go
generated
vendored
94
vendor/github.com/bwmarrin/discordgo/message.go
generated
vendored
@ -10,9 +10,24 @@
|
||||
package discordgo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MessageType is the type of Message
|
||||
type MessageType int
|
||||
|
||||
// Block contains the valid known MessageType values
|
||||
const (
|
||||
MessageTypeDefault MessageType = iota
|
||||
MessageTypeRecipientAdd
|
||||
MessageTypeRecipientRemove
|
||||
MessageTypeCall
|
||||
MessageTypeChannelNameChange
|
||||
MessageTypeChannelIconChange
|
||||
MessageTypeChannelPinnedMessage
|
||||
MessageTypeGuildMemberJoin
|
||||
)
|
||||
|
||||
// A Message stores all data related to a specific Discord message.
|
||||
@ -30,12 +45,14 @@ type Message struct {
|
||||
Embeds []*MessageEmbed `json:"embeds"`
|
||||
Mentions []*User `json:"mentions"`
|
||||
Reactions []*MessageReactions `json:"reactions"`
|
||||
Type MessageType `json:"type"`
|
||||
}
|
||||
|
||||
// File stores info about files you e.g. send in messages.
|
||||
type File struct {
|
||||
Name string
|
||||
Reader io.Reader
|
||||
Name string
|
||||
ContentType string
|
||||
Reader io.Reader
|
||||
}
|
||||
|
||||
// MessageSend stores all parameters you can send with ChannelMessageSendComplex.
|
||||
@ -43,7 +60,10 @@ type MessageSend struct {
|
||||
Content string `json:"content,omitempty"`
|
||||
Embed *MessageEmbed `json:"embed,omitempty"`
|
||||
Tts bool `json:"tts"`
|
||||
File *File `json:"file"`
|
||||
Files []*File `json:"-"`
|
||||
|
||||
// TODO: Remove this when compatibility is not required.
|
||||
File *File `json:"-"`
|
||||
}
|
||||
|
||||
// MessageEdit is used to chain parameters via ChannelMessageEditComplex, which
|
||||
@ -168,13 +188,65 @@ type MessageReactions struct {
|
||||
|
||||
// ContentWithMentionsReplaced will replace all @<id> mentions with the
|
||||
// username of the mention.
|
||||
func (m *Message) ContentWithMentionsReplaced() string {
|
||||
if m.Mentions == nil {
|
||||
return m.Content
|
||||
}
|
||||
content := m.Content
|
||||
func (m *Message) ContentWithMentionsReplaced() (content string) {
|
||||
content = m.Content
|
||||
|
||||
for _, user := range m.Mentions {
|
||||
content = regexp.MustCompile(fmt.Sprintf("<@!?(%s)>", user.ID)).ReplaceAllString(content, "@"+user.Username)
|
||||
content = strings.NewReplacer(
|
||||
"<@"+user.ID+">", "@"+user.Username,
|
||||
"<@!"+user.ID+">", "@"+user.Username,
|
||||
).Replace(content)
|
||||
}
|
||||
return content
|
||||
return
|
||||
}
|
||||
|
||||
var patternChannels = regexp.MustCompile("<#[^>]*>")
|
||||
|
||||
// ContentWithMoreMentionsReplaced will replace all @<id> mentions with the
|
||||
// username of the mention, but also role IDs and more.
|
||||
func (m *Message) ContentWithMoreMentionsReplaced(s *Session) (content string, err error) {
|
||||
content = m.Content
|
||||
|
||||
if !s.StateEnabled {
|
||||
content = m.ContentWithMentionsReplaced()
|
||||
return
|
||||
}
|
||||
|
||||
channel, err := s.State.Channel(m.ChannelID)
|
||||
if err != nil {
|
||||
content = m.ContentWithMentionsReplaced()
|
||||
return
|
||||
}
|
||||
|
||||
for _, user := range m.Mentions {
|
||||
nick := user.Username
|
||||
|
||||
member, err := s.State.Member(channel.GuildID, user.ID)
|
||||
if err == nil && member.Nick != "" {
|
||||
nick = member.Nick
|
||||
}
|
||||
|
||||
content = strings.NewReplacer(
|
||||
"<@"+user.ID+">", "@"+user.Username,
|
||||
"<@!"+user.ID+">", "@"+nick,
|
||||
).Replace(content)
|
||||
}
|
||||
for _, roleID := range m.MentionRoles {
|
||||
role, err := s.State.Role(channel.GuildID, roleID)
|
||||
if err != nil || !role.Mentionable {
|
||||
continue
|
||||
}
|
||||
|
||||
content = strings.Replace(content, "<&"+role.ID+">", "@"+role.Name, -1)
|
||||
}
|
||||
|
||||
content = patternChannels.ReplaceAllStringFunc(content, func(mention string) string {
|
||||
channel, err := s.State.Channel(mention[2 : len(mention)-1])
|
||||
if err != nil || channel.Type == ChannelTypeGuildVoice {
|
||||
return mention
|
||||
}
|
||||
|
||||
return "#" + channel.Name
|
||||
})
|
||||
return
|
||||
}
|
||||
|
47
vendor/github.com/bwmarrin/discordgo/ratelimit.go
generated
vendored
47
vendor/github.com/bwmarrin/discordgo/ratelimit.go
generated
vendored
@ -3,17 +3,26 @@ package discordgo
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// customRateLimit holds information for defining a custom rate limit
|
||||
type customRateLimit struct {
|
||||
suffix string
|
||||
requests int
|
||||
reset time.Duration
|
||||
}
|
||||
|
||||
// RateLimiter holds all ratelimit buckets
|
||||
type RateLimiter struct {
|
||||
sync.Mutex
|
||||
global *int64
|
||||
buckets map[string]*Bucket
|
||||
globalRateLimit time.Duration
|
||||
global *int64
|
||||
buckets map[string]*Bucket
|
||||
globalRateLimit time.Duration
|
||||
customRateLimits []*customRateLimit
|
||||
}
|
||||
|
||||
// NewRatelimiter returns a new RateLimiter
|
||||
@ -22,6 +31,13 @@ func NewRatelimiter() *RateLimiter {
|
||||
return &RateLimiter{
|
||||
buckets: make(map[string]*Bucket),
|
||||
global: new(int64),
|
||||
customRateLimits: []*customRateLimit{
|
||||
&customRateLimit{
|
||||
suffix: "//reactions//",
|
||||
requests: 1,
|
||||
reset: 200 * time.Millisecond,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,6 +56,14 @@ func (r *RateLimiter) getBucket(key string) *Bucket {
|
||||
global: r.global,
|
||||
}
|
||||
|
||||
// Check if there is a custom ratelimit set for this bucket ID.
|
||||
for _, rl := range r.customRateLimits {
|
||||
if strings.HasSuffix(b.Key, rl.suffix) {
|
||||
b.customRateLimit = rl
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
r.buckets[key] = b
|
||||
return b
|
||||
}
|
||||
@ -76,13 +100,28 @@ type Bucket struct {
|
||||
limit int
|
||||
reset time.Time
|
||||
global *int64
|
||||
|
||||
lastReset time.Time
|
||||
customRateLimit *customRateLimit
|
||||
}
|
||||
|
||||
// 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()
|
||||
|
||||
// Check if the bucket uses a custom ratelimiter
|
||||
if rl := b.customRateLimit; rl != nil {
|
||||
if time.Now().Sub(b.lastReset) >= rl.reset {
|
||||
b.remaining = rl.requests - 1
|
||||
b.lastReset = time.Now()
|
||||
}
|
||||
if b.remaining < 1 {
|
||||
b.reset = time.Now().Add(rl.reset)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if headers == nil {
|
||||
return nil
|
||||
}
|
||||
|
138
vendor/github.com/bwmarrin/discordgo/restapi.go
generated
vendored
138
vendor/github.com/bwmarrin/discordgo/restapi.go
generated
vendored
@ -23,6 +23,7 @@ import (
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -309,8 +310,8 @@ func (s *Session) UserUpdate(email, password, username, avatar, newPassword stri
|
||||
// If left blank, avatar will be set to null/blank
|
||||
|
||||
data := struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Email string `json:"email,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Avatar string `json:"avatar,omitempty"`
|
||||
NewPassword string `json:"new_password,omitempty"`
|
||||
@ -763,7 +764,21 @@ func (s *Session) GuildMember(guildID, userID string) (st *Member, err error) {
|
||||
// userID : The ID of a User
|
||||
func (s *Session) GuildMemberDelete(guildID, userID string) (err error) {
|
||||
|
||||
_, err = s.RequestWithBucketID("DELETE", EndpointGuildMember(guildID, userID), nil, EndpointGuildMember(guildID, ""))
|
||||
return s.GuildMemberDeleteWithReason(guildID, userID, "")
|
||||
}
|
||||
|
||||
// GuildMemberDeleteWithReason removes the given user from the given guild.
|
||||
// guildID : The ID of a Guild.
|
||||
// userID : The ID of a User
|
||||
// reason : The reason for the kick
|
||||
func (s *Session) GuildMemberDeleteWithReason(guildID, userID, reason string) (err error) {
|
||||
|
||||
uri := EndpointGuildMember(guildID, userID)
|
||||
if reason != "" {
|
||||
uri += "?reason=" + url.QueryEscape(reason)
|
||||
}
|
||||
|
||||
_, err = s.RequestWithBucketID("DELETE", uri, nil, EndpointGuildMember(guildID, ""))
|
||||
return
|
||||
}
|
||||
|
||||
@ -1316,6 +1331,8 @@ func (s *Session) ChannelMessageSend(channelID string, content string) (*Message
|
||||
})
|
||||
}
|
||||
|
||||
var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
|
||||
|
||||
// ChannelMessageSendComplex sends a message to the given channel.
|
||||
// channelID : The ID of a Channel.
|
||||
// data : The message struct to send.
|
||||
@ -1326,48 +1343,62 @@ func (s *Session) ChannelMessageSendComplex(channelID string, data *MessageSend)
|
||||
|
||||
endpoint := EndpointChannelMessages(channelID)
|
||||
|
||||
var response []byte
|
||||
// TODO: Remove this when compatibility is not required.
|
||||
files := data.Files
|
||||
if data.File != nil {
|
||||
if files == nil {
|
||||
files = []*File{data.File}
|
||||
} else {
|
||||
err = fmt.Errorf("cannot specify both File and Files")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var response []byte
|
||||
if len(files) > 0 {
|
||||
body := &bytes.Buffer{}
|
||||
bodywriter := multipart.NewWriter(body)
|
||||
|
||||
// What's a better way of doing this? Reflect? Generator? I'm open to suggestions
|
||||
|
||||
if data.Content != "" {
|
||||
if err = bodywriter.WriteField("content", data.Content); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if data.Embed != nil {
|
||||
var embed []byte
|
||||
embed, err = json.Marshal(data.Embed)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = bodywriter.WriteField("embed", string(embed))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if data.Tts {
|
||||
if err = bodywriter.WriteField("tts", "true"); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var writer io.Writer
|
||||
writer, err = bodywriter.CreateFormFile("file", data.File.Name)
|
||||
var payload []byte
|
||||
payload, err = json.Marshal(data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = io.Copy(writer, data.File.Reader)
|
||||
var p io.Writer
|
||||
|
||||
h := make(textproto.MIMEHeader)
|
||||
h.Set("Content-Disposition", `form-data; name="payload_json"`)
|
||||
h.Set("Content-Type", "application/json")
|
||||
|
||||
p, err = bodywriter.CreatePart(h)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = p.Write(payload); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for i, file := range files {
|
||||
h := make(textproto.MIMEHeader)
|
||||
h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="file%d"; filename="%s"`, i, quoteEscaper.Replace(file.Name)))
|
||||
contentType := file.ContentType
|
||||
if contentType == "" {
|
||||
contentType = "application/octet-stream"
|
||||
}
|
||||
h.Set("Content-Type", contentType)
|
||||
|
||||
p, err = bodywriter.CreatePart(h)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = io.Copy(p, file.Reader); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = bodywriter.Close()
|
||||
if err != nil {
|
||||
return
|
||||
@ -1685,6 +1716,28 @@ func (s *Session) Gateway() (gateway string, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// GatewayBot returns the websocket Gateway address and the recommended number of shards
|
||||
func (s *Session) GatewayBot() (st *GatewayBotResponse, err error) {
|
||||
|
||||
response, err := s.RequestWithBucketID("GET", EndpointGatewayBot, nil, EndpointGatewayBot)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = unmarshal(response, &st)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure the gateway always has a trailing slash.
|
||||
// MacOS will fail to connect if we add query params without a trailing slash on the base domain.
|
||||
if !strings.HasSuffix(st.URL, "/") {
|
||||
st.URL += "/"
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Functions specific to Webhooks
|
||||
|
||||
// WebhookCreate returns a new Webhook.
|
||||
@ -1810,14 +1863,9 @@ func (s *Session) WebhookEditWithToken(webhookID, token, name, avatar string) (s
|
||||
|
||||
// WebhookDelete deletes a webhook for a given ID
|
||||
// webhookID: The ID of a webhook.
|
||||
func (s *Session) WebhookDelete(webhookID string) (st *Webhook, err error) {
|
||||
func (s *Session) WebhookDelete(webhookID string) (err error) {
|
||||
|
||||
body, err := s.RequestWithBucketID("DELETE", EndpointWebhook(webhookID), nil, EndpointWebhooks)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = unmarshal(body, &st)
|
||||
_, err = s.RequestWithBucketID("DELETE", EndpointWebhook(webhookID), nil, EndpointWebhooks)
|
||||
|
||||
return
|
||||
}
|
||||
@ -1875,6 +1923,16 @@ func (s *Session) MessageReactionRemove(channelID, messageID, emojiID, userID st
|
||||
return err
|
||||
}
|
||||
|
||||
// MessageReactionsRemoveAll deletes all reactions from a message
|
||||
// channelID : The channel ID
|
||||
// messageID : The message ID.
|
||||
func (s *Session) MessageReactionsRemoveAll(channelID, messageID string) error {
|
||||
|
||||
_, err := s.RequestWithBucketID("DELETE", EndpointMessageReactionsAll(channelID, messageID), nil, EndpointMessageReactionsAll(channelID, messageID))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// MessageReactions gets all the users reactions for a specific emoji.
|
||||
// channelID : The channel ID.
|
||||
// messageID : The message ID.
|
||||
|
72
vendor/github.com/bwmarrin/discordgo/state.go
generated
vendored
72
vendor/github.com/bwmarrin/discordgo/state.go
generated
vendored
@ -42,6 +42,7 @@ type State struct {
|
||||
|
||||
guildMap map[string]*Guild
|
||||
channelMap map[string]*Channel
|
||||
memberMap map[string]map[string]*Member
|
||||
}
|
||||
|
||||
// NewState creates an empty state.
|
||||
@ -59,9 +60,18 @@ func NewState() *State {
|
||||
TrackPresences: true,
|
||||
guildMap: make(map[string]*Guild),
|
||||
channelMap: make(map[string]*Channel),
|
||||
memberMap: make(map[string]map[string]*Member),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *State) createMemberMap(guild *Guild) {
|
||||
members := make(map[string]*Member)
|
||||
for _, m := range guild.Members {
|
||||
members[m.User.ID] = m
|
||||
}
|
||||
s.memberMap[guild.ID] = members
|
||||
}
|
||||
|
||||
// GuildAdd adds a guild to the current world state, or
|
||||
// updates it if it already exists.
|
||||
func (s *State) GuildAdd(guild *Guild) error {
|
||||
@ -77,6 +87,14 @@ func (s *State) GuildAdd(guild *Guild) error {
|
||||
s.channelMap[c.ID] = c
|
||||
}
|
||||
|
||||
// If this guild contains a new member slice, we must regenerate the member map so the pointers stay valid
|
||||
if guild.Members != nil {
|
||||
s.createMemberMap(guild)
|
||||
} else if _, ok := s.memberMap[guild.ID]; !ok {
|
||||
// Even if we have no new member slice, we still initialize the member map for this guild if it doesn't exist
|
||||
s.memberMap[guild.ID] = make(map[string]*Member)
|
||||
}
|
||||
|
||||
if g, ok := s.guildMap[guild.ID]; ok {
|
||||
// We are about to replace `g` in the state with `guild`, but first we need to
|
||||
// make sure we preserve any fields that the `guild` doesn't contain from `g`.
|
||||
@ -271,14 +289,19 @@ func (s *State) MemberAdd(member *Member) error {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
for i, m := range guild.Members {
|
||||
if m.User.ID == member.User.ID {
|
||||
guild.Members[i] = member
|
||||
return nil
|
||||
}
|
||||
members, ok := s.memberMap[member.GuildID]
|
||||
if !ok {
|
||||
return ErrStateNotFound
|
||||
}
|
||||
|
||||
m, ok := members[member.User.ID]
|
||||
if !ok {
|
||||
members[member.User.ID] = member
|
||||
guild.Members = append(guild.Members, member)
|
||||
} else {
|
||||
*m = *member // Update the actual data, which will also update the member pointer in the slice
|
||||
}
|
||||
|
||||
guild.Members = append(guild.Members, member)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -296,6 +319,17 @@ func (s *State) MemberRemove(member *Member) error {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
members, ok := s.memberMap[member.GuildID]
|
||||
if !ok {
|
||||
return ErrStateNotFound
|
||||
}
|
||||
|
||||
_, ok = members[member.User.ID]
|
||||
if !ok {
|
||||
return ErrStateNotFound
|
||||
}
|
||||
delete(members, member.User.ID)
|
||||
|
||||
for i, m := range guild.Members {
|
||||
if m.User.ID == member.User.ID {
|
||||
guild.Members = append(guild.Members[:i], guild.Members[i+1:]...)
|
||||
@ -312,18 +346,17 @@ func (s *State) Member(guildID, userID string) (*Member, error) {
|
||||
return nil, ErrNilState
|
||||
}
|
||||
|
||||
guild, err := s.Guild(guildID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
|
||||
for _, m := range guild.Members {
|
||||
if m.User.ID == userID {
|
||||
return m, nil
|
||||
}
|
||||
members, ok := s.memberMap[guildID]
|
||||
if !ok {
|
||||
return nil, ErrStateNotFound
|
||||
}
|
||||
|
||||
m, ok := members[userID]
|
||||
if ok {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
return nil, ErrStateNotFound
|
||||
@ -427,7 +460,7 @@ func (s *State) ChannelAdd(channel *Channel) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if channel.IsPrivate {
|
||||
if channel.Type == ChannelTypeDM || channel.Type == ChannelTypeGroupDM {
|
||||
s.PrivateChannels = append(s.PrivateChannels, channel)
|
||||
} else {
|
||||
guild, ok := s.guildMap[channel.GuildID]
|
||||
@ -454,7 +487,7 @@ func (s *State) ChannelRemove(channel *Channel) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if channel.IsPrivate {
|
||||
if channel.Type == ChannelTypeDM || channel.Type == ChannelTypeGroupDM {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
@ -735,6 +768,7 @@ func (s *State) onReady(se *Session, r *Ready) (err error) {
|
||||
|
||||
for _, g := range s.Guilds {
|
||||
s.guildMap[g.ID] = g
|
||||
s.createMemberMap(g)
|
||||
|
||||
for _, c := range g.Channels {
|
||||
s.channelMap[c.ID] = c
|
||||
@ -748,8 +782,8 @@ func (s *State) onReady(se *Session, r *Ready) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// onInterface handles all events related to states.
|
||||
func (s *State) onInterface(se *Session, i interface{}) (err error) {
|
||||
// OnInterface handles all events related to states.
|
||||
func (s *State) OnInterface(se *Session, i interface{}) (err error) {
|
||||
if s == nil {
|
||||
return ErrNilState
|
||||
}
|
||||
|
87
vendor/github.com/bwmarrin/discordgo/structs.go
generated
vendored
87
vendor/github.com/bwmarrin/discordgo/structs.go
generated
vendored
@ -50,6 +50,10 @@ type Session struct {
|
||||
// active guilds and the members of the guilds.
|
||||
StateEnabled bool
|
||||
|
||||
// Whether or not to call event handlers synchronously.
|
||||
// e.g false = launch event handlers in their own goroutines.
|
||||
SyncEvents bool
|
||||
|
||||
// Exposed but should not be modified by User.
|
||||
|
||||
// Whether the Data Websocket is ready
|
||||
@ -78,6 +82,9 @@ type Session struct {
|
||||
// The http client used for REST requests
|
||||
Client *http.Client
|
||||
|
||||
// Stores the last HeartbeatAck that was recieved (in UTC)
|
||||
LastHeartbeatAck time.Time
|
||||
|
||||
// Event handlers
|
||||
handlersMu sync.RWMutex
|
||||
handlers map[string][]*eventHandlerInstance
|
||||
@ -141,18 +148,30 @@ type Invite struct {
|
||||
Temporary bool `json:"temporary"`
|
||||
}
|
||||
|
||||
// ChannelType is the type of a Channel
|
||||
type ChannelType int
|
||||
|
||||
// Block contains known ChannelType values
|
||||
const (
|
||||
ChannelTypeGuildText ChannelType = iota
|
||||
ChannelTypeDM
|
||||
ChannelTypeGuildVoice
|
||||
ChannelTypeGroupDM
|
||||
ChannelTypeGuildCategory
|
||||
)
|
||||
|
||||
// A Channel holds all data related to an individual Discord channel.
|
||||
type Channel struct {
|
||||
ID string `json:"id"`
|
||||
GuildID string `json:"guild_id"`
|
||||
Name string `json:"name"`
|
||||
Topic string `json:"topic"`
|
||||
Type string `json:"type"`
|
||||
Type ChannelType `json:"type"`
|
||||
LastMessageID string `json:"last_message_id"`
|
||||
NSFW bool `json:"nsfw"`
|
||||
Position int `json:"position"`
|
||||
Bitrate int `json:"bitrate"`
|
||||
IsPrivate bool `json:"is_private"`
|
||||
Recipient *User `json:"recipient"`
|
||||
Recipients []*User `json:"recipient"`
|
||||
Messages []*Message `json:"-"`
|
||||
PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites"`
|
||||
}
|
||||
@ -292,13 +311,14 @@ type Presence struct {
|
||||
Game *Game `json:"game"`
|
||||
Nick string `json:"nick"`
|
||||
Roles []string `json:"roles"`
|
||||
Since *int `json:"since"`
|
||||
}
|
||||
|
||||
// A Game struct holds the name of the "playing .." game for a user
|
||||
type Game struct {
|
||||
Name string `json:"name"`
|
||||
Type int `json:"type"`
|
||||
URL string `json:"url"`
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals json to Game struct
|
||||
@ -509,6 +529,12 @@ type MessageReaction struct {
|
||||
ChannelID string `json:"channel_id"`
|
||||
}
|
||||
|
||||
// GatewayBotResponse stores the data for the gateway/bot response
|
||||
type GatewayBotResponse struct {
|
||||
URL string `json:"url"`
|
||||
Shards int `json:"shards"`
|
||||
}
|
||||
|
||||
// Constants for the different bit offsets of text channel permissions
|
||||
const (
|
||||
PermissionReadMessages = 1 << (iota + 10)
|
||||
@ -579,3 +605,56 @@ const (
|
||||
PermissionManageServer |
|
||||
PermissionAdministrator
|
||||
)
|
||||
|
||||
// Block contains Discord JSON Error Response codes
|
||||
const (
|
||||
ErrCodeUnknownAccount = 10001
|
||||
ErrCodeUnknownApplication = 10002
|
||||
ErrCodeUnknownChannel = 10003
|
||||
ErrCodeUnknownGuild = 10004
|
||||
ErrCodeUnknownIntegration = 10005
|
||||
ErrCodeUnknownInvite = 10006
|
||||
ErrCodeUnknownMember = 10007
|
||||
ErrCodeUnknownMessage = 10008
|
||||
ErrCodeUnknownOverwrite = 10009
|
||||
ErrCodeUnknownProvider = 10010
|
||||
ErrCodeUnknownRole = 10011
|
||||
ErrCodeUnknownToken = 10012
|
||||
ErrCodeUnknownUser = 10013
|
||||
ErrCodeUnknownEmoji = 10014
|
||||
|
||||
ErrCodeBotsCannotUseEndpoint = 20001
|
||||
ErrCodeOnlyBotsCanUseEndpoint = 20002
|
||||
|
||||
ErrCodeMaximumGuildsReached = 30001
|
||||
ErrCodeMaximumFriendsReached = 30002
|
||||
ErrCodeMaximumPinsReached = 30003
|
||||
ErrCodeMaximumGuildRolesReached = 30005
|
||||
ErrCodeTooManyReactions = 30010
|
||||
|
||||
ErrCodeUnauthorized = 40001
|
||||
|
||||
ErrCodeMissingAccess = 50001
|
||||
ErrCodeInvalidAccountType = 50002
|
||||
ErrCodeCannotExecuteActionOnDMChannel = 50003
|
||||
ErrCodeEmbedCisabled = 50004
|
||||
ErrCodeCannotEditFromAnotherUser = 50005
|
||||
ErrCodeCannotSendEmptyMessage = 50006
|
||||
ErrCodeCannotSendMessagesToThisUser = 50007
|
||||
ErrCodeCannotSendMessagesInVoiceChannel = 50008
|
||||
ErrCodeChannelVerificationLevelTooHigh = 50009
|
||||
ErrCodeOAuth2ApplicationDoesNotHaveBot = 50010
|
||||
ErrCodeOAuth2ApplicationLimitReached = 50011
|
||||
ErrCodeInvalidOAuthState = 50012
|
||||
ErrCodeMissingPermissions = 50013
|
||||
ErrCodeInvalidAuthenticationToken = 50014
|
||||
ErrCodeNoteTooLong = 50015
|
||||
ErrCodeTooFewOrTooManyMessagesToDelete = 50016
|
||||
ErrCodeCanOnlyPinMessageToOriginatingChannel = 50019
|
||||
ErrCodeCannotExecuteActionOnSystemMessage = 50021
|
||||
ErrCodeMessageProvidedTooOldForBulkDelete = 50034
|
||||
ErrCodeInvalidFormBody = 50035
|
||||
ErrCodeInviteAcceptedToGuildApplicationsBotNotIn = 50036
|
||||
|
||||
ErrCodeReactionBlocked = 90001
|
||||
)
|
||||
|
18
vendor/github.com/bwmarrin/discordgo/user.go
generated
vendored
18
vendor/github.com/bwmarrin/discordgo/user.go
generated
vendored
@ -1,6 +1,9 @@
|
||||
package discordgo
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A User stores all data for an individual Discord user.
|
||||
type User struct {
|
||||
@ -24,3 +27,16 @@ func (u *User) String() string {
|
||||
func (u *User) Mention() string {
|
||||
return fmt.Sprintf("<@%s>", u.ID)
|
||||
}
|
||||
|
||||
// AvatarURL returns a URL to the user's avatar.
|
||||
// size: The size of the user's avatar as a power of two
|
||||
func (u *User) AvatarURL(size string) string {
|
||||
var URL string
|
||||
if strings.HasPrefix(u.Avatar, "a_") {
|
||||
URL = EndpointUserAvatarAnimated(u.ID, u.Avatar)
|
||||
} else {
|
||||
URL = EndpointUserAvatar(u.ID, u.Avatar)
|
||||
}
|
||||
|
||||
return URL + "?size=" + size
|
||||
}
|
||||
|
13
vendor/github.com/bwmarrin/discordgo/voice.go
generated
vendored
13
vendor/github.com/bwmarrin/discordgo/voice.go
generated
vendored
@ -796,7 +796,7 @@ func (v *VoiceConnection) opusReceiver(udpConn *net.UDPConn, close <-chan struct
|
||||
}
|
||||
|
||||
// For now, skip anything except audio.
|
||||
if rlen < 12 || recvbuf[0] != 0x80 {
|
||||
if rlen < 12 || (recvbuf[0] != 0x80 && recvbuf[0] != 0x90) {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -810,8 +810,17 @@ func (v *VoiceConnection) opusReceiver(udpConn *net.UDPConn, close <-chan struct
|
||||
copy(nonce[:], recvbuf[0:12])
|
||||
p.Opus, _ = secretbox.Open(nil, recvbuf[12:rlen], &nonce, &v.op4.SecretKey)
|
||||
|
||||
if len(p.Opus) > 8 && recvbuf[0] == 0x90 {
|
||||
// Extension bit is set, first 8 bytes is the extended header
|
||||
p.Opus = p.Opus[8:]
|
||||
}
|
||||
|
||||
if c != nil {
|
||||
c <- &p
|
||||
select {
|
||||
case c <- &p:
|
||||
case <-close:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
58
vendor/github.com/bwmarrin/discordgo/wsapi.go
generated
vendored
58
vendor/github.com/bwmarrin/discordgo/wsapi.go
generated
vendored
@ -15,7 +15,6 @@ import (
|
||||
"compress/zlib"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"runtime"
|
||||
@ -87,7 +86,7 @@ func (s *Session) Open() (err error) {
|
||||
}
|
||||
|
||||
// Add the version and encoding to the URL
|
||||
s.gateway = fmt.Sprintf("%s?v=5&encoding=json", s.gateway)
|
||||
s.gateway = s.gateway + "?v=" + APIVersion + "&encoding=json"
|
||||
}
|
||||
|
||||
header := http.Header{}
|
||||
@ -131,6 +130,7 @@ func (s *Session) Open() (err error) {
|
||||
// lock.
|
||||
s.listening = make(chan interface{})
|
||||
go s.listen(s.wsConn, s.listening)
|
||||
s.LastHeartbeatAck = time.Now().UTC()
|
||||
|
||||
s.Unlock()
|
||||
|
||||
@ -199,10 +199,13 @@ type helloOp struct {
|
||||
Trace []string `json:"_trace"`
|
||||
}
|
||||
|
||||
// FailedHeartbeatAcks is the Number of heartbeat intervals to wait until forcing a connection restart.
|
||||
const FailedHeartbeatAcks time.Duration = 5 * time.Millisecond
|
||||
|
||||
// heartbeat sends regular heartbeats to Discord so it knows the client
|
||||
// is still connected. If you do not send these heartbeats Discord will
|
||||
// disconnect the websocket connection after a few seconds.
|
||||
func (s *Session) heartbeat(wsConn *websocket.Conn, listening <-chan interface{}, i time.Duration) {
|
||||
func (s *Session) heartbeat(wsConn *websocket.Conn, listening <-chan interface{}, heartbeatIntervalMsec time.Duration) {
|
||||
|
||||
s.log(LogInformational, "called")
|
||||
|
||||
@ -211,20 +214,26 @@ func (s *Session) heartbeat(wsConn *websocket.Conn, listening <-chan interface{}
|
||||
}
|
||||
|
||||
var err error
|
||||
ticker := time.NewTicker(i * time.Millisecond)
|
||||
ticker := time.NewTicker(heartbeatIntervalMsec * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
s.RLock()
|
||||
last := s.LastHeartbeatAck
|
||||
s.RUnlock()
|
||||
sequence := atomic.LoadInt64(s.sequence)
|
||||
s.log(LogInformational, "sending gateway websocket heartbeat seq %d", sequence)
|
||||
s.wsMutex.Lock()
|
||||
err = wsConn.WriteJSON(heartbeatOp{1, sequence})
|
||||
s.wsMutex.Unlock()
|
||||
if err != nil {
|
||||
s.log(LogError, "error sending heartbeat to gateway %s, %s", s.gateway, err)
|
||||
s.Lock()
|
||||
s.DataReady = false
|
||||
s.Unlock()
|
||||
if err != nil || time.Now().UTC().Sub(last) > (heartbeatIntervalMsec*FailedHeartbeatAcks) {
|
||||
if err != nil {
|
||||
s.log(LogError, "error sending heartbeat to gateway %s, %s", s.gateway, err)
|
||||
} else {
|
||||
s.log(LogError, "haven't gotten a heartbeat ACK in %v, triggering a reconnection", time.Now().UTC().Sub(last))
|
||||
}
|
||||
s.Close()
|
||||
s.reconnect()
|
||||
return
|
||||
}
|
||||
s.Lock()
|
||||
@ -241,8 +250,10 @@ func (s *Session) heartbeat(wsConn *websocket.Conn, listening <-chan interface{}
|
||||
}
|
||||
|
||||
type updateStatusData struct {
|
||||
IdleSince *int `json:"idle_since"`
|
||||
Game *Game `json:"game"`
|
||||
IdleSince *int `json:"since"`
|
||||
Game *Game `json:"game"`
|
||||
AFK bool `json:"afk"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type updateStatusOp struct {
|
||||
@ -265,7 +276,10 @@ func (s *Session) UpdateStreamingStatus(idle int, game string, url string) (err
|
||||
return ErrWSNotFound
|
||||
}
|
||||
|
||||
var usd updateStatusData
|
||||
usd := updateStatusData{
|
||||
Status: "online",
|
||||
}
|
||||
|
||||
if idle > 0 {
|
||||
usd.IdleSince = &idle
|
||||
}
|
||||
@ -398,7 +412,10 @@ func (s *Session) onEvent(messageType int, message []byte) {
|
||||
// Reconnect
|
||||
// Must immediately disconnect from gateway and reconnect to new gateway.
|
||||
if e.Operation == 7 {
|
||||
// TODO
|
||||
s.log(LogInformational, "Closing and reconnecting in response to Op7")
|
||||
s.Close()
|
||||
s.reconnect()
|
||||
return
|
||||
}
|
||||
|
||||
// Invalid Session
|
||||
@ -426,6 +443,14 @@ func (s *Session) onEvent(messageType int, message []byte) {
|
||||
return
|
||||
}
|
||||
|
||||
if e.Operation == 11 {
|
||||
s.Lock()
|
||||
s.LastHeartbeatAck = time.Now().UTC()
|
||||
s.Unlock()
|
||||
s.log(LogInformational, "got heartbeat ACK")
|
||||
return
|
||||
}
|
||||
|
||||
// Do not try to Dispatch a non-Dispatch Message
|
||||
if e.Operation != 0 {
|
||||
// But we probably should be doing something with them.
|
||||
@ -688,6 +713,13 @@ func (s *Session) reconnect() {
|
||||
return
|
||||
}
|
||||
|
||||
// Certain race conditions can call reconnect() twice. If this happens, we
|
||||
// just break out of the reconnect loop
|
||||
if err == ErrWSAlreadyOpen {
|
||||
s.log(LogInformational, "Websocket already exists, no need to reconnect")
|
||||
return
|
||||
}
|
||||
|
||||
s.log(LogError, "error reconnecting to gateway, %s", err)
|
||||
|
||||
<-time.After(wait * time.Second)
|
||||
|
197
vendor/github.com/go-telegram-bot-api/telegram-bot-api/bot.go
generated
vendored
197
vendor/github.com/go-telegram-bot-api/telegram-bot-api/bot.go
generated
vendored
@ -191,7 +191,11 @@ func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldna
|
||||
}
|
||||
|
||||
var apiResp APIResponse
|
||||
json.Unmarshal(bytes, &apiResp)
|
||||
|
||||
err = json.Unmarshal(bytes, &apiResp)
|
||||
if err != nil {
|
||||
return APIResponse{}, err
|
||||
}
|
||||
|
||||
if !apiResp.Ok {
|
||||
return APIResponse{}, errors.New(apiResp.Description)
|
||||
@ -438,14 +442,7 @@ func (bot *BotAPI) SetWebhook(config WebhookConfig) (APIResponse, error) {
|
||||
return APIResponse{}, err
|
||||
}
|
||||
|
||||
var apiResp APIResponse
|
||||
json.Unmarshal(resp.Result, &apiResp)
|
||||
|
||||
if bot.Debug {
|
||||
log.Printf("setWebhook resp: %+v\n", apiResp)
|
||||
}
|
||||
|
||||
return apiResp, nil
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetWebhookInfo allows you to fetch information about a webhook and if
|
||||
@ -550,7 +547,7 @@ func (bot *BotAPI) AnswerCallbackQuery(config CallbackConfig) (APIResponse, erro
|
||||
// KickChatMember kicks a user from a chat. Note that this only will work
|
||||
// in supergroups, and requires the bot to be an admin. Also note they
|
||||
// will be unable to rejoin until they are unbanned.
|
||||
func (bot *BotAPI) KickChatMember(config ChatMemberConfig) (APIResponse, error) {
|
||||
func (bot *BotAPI) KickChatMember(config KickChatMemberConfig) (APIResponse, error) {
|
||||
v := url.Values{}
|
||||
|
||||
if config.SuperGroupUsername == "" {
|
||||
@ -560,6 +557,10 @@ func (bot *BotAPI) KickChatMember(config ChatMemberConfig) (APIResponse, error)
|
||||
}
|
||||
v.Add("user_id", strconv.Itoa(config.UserID))
|
||||
|
||||
if config.UntilDate != 0 {
|
||||
v.Add("until_date", strconv.FormatInt(config.UntilDate, 10))
|
||||
}
|
||||
|
||||
bot.debugLog("kickChatMember", v, nil)
|
||||
|
||||
return bot.MakeRequest("kickChatMember", v)
|
||||
@ -677,14 +678,16 @@ func (bot *BotAPI) GetChatMember(config ChatConfigWithUser) (ChatMember, error)
|
||||
}
|
||||
|
||||
// UnbanChatMember unbans a user from a chat. Note that this only will work
|
||||
// in supergroups, and requires the bot to be an admin.
|
||||
// in supergroups and channels, and requires the bot to be an admin.
|
||||
func (bot *BotAPI) UnbanChatMember(config ChatMemberConfig) (APIResponse, error) {
|
||||
v := url.Values{}
|
||||
|
||||
if config.SuperGroupUsername == "" {
|
||||
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
|
||||
} else {
|
||||
if config.SuperGroupUsername != "" {
|
||||
v.Add("chat_id", config.SuperGroupUsername)
|
||||
} else if config.ChannelUsername != "" {
|
||||
v.Add("chat_id", config.ChannelUsername)
|
||||
} else {
|
||||
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
|
||||
}
|
||||
v.Add("user_id", strconv.Itoa(config.UserID))
|
||||
|
||||
@ -693,6 +696,82 @@ func (bot *BotAPI) UnbanChatMember(config ChatMemberConfig) (APIResponse, error)
|
||||
return bot.MakeRequest("unbanChatMember", v)
|
||||
}
|
||||
|
||||
// RestrictChatMember to restrict a user in a supergroup. The bot must be an
|
||||
//administrator in the supergroup for this to work and must have the
|
||||
//appropriate admin rights. Pass True for all boolean parameters to lift
|
||||
//restrictions from a user. Returns True on success.
|
||||
func (bot *BotAPI) RestrictChatMember(config RestrictChatMemberConfig) (APIResponse, error) {
|
||||
v := url.Values{}
|
||||
|
||||
if config.SuperGroupUsername != "" {
|
||||
v.Add("chat_id", config.SuperGroupUsername)
|
||||
} else if config.ChannelUsername != "" {
|
||||
v.Add("chat_id", config.ChannelUsername)
|
||||
} else {
|
||||
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
|
||||
}
|
||||
v.Add("user_id", strconv.Itoa(config.UserID))
|
||||
|
||||
if &config.CanSendMessages != nil {
|
||||
v.Add("can_send_messages", strconv.FormatBool(*config.CanSendMessages))
|
||||
}
|
||||
if &config.CanSendMediaMessages != nil {
|
||||
v.Add("can_send_media_messages", strconv.FormatBool(*config.CanSendMediaMessages))
|
||||
}
|
||||
if &config.CanSendOtherMessages != nil {
|
||||
v.Add("can_send_other_messages", strconv.FormatBool(*config.CanSendOtherMessages))
|
||||
}
|
||||
if &config.CanAddWebPagePreviews != nil {
|
||||
v.Add("can_add_web_page_previews", strconv.FormatBool(*config.CanAddWebPagePreviews))
|
||||
}
|
||||
|
||||
bot.debugLog("restrictChatMember", v, nil)
|
||||
|
||||
return bot.MakeRequest("restrictChatMember", v)
|
||||
}
|
||||
|
||||
func (bot *BotAPI) PromoteChatMember(config PromoteChatMemberConfig) (APIResponse, error) {
|
||||
v := url.Values{}
|
||||
|
||||
if config.SuperGroupUsername != "" {
|
||||
v.Add("chat_id", config.SuperGroupUsername)
|
||||
} else if config.ChannelUsername != "" {
|
||||
v.Add("chat_id", config.ChannelUsername)
|
||||
} else {
|
||||
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
|
||||
}
|
||||
v.Add("user_id", strconv.Itoa(config.UserID))
|
||||
|
||||
if &config.CanChangeInfo != nil {
|
||||
v.Add("can_change_info", strconv.FormatBool(*config.CanChangeInfo))
|
||||
}
|
||||
if &config.CanPostMessages != nil {
|
||||
v.Add("can_post_messages", strconv.FormatBool(*config.CanPostMessages))
|
||||
}
|
||||
if &config.CanEditMessages != nil {
|
||||
v.Add("can_edit_messages", strconv.FormatBool(*config.CanEditMessages))
|
||||
}
|
||||
if &config.CanDeleteMessages != nil {
|
||||
v.Add("can_delete_messages", strconv.FormatBool(*config.CanDeleteMessages))
|
||||
}
|
||||
if &config.CanInviteUsers != nil {
|
||||
v.Add("can_invite_users", strconv.FormatBool(*config.CanInviteUsers))
|
||||
}
|
||||
if &config.CanRestrictMembers != nil {
|
||||
v.Add("can_restrict_members", strconv.FormatBool(*config.CanRestrictMembers))
|
||||
}
|
||||
if &config.CanPinMessages != nil {
|
||||
v.Add("can_pin_messages", strconv.FormatBool(*config.CanPinMessages))
|
||||
}
|
||||
if &config.CanPromoteMembers != nil {
|
||||
v.Add("can_promote_members", strconv.FormatBool(*config.CanPromoteMembers))
|
||||
}
|
||||
|
||||
bot.debugLog("promoteChatMember", v, nil)
|
||||
|
||||
return bot.MakeRequest("promoteChatMember", v)
|
||||
}
|
||||
|
||||
// GetGameHighScores allows you to get the high scores for a game.
|
||||
func (bot *BotAPI) GetGameHighScores(config GetGameHighScoresConfig) ([]GameHighScore, error) {
|
||||
v, _ := config.values()
|
||||
@ -707,3 +786,93 @@ func (bot *BotAPI) GetGameHighScores(config GetGameHighScoresConfig) ([]GameHigh
|
||||
|
||||
return highScores, err
|
||||
}
|
||||
|
||||
// AnswerShippingQuery allows you to reply to Update with shipping_query parameter.
|
||||
func (bot *BotAPI) AnswerShippingQuery(config ShippingConfig) (APIResponse, error) {
|
||||
v := url.Values{}
|
||||
|
||||
v.Add("shipping_query_id", config.ShippingQueryID)
|
||||
v.Add("ok", strconv.FormatBool(config.OK))
|
||||
if config.OK == true {
|
||||
data, err := json.Marshal(config.ShippingOptions)
|
||||
if err != nil {
|
||||
return APIResponse{}, err
|
||||
}
|
||||
v.Add("shipping_options", string(data))
|
||||
} else {
|
||||
v.Add("error_message", config.ErrorMessage)
|
||||
}
|
||||
|
||||
bot.debugLog("answerShippingQuery", v, nil)
|
||||
|
||||
return bot.MakeRequest("answerShippingQuery", v)
|
||||
}
|
||||
|
||||
// AnswerPreCheckoutQuery allows you to reply to Update with pre_checkout_query.
|
||||
func (bot *BotAPI) AnswerPreCheckoutQuery(config PreCheckoutConfig) (APIResponse, error) {
|
||||
v := url.Values{}
|
||||
|
||||
v.Add("pre_checkout_query_id", config.PreCheckoutQueryID)
|
||||
v.Add("ok", strconv.FormatBool(config.OK))
|
||||
if config.OK != true {
|
||||
v.Add("error", config.ErrorMessage)
|
||||
}
|
||||
|
||||
bot.debugLog("answerPreCheckoutQuery", v, nil)
|
||||
|
||||
return bot.MakeRequest("answerPreCheckoutQuery", v)
|
||||
}
|
||||
|
||||
// DeleteMessage deletes a message in a chat
|
||||
func (bot *BotAPI) DeleteMessage(config DeleteMessageConfig) (APIResponse, error) {
|
||||
v, err := config.values()
|
||||
if err != nil {
|
||||
return APIResponse{}, err
|
||||
}
|
||||
|
||||
bot.debugLog(config.method(), v, nil)
|
||||
|
||||
return bot.MakeRequest(config.method(), v)
|
||||
}
|
||||
|
||||
// GetInviteLink get InviteLink for a chat
|
||||
func (bot *BotAPI) GetInviteLink(config ChatConfig) (string, error) {
|
||||
v := url.Values{}
|
||||
|
||||
if config.SuperGroupUsername == "" {
|
||||
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
|
||||
} else {
|
||||
v.Add("chat_id", config.SuperGroupUsername)
|
||||
}
|
||||
|
||||
resp, err := bot.MakeRequest("exportChatInviteLink", v)
|
||||
|
||||
var inviteLink string
|
||||
err = json.Unmarshal(resp.Result, &inviteLink)
|
||||
|
||||
return inviteLink, err
|
||||
}
|
||||
|
||||
// Pin message in supergroup
|
||||
func (bot *BotAPI) PinChatMessage(config PinChatMessageConfig) (APIResponse, error) {
|
||||
v, err := config.values()
|
||||
if err != nil {
|
||||
return APIResponse{}, err
|
||||
}
|
||||
|
||||
bot.debugLog(config.method(), v, nil)
|
||||
|
||||
return bot.MakeRequest(config.method(), v)
|
||||
}
|
||||
|
||||
// Unpin message in supergroup
|
||||
func (bot *BotAPI) UnpinChatMessage(config UnpinChatMessageConfig) (APIResponse, error) {
|
||||
v, err := config.values()
|
||||
if err != nil {
|
||||
return APIResponse{}, err
|
||||
}
|
||||
|
||||
bot.debugLog(config.method(), v, nil)
|
||||
|
||||
return bot.MakeRequest(config.method(), v)
|
||||
}
|
243
vendor/github.com/go-telegram-bot-api/telegram-bot-api/configs.go
generated
vendored
243
vendor/github.com/go-telegram-bot-api/telegram-bot-api/configs.go
generated
vendored
@ -349,6 +349,7 @@ func (config AudioConfig) method() string {
|
||||
// DocumentConfig contains information about a SendDocument request.
|
||||
type DocumentConfig struct {
|
||||
BaseFile
|
||||
Caption string
|
||||
}
|
||||
|
||||
// values returns a url.Values representation of DocumentConfig.
|
||||
@ -359,6 +360,9 @@ func (config DocumentConfig) values() (url.Values, error) {
|
||||
}
|
||||
|
||||
v.Add(config.name(), config.FileID)
|
||||
if config.Caption != "" {
|
||||
v.Add("caption", config.Caption)
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
@ -367,6 +371,10 @@ func (config DocumentConfig) values() (url.Values, error) {
|
||||
func (config DocumentConfig) params() (map[string]string, error) {
|
||||
params, _ := config.BaseFile.params()
|
||||
|
||||
if config.Caption != "" {
|
||||
params["caption"] = config.Caption
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
@ -443,6 +451,10 @@ func (config VideoConfig) values() (url.Values, error) {
|
||||
func (config VideoConfig) params() (map[string]string, error) {
|
||||
params, _ := config.BaseFile.params()
|
||||
|
||||
if config.Caption != "" {
|
||||
params["caption"] = config.Caption
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
@ -456,6 +468,57 @@ func (config VideoConfig) method() string {
|
||||
return "sendVideo"
|
||||
}
|
||||
|
||||
// VideoNoteConfig contains information about a SendVideoNote request.
|
||||
type VideoNoteConfig struct {
|
||||
BaseFile
|
||||
Duration int
|
||||
Length int
|
||||
}
|
||||
|
||||
// values returns a url.Values representation of VideoNoteConfig.
|
||||
func (config VideoNoteConfig) values() (url.Values, error) {
|
||||
v, err := config.BaseChat.values()
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
|
||||
v.Add(config.name(), config.FileID)
|
||||
if config.Duration != 0 {
|
||||
v.Add("duration", strconv.Itoa(config.Duration))
|
||||
}
|
||||
|
||||
// Telegram API seems to have a bug, if no length is provided or it is 0, it will send an error response
|
||||
if config.Length != 0 {
|
||||
v.Add("length", strconv.Itoa(config.Length))
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// params returns a map[string]string representation of VideoNoteConfig.
|
||||
func (config VideoNoteConfig) params() (map[string]string, error) {
|
||||
params, _ := config.BaseFile.params()
|
||||
|
||||
if config.Length != 0 {
|
||||
params["length"] = strconv.Itoa(config.Length)
|
||||
}
|
||||
if config.Duration != 0 {
|
||||
params["duration"] = strconv.Itoa(config.Duration)
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// name returns the field name for the VideoNote.
|
||||
func (config VideoNoteConfig) name() string {
|
||||
return "video_note"
|
||||
}
|
||||
|
||||
// method returns Telegram API method name for sending VideoNote.
|
||||
func (config VideoNoteConfig) method() string {
|
||||
return "sendVideoNote"
|
||||
}
|
||||
|
||||
// VoiceConfig contains information about a SendVoice request.
|
||||
type VoiceConfig struct {
|
||||
BaseFile
|
||||
@ -474,6 +537,9 @@ func (config VoiceConfig) values() (url.Values, error) {
|
||||
if config.Duration != 0 {
|
||||
v.Add("duration", strconv.Itoa(config.Duration))
|
||||
}
|
||||
if config.Caption != "" {
|
||||
v.Add("caption", config.Caption)
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
@ -485,6 +551,9 @@ func (config VoiceConfig) params() (map[string]string, error) {
|
||||
if config.Duration != 0 {
|
||||
params["duration"] = strconv.Itoa(config.Duration)
|
||||
}
|
||||
if config.Caption != "" {
|
||||
params["caption"] = config.Caption
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
@ -814,9 +883,39 @@ type CallbackConfig struct {
|
||||
type ChatMemberConfig struct {
|
||||
ChatID int64
|
||||
SuperGroupUsername string
|
||||
ChannelUsername string
|
||||
UserID int
|
||||
}
|
||||
|
||||
// KickChatMemberConfig contains extra fields to kick user
|
||||
type KickChatMemberConfig struct {
|
||||
ChatMemberConfig
|
||||
UntilDate int64
|
||||
}
|
||||
|
||||
// RestrictChatMemberConfig contains fields to restrict members of chat
|
||||
type RestrictChatMemberConfig struct {
|
||||
ChatMemberConfig
|
||||
UntilDate int64
|
||||
CanSendMessages *bool
|
||||
CanSendMediaMessages *bool
|
||||
CanSendOtherMessages *bool
|
||||
CanAddWebPagePreviews *bool
|
||||
}
|
||||
|
||||
// PromoteChatMemberConfig contains fields to promote members of chat
|
||||
type PromoteChatMemberConfig struct {
|
||||
ChatMemberConfig
|
||||
CanChangeInfo *bool
|
||||
CanPostMessages *bool
|
||||
CanEditMessages *bool
|
||||
CanDeleteMessages *bool
|
||||
CanInviteUsers *bool
|
||||
CanRestrictMembers *bool
|
||||
CanPinMessages *bool
|
||||
CanPromoteMembers *bool
|
||||
}
|
||||
|
||||
// ChatConfig contains information about getting information on a chat.
|
||||
type ChatConfig struct {
|
||||
ChatID int64
|
||||
@ -830,3 +929,147 @@ type ChatConfigWithUser struct {
|
||||
SuperGroupUsername string
|
||||
UserID int
|
||||
}
|
||||
|
||||
// InvoiceConfig contains information for sendInvoice request.
|
||||
type InvoiceConfig struct {
|
||||
BaseChat
|
||||
Title string // required
|
||||
Description string // required
|
||||
Payload string // required
|
||||
ProviderToken string // required
|
||||
StartParameter string // required
|
||||
Currency string // required
|
||||
Prices *[]LabeledPrice // required
|
||||
PhotoURL string
|
||||
PhotoSize int
|
||||
PhotoWidth int
|
||||
PhotoHeight int
|
||||
NeedName bool
|
||||
NeedPhoneNumber bool
|
||||
NeedEmail bool
|
||||
NeedShippingAddress bool
|
||||
IsFlexible bool
|
||||
}
|
||||
|
||||
func (config InvoiceConfig) values() (url.Values, error) {
|
||||
v, err := config.BaseChat.values()
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
v.Add("title", config.Title)
|
||||
v.Add("description", config.Description)
|
||||
v.Add("payload", config.Payload)
|
||||
v.Add("provider_token", config.ProviderToken)
|
||||
v.Add("start_parameter", config.StartParameter)
|
||||
v.Add("currency", config.Currency)
|
||||
data, err := json.Marshal(config.Prices)
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
v.Add("prices", string(data))
|
||||
if config.PhotoURL != "" {
|
||||
v.Add("photo_url", config.PhotoURL)
|
||||
}
|
||||
if config.PhotoSize != 0 {
|
||||
v.Add("photo_size", strconv.Itoa(config.PhotoSize))
|
||||
}
|
||||
if config.PhotoWidth != 0 {
|
||||
v.Add("photo_width", strconv.Itoa(config.PhotoWidth))
|
||||
}
|
||||
if config.PhotoHeight != 0 {
|
||||
v.Add("photo_height", strconv.Itoa(config.PhotoHeight))
|
||||
}
|
||||
if config.NeedName != false {
|
||||
v.Add("need_name", strconv.FormatBool(config.NeedName))
|
||||
}
|
||||
if config.NeedPhoneNumber != false {
|
||||
v.Add("need_phone_number", strconv.FormatBool(config.NeedPhoneNumber))
|
||||
}
|
||||
if config.NeedEmail != false {
|
||||
v.Add("need_email", strconv.FormatBool(config.NeedEmail))
|
||||
}
|
||||
if config.NeedShippingAddress != false {
|
||||
v.Add("need_shipping_address", strconv.FormatBool(config.NeedShippingAddress))
|
||||
}
|
||||
if config.IsFlexible != false {
|
||||
v.Add("is_flexible", strconv.FormatBool(config.IsFlexible))
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (config InvoiceConfig) method() string {
|
||||
return "sendInvoice"
|
||||
}
|
||||
|
||||
// ShippingConfig contains information for answerShippingQuery request.
|
||||
type ShippingConfig struct {
|
||||
ShippingQueryID string // required
|
||||
OK bool // required
|
||||
ShippingOptions *[]ShippingOption
|
||||
ErrorMessage string
|
||||
}
|
||||
|
||||
// PreCheckoutConfig conatins information for answerPreCheckoutQuery request.
|
||||
type PreCheckoutConfig struct {
|
||||
PreCheckoutQueryID string // required
|
||||
OK bool // required
|
||||
ErrorMessage string
|
||||
}
|
||||
|
||||
// DeleteMessageConfig contains information of a message in a chat to delete.
|
||||
type DeleteMessageConfig struct {
|
||||
ChatID int64
|
||||
MessageID int
|
||||
}
|
||||
|
||||
func (config DeleteMessageConfig) method() string {
|
||||
return "deleteMessage"
|
||||
}
|
||||
|
||||
func (config DeleteMessageConfig) values() (url.Values, error) {
|
||||
v := url.Values{}
|
||||
|
||||
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
|
||||
v.Add("message_id", strconv.Itoa(config.MessageID))
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// PinChatMessageConfig contains information of a message in a chat to pin.
|
||||
type PinChatMessageConfig struct {
|
||||
ChatID int64
|
||||
MessageID int
|
||||
DisableNotification bool
|
||||
}
|
||||
|
||||
func (config PinChatMessageConfig) method() string {
|
||||
return "pinChatMessage"
|
||||
}
|
||||
|
||||
func (config PinChatMessageConfig) values() (url.Values, error) {
|
||||
v := url.Values{}
|
||||
|
||||
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
|
||||
v.Add("message_id", strconv.Itoa(config.MessageID))
|
||||
v.Add("disable_notification", strconv.FormatBool(config.DisableNotification))
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// UnpinChatMessageConfig contains information of chat to unpin.
|
||||
type UnpinChatMessageConfig struct {
|
||||
ChatID int64
|
||||
}
|
||||
|
||||
func (config UnpinChatMessageConfig) method() string {
|
||||
return "unpinChatMessage"
|
||||
}
|
||||
|
||||
func (config UnpinChatMessageConfig) values() (url.Values, error) {
|
||||
v := url.Values{}
|
||||
|
||||
v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
|
||||
|
||||
return v, nil
|
||||
}
|
44
vendor/github.com/go-telegram-bot-api/telegram-bot-api/helpers.go
generated
vendored
44
vendor/github.com/go-telegram-bot-api/telegram-bot-api/helpers.go
generated
vendored
@ -194,6 +194,37 @@ func NewVideoShare(chatID int64, fileID string) VideoConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// NewVideoNoteUpload creates a new video note uploader.
|
||||
//
|
||||
// chatID is where to send it, file is a string path to the file,
|
||||
// FileReader, or FileBytes.
|
||||
func NewVideoNoteUpload(chatID int64, length int, file interface{}) VideoNoteConfig {
|
||||
return VideoNoteConfig{
|
||||
BaseFile: BaseFile{
|
||||
BaseChat: BaseChat{ChatID: chatID},
|
||||
File: file,
|
||||
UseExisting: false,
|
||||
},
|
||||
Length: length,
|
||||
}
|
||||
}
|
||||
|
||||
// NewVideoNoteShare shares an existing video.
|
||||
// You may use this to reshare an existing video without reuploading it.
|
||||
//
|
||||
// chatID is where to send it, fileID is the ID of the video
|
||||
// already uploaded.
|
||||
func NewVideoNoteShare(chatID int64, length int, fileID string) VideoNoteConfig {
|
||||
return VideoNoteConfig{
|
||||
BaseFile: BaseFile{
|
||||
BaseChat: BaseChat{ChatID: chatID},
|
||||
FileID: fileID,
|
||||
UseExisting: true,
|
||||
},
|
||||
Length: length,
|
||||
}
|
||||
}
|
||||
|
||||
// NewVoiceUpload creates a new voice uploader.
|
||||
//
|
||||
// chatID is where to send it, file is a string path to the file,
|
||||
@ -609,3 +640,16 @@ func NewCallbackWithAlert(id, text string) CallbackConfig {
|
||||
ShowAlert: true,
|
||||
}
|
||||
}
|
||||
|
||||
// NewInvoice created a new Invoice request to the user.
|
||||
func NewInvoice(chatID int64, title, description, payload, providerToken, startParameter, currency string, prices *[]LabeledPrice) InvoiceConfig {
|
||||
return InvoiceConfig{
|
||||
BaseChat: BaseChat{ChatID: chatID},
|
||||
Title: title,
|
||||
Description: description,
|
||||
Payload: payload,
|
||||
ProviderToken: providerToken,
|
||||
StartParameter: startParameter,
|
||||
Currency: currency,
|
||||
Prices: prices}
|
||||
}
|
||||
|
205
vendor/github.com/go-telegram-bot-api/telegram-bot-api/types.go
generated
vendored
205
vendor/github.com/go-telegram-bot-api/telegram-bot-api/types.go
generated
vendored
@ -35,6 +35,8 @@ type Update struct {
|
||||
InlineQuery *InlineQuery `json:"inline_query"`
|
||||
ChosenInlineResult *ChosenInlineResult `json:"chosen_inline_result"`
|
||||
CallbackQuery *CallbackQuery `json:"callback_query"`
|
||||
ShippingQuery *ShippingQuery `json:"shipping_query"`
|
||||
PreCheckoutQuery *PreCheckoutQuery `json:"pre_checkout_query"`
|
||||
}
|
||||
|
||||
// UpdatesChannel is the channel for getting updates.
|
||||
@ -49,10 +51,11 @@ func (ch UpdatesChannel) Clear() {
|
||||
|
||||
// User is a user on Telegram.
|
||||
type User struct {
|
||||
ID int `json:"id"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"` // optional
|
||||
UserName string `json:"username"` // optional
|
||||
ID int `json:"id"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"` // optional
|
||||
UserName string `json:"username"` // optional
|
||||
LanguageCode string `json:"language_code"` // optional
|
||||
}
|
||||
|
||||
// String displays a simple text version of a user.
|
||||
@ -78,15 +81,24 @@ type GroupChat struct {
|
||||
Title string `json:"title"`
|
||||
}
|
||||
|
||||
// ChatPhoto represents a chat photo.
|
||||
type ChatPhoto struct {
|
||||
SmallFileID string `json:"small_file_id"`
|
||||
BigFileID string `json:"big_file_id"`
|
||||
}
|
||||
|
||||
// Chat contains information about the place a message was sent.
|
||||
type Chat struct {
|
||||
ID int64 `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Title string `json:"title"` // optional
|
||||
UserName string `json:"username"` // optional
|
||||
FirstName string `json:"first_name"` // optional
|
||||
LastName string `json:"last_name"` // optional
|
||||
AllMembersAreAdmins bool `json:"all_members_are_administrators"` // optional
|
||||
ID int64 `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Title string `json:"title"` // optional
|
||||
UserName string `json:"username"` // optional
|
||||
FirstName string `json:"first_name"` // optional
|
||||
LastName string `json:"last_name"` // optional
|
||||
AllMembersAreAdmins bool `json:"all_members_are_administrators"` // optional
|
||||
Photo *ChatPhoto `json:"photo"`
|
||||
Description string `json:"description,omitempty"` // optional
|
||||
InviteLink string `json:"invite_link,omitempty"` // optional
|
||||
}
|
||||
|
||||
// IsPrivate returns if the Chat is a private conversation.
|
||||
@ -117,40 +129,43 @@ func (c Chat) ChatConfig() ChatConfig {
|
||||
// Message is returned by almost every request, and contains data about
|
||||
// almost anything.
|
||||
type Message struct {
|
||||
MessageID int `json:"message_id"`
|
||||
From *User `json:"from"` // optional
|
||||
Date int `json:"date"`
|
||||
Chat *Chat `json:"chat"`
|
||||
ForwardFrom *User `json:"forward_from"` // optional
|
||||
ForwardFromChat *Chat `json:"forward_from_chat"` // optional
|
||||
ForwardFromMessageID int `json:"forward_from_message_id"` // optional
|
||||
ForwardDate int `json:"forward_date"` // optional
|
||||
ReplyToMessage *Message `json:"reply_to_message"` // optional
|
||||
EditDate int `json:"edit_date"` // optional
|
||||
Text string `json:"text"` // optional
|
||||
Entities *[]MessageEntity `json:"entities"` // optional
|
||||
Audio *Audio `json:"audio"` // optional
|
||||
Document *Document `json:"document"` // optional
|
||||
Game *Game `json:"game"` // optional
|
||||
Photo *[]PhotoSize `json:"photo"` // optional
|
||||
Sticker *Sticker `json:"sticker"` // optional
|
||||
Video *Video `json:"video"` // optional
|
||||
Voice *Voice `json:"voice"` // optional
|
||||
Caption string `json:"caption"` // optional
|
||||
Contact *Contact `json:"contact"` // optional
|
||||
Location *Location `json:"location"` // optional
|
||||
Venue *Venue `json:"venue"` // optional
|
||||
NewChatMember *User `json:"new_chat_member"` // optional
|
||||
LeftChatMember *User `json:"left_chat_member"` // optional
|
||||
NewChatTitle string `json:"new_chat_title"` // optional
|
||||
NewChatPhoto *[]PhotoSize `json:"new_chat_photo"` // optional
|
||||
DeleteChatPhoto bool `json:"delete_chat_photo"` // optional
|
||||
GroupChatCreated bool `json:"group_chat_created"` // optional
|
||||
SuperGroupChatCreated bool `json:"supergroup_chat_created"` // optional
|
||||
ChannelChatCreated bool `json:"channel_chat_created"` // optional
|
||||
MigrateToChatID int64 `json:"migrate_to_chat_id"` // optional
|
||||
MigrateFromChatID int64 `json:"migrate_from_chat_id"` // optional
|
||||
PinnedMessage *Message `json:"pinned_message"` // optional
|
||||
MessageID int `json:"message_id"`
|
||||
From *User `json:"from"` // optional
|
||||
Date int `json:"date"`
|
||||
Chat *Chat `json:"chat"`
|
||||
ForwardFrom *User `json:"forward_from"` // optional
|
||||
ForwardFromChat *Chat `json:"forward_from_chat"` // optional
|
||||
ForwardFromMessageID int `json:"forward_from_message_id"` // optional
|
||||
ForwardDate int `json:"forward_date"` // optional
|
||||
ReplyToMessage *Message `json:"reply_to_message"` // optional
|
||||
EditDate int `json:"edit_date"` // optional
|
||||
Text string `json:"text"` // optional
|
||||
Entities *[]MessageEntity `json:"entities"` // optional
|
||||
Audio *Audio `json:"audio"` // optional
|
||||
Document *Document `json:"document"` // optional
|
||||
Game *Game `json:"game"` // optional
|
||||
Photo *[]PhotoSize `json:"photo"` // optional
|
||||
Sticker *Sticker `json:"sticker"` // optional
|
||||
Video *Video `json:"video"` // optional
|
||||
VideoNote *VideoNote `json:"video_note"` // optional
|
||||
Voice *Voice `json:"voice"` // optional
|
||||
Caption string `json:"caption"` // optional
|
||||
Contact *Contact `json:"contact"` // optional
|
||||
Location *Location `json:"location"` // optional
|
||||
Venue *Venue `json:"venue"` // optional
|
||||
NewChatMembers *[]User `json:"new_chat_members"` // optional
|
||||
LeftChatMember *User `json:"left_chat_member"` // optional
|
||||
NewChatTitle string `json:"new_chat_title"` // optional
|
||||
NewChatPhoto *[]PhotoSize `json:"new_chat_photo"` // optional
|
||||
DeleteChatPhoto bool `json:"delete_chat_photo"` // optional
|
||||
GroupChatCreated bool `json:"group_chat_created"` // optional
|
||||
SuperGroupChatCreated bool `json:"supergroup_chat_created"` // optional
|
||||
ChannelChatCreated bool `json:"channel_chat_created"` // optional
|
||||
MigrateToChatID int64 `json:"migrate_to_chat_id"` // optional
|
||||
MigrateFromChatID int64 `json:"migrate_from_chat_id"` // optional
|
||||
PinnedMessage *Message `json:"pinned_message"` // optional
|
||||
Invoice *Invoice `json:"invoice"` // optional
|
||||
SuccessfulPayment *SuccessfulPayment `json:"successful_payment"` // optional
|
||||
}
|
||||
|
||||
// Time converts the message timestamp into a Time.
|
||||
@ -263,6 +278,15 @@ type Video struct {
|
||||
FileSize int `json:"file_size"` // optional
|
||||
}
|
||||
|
||||
// VideoNote contains information about a video.
|
||||
type VideoNote struct {
|
||||
FileID string `json:"file_id"`
|
||||
Length int `json:"length"`
|
||||
Duration int `json:"duration"`
|
||||
Thumbnail *PhotoSize `json:"thumb"` // optional
|
||||
FileSize int `json:"file_size"` // optional
|
||||
}
|
||||
|
||||
// Voice contains information about a voice.
|
||||
type Voice struct {
|
||||
FileID string `json:"file_id"`
|
||||
@ -361,6 +385,7 @@ type InlineKeyboardButton struct {
|
||||
SwitchInlineQuery *string `json:"switch_inline_query,omitempty"` // optional
|
||||
SwitchInlineQueryCurrentChat *string `json:"switch_inline_query_current_chat,omitempty"` // optional
|
||||
CallbackGame *CallbackGame `json:"callback_game,omitempty"` // optional
|
||||
Pay bool `json:"pay,omitempty"` // optional
|
||||
}
|
||||
|
||||
// CallbackQuery is data sent when a keyboard button with callback data
|
||||
@ -384,8 +409,22 @@ type ForceReply struct {
|
||||
|
||||
// ChatMember is information about a member in a chat.
|
||||
type ChatMember struct {
|
||||
User *User `json:"user"`
|
||||
Status string `json:"status"`
|
||||
User *User `json:"user"`
|
||||
Status string `json:"status"`
|
||||
UntilDate int64 `json:"until_date,omitempty"` // optional
|
||||
CanBeEdited bool `json:"can_be_edited,omitempty"` // optional
|
||||
CanChangeInfo bool `json:"can_change_info,omitempty"` // optional
|
||||
CanPostMessages bool `json:"can_post_messages,omitempty"` // optional
|
||||
CanEditMessages bool `json:"can_edit_messages,omitempty"` // optional
|
||||
CanDeleteMessages bool `json:"can_delete_messages,omitempty"` // optional
|
||||
CanInviteUsers bool `json:"can_invite_users,omitempty"` // optional
|
||||
CanRestrictMembers bool `json:"can_restrict_members,omitempty"` // optional
|
||||
CanPinMessages bool `json:"can_pin_messages,omitempty"` // optional
|
||||
CanPromoteMembers bool `json:"can_promote_members,omitempty"` // optional
|
||||
CanSendMessages bool `json:"can_send_messages,omitempty"` // optional
|
||||
CanSendMediaMessages bool `json:"can_send_media_messages,omitempty"` // optional
|
||||
CanSendOtherMessages bool `json:"can_send_other_messages,omitempty"` // optional
|
||||
CanAddWebPagePreviews bool `json:"can_add_web_page_previews,omitempty"` // optional
|
||||
}
|
||||
|
||||
// IsCreator returns if the ChatMember was the creator of the chat.
|
||||
@ -493,6 +532,7 @@ type InlineQueryResultGIF struct {
|
||||
URL string `json:"gif_url"` // required
|
||||
Width int `json:"gif_width"`
|
||||
Height int `json:"gif_height"`
|
||||
Duration int `json:"gif_duration"`
|
||||
ThumbURL string `json:"thumb_url"`
|
||||
Title string `json:"title"`
|
||||
Caption string `json:"caption"`
|
||||
@ -507,6 +547,7 @@ type InlineQueryResultMPEG4GIF struct {
|
||||
URL string `json:"mpeg4_url"` // required
|
||||
Width int `json:"mpeg4_width"`
|
||||
Height int `json:"mpeg4_height"`
|
||||
Duration int `json:"mpeg4_duration"`
|
||||
ThumbURL string `json:"thumb_url"`
|
||||
Title string `json:"title"`
|
||||
Caption string `json:"caption"`
|
||||
@ -635,3 +676,73 @@ type InputContactMessageContent struct {
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
}
|
||||
|
||||
// Invoice contains basic information about an invoice.
|
||||
type Invoice struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
StartParameter string `json:"start_parameter"`
|
||||
Currency string `json:"currency"`
|
||||
TotalAmount int `json:"total_amount"`
|
||||
}
|
||||
|
||||
// LabeledPrice represents a portion of the price for goods or services.
|
||||
type LabeledPrice struct {
|
||||
Label string `json:"label"`
|
||||
Amount int `json:"amount"`
|
||||
}
|
||||
|
||||
// ShippingAddress represents a shipping address.
|
||||
type ShippingAddress struct {
|
||||
CountryCode string `json:"country_code"`
|
||||
State string `json:"state"`
|
||||
City string `json:"city"`
|
||||
StreetLine1 string `json:"street_line1"`
|
||||
StreetLine2 string `json:"street_line2"`
|
||||
PostCode string `json:"post_code"`
|
||||
}
|
||||
|
||||
// OrderInfo represents information about an order.
|
||||
type OrderInfo struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
PhoneNumber string `json:"phone_number,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
ShippingAddress *ShippingAddress `json:"shipping_address,omitempty"`
|
||||
}
|
||||
|
||||
// ShippingOption represents one shipping option.
|
||||
type ShippingOption struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Prices *[]LabeledPrice `json:"prices"`
|
||||
}
|
||||
|
||||
// SuccessfulPayment contains basic information about a successful payment.
|
||||
type SuccessfulPayment struct {
|
||||
Currency string `json:"currency"`
|
||||
TotalAmount int `json:"total_amount"`
|
||||
InvoicePayload string `json:"invoice_payload"`
|
||||
ShippingOptionID string `json:"shipping_option_id,omitempty"`
|
||||
OrderInfo *OrderInfo `json:"order_info,omitempty"`
|
||||
TelegramPaymentChargeID string `json:"telegram_payment_charge_id"`
|
||||
ProviderPaymentChargeID string `json:"provider_payment_charge_id"`
|
||||
}
|
||||
|
||||
// ShippingQuery contains information about an incoming shipping query.
|
||||
type ShippingQuery struct {
|
||||
ID string `json:"id"`
|
||||
From *User `json:"from"`
|
||||
InvoicePayload string `json:"invoice_payload"`
|
||||
ShippingAddress *ShippingAddress `json:"shipping_address"`
|
||||
}
|
||||
|
||||
// PreCheckoutQuery contains information about an incoming pre-checkout query.
|
||||
type PreCheckoutQuery struct {
|
||||
ID string `json:"id"`
|
||||
From *User `json:"from"`
|
||||
Currency string `json:"currency"`
|
||||
TotalAmount int `json:"total_amount"`
|
||||
InvoicePayload string `json:"invoice_payload"`
|
||||
ShippingOptionID string `json:"shipping_option_id,omitempty"`
|
||||
OrderInfo *OrderInfo `json:"order_info,omitempty"`
|
||||
}
|
||||
|
2
vendor/github.com/nlopes/slack/websocket_managed_conn.go
generated
vendored
2
vendor/github.com/nlopes/slack/websocket_managed_conn.go
generated
vendored
@ -302,6 +302,8 @@ func (rtm *RTM) handleRawEvent(rawEvent json.RawMessage) {
|
||||
rtm.IncomingEvents <- RTMEvent{"hello", &HelloEvent{}}
|
||||
case "pong":
|
||||
rtm.handlePong(rawEvent)
|
||||
case "desktop_notification":
|
||||
rtm.Debugln("Received desktop notification, ignoring")
|
||||
default:
|
||||
rtm.handleEvent(event.Type, rawEvent)
|
||||
}
|
||||
|
6
vendor/manifest
vendored
6
vendor/manifest
vendored
@ -61,7 +61,7 @@
|
||||
"importpath": "github.com/bwmarrin/discordgo",
|
||||
"repository": "https://github.com/bwmarrin/discordgo",
|
||||
"vcs": "git",
|
||||
"revision": "d420e28024ad527390b43aa7f64e029083e11989",
|
||||
"revision": "2fda7ce223a66a5b70b66987c22c3c94d022ee66",
|
||||
"branch": "master",
|
||||
"notests": true
|
||||
},
|
||||
@ -136,7 +136,7 @@
|
||||
"importpath": "github.com/go-telegram-bot-api/telegram-bot-api",
|
||||
"repository": "https://github.com/go-telegram-bot-api/telegram-bot-api",
|
||||
"vcs": "git",
|
||||
"revision": "89bbb1edff353a7c6d10ef10cfd2675056ad8a58",
|
||||
"revision": "9dda67c714e5e2cba837b28a0172cca2ed54f078",
|
||||
"branch": "master",
|
||||
"notests": true
|
||||
},
|
||||
@ -429,7 +429,7 @@
|
||||
"importpath": "github.com/nlopes/slack",
|
||||
"repository": "https://github.com/nlopes/slack",
|
||||
"vcs": "git",
|
||||
"revision": "ca8436d76f805ec1e682eaae2de3c3a9bc894b0f",
|
||||
"revision": "5cde21b8b96a43fc3435a1f514123d14fd7eabdc",
|
||||
"branch": "master",
|
||||
"notests": true
|
||||
},
|
||||
|
Reference in New Issue
Block a user