Mercurial > lbo > hg > goe_bot
changeset 55:c1fc20f0ceaf
Add functionality for listing and deleting reminders
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Sat, 10 Dec 2016 21:15:28 +0100 |
parents | b56ef3fe4bff |
children | 22ac80ac041b |
files | handler_remind.go handler_todo.go handlers.go remind.go sql/remind.go |
diffstat | 5 files changed, 128 insertions(+), 17 deletions(-) [+] |
line wrap: on
line diff
--- a/handler_remind.go Sat Dec 10 20:35:48 2016 +0100 +++ b/handler_remind.go Sat Dec 10 21:15:28 2016 +0100 @@ -119,14 +119,17 @@ ) // Handler for /remind messages -// Allowed formats: hh:mm (today), +XXs, +XXm, +XXh, +XXd, "yyyy-mm-dd hh:mm", -// {Mo,Di,Mi,Do,Fr,Sa,So} hh:mm, +// If no time or text is given, lists the reminders with option to cancel func reminderHandler(ctx context.Context, msg message) (replyContent, error) { - // TODO: This doesn't work with multi-part formats like dateRE or dateOfWeekRE! - // first part is time + // list reminders + if msg.Text == "" { + return listReminders(ctx, msg.Chat.ID) + } + + // restStart is the index of the first character of the remaining text alertTime, restStart := parseReminderString(strings.Trim(msg.Text, " ")) - if restStart == 0 || alertTime.IsZero() { + if alertTime.IsZero() { return replyContent{text: formatHelpText}, nil } @@ -152,5 +155,66 @@ return replyContent{text: "_Ich konnte leider keine Erinnerung setzen._"}, nil } - return replyContent{text: fmt.Sprintf("*✓* Erinnerung #%d in %v", id, alertTime.Sub(time.Now()))}, nil + remaining := alertTime.Sub(time.Now()) + hours := uint64(remaining.Seconds()) / 3600 + minutes := (uint64(remaining.Seconds()) % 3600) / 60 + return replyContent{text: fmt.Sprintf("*✓* Erinnerung #%d in %d:%02d", id, hours, minutes)}, nil } + +func listReminders(ctx context.Context, chatID int64) (replyContent, error) { + db, err := backend.Reminders() + + if err != nil { + log.Println("Couldn't get Reminders object:", err) + return replyContent{text: "_Konnte mich nicht mit der Datenbank verbinden..._"}, nil + } + + rms, err := db.ListActives(chatID) + + if err != nil { + log.Println("Couldn't get active reminders:", err, chatID) + return replyContent{text: "_Konnte keine Erinnerungen finden..._"}, nil + } + + // create buttons + buttons := make([][]inlineKeyboardButton, len(rms)) + + for i := range rms { + t := rms[i].Due.Add(time.Duration(*flagLocalOffset) * time.Second) + date := fmt.Sprintf("%02d-%02d-%02d %02d:%02d", t.Year(), int(t.Month()), t.Day(), t.Hour(), t.Minute()) + buttons[i] = []inlineKeyboardButton{inlineKeyboardButton{ + Text: fmt.Sprintf("[%s] %s (%s)", date, rms[i].Text, rms[i].Owner), + Callback_Data: fmt.Sprintf("%s:%d", cancelReminderCallback, rms[i].ReminderID), + }} + } + + return replyContent{text: "Erinnerung anklicken, um sie zu löschen", buttons: inlineKeyboardMarkup{Inline_Keyboard: buttons}}, nil +} + +func deleteReminderCallback(ctx context.Context, token string, cbq callbackQuery) (replyContent, error) { + log.Println("Callback for removing reminder", token) + + db, err := backend.Reminders() + + if err != nil { + log.Println("Couldn't get Reminders object:", err) + return replyContent{text: "_Konnte mich nicht mit der Datenbank verbinden..._"}, nil + } + + id, err := strconv.ParseUint(token, 10, 32) + + if err != nil { + log.Println("Bad token", token) + return replyContent{text: "_Interner Fehler D:_"}, nil + } + + n, err := db.MarkRemindersDone([]uint{uint(id)}) + + if err != nil { + return replyContent{text: "_Konnte Erinnerung nicht entfernen..._"}, nil + } else if n == 0 { + return replyContent{text: "Diese Erinnerung wurde bereits gelöscht!"}, nil + } + + return replyContent{text: fmt.Sprintf("*✓* Erinnerung #%d gelöscht", id)}, nil +}
--- a/handler_todo.go Sat Dec 10 20:35:48 2016 +0100 +++ b/handler_todo.go Sat Dec 10 21:15:28 2016 +0100 @@ -30,7 +30,7 @@ return replyContent{text: "_Erstellung fehlgeschlagen:_ " + err.Error()}, err } - return replyContent{text: fmt.Sprintf("Aufgabe #%d erstellt", id)}, nil + return replyContent{text: fmt.Sprintf("*✓* Aufgabe #%d erstellt", id)}, nil } else { // Query open items todos, err := todo.GetOpenTodos("") // default list
--- a/handlers.go Sat Dec 10 20:35:48 2016 +0100 +++ b/handlers.go Sat Dec 10 21:15:28 2016 +0100 @@ -17,7 +17,8 @@ statusCmd = "status" todoCmd = "todo" - todoDoneCallback = "markdone" + todoDoneCallback = "markdone" + cancelReminderCallback = "cancelrm" ) type handler struct { @@ -38,7 +39,8 @@ } callbackHandlers = map[string]callbackHandler{ - todoDoneCallback: todoDoneHandler, + todoDoneCallback: todoDoneHandler, + cancelReminderCallback: deleteReminderCallback, } )
--- a/remind.go Sat Dec 10 20:35:48 2016 +0100 +++ b/remind.go Sat Dec 10 21:15:28 2016 +0100 @@ -51,7 +51,9 @@ return err } - return reminders.MarkRemindersDone([]uint{rm.ReminderID}) + _, err = reminders.MarkRemindersDone([]uint{rm.ReminderID}) + + return err } type reminders struct {
--- a/sql/remind.go Sat Dec 10 20:35:48 2016 +0100 +++ b/sql/remind.go Sat Dec 10 21:15:28 2016 +0100 @@ -18,8 +18,12 @@ // Parameters: n/a // Returns: id INTEGER, due TIMESTAMP, owner TEXT, description TEXT, chat_id INTEGER, orig_msg_id INTEGER selectDueReminders = `SELECT id, due, owner, description, chat_id, orig_msg_id FROM reminders WHERE NOT done AND now() > due ORDER BY due ASC` + // Parameter: 1 = chat ID + // Returns: id INTEGER, due TIMESTAMP, owner TEXT, description TEXT + listFutureReminders = `SELECT id, due, owner, description FROM reminders WHERE NOT done AND due > now() AND chat_id = $1 ORDER BY due ASC` // Parameters: 1 = reminder ID - markReminderDone = `UPDATE reminders SET done = true WHERE id = $1` + // Returns: n/a + markReminderDone = `UPDATE reminders SET done = true WHERE id = $1 AND NOT done` ) type Reminder struct { @@ -48,7 +52,8 @@ return r.db.prewarm([]string{ insertReminder, selectDueReminders, - markReminderDone}) + markReminderDone, + listFutureReminders}) } func (r Reminders) Remove(reminderID uint) error { @@ -66,6 +71,38 @@ } } +func (r Reminders) ListActives(chatID int64) ([]Reminder, error) { + if stmt, ok := r.db.prepared[listFutureReminders]; ok { + rows, err := stmt.Query(chatID) + + if err != nil { + log.Println("Couldn't query due reminders:", err) + return nil, err + } + + defer rows.Close() + + rm := make([]Reminder, 0, 16) + + for rows.Next() { + reminder := Reminder{} + err := rows.Scan(&reminder.ReminderID, &reminder.Due, &reminder.Owner, &reminder.Text) + + if err != nil { + log.Println("Couldn't scan reminder row:", err) + return nil, err + } + + rm = append(rm, reminder) + } + + return rm, nil + } else { + log.Println("Couldn't find prepared statement for", selectDueReminders) + return nil, errors.New("couldn't find prepared statement") + } +} + func (r Reminders) InsertReminder(rm Reminder) (uint, error) { if stmt, ok := r.db.prepared[insertReminder]; ok { row := stmt.QueryRow(pq.FormatTimestamp(rm.Due), rm.Text, rm.Owner, rm.ChatID, rm.ReplyTo) @@ -120,20 +157,26 @@ } // If this returns an error, it can be called with the same IDs again. -func (r Reminders) MarkRemindersDone(ids []uint) error { +// Returns how many rows were affected. +func (r Reminders) MarkRemindersDone(ids []uint) (int, error) { if stmt, ok := r.db.prepared[markReminderDone]; ok { + var i int64 for _, id := range ids { - _, err := stmt.Exec(id) + rows, err := stmt.Exec(id) if err != nil { log.Println("Couldn't mark reminder", id, "as done:", err) - return err + return int(i), err + } + + if n, err := rows.RowsAffected(); err == nil { + i += n } } - return nil + return int(i), nil } else { log.Println("Couldn't find prepared statement for", markReminderDone) - return errors.New("couldn't find prepared statement") + return 0, errors.New("couldn't find prepared statement") } }