Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PR-70]: Added the ability to send DMs from bot accounts #129

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
56 changes: 29 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,38 +31,39 @@ Use this plugin to improve onboarding and HR processes. It adds a Welcome Bot th
To configure the Welcome Bot, edit your `config.json` file with a message you want to send to a user in the following format:

```
mickmister marked this conversation as resolved.
Show resolved Hide resolved
"Plugins": {
"com.mattermost.welcomebot": {
"WelcomeMessages": [
"Plugins": {
"com.mattermost.welcomebot": {
"WelcomeMessages": [
{
"TeamName": "your-team-name",
"DelayInSeconds": 3,
"Message": [
"Your welcome message here. Each list item specifies one line in the message text."
],
"AttachmentMessage": [
"Attachment message containing user actions"
],
"Actions" : [
{
"TeamName": "your-team-name",
"DelayInSeconds": 3,
"IncludeGuests": false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're removing a piece of data here, the IncludeGuests property

"Message": [
"Your welcome message here. Each list item specifies one line in the message text."
"ActionType": "button",
"ActionDisplayName": "User Action",
"ActionName": "action-name",
"ActionDirectMessagePost": "Message send to new direct messages",
"ActionSuccessfulMessage": [
"Message posted after the user takes this action and joins channels specified by 'ChannelsAddedTo'."
],
"AttachmentMessage": [
"Attachment message containing user actions"
],
"Actions" : [
{
"ActionType": "button",
"ActionDisplayName": "User Action",
"ActionName": "action-name",
"ActionSuccessfulMessage": [
"Message posted after the user takes this action and joins channels specified by 'ChannelsAddedTo'."
],
"ChannelsAddedTo": ["channel-1", "channel-2"]
},
{
"ActionType": "automatic",
"ChannelsAddedTo": ["channel-3", "channel-4"]
}
]
"ChannelsAddedTo": ["channel-1", "channel-2", "@example-bot"]
},
{
"ActionType": "automatic",
"ActionDirectMessagePost": "Message send to new direct messages",
"ChannelsAddedTo": ["channel-3", "channel-4", "@another-bot"]
}
]
}
},
]
}
},
```

where
Expand All @@ -78,6 +79,7 @@ where
- **ActionName**: Sets the action name used by the plugin to identify which action is taken by a user.
- **ActionSuccessfulMessage**: Message posted after the user takes this action and joins the specified channels.
- **ChannelsAddedTo**: List of channel names the user is added to. Must be the channel handle used in the URL, in lowercase. For example, in the following URL the **channel name** value is `my-channel`: https://example.com/my-team/channels/my-channel
- **ActionDirectMessagePost**: The post to send users when creating the bot direct message channel

The preview of the configured messages, as well as the creation of a channel welcome message, can be done via bot commands:
* `/welcomebot help` - Displays usage information.
Expand Down
7 changes: 4 additions & 3 deletions server/action_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package main

// ActionContext passed from action buttons
type ActionContext struct {
TeamID string `json:"team_id"`
UserID string `json:"user_id"`
Action string `json:"action"`
TeamID string `json:"team_id"`
UserID string `json:"user_id"`
Action string `json:"action"`
DirectMessagePost string `json:"direct_message_post"`
}

// Action type for decoding action buttons
Expand Down
3 changes: 3 additions & 0 deletions server/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ type ConfigMessageAction struct {
// The message that's display after this action was successful
ActionSuccessfulMessage []string

// The message that is posted to direct message channels
ActionDirectMessagePost string

// The names of the channels that a users should be added to
ChannelsAddedTo []string
}
Expand Down
61 changes: 53 additions & 8 deletions server/welcomebot.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"time"

"github.com/mattermost/mattermost-server/v6/model"
"github.com/pkg/errors"
)

func (p *Plugin) constructMessageTemplate(userID, teamID string) *MessageTemplate {
Expand Down Expand Up @@ -109,6 +110,7 @@ func (p *Plugin) renderWelcomeMessage(messageTemplate MessageTemplate, configMes
action.Context = &ActionContext{}
action.Context.TeamID = messageTemplate.Team.Id
action.Context.UserID = messageTemplate.User.Id
action.Context.DirectMessagePost = configAction.ActionDirectMessagePost
action.Context.Action = "automatic"

for _, channelName := range configAction.ChannelsAddedTo {
Expand All @@ -121,9 +123,10 @@ func (p *Plugin) renderWelcomeMessage(messageTemplate MessageTemplate, configMes
Name: configAction.ActionDisplayName,
Integration: &model.PostActionIntegration{
Context: map[string]interface{}{
"action": configAction.ActionName,
"team_id": messageTemplate.Team.Id,
"user_id": messageTemplate.User.Id,
"action": configAction.ActionName,
"team_id": messageTemplate.Team.Id,
"user_id": messageTemplate.User.Id,
"direct_message_post": configAction.ActionDirectMessagePost,
},
URL: fmt.Sprintf("%v/plugins/%v/addchannels", p.getSiteURL(), manifest.ID),
},
Expand Down Expand Up @@ -228,12 +231,54 @@ func (p *Plugin) processActionMessage(messageTemplate MessageTemplate, action *A
}

func (p *Plugin) joinChannel(action *Action, channelName string) {
if channel, err := p.API.GetChannelByName(action.Context.TeamID, channelName, false); err == nil {
if _, err := p.API.AddChannelMember(channel.Id, action.Context.UserID); err != nil {
p.API.LogError("Couldn't add user to the channel, continuing to next channel", "user_id", action.Context.UserID, "channel_id", channel.Id)
// If it begins with @ create a DM channel
if strings.HasPrefix(channelName, "@") {
if err := p.handleDMs(action, channelName); err != nil {
p.API.LogError("failed to handle DM channel, continuing to next channel. " + err.Error())
}
} else { // Otherwise treat it like a normal channel
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can return here instead of having the else?

if channel, err := p.API.GetChannelByName(action.Context.TeamID, channelName, false); err == nil {
if _, err := p.API.AddChannelMember(channel.Id, action.Context.UserID); err != nil {
p.API.LogError("Couldn't add user to the channel, continuing to next channel", "channel_name", channelName, "user_id", action.Context.UserID, channel.Id)
return
}
} else {
p.API.LogError("failed to get channel, continuing to the next channel", "channel_name", channelName, "user_id", action.Context.UserID)
return
}
} else {
p.API.LogError("failed to get channel, continuing to the next channel", "channel_name", channelName, "user_id", action.Context.UserID)
}
}

func (p *Plugin) handleDMs(action *Action, channelName string) error {
username := channelName[1:]
dmUser, userErr := p.API.GetUserByUsername(username)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use p.client instead of p.API whenever possible

if userErr != nil {
return errors.Wrapf(userErr, "couldn't find DM channel for username %s", username)
}

if !dmUser.IsBot {
return errors.Wrapf(userErr, "Specified DM user is not a bot for username %s", username)
}
Comment on lines +259 to +261
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


dmChannel, err := p.API.GetDirectChannel(dmUser.Id, action.Context.UserID)
if err != nil {
return errors.Wrapf(err, "Couldn't create or get DM channel for user_id %s and channel_id %s", action.Context.UserID, dmChannel.Id)
}

dmMessage := "Welcome to the team!"
if len(action.Context.DirectMessagePost) != 0 {
dmMessage = action.Context.DirectMessagePost
}
Comment on lines +268 to +271
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we make this required instead? "Welcome to the team!" seems not too useful


post := &model.Post{
Message: dmMessage,
ChannelId: dmChannel.Id,
UserId: dmUser.Id,
}

if _, err := p.API.CreatePost(post); err != nil {
return errors.Wrapf(err, "Could not create direct message for user_id %s", post.UserId)
}

return nil
}
Loading