-
Notifications
You must be signed in to change notification settings - Fork 69
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
GitHub Issue #170 - Update status when starting/ending a zoom meeting #171
Changes from 14 commits
1c65449
3d3971f
58c00b7
b69fba9
95008bd
fedc699
c92363d
ff80b69
da42848
b844efe
45db34d
756a614
c3b3ac5
ec0a039
077e049
70102a6
52060a2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -25,6 +25,11 @@ import ( | |||||||||||||||||
|
||||||||||||||||||
const ( | ||||||||||||||||||
defaultMeetingTopic = "Zoom Meeting" | ||||||||||||||||||
postActionPath = "/action/status" | ||||||||||||||||||
yes = "yes" | ||||||||||||||||||
no = "no" | ||||||||||||||||||
ContextAccept = "accept" | ||||||||||||||||||
ContextMeetingID = "meetingId" | ||||||||||||||||||
zoomOAuthUserStateLength = 3 | ||||||||||||||||||
) | ||||||||||||||||||
|
||||||||||||||||||
|
@@ -53,11 +58,52 @@ func (p *Plugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Req | |||||||||||||||||
p.completeUserOAuthToZoom(w, r) | ||||||||||||||||||
case "/deauthorization": | ||||||||||||||||||
p.deauthorizeUser(w, r) | ||||||||||||||||||
case postActionPath: | ||||||||||||||||||
p.postActionConfirm(w, r) | ||||||||||||||||||
default: | ||||||||||||||||||
http.NotFound(w, r) | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
func (p *Plugin) postActionConfirm(w http.ResponseWriter, r *http.Request) { | ||||||||||||||||||
userID := r.Header.Get("Mattermost-User-ID") | ||||||||||||||||||
response := model.PostActionIntegrationResponse{} | ||||||||||||||||||
request := model.PostActionIntegrationRequestFromJson(r.Body) | ||||||||||||||||||
accepted := request.Context[ContextAccept].(bool) | ||||||||||||||||||
meetingID := request.Context[ContextMeetingID].(float64) | ||||||||||||||||||
|
||||||||||||||||||
post := &model.Post{} | ||||||||||||||||||
key := fmt.Sprintf("%v_%v", changeStatusKey, userID) | ||||||||||||||||||
|
||||||||||||||||||
message := "Ok, the status won't be updated automatically" | ||||||||||||||||||
changeStatus := no | ||||||||||||||||||
if accepted { | ||||||||||||||||||
changeStatus = yes | ||||||||||||||||||
message = "You have accepted automatic status change. Yay!" | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
appErr := p.API.KVSet(key, []byte(changeStatus)) | ||||||||||||||||||
if appErr != nil { | ||||||||||||||||||
p.API.LogDebug("failed to set status change preference", "error", appErr.Error()) | ||||||||||||||||||
} | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should return here instead of proceeding after the if statement. Maybe have this function return an error, and call this function with a smaller function that handles the error. Any error that occurs in this function should cause the function to return early. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mickmister Here if we don't return early, the next call is to setUserStatus which would automatically send another slack attachment for user confirmation on automatic status change. Won't that be more convenient? Also, if I understand this correctly, you are suggesting we remove the |
||||||||||||||||||
|
||||||||||||||||||
err := p.setUserStatus(userID, int(meetingID), false) | ||||||||||||||||||
if appErr != nil { | ||||||||||||||||||
p.API.LogDebug("failed to change user status", "error", err) | ||||||||||||||||||
} | ||||||||||||||||||
Comment on lines
+90
to
+93
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||
|
||||||||||||||||||
sa := &model.SlackAttachment{ | ||||||||||||||||||
Title: "Status Change", | ||||||||||||||||||
Text: message, | ||||||||||||||||||
} | ||||||||||||||||||
model.ParseSlackAttachment(post, []*model.SlackAttachment{sa}) | ||||||||||||||||||
response.Update = post | ||||||||||||||||||
_, err = w.Write(response.ToJson()) | ||||||||||||||||||
if err != nil { | ||||||||||||||||||
p.API.LogDebug("failed to write response") | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
func (p *Plugin) connectUserToZoom(w http.ResponseWriter, r *http.Request) { | ||||||||||||||||||
userID := r.Header.Get("Mattermost-User-ID") | ||||||||||||||||||
if userID == "" { | ||||||||||||||||||
|
@@ -261,12 +307,17 @@ func (p *Plugin) handleMeetingEnded(w http.ResponseWriter, r *http.Request, webh | |||||||||||||||||
return | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
err := p.setUserStatus(post.UserId, int(meetingID), true) | ||||||||||||||||||
if err != nil { | ||||||||||||||||||
p.API.LogDebug("failed to change user status", "error", err) | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we return the error, and handle it on the caller's side? I think we will need to make a separate small function to do so. |
||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
if appErr = p.deleteMeetingPostID(meetingPostID); appErr != nil { | ||||||||||||||||||
p.API.LogWarn("failed to delete db entry", "error", appErr.Error()) | ||||||||||||||||||
return | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
_, err := w.Write([]byte(post.ToJson())) | ||||||||||||||||||
_, err = w.Write([]byte(post.ToJson())) | ||||||||||||||||||
if err != nil { | ||||||||||||||||||
p.API.LogWarn("failed to write response", "error", err.Error()) | ||||||||||||||||||
} | ||||||||||||||||||
|
@@ -307,6 +358,11 @@ func (p *Plugin) postMeeting(creator *model.User, meetingID int, channelID strin | |||||||||||||||||
return appErr | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
err := p.setUserStatus(creator.Id, meetingID, false) | ||||||||||||||||||
if err != nil { | ||||||||||||||||||
p.API.LogDebug("failed to change user status", "error", err) | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
if appErr = p.storeMeetingPostID(meetingID, createdPost.Id); appErr != nil { | ||||||||||||||||||
p.API.LogDebug("failed to store post id", "err", appErr) | ||||||||||||||||||
} | ||||||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -18,11 +18,15 @@ import ( | |||||||||||||||||||||
) | ||||||||||||||||||||||
|
||||||||||||||||||||||
const ( | ||||||||||||||||||||||
oldStatusKey = "old_status_" | ||||||||||||||||||||||
changeStatusKey = "zoom_status_change" | ||||||||||||||||||||||
|
||||||||||||||||||||||
botUserName = "zoom" | ||||||||||||||||||||||
botDisplayName = "Zoom" | ||||||||||||||||||||||
botDescription = "Created by the Zoom plugin." | ||||||||||||||||||||||
|
||||||||||||||||||||||
zoomProviderName = "Zoom" | ||||||||||||||||||||||
pluginURLPath = "/plugins/" + botUserName | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we compute the URL in a separate function like this? https://github.com/mattermost/mattermost-plugin-jira/blob/dc4266c61871200cc79d5a9feb97676aa330d50b/server/plugin.go#L396 |
||||||||||||||||||||||
) | ||||||||||||||||||||||
|
||||||||||||||||||||||
type Plugin struct { | ||||||||||||||||||||||
|
@@ -185,3 +189,117 @@ func (p *Plugin) sendDirectMessage(userID string, message string) error { | |||||||||||||||||||||
_, err = p.API.CreatePost(post) | ||||||||||||||||||||||
return err | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
func (p *Plugin) setUserStatus(userID string, meetingID int, meetingEnd bool) error { | ||||||||||||||||||||||
storedStatusPref, appErr := p.API.KVGet(fmt.Sprintf("%v_%v", changeStatusKey, userID)) | ||||||||||||||||||||||
if appErr != nil { | ||||||||||||||||||||||
p.API.LogDebug("Could not get stored status preference from KV ", appErr) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
Comment on lines
+216
to
+218
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we return here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, we should. |
||||||||||||||||||||||
|
||||||||||||||||||||||
if storedStatusPref == nil { | ||||||||||||||||||||||
err := p.sendStatusChangeAttachment(userID, p.botUserID, meetingID) | ||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||
p.API.LogDebug("could not send status change attachment ", "error", err) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
Comment on lines
+220
to
+225
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we supposed to return in this if block? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can return here but the function would anyway return in the next statement. I will add return here to be clear. |
||||||||||||||||||||||
|
||||||||||||||||||||||
if string(storedStatusPref) != yes { | ||||||||||||||||||||||
return nil | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
statusKey := fmt.Sprintf("%v%v", oldStatusKey, meetingID) | ||||||||||||||||||||||
if meetingEnd { | ||||||||||||||||||||||
statusVal, err := p.API.KVGet(statusKey) | ||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||
p.API.LogDebug("Could not get old status from KVStore", "err", appErr.Error()) | ||||||||||||||||||||||
return err | ||||||||||||||||||||||
} | ||||||||||||||||||||||
Comment on lines
+233
to
+237
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||
|
||||||||||||||||||||||
newStatus := string(statusVal) | ||||||||||||||||||||||
if newStatus == "" { | ||||||||||||||||||||||
newStatus = model.STATUS_ONLINE | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
_, appErr = p.API.UpdateUserStatus(userID, newStatus) | ||||||||||||||||||||||
if appErr != nil { | ||||||||||||||||||||||
p.API.LogDebug("Could not get update status", "err", appErr.Error()) | ||||||||||||||||||||||
return appErr | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
return nil | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
currentStatus, appErr := p.API.GetUserStatus(userID) | ||||||||||||||||||||||
if appErr != nil { | ||||||||||||||||||||||
p.API.LogDebug("Failed to update user status", "err", appErr) | ||||||||||||||||||||||
return appErr | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
oldStatus := "" | ||||||||||||||||||||||
if currentStatus.Manual { | ||||||||||||||||||||||
oldStatus = currentStatus.Status | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
appErr = p.API.KVSetWithExpiry(statusKey, []byte(oldStatus), meetingPostIDTTL) | ||||||||||||||||||||||
if appErr != nil { | ||||||||||||||||||||||
p.API.LogDebug("failed to store old status", "err", appErr) | ||||||||||||||||||||||
return appErr | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
_, appErr = p.API.UpdateUserStatus(userID, model.STATUS_DND) | ||||||||||||||||||||||
if appErr != nil { | ||||||||||||||||||||||
p.API.LogDebug("Failed to update user status", "err", appErr) | ||||||||||||||||||||||
return appErr | ||||||||||||||||||||||
Comment on lines
+272
to
+273
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we use |
||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
return nil | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
func (p *Plugin) sendStatusChangeAttachment(userID, botUserID string, meetingID int) error { | ||||||||||||||||||||||
url := pluginURLPath + postActionPath | ||||||||||||||||||||||
actionYes := &model.PostAction{ | ||||||||||||||||||||||
Name: "Yes", | ||||||||||||||||||||||
Integration: &model.PostActionIntegration{ | ||||||||||||||||||||||
URL: url, | ||||||||||||||||||||||
Context: map[string]interface{}{ | ||||||||||||||||||||||
ContextAccept: true, | ||||||||||||||||||||||
ContextMeetingID: meetingID, | ||||||||||||||||||||||
}, | ||||||||||||||||||||||
}, | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
actionNo := &model.PostAction{ | ||||||||||||||||||||||
Name: "No", | ||||||||||||||||||||||
Integration: &model.PostActionIntegration{ | ||||||||||||||||||||||
URL: url, | ||||||||||||||||||||||
Context: map[string]interface{}{ | ||||||||||||||||||||||
ContextAccept: false, | ||||||||||||||||||||||
ContextMeetingID: meetingID, | ||||||||||||||||||||||
}, | ||||||||||||||||||||||
}, | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
sa := &model.SlackAttachment{ | ||||||||||||||||||||||
Title: "Status change", | ||||||||||||||||||||||
Text: "Allow Zoom plugin to automatically change status", | ||||||||||||||||||||||
Actions: []*model.PostAction{actionYes, actionNo}, | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
attachmentPost := model.Post{} | ||||||||||||||||||||||
model.ParseSlackAttachment(&attachmentPost, []*model.SlackAttachment{sa}) | ||||||||||||||||||||||
directChannel, appErr := p.API.GetDirectChannel(userID, botUserID) | ||||||||||||||||||||||
larkox marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||
if appErr != nil { | ||||||||||||||||||||||
p.API.LogDebug("Create Attachment: ", appErr) | ||||||||||||||||||||||
return appErr | ||||||||||||||||||||||
Comment on lines
+313
to
+314
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||
} | ||||||||||||||||||||||
attachmentPost.ChannelId = directChannel.Id | ||||||||||||||||||||||
attachmentPost.UserId = botUserID | ||||||||||||||||||||||
|
||||||||||||||||||||||
_, appErr = p.API.CreatePost(&attachmentPost) | ||||||||||||||||||||||
if appErr != nil { | ||||||||||||||||||||||
p.API.LogDebug("Create Attachment: ", appErr) | ||||||||||||||||||||||
return appErr | ||||||||||||||||||||||
Comment on lines
+321
to
+322
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
return nil | ||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this message convey the fact of "Your status will be updated" or "Your status will not be updated"?