diff --git a/bot.go b/bot.go index e1e431a..45ea689 100644 --- a/bot.go +++ b/bot.go @@ -5,17 +5,19 @@ import ( "time" "watn3y/bloaterbotv3/botIO" "watn3y/bloaterbotv3/commands" + "watn3y/bloaterbotv3/commands/notify" "watn3y/bloaterbotv3/text" ) func bot() { updates, bot := botIO.Authenticate() + go notify.NotifyHandler(bot) now := time.Now().UTC() for update := range updates { if update.Message == nil || update.Message.Time().UTC().Unix() < now.UTC().Unix() { continue } - log.Printf("[bot] Recieved Message: [%s] %s", update.Message.From.UserName, update.Message.Text) + log.Printf("[bot] Recieved Message: [%s] %s", update.Message.From.FirstName, update.Message.Text) if update.Message.IsCommand() { commands.Commands(update, bot) diff --git a/commands/commands.go b/commands/commands.go index f6797db..a66939a 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -8,6 +8,7 @@ import ( "time" "watn3y/bloaterbotv3/botIO" "watn3y/bloaterbotv3/commands/gaypoints" + "watn3y/bloaterbotv3/commands/notify" "watn3y/bloaterbotv3/config" ) @@ -26,6 +27,8 @@ func Commands(update tgbotapi.Update, bot *tgbotapi.BotAPI) { gaypoints.SetGP(update, bot) case "subtractgp": gaypoints.SetGP(update, bot) + case "remindme": + notify.Reminder(update, bot) } } diff --git a/commands/notify/notify.go b/commands/notify/notify.go new file mode 100644 index 0000000..3a1ae15 --- /dev/null +++ b/commands/notify/notify.go @@ -0,0 +1,116 @@ +package notify + +import ( + tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" + "log" + "regexp" + "strconv" + "strings" + "time" + "watn3y/bloaterbotv3/botIO" +) + +func Reminder(update tgbotapi.Update, bot *tgbotapi.BotAPI) { + commandArgs := strings.Fields(update.Message.CommandArguments()) + + var timeArg string + var textArg string + + if len(commandArgs) >= 1 { + timeArg = strings.ToLower(commandArgs[0]) + textArg = strings.Replace(update.Message.CommandArguments(), timeArg, "", 1) + } else { + message := tgbotapi.MessageConfig{ + BaseChat: tgbotapi.BaseChat{ChatID: update.Message.Chat.ID, ReplyToMessageID: update.Message.MessageID}, + ParseMode: "html", + DisableWebPagePreview: false, + Text: "Please specify a valid notify time in this format: 1m, 6h, 2d", + } + botIO.SendMessage(message, bot) + return + } + + log.Println("[notify] Attempting to set reminder for user " + strconv.FormatInt(update.Message.From.ID, 10) + " in chat " + strconv.FormatInt(update.Message.Chat.ID, 10) + " with arguments " + timeArg) + + isValidFormat := regexp.MustCompile(`(?m)^\d{1,3}[mhd]$`) + + if !isValidFormat.MatchString(timeArg) { + message := tgbotapi.MessageConfig{ + BaseChat: tgbotapi.BaseChat{ChatID: update.Message.Chat.ID, ReplyToMessageID: update.Message.MessageID}, + ParseMode: "html", + DisableWebPagePreview: false, + Text: "Please specify a valid notify time in this format: 1m, 6h, 2d", + } + botIO.SendMessage(message, bot) + return + } + + getChar := regexp.MustCompile(`(?m)[mhd]`) + getNumbers := regexp.MustCompile(`(?m)\d{1,3}`) + + numberString := getNumbers.FindString(timeArg) + number, _ := strconv.Atoi(numberString) + char := getChar.FindString(timeArg) + + var modifyTime time.Duration + switch char { + case "m": + modifyTime = time.Minute + case "h": + modifyTime = time.Hour + case "d": + modifyTime = time.Hour * 24 + } + + notifyTime := time.Now().UTC().Add(time.Duration(number) * modifyTime) + + var reminder reminderConfig + + reminder = reminderConfig{ + updateID: update.UpdateID, + notifyTime: notifyTime.Unix(), + chatID: update.Message.Chat.ID, + userID: update.Message.From.ID, + messageToReplyToID: update.Message.MessageID, + reminderText: textArg, + } + + sqlAddReminder(reminder) + + message := tgbotapi.MessageConfig{ + BaseChat: tgbotapi.BaseChat{ChatID: update.Message.Chat.ID, ReplyToMessageID: update.Message.MessageID}, + ParseMode: "html", + DisableWebPagePreview: false, + Text: "Set reminder for " + notifyTime.Format("02.01.2006") + " at " + notifyTime.Format("15:04") + " UTC ", + } + botIO.SendMessage(message, bot) + return + +} + +func NotifyHandler(bot *tgbotapi.BotAPI) { + + for { + reminders := sqlGetReminders() + + for updateID, reminderTime := range reminders { + if reminderTime <= time.Now().UTC().Unix() { + details := sqlGetReminderDetails(updateID) + + message := tgbotapi.MessageConfig{ + BaseChat: tgbotapi.BaseChat{ChatID: details.chatID, ReplyToMessageID: details.messageToReplyToID}, + ParseMode: "html", + DisableWebPagePreview: false, + + Text: details.reminderText, + } + + botIO.SendMessage(message, bot) + sqlDeleteReminder(updateID) + } + + } + time.Sleep(time.Second * 10) + } + +} diff --git a/commands/notify/sql.go b/commands/notify/sql.go new file mode 100644 index 0000000..de9b7b6 --- /dev/null +++ b/commands/notify/sql.go @@ -0,0 +1,101 @@ +package notify + +import ( + "database/sql" + "log" + "strconv" +) + +var notifySetReminder *sql.Stmt +var notifyGetReminders *sql.Stmt +var notifyGetReminderDetails *sql.Stmt +var notifyDeleteReminder *sql.Stmt + +func InitDB() { + const dbPath string = "./bloater.db" + db, err := sql.Open("sqlite3", dbPath) + if err != nil { + log.Panicf("Failed to open sqlite database: %v\n", err) + } + + _, err = db.Exec("CREATE TABLE IF NOT EXISTS notify (updateID INTEGER,notifyTime INTEGER,chatID INTEGER, userID INTEGER,messageToReplyToID INTEGER,notifyText TEXT)") + if err != nil { + log.Panicf("Failed to create table: %v\n", err) + } + log.Println("[notify.sql] Created sqlite table in database at " + dbPath) + + notifySetReminder, err = db.Prepare("INSERT OR REPLACE INTO notify (updateID,notifyTime,chatID, userID,messageToReplyToID,notifyText) values (?,?,?,?,?,?)") + + if err != nil { + log.Panicf("Failed to prepare sql insert: %v\n", err) + } + + notifyGetReminders, err = db.Prepare("SELECT updateID,notifyTime FROM notify") + + if err != nil { + log.Panicf("Failed to prepare sql select: %v\n", err) + } + + notifyGetReminderDetails, err = db.Prepare("SELECT chatID, userID,messageToReplyToID,notifyText FROM notify WHERE updateID=?") + + if err != nil { + log.Panicf("Failed to prepare sql select: %v\n", err) + } + + notifyDeleteReminder, err = db.Prepare("DELETE FROM notify WHERE updateID=?") + + if err != nil { + log.Panicf("Failed to prepare sql select: %v\n", err) + } +} + +func sqlDeleteReminder(updateID int) { + _, err := notifyDeleteReminder.Exec(updateID) + if err != nil { + log.Panicf("Failed to delete reminder: %v\n", err) + } +} + +func sqlAddReminder(reminder reminderConfig) { + _, err := notifySetReminder.Exec(reminder.updateID, reminder.notifyTime, reminder.chatID, reminder.userID, reminder.messageToReplyToID, reminder.reminderText) + if err != nil { + log.Panicf("Failed to set add reminder: %v\n", err) + } + log.Println("[notify.sql] Set new reminder for " + strconv.FormatInt(reminder.userID, 10) + " in chat " + strconv.FormatInt(reminder.chatID, 10)) +} + +func sqlGetReminders() (reminders map[int]int64) { + rows, err := notifyGetReminders.Query() + if err != nil { + log.Panicf("Failed to query reminders: %v\n", err) + } + + reminders = make(map[int]int64) + var c int + var b int64 + for rows.Next() { + switch err := rows.Scan(&c, &b); err { + case nil: + reminders[c] = b + default: + log.Panicf("Failed to query gaypoints: %v\n", err) + + } + } + + return reminders +} + +func sqlGetReminderDetails(updateID int) (reminder reminderConfig) { + row := notifyGetReminderDetails.QueryRow(updateID) + + switch err := row.Scan(&reminder.chatID, &reminder.userID, &reminder.messageToReplyToID, &reminder.reminderText); err { + case nil: + break + default: + log.Panicf("Failed to get reminder details: %v\n", err) + + } + log.Println("[notify.sql] Got reminder details for update ID" + string(rune(updateID))) + return reminder +} diff --git a/commands/notify/types.go b/commands/notify/types.go new file mode 100644 index 0000000..08852b3 --- /dev/null +++ b/commands/notify/types.go @@ -0,0 +1,10 @@ +package notify + +type reminderConfig struct { + updateID int + notifyTime int64 + chatID int64 + userID int64 + messageToReplyToID int + reminderText string +}