matterbridge/bridge/discord/discord.go

307 lines
7.2 KiB
Go
Raw Normal View History

2016-09-19 12:05:13 -07:00
package bdiscord
import (
"bytes"
2018-02-23 15:05:43 -08:00
"fmt"
"strings"
"sync"
2018-02-26 15:33:21 -08:00
"github.com/42wim/matterbridge/bridge"
2016-09-19 12:05:13 -07:00
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
"github.com/bwmarrin/discordgo"
2016-09-19 12:05:13 -07:00
)
const MessageLength = 1950
2018-02-24 14:22:15 -08:00
type Bdiscord struct {
*bridge.Config
c *discordgo.Session
nick string
useChannelID bool
guildID string
webhookID string
webhookToken string
channelsMutex sync.RWMutex
channels []*discordgo.Channel
channelInfoMap map[string]*config.ChannelInfo
membersMutex sync.RWMutex
userMemberMap map[string]*discordgo.Member
nickMemberMap map[string]*discordgo.Member
2016-09-19 12:05:13 -07:00
}
func New(cfg *bridge.Config) bridge.Bridger {
b := &Bdiscord{Config: cfg}
b.userMemberMap = make(map[string]*discordgo.Member)
b.nickMemberMap = make(map[string]*discordgo.Member)
b.channelInfoMap = make(map[string]*config.ChannelInfo)
if b.GetString("WebhookURL") != "" {
2018-02-26 15:33:21 -08:00
b.Log.Debug("Configuring Discord Incoming Webhook")
b.webhookID, b.webhookToken = b.splitURL(b.GetString("WebhookURL"))
}
2016-09-19 12:05:13 -07:00
return b
}
2018-02-24 14:22:15 -08:00
func (b *Bdiscord) Connect() error {
2016-09-19 12:05:13 -07:00
var err error
var token string
2018-02-26 15:33:21 -08:00
b.Log.Info("Connecting")
if b.GetString("WebhookURL") == "" {
2018-02-26 15:33:21 -08:00
b.Log.Info("Connecting using token")
} else {
2018-02-26 15:33:21 -08:00
b.Log.Info("Connecting using webhookurl (for posting) and token")
}
if !strings.HasPrefix(b.GetString("Token"), "Bot ") {
token = "Bot " + b.GetString("Token")
}
b.c, err = discordgo.New(token)
2016-09-19 12:05:13 -07:00
if err != nil {
return err
}
2018-02-26 15:33:21 -08:00
b.Log.Info("Connection succeeded")
2016-09-19 12:05:13 -07:00
b.c.AddHandler(b.messageCreate)
b.c.AddHandler(b.memberUpdate)
b.c.AddHandler(b.messageUpdate)
b.c.AddHandler(b.messageDelete)
2016-09-19 12:05:13 -07:00
err = b.c.Open()
if err != nil {
return err
}
2017-07-16 05:39:00 -07:00
guilds, err := b.c.UserGuilds(100, "", "")
2016-09-19 12:05:13 -07:00
if err != nil {
return err
}
userinfo, err := b.c.User("@me")
if err != nil {
return err
}
serverName := strings.Replace(b.GetString("Server"), "ID:", "", -1)
b.nick = userinfo.Username
b.channelsMutex.Lock()
2016-09-19 12:05:13 -07:00
for _, guild := range guilds {
if guild.Name == serverName || guild.ID == serverName {
b.channels, err = b.c.GuildChannels(guild.ID)
b.guildID = guild.ID
2016-09-19 12:05:13 -07:00
if err != nil {
break
2016-09-19 12:05:13 -07:00
}
}
}
b.channelsMutex.Unlock()
if err != nil {
return err
}
b.channelsMutex.RLock()
for _, channel := range b.channels {
2018-03-17 14:56:58 -07:00
b.Log.Debugf("found channel %#v", channel)
}
b.channelsMutex.RUnlock()
// Obtaining guild members and initializing nickname mapping.
b.membersMutex.Lock()
defer b.membersMutex.Unlock()
members, err := b.c.GuildMembers(b.guildID, "", 1000)
if err != nil {
b.Log.Error("Error obtaining guild members", err)
return err
}
for _, member := range members {
if member == nil {
b.Log.Warnf("Skipping missing information for a user.")
continue
}
b.userMemberMap[member.User.ID] = member
b.nickMemberMap[member.User.Username] = member
if member.Nick != "" {
b.nickMemberMap[member.Nick] = member
}
}
2016-09-19 12:05:13 -07:00
return nil
}
2018-02-24 14:22:15 -08:00
func (b *Bdiscord) Disconnect() error {
return b.c.Close()
}
2018-02-24 14:22:15 -08:00
func (b *Bdiscord) JoinChannel(channel config.ChannelInfo) error {
b.channelsMutex.Lock()
defer b.channelsMutex.Unlock()
b.channelInfoMap[channel.ID] = &channel
idcheck := strings.Split(channel.Name, "ID:")
if len(idcheck) > 1 {
b.useChannelID = true
}
2016-09-19 12:05:13 -07:00
return nil
}
2018-02-24 14:22:15 -08:00
func (b *Bdiscord) Send(msg config.Message) (string, error) {
2018-02-28 13:23:29 -08:00
b.Log.Debugf("=> Receiving %#v", msg)
2018-02-23 15:05:43 -08:00
2016-09-19 12:05:13 -07:00
channelID := b.getChannelID(msg.Channel)
if channelID == "" {
2018-02-23 15:05:43 -08:00
return "", fmt.Errorf("Could not find channelID for %v", msg.Channel)
2016-09-19 12:05:13 -07:00
}
2018-02-23 15:05:43 -08:00
// Make a action /me of the message
if msg.Event == config.EventUserAction {
msg.Text = "_" + msg.Text + "_"
}
2018-02-23 15:05:43 -08:00
// use initial webhook
wID := b.webhookID
wToken := b.webhookToken
2018-02-23 15:05:43 -08:00
// check if have a channel specific webhook
b.channelsMutex.RLock()
if ci, ok := b.channelInfoMap[msg.Channel+b.Account]; ok {
if ci.Options.WebhookURL != "" {
wID, wToken = b.splitURL(ci.Options.WebhookURL)
}
}
b.channelsMutex.RUnlock()
2018-02-23 15:05:43 -08:00
// Use webhook to send the message
if wID != "" {
2018-02-24 13:38:27 -08:00
// skip events
if msg.Event != "" && msg.Event != config.EventJoinLeave && msg.Event != config.EventTopicChange {
2018-02-24 13:38:27 -08:00
return "", nil
}
2018-02-26 15:33:21 -08:00
b.Log.Debugf("Broadcasting using Webhook")
for _, f := range msg.Extra["file"] {
fi := f.(config.FileInfo)
if fi.URL != "" {
msg.Text += " " + fi.URL
}
}
// skip empty messages
if msg.Text == "" {
return "", nil
}
msg.Text = helper.ClipMessage(msg.Text, MessageLength)
msg.Text = b.replaceUserMentions(msg.Text)
// discord username must be [0..32] max
if len(msg.Username) > 32 {
msg.Username = msg.Username[0:32]
}
2018-02-23 15:05:43 -08:00
err := b.c.WebhookExecute(
wID,
wToken,
true,
&discordgo.WebhookParams{
Content: msg.Text,
Username: msg.Username,
AvatarURL: msg.Avatar,
})
return "", err
}
2018-02-26 15:33:21 -08:00
b.Log.Debugf("Broadcasting using token (API)")
2018-02-23 15:05:43 -08:00
// Delete message
if msg.Event == config.EventMsgDelete {
2018-02-23 15:05:43 -08:00
if msg.ID == "" {
return "", nil
}
2018-02-23 15:05:43 -08:00
err := b.c.ChannelMessageDelete(channelID, msg.ID)
return "", err
}
2018-02-23 15:05:43 -08:00
// Upload a file if it exists
if msg.Extra != nil {
for _, rmsg := range helper.HandleExtra(&msg, b.General) {
rmsg.Text = helper.ClipMessage(rmsg.Text, MessageLength)
if _, err := b.c.ChannelMessageSend(channelID, rmsg.Username+rmsg.Text); err != nil {
b.Log.Errorf("Could not send message %#v: %v", rmsg, err)
}
2018-02-23 15:05:43 -08:00
}
// check if we have files to upload (from slack, telegram or mattermost)
if len(msg.Extra["file"]) > 0 {
return b.handleUploadFile(&msg, channelID)
}
2018-02-23 15:05:43 -08:00
}
msg.Text = helper.ClipMessage(msg.Text, MessageLength)
msg.Text = b.replaceUserMentions(msg.Text)
2018-02-23 15:05:43 -08:00
// Edit message
if msg.ID != "" {
_, err := b.c.ChannelMessageEdit(channelID, msg.ID, msg.Username+msg.Text)
return msg.ID, err
}
// Post normal message
res, err := b.c.ChannelMessageSend(channelID, msg.Username+msg.Text)
if err != nil {
return "", err
}
return res.ID, err
2016-09-19 12:05:13 -07:00
}
// useWebhook returns true if we have a webhook defined somewhere
2018-02-24 14:22:15 -08:00
func (b *Bdiscord) useWebhook() bool {
if b.GetString("WebhookURL") != "" {
return true
}
b.channelsMutex.RLock()
defer b.channelsMutex.RUnlock()
for _, channel := range b.channelInfoMap {
if channel.Options.WebhookURL != "" {
return true
}
}
return false
}
// isWebhookID returns true if the specified id is used in a defined webhook
2018-02-24 14:22:15 -08:00
func (b *Bdiscord) isWebhookID(id string) bool {
if b.GetString("WebhookURL") != "" {
wID, _ := b.splitURL(b.GetString("WebhookURL"))
if wID == id {
return true
}
}
b.channelsMutex.RLock()
defer b.channelsMutex.RUnlock()
for _, channel := range b.channelInfoMap {
if channel.Options.WebhookURL != "" {
wID, _ := b.splitURL(channel.Options.WebhookURL)
if wID == id {
return true
}
}
}
return false
}
2018-02-23 15:05:43 -08:00
// handleUploadFile handles native upload of files
2018-02-24 14:22:15 -08:00
func (b *Bdiscord) handleUploadFile(msg *config.Message, channelID string) (string, error) {
2018-02-23 15:05:43 -08:00
var err error
for _, f := range msg.Extra["file"] {
fi := f.(config.FileInfo)
file := discordgo.File{
Name: fi.Name,
ContentType: "",
Reader: bytes.NewReader(*fi.Data),
}
m := discordgo.MessageSend{
Content: msg.Username + fi.Comment,
Files: []*discordgo.File{&file},
}
_, err = b.c.ChannelMessageSendComplex(channelID, &m)
2018-02-23 15:05:43 -08:00
if err != nil {
return "", fmt.Errorf("file upload failed: %#v", err)
}
}
return "", nil
}