diff --git a/main.go b/main.go index 043d668..f24810a 100644 --- a/main.go +++ b/main.go @@ -61,7 +61,10 @@ MARIADB_DATABASE_NAME=db_database_name`) } func main() { - app := fiber.New() + app := fiber.New(fiber.Config{ + // deactivate zero allocation + Immutable: true, + }) app.Use(cors.New()) @@ -79,51 +82,3 @@ func main() { app.Listen(config.Cfg.Host + ":" + config.Cfg.Port) } - -/* -func main() { - // Setze deinen Telegram Bot Token hier ein - bot, err := tgbotapi.NewBotAPI("6450280279:AAGxXdPv_YwRtl_pXIqYojqOecBrEjn3fJo") - if err != nil { - log.Panic(err) - } - - bot.Debug = true - - log.Printf("Angemeldet als %s", bot.Self.UserName) - - // Erstelle eine Nachricht, die du senden möchtest - msg := tgbotapi.NewMessage(935432572, "Das freut mich mein Hase. Ich liebe dich.") - - // Sende die Nachricht - _, err = bot.Send(msg) - if err != nil { - log.Panic(err) - } - - // Erstelle eine Update-Konfiguration, um auf eingehende Nachrichten zu reagieren - updateConfig := tgbotapi.NewUpdate(0) - updateConfig.Timeout = 60 - - updates, err := bot.GetUpdatesChan(updateConfig) - - for update := range updates { - if update.Message == nil { - continue - } - - chatID := update.Message.Chat.ID - text := update.Message.Text - - log.Printf("[%s] %s", update.Message.From.UserName, text) - - // Hier kannst du auf die empfangene Nachricht reagieren - // Zum Beispiel, eine Antwort senden - reply := tgbotapi.NewMessage(chatID, "Du hast folgende Nachricht gesendet: "+text) - _, err := bot.Send(reply) - if err != nil { - log.Panic(err) - } - } -} -*/ diff --git a/modules/cache/tempverifycodes.go b/modules/cache/tempverifycodes.go index 02ab5b7..c98fb93 100644 --- a/modules/cache/tempverifycodes.go +++ b/modules/cache/tempverifycodes.go @@ -15,17 +15,17 @@ func AddTempVerifyCode(tempVerifyCode structs.TempVerifyCode) { tempVerifyCodes[tempVerifyCode.UserId] = tempVerifyCode } -func GetTempVerifyByCode(code string) (userId string, found bool) { +func GetTempVerifyByCode(code string) (tempVerify structs.TempVerifyCode, found bool) { tMu.RLock() defer tMu.RUnlock() for _, v := range tempVerifyCodes { if v.Code == code { - return v.UserId, true + return v, true } } - return "", false + return structs.TempVerifyCode{}, false } func GetTempVerifyCode(userId string) (string, bool) { diff --git a/modules/structs/verifycode.go b/modules/structs/verifycode.go index accce00..da59f47 100644 --- a/modules/structs/verifycode.go +++ b/modules/structs/verifycode.go @@ -5,6 +5,7 @@ import "time" type VerifiedUser struct { UserId string ChatId int + Filter string CreatedAt time.Time } diff --git a/modules/telegram/telegram.go b/modules/telegram/telegram.go index 208161d..4a5f397 100644 --- a/modules/telegram/telegram.go +++ b/modules/telegram/telegram.go @@ -7,6 +7,7 @@ import ( "jannex/telegram-bot-manager/modules/structs" "jannex/telegram-bot-manager/modules/utils" "regexp" + "strings" "time" "git.ex.umbach.dev/Alex/roese-utils/rslogger" @@ -56,14 +57,30 @@ func IncomingMessagesHandler() { continue } + regStart := regexp.MustCompile(`/start`) + regHelp := regexp.MustCompile(`/help`) + regVerify := regexp.MustCompile(`/verify (\w+)`) regVerifyMatches := regVerify.FindStringSubmatch(text) regUnsubscribe := regexp.MustCompile(`/unsubscribe`) + regFilter := regexp.MustCompile(`/filter`) + regFilterWithArgs := regexp.MustCompile(`/filter (.+)`) + regFilterMatches := regFilterWithArgs.FindStringSubmatch(text) + var replyMessage string - if len(regVerifyMatches) == 2 { + if regStart.MatchString(text) || regHelp.MatchString(text) { // command /start or /help + replyMessage = "Welcome!\n\n" + + "Type /verify to start receiving notifications. The code can be found in the user profile on the dashboard.\n\n" + + "Type /unsubscribe to stop receiving notifications.\n\n" + + "Type /filter to filter notifications. Filter can be: success, info, warning, error. For example: /filter info,warning,error will filter all notifications except success." + + SendNotification(chatID, replyMessage) + + logger.AddSystemLog(rslogger.LogTypeInfo, "Received command %s from user: %s %s", text, update.Message.From.FirstName, update.Message.From.LastName) + } else if len(regVerifyMatches) == 2 { // command /verify code := regVerifyMatches[1] if len(code) != utils.VerifyCodeLength { @@ -71,10 +88,10 @@ func IncomingMessagesHandler() { continue } - var userId string + var tempVerify structs.TempVerifyCode var ok bool - if userId, ok = cache.GetTempVerifyByCode(code); !ok { + if tempVerify, ok = cache.GetTempVerifyByCode(code); !ok { // code not found in cache logger.AddSystemLog(rslogger.LogTypeWarning, "Received code which was not found in cache from user: %s %s. Message: %s", update.Message.From.FirstName, update.Message.From.LastName, code) @@ -83,8 +100,6 @@ func IncomingMessagesHandler() { } else { // code found in cache - cache.RemoveTempVerifyCode(userId) - var foundVerifiedUser structs.VerifiedUser database.DB.Where("chat_id = ?", update.Message.From.ID).First(&foundVerifiedUser) @@ -101,17 +116,19 @@ func IncomingMessagesHandler() { replyMessage = "You have successfully verified your account. You will now receive notifications. Type /unsubscribe to stop receiving notifications." database.DB.Create(&structs.VerifiedUser{ - UserId: userId, + UserId: tempVerify.UserId, ChatId: update.Message.From.ID, CreatedAt: time.Now(), }) logger.AddSystemLog(rslogger.LogTypeInfo, "User: %s %s has subscribed to receive notifications", update.Message.From.FirstName, update.Message.From.LastName) } + + cache.RemoveTempVerifyCode(tempVerify.UserId) } SendNotification(chatID, replyMessage) - } else if regUnsubscribe.MatchString(text) { + } else if regUnsubscribe.MatchString(text) { // command /unsubscribe res := database.DB.Delete(&structs.VerifiedUser{}, "chat_id = ?", update.Message.From.ID) if res.Error != nil { @@ -128,6 +145,20 @@ func IncomingMessagesHandler() { } SendNotification(chatID, replyMessage) + } else if len(regFilterMatches) == 2 { // command /filter + setFilter(chatID, update, text, regFilterMatches) + } else if regFilter.MatchString(text) { // command /filter + var foundVerifiedUser structs.VerifiedUser + database.DB.Where("chat_id = ?", update.Message.From.ID).First(&foundVerifiedUser) + + if foundVerifiedUser.UserId == "" { + logger.AddSystemLog(rslogger.LogTypeWarning, "User: %s %s has tried to get filter, but has not been verified", update.Message.From.FirstName, update.Message.From.LastName) + SendNotification(chatID, "You have not been verified yet. Type /verify to start receiving notifications. The code can be found in the user profile on the dashboard.") + } else { + formattedFilters := utils.GetFormattedFilters(foundVerifiedUser.Filter) + logger.AddSystemLog(rslogger.LogTypeInfo, "User: %s %s has requested filter, current filter: %s", update.Message.From.FirstName, update.Message.From.LastName, formattedFilters) + SendNotification(chatID, "Your current filter is: "+formattedFilters) + } } else { logger.AddSystemLog(rslogger.LogTypeWarning, "Received unknown message: %s from user: %s %s", text, update.Message.From.FirstName, update.Message.From.LastName) } @@ -141,3 +172,65 @@ func SendNotification(chatId int64, message string) { logger.AddSystemLog(rslogger.LogTypeError, "Failed to send notification to chatId %v message: %s, err: %v", chatId, message, err.Error()) } } + +func setFilter(chatID int64, update tgbotapi.Update, text string, regFilterMatches []string) { + logger.AddSystemLog(rslogger.LogTypeInfo, "Received filter message: %s from user: %s %s", text, update.Message.From.FirstName, update.Message.From.LastName) + + filter := regFilterMatches[1] + filterValues := strings.Split(filter, ",") + + for _, v := range filterValues { + switch v { + case utils.NotificationTypeSuccess, + utils.NotificationTypeInfoString, + utils.NotificationTypeWarningString, + utils.NotificationTypeErrorString: + continue + default: + logger.AddSystemLog(rslogger.LogTypeWarning, "Received unknown filter value: %s from user: %s %s", v, update.Message.From.FirstName, update.Message.From.LastName) + SendNotification(chatID, "You have entered an invalid filter value. Filter can be: success, info, warning, error. For example: /filter info,warning,error will filter all notifications except success.") + return + } + } + + var filteredValues []string + var filteredTextValues []string + + for _, v := range filterValues { + switch v { + case utils.NotificationTypeSuccessString: + filteredValues = append(filteredValues, utils.NotificationTypeSuccess) + filteredTextValues = append(filteredTextValues, utils.NotificationTypeSuccessString) + case utils.NotificationTypeInfoString: + filteredValues = append(filteredValues, utils.NotificationTypeInfo) + filteredTextValues = append(filteredTextValues, utils.NotificationTypeInfoString) + case utils.NotificationTypeWarningString: + filteredValues = append(filteredValues, utils.NotificationTypeWarning) + filteredTextValues = append(filteredTextValues, utils.NotificationTypeWarningString) + case utils.NotificationTypeErrorString: + filteredValues = append(filteredValues, utils.NotificationTypeError) + filteredTextValues = append(filteredTextValues, utils.NotificationTypeErrorString) + } + } + + var foundVerifiedUser structs.VerifiedUser + database.DB.Where("chat_id = ?", update.Message.From.ID).First(&foundVerifiedUser) + + if foundVerifiedUser.UserId == "" { + logger.AddSystemLog(rslogger.LogTypeWarning, "User: %s %s has tried to set filter, but has not been verified", update.Message.From.FirstName, update.Message.From.LastName) + SendNotification(chatID, "You have not been verified yet. Type /verify to start receiving notifications. The code can be found in the user profile on the dashboard.") + return + } + + filter = strings.Join(filteredValues, ",") + + database.DB.Model(&structs.VerifiedUser{}). + Where("chat_id = ?", update.Message.From.ID). + Updates(structs.VerifiedUser{Filter: filter}) + + formattedFilteredTextValues := strings.Join(filteredTextValues, ", ") + + logger.AddSystemLog(rslogger.LogTypeInfo, "User: %s %s has set filter to %s (%s)", update.Message.From.FirstName, update.Message.From.LastName, filter, formattedFilteredTextValues) + + SendNotification(chatID, "You have successfully set filter to "+formattedFilteredTextValues+". You will now only receive notifications with the selected filter.") +} diff --git a/modules/utils/globals.go b/modules/utils/globals.go index 067a097..4be2db9 100644 --- a/modules/utils/globals.go +++ b/modules/utils/globals.go @@ -1,6 +1,8 @@ package utils -import "time" +import ( + "time" +) const ( VerifyCodeLength = 6 @@ -10,4 +12,14 @@ const ( NotificationSymbolInfo = "🔵" NotificationSymbolWarnung = "🟠" NotificationSymbolError = "🔴" + + NotificationTypeSuccess = "1" + NotificationTypeInfo = "2" + NotificationTypeWarning = "3" + NotificationTypeError = "4" + + NotificationTypeSuccessString = "success" + NotificationTypeInfoString = "info" + NotificationTypeWarningString = "warning" + NotificationTypeErrorString = "error" ) diff --git a/modules/utils/utils.go b/modules/utils/utils.go index 4f67272..515a613 100644 --- a/modules/utils/utils.go +++ b/modules/utils/utils.go @@ -1,6 +1,11 @@ package utils -import "jannex/telegram-bot-manager/modules/structs" +import ( + "jannex/telegram-bot-manager/modules/logger" + "strings" + + "git.ex.umbach.dev/Alex/roese-utils/rslogger" +) func GetNotificationIconByType(notificationType uint8) string { switch notificationType { @@ -13,16 +18,52 @@ func GetNotificationIconByType(notificationType uint8) string { case 4: return NotificationSymbolError default: - return "X" + logger.AddSystemLog(rslogger.LogTypeError, "Unknown notification type: %v", notificationType) + return "" } } -func GetUserIds(list []structs.VerifiedUser) []string { - var userIds []string +func GetFormattedFilters(filter string) string { + filterValues := strings.Split(filter, ",") + var formattedFilters []string - for _, v := range list { - userIds = append(userIds, v.UserId) + for _, v := range filterValues { + switch v { + case NotificationTypeSuccess: + formattedFilters = append(formattedFilters, NotificationTypeSuccessString) + case NotificationTypeInfo: + formattedFilters = append(formattedFilters, NotificationTypeInfoString) + case NotificationTypeWarning: + formattedFilters = append(formattedFilters, NotificationTypeWarningString) + case NotificationTypeError: + formattedFilters = append(formattedFilters, NotificationTypeErrorString) + } } - return userIds + return strings.Join(formattedFilters, ", ") +} + +func IsNotificationTypeInFilter(filter []string, notificationType uint8) bool { + for _, v := range filter { + switch v { + case NotificationTypeSuccess: + if notificationType == 1 { + return true + } + case NotificationTypeInfo: + if notificationType == 2 { + return true + } + case NotificationTypeWarning: + if notificationType == 3 { + return true + } + case NotificationTypeError: + if notificationType == 4 { + return true + } + } + } + + return false } diff --git a/public/qrcode.png b/public/qrcode.png index ff5c466..f811066 100644 Binary files a/public/qrcode.png and b/public/qrcode.png differ diff --git a/routers/api/v1/notification/notification.go b/routers/api/v1/notification/notification.go index d39c70e..7d0383e 100644 --- a/routers/api/v1/notification/notification.go +++ b/routers/api/v1/notification/notification.go @@ -6,6 +6,7 @@ import ( "jannex/telegram-bot-manager/modules/structs" "jannex/telegram-bot-manager/modules/telegram" "jannex/telegram-bot-manager/modules/utils" + "strings" "git.ex.umbach.dev/Alex/roese-utils/rslogger" "git.ex.umbach.dev/Alex/roese-utils/rsutils" @@ -46,12 +47,26 @@ func SendNotification(c *fiber.Ctx) error { formattedMessage := utils.GetNotificationIconByType(body.Type) + " " + body.Title + var notifiedUsers []string + // send message to all verified users for _, user := range foundVerifiedUsers { + if user.Filter == "" { + continue + } + + filter := strings.Split(user.Filter, ",") + + if !utils.IsNotificationTypeInFilter(filter, body.Type) { + continue + } + telegram.SendNotification(int64(user.ChatId), formattedMessage) + + notifiedUsers = append(notifiedUsers, user.UserId) } - logger.AddSystemLog(rslogger.LogTypeInfo, "Sent notification: %s to %d users: %v", formattedMessage, len(foundVerifiedUsers), utils.GetUserIds(foundVerifiedUsers)) + logger.AddSystemLog(rslogger.LogTypeInfo, "Sent notification: %s to %d users: %v", formattedMessage, len(notifiedUsers), notifiedUsers) return c.SendStatus(fiber.StatusOK) } diff --git a/routers/api/v1/status/status.go b/routers/api/v1/status/status.go index 25043eb..fee726f 100644 --- a/routers/api/v1/status/status.go +++ b/routers/api/v1/status/status.go @@ -6,7 +6,6 @@ import ( "git.ex.umbach.dev/Alex/roese-utils/rsutils" "github.com/gofiber/fiber/v2" - "github.com/rs/zerolog/log" ) func UserVerified(c *fiber.Ctx) error { @@ -42,7 +41,5 @@ func UserVerified(c *fiber.Ctx) error { // get verified user by userId database.DB.Where("user_id = ?", params.UserId).First(&foundVerifiedUser) - log.Info().Msgf("Found verified user: %v", foundVerifiedUser) - return c.JSON(structs.StatusResponse{Status: foundVerifiedUser.UserId != ""}) }