Skip to content

Commit

Permalink
Implement autorespond feature (#188)
Browse files Browse the repository at this point in the history
* Implement autorespond feature

* Better handling for cast errors

* Add tests

* Fix indentation

* Add empty setting for auto respond message instructions

* Use read-only setting, not empty setting

* Fix formatting

* Address requested changes

* Fix naming

* Add interactive dialog

* Fix naming

* Remove calls to GetSetting, add FalseSkip, fix error message

* Change naming, add logging, fix test code

* Rename API route

* Fix API path variable

* Use default message as dialog placeholder

* Fix diff

* Change dialog title

* Fix merge conflict
  • Loading branch information
MatthewDorner authored Aug 21, 2020
1 parent 5e9b8e3 commit b8aff5a
Show file tree
Hide file tree
Showing 18 changed files with 556 additions and 13 deletions.
3 changes: 3 additions & 0 deletions server/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ func Init(h *httputils.Handler, env mscalendar.Env, notificationProcessor mscale
apiRouter := h.Router.PathPrefix(config.PathAPI).Subrouter()
apiRouter.HandleFunc("/authorized", api.getAuthorized).Methods("GET")

dialogRouter := h.Router.PathPrefix(config.PathDialogs).Subrouter()
dialogRouter.HandleFunc(config.PathSetAutoRespondMessage, api.setAutoRespondMessage).Methods("POST")

notificationRouter := h.Router.PathPrefix(config.PathNotification).Subrouter()
notificationRouter.HandleFunc(config.PathEvent, api.notification).Methods("POST")

Expand Down
56 changes: 56 additions & 0 deletions server/api/auto_respond.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright (c) 2019-present Mattermost, Inc. All Rights Reserved.
// See License for license information.

package api

import (
"encoding/json"
"net/http"

"github.com/mattermost/mattermost-server/v5/model"

"github.com/mattermost/mattermost-plugin-mscalendar/server/mscalendar"
)

func (api *api) setAutoRespondMessage(w http.ResponseWriter, req *http.Request) {
mattermostUserID := req.Header.Get("Mattermost-User-ID")
if mattermostUserID == "" {
dialogResponseError(w, "Not authorized.")
return
}

v := model.SubmitDialogRequest{}
err := json.NewDecoder(req.Body).Decode(&v)
if err != nil {
api.Logger.Warnf("Failed to unmarshal auto-respond message dialog request. err=%v", err)
dialogResponseError(w, "Failed to process submit dialog response")
return
}

m := mscalendar.New(api.Env, mattermostUserID)
message, ok := v.Submission["auto_respond_message"].(string)
if !ok {
dialogResponseError(w, `No recognizable value for property "auto_respond_message".`)
return
}

err = m.SetUserAutoRespondMessage(mattermostUserID, message)
if err != nil {
api.Logger.Warnf("Failed to set auto-respond message. err=%v", err)
dialogResponseError(w, "Failed to set auto-respond message")
return
}

response := model.SubmitDialogResponse{}
w.Header().Set("Content-Type", "application/json")
w.Write(response.ToJson())
api.Env.Poster.Ephemeral(mattermostUserID, v.ChannelId, "Auto-respond message changed to: '%s'", message)
}

func dialogResponseError(w http.ResponseWriter, errorMessage string) {
response := model.SubmitDialogResponse{
Error: errorMessage,
}
w.Header().Set("Content-Type", "application/json")
w.Write(response.ToJson())
}
46 changes: 46 additions & 0 deletions server/command/auto_respond.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package command

import (
"fmt"
"strings"

"github.com/mattermost/mattermost-server/v5/model"

"github.com/mattermost/mattermost-plugin-mscalendar/server/config"
"github.com/mattermost/mattermost-plugin-mscalendar/server/mscalendar"
)

func (c *Command) autoRespond(parameters ...string) (string, bool, error) {
if len(parameters) == 0 {
request := model.OpenDialogRequest{
TriggerId: c.Args.TriggerId,
URL: c.Config.PluginURL + config.PathDialogs + config.PathSetAutoRespondMessage,
Dialog: model.Dialog{
CallbackId: "",
Title: "Microsoft Calendar",
Elements: []model.DialogElement{
{
DisplayName: "Auto-Respond Message",
Name: "auto_respond_message",
Type: "text",
Placeholder: mscalendar.DefaultAutoRespondMessage,
},
},
SubmitLabel: "Submit",
NotifyOnCancel: false,
State: "",
},
}

c.MSCalendar.OpenAutoRespondDialog(request)
return "", false, nil
}

autoRespondMessage := strings.Join(parameters, " ")
err := c.MSCalendar.SetUserAutoRespondMessage(c.Args.UserId, autoRespondMessage)
if err != nil {
return fmt.Sprintf("Failed to set auto-respond message. err=%v", err), false, nil
}

return fmt.Sprintf("Auto-respond message changed to: '%s'", autoRespondMessage), false, nil
}
4 changes: 3 additions & 1 deletion server/command/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func Register(registerFunc RegisterFunc) {
DisplayName: "Microsoft Calendar",
Description: "Interact with your outlook calendar.",
AutoComplete: true,
AutoCompleteDesc: "help, info, connect, disconnect, connect_bot, disconnect_bot, subscribe, showcals, viewcal, createcal, deletecal, createevent, findmeetings, availability, summary",
AutoCompleteDesc: "help, info, connect, disconnect, connect_bot, disconnect_bot, subscribe, showcals, viewcal, createcal, deletecal, createevent, findmeetings, availability, autorespond, summary",
AutoCompleteHint: "(subcommand)",
})
}
Expand Down Expand Up @@ -82,6 +82,8 @@ func (c *Command) Handle() (string, bool, error) {
handler = c.requireConnectedUser(c.showCalendars)
case "availability":
handler = c.requireConnectedUser(c.requireAdminUser(c.debugAvailability))
case "autorespond":
handler = c.requireConnectedUser(c.autoRespond)
case "settings":
handler = c.requireConnectedUser(c.settings)
}
Expand Down
1 change: 1 addition & 0 deletions server/command/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ func (c *Command) help(parameters ...string) (string, bool, error) {
resp += getCommandText("connect")
resp += getCommandText("disconnect")
resp += getCommandText("settings - Edit your user personal settings.")
resp += getCommandText("autorespond <message> - Set your auto-respond message.")
resp += getCommandText("summary - View your events for today, or edit the settings for your daily summary.")
resp += getCommandText("viewcal - View your events for the upcoming week.")
resp += getCommandText("subscribe - Enable notifications for event invitations and updates.")
Expand Down
24 changes: 13 additions & 11 deletions server/config/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,19 @@ const (
CommandTrigger = "mscalendar"
TelemetryShortName = "mscalendar"

PathOAuth2 = "/oauth2"
PathComplete = "/complete"
PathAPI = "/api/v1"
PathPostAction = "/action"
PathRespond = "/respond"
PathAccept = "/accept"
PathDecline = "/decline"
PathTentative = "/tentative"
PathConfirmStatusChange = "/confirm"
PathNotification = "/notification/v1"
PathEvent = "/event"
PathOAuth2 = "/oauth2"
PathComplete = "/complete"
PathAPI = "/api/v1"
PathDialogs = "/dialogs"
PathSetAutoRespondMessage = "/set-auto-respond-message"
PathPostAction = "/action"
PathRespond = "/respond"
PathAccept = "/accept"
PathDecline = "/decline"
PathTentative = "/tentative"
PathConfirmStatusChange = "/confirm"
PathNotification = "/notification/v1"
PathEvent = "/event"

FullPathEventNotification = PathNotification + PathEvent
FullPathOAuth2Redirect = PathOAuth2 + PathComplete
Expand Down
70 changes: 70 additions & 0 deletions server/mscalendar/auto_respond.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright (c) 2019-present Mattermost, Inc. All Rights Reserved.
// See License for license information.

package mscalendar

import (
"github.com/mattermost/mattermost-plugin-mscalendar/server/store"
"github.com/mattermost/mattermost-server/v5/model"
)

const DefaultAutoRespondMessage = "This user is currently in a meeting."

type AutoRespond interface {
HandleBusyDM(post *model.Post) error
SetUserAutoRespondMessage(userID string, message string) error
OpenAutoRespondDialog(request model.OpenDialogRequest) error
}

func (m *mscalendar) HandleBusyDM(post *model.Post) error {
channel, err := m.PluginAPI.GetMattermostChannel(post.ChannelId)
if err != nil {
return err
}

if channel.Type != model.CHANNEL_DIRECT {
return nil
}

usersInChannel, err := m.PluginAPI.GetMattermostUsersInChannel(post.ChannelId, model.CHANNEL_SORT_BY_USERNAME, 0, 2)
if err != nil {
return err
}

var storedRecipient *store.User
for _, u := range usersInChannel {
storedUser, _ := m.Store.LoadUser(u.Id)
if u.Id != post.UserId {
storedRecipient = storedUser
break
}
}

if storedRecipient == nil || !storedRecipient.Settings.AutoRespond || len(storedRecipient.ActiveEvents) == 0 {
return nil
}

recipientStatus, err := m.PluginAPI.GetMattermostUserStatus(storedRecipient.MattermostUserID)
if err != nil {
return err
}
if recipientStatus.Status == model.STATUS_ONLINE {
return nil
}

message := storedRecipient.Settings.AutoRespondMessage
if message == "" {
message = DefaultAutoRespondMessage
}

m.Poster.Ephemeral(post.UserId, post.ChannelId, message)
return nil
}

func (m *mscalendar) SetUserAutoRespondMessage(userID string, message string) error {
return m.Store.SetSetting(userID, store.AutoRespondMessageSettingID, message)
}

func (m *mscalendar) OpenAutoRespondDialog(request model.OpenDialogRequest) error {
return m.PluginAPI.OpenInteractiveDialog(request)
}
Loading

0 comments on commit b8aff5a

Please sign in to comment.