From 85324b5117ae85c98efd4fc3523e106fa9858040 Mon Sep 17 00:00:00 2001 From: Vinod Thorat Date: Mon, 22 May 2023 22:46:16 +0530 Subject: [PATCH 1/5] Added support for removing any todo --- .gitignore | 1 + server/command.go | 63 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 36d3a9c3..da90d986 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ dist .DS_Store +node_modules diff --git a/server/command.go b/server/command.go index 03271397..ae9d96c3 100644 --- a/server/command.go +++ b/server/command.go @@ -125,6 +125,8 @@ func (p *Plugin) ExecuteCommand(c *plugin.Context, args *model.CommandArgs) (*mo handler = p.runListCommand case "pop": handler = p.runPopCommand + case "remove": + handler = p.runRemoveCommand case "send": handler = p.runSendCommand case "settings": @@ -327,6 +329,61 @@ func (p *Plugin) runPopCommand(args []string, extra *model.CommandArgs) (bool, e return false, nil } +func (p *Plugin) runRemoveCommand(args []string, extra *model.CommandArgs) (bool, error) { + message := strings.Join(args, " ") + listID := MyListKey + issueList, err := p.listManager.GetIssueList(extra.UserId, listID) + if err != nil { + return false, err + } + + var issueID string + for _, issue := range issueList { + if issue.Message == message { + issueID = issue.ID + } + } + + if issueID == "" { + p.postCommandResponse(extra, "Can't find the todo in list") + return false, nil + } + + issue, foreignID, _, _, err := p.listManager.RemoveIssue(extra.UserId, issueID) + if err != nil { + return false, err + } + + userName := p.listManager.GetUserName(extra.UserId) + + if foreignID != "" { + p.sendRefreshEvent(foreignID, []string{OutListKey}) + + message := fmt.Sprintf("@%s removed a Todo you sent: %s", userName, issue.Message) + p.PostBotDM(foreignID, message) + } + + p.sendRefreshEvent(extra.UserId, []string{MyListKey}) + + responseMessage := fmt.Sprintf("Removed given Todo (%s).", issue.Message) + + replyMessage := fmt.Sprintf("@%s removed a todo attached to this thread", userName) + p.postReplyIfNeeded(issue.PostID, replyMessage, issue.Message) + + issues, err := p.listManager.GetIssueList(extra.UserId, MyListKey) + if err != nil { + p.API.LogError(err.Error()) + p.postCommandResponse(extra, responseMessage) + return false, nil + } + + responseMessage += listHeaderMessage + responseMessage += issuesListToString(issues) + p.postCommandResponse(extra, responseMessage) + + return false, nil +} + func (p *Plugin) runSettingsCommand(args []string, extra *model.CommandArgs) (bool, error) { const ( on = "on" @@ -418,7 +475,7 @@ func (p *Plugin) runSettingsCommand(args []string, extra *model.CommandArgs) (bo } func getAutocompleteData() *model.AutocompleteData { - todo := model.NewAutocompleteData("todo", "[command]", "Available commands: list, add, pop, send, settings, help") + todo := model.NewAutocompleteData("todo", "[command]", "Available commands: list, add, pop, remove, send, settings, help") add := model.NewAutocompleteData("add", "[message]", "Adds a Todo") add.AddTextArgument("E.g. be awesome", "[message]", "") @@ -440,6 +497,10 @@ func getAutocompleteData() *model.AutocompleteData { pop := model.NewAutocompleteData("pop", "", "Removes the Todo issue at the top of the list") todo.AddCommand(pop) + remove := model.NewAutocompleteData("remove", "", "Removes the given Todo") + remove.AddTextArgument("E.g. be average", "[message]", "") + todo.AddCommand(remove) + send := model.NewAutocompleteData("send", "[user] [todo]", "Sends a Todo to a specified user") send.AddTextArgument("Whom to send", "[@awesomePerson]", "") send.AddTextArgument("Todo message", "[message]", "") From eb70898d5a5de4509f373cd0610b140dc7dbf433 Mon Sep 17 00:00:00 2001 From: Vinod Thorat Date: Thu, 1 Jun 2023 11:31:02 +0530 Subject: [PATCH 2/5] Added autocomplete for delete command --- .gitignore | 1 - server/command.go | 38 +++++++++++------------ server/constants.go | 6 ++++ server/plugin.go | 74 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 22 deletions(-) create mode 100644 server/constants.go diff --git a/.gitignore b/.gitignore index da90d986..36d3a9c3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ dist .DS_Store -node_modules diff --git a/server/command.go b/server/command.go index ae9d96c3..677c7249 100644 --- a/server/command.go +++ b/server/command.go @@ -125,8 +125,8 @@ func (p *Plugin) ExecuteCommand(c *plugin.Context, args *model.CommandArgs) (*mo handler = p.runListCommand case "pop": handler = p.runPopCommand - case "remove": - handler = p.runRemoveCommand + case "delete": + handler = p.runDeleteCommand case "send": handler = p.runSendCommand case "settings": @@ -329,27 +329,19 @@ func (p *Plugin) runPopCommand(args []string, extra *model.CommandArgs) (bool, e return false, nil } -func (p *Plugin) runRemoveCommand(args []string, extra *model.CommandArgs) (bool, error) { - message := strings.Join(args, " ") - listID := MyListKey - issueList, err := p.listManager.GetIssueList(extra.UserId, listID) - if err != nil { - return false, err - } - - var issueID string - for _, issue := range issueList { - if issue.Message == message { - issueID = issue.ID - } +func (p *Plugin) runDeleteCommand(args []string, extra *model.CommandArgs) (bool, error) { + if args[0] != "--todo" { + p.postCommandResponse(extra, "Invalid command provided") + return false, nil } + todoID := args[1] - if issueID == "" { - p.postCommandResponse(extra, "Can't find the todo in list") + if todoID == "" { + p.postCommandResponse(extra, "Empty todoID provided") return false, nil } - issue, foreignID, _, _, err := p.listManager.RemoveIssue(extra.UserId, issueID) + issue, foreignID, _, _, err := p.listManager.RemoveIssue(extra.UserId, todoID) if err != nil { return false, err } @@ -497,9 +489,9 @@ func getAutocompleteData() *model.AutocompleteData { pop := model.NewAutocompleteData("pop", "", "Removes the Todo issue at the top of the list") todo.AddCommand(pop) - remove := model.NewAutocompleteData("remove", "", "Removes the given Todo") - remove.AddTextArgument("E.g. be average", "[message]", "") - todo.AddCommand(remove) + delete := model.NewAutocompleteData("delete", "--todo id", "Deletes the given Todo") + delete.AddNamedDynamicListArgument("todo", "--todo todoID", getAutocompletePath(AutocompletePathRemoveTodoSuggestions), true) + todo.AddCommand(delete) send := model.NewAutocompleteData("send", "[user] [todo]", "Sends a Todo to a specified user") send.AddTextArgument("Whom to send", "[@awesomePerson]", "") @@ -527,3 +519,7 @@ func getAutocompleteData() *model.AutocompleteData { todo.AddCommand(help) return todo } + +func getAutocompletePath(path string) string { + return "plugins/" + manifest.Id + "/autocomplete" + path +} diff --git a/server/constants.go b/server/constants.go new file mode 100644 index 00000000..a87564ba --- /dev/null +++ b/server/constants.go @@ -0,0 +1,6 @@ +package main + +const ( + AutocompletePath = "/autocomplete" + AutocompletePathRemoveTodoSuggestions = "/getRemoveTodoSuggestions" +) diff --git a/server/plugin.go b/server/plugin.go index 0617eaef..9cb28c21 100644 --- a/server/plugin.go +++ b/server/plugin.go @@ -127,6 +127,8 @@ func (p *Plugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Req p.handleEdit(w, r) case "/change_assignment": p.handleChangeAssignment(w, r) + case AutocompletePath + AutocompletePathRemoveTodoSuggestions: + p.getDeleteTodoSuggestions(w, r) default: http.NotFound(w, r) } @@ -669,3 +671,75 @@ func (p *Plugin) handleErrorWithCode(w http.ResponseWriter, code int, errTitle s }) _, _ = w.Write(b) } + +func (p *Plugin) getDeleteTodoSuggestions(w http.ResponseWriter, r *http.Request) { + userID := r.Header.Get("Mattermost-User-ID") + if userID == "" { + http.Error(w, "Not authorized", http.StatusUnauthorized) + return + } + + listInput := r.URL.Query().Get("list") + listID := MyListKey + switch listInput { + case OutFlag: + listID = OutListKey + case InFlag: + listID = InListKey + } + + issues, err := p.listManager.GetIssueList(userID, listID) + if err != nil { + p.API.LogError("Unable to get issues for user err=" + err.Error()) + p.handleErrorWithCode(w, http.StatusInternalServerError, "Unable to get issues for user", err) + return + } + + if len(issues) > 0 && r.URL.Query().Get("reminder") == "true" && p.getReminderPreference(userID) { + var lastReminderAt int64 + lastReminderAt, err = p.getLastReminderTimeForUser(userID) + if err != nil { + p.API.LogError("Unable to send reminder err=" + err.Error()) + p.handleErrorWithCode(w, http.StatusInternalServerError, "Unable to send reminder", err) + return + } + + var timezone *time.Location + offset, _ := strconv.Atoi(r.Header.Get("X-Timezone-Offset")) + timezone = time.FixedZone("local", -60*offset) + + // Post reminder message if it's the next day and been more than an hour since the last post + now := model.GetMillis() + nt := time.Unix(now/1000, 0).In(timezone) + lt := time.Unix(lastReminderAt/1000, 0).In(timezone) + if nt.Sub(lt).Hours() >= 1 && (nt.Day() != lt.Day() || nt.Month() != lt.Month() || nt.Year() != lt.Year()) { + p.PostBotDM(userID, "Daily Reminder:\n\n"+issuesListToString(issues)) + p.trackDailySummary(userID) + err = p.saveLastReminderTimeForUser(userID) + if err != nil { + p.API.LogError("Unable to save last reminder for user err=" + err.Error()) + } + } + } + out := []model.AutocompleteListItem{} + for _, issue := range issues { + s := model.AutocompleteListItem{ + Item: issue.ID, + Hint: issue.Message, + HelpText: issue.Description, + } + out = append(out, s) + } + + outJSON, err := json.Marshal(out) + if err != nil { + p.API.LogError("Unable marhsal issues list to json err=" + err.Error()) + p.handleErrorWithCode(w, http.StatusInternalServerError, "Unable marhsal issues list to json", err) + return + } + + _, err = w.Write(outJSON) + if err != nil { + p.API.LogError("Unable to write json response err=" + err.Error()) + } +} From 96b524008c4230c80413a8a449757266bd6d11f8 Mon Sep 17 00:00:00 2001 From: Vinod Thorat Date: Thu, 1 Jun 2023 12:01:58 +0530 Subject: [PATCH 3/5] rename delete to remove --- server/command.go | 14 +++++++------- server/plugin.go | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/server/command.go b/server/command.go index 677c7249..9e109cfb 100644 --- a/server/command.go +++ b/server/command.go @@ -125,8 +125,8 @@ func (p *Plugin) ExecuteCommand(c *plugin.Context, args *model.CommandArgs) (*mo handler = p.runListCommand case "pop": handler = p.runPopCommand - case "delete": - handler = p.runDeleteCommand + case "remove": + handler = p.runRemoveCommand case "send": handler = p.runSendCommand case "settings": @@ -329,13 +329,13 @@ func (p *Plugin) runPopCommand(args []string, extra *model.CommandArgs) (bool, e return false, nil } -func (p *Plugin) runDeleteCommand(args []string, extra *model.CommandArgs) (bool, error) { +func (p *Plugin) runRemoveCommand(args []string, extra *model.CommandArgs) (bool, error) { if args[0] != "--todo" { p.postCommandResponse(extra, "Invalid command provided") return false, nil } - todoID := args[1] + todoID := args[1] if todoID == "" { p.postCommandResponse(extra, "Empty todoID provided") return false, nil @@ -489,9 +489,9 @@ func getAutocompleteData() *model.AutocompleteData { pop := model.NewAutocompleteData("pop", "", "Removes the Todo issue at the top of the list") todo.AddCommand(pop) - delete := model.NewAutocompleteData("delete", "--todo id", "Deletes the given Todo") - delete.AddNamedDynamicListArgument("todo", "--todo todoID", getAutocompletePath(AutocompletePathRemoveTodoSuggestions), true) - todo.AddCommand(delete) + remove := model.NewAutocompleteData("remove", "--todo id", "Removes the given Todo") + remove.AddNamedDynamicListArgument("todo", "--todo todoID", getAutocompletePath(AutocompletePathRemoveTodoSuggestions), true) + todo.AddCommand(remove) send := model.NewAutocompleteData("send", "[user] [todo]", "Sends a Todo to a specified user") send.AddTextArgument("Whom to send", "[@awesomePerson]", "") diff --git a/server/plugin.go b/server/plugin.go index 9cb28c21..5a994412 100644 --- a/server/plugin.go +++ b/server/plugin.go @@ -128,7 +128,7 @@ func (p *Plugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Req case "/change_assignment": p.handleChangeAssignment(w, r) case AutocompletePath + AutocompletePathRemoveTodoSuggestions: - p.getDeleteTodoSuggestions(w, r) + p.getRemoveTodoSuggestions(w, r) default: http.NotFound(w, r) } @@ -672,7 +672,7 @@ func (p *Plugin) handleErrorWithCode(w http.ResponseWriter, code int, errTitle s _, _ = w.Write(b) } -func (p *Plugin) getDeleteTodoSuggestions(w http.ResponseWriter, r *http.Request) { +func (p *Plugin) getRemoveTodoSuggestions(w http.ResponseWriter, r *http.Request) { userID := r.Header.Get("Mattermost-User-ID") if userID == "" { http.Error(w, "Not authorized", http.StatusUnauthorized) From 1e612b761faf52cd2ca36f19cde798f622ead62a Mon Sep 17 00:00:00 2001 From: Vinod Thorat Date: Thu, 1 Jun 2023 12:24:48 +0530 Subject: [PATCH 4/5] removed unnecessary code from getRemoveTodoSuggestions --- server/plugin.go | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/server/plugin.go b/server/plugin.go index 5a994412..50ee9fc8 100644 --- a/server/plugin.go +++ b/server/plugin.go @@ -678,16 +678,7 @@ func (p *Plugin) getRemoveTodoSuggestions(w http.ResponseWriter, r *http.Request http.Error(w, "Not authorized", http.StatusUnauthorized) return } - - listInput := r.URL.Query().Get("list") listID := MyListKey - switch listInput { - case OutFlag: - listID = OutListKey - case InFlag: - listID = InListKey - } - issues, err := p.listManager.GetIssueList(userID, listID) if err != nil { p.API.LogError("Unable to get issues for user err=" + err.Error()) @@ -695,32 +686,6 @@ func (p *Plugin) getRemoveTodoSuggestions(w http.ResponseWriter, r *http.Request return } - if len(issues) > 0 && r.URL.Query().Get("reminder") == "true" && p.getReminderPreference(userID) { - var lastReminderAt int64 - lastReminderAt, err = p.getLastReminderTimeForUser(userID) - if err != nil { - p.API.LogError("Unable to send reminder err=" + err.Error()) - p.handleErrorWithCode(w, http.StatusInternalServerError, "Unable to send reminder", err) - return - } - - var timezone *time.Location - offset, _ := strconv.Atoi(r.Header.Get("X-Timezone-Offset")) - timezone = time.FixedZone("local", -60*offset) - - // Post reminder message if it's the next day and been more than an hour since the last post - now := model.GetMillis() - nt := time.Unix(now/1000, 0).In(timezone) - lt := time.Unix(lastReminderAt/1000, 0).In(timezone) - if nt.Sub(lt).Hours() >= 1 && (nt.Day() != lt.Day() || nt.Month() != lt.Month() || nt.Year() != lt.Year()) { - p.PostBotDM(userID, "Daily Reminder:\n\n"+issuesListToString(issues)) - p.trackDailySummary(userID) - err = p.saveLastReminderTimeForUser(userID) - if err != nil { - p.API.LogError("Unable to save last reminder for user err=" + err.Error()) - } - } - } out := []model.AutocompleteListItem{} for _, issue := range issues { s := model.AutocompleteListItem{ From 04b917ff76de7386f5f94522d7ed5b5ba55b6997 Mon Sep 17 00:00:00 2001 From: Vinod Thorat Date: Wed, 7 Jun 2023 07:58:37 +0530 Subject: [PATCH 5/5] Addressed comments --- server/command.go | 2 +- server/plugin.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/server/command.go b/server/command.go index 9e109cfb..71d0e41e 100644 --- a/server/command.go +++ b/server/command.go @@ -330,7 +330,7 @@ func (p *Plugin) runPopCommand(args []string, extra *model.CommandArgs) (bool, e } func (p *Plugin) runRemoveCommand(args []string, extra *model.CommandArgs) (bool, error) { - if args[0] != "--todo" { + if len(args) < 2 || args[0] != "--todo" { p.postCommandResponse(extra, "Invalid command provided") return false, nil } diff --git a/server/plugin.go b/server/plugin.go index 50ee9fc8..269c8f69 100644 --- a/server/plugin.go +++ b/server/plugin.go @@ -318,8 +318,8 @@ func (p *Plugin) handleList(w http.ResponseWriter, r *http.Request) { issuesJSON, err := json.Marshal(issues) if err != nil { - p.API.LogError("Unable marhsal issues list to json err=" + err.Error()) - p.handleErrorWithCode(w, http.StatusInternalServerError, "Unable marhsal issues list to json", err) + p.API.LogError("Unable marshal issues list to json err=" + err.Error()) + p.handleErrorWithCode(w, http.StatusInternalServerError, "Unable marshal issues list to json", err) return } @@ -698,8 +698,8 @@ func (p *Plugin) getRemoveTodoSuggestions(w http.ResponseWriter, r *http.Request outJSON, err := json.Marshal(out) if err != nil { - p.API.LogError("Unable marhsal issues list to json err=" + err.Error()) - p.handleErrorWithCode(w, http.StatusInternalServerError, "Unable marhsal issues list to json", err) + p.API.LogError("Unable marshal issues list to json err=" + err.Error()) + p.handleErrorWithCode(w, http.StatusInternalServerError, "Unable marshal issues list to json", err) return }