From 5bc8231b2d21b68b2d592e4d69e8dae02c7754f3 Mon Sep 17 00:00:00 2001 From: Yousef Mansy Date: Wed, 15 Mar 2023 02:12:49 -0700 Subject: [PATCH] Add welcome message functionality (Telegram and whatsapp for now) --- bridge/config/config.go | 1 + bridge/telegram/handlers.go | 2 + bridge/whatsappmulti/handlers.go | 2 + gateway/channelstore.go | 68 ++++++++++++++++ gateway/command.go | 130 ++++++++++++++++++++++++++----- gateway/handlers.go | 14 ++++ gateway/router.go | 20 +++-- 7 files changed, 211 insertions(+), 26 deletions(-) create mode 100644 gateway/channelstore.go diff --git a/bridge/config/config.go b/bridge/config/config.go index 2d83b6e8..ea69ef4b 100644 --- a/bridge/config/config.go +++ b/bridge/config/config.go @@ -15,6 +15,7 @@ import ( ) const ( + EventWelcomeMsg = "welcome" EventJoinLeave = "join_leave" EventTopicChange = "topic_change" EventFailure = "failure" diff --git a/bridge/telegram/handlers.go b/bridge/telegram/handlers.go index d758b302..a98bd1d2 100644 --- a/bridge/telegram/handlers.go +++ b/bridge/telegram/handlers.go @@ -287,7 +287,9 @@ func (b *Btelegram) handleUserJoin(update tgbotapi.Update) { Event: config.EventJoinLeave, Text: "joined chat", } + b.Remote <- rmsg + rmsg.Event = config.EventWelcomeMsg b.Remote <- rmsg } } diff --git a/bridge/whatsappmulti/handlers.go b/bridge/whatsappmulti/handlers.go index 8791df5e..cb7f21ae 100644 --- a/bridge/whatsappmulti/handlers.go +++ b/bridge/whatsappmulti/handlers.go @@ -53,7 +53,9 @@ func (b *Bwhatsapp) handleUserJoin(event *events.GroupInfo) { Event: config.EventJoinLeave, Text: "joined chat", } + b.Remote <- rmsg + rmsg.Event = config.EventWelcomeMsg b.Remote <- rmsg } } diff --git a/gateway/channelstore.go b/gateway/channelstore.go new file mode 100644 index 00000000..f0bbd1ac --- /dev/null +++ b/gateway/channelstore.go @@ -0,0 +1,68 @@ +package gateway + +import ( + "encoding/gob" + + "github.com/42wim/matterbridge/bridge/config" + "github.com/philippgille/gokv" + "github.com/philippgille/gokv/bbolt" + "github.com/philippgille/gokv/encoding" +) + +type ChannelData struct { + HasWelcomeMessage bool + WelcomeMessage config.Message +} + +func (r *Router) getChannelStore(path string) gokv.Store { + gob.Register(map[string]interface{}{}) + gob.Register(config.FileInfo{}) + + options := bbolt.Options{ + BucketName: "ChannelData", + Path: path, + Codec: encoding.Gob, + } + + gob.Register(map[string]interface{}{}) + + store, err := bbolt.NewStore(options) + if err != nil { + r.logger.Errorf("Could not connect to db: %s", path) + } + + return store +} + +func (r *Router) getWelcomeMessage(Channel string) *config.Message { + channelData := new(ChannelData) + found, err := r.ChannelStore.Get(Channel, channelData) + if err != nil { + r.logger.Error(err) + } + + if found && channelData.HasWelcomeMessage { + return &channelData.WelcomeMessage + } + + return nil +} + +func (r *Router) setWelcomeMessage(Channel string, newWelcomeMessage *config.Message) error { + channelData := new(ChannelData) + r.ChannelStore.Get(Channel, channelData) + + if newWelcomeMessage == nil { + channelData.HasWelcomeMessage = false + channelData.WelcomeMessage = config.Message{} + } else { + channelData.HasWelcomeMessage = true + channelData.WelcomeMessage = *newWelcomeMessage + } + + err := r.ChannelStore.Set(Channel, channelData) + if err != nil { + r.logger.Errorf(err.Error()) + } + return err +} diff --git a/gateway/command.go b/gateway/command.go index b749da0a..61f29bec 100644 --- a/gateway/command.go +++ b/gateway/command.go @@ -1,43 +1,82 @@ package gateway import ( + "strings" + "github.com/42wim/matterbridge/bridge/config" ) // returns true if a command was registered (therefore a should not be relayed func (r *Router) handleCommand(msg *config.Message) bool { - switch text := msg.Text; text { - case "!chatId": + help := `!optout - opt out from all message relaying + !optoutmedia - only opt out from relaying attachments + !optin - opt back into chat relaying + !setwelcome - set channel welcome message (admin) + !unsetwelcome - clear channel welcome message (admin) + !help - display this message` + + isAdmin := r.isAdmin(msg) + addTextFromCaptions(msg) + cmd := msg.Text + + switch { + case cmd == "!help": + r.logger.Debug("!help") + r.replyCmd(msg, help) + case cmd == "!chatId": r.logger.Infof("!chatId: %s", msg.Channel) - case "!optin": + case cmd == "!userId": + r.logger.Infof("!userId: %s", msg.UserID) + case cmd == "!ping": + r.logger.Debug("!pong: %s,%s", msg.Channel, msg.UserID) + r.replyCmd(msg, "pong!") + case cmd == "!pingdm": + r.logger.Debug("!pongdm: %s,%s", msg.Channel, msg.UserID) + r.replyDM(msg, "pong!") + case cmd == "!optin": r.logger.Debugf("!optin: %s", msg.UserID) r.handleOptOutCmd(msg, OptIn) - case "!optout": + case cmd == "!optout": r.logger.Debugf("!optout: %s", msg.UserID) r.handleOptOutCmd(msg, OptOut) - case "!optoutmedia": + case cmd == "!optoutmedia": r.logger.Debugf("!optoutmedia: %s", msg.UserID) r.handleOptOutCmd(msg, OptOutMediaOnly) - case "!help": - r.logger.Debug("!help") - help := `!optout - opt out from all message relaying -!optoutmedia - only opt out from relaying attachments -!optin - opt back into chat relaying -!help - display this message` - - r.replyCmd(msg, help) - case "!ping": - r.logger.Debug("!pong") - r.replyCmd(msg, "pong!") - case "!pingdm": - r.logger.Debug("!pongdm") - r.replyDM(msg, "pong!") + case isAdmin && strings.HasPrefix(cmd, "!setwelcome"): + r.logger.Debugf("!setwelcome: %s - %+v", msg.Channel, msg) + r.handleWelcomeCmd(msg, msg) + case isAdmin && strings.HasPrefix(cmd, "!unsetwelcome"): + r.logger.Debugf("!unsetwelcome: %s", msg.Channel) + r.handleWelcomeCmd(msg, nil) + case cmd == "!echowelcome": + r.logger.Debugf("!echowelcome: %s,%s", msg.Channel, msg.UserID) + r.handleEchoWelcomeCmd(msg) default: return false } + return true } +func (r *Router) isAdmin(msg *config.Message) bool { + admins, _ := r.GetStringSlice("Admins") + + for _, ID := range admins { + if msg.UserID == ID { + return true + } + } + return false +} + +func addTextFromCaptions(msg *config.Message) { + for _, f := range msg.Extra["file"] { + fi := f.(config.FileInfo) + + msg.Text += fi.Comment + } +} + func (r *Router) replyCmd(msg *config.Message, str string) { srcBridge := r.getBridge(msg.Account) @@ -71,8 +110,20 @@ func (r *Router) replyDM(msg *config.Message, str string) { srcBridge.Send(reply) } -func (r *Router) handleOptOutCmd(msg *config.Message, newStaus OptOutStatus) { - err := r.setOptOutStatus(msg.UserID, newStaus) +func (r *Router) sendDM(msg *config.Message, dmChannel string) { + srcBridge := r.getBridge(msg.Account) + + msg.Channel = dmChannel + msg.Username = "" + msg.UserID = "" + msg.ID = "" + msg.Event = "" + + srcBridge.Send(*msg) +} + +func (r *Router) handleOptOutCmd(msg *config.Message, newStatus OptOutStatus) { + err := r.setOptOutStatus(msg.UserID, newStatus) reply := "Successfully set message relay preferences." if err != nil { @@ -81,3 +132,40 @@ func (r *Router) handleOptOutCmd(msg *config.Message, newStaus OptOutStatus) { r.replyCmd(msg, reply) } + +func (r *Router) handleWelcomeCmd(msg *config.Message, welcomeMsg *config.Message) { + + if welcomeMsg != nil { + welcomeMsg.Text = strings.Replace(welcomeMsg.Text, "!setwelcome ", "", 1) + + for i, f := range welcomeMsg.Extra["file"] { + fi := f.(config.FileInfo) + fi.Comment = strings.Replace(fi.Comment, "!setwelcome ", "", 1) + + welcomeMsg.Extra["file"][i] = fi + } + } + + err := r.setWelcomeMessage(msg.Channel, welcomeMsg) + + reply := "Successfully set welcome message for channel." + if welcomeMsg == nil { + reply = "Successfully removed welcome message for channel." + } + if err != nil { + reply = "Error setting channel welcome message, try again later or contact the moderators." + } + + r.replyCmd(msg, reply) +} + +func (r *Router) handleEchoWelcomeCmd(msg *config.Message) { + msg.Event = config.EventWelcomeMsg + + if r.getWelcomeMessage(msg.Channel) == nil { + r.replyCmd(msg, "No welcome message configured, set with !setwelcome") + return + } + + r.handleEventWelcome(msg) +} diff --git a/gateway/handlers.go b/gateway/handlers.go index 531df6c4..6845a3d7 100644 --- a/gateway/handlers.go +++ b/gateway/handlers.go @@ -49,6 +49,20 @@ func (r *Router) handleEventGetChannelMembers(msg *config.Message) { } } +func (r *Router) handleEventWelcome(msg *config.Message) bool { + if msg.Event != config.EventWelcomeMsg { + return false + } + + welcomeMsg := r.getWelcomeMessage(msg.Channel) + + if welcomeMsg != nil { + r.sendDM(welcomeMsg, msg.UserID) + } + + return true +} + // handleEventRejoinChannels handles rejoining of channels. func (r *Router) handleEventRejoinChannels(msg *config.Message) { if msg.Event != config.EventRejoinChannels { diff --git a/gateway/router.go b/gateway/router.go index 560f4cc6..b7a80b46 100644 --- a/gateway/router.go +++ b/gateway/router.go @@ -21,6 +21,7 @@ type Router struct { Message chan config.Message MattermostPlugin chan config.Message UserStore gokv.Store + ChannelStore gokv.Store logger *logrus.Entry } @@ -101,9 +102,13 @@ func (r *Router) Start() error { } } } - userStorePath, exists := r.Config.GetString("UserStorePath") + dataStorePath, exists := r.Config.GetString("UserStore") if exists { - r.UserStore = r.getUserStore(userStorePath) + r.UserStore = r.getUserStore(dataStorePath) + } + dataStorePath, exists = r.Config.GetString("ChannelStore") + if exists { + r.ChannelStore = r.getChannelStore(dataStorePath) } go r.handleReceive() @@ -137,14 +142,19 @@ func (r *Router) getBridge(account string) *bridge.Bridge { func (r *Router) handleReceive() { for msg := range r.Message { msg := msg // scopelint - if r.handleCommand(&msg) { - continue - } + + skipMsg := false + skipMsg = skipMsg || r.handleCommand(&msg) + skipMsg = skipMsg || r.handleEventWelcome(&msg) r.handleEventGetChannelMembers(&msg) r.handleEventFailure(&msg) r.handleEventRejoinChannels(&msg) r.handleOptOutUser(&msg) + if skipMsg { + continue + } + // Set message protocol based on the account it came from msg.Protocol = r.getBridge(msg.Account).Protocol