package reminder import ( "fmt" "regexp" "strconv" "strings" "time" "git.nakama.town/fmartingr/butterrobot/internal/model" "git.nakama.town/fmartingr/butterrobot/internal/plugin" ) // Duration regex patterns to match reminders var ( remindMePattern = regexp.MustCompile(`(?i)^!remindme\s(\d+)(y|mo|d|h|m|s)$`) ) // ReminderCreator is an interface for creating reminders type ReminderCreator interface { CreateReminder(platform, channelID, messageID, replyToID, userID, username, content string, triggerAt time.Time) (*model.Reminder, error) } // Reminder is a plugin that sets reminders for messages type Reminder struct { plugin.BasePlugin creator ReminderCreator } // New creates a new Reminder plugin func New(creator ReminderCreator) *Reminder { return &Reminder{ BasePlugin: plugin.BasePlugin{ ID: "reminder.remindme", Name: "Remind Me", Help: "Reply to a message with `!remindme ` to set a reminder (e.g., `!remindme 2d` for 2 days, `!remindme 1y` for 1 year).", ConfigRequired: false, }, creator: creator, } } // OnMessage processes incoming messages func (r *Reminder) OnMessage(msg *model.Message, config map[string]interface{}) []*model.Message { // Only process replies to messages if msg.ReplyTo == "" { return nil } // Check if the message is a reminder command match := remindMePattern.FindStringSubmatch(msg.Text) if match == nil { return nil } // Parse the duration amount, err := strconv.Atoi(match[1]) if err != nil { return []*model.Message{ { Text: "Invalid duration format. Please use a number followed by y (years), mo (months), d (days), h (hours), m (minutes), or s (seconds).", Chat: msg.Chat, Channel: msg.Channel, Author: "bot", FromBot: true, Date: time.Now(), ReplyTo: msg.ID, }, } } // Calculate the trigger time var duration time.Duration unit := match[2] switch strings.ToLower(unit) { case "y": duration = time.Duration(amount) * 365 * 24 * time.Hour case "mo": duration = time.Duration(amount) * 30 * 24 * time.Hour case "d": duration = time.Duration(amount) * 24 * time.Hour case "h": duration = time.Duration(amount) * time.Hour case "m": duration = time.Duration(amount) * time.Minute case "s": duration = time.Duration(amount) * time.Second default: return []*model.Message{ { Text: "Invalid duration unit. Please use y (years), mo (months), d (days), h (hours), m (minutes), or s (seconds).", Chat: msg.Chat, Channel: msg.Channel, Author: "bot", FromBot: true, Date: time.Now(), ReplyTo: msg.ID, }, } } triggerAt := time.Now().Add(duration) // Determine the username for the reminder username := msg.Author if username == "" { // Try to extract username from message raw data if authorData, ok := msg.Raw["author"].(map[string]interface{}); ok { if name, ok := authorData["username"].(string); ok { username = name } else if name, ok := authorData["name"].(string); ok { username = name } } } // Create the reminder _, err = r.creator.CreateReminder( msg.Channel.Platform, msg.Chat, msg.ID, msg.ReplyTo, msg.Author, username, "", // No additional content for now triggerAt, ) if err != nil { return []*model.Message{ { Text: fmt.Sprintf("Failed to create reminder: %v", err), Chat: msg.Chat, Channel: msg.Channel, Author: "bot", FromBot: true, Date: time.Now(), ReplyTo: msg.ID, }, } } // Format the acknowledgment message var confirmText string switch strings.ToLower(unit) { case "y": confirmText = fmt.Sprintf("I'll remind you about this message in %d year(s) on %s", amount, triggerAt.Format("Mon, Jan 2, 2006 at 15:04")) case "mo": confirmText = fmt.Sprintf("I'll remind you about this message in %d month(s) on %s", amount, triggerAt.Format("Mon, Jan 2 at 15:04")) case "d": confirmText = fmt.Sprintf("I'll remind you about this message in %d day(s) on %s", amount, triggerAt.Format("Mon, Jan 2 at 15:04")) case "h": confirmText = fmt.Sprintf("I'll remind you about this message in %d hour(s) at %s", amount, triggerAt.Format("15:04")) case "m": confirmText = fmt.Sprintf("I'll remind you about this message in %d minute(s) at %s", amount, triggerAt.Format("15:04")) case "s": confirmText = fmt.Sprintf("I'll remind you about this message in %d second(s)", amount) } return []*model.Message{ { Text: confirmText, Chat: msg.Chat, Channel: msg.Channel, Author: "bot", FromBot: true, Date: time.Now(), ReplyTo: msg.ID, }, } }