2016-09-19 21:05:13 +02:00
package bdiscord
import (
"github.com/42wim/matterbridge/bridge/config"
log "github.com/Sirupsen/logrus"
"github.com/bwmarrin/discordgo"
2017-04-15 16:23:34 +02:00
"regexp"
2016-10-26 01:01:36 +02:00
"strings"
2017-02-13 18:52:52 +01:00
"sync"
2016-09-19 21:05:13 +02:00
)
type bdiscord struct {
2017-02-13 18:52:52 +01:00
c * discordgo . Session
Config * config . Protocol
Remote chan config . Message
Account string
Channels [ ] * discordgo . Channel
Nick string
UseChannelID bool
userMemberMap map [ string ] * discordgo . Member
guildID string
2017-06-26 20:07:27 +02:00
webhookID string
webhookToken string
2017-02-13 18:52:52 +01:00
sync . RWMutex
2016-09-19 21:05:13 +02:00
}
var flog * log . Entry
var protocol = "discord"
func init ( ) {
flog = log . WithFields ( log . Fields { "module" : protocol } )
}
2016-11-13 23:06:37 +01:00
func New ( cfg config . Protocol , account string , c chan config . Message ) * bdiscord {
2016-09-19 21:05:13 +02:00
b := & bdiscord { }
2016-11-04 00:05:15 +01:00
b . Config = & cfg
2016-09-19 21:05:13 +02:00
b . Remote = c
2016-11-13 23:06:37 +01:00
b . Account = account
2017-02-13 18:52:52 +01:00
b . userMemberMap = make ( map [ string ] * discordgo . Member )
2017-06-26 20:07:27 +02:00
if b . Config . WebhookURL != "" {
flog . Debug ( "Configuring Discord Incoming Webhook" )
webhookURLSplit := strings . Split ( b . Config . WebhookURL , "/" )
b . webhookToken = webhookURLSplit [ len ( webhookURLSplit ) - 1 ]
b . webhookID = webhookURLSplit [ len ( webhookURLSplit ) - 2 ]
}
2016-09-19 21:05:13 +02:00
return b
}
func ( b * bdiscord ) Connect ( ) error {
var err error
2016-09-20 00:15:30 +02:00
flog . Info ( "Connecting" )
2017-06-24 19:36:10 +02:00
if b . Config . WebhookURL == "" {
flog . Info ( "Connecting using token" )
} else {
flog . Info ( "Connecting using webhookurl (for posting) and token" )
}
2016-11-14 16:30:43 +01:00
if ! strings . HasPrefix ( b . Config . Token , "Bot " ) {
b . Config . Token = "Bot " + b . Config . Token
}
2016-09-19 21:05:13 +02:00
b . c , err = discordgo . New ( b . Config . Token )
if err != nil {
flog . Debugf ( "%#v" , err )
return err
}
flog . Info ( "Connection succeeded" )
b . c . AddHandler ( b . messageCreate )
2017-02-13 18:52:52 +01:00
b . c . AddHandler ( b . memberUpdate )
2017-04-15 19:00:15 +02:00
b . c . AddHandler ( b . messageUpdate )
2016-09-19 21:05:13 +02:00
err = b . c . Open ( )
if err != nil {
flog . Debugf ( "%#v" , err )
return err
}
guilds , err := b . c . UserGuilds ( )
if err != nil {
flog . Debugf ( "%#v" , err )
return err
}
userinfo , err := b . c . User ( "@me" )
if err != nil {
flog . Debugf ( "%#v" , err )
return err
}
b . Nick = userinfo . Username
for _ , guild := range guilds {
2016-10-23 19:50:12 +02:00
if guild . Name == b . Config . Server {
2016-09-19 21:05:13 +02:00
b . Channels , err = b . c . GuildChannels ( guild . ID )
2017-02-13 18:52:52 +01:00
b . guildID = guild . ID
2016-09-19 21:05:13 +02:00
if err != nil {
flog . Debugf ( "%#v" , err )
return err
}
}
}
return nil
}
2017-02-14 21:12:02 +01:00
func ( b * bdiscord ) Disconnect ( ) error {
return nil
}
2016-09-19 21:05:13 +02:00
func ( b * bdiscord ) JoinChannel ( channel string ) error {
2016-10-26 01:01:36 +02:00
idcheck := strings . Split ( channel , "ID:" )
if len ( idcheck ) > 1 {
b . UseChannelID = true
}
2016-09-19 21:05:13 +02:00
return nil
}
func ( b * bdiscord ) Send ( msg config . Message ) error {
2016-09-20 00:15:30 +02:00
flog . Debugf ( "Receiving %#v" , msg )
2016-09-19 21:05:13 +02:00
channelID := b . getChannelID ( msg . Channel )
if channelID == "" {
flog . Errorf ( "Could not find channelID for %v" , msg . Channel )
return nil
}
2017-06-26 23:01:35 +02:00
if b . Config . WebhookURL == "" {
2017-06-24 19:36:10 +02:00
flog . Debugf ( "Broadcasting using token (API)" )
2017-06-26 20:07:27 +02:00
b . c . ChannelMessageSend ( channelID , msg . Username + msg . Text )
} else {
flog . Debugf ( "Broadcasting using Webhook" )
b . c . WebhookExecute (
b . webhookID ,
b . webhookToken ,
true ,
& discordgo . WebhookParams {
2017-06-26 23:01:35 +02:00
Content : msg . Text ,
Username : msg . Username ,
2017-06-26 20:07:27 +02:00
AvatarURL : msg . Avatar ,
2017-06-26 23:01:35 +02:00
} )
2017-06-26 20:07:27 +02:00
}
2016-09-19 21:05:13 +02:00
return nil
}
2017-04-15 19:00:15 +02:00
func ( b * bdiscord ) messageUpdate ( s * discordgo . Session , m * discordgo . MessageUpdate ) {
if b . Config . EditDisable {
return
}
// only when message is actually edited
if m . Message . EditedTimestamp != "" {
flog . Debugf ( "Sending edit message" )
m . Content = m . Content + b . Config . EditSuffix
b . messageCreate ( s , ( * discordgo . MessageCreate ) ( m ) )
}
}
2016-09-19 21:05:13 +02:00
func ( b * bdiscord ) messageCreate ( s * discordgo . Session , m * discordgo . MessageCreate ) {
// not relay our own messages
if m . Author . Username == b . Nick {
return
}
2017-06-26 20:07:27 +02:00
// if using webhooks, do not relay if it's ours
if b . Config . WebhookURL != "" && m . Author . Bot && m . Author . ID == b . webhookID {
return
}
2016-10-26 00:09:22 +02:00
if len ( m . Attachments ) > 0 {
for _ , attach := range m . Attachments {
m . Content = m . Content + "\n" + attach . URL
}
}
2016-10-26 00:12:31 +02:00
if m . Content == "" {
return
}
2017-06-26 23:01:35 +02:00
flog . Debugf ( "Receiving message %#v" , m . Message )
2016-10-26 01:01:36 +02:00
channelName := b . getChannelName ( m . ChannelID )
if b . UseChannelID {
channelName = "ID:" + m . ChannelID
}
2017-02-13 18:52:52 +01:00
username := b . getNick ( m . Author )
2017-03-18 16:50:09 +01:00
if len ( m . MentionRoles ) > 0 {
m . Message . Content = b . replaceRoleMentions ( m . Message . Content )
}
2017-04-15 16:23:34 +02:00
m . Message . Content = b . stripCustomoji ( m . Message . Content )
2017-05-23 22:26:37 +02:00
m . Message . Content = b . replaceChannelMentions ( m . Message . Content )
2017-06-24 23:17:57 +02:00
text := m . ContentWithMentionsReplaced ( )
if b . Config . ShowEmbeds && m . Message . Embeds != nil {
for _ , embed := range m . Message . Embeds {
text = text + "embed: " + embed . Title + " - " + embed . Description + " - " + embed . URL + "\n"
}
}
2017-06-26 23:01:35 +02:00
flog . Debugf ( "Sending message from %s on %s to gateway" , m . Author . Username , b . Account )
2017-06-24 23:17:57 +02:00
b . Remote <- config . Message { Username : username , Text : text , Channel : channelName ,
2017-06-18 15:44:54 +02:00
Account : b . Account , Avatar : "https://cdn.discordapp.com/avatars/" + m . Author . ID + "/" + m . Author . Avatar + ".jpg" ,
UserID : m . Author . ID }
2016-09-19 21:05:13 +02:00
}
2017-02-13 18:52:52 +01:00
func ( b * bdiscord ) memberUpdate ( s * discordgo . Session , m * discordgo . GuildMemberUpdate ) {
b . Lock ( )
if _ , ok := b . userMemberMap [ m . Member . User . ID ] ; ok {
flog . Debugf ( "%s: memberupdate: user %s (nick %s) changes nick to %s" , b . Account , m . Member . User . Username , b . userMemberMap [ m . Member . User . ID ] . Nick , m . Member . Nick )
}
b . userMemberMap [ m . Member . User . ID ] = m . Member
b . Unlock ( )
}
func ( b * bdiscord ) getNick ( user * discordgo . User ) string {
var err error
b . Lock ( )
defer b . Unlock ( )
if _ , ok := b . userMemberMap [ user . ID ] ; ok {
2017-05-22 21:57:19 +02:00
if b . userMemberMap [ user . ID ] != nil {
if b . userMemberMap [ user . ID ] . Nick != "" {
// only return if nick is set
return b . userMemberMap [ user . ID ] . Nick
}
// otherwise return username
return user . Username
2017-02-13 18:52:52 +01:00
}
}
// if we didn't find nick, search for it
2017-06-15 22:29:01 +02:00
member , err := b . c . GuildMember ( b . guildID , user . ID )
2017-02-13 18:52:52 +01:00
if err != nil {
return user . Username
}
2017-06-15 22:29:01 +02:00
b . userMemberMap [ user . ID ] = member
2017-02-13 18:52:52 +01:00
// only return if nick is set
if b . userMemberMap [ user . ID ] . Nick != "" {
return b . userMemberMap [ user . ID ] . Nick
}
return user . Username
}
2016-09-19 21:05:13 +02:00
func ( b * bdiscord ) getChannelID ( name string ) string {
2016-10-26 01:01:36 +02:00
idcheck := strings . Split ( name , "ID:" )
if len ( idcheck ) > 1 {
return idcheck [ 1 ]
}
2016-09-19 21:05:13 +02:00
for _ , channel := range b . Channels {
if channel . Name == name {
return channel . ID
}
}
return ""
}
func ( b * bdiscord ) getChannelName ( id string ) string {
for _ , channel := range b . Channels {
if channel . ID == id {
return channel . Name
}
}
return ""
}
2017-03-18 16:50:09 +01:00
func ( b * bdiscord ) replaceRoleMentions ( text string ) string {
roles , err := b . c . GuildRoles ( b . guildID )
if err != nil {
flog . Debugf ( "%#v" , string ( err . ( * discordgo . RESTError ) . ResponseBody ) )
return text
}
for _ , role := range roles {
text = strings . Replace ( text , "<@&" + role . ID + ">" , "@" + role . Name , - 1 )
}
return text
}
2017-04-15 16:23:34 +02:00
2017-05-23 22:26:37 +02:00
func ( b * bdiscord ) replaceChannelMentions ( text string ) string {
var err error
re := regexp . MustCompile ( "<#[0-9]+>" )
text = re . ReplaceAllStringFunc ( text , func ( m string ) string {
channel := b . getChannelName ( m [ 2 : len ( m ) - 1 ] )
// if at first don't succeed, try again
if channel == "" {
b . Channels , err = b . c . GuildChannels ( b . guildID )
if err != nil {
return "#unknownchannel"
}
channel = b . getChannelName ( m [ 2 : len ( m ) - 1 ] )
2017-06-03 18:21:47 +02:00
return "#" + channel
2017-05-23 22:26:37 +02:00
}
2017-06-03 18:21:47 +02:00
return "#" + channel
2017-05-23 22:26:37 +02:00
} )
return text
}
2017-04-15 16:23:34 +02:00
func ( b * bdiscord ) stripCustomoji ( text string ) string {
// <:doge:302803592035958784>
re := regexp . MustCompile ( "<(:.*?:)[0-9]+>" )
return re . ReplaceAllString ( text , ` $1 ` )
}