send notification

main
alex 2023-10-20 22:17:18 +02:00
parent 19557e0bea
commit 6e09cea56c
8 changed files with 146 additions and 576 deletions

View File

@ -1,7 +1,8 @@
package structs package structs
// swagger:model NotificationBody
type NotificationBody struct { type NotificationBody struct {
UsersIds []string UserIds []string
Type uint8 Type uint8
Message string Message string
} }

View File

@ -12,7 +12,7 @@ type VerifyCodeParams struct {
UserId string UserId string
} }
// swagger:response VerifyCodeResponse // swagger:model VerifyCodeResponse
type VerifyCodeResponse struct { type VerifyCodeResponse struct {
Code string Code string
} }

View File

@ -87,12 +87,7 @@ func IncomingMessagesHandler() {
var foundVerifiedUser structs.VerifiedUser var foundVerifiedUser structs.VerifiedUser
res := database.DB.Where("chat_id = ?", update.Message.From.ID).First(&foundVerifiedUser) database.DB.Where("chat_id = ?", update.Message.From.ID).First(&foundVerifiedUser)
if res.Error != nil {
logger.AddSystemLog(rslogger.LogTypeError, "Failed to get verified user from database, err: %v", res.Error.Error())
continue
}
if foundVerifiedUser.UserId != "" { if foundVerifiedUser.UserId != "" {
// user already verified // user already verified
@ -115,15 +110,7 @@ func IncomingMessagesHandler() {
} }
} }
// reply to user SendNotification(chatID, replyMessage)
reply := tgbotapi.NewMessage(chatID, replyMessage)
_, err := bot.Send(reply)
if err != nil {
log.Error().Msgf("Failed to send subscribed reply to user, err: %v", err.Error())
logger.AddSystemLog(rslogger.LogTypeError, "Failed to send reply to user, err: %v", err.Error())
}
} else if regUnsubscribe.MatchString(text) { } else if regUnsubscribe.MatchString(text) {
res := database.DB.Delete(&structs.VerifiedUser{}, "chat_id = ?", update.Message.From.ID) res := database.DB.Delete(&structs.VerifiedUser{}, "chat_id = ?", update.Message.From.ID)
@ -140,16 +127,17 @@ func IncomingMessagesHandler() {
replyMessage = "You have unsubscribed from receiving notifications. Type /verify <code> to start receiving notifications again. The code can be found in the user profile on the dashboard." replyMessage = "You have unsubscribed from receiving notifications. Type /verify <code> to start receiving notifications again. The code can be found in the user profile on the dashboard."
} }
reply := tgbotapi.NewMessage(chatID, replyMessage) SendNotification(chatID, replyMessage)
_, err := bot.Send(reply)
if err != nil {
logger.AddSystemLog(rslogger.LogTypeError, "Failed to send reply to user, err: %v", err.Error())
log.Error().Msgf("Failed to send unsubscribed reply to user, err: %v", err.Error())
}
} else { } else {
logger.AddSystemLog(rslogger.LogTypeWarning, "Received unknown message: %s from user: %s %s", text, update.Message.From.FirstName, update.Message.From.LastName) logger.AddSystemLog(rslogger.LogTypeWarning, "Received unknown message: %s from user: %s %s", text, update.Message.From.FirstName, update.Message.From.LastName)
} }
} }
} }
func SendNotification(chatId int64, message string) {
msg := tgbotapi.NewMessage(chatId, message)
if _, err := bot.Send(msg); err != nil {
logger.AddSystemLog(rslogger.LogTypeError, "Failed to send notification to chatId %v message: %s, err: %v", chatId, message, err.Error())
}
}

View File

@ -5,4 +5,9 @@ import "time"
const ( const (
VerifyCodeLength = 6 VerifyCodeLength = 6
TempVerifyCodeExpirationTime = 5 * time.Minute TempVerifyCodeExpirationTime = 5 * time.Minute
NotificationIconSuccess = "🟢"
NotificationSymbolInfo = "🔵"
NotificationSymbolWarnung = "🟠"
NotificationSymbolError = "🔴"
) )

28
modules/utils/utils.go Normal file
View File

@ -0,0 +1,28 @@
package utils
import "jannex/telegram-bot-manager/modules/structs"
func GetNotificationIconByType(notificationType uint8) string {
switch notificationType {
case 1:
return NotificationIconSuccess
case 2:
return NotificationSymbolInfo
case 3:
return NotificationSymbolWarnung
case 4:
return NotificationSymbolError
default:
return "X"
}
}
func GetUserIds(list []structs.VerifiedUser) []string {
var userIds []string
for _, v := range list {
userIds = append(userIds, v.UserId)
}
return userIds
}

View File

@ -1,22 +1,41 @@
{ {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"schemes": [
"https"
],
"swagger": "2.0", "swagger": "2.0",
"info": {
"title": "JNX Robot Control Manager API Documentation.",
"version": "1.0.0"
},
"host": "jannex",
"basePath": "/v1",
"paths": { "paths": {
"/api/v1/robots": { "/v1/notification": {
"post": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"notification"
],
"summary": "Send notification to users",
"operationId": "sendNotification",
"parameters": [
{
"description": "Notification body",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/NotificationBody"
}
}
],
"responses": {
"200": {
"description": "OK"
},
"400": {
"description": "Bad request"
}
}
}
},
"/v1/verifycode/{userId}": {
"get": { "get": {
"consumes": [ "consumes": [
"application/json" "application/json"
@ -25,576 +44,64 @@
"application/json" "application/json"
], ],
"tags": [ "tags": [
"robots" "verifycode"
], ],
"summary": "Get all robots", "summary": "Get verify code for user",
"operationId": "getRobots", "operationId": "getVerifyCode",
"parameters": [ "parameters": [
{ {
"description": "Page number", "type": "string",
"name": "page", "description": "User ID",
"in": "query" "name": "userId",
}
],
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/RobotsResponse"
}
}
}
}
},
"/api/v1/urobots": {
"get": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"robots"
],
"summary": "Get all unauthorized robots",
"operationId": "getUnauthorizedRobots",
"parameters": [
{
"description": "Page number",
"name": "page",
"in": "query"
}
],
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/UnauthorizedRobotsResponse"
}
}
}
}
},
"/control/1": {
"post": {
"description": "This is used to control Rex.\n",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"control"
],
"summary": "Control Rex.",
"operationId": "controlRex",
"parameters": [
{
"description": "Control Rex body.",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/ControlBody"
}
}
],
"responses": {
"200": {
"description": "Control Rex"
},
"400": {
"description": "Invalid request body"
},
"422": {
"description": "Robot not found"
}
}
}
},
"/control/1/finish": {
"post": {
"description": "This is used to finish control Rex.\n",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"control"
],
"summary": "Finish control Rex.",
"operationId": "finishControlRex",
"responses": {
"200": {
"description": "Finish control Rex"
},
"400": {
"description": "Invalid robot name"
},
"422": {
"description": "Robot not found"
}
}
}
},
"/permitjoin": {
"get": {
"description": "This is used to get the current permit join status.\n",
"produces": [
"application/json"
],
"tags": [
"permitjoin"
],
"summary": "Get permit join.",
"operationId": "getPermitJoin",
"responses": {
"200": {
"description": "Permit join status",
"schema": {
"$ref": "#/definitions/PermitJoinResponse"
}
}
}
}
},
"/permitjoin/{enabled}": {
"post": {
"description": "This is used to enable or disable permit join.\n",
"tags": [
"permitjoin"
],
"summary": "Set permit join.",
"operationId": "setPermitJoin",
"parameters": [
{
"description": "Enable or disable permit join. 0 = disable, 1 = enable.",
"name": "enabled",
"in": "path",
"required": true,
"schema": {
"type": "integer",
"enum": [
0,
1
]
}
}
],
"responses": {
"200": {
"description": "Permit join set"
},
"400": {
"description": "Invalid request body"
}
}
}
},
"/robot": {
"post": {
"description": "This is the first request from the robot. It will be used to identify the robot.\n",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"robot"
],
"summary": "First request from robot.",
"operationId": "robotFirstRequest",
"parameters": [
{
"description": "First request body.",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/FirstRequestBody"
}
}
],
"responses": {
"200": {
"description": "Robot identified",
"schema": {
"$ref": "#/definitions/StatusResponse"
}
},
"400": {
"description": "Invalid request body"
},
"403": {
"description": "Permit join is enabled"
}
}
},
"patch": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"robot"
],
"summary": "Update robot.",
"operationId": "robotUpdate",
"parameters": [
{
"description": "Update robot body.",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/Robot"
}
}
],
"responses": {
"200": {
"description": "Robot updated"
},
"400": {
"description": "Invalid request body"
},
"422": {
"description": "Robot not found"
}
}
}
},
"/robot/authorize/{robotId}": {
"post": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"robot"
],
"summary": "Authorize robot.",
"operationId": "robotAuthorize",
"parameters": [
{
"description": "Robot id.",
"name": "robotId",
"in": "params", "in": "params",
"required": true, "required": true
"schema": {
"type": "string"
}
} }
], ],
"responses": { "responses": {
"200": { "200": {
"description": "Robot authorized" "description": "OK",
},
"400": {
"description": "Invalid robot id"
},
"422": {
"description": "Robot not found"
}
}
}
},
"/robot/deny/{robotId}": {
"delete": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"robot"
],
"summary": "Deny unauthorized robot.",
"operationId": "robotDenyUnauthorizedRobot",
"parameters": [
{
"description": "Robot id.",
"name": "robotId",
"in": "params",
"required": true,
"schema": { "schema": {
"type": "string" "$ref": "#/definitions/VerifyCodeResponse"
} }
}
],
"responses": {
"200": {
"description": "Robot denied"
}, },
"400": { "400": {
"description": "Invalid robot id" "description": "Bad request"
}
}
}
},
"/robot/fuj/{robotId}": {
"patch": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"robot"
],
"summary": "Free up job from robot.",
"operationId": "robotFreeUpJob",
"parameters": [
{
"description": "Robot id.",
"name": "robotId",
"in": "params",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Job freed up"
}, },
"400": { "500": {
"description": "Invalid robot id" "description": "Internal server error"
}
}
}
},
"/robot/{robotId}": {
"delete": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"robot"
],
"summary": "Delete robot.",
"operationId": "robotDelete",
"parameters": [
{
"description": "Robot id.",
"name": "robotId",
"in": "params",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Robot deleted"
},
"400": {
"description": "Invalid robot id"
} }
} }
} }
} }
}, },
"definitions": { "definitions": {
"APIRobot": { "NotificationBody": {
"type": "object", "type": "object",
"properties": { "properties": {
"Address": { "Message": {
"type": "string" "type": "string"
}, },
"ConnectedAt": { "Type": {
"type": "string",
"format": "date-time"
},
"CreatedAt": {
"type": "string",
"format": "date-time"
},
"CurrentJobName": {
"type": "string"
},
"FirmwareVersion": {
"type": "string"
},
"Id": {
"type": "string"
},
"JobsWaitingCount": {
"type": "integer", "type": "integer",
"format": "int64" "format": "uint8"
}, },
"JobsWaitingNameList": { "UserIds": {
"type": "array", "type": "array",
"items": { "items": {
"type": "string" "type": "string"
} }
},
"Name": {
"type": "string"
},
"Status": {
"type": "integer",
"format": "uint8"
},
"Type": {
"type": "integer",
"format": "uint8"
} }
}, },
"x-go-package": "jannex/robot-control-manager/modules/structs" "x-go-package": "jannex/telegram-bot-manager/modules/structs"
}, },
"ControlBody": { "VerifyCodeResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
"JobId": { "Code": {
"type": "string" "type": "string"
},
"JobName": {
"type": "string"
},
"RobotName": {
"type": "string"
},
"Task": {
"$ref": "#/definitions/ControlTask"
} }
}, },
"x-go-package": "jannex/robot-control-manager/modules/structs" "x-go-package": "jannex/telegram-bot-manager/modules/structs"
},
"ControlTask": {
"type": "object",
"properties": {
"ConnectedModule": {
"type": "integer",
"format": "int64"
},
"X": {
"type": "integer",
"format": "int64"
},
"Y": {
"type": "integer",
"format": "int64"
},
"Z": {
"type": "integer",
"format": "int64"
}
},
"x-go-package": "jannex/robot-control-manager/modules/structs"
},
"FirstRequestBody": {
"type": "object",
"properties": {
"FirmwareVersion": {
"description": "used firmware version of the robot",
"type": "string"
},
"Id": {
"description": "robot id",
"type": "string"
},
"Type": {
"description": "robot type",
"type": "integer",
"format": "uint8"
}
},
"x-go-package": "jannex/robot-control-manager/modules/structs"
},
"PermitJoinResponse": {
"type": "object",
"properties": {
"Enabled": {
"type": "boolean"
}
},
"x-go-package": "jannex/robot-control-manager/modules/structs"
},
"RobotsResponse": {
"type": "object",
"properties": {
"Robots": {
"type": "array",
"items": {
"$ref": "#/definitions/APIRobot"
}
},
"TotalPages": {
"type": "integer",
"format": "int64"
}
},
"x-go-package": "jannex/robot-control-manager/modules/structs"
},
"StatusResponse": {
"type": "object",
"properties": {
"Status": {
"type": "integer",
"format": "int64"
}
},
"x-go-package": "jannex/robot-control-manager/modules/structs"
},
"UnauthorizedRobot": {
"type": "object",
"properties": {
"Address": {
"type": "string"
},
"ConnectedAt": {
"type": "string",
"format": "date-time"
},
"CreatedAt": {
"type": "string",
"format": "date-time"
},
"FirmwareVersion": {
"type": "string"
},
"Id": {
"type": "string"
},
"Type": {
"type": "integer",
"format": "uint8"
}
},
"x-go-package": "jannex/robot-control-manager/modules/structs"
},
"UnauthorizedRobotsResponse": {
"type": "object",
"properties": {
"TotalPages": {
"type": "integer",
"format": "int64"
},
"UnauthorizedRobots": {
"type": "array",
"items": {
"$ref": "#/definitions/UnauthorizedRobot"
}
}
},
"x-go-package": "jannex/robot-control-manager/modules/structs"
} }
} }
} }

View File

@ -1,18 +1,57 @@
package notification package notification
import ( import (
"jannex/telegram-bot-manager/modules/database"
"jannex/telegram-bot-manager/modules/logger"
"jannex/telegram-bot-manager/modules/structs" "jannex/telegram-bot-manager/modules/structs"
"jannex/telegram-bot-manager/modules/telegram"
"jannex/telegram-bot-manager/modules/utils"
"git.ex.umbach.dev/Alex/roese-utils/rslogger"
"git.ex.umbach.dev/Alex/roese-utils/rsutils" "git.ex.umbach.dev/Alex/roese-utils/rsutils"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
func SendNotification(c *fiber.Ctx) error { func SendNotification(c *fiber.Ctx) error {
// swagger:operation POST /v1/notification notification sendNotification
// ---
// summary: Send notification to users
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: body
// in: body
// description: Notification body
// required: true
// schema:
// "$ref": "#/definitions/NotificationBody"
// responses:
// '200':
// description: OK
// '400':
// description: Bad request
var body structs.NotificationBody var body structs.NotificationBody
if err := rsutils.BodyParserHelper(c, &body); err != nil { if err := rsutils.BodyParserHelper(c, &body); err != nil {
return c.SendStatus(fiber.StatusBadRequest) return c.SendStatus(fiber.StatusBadRequest)
} }
var foundVerifiedUsers []structs.VerifiedUser
// get all verified users by body userIds
database.DB.Where("user_id IN ?", body.UserIds).Find(&foundVerifiedUsers)
formattedMessage := utils.GetNotificationIconByType(body.Type) + " " + body.Message
// send message to all verified users
for _, user := range foundVerifiedUsers {
telegram.SendNotification(int64(user.ChatId), formattedMessage)
}
logger.AddSystemLog(rslogger.LogTypeInfo, "Sent notification: %s to %d users: %v", formattedMessage, len(foundVerifiedUsers), utils.GetUserIds(foundVerifiedUsers))
return c.SendStatus(fiber.StatusOK) return c.SendStatus(fiber.StatusOK)
} }

View File

@ -1,6 +1,7 @@
package router package router
import ( import (
"jannex/telegram-bot-manager/routers/api/v1/notification"
"jannex/telegram-bot-manager/routers/api/v1/verifycode" "jannex/telegram-bot-manager/routers/api/v1/verifycode"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
@ -13,6 +14,7 @@ func SetupRoutes(app *fiber.App) {
vc.Get("/:userId", verifycode.GetVerifyCode) vc.Get("/:userId", verifycode.GetVerifyCode)
n := v1.Group("/notification") n := v1.Group("/notification")
n.Post("/", notification.SendNotification)
app.Static("/", "./public/") app.Static("/", "./public/")
} }