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

[GH-199] Added a ephemeral message if the user is using PMI and his/her PMI setting is disable on Zoom. #324

Merged
merged 17 commits into from
Jan 2, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions server/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,19 @@ func (p *Plugin) runStartCommand(args *model.CommandArgs, user *model.User, topi
createMeetingWithPMI := false
switch userPMISettingPref {
case zoomPMISettingValueAsk:
p.askPreferenceForMeeting(user.Id, args.ChannelId)
p.askPreferenceForMeeting(user.Id, args.ChannelId, args.RootId)
return "", nil
case "", trueString:
createMeetingWithPMI = true
meetingID = zoomUser.Pmi

if meetingID <= 0 {
meetingID, createMeetingErr = p.createMeetingWithoutPMI(user, zoomUser, args.ChannelId, topic)
if createMeetingErr != nil {
return "", errors.Wrap(createMeetingErr, "failed to create the meeting")
}
p.sendEnableZoomPMISettingMessage(user.Id, args.ChannelId, args.RootId)
}
default:
meetingID, createMeetingErr = p.createMeetingWithoutPMI(user, zoomUser, args.ChannelId, topic)
if createMeetingErr != nil {
Expand Down Expand Up @@ -253,7 +261,7 @@ func (p *Plugin) runHelpCommand(user *model.User) (string, error) {

func (p *Plugin) runSettingCommand(args *model.CommandArgs, params []string, user *model.User) (string, error) {
if len(params) == 0 {
if err := p.sendUserSettingForm(user.Id, args.ChannelId); err != nil {
if err := p.sendUserSettingForm(user.Id, args.ChannelId, args.RootId); err != nil {
return "", err
}
return "", nil
Expand Down
60 changes: 47 additions & 13 deletions server/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const (
actionForContext = "action"
userIDForContext = "userID"
channelIDForContext = "channelID"
rootIDForContext = "rootID"
usePersonalMeetingID = "USE PERSONAL MEETING ID"
useAUniqueMeetingID = "USE A UNIQUE MEETING ID"
MattermostUserIDHeader = "Mattermost-User-ID"
Expand Down Expand Up @@ -93,6 +94,7 @@ func (p *Plugin) submitFormPMIForMeeting(w http.ResponseWriter, r *http.Request)
action := postActionIntegrationRequest.Context[actionForContext].(string)
userID := postActionIntegrationRequest.Context[userIDForContext].(string)
channelID := postActionIntegrationRequest.Context[channelIDForContext].(string)
rootID := postActionIntegrationRequest.Context[rootIDForContext].(string)

slackAttachment := model.SlackAttachment{
Text: fmt.Sprintf("You have selected `%s` to start the meeting.", action),
Expand All @@ -113,10 +115,10 @@ func (p *Plugin) submitFormPMIForMeeting(w http.ResponseWriter, r *http.Request)
p.API.LogError("failed to write response", "Error", err.Error())
}

p.startMeeting(action, userID, channelID)
p.startMeeting(action, userID, channelID, rootID)
}

func (p *Plugin) startMeeting(action string, userID string, channelID string) {
func (p *Plugin) startMeeting(action, userID, channelID, rootID string) {
user, appErr := p.API.GetUser(userID)
if appErr != nil {
p.API.LogWarn("failed to get the user from userID", "Error", appErr.Error())
Expand All @@ -135,15 +137,24 @@ func (p *Plugin) startMeeting(action string, userID string, channelID string) {
if action == usePersonalMeetingID {
createMeetingWithPMI = true
meetingID = zoomUser.Pmi

if meetingID <= 0 {
meetingID, createMeetingErr = p.createMeetingWithoutPMI(user, zoomUser, channelID, defaultMeetingTopic)
if createMeetingErr != nil {
p.API.LogWarn("failed to create the meeting", "Error", createMeetingErr.Error())
return
}
p.sendEnableZoomPMISettingMessage(userID, channelID, rootID)
}
} else {
meetingID, createMeetingErr = p.createMeetingWithoutPMI(user, zoomUser, channelID, defaultMeetingTopic)
}
if createMeetingErr != nil {
p.API.LogWarn("failed to create the meeting", "Error", createMeetingErr.Error())
return
if createMeetingErr != nil {
p.API.LogWarn("failed to create the meeting", "Error", createMeetingErr.Error())
return
}
}

if postMeetingErr := p.postMeeting(user, meetingID, channelID, "", defaultMeetingTopic); postMeetingErr != nil {
if postMeetingErr := p.postMeeting(user, meetingID, channelID, rootID, defaultMeetingTopic); postMeetingErr != nil {
p.API.LogWarn("failed to post the meeting", "Error", postMeetingErr.Error())
return
}
Expand Down Expand Up @@ -319,7 +330,7 @@ func (p *Plugin) completeUserOAuthToZoom(w http.ResponseWriter, r *http.Request)
p.trackConnect(userID)

if justConnect {
p.postEphemeral(userID, channelID, "Successfully connected to Zoom \nType `/zoom settings` to change your meeting ID preference")
p.postEphemeral(userID, channelID, "", "Successfully connected to Zoom \nType `/zoom settings` to change your meeting ID preference")
} else {
meeting, err := client.CreateMeeting(zoomUser, defaultMeetingTopic)
if err != nil {
Expand Down Expand Up @@ -401,7 +412,7 @@ func (p *Plugin) postMeeting(creator *model.User, meetingID int, channelID strin
return nil
}

func (p *Plugin) askPreferenceForMeeting(userID, channelID string) {
func (p *Plugin) askPreferenceForMeeting(userID, channelID, rootID string) {
apiEndPoint := fmt.Sprintf("/plugins/%s%s", manifest.ID, pathAskPMI)

slackAttachment := model.SlackAttachment{
Expand All @@ -418,6 +429,7 @@ func (p *Plugin) askPreferenceForMeeting(userID, channelID string) {
actionForContext: usePersonalMeetingID,
userIDForContext: userID,
channelIDForContext: channelID,
rootIDForContext: rootID,
},
},
},
Expand All @@ -432,6 +444,7 @@ func (p *Plugin) askPreferenceForMeeting(userID, channelID string) {
actionForContext: useAUniqueMeetingID,
userIDForContext: userID,
channelIDForContext: channelID,
rootIDForContext: rootID,
},
},
},
Expand All @@ -441,6 +454,7 @@ func (p *Plugin) askPreferenceForMeeting(userID, channelID string) {
post := &model.Post{
ChannelId: channelID,
UserId: p.botUserID,
RootId: rootID,
}
model.ParseSlackAttachment(post, []*model.SlackAttachment{&slackAttachment})
p.API.SendEphemeralPost(userID, post)
Expand Down Expand Up @@ -525,18 +539,32 @@ func (p *Plugin) handleStartMeeting(w http.ResponseWriter, r *http.Request) {
userPMISettingPref, err := p.getPMISettingData(user.Id)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
p.askPreferenceForMeeting(user.Id, req.ChannelID)
p.askPreferenceForMeeting(user.Id, req.ChannelID, req.RootID)
return
}

createMeetingWithPMI := false
switch userPMISettingPref {
case zoomPMISettingValueAsk:
p.askPreferenceForMeeting(user.Id, req.ChannelID)
p.askPreferenceForMeeting(user.Id, req.ChannelID, req.RootID)

if _, err = w.Write([]byte(`{"meeting_url": ""}`)); err != nil {
p.API.LogWarn("failed to write the response", "Error", err.Error())
}
return
case "", trueString:
createMeetingWithPMI = true
meetingID = zoomUser.Pmi

if meetingID <= 0 {
meetingID, createMeetingErr = p.createMeetingWithoutPMI(user, zoomUser, req.ChannelID, topic)
if createMeetingErr != nil {
p.API.LogWarn("failed to create the meeting", "Error", createMeetingErr.Error())
http.Error(w, createMeetingErr.Error(), http.StatusInternalServerError)
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this call to http.Error make this error show in the client? Maybe we want something more user-friendly if so. Wondering about the other calls to http.Error as well

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mickmister Yes, the error is shown to the client. Do you want us to show the user a generic message whenever we get an API error?

Copy link
Contributor

Choose a reason for hiding this comment

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

It depends how low-level or cryptic these errors are. I trust your judgment on this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mickmister Updated, displaying a generic error to the user now.

return
}
p.sendEnableZoomPMISettingMessage(userID, req.ChannelID, req.RootID)
}
default:
meetingID, createMeetingErr = p.createMeetingWithoutPMI(user, zoomUser, req.ChannelID, topic)
if createMeetingErr != nil {
Expand Down Expand Up @@ -634,10 +662,15 @@ func (p *Plugin) postAuthenticationMessage(channelID string, userID string, mess
return p.API.SendEphemeralPost(userID, post)
}

func (p *Plugin) postEphemeral(userID, channelID, message string) *model.Post {
func (p *Plugin) sendEnableZoomPMISettingMessage(userID, channelID, rootID string) {
p.postEphemeral(userID, channelID, rootID, "The meeting below is created with an unique meeting ID, to use Personal Meeting ID (PMI) for creating the meeting, you need to `Enable Personal Meeting ID` from your [zoom settings](https://zoom.us/profile/setting).")
}

func (p *Plugin) postEphemeral(userID, channelID, rootID, message string) *model.Post {
post := &model.Post{
UserId: p.botUserID,
ChannelId: channelID,
RootId: rootID,
Message: message,
}

Expand Down Expand Up @@ -758,7 +791,7 @@ func parseOAuthUserState(state string) (userID, channelID string, justConnect bo
return stateComponents[1], stateComponents[2], stateComponents[3] == trueString, nil
}

func (p *Plugin) sendUserSettingForm(userID string, channelID string) error {
func (p *Plugin) sendUserSettingForm(userID, channelID, rootID string) error {
var currentValue string
userPMISettingPref, err := p.getPMISettingData(userID)
if err != nil {
Expand All @@ -778,6 +811,7 @@ func (p *Plugin) sendUserSettingForm(userID string, channelID string) error {
post := &model.Post{
ChannelId: channelID,
UserId: p.botUserID,
RootId: rootID,
}

model.ParseSlackAttachment(post, []*model.SlackAttachment{slackAttachment})
Expand Down
12 changes: 1 addition & 11 deletions webapp/src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,6 @@ export function startMeeting(channelId, rootId = '', force = false, topic = '')
window.open(meetingURL);
}
} catch (error) {
let m = error.message;
if (error.message && error.message[0] === '{') {
const e = JSON.parse(error.message);

// Error is from Zoom API
if (e && e.message) {
m = '\nZoom error: ' + e.message;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm this error may have some useful context for why the meeting couldn't start, like something going on with the user's zoom account. I'm thinking this can stay

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mickmister Reverted the changes to display generic error message

Copy link
Contributor

Choose a reason for hiding this comment

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

It's mainly when we enter the "Error is from Zoom API" block that I'm thinking we should show the provided error. If the others from our server are more cryptic/low-level, then let's only show the error from error.message if the error came from Zoom, meaning we are in the "Error is from Zoom API" block here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mickmister Just to clarify, if we are in the "Error is from Zoom API" block, then we show the error from the API and show a generic error otherwise. Right?

Copy link
Contributor

Choose a reason for hiding this comment

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

@raghavaggarwal2308 Yes that sounds right to me

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mickmister Updated


const post = {
id: 'zoomPlugin' + Date.now(),
create_at: Date.now(),
Expand All @@ -35,7 +25,7 @@ export function startMeeting(channelId, rootId = '', force = false, topic = '')
root_id: rootId,
parent_id: '',
original_id: '',
message: m,
message: 'Error occurred while starting the Zoom meeting.',
type: 'system_ephemeral',
props: {},
hashtags: '',
Expand Down
Loading