2016-09-18 19:21:15 +02:00
package gateway
import (
2020-10-21 21:57:14 +02:00
"fmt"
2019-02-23 16:39:44 +01:00
"io/ioutil"
2019-01-07 00:26:11 +01:00
"os"
2018-11-13 20:51:19 +01:00
"regexp"
"strings"
"time"
2018-06-08 22:30:35 +02:00
2016-09-18 19:21:15 +02:00
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
2019-04-19 18:27:31 +02:00
"github.com/42wim/matterbridge/internal"
2020-01-09 21:52:19 +01:00
"github.com/d5/tengo/v2"
"github.com/d5/tengo/v2/stdlib"
2019-02-23 22:51:27 +01:00
lru "github.com/hashicorp/golang-lru"
2021-04-03 19:16:46 +02:00
"github.com/kyokomi/emoji/v2"
2023-03-01 07:50:17 +01:00
"github.com/philippgille/gokv"
2018-12-26 15:16:09 +01:00
"github.com/sirupsen/logrus"
2016-09-18 19:21:15 +02:00
)
type Gateway struct {
2018-11-13 23:30:56 +01:00
config . Config
2017-07-25 20:11:52 +02:00
Router * Router
MyConfig * config . Gateway
Bridges map [ string ] * bridge . Bridge
Channels map [ string ] * config . ChannelInfo
ChannelOptions map [ string ] config . ChannelOptions
Message chan config . Message
Name string
2017-08-28 00:33:17 +02:00
Messages * lru . Cache
2023-03-01 07:50:17 +01:00
MessageStore gokv . Store
CanonicalStore gokv . Store
2019-02-23 22:51:27 +01:00
logger * logrus . Entry
2017-08-27 22:59:37 +02:00
}
2017-08-28 00:33:17 +02:00
type BrMsgID struct {
2023-03-01 07:50:17 +01:00
Protocol string
DestName string
2018-01-20 21:58:59 +01:00
ChannelID string
2023-03-01 07:50:17 +01:00
ID string
2016-09-18 19:21:15 +02:00
}
2019-02-23 22:51:27 +01:00
const apiProtocol = "api"
2018-02-21 00:20:25 +01:00
2019-02-23 22:51:27 +01:00
// New creates a new Gateway object associated with the specified router and
// following the given configuration.
2023-03-12 00:36:17 +01:00
func New ( rootLogger * logrus . Logger , cfg * config . Gateway , r * Router ) ( * Gateway , error ) {
2019-02-23 22:51:27 +01:00
logger := rootLogger . WithFields ( logrus . Fields { "prefix" : "gateway" } )
2018-11-08 22:20:03 +01:00
2017-08-28 00:33:17 +02:00
cache , _ := lru . New ( 5000 )
2019-02-23 22:51:27 +01:00
gw := & Gateway {
Channels : make ( map [ string ] * config . ChannelInfo ) ,
Message : r . Message ,
Router : r ,
Bridges : make ( map [ string ] * bridge . Bridge ) ,
Config : r . Config ,
Messages : cache ,
logger : logger ,
}
if err := gw . AddConfig ( cfg ) ; err != nil {
logger . Errorf ( "Failed to add configuration to gateway: %#v" , err )
2018-12-12 23:57:17 +01:00
}
2023-03-01 07:50:17 +01:00
persistentMessageStorePath , usePersistent := gw . Config . GetString ( "PersistentMessageStorePath" )
if usePersistent {
rootPath := fmt . Sprintf ( "%s/%s" , persistentMessageStorePath , gw . Name )
2023-03-12 00:36:17 +01:00
err := os . MkdirAll ( rootPath , os . ModePerm )
if err != nil {
return nil , err
}
MessageStore , err := gw . getMessageMapStore ( fmt . Sprintf ( "%s/Messages" , rootPath ) )
if err != nil {
return nil , err
}
gw . MessageStore = MessageStore
CanonicalStore , err := gw . getMessageMapStore ( fmt . Sprintf ( "%s/Canonical" , rootPath ) )
if err != nil {
return nil , err
}
gw . CanonicalStore = CanonicalStore
2023-03-01 07:50:17 +01:00
}
2023-03-12 00:36:17 +01:00
return gw , nil
2016-11-08 23:44:16 +01:00
}
2023-03-01 07:50:17 +01:00
func ( gw * Gateway ) SetMessageMap ( canonicalMsgID string , msgIDs [ ] * BrMsgID ) {
_ , usePersistent := gw . Config . GetString ( "PersistentMessageStorePath" )
if usePersistent {
gw . setDestMessagesToStore ( canonicalMsgID , msgIDs )
} else {
gw . Messages . Add ( canonicalMsgID , msgIDs )
}
}
2019-02-23 22:51:27 +01:00
// FindCanonicalMsgID returns the ID under which a message was stored in the cache.
2018-11-19 21:28:23 +01:00
func ( gw * Gateway ) FindCanonicalMsgID ( protocol string , mID string ) string {
ID := protocol + " " + mID
2023-03-01 07:50:17 +01:00
_ , usePersistent := gw . Config . GetString ( "PersistentMessageStorePath" )
if usePersistent {
return gw . getCanonicalMessageFromStore ( ID )
} else {
return gw . getCanonicalMessageFromMemCache ( ID )
}
}
func ( gw * Gateway ) getCanonicalMessageFromMemCache ( ID string ) string {
2018-11-19 21:28:23 +01:00
if gw . Messages . Contains ( ID ) {
2022-01-09 23:46:59 +01:00
return ID
2018-11-07 09:14:31 +01:00
}
// If not keyed, iterate through cache for downstream, and infer upstream.
for _ , mid := range gw . Messages . Keys ( ) {
v , _ := gw . Messages . Peek ( mid )
ids := v . ( [ ] * BrMsgID )
for _ , downstreamMsgObj := range ids {
2018-11-19 21:28:23 +01:00
if ID == downstreamMsgObj . ID {
2022-01-09 23:46:59 +01:00
return mid . ( string )
2018-11-07 09:14:31 +01:00
}
}
}
return ""
}
2019-02-23 22:51:27 +01:00
// AddBridge sets up a new bridge in the gateway object with the specified configuration.
2016-11-08 23:44:16 +01:00
func ( gw * Gateway ) AddBridge ( cfg * config . Bridge ) error {
2017-07-25 20:11:52 +02:00
br := gw . Router . getBridge ( cfg . Account )
if br == nil {
2019-09-09 23:48:00 +02:00
gw . checkConfig ( cfg )
2018-02-27 00:33:21 +01:00
br = bridge . New ( cfg )
2018-03-04 23:52:14 +01:00
br . Config = gw . Router . Config
2018-11-15 20:43:43 +01:00
br . General = & gw . BridgeValues ( ) . General
2019-02-23 22:51:27 +01:00
br . Log = gw . logger . WithFields ( logrus . Fields { "prefix" : br . Protocol } )
brconfig := & bridge . Config {
Remote : gw . Message ,
Bridge : br ,
}
2018-02-27 00:33:21 +01:00
// add the actual bridger for this protocol to this bridge using the bridgeMap
2019-02-26 18:03:50 +01:00
if _ , ok := gw . Router . BridgeMap [ br . Protocol ] ; ! ok {
gw . logger . Fatalf ( "Incorrect protocol %s specified in gateway configuration %s, exiting." , br . Protocol , cfg . Account )
}
2018-11-30 23:53:00 +01:00
br . Bridger = gw . Router . BridgeMap [ br . Protocol ] ( brconfig )
2016-11-08 23:44:16 +01:00
}
2017-03-28 23:56:58 +02:00
gw . mapChannelsToBridge ( br )
2016-11-13 23:06:37 +01:00
gw . Bridges [ cfg . Account ] = br
2016-09-18 19:21:15 +02:00
return nil
}
2019-09-09 23:48:00 +02:00
func ( gw * Gateway ) checkConfig ( cfg * config . Bridge ) {
match := false
for _ , key := range gw . Router . Config . Viper ( ) . AllKeys ( ) {
2020-04-21 20:42:11 +02:00
if strings . HasPrefix ( key , strings . ToLower ( cfg . Account ) ) {
2019-09-09 23:48:00 +02:00
match = true
break
}
}
if ! match {
gw . logger . Fatalf ( "Account %s defined in gateway %s but no configuration found, exiting." , cfg . Account , gw . Name )
}
}
2019-02-23 22:51:27 +01:00
// AddConfig associates a new configuration with the gateway object.
2017-04-01 17:24:19 +02:00
func ( gw * Gateway ) AddConfig ( cfg * config . Gateway ) error {
gw . Name = cfg . Name
gw . MyConfig = cfg
2018-12-12 23:57:17 +01:00
if err := gw . mapChannels ( ) ; err != nil {
2019-02-23 22:51:27 +01:00
gw . logger . Errorf ( "mapChannels() failed: %s" , err )
2018-12-12 23:57:17 +01:00
}
2017-04-01 17:24:19 +02:00
for _ , br := range append ( gw . MyConfig . In , append ( gw . MyConfig . InOut , gw . MyConfig . Out ... ) ... ) {
2021-04-03 19:16:46 +02:00
br := br // scopelint
2017-04-01 17:24:19 +02:00
err := gw . AddBridge ( & br )
if err != nil {
return err
}
}
return nil
}
2017-03-28 23:56:58 +02:00
func ( gw * Gateway ) mapChannelsToBridge ( br * bridge . Bridge ) {
for ID , channel := range gw . Channels {
if br . Account == channel . Account {
br . Channels [ ID ] = * channel
2017-02-14 23:52:45 +01:00
}
}
}
2017-02-14 21:12:02 +01:00
func ( gw * Gateway ) reconnectBridge ( br * bridge . Bridge ) {
2018-12-12 23:57:17 +01:00
if err := br . Disconnect ( ) ; err != nil {
2019-02-23 22:51:27 +01:00
gw . logger . Errorf ( "Disconnect() %s failed: %s" , br . Account , err )
2018-12-12 23:57:17 +01:00
}
2017-02-14 21:12:02 +01:00
time . Sleep ( time . Second * 5 )
RECONNECT :
2019-02-23 22:51:27 +01:00
gw . logger . Infof ( "Reconnecting %s" , br . Account )
2017-02-14 21:12:02 +01:00
err := br . Connect ( )
if err != nil {
2019-02-23 22:51:27 +01:00
gw . logger . Errorf ( "Reconnection failed: %s. Trying again in 60 seconds" , err )
2017-02-14 21:12:02 +01:00
time . Sleep ( time . Second * 60 )
goto RECONNECT
}
2017-04-01 17:24:19 +02:00
br . Joined = make ( map [ string ] bool )
2018-12-12 23:57:17 +01:00
if err := br . JoinChannels ( ) ; err != nil {
2019-02-23 22:51:27 +01:00
gw . logger . Errorf ( "JoinChannels() %s failed: %s" , br . Account , err )
2018-12-12 23:57:17 +01:00
}
2017-02-14 21:12:02 +01:00
}
2017-07-22 17:25:22 +02:00
func ( gw * Gateway ) mapChannelConfig ( cfg [ ] config . Bridge , direction string ) {
for _ , br := range cfg {
2018-11-15 20:43:43 +01:00
if isAPI ( br . Account ) {
2018-11-08 22:20:03 +01:00
br . Channel = apiProtocol
2017-06-15 00:40:23 +02:00
}
2018-01-28 19:15:13 +01:00
// make sure to lowercase irc channels in config #348
if strings . HasPrefix ( br . Account , "irc." ) {
br . Channel = strings . ToLower ( br . Channel )
}
2019-01-07 00:26:11 +01:00
if strings . HasPrefix ( br . Account , "mattermost." ) && strings . HasPrefix ( br . Channel , "#" ) {
2019-02-23 22:51:27 +01:00
gw . logger . Errorf ( "Mattermost channels do not start with a #: remove the # in %s" , br . Channel )
2019-01-07 00:26:11 +01:00
os . Exit ( 1 )
}
2019-03-02 20:31:38 +01:00
if strings . HasPrefix ( br . Account , "zulip." ) && ! strings . Contains ( br . Channel , "/topic:" ) {
gw . logger . Errorf ( "Breaking change, since matterbridge 1.14.0 zulip channels need to specify the topic with channel/topic:mytopic in %s of %s" , br . Channel , br . Account )
os . Exit ( 1 )
}
2017-03-28 23:56:58 +02:00
ID := br . Channel + br . Account
2017-07-22 17:25:22 +02:00
if _ , ok := gw . Channels [ ID ] ; ! ok {
2019-02-23 22:51:27 +01:00
channel := & config . ChannelInfo {
Name : br . Channel ,
Direction : direction ,
ID : ID ,
Options : br . Options ,
Account : br . Account ,
SameChannel : make ( map [ string ] bool ) ,
}
2017-04-01 17:24:19 +02:00
channel . SameChannel [ gw . Name ] = br . SameChannel
2017-03-28 23:56:58 +02:00
gw . Channels [ channel . ID ] = channel
2017-07-22 17:25:22 +02:00
} else {
// if we already have a key and it's not our current direction it means we have a bidirectional inout
if gw . Channels [ ID ] . Direction != direction {
gw . Channels [ ID ] . Direction = "inout"
}
2017-03-28 23:56:58 +02:00
}
2017-04-01 17:24:19 +02:00
gw . Channels [ ID ] . SameChannel [ gw . Name ] = br . SameChannel
2016-11-20 23:01:44 +01:00
}
2017-07-22 17:25:22 +02:00
}
2017-07-25 20:11:52 +02:00
2017-07-22 17:25:22 +02:00
func ( gw * Gateway ) mapChannels ( ) error {
gw . mapChannelConfig ( gw . MyConfig . In , "in" )
gw . mapChannelConfig ( gw . MyConfig . Out , "out" )
gw . mapChannelConfig ( gw . MyConfig . InOut , "inout" )
2016-09-18 19:21:15 +02:00
return nil
}
2017-04-01 17:24:19 +02:00
func ( gw * Gateway ) getDestChannel ( msg * config . Message , dest bridge . Bridge ) [ ] config . ChannelInfo {
2017-03-28 23:56:58 +02:00
var channels [ ] config . ChannelInfo
2018-01-21 12:21:55 +01:00
// for messages received from the api check that the gateway is the specified one
2018-11-08 22:20:03 +01:00
if msg . Protocol == apiProtocol && gw . Name != msg . Gateway {
2018-01-21 12:21:55 +01:00
return channels
}
2019-02-17 21:49:45 +01:00
// discord join/leave is for the whole bridge, isn't a per channel join/leave
2019-02-17 22:45:23 +01:00
if msg . Event == config . EventJoinLeave && getProtocol ( msg ) == "discord" && msg . Channel == "" {
2019-02-17 21:49:45 +01:00
for _ , channel := range gw . Channels {
2019-02-17 22:45:23 +01:00
if channel . Account == dest . Account && strings . Contains ( channel . Direction , "out" ) &&
2019-02-17 21:49:45 +01:00
gw . validGatewayDest ( msg ) {
channels = append ( channels , * channel )
}
}
return channels
}
2017-07-22 17:25:22 +02:00
// if source channel is in only, do nothing
for _ , channel := range gw . Channels {
// lookup the channel from the message
2019-02-23 22:51:27 +01:00
if channel . ID == getChannelID ( msg ) {
2017-07-22 17:25:22 +02:00
// we only have destinations if the original message is from an "in" (sending) channel
if ! strings . Contains ( channel . Direction , "in" ) {
return channels
}
continue
}
}
2017-03-28 23:56:58 +02:00
for _ , channel := range gw . Channels {
2019-02-23 22:51:27 +01:00
if _ , ok := gw . Channels [ getChannelID ( msg ) ] ; ! ok {
2017-04-01 17:24:19 +02:00
continue
}
2017-06-27 00:28:18 +02:00
2019-02-23 22:51:27 +01:00
// do samechannelgateway logic
2017-06-27 00:28:18 +02:00
if channel . SameChannel [ msg . Gateway ] {
if msg . Channel == channel . Name && msg . Account != dest . Account {
channels = append ( channels , * channel )
}
continue
}
2018-11-08 00:29:30 +01:00
if strings . Contains ( channel . Direction , "out" ) && channel . Account == dest . Account && gw . validGatewayDest ( msg ) {
2017-03-28 23:56:58 +02:00
channels = append ( channels , * channel )
2016-10-23 20:58:04 +02:00
}
}
2017-03-28 23:56:58 +02:00
return channels
2016-09-18 19:21:15 +02:00
}
2019-02-23 22:51:27 +01:00
func ( gw * Gateway ) getDestMsgID ( msgID string , dest * bridge . Bridge , channel * config . ChannelInfo ) string {
2023-03-01 07:50:17 +01:00
var destID string
_ , usePersistent := gw . Config . GetString ( "PersistentMessageStorePath" )
if usePersistent {
destID = gw . getDestMessagesFromStore ( msgID , dest , channel )
} else {
destID = gw . getDestMessageFromMemCache ( msgID , dest , channel )
}
return strings . Replace ( destID , dest . Protocol + " " , "" , 1 )
}
func ( gw * Gateway ) getDestMessageFromMemCache ( msgID string , dest * bridge . Bridge , channel * config . ChannelInfo ) string {
2018-11-07 09:14:31 +01:00
if res , ok := gw . Messages . Get ( msgID ) ; ok {
IDs := res . ( [ ] * BrMsgID )
for _ , id := range IDs {
// check protocol, bridge name and channelname
// for people that reuse the same bridge multiple times. see #342
2023-03-01 07:50:17 +01:00
if dest . Protocol == id . Protocol && dest . Name == id . DestName && channel . ID == id . ChannelID {
return id . ID
2018-11-07 09:14:31 +01:00
}
}
}
return ""
}
2018-12-12 23:57:17 +01:00
// ignoreTextEmpty returns true if we need to ignore a message with an empty text.
func ( gw * Gateway ) ignoreTextEmpty ( msg * config . Message ) bool {
if msg . Text != "" {
return false
2018-02-02 21:04:43 +01:00
}
2018-12-12 23:57:17 +01:00
if msg . Event == config . EventUserTyping {
return false
2016-11-14 22:53:06 +01:00
}
2018-12-12 23:57:17 +01:00
// we have an attachment or actual bytes, do not ignore
if msg . Extra != nil &&
( msg . Extra [ "attachments" ] != nil ||
len ( msg . Extra [ "file" ] ) > 0 ||
len ( msg . Extra [ config . EventFileFailureSize ] ) > 0 ) {
return false
2018-11-07 09:14:31 +01:00
}
2019-02-23 22:51:27 +01:00
gw . logger . Debugf ( "ignoring empty message %#v from %s" , msg , msg . Account )
2018-12-12 23:57:17 +01:00
return true
}
2018-11-07 09:14:31 +01:00
2016-09-18 19:21:15 +02:00
func ( gw * Gateway ) ignoreMessage ( msg * config . Message ) bool {
2017-07-30 16:09:05 +02:00
// if we don't have the bridge, ignore it
if _ , ok := gw . Bridges [ msg . Account ] ; ! ok {
2017-07-25 20:11:52 +02:00
return true
}
2018-02-27 22:16:44 +01:00
2018-12-12 23:57:17 +01:00
igNicks := strings . Fields ( gw . Bridges [ msg . Account ] . GetString ( "IgnoreNicks" ) )
igMessages := strings . Fields ( gw . Bridges [ msg . Account ] . GetString ( "IgnoreMessages" ) )
2022-03-31 23:50:38 +02:00
if gw . ignoreTextEmpty ( msg ) || gw . ignoreText ( msg . Username , igNicks ) || gw . ignoreText ( msg . Text , igMessages ) || gw . ignoreFilesComment ( msg . Extra , igMessages ) {
2017-03-02 23:51:19 +01:00
return true
}
2018-02-27 22:16:44 +01:00
2016-09-18 19:21:15 +02:00
return false
}
2022-03-31 23:50:38 +02:00
// ignoreFilesComment returns true if we need to ignore a file with matched comment.
func ( gw * Gateway ) ignoreFilesComment ( extra map [ string ] [ ] interface { } , igMessages [ ] string ) bool {
if extra == nil {
return false
}
for _ , f := range extra [ "file" ] {
fi , ok := f . ( config . FileInfo )
if ! ok {
continue
}
if gw . ignoreText ( fi . Comment , igMessages ) {
return true
}
}
return false
}
2019-02-23 22:51:27 +01:00
func ( gw * Gateway ) modifyUsername ( msg * config . Message , dest * bridge . Bridge ) string {
2018-12-01 00:24:07 +01:00
if dest . GetBool ( "StripNick" ) {
2017-10-27 00:07:33 +02:00
re := regexp . MustCompile ( "[^a-zA-Z0-9]+" )
msg . Username = re . ReplaceAllString ( msg . Username , "" )
}
2018-03-04 23:52:14 +01:00
nick := dest . GetString ( "RemoteNickFormat" )
2017-11-20 23:27:27 +01:00
// loop to replace nicks
2020-02-09 22:07:26 +01:00
br := gw . Bridges [ msg . Account ]
2018-03-04 23:52:14 +01:00
for _ , outer := range br . GetStringSlice2D ( "ReplaceNicks" ) {
2017-11-20 23:27:27 +01:00
search := outer [ 0 ]
replace := outer [ 1 ]
// TODO move compile to bridge init somewhere
re , err := regexp . Compile ( search )
if err != nil {
2019-02-23 22:51:27 +01:00
gw . logger . Errorf ( "regexp in %s failed: %s" , msg . Account , err )
2017-11-20 23:27:27 +01:00
break
}
msg . Username = re . ReplaceAllString ( msg . Username , replace )
}
2017-06-07 23:54:50 +02:00
if len ( msg . Username ) > 0 {
2017-06-15 00:07:12 +02:00
// fix utf-8 issue #193
i := 0
for index := range msg . Username {
if i == 1 {
i = index
break
}
i ++
}
2020-11-25 23:54:27 +01:00
nick = strings . ReplaceAll ( nick , "{NOPINGNICK}" , msg . Username [ : i ] + "\u200b" + msg . Username [ i : ] )
2017-06-07 23:54:50 +02:00
}
2018-02-27 22:16:44 +01:00
2020-11-25 23:54:27 +01:00
nick = strings . ReplaceAll ( nick , "{BRIDGE}" , br . Name )
nick = strings . ReplaceAll ( nick , "{PROTOCOL}" , br . Protocol )
nick = strings . ReplaceAll ( nick , "{GATEWAY}" , gw . Name )
nick = strings . ReplaceAll ( nick , "{LABEL}" , br . GetString ( "Label" ) )
nick = strings . ReplaceAll ( nick , "{NICK}" , msg . Username )
nick = strings . ReplaceAll ( nick , "{USERID}" , msg . UserID )
nick = strings . ReplaceAll ( nick , "{CHANNEL}" , msg . Channel )
2019-04-08 20:58:21 +02:00
tengoNick , err := gw . modifyUsernameTengo ( msg , br )
if err != nil {
gw . logger . Errorf ( "modifyUsernameTengo error: %s" , err )
}
2020-11-25 23:54:27 +01:00
nick = strings . ReplaceAll ( nick , "{TENGO}" , tengoNick )
2017-07-21 17:04:03 +02:00
return nick
2016-11-13 23:06:37 +01:00
}
2017-03-28 23:56:58 +02:00
2019-02-23 22:51:27 +01:00
func ( gw * Gateway ) modifyAvatar ( msg * config . Message , dest * bridge . Bridge ) string {
2018-12-01 00:24:07 +01:00
iconurl := dest . GetString ( "IconURL" )
2017-04-08 00:16:46 +02:00
iconurl = strings . Replace ( iconurl , "{NICK}" , msg . Username , - 1 )
if msg . Avatar == "" {
msg . Avatar = iconurl
}
2017-07-21 17:04:03 +02:00
return msg . Avatar
2017-04-08 00:16:46 +02:00
}
2017-07-09 13:59:50 +02:00
func ( gw * Gateway ) modifyMessage ( msg * config . Message ) {
2020-10-21 20:35:22 +02:00
if gw . BridgeValues ( ) . General . TengoModifyMessage != "" {
gw . logger . Warnf ( "General TengoModifyMessage=%s is deprecated and will be removed in v1.20.0, please move to Tengo InMessage=%s" , gw . BridgeValues ( ) . General . TengoModifyMessage , gw . BridgeValues ( ) . General . TengoModifyMessage )
}
if err := modifyInMessageTengo ( gw . BridgeValues ( ) . General . TengoModifyMessage , msg ) ; err != nil {
2019-02-23 22:51:27 +01:00
gw . logger . Errorf ( "TengoModifyMessage failed: %s" , err )
2019-02-23 16:39:44 +01:00
}
2020-10-21 20:35:22 +02:00
inMessage := gw . BridgeValues ( ) . Tengo . InMessage
if inMessage == "" {
inMessage = gw . BridgeValues ( ) . Tengo . Message
if inMessage != "" {
gw . logger . Warnf ( "Tengo Message=%s is deprecated and will be removed in v1.20.0, please move to Tengo InMessage=%s" , inMessage , inMessage )
}
}
if err := modifyInMessageTengo ( inMessage , msg ) ; err != nil {
2019-04-08 20:58:21 +02:00
gw . logger . Errorf ( "Tengo.Message failed: %s" , err )
}
2019-02-23 16:39:44 +01:00
2017-07-09 13:59:50 +02:00
// replace :emoji: to unicode
2021-04-03 19:16:46 +02:00
emoji . ReplacePadding = ""
2019-11-17 23:01:03 +01:00
msg . Text = emoji . Sprint ( msg . Text )
2018-02-27 22:16:44 +01:00
2017-11-15 23:32:49 +01:00
br := gw . Bridges [ msg . Account ]
// loop to replace messages
2018-03-04 23:52:14 +01:00
for _ , outer := range br . GetStringSlice2D ( "ReplaceMessages" ) {
2017-11-15 23:32:49 +01:00
search := outer [ 0 ]
replace := outer [ 1 ]
// TODO move compile to bridge init somewhere
re , err := regexp . Compile ( search )
if err != nil {
2019-02-23 22:51:27 +01:00
gw . logger . Errorf ( "regexp in %s failed: %s" , msg . Account , err )
2017-11-15 23:32:49 +01:00
break
}
msg . Text = re . ReplaceAllString ( msg . Text , replace )
}
2018-01-21 12:21:55 +01:00
2019-02-23 16:35:54 +01:00
gw . handleExtractNicks ( msg )
2018-01-21 12:21:55 +01:00
// messages from api have Gateway specified, don't overwrite
2018-11-08 22:20:03 +01:00
if msg . Protocol != apiProtocol {
2018-01-21 12:21:55 +01:00
msg . Gateway = gw . Name
}
2017-07-09 13:59:50 +02:00
}
2019-02-23 22:51:27 +01:00
// SendMessage sends a message (with specified parentID) to the channel on the selected
// destination bridge and returns a message ID or an error.
func ( gw * Gateway ) SendMessage (
rmsg * config . Message ,
dest * bridge . Bridge ,
channel * config . ChannelInfo ,
canonicalParentMsgID string ,
) ( string , error ) {
msg := * rmsg
2018-12-12 23:57:17 +01:00
// Only send the avatar download event to ourselves.
if msg . Event == config . EventAvatarDownload {
2019-02-23 22:51:27 +01:00
if channel . ID != getChannelID ( rmsg ) {
2018-12-12 23:57:17 +01:00
return "" , nil
}
} else {
// do not send to ourself for any other event
2019-02-23 22:51:27 +01:00
if channel . ID == getChannelID ( rmsg ) {
2018-12-12 23:57:17 +01:00
return "" , nil
}
2018-06-08 22:30:35 +02:00
}
2020-11-22 22:21:02 +01:00
// Only send irc notices to irc
if msg . Event == config . EventNoticeIRC && dest . Protocol != "irc" {
return "" , nil
}
2018-12-12 23:57:17 +01:00
// Too noisy to log like other events
2020-10-21 21:57:14 +02:00
debugSendMessage := ""
2018-12-12 23:57:17 +01:00
if msg . Event != config . EventUserTyping {
2020-10-21 21:57:14 +02:00
debugSendMessage = fmt . Sprintf ( "=> Sending %#v from %s (%s) to %s (%s)" , msg , msg . Account , rmsg . Channel , dest . Account , channel . Name )
2018-06-08 22:30:35 +02:00
}
2018-12-12 23:57:17 +01:00
msg . Channel = channel . Name
2019-02-23 22:51:27 +01:00
msg . Avatar = gw . modifyAvatar ( rmsg , dest )
msg . Username = gw . modifyUsername ( rmsg , dest )
2018-06-08 22:30:35 +02:00
2022-02-05 14:45:54 +01:00
// exclude file delete event as the msg ID here is the native file ID that needs to be deleted
if msg . Event != config . EventFileDelete {
msg . ID = gw . getDestMsgID ( rmsg . Protocol + " " + rmsg . ID , dest , channel )
}
2018-06-08 22:30:35 +02:00
2018-12-12 23:57:17 +01:00
// for api we need originchannel as channel
if dest . Protocol == apiProtocol {
2019-02-23 22:51:27 +01:00
msg . Channel = rmsg . Channel
2018-12-12 23:57:17 +01:00
}
2018-06-08 22:30:35 +02:00
2022-01-09 23:46:59 +01:00
msg . ParentID = gw . getDestMsgID ( canonicalParentMsgID , dest , channel )
2018-12-12 23:57:17 +01:00
if msg . ParentID == "" {
2022-01-09 23:46:59 +01:00
msg . ParentID = strings . Replace ( canonicalParentMsgID , dest . Protocol + " " , "" , 1 )
2018-12-12 23:57:17 +01:00
}
2018-06-08 22:30:35 +02:00
2019-02-10 17:23:50 +01:00
// if the parentID is still empty and we have a parentID set in the original message
2020-12-31 19:01:57 +01:00
// this means that we didn't find it in the cache so set it to a "msg-parent-not-found" constant
2019-02-23 22:51:27 +01:00
if msg . ParentID == "" && rmsg . ParentID != "" {
2020-12-31 19:01:57 +01:00
msg . ParentID = config . ParentIDNotFound
2019-02-10 17:23:50 +01:00
}
2020-10-21 21:57:14 +02:00
drop , err := gw . modifyOutMessageTengo ( rmsg , & msg , dest )
2019-04-19 18:27:31 +02:00
if err != nil {
gw . logger . Errorf ( "modifySendMessageTengo: %s" , err )
}
2020-10-21 21:57:14 +02:00
if drop {
gw . logger . Debugf ( "=> Tengo dropping %#v from %s (%s) to %s (%s)" , msg , msg . Account , rmsg . Channel , dest . Account , channel . Name )
return "" , nil
}
if debugSendMessage != "" {
gw . logger . Debug ( debugSendMessage )
}
2018-12-12 23:57:17 +01:00
// if we are using mattermost plugin account, send messages to MattermostPlugin channel
// that can be picked up by the mattermost matterbridge plugin
if dest . Account == "mattermost.plugin" {
gw . Router . MattermostPlugin <- msg
}
2018-06-08 22:30:35 +02:00
2020-11-22 17:20:20 +01:00
defer func ( t time . Time ) {
gw . logger . Debugf ( "=> Send from %s (%s) to %s (%s) took %s" , msg . Account , rmsg . Channel , dest . Account , channel . Name , time . Since ( t ) )
} ( time . Now ( ) )
2018-12-12 23:57:17 +01:00
mID , err := dest . Send ( msg )
if err != nil {
return mID , err
}
2018-06-08 22:30:35 +02:00
2018-12-12 23:57:17 +01:00
// append the message ID (mID) from this bridge (dest) to our brMsgIDs slice
if mID != "" {
2019-02-23 22:51:27 +01:00
gw . logger . Debugf ( "mID %s: %s" , dest . Account , mID )
2018-12-12 23:57:17 +01:00
return mID , nil
2021-04-03 19:16:46 +02:00
// brMsgIDs = append(brMsgIDs, &BrMsgID{dest, dest.Protocol + " " + mID, channel.ID})
2017-11-24 22:36:19 +01:00
}
2018-12-12 23:57:17 +01:00
return "" , nil
2017-11-24 22:36:19 +01:00
}
2018-11-08 00:29:30 +01:00
func ( gw * Gateway ) validGatewayDest ( msg * config . Message ) bool {
2017-07-25 20:11:52 +02:00
return msg . Gateway == gw . Name
2017-04-01 17:24:19 +02:00
}
2017-06-15 00:40:23 +02:00
2019-02-23 22:51:27 +01:00
func getChannelID ( msg * config . Message ) string {
2018-02-27 22:16:44 +01:00
return msg . Channel + msg . Account
2017-06-15 00:40:23 +02:00
}
2018-02-27 00:33:21 +01:00
2018-11-15 20:43:43 +01:00
func isAPI ( account string ) bool {
2018-02-27 22:16:44 +01:00
return strings . HasPrefix ( account , "api." )
}
2019-02-17 21:49:28 +01:00
// ignoreText returns true if text matches any of the input regexes.
func ( gw * Gateway ) ignoreText ( text string , input [ ] string ) bool {
for _ , entry := range input {
if entry == "" {
continue
}
// TODO do not compile regexps everytime
re , err := regexp . Compile ( entry )
if err != nil {
2019-02-23 22:51:27 +01:00
gw . logger . Errorf ( "incorrect regexp %s" , entry )
2019-02-17 21:49:28 +01:00
continue
}
if re . MatchString ( text ) {
2019-02-23 22:51:27 +01:00
gw . logger . Debugf ( "matching %s. ignoring %s" , entry , text )
2019-02-17 21:49:28 +01:00
return true
}
}
return false
}
2019-02-17 22:43:04 +01:00
func getProtocol ( msg * config . Message ) string {
p := strings . Split ( msg . Account , "." )
return p [ 0 ]
}
2019-02-23 16:39:44 +01:00
2020-10-21 20:35:22 +02:00
func modifyInMessageTengo ( filename string , msg * config . Message ) error {
2019-02-23 16:39:44 +01:00
if filename == "" {
return nil
}
res , err := ioutil . ReadFile ( filename )
if err != nil {
return err
}
2020-01-09 21:52:19 +01:00
s := tengo . NewScript ( res )
2019-04-06 22:18:25 +02:00
s . SetImports ( stdlib . GetModuleMap ( stdlib . AllModuleNames ( ) ... ) )
2019-02-23 16:39:44 +01:00
_ = s . Add ( "msgText" , msg . Text )
_ = s . Add ( "msgUsername" , msg . Username )
2020-11-25 23:54:27 +01:00
_ = s . Add ( "msgUserID" , msg . UserID )
2019-02-23 16:39:44 +01:00
_ = s . Add ( "msgAccount" , msg . Account )
_ = s . Add ( "msgChannel" , msg . Channel )
c , err := s . Compile ( )
if err != nil {
return err
}
if err := c . Run ( ) ; err != nil {
return err
}
msg . Text = c . Get ( "msgText" ) . String ( )
msg . Username = c . Get ( "msgUsername" ) . String ( )
return nil
}
2019-04-08 20:58:21 +02:00
func ( gw * Gateway ) modifyUsernameTengo ( msg * config . Message , br * bridge . Bridge ) ( string , error ) {
filename := gw . BridgeValues ( ) . Tengo . RemoteNickFormat
if filename == "" {
return "" , nil
}
res , err := ioutil . ReadFile ( filename )
if err != nil {
return "" , err
}
2020-01-09 21:52:19 +01:00
s := tengo . NewScript ( res )
2019-04-08 20:58:21 +02:00
s . SetImports ( stdlib . GetModuleMap ( stdlib . AllModuleNames ( ) ... ) )
_ = s . Add ( "result" , "" )
_ = s . Add ( "msgText" , msg . Text )
_ = s . Add ( "msgUsername" , msg . Username )
2020-11-25 23:54:27 +01:00
_ = s . Add ( "msgUserID" , msg . UserID )
2019-04-08 20:58:21 +02:00
_ = s . Add ( "nick" , msg . Username )
_ = s . Add ( "msgAccount" , msg . Account )
_ = s . Add ( "msgChannel" , msg . Channel )
_ = s . Add ( "channel" , msg . Channel )
_ = s . Add ( "msgProtocol" , msg . Protocol )
_ = s . Add ( "remoteAccount" , br . Account )
_ = s . Add ( "protocol" , br . Protocol )
_ = s . Add ( "bridge" , br . Name )
_ = s . Add ( "gateway" , gw . Name )
c , err := s . Compile ( )
if err != nil {
return "" , err
}
if err := c . Run ( ) ; err != nil {
return "" , err
}
return c . Get ( "result" ) . String ( ) , nil
}
2019-04-19 18:27:31 +02:00
2020-10-21 21:57:14 +02:00
func ( gw * Gateway ) modifyOutMessageTengo ( origmsg * config . Message , msg * config . Message , br * bridge . Bridge ) ( bool , error ) {
2019-04-19 18:27:31 +02:00
filename := gw . BridgeValues ( ) . Tengo . OutMessage
2020-10-21 21:57:14 +02:00
var (
res [ ] byte
err error
drop bool
)
2019-04-19 18:27:31 +02:00
if filename == "" {
res , err = internal . Asset ( "tengo/outmessage.tengo" )
if err != nil {
2020-10-21 21:57:14 +02:00
return drop , err
2019-04-19 18:27:31 +02:00
}
} else {
res , err = ioutil . ReadFile ( filename )
if err != nil {
2020-10-21 21:57:14 +02:00
return drop , err
2019-04-19 18:27:31 +02:00
}
}
2020-10-21 21:57:14 +02:00
2020-01-09 21:52:19 +01:00
s := tengo . NewScript ( res )
2020-10-21 21:57:14 +02:00
2019-04-19 18:27:31 +02:00
s . SetImports ( stdlib . GetModuleMap ( stdlib . AllModuleNames ( ) ... ) )
_ = s . Add ( "inAccount" , origmsg . Account )
_ = s . Add ( "inProtocol" , origmsg . Protocol )
_ = s . Add ( "inChannel" , origmsg . Channel )
_ = s . Add ( "inGateway" , origmsg . Gateway )
2019-04-24 22:47:37 +02:00
_ = s . Add ( "inEvent" , origmsg . Event )
2019-04-19 18:27:31 +02:00
_ = s . Add ( "outAccount" , br . Account )
_ = s . Add ( "outProtocol" , br . Protocol )
_ = s . Add ( "outChannel" , msg . Channel )
_ = s . Add ( "outGateway" , gw . Name )
2019-04-24 22:47:37 +02:00
_ = s . Add ( "outEvent" , msg . Event )
2019-04-19 18:27:31 +02:00
_ = s . Add ( "msgText" , msg . Text )
_ = s . Add ( "msgUsername" , msg . Username )
2020-11-25 23:54:27 +01:00
_ = s . Add ( "msgUserID" , msg . UserID )
2020-10-21 21:57:14 +02:00
_ = s . Add ( "msgDrop" , drop )
2019-04-19 18:27:31 +02:00
c , err := s . Compile ( )
if err != nil {
2020-10-21 21:57:14 +02:00
return drop , err
2019-04-19 18:27:31 +02:00
}
2020-10-21 21:57:14 +02:00
2019-04-19 18:27:31 +02:00
if err := c . Run ( ) ; err != nil {
2020-10-21 21:57:14 +02:00
return drop , err
2019-04-19 18:27:31 +02:00
}
2020-10-21 21:57:14 +02:00
drop = c . Get ( "msgDrop" ) . Bool ( )
2019-04-19 18:27:31 +02:00
msg . Text = c . Get ( "msgText" ) . String ( )
msg . Username = c . Get ( "msgUsername" ) . String ( )
2020-10-21 21:57:14 +02:00
return drop , nil
2019-04-19 18:27:31 +02:00
}