diff --git a/cmd/bots/discord/discord.go b/cmd/bots/discord/discord.go
index c6ad5012..73188cdd 100644
--- a/cmd/bots/discord/discord.go
+++ b/cmd/bots/discord/discord.go
@@ -68,14 +68,14 @@ func runDiscordBot() error {
ctx, done := signal.NotifyContext(context.Background(), os.Interrupt)
defer done()
- discordBotEnv, err := initial.InitDiscordBot(discordBotToken, discordChatID, zap)
+ pairRequest := make(chan pairResponses.RequestPair)
+ pairResponse := make(chan pairResponses.ResponsePair)
+
+ discordBotEnv, err := initial.InitDiscordBot(discordBotToken, discordChatID, zap, pairRequest, pairResponse)
if err != nil {
return errors.Wrap(err, "failed to init discord bot")
}
-
- pairRequest := make(chan pairResponses.RequestPair)
- pairResponse := make(chan pairResponses.ResponsePair)
- handlers.InitDscHandlers(discordBotEnv, pairRequest, pairResponse)
+ handlers.InitDscHandlers(discordBotEnv, pairRequest, pairResponse, zap)
go func() {
err := pubsub.StartSubMessagingClient(ctx, nanomsgPubSubURL, discordBotEnv, zap)
diff --git a/cmd/bots/internal/common/environment.go b/cmd/bots/internal/common/environment.go
index 3c11294c..e5d0630f 100644
--- a/cmd/bots/internal/common/environment.go
+++ b/cmd/bots/internal/common/environment.go
@@ -38,11 +38,11 @@ var (
templateFiles embed.FS
)
-type expectedExtension string
+type ExpectedExtension string
const (
- Html expectedExtension = ".html"
- Markdown expectedExtension = ".md"
+ Html ExpectedExtension = ".html"
+ Markdown ExpectedExtension = ".md"
)
var errUnknownAlertType = errors.New("received unknown alert type")
@@ -79,22 +79,25 @@ func (s *subscriptions) MapR(f func()) {
}
type DiscordBotEnvironment struct {
- ChatID string
- Bot *discordgo.Session
- subSocket protocol.Socket
- Subscriptions subscriptions
- Zap *zap.Logger
+ ChatID string
+ Bot *discordgo.Session
+ subSocket protocol.Socket
+ Subscriptions subscriptions
+ zap *zap.Logger
+ requestType chan<- pair.RequestPair
+ responsePairType <-chan pair.ResponsePair
}
-func NewDiscordBotEnvironment(bot *discordgo.Session, chatID string, zap *zap.Logger) *DiscordBotEnvironment {
- return &DiscordBotEnvironment{Bot: bot, ChatID: chatID, Subscriptions: subscriptions{subs: make(map[entities.AlertType]string), mu: new(sync.RWMutex)}, Zap: zap}
+func NewDiscordBotEnvironment(bot *discordgo.Session, chatID string, zap *zap.Logger, requestType chan<- pair.RequestPair,
+ responsePairType <-chan pair.ResponsePair) *DiscordBotEnvironment {
+ return &DiscordBotEnvironment{Bot: bot, ChatID: chatID, Subscriptions: subscriptions{subs: make(map[entities.AlertType]string), mu: new(sync.RWMutex)}, zap: zap, requestType: requestType, responsePairType: responsePairType}
}
func (dscBot *DiscordBotEnvironment) Start() error {
- dscBot.Zap.Info("Discord bot started")
+ dscBot.zap.Info("Discord bot started")
err := dscBot.Bot.Open()
if err != nil {
- dscBot.Zap.Error("failed to open discord bot", zap.Error(err))
+ dscBot.zap.Error("failed to open discord bot", zap.Error(err))
return err
}
return nil
@@ -107,36 +110,41 @@ func (dscBot *DiscordBotEnvironment) SetSubSocket(subSocket protocol.Socket) {
func (dscBot *DiscordBotEnvironment) SendMessage(msg string) {
_, err := dscBot.Bot.ChannelMessageSend(dscBot.ChatID, msg)
if err != nil {
- dscBot.Zap.Error("failed to send a message to discord", zap.Error(err))
+ dscBot.zap.Error("failed to send a message to discord", zap.Error(err))
}
}
func (dscBot *DiscordBotEnvironment) SendAlertMessage(msg []byte) {
if len(msg) == 0 {
- dscBot.Zap.Error("received empty alert message")
+ dscBot.zap.Error("received empty alert message")
return
}
alertType := entities.AlertType(msg[0])
_, ok := entities.AlertTypes[alertType]
if !ok {
- dscBot.Zap.Sugar().Errorf("failed to construct message, unknown alert type %c, %v", byte(alertType), errUnknownAlertType)
+ dscBot.zap.Sugar().Errorf("failed to construct message, unknown alert type %c, %v", byte(alertType), errUnknownAlertType)
_, err := dscBot.Bot.ChannelMessageSend(dscBot.ChatID, errUnknownAlertType.Error())
if err != nil {
- dscBot.Zap.Error("failed to send a message to discord", zap.Error(err))
+ dscBot.zap.Error("failed to send a message to discord", zap.Error(err))
}
return
}
- messageToBot, err := constructMessage(alertType, msg[1:], Markdown)
+ nodes, err := messaging.RequestAllNodes(dscBot.requestType, dscBot.responsePairType)
if err != nil {
- dscBot.Zap.Error("failed to construct message", zap.Error(err))
+ dscBot.zap.Error("failed to get nodes list", zap.Error(err))
+ }
+
+ messageToBot, err := constructMessage(alertType, msg[1:], Markdown, nodes)
+ if err != nil {
+ dscBot.zap.Error("failed to construct message", zap.Error(err))
return
}
_, err = dscBot.Bot.ChannelMessageSend(dscBot.ChatID, messageToBot)
if err != nil {
- dscBot.Zap.Error("failed to send a message to discord", zap.Error(err))
+ dscBot.zap.Error("failed to send a message to discord", zap.Error(err))
}
}
@@ -151,7 +159,7 @@ func (dscBot *DiscordBotEnvironment) SubscribeToAllAlerts() error {
return err
}
dscBot.Subscriptions.Add(alertType, alertName)
- dscBot.Zap.Sugar().Infof("subscribed to %s", alertName)
+ dscBot.zap.Sugar().Infof("subscribed to %s", alertName)
}
return nil
@@ -167,16 +175,18 @@ func (dscBot *DiscordBotEnvironment) IsEligibleForAction(chatID string) bool {
}
type TelegramBotEnvironment struct {
- ChatID int64
- Bot *telebot.Bot
- Mute bool // If it used elsewhere, should be protected by mutex
- subSocket protocol.Socket
- subscriptions subscriptions
- Zap *zap.Logger
+ ChatID int64
+ Bot *telebot.Bot
+ Mute bool // If it used elsewhere, should be protected by mutex
+ subSocket protocol.Socket
+ subscriptions subscriptions
+ Zap *zap.Logger
+ requestType chan<- pair.RequestPair
+ responsePairType <-chan pair.ResponsePair
}
-func NewTelegramBotEnvironment(bot *telebot.Bot, chatID int64, mute bool, zap *zap.Logger) *TelegramBotEnvironment {
- return &TelegramBotEnvironment{Bot: bot, ChatID: chatID, Mute: mute, subscriptions: subscriptions{subs: make(map[entities.AlertType]string), mu: new(sync.RWMutex)}, Zap: zap}
+func NewTelegramBotEnvironment(bot *telebot.Bot, chatID int64, mute bool, zap *zap.Logger, requestType chan<- pair.RequestPair, responsePairType <-chan pair.ResponsePair) *TelegramBotEnvironment {
+ return &TelegramBotEnvironment{Bot: bot, ChatID: chatID, Mute: mute, subscriptions: subscriptions{subs: make(map[entities.AlertType]string), mu: new(sync.RWMutex)}, Zap: zap, requestType: requestType, responsePairType: responsePairType}
}
func (tgEnv *TelegramBotEnvironment) Start() error {
@@ -218,7 +228,12 @@ func (tgEnv *TelegramBotEnvironment) SendAlertMessage(msg []byte) {
return
}
- messageToBot, err := constructMessage(alertType, msg[1:], Html)
+ nodes, err := messaging.RequestAllNodes(tgEnv.requestType, tgEnv.responsePairType)
+ if err != nil {
+ tgEnv.Zap.Error("failed to get nodes list", zap.Error(err))
+ }
+
+ messageToBot, err := constructMessage(alertType, msg[1:], Html, nodes)
if err != nil {
tgEnv.Zap.Error("failed to construct message", zap.Error(err))
return
@@ -398,15 +413,11 @@ func ScheduleNodesStatus(
responsePairType <-chan pair.ResponsePair, bot messaging.Bot, zapLogger *zap.Logger) error {
_, err := taskScheduler.ScheduleWithCron(func(ctx context.Context) {
- urls, err := messaging.RequestNodesList(requestType, responsePairType, false)
+ nodes, err := messaging.RequestAllNodes(requestType, responsePairType)
if err != nil {
zapLogger.Error("failed to get nodes list", zap.Error(err))
}
- additionalUrls, err := messaging.RequestNodesList(requestType, responsePairType, true)
- if err != nil {
- zapLogger.Error("failed to get additional nodes list", zap.Error(err))
- }
- urls = append(urls, additionalUrls...)
+ urls := messaging.NodesToUrls(nodes)
nodesStatus, err := messaging.RequestNodesStatus(requestType, responsePairType, urls)
if err != nil {
@@ -417,12 +428,12 @@ func ScheduleNodesStatus(
statusCondition := StatusCondition{AllNodesAreOk: false, NodesNumber: 0, Height: ""}
switch bot.(type) {
case *TelegramBotEnvironment:
- handledNodesStatus, statusCondition, err = HandleNodesStatus(nodesStatus, Html)
+ handledNodesStatus, statusCondition, err = HandleNodesStatus(nodesStatus, Html, nodes)
if err != nil {
zapLogger.Error("failed to handle nodes status", zap.Error(err))
}
case *DiscordBotEnvironment:
- handledNodesStatus, statusCondition, err = HandleNodesStatus(nodesStatus, Markdown)
+ handledNodesStatus, statusCondition, err = HandleNodesStatus(nodesStatus, Markdown, nodes)
if err != nil {
zapLogger.Error("failed to handle nodes status", zap.Error(err))
}
@@ -494,7 +505,7 @@ func sortNodesStatuses(statuses []NodeStatus) {
})
}
-func executeTemplate(template string, data any, extension expectedExtension) (string, error) {
+func executeTemplate(template string, data any, extension ExpectedExtension) (string, error) {
switch extension {
case Html:
tmpl, err := htmlTemplate.ParseFS(templateFiles, template+string(extension))
@@ -521,6 +532,212 @@ func executeTemplate(template string, data any, extension expectedExtension) (st
default:
return "", errors.New("unknown message type to execute a template")
}
+}
+
+func replaceNodeWithAlias(node string, nodesAlias map[string]string) string {
+ if alias, ok := nodesAlias[node]; ok {
+ return alias
+ }
+ node = strings.ReplaceAll(node, entities.HttpsScheme+"://", "")
+ node = strings.ReplaceAll(node, entities.HttpScheme+"://", "")
+ return node
+}
+
+type heightStatementGroup struct {
+ Nodes []string
+ Height int
+}
+
+type heightStatement struct {
+ HeightDifference int
+ FirstGroup heightStatementGroup
+ SecondGroup heightStatementGroup
+}
+
+type stateHashStatementGroup struct {
+ BlockID string
+ Nodes []string
+ StateHash string
+}
+
+type stateHashStatement struct {
+ SameHeight int
+ LastCommonStateHashExist bool
+ ForkHeight int
+ ForkBlockID string
+ ForkStateHash string
+ FirstGroup stateHashStatementGroup
+ SecondGroup stateHashStatementGroup
+}
+
+type fixedStatement struct {
+ PreviousAlert string
+}
+
+func executeAlertTemplate(alertType entities.AlertType, alertJson []byte, extension ExpectedExtension, allNodes []entities.Node) (string, error) {
+ nodesAliases := make(map[string]string)
+ for _, n := range allNodes {
+ if n.Alias != "" {
+ nodesAliases[n.URL] = n.Alias
+ }
+ }
+ var msg string
+ switch alertType {
+ case entities.UnreachableAlertType:
+ var unreachableAlert entities.UnreachableAlert
+ err := json.Unmarshal(alertJson, &unreachableAlert)
+ if err != nil {
+ return "", err
+ }
+
+ unreachableAlert.Node = replaceNodeWithAlias(unreachableAlert.Node, nodesAliases)
+
+ msg, err = executeTemplate("templates/alerts/unreachable_alert", unreachableAlert, extension)
+ if err != nil {
+ return "", err
+ }
+ case entities.IncompleteAlertType:
+ var incompleteAlert entities.IncompleteAlert
+ err := json.Unmarshal(alertJson, &incompleteAlert)
+ if err != nil {
+ return "", err
+ }
+ incompleteStatement := incompleteAlert.NodeStatement
+ incompleteStatement.Node = replaceNodeWithAlias(incompleteStatement.Node, nodesAliases)
+
+ msg, err = executeTemplate("templates/alerts/incomplete_alert", incompleteStatement, extension)
+ if err != nil {
+ return "", err
+ }
+ case entities.InvalidHeightAlertType:
+ var invalidHeightAlert entities.InvalidHeightAlert
+ err := json.Unmarshal(alertJson, &invalidHeightAlert)
+ if err != nil {
+ return "", err
+ }
+ invalidHeightStatement := invalidHeightAlert.NodeStatement
+
+ invalidHeightStatement.Node = replaceNodeWithAlias(invalidHeightStatement.Node, nodesAliases)
+
+ msg, err = executeTemplate("templates/alerts/invalid_height_alert", invalidHeightStatement, extension)
+ if err != nil {
+ return "", err
+ }
+ case entities.HeightAlertType:
+ var heightAlert entities.HeightAlert
+ err := json.Unmarshal(alertJson, &heightAlert)
+ if err != nil {
+ return "", err
+ }
+
+ for i := range heightAlert.MaxHeightGroup.Nodes {
+ heightAlert.MaxHeightGroup.Nodes[i] = replaceNodeWithAlias(heightAlert.MaxHeightGroup.Nodes[i], nodesAliases)
+ }
+ for i := range heightAlert.OtherHeightGroup.Nodes {
+ heightAlert.OtherHeightGroup.Nodes[i] = replaceNodeWithAlias(heightAlert.OtherHeightGroup.Nodes[i], nodesAliases)
+ }
+
+ heightStatement := heightStatement{
+ HeightDifference: heightAlert.MaxHeightGroup.Height - heightAlert.OtherHeightGroup.Height,
+ FirstGroup: heightStatementGroup{
+ Nodes: heightAlert.MaxHeightGroup.Nodes,
+ Height: heightAlert.MaxHeightGroup.Height,
+ },
+ SecondGroup: heightStatementGroup{
+ Nodes: heightAlert.OtherHeightGroup.Nodes,
+ Height: heightAlert.OtherHeightGroup.Height,
+ },
+ }
+
+ msg, err = executeTemplate("templates/alerts/height_alert", heightStatement, extension)
+ if err != nil {
+ return "", err
+ }
+ case entities.StateHashAlertType:
+ var stateHashAlert entities.StateHashAlert
+ err := json.Unmarshal(alertJson, &stateHashAlert)
+ if err != nil {
+ return "", err
+ }
+
+ for i := range stateHashAlert.FirstGroup.Nodes {
+ stateHashAlert.FirstGroup.Nodes[i] = replaceNodeWithAlias(stateHashAlert.FirstGroup.Nodes[i], nodesAliases)
+ }
+ for i := range stateHashAlert.SecondGroup.Nodes {
+ stateHashAlert.SecondGroup.Nodes[i] = replaceNodeWithAlias(stateHashAlert.SecondGroup.Nodes[i], nodesAliases)
+ }
+
+ stateHashStatement := stateHashStatement{
+ SameHeight: stateHashAlert.CurrentGroupsBucketHeight,
+ LastCommonStateHashExist: stateHashAlert.LastCommonStateHashExist,
+ ForkHeight: stateHashAlert.LastCommonStateHashHeight,
+ ForkBlockID: stateHashAlert.LastCommonStateHash.BlockID.String(),
+ ForkStateHash: stateHashAlert.LastCommonStateHash.SumHash.Hex(),
+
+ FirstGroup: stateHashStatementGroup{
+ BlockID: stateHashAlert.FirstGroup.StateHash.BlockID.String(),
+ Nodes: stateHashAlert.FirstGroup.Nodes,
+ StateHash: stateHashAlert.FirstGroup.StateHash.SumHash.Hex(),
+ },
+ SecondGroup: stateHashStatementGroup{
+ BlockID: stateHashAlert.SecondGroup.StateHash.BlockID.String(),
+ Nodes: stateHashAlert.SecondGroup.Nodes,
+ StateHash: stateHashAlert.SecondGroup.StateHash.SumHash.Hex(),
+ },
+ }
+
+ msg, err = executeTemplate("templates/alerts/state_hash_alert", stateHashStatement, extension)
+ if err != nil {
+ return "", err
+ }
+ case entities.AlertFixedType:
+ var alertFixed entities.AlertFixed
+ err := json.Unmarshal(alertJson, &alertFixed)
+ if err != nil {
+ return "", err
+ }
+
+ // TODO there is no alias here right now, but AlertFixed needs to be changed. Make previous alert to look like a number
+
+ fixedStatement := fixedStatement{
+ PreviousAlert: alertFixed.Fixed.Message(),
+ }
+
+ msg, err = executeTemplate("templates/alerts/alert_fixed", fixedStatement, extension)
+ if err != nil {
+ return "", err
+ }
+ case entities.BaseTargetAlertType:
+ var baseTargetAlert entities.BaseTargetAlert
+ err := json.Unmarshal(alertJson, &baseTargetAlert)
+
+ for i := range baseTargetAlert.BaseTargetValues {
+ baseTargetAlert.BaseTargetValues[i].Node = replaceNodeWithAlias(baseTargetAlert.BaseTargetValues[i].Node, nodesAliases)
+ }
+
+ if err != nil {
+ return "", err
+ }
+
+ msg, err = executeTemplate("templates/alerts/base_target_alert", baseTargetAlert, extension)
+ if err != nil {
+ return "", err
+ }
+ case entities.InternalErrorAlertType:
+ var internalErrorAlert entities.InternalErrorAlert
+ err := json.Unmarshal(alertJson, &internalErrorAlert)
+ if err != nil {
+ return "", err
+ }
+ msg, err = executeTemplate("templates/alerts/internal_error_alert", internalErrorAlert, extension)
+ if err != nil {
+ return "", err
+ }
+ default:
+ return "", errors.New("unknown alert type")
+ }
+
+ return msg, nil
}
@@ -530,7 +747,7 @@ type StatusCondition struct {
Height string
}
-func HandleNodesStatusError(nodesStatusResp *pair.NodesStatusResponse, extension expectedExtension) (string, StatusCondition, error) {
+func HandleNodesStatusError(nodesStatusResp *pair.NodesStatusResponse, extension ExpectedExtension) (string, StatusCondition, error) {
statusCondition := StatusCondition{AllNodesAreOk: false, NodesNumber: 0, Height: ""}
var differentHeightsNodes []NodeStatus
@@ -568,15 +785,21 @@ func HandleNodesStatusError(nodesStatusResp *pair.NodesStatusResponse, extension
return fmt.Sprintf("%s\n\n%s", nodesStatusResp.ErrMessage, msg), statusCondition, nil
}
return nodesStatusResp.ErrMessage, statusCondition, nil
+
}
-func HandleNodesStatus(nodesStatusResp *pair.NodesStatusResponse, extension expectedExtension) (string, StatusCondition, error) {
+func HandleNodesStatus(nodesStatusResp *pair.NodesStatusResponse,
+ extension ExpectedExtension, allNodes []entities.Node) (string, StatusCondition, error) {
statusCondition := StatusCondition{AllNodesAreOk: false, NodesNumber: 0, Height: ""}
- // remove all https and http prefixes
+ nodesAliases := make(map[string]string)
+ for _, n := range allNodes {
+ if n.Alias != "" {
+ nodesAliases[n.URL] = n.Alias
+ }
+ }
for i := range nodesStatusResp.NodesStatus {
- nodesStatusResp.NodesStatus[i].Url = strings.ReplaceAll(nodesStatusResp.NodesStatus[i].Url, entities.HttpsScheme+"://", "")
- nodesStatusResp.NodesStatus[i].Url = strings.ReplaceAll(nodesStatusResp.NodesStatus[i].Url, entities.HttpScheme+"://", "")
+ nodesStatusResp.NodesStatus[i].Url = replaceNodeWithAlias(nodesStatusResp.NodesStatus[i].Url, nodesAliases)
}
if nodesStatusResp.ErrMessage != "" {
@@ -642,7 +865,7 @@ func HandleNodesStatus(nodesStatusResp *pair.NodesStatusResponse, extension expe
return msg, statusCondition, nil
}
-func HandleNodeStatement(nodeStatementResp *pair.NodeStatementResponse, extension expectedExtension) (string, error) {
+func HandleNodeStatement(nodeStatementResp *pair.NodeStatementResponse, extension ExpectedExtension) (string, error) {
nodeStatementResp.NodeStatement.Node = strings.ReplaceAll(nodeStatementResp.NodeStatement.Node, entities.HttpsScheme+"://", "")
nodeStatementResp.NodeStatement.Node = strings.ReplaceAll(nodeStatementResp.NodeStatement.Node, entities.HttpScheme+"://", "")
@@ -671,51 +894,20 @@ func HandleNodeStatement(nodeStatementResp *pair.NodeStatementResponse, extensio
return msg, nil
}
-func constructMessage(alertType entities.AlertType, alertJson []byte, extension expectedExtension) (string, error) {
+func constructMessage(alertType entities.AlertType, alertJson []byte, extension ExpectedExtension, allNodes []entities.Node) (string, error) {
alert := generalMessaging.Alert{}
err := json.Unmarshal(alertJson, &alert)
if err != nil {
return "", errors.Wrap(err, "failed to unmarshal json")
}
- prettyAlert := makeMessagePretty(alertType, alert)
-
- msg, err := executeTemplate("templates/alert", prettyAlert, extension)
+ msg, err := executeAlertTemplate(alertType, alertJson, extension, allNodes)
if err != nil {
- return "", err
+ return "", errors.Errorf("failed to execute an alert template, %v", err)
}
return msg, nil
}
-func makeMessagePretty(alertType entities.AlertType, alert generalMessaging.Alert) generalMessaging.Alert {
- alert.Details = strings.ReplaceAll(alert.Details, entities.HttpScheme+"://", "")
- alert.Details = strings.ReplaceAll(alert.Details, entities.HttpsScheme+"://", "")
- // simple alert is skipped because it needs to be deleted
- switch alertType {
- case entities.UnreachableAlertType, entities.InvalidHeightAlertType, entities.StateHashAlertType, entities.HeightAlertType:
- alert.AlertDescription += fmt.Sprintf(" %s", commonMessages.ErrorOrDeleteMsg)
- case entities.InternalErrorAlertType:
- alert.AlertDescription += fmt.Sprintf(" %s", commonMessages.WarnMsg)
- case entities.IncompleteAlertType:
- alert.AlertDescription += fmt.Sprintf(" %s", commonMessages.QuestionMsg)
- case entities.AlertFixedType:
- alert.AlertDescription += fmt.Sprintf(" %s", commonMessages.OkMsg)
- default:
-
- }
- switch alert.Level {
- case entities.InfoLevel:
- alert.Level += fmt.Sprintf(" %s", commonMessages.InfoMsg)
- case entities.WarnLevel:
- alert.Level += fmt.Sprintf(" %s", commonMessages.WarnMsg)
- case entities.ErrorLevel:
- alert.Level += fmt.Sprintf(" %s", commonMessages.ErrorOrDeleteMsg)
- default:
- }
-
- return alert
-}
-
func FindAlertTypeByName(alertName string) (entities.AlertType, bool) {
for key, val := range entities.AlertTypes {
if val == alertName {
diff --git a/cmd/bots/internal/common/init/init.go b/cmd/bots/internal/common/init/init.go
index bd9c4dd3..d02dcd18 100644
--- a/cmd/bots/internal/common/init/init.go
+++ b/cmd/bots/internal/common/init/init.go
@@ -7,6 +7,7 @@ import (
tele "gopkg.in/telebot.v3"
"nodemon/cmd/bots/internal/common"
"nodemon/cmd/bots/internal/telegram/config"
+ "nodemon/pkg/messaging/pair"
)
func InitTgBot(behavior string,
@@ -15,6 +16,8 @@ func InitTgBot(behavior string,
botToken string,
chatID int64,
logger *zap.Logger,
+ requestType chan<- pair.RequestPair,
+ responsePairType <-chan pair.ResponsePair,
) (*common.TelegramBotEnvironment, error) {
botSettings, err := config.NewTgBotSettings(behavior, webhookLocalAddress, publicURL, botToken)
if err != nil {
@@ -27,7 +30,7 @@ func InitTgBot(behavior string,
logger.Sugar().Debugf("telegram chat id for sending alerts is %d", chatID)
- tgBotEnv := common.NewTelegramBotEnvironment(bot, chatID, false, logger)
+ tgBotEnv := common.NewTelegramBotEnvironment(bot, chatID, false, logger, requestType, responsePairType)
return tgBotEnv, nil
}
@@ -35,6 +38,8 @@ func InitDiscordBot(
botToken string,
chatID string,
logger *zap.Logger,
+ requestType chan<- pair.RequestPair,
+ responsePairType <-chan pair.ResponsePair,
) (*common.DiscordBotEnvironment, error) {
bot, err := discordgo.New("Bot " + botToken)
if err != nil {
@@ -43,6 +48,6 @@ func InitDiscordBot(
logger.Sugar().Debugf("discord chat id for sending alerts is %s", chatID)
bot.Identify.Intents = discordgo.IntentsGuildMessages | discordgo.IntentsMessageContent
- dscBotEnv := common.NewDiscordBotEnvironment(bot, chatID, logger)
+ dscBotEnv := common.NewDiscordBotEnvironment(bot, chatID, logger, requestType, responsePairType)
return dscBotEnv, nil
}
diff --git a/cmd/bots/internal/common/messaging/handlers.go b/cmd/bots/internal/common/messaging/handlers.go
index 1825b11c..76fc38d3 100644
--- a/cmd/bots/internal/common/messaging/handlers.go
+++ b/cmd/bots/internal/common/messaging/handlers.go
@@ -43,6 +43,26 @@ func AddNewNodeHandler(
return fmt.Sprintf("New node '%s' was added", updatedUrl), nil
}
+func UpdateAliasHandler(
+ chatID string,
+ bot Bot,
+ requestType chan<- pair.RequestPair,
+ url string,
+ alias string) (string, error) {
+
+ if !bot.IsEligibleForAction(chatID) {
+ return insufficientPermissionMsg, InsufficientPermissionsError
+ }
+
+ updatedUrl, err := entities.CheckAndUpdateURL(url)
+ if err != nil {
+ return incorrectUrlMsg, IncorrectUrlError
+ }
+ requestType <- &pair.UpdateNodeRequest{Url: updatedUrl, Alias: alias}
+
+ return fmt.Sprintf("Node '%s' was updated with alias %s", updatedUrl, alias), nil
+}
+
func RemoveNodeHandler(
chatID string,
bot Bot,
@@ -78,18 +98,51 @@ func RequestNodesStatus(
}
-func RequestNodesList(requestType chan<- pair.RequestPair, responsePairType <-chan pair.ResponsePair, specific bool) ([]string, error) {
+func RequestNodesUrls(requestType chan<- pair.RequestPair, responsePairType <-chan pair.ResponsePair, specific bool) ([]string, error) {
requestType <- &pair.NodesListRequest{Specific: specific}
responsePair := <-responsePairType
nodesList, ok := responsePair.(*pair.NodesListResponse)
if !ok {
return nil, errors.New("failed to convert response interface to the node list type")
}
- urls := nodesList.Urls
+ urls := NodesToUrls(nodesList.Nodes)
sort.Strings(urls)
return urls, nil
}
+func RequestNodes(requestType chan<- pair.RequestPair, responsePairType <-chan pair.ResponsePair, specific bool) ([]entities.Node, error) {
+ requestType <- &pair.NodesListRequest{Specific: specific}
+ responsePair := <-responsePairType
+ nodesList, ok := responsePair.(*pair.NodesListResponse)
+ if !ok {
+ return nil, errors.New("failed to convert response interface to the node list type")
+ }
+ return nodesList.Nodes, nil
+}
+
+func RequestAllNodes(requestType chan<- pair.RequestPair, responsePairType <-chan pair.ResponsePair) ([]entities.Node, error) {
+ regularNodes, err := RequestNodes(requestType, responsePairType, false)
+ if err != nil {
+ return nil, err
+ }
+ specificNodes, err := RequestNodes(requestType, responsePairType, true)
+ if err != nil {
+ return nil, err
+ }
+ var nodes []entities.Node
+ nodes = append(nodes, regularNodes...)
+ nodes = append(nodes, specificNodes...)
+ return nodes, nil
+}
+
+func NodesToUrls(nodes []entities.Node) []string {
+ urls := make([]string, len(nodes))
+ for i, n := range nodes {
+ urls[i] = n.URL
+ }
+ return urls
+}
+
func RequestNodeStatement(requestType chan<- pair.RequestPair, responsePairType <-chan pair.ResponsePair, node string, height int) (*pair.NodeStatementResponse, error) {
requestType <- &pair.NodeStatementRequest{Url: node, Height: height}
responsePair := <-responsePairType
diff --git a/cmd/bots/internal/common/messaging/pair/client.go b/cmd/bots/internal/common/messaging/pair/client.go
index 1a77ac21..3133e725 100644
--- a/cmd/bots/internal/common/messaging/pair/client.go
+++ b/cmd/bots/internal/common/messaging/pair/client.go
@@ -77,6 +77,22 @@ func StartPairMessagingClient(ctx context.Context, nanomsgURL string, requestPai
logger.Error("failed to send message", zap.Error(err))
}
+ case *pair.UpdateNodeRequest:
+ message.WriteByte(byte(pair.RequestUpdateNode))
+ node := entities.Node{
+ URL: r.Url,
+ Enabled: true,
+ Alias: r.Alias,
+ }
+ nodeInfo, err := json.Marshal(node)
+ if err != nil {
+ logger.Error("failed to marshal node's info")
+ }
+ message.Write(nodeInfo)
+ err = pairSocket.Send(message.Bytes())
+ if err != nil {
+ logger.Error("failed to send message", zap.Error(err))
+ }
case *pair.DeleteNodeRequest:
message.WriteByte(byte(pair.RequestDeleteNodeT))
diff --git a/cmd/bots/internal/common/templates/alert.html b/cmd/bots/internal/common/templates/alert.html
deleted file mode 100644
index 42c3a667..00000000
--- a/cmd/bots/internal/common/templates/alert.html
+++ /dev/null
@@ -1,5 +0,0 @@
-Alert type: {{ .AlertDescription}}
-
-Level: {{ .Level}}
-
-Details: {{ .Details}}
diff --git a/cmd/bots/internal/common/templates/alert.md b/cmd/bots/internal/common/templates/alert.md
deleted file mode 100644
index 0d47cf78..00000000
--- a/cmd/bots/internal/common/templates/alert.md
+++ /dev/null
@@ -1,7 +0,0 @@
-```yaml
-Alert type: {{ .AlertDescription}}
-
-Level: {{ .Level}}
-
-Details: {{ .Details}}
-```
\ No newline at end of file
diff --git a/cmd/bots/internal/common/templates/alerts/alert_fixed.html b/cmd/bots/internal/common/templates/alerts/alert_fixed.html
new file mode 100644
index 00000000..3277b07c
--- /dev/null
+++ b/cmd/bots/internal/common/templates/alerts/alert_fixed.html
@@ -0,0 +1,5 @@
+Alert type: Resolved ✅
+
+Level: Info ℹ
+
+Details: The issue has been resolved: {{ .PreviousAlert}}
diff --git a/cmd/bots/internal/common/templates/alerts/alert_fixed.md b/cmd/bots/internal/common/templates/alerts/alert_fixed.md
new file mode 100644
index 00000000..07a11106
--- /dev/null
+++ b/cmd/bots/internal/common/templates/alerts/alert_fixed.md
@@ -0,0 +1,7 @@
+```yaml
+Alert type: Resolved ✅
+
+Level: Info ℹ
+
+Details: The issue has been resolved, {{ .PreviousAlert}}
+```
diff --git a/cmd/bots/internal/common/templates/alerts/base_target_alert.html b/cmd/bots/internal/common/templates/alerts/base_target_alert.html
new file mode 100644
index 00000000..a87c3fb7
--- /dev/null
+++ b/cmd/bots/internal/common/templates/alerts/base_target_alert.html
@@ -0,0 +1,12 @@
+Alert type: Base Target
+
+Level: Error ❌
+
+Details: Base target is greater than the threshold value. The threshold value is {{ .Threshold}}
+
+{{ with .BaseTargetValues }}
+{{range .}}
+ Node: {{ .Node}}
+ Base Target: {{ .BaseTarget}}
+{{end}}
+{{end}}
diff --git a/cmd/bots/internal/common/templates/alerts/base_target_alert.md b/cmd/bots/internal/common/templates/alerts/base_target_alert.md
new file mode 100644
index 00000000..d5ee1475
--- /dev/null
+++ b/cmd/bots/internal/common/templates/alerts/base_target_alert.md
@@ -0,0 +1,14 @@
+```yaml
+Alert type: Base Target
+
+Level: Error ❌
+
+Details: Base target is greater than the threshold value. The threshold value is {{ .Threshold }}
+
+{{ with .BaseTargetValues }}
+{{ range . }}
+Node: {{ .Node}}
+Base Target: {{ .BaseTarget}}
+{{end}}
+{{end}}
+```
diff --git a/cmd/bots/internal/common/templates/alerts/height_alert.html b/cmd/bots/internal/common/templates/alerts/height_alert.html
new file mode 100644
index 00000000..d2a76f6f
--- /dev/null
+++ b/cmd/bots/internal/common/templates/alerts/height_alert.html
@@ -0,0 +1,19 @@
+Alert type: Height ❌
+
+Level: Error ❌
+
+Details: Some node(s) are {{ .HeightDifference}} blocks behind
+
+{{ with .FirstGroup }}
+First group with height {{ .Height}}
:
+{{range .Nodes}}
+{{.}}
+{{end}}
+{{end}}
+
+{{ with .SecondGroup }}
+Second group with height {{ .Height}}
:
+{{range .Nodes}}
+{{.}}
+{{end}}
+{{end}}
diff --git a/cmd/bots/internal/common/templates/alerts/height_alert.md b/cmd/bots/internal/common/templates/alerts/height_alert.md
new file mode 100644
index 00000000..7ffa879f
--- /dev/null
+++ b/cmd/bots/internal/common/templates/alerts/height_alert.md
@@ -0,0 +1,21 @@
+```yaml
+Alert type: Height ❌
+
+Level: Error ❌
+
+Details: Some node(s) are {{ .HeightDifference}} blocks behind
+
+{{ with .FirstGroup }}
+First group with height {{ .Height}}:
+{{range .Nodes}}
+{{.}}
+{{end}}
+{{end}}
+
+{{ with .SecondGroup }}
+Second group with height {{ .Height}}:
+{{range .Nodes}}
+{{.}}
+{{end}}
+{{end}}
+```
diff --git a/cmd/bots/internal/common/templates/alerts/incomplete_alert.html b/cmd/bots/internal/common/templates/alerts/incomplete_alert.html
new file mode 100644
index 00000000..266f1b2c
--- /dev/null
+++ b/cmd/bots/internal/common/templates/alerts/incomplete_alert.html
@@ -0,0 +1,5 @@
+Alert type: Incomplete ❔
+
+Level: Warning ❗
+
+Details: Incomplete statement for node {{ .Node}} {{ .Version}} at height {{ .Height}}
diff --git a/cmd/bots/internal/common/templates/alerts/incomplete_alert.md b/cmd/bots/internal/common/templates/alerts/incomplete_alert.md
new file mode 100644
index 00000000..58687b59
--- /dev/null
+++ b/cmd/bots/internal/common/templates/alerts/incomplete_alert.md
@@ -0,0 +1,7 @@
+```yaml
+Alert type: Incomplete ❔
+
+Level: Warning ❗
+
+Details: Incomplete statement for node {{ .Node}} {{ .Version}} at height {{ .Height}}
+```
diff --git a/cmd/bots/internal/common/templates/alerts/internal_error_alert.html b/cmd/bots/internal/common/templates/alerts/internal_error_alert.html
new file mode 100644
index 00000000..979491fa
--- /dev/null
+++ b/cmd/bots/internal/common/templates/alerts/internal_error_alert.html
@@ -0,0 +1,5 @@
+Alert type: InternalErrorAlert ❗️
+
+Level: Warning ❗
+
+Details: An internal error has occurred: {{ .Error}}
diff --git a/cmd/bots/internal/common/templates/alerts/internal_error_alert.md b/cmd/bots/internal/common/templates/alerts/internal_error_alert.md
new file mode 100644
index 00000000..d7c59ce1
--- /dev/null
+++ b/cmd/bots/internal/common/templates/alerts/internal_error_alert.md
@@ -0,0 +1,7 @@
+```yaml
+Alert type: InternalErrorAlert ❗️
+
+Level: Warning ❗
+
+Details: An internal error has occurred, {{ .Error}}
+```
diff --git a/cmd/bots/internal/common/templates/alerts/invalid_height_alert.html b/cmd/bots/internal/common/templates/alerts/invalid_height_alert.html
new file mode 100644
index 00000000..4a417fc9
--- /dev/null
+++ b/cmd/bots/internal/common/templates/alerts/invalid_height_alert.html
@@ -0,0 +1,5 @@
+Alert type: Invalid Height ❌
+
+Level: Warning ❗
+
+Details: Node {{ .Node}} {{ .Version}} has an invalid height {{ .Height}}
diff --git a/cmd/bots/internal/common/templates/alerts/invalid_height_alert.md b/cmd/bots/internal/common/templates/alerts/invalid_height_alert.md
new file mode 100644
index 00000000..4f1274f7
--- /dev/null
+++ b/cmd/bots/internal/common/templates/alerts/invalid_height_alert.md
@@ -0,0 +1,7 @@
+```yaml
+Alert type: Invalid Height ❌
+
+Level: Warning ❗
+
+Details: Node {{ .Node}} {{ .Version}} has an invalid height {{ .Height}}
+```
diff --git a/cmd/bots/internal/common/templates/alerts/state_hash_alert.html b/cmd/bots/internal/common/templates/alerts/state_hash_alert.html
new file mode 100644
index 00000000..14081739
--- /dev/null
+++ b/cmd/bots/internal/common/templates/alerts/state_hash_alert.html
@@ -0,0 +1,31 @@
+Alert type: State Hash ❌
+
+Level: Error ❌
+
+Details: Nodes have different state hashes at the same height {{ .SameHeight}}
+
+{{ with .FirstGroup }}
+First group:
+BlockID: {{ .BlockID}}
+State Hash: {{ .StateHash}}
+Nodes:
+{{range .Nodes}}
+{{.}}
+{{end}}
+{{end}}
+
+{{ with .SecondGroup }}
+Second group:
+BlockID: {{ .BlockID}}
+State Hash: {{ .StateHash}}
+Nodes:
+{{range .Nodes}}
+{{.}}
+{{end}}
+{{end}}
+
+{{ if .LastCommonStateHashExist }}
+Fork occurred after block {{ .ForkHeight}}
+BlockID: {{ .ForkBlockID}}
+State Hash: {{ .ForkStateHash}}
+{{ end }}
diff --git a/cmd/bots/internal/common/templates/alerts/state_hash_alert.md b/cmd/bots/internal/common/templates/alerts/state_hash_alert.md
new file mode 100644
index 00000000..bd285bb6
--- /dev/null
+++ b/cmd/bots/internal/common/templates/alerts/state_hash_alert.md
@@ -0,0 +1,33 @@
+```yaml
+Alert type: State Hash ❌
+
+Level: Error ❌
+
+Details: Nodes have different state hashes at the same height {{ .SameHeight}}
+
+{{ with .FirstGroup }}
+BlockID (First group): {{ .BlockID}}
+State Hash (First group): {{ .StateHash}}
+
+{{range .Nodes}}
+{{.}}
+{{end}}
+{{end}}
+
+
+{{ with .SecondGroup }}
+BlockID (Second group): {{ .BlockID}}
+State Hash (Second group): {{ .StateHash}}
+
+{{range .Nodes}}
+{{.}}
+{{end}}
+{{end}}
+
+
+{{ if .LastCommonStateHashExist }}
+Fork occurred after block {{ .ForkHeight}}
+BlockID: {{ .ForkBlockID}}
+State Hash: {{ .ForkStateHash}}
+{{ end }}
+```
diff --git a/cmd/bots/internal/common/templates/alerts/unreachable_alert.html b/cmd/bots/internal/common/templates/alerts/unreachable_alert.html
new file mode 100644
index 00000000..e00d9cfd
--- /dev/null
+++ b/cmd/bots/internal/common/templates/alerts/unreachable_alert.html
@@ -0,0 +1,5 @@
+Alert type: Unreachable ❌
+
+Level: Error ❌
+
+Details: Node {{ .Node}} is unreachable
diff --git a/cmd/bots/internal/common/templates/alerts/unreachable_alert.md b/cmd/bots/internal/common/templates/alerts/unreachable_alert.md
new file mode 100644
index 00000000..266aa03d
--- /dev/null
+++ b/cmd/bots/internal/common/templates/alerts/unreachable_alert.md
@@ -0,0 +1,7 @@
+```yaml
+Alert type: Unreachable ❌
+
+Level: Error ❌
+
+Details: Node {{ .Node}} is unreachable
+```
diff --git a/cmd/bots/internal/common/templates_test.go b/cmd/bots/internal/common/templates_test.go
new file mode 100644
index 00000000..3d2b992d
--- /dev/null
+++ b/cmd/bots/internal/common/templates_test.go
@@ -0,0 +1,230 @@
+package common
+
+import (
+ "encoding/binary"
+ "testing"
+
+ "github.com/wavesplatform/gowaves/pkg/crypto"
+ "github.com/wavesplatform/gowaves/pkg/proto"
+ "nodemon/pkg/entities"
+)
+
+var formats = []ExpectedExtension{Html, Markdown}
+
+func TestBaseTargetTemplate(t *testing.T) {
+ data := &entities.BaseTargetAlert{
+ Timestamp: 100,
+ BaseTargetValues: []entities.BaseTargetValue{
+ {
+ Node: "test1",
+ BaseTarget: 150,
+ },
+ {
+ Node: "test2",
+ BaseTarget: 510,
+ },
+ {
+ Node: "test1",
+ BaseTarget: 150,
+ },
+ {
+ Node: "test2",
+ BaseTarget: 510,
+ },
+ {
+ Node: "test1",
+ BaseTarget: 150,
+ },
+ {
+ Node: "test2",
+ BaseTarget: 510,
+ },
+ },
+ Threshold: 101,
+ }
+ for _, f := range formats {
+ _, err := executeTemplate("templates/alerts/base_target_alert", data, f)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+}
+
+func TestUnreachableTemplate(t *testing.T) {
+ data := &entities.UnreachableAlert{
+ Timestamp: 100,
+ Node: "node",
+ }
+ for _, f := range formats {
+ _, err := executeTemplate("templates/alerts/unreachable_alert", data, f)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+}
+
+func TestAlertFixed(t *testing.T) {
+ unreachable := &entities.UnreachableAlert{
+ Timestamp: 100,
+ Node: "blabla",
+ }
+
+ data := &entities.AlertFixed{
+ Timestamp: 100,
+ Fixed: unreachable,
+ }
+
+ fixedStatement := fixedStatement{
+ PreviousAlert: data.Fixed.Message(),
+ }
+ for _, f := range formats {
+ _, err := executeTemplate("templates/alerts/alert_fixed", fixedStatement, f)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+}
+
+func TestHeightTemplate(t *testing.T) {
+ heightAlert := &entities.HeightAlert{
+ Timestamp: 100,
+ MaxHeightGroup: entities.HeightGroup{
+ Height: 2,
+ Nodes: entities.Nodes{"node 3", "node 4"},
+ },
+ OtherHeightGroup: entities.HeightGroup{
+ Height: 1,
+ Nodes: entities.Nodes{"node 1", "node 2"},
+ },
+ }
+
+ heightStatement := heightStatement{
+ HeightDifference: heightAlert.MaxHeightGroup.Height - heightAlert.OtherHeightGroup.Height,
+ FirstGroup: heightStatementGroup{
+ Nodes: heightAlert.MaxHeightGroup.Nodes,
+ Height: heightAlert.MaxHeightGroup.Height,
+ },
+ SecondGroup: heightStatementGroup{
+ Nodes: heightAlert.OtherHeightGroup.Nodes,
+ Height: heightAlert.OtherHeightGroup.Height,
+ },
+ }
+ for _, f := range formats {
+ _, err := executeTemplate("templates/alerts/height_alert", heightStatement, f)
+
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+}
+
+type shInfo struct {
+ id proto.BlockID
+ sh proto.StateHash
+}
+
+func sequentialBlockID(i int) proto.BlockID {
+ d := crypto.Digest{}
+ binary.BigEndian.PutUint64(d[:8], uint64(i))
+ return proto.NewBlockIDFromDigest(d)
+}
+
+func sequentialStateHash(blockID proto.BlockID, i int) proto.StateHash {
+ d := crypto.Digest{}
+ binary.BigEndian.PutUint64(d[:8], uint64(i))
+ return proto.StateHash{
+ BlockID: blockID,
+ SumHash: d,
+ FieldsHashes: proto.FieldsHashes{},
+ }
+}
+
+func generateStateHashes(o, n int) []shInfo {
+ r := make([]shInfo, n)
+ for i := 0; i < n; i++ {
+ id := sequentialBlockID(o + i + 1)
+ sh := sequentialStateHash(id, o+i+101)
+ r[i] = shInfo{id: id, sh: sh}
+ }
+ return r
+}
+
+func TestStateHashTemplate(t *testing.T) {
+
+ shInfo := generateStateHashes(1, 5)
+ stateHashAlert := &entities.StateHashAlert{
+ CurrentGroupsBucketHeight: 100,
+ LastCommonStateHashExist: true,
+ LastCommonStateHashHeight: 1,
+ LastCommonStateHash: shInfo[0].sh,
+ FirstGroup: entities.StateHashGroup{
+ Nodes: entities.Nodes{"a"},
+ StateHash: shInfo[0].sh,
+ },
+ SecondGroup: entities.StateHashGroup{
+ Nodes: entities.Nodes{"b"},
+ StateHash: shInfo[0].sh,
+ },
+ }
+
+ stateHashStatement := stateHashStatement{
+ SameHeight: stateHashAlert.CurrentGroupsBucketHeight,
+ LastCommonStateHashExist: stateHashAlert.LastCommonStateHashExist,
+ ForkHeight: stateHashAlert.LastCommonStateHashHeight,
+ ForkBlockID: stateHashAlert.LastCommonStateHash.BlockID.String(),
+ ForkStateHash: stateHashAlert.LastCommonStateHash.SumHash.Hex(),
+
+ FirstGroup: stateHashStatementGroup{
+ BlockID: stateHashAlert.FirstGroup.StateHash.BlockID.String(),
+ Nodes: stateHashAlert.FirstGroup.Nodes,
+ StateHash: stateHashAlert.FirstGroup.StateHash.SumHash.Hex(),
+ },
+ SecondGroup: stateHashStatementGroup{
+ BlockID: stateHashAlert.SecondGroup.StateHash.BlockID.String(),
+ Nodes: stateHashAlert.SecondGroup.Nodes,
+ StateHash: stateHashAlert.SecondGroup.StateHash.SumHash.Hex(),
+ },
+ }
+ for _, f := range formats {
+ _, err := executeTemplate("templates/alerts/state_hash_alert", stateHashStatement, f)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+}
+
+func TestIncompleteTemplate(t *testing.T) {
+ data := &entities.IncompleteAlert{
+ NodeStatement: entities.NodeStatement{Node: "a", Version: "1", Height: 1},
+ }
+ for _, f := range formats {
+ _, err := executeTemplate("templates/alerts/incomplete_alert", data, f)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+}
+
+func TestInternalErrorTemplate(t *testing.T) {
+ data := &entities.InternalErrorAlert{
+ Error: "error",
+ }
+ for _, f := range formats {
+ _, err := executeTemplate("templates/alerts/internal_error_alert", data, f)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+}
+
+func TestInvalidHeightTemplate(t *testing.T) {
+ data := &entities.InvalidHeightAlert{
+ NodeStatement: entities.NodeStatement{Node: "a", Version: "1", Height: 1},
+ }
+ for _, f := range formats {
+ _, err := executeTemplate("templates/alerts/invalid_height_alert", data, f)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+}
diff --git a/cmd/bots/internal/discord/handlers/handlers.go b/cmd/bots/internal/discord/handlers/handlers.go
index 9b4d4e59..9c9d0e79 100644
--- a/cmd/bots/internal/discord/handlers/handlers.go
+++ b/cmd/bots/internal/discord/handlers/handlers.go
@@ -13,7 +13,7 @@ import (
"nodemon/pkg/messaging/pair"
)
-func InitDscHandlers(environment *common.DiscordBotEnvironment, requestType chan<- pair.RequestPair, responsePairType <-chan pair.ResponsePair) {
+func InitDscHandlers(environment *common.DiscordBotEnvironment, requestType chan<- pair.RequestPair, responsePairType <-chan pair.ResponsePair, logger *zap.Logger) {
environment.Bot.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
if m.Author.ID == s.State.User.ID {
return
@@ -21,35 +21,31 @@ func InitDscHandlers(environment *common.DiscordBotEnvironment, requestType chan
if m.Content == "/ping" {
_, err := s.ChannelMessageSend(environment.ChatID, "Pong!")
if err != nil {
- environment.Zap.Error("failed to send a message to discord", zap.Error(err))
+ logger.Error("failed to send a message to discord", zap.Error(err))
}
}
if m.Content == "/help" {
_, err := s.ChannelMessageSend(environment.ChatID, messages.HelpInfoText)
if err != nil {
- environment.Zap.Error("failed to send a message to discord", zap.Error(err))
+ logger.Error("failed to send a message to discord", zap.Error(err))
}
}
if m.Content == "/status" {
- urls, err := messaging.RequestNodesList(requestType, responsePairType, false)
+ nodes, err := messaging.RequestAllNodes(requestType, responsePairType)
if err != nil {
- environment.Zap.Error("failed to get a list of nodes", zap.Error(err))
+ logger.Error("failed to get nodes list", zap.Error(err))
}
- additionalUrls, err := messaging.RequestNodesList(requestType, responsePairType, true)
- if err != nil {
- environment.Zap.Error("failed to get a list of nodes", zap.Error(err))
- }
- urls = append(urls, additionalUrls...)
+ urls := messaging.NodesToUrls(nodes)
nodesStatus, err := messaging.RequestNodesStatus(requestType, responsePairType, urls)
if err != nil {
- environment.Zap.Error("failed to request nodes status", zap.Error(err))
+ logger.Error("failed to request nodes status", zap.Error(err))
}
- msg, statusCondition, err := common.HandleNodesStatus(nodesStatus, common.Markdown)
+ msg, statusCondition, err := common.HandleNodesStatus(nodesStatus, common.Markdown, nodes)
if err != nil {
- environment.Zap.Error("failed to handle nodes status", zap.Error(err))
+ logger.Error("failed to handle nodes status", zap.Error(err))
}
if statusCondition.AllNodesAreOk {
msg = fmt.Sprintf("%d %s", statusCondition.NodesNumber, msg)
@@ -58,7 +54,7 @@ func InitDscHandlers(environment *common.DiscordBotEnvironment, requestType chan
msg = fmt.Sprintf("```yaml\n%s\n```", msg)
_, err = s.ChannelMessageSend(environment.ChatID, msg)
if err != nil {
- environment.Zap.Error("failed to send a message to discord", zap.Error(err))
+ logger.Error("failed to send a message to discord", zap.Error(err))
}
}
@@ -67,7 +63,7 @@ func InitDscHandlers(environment *common.DiscordBotEnvironment, requestType chan
if url == "" {
_, err := s.ChannelMessageSend(environment.ChatID, "Please provide a URL to add")
if err != nil {
- environment.Zap.Error("failed to send a message to discord", zap.Error(err))
+ logger.Error("failed to send a message to discord", zap.Error(err))
}
return
}
@@ -75,7 +71,7 @@ func InitDscHandlers(environment *common.DiscordBotEnvironment, requestType chan
if err != nil {
_, err := s.ChannelMessageSend(environment.ChatID, "Failed to add a node, "+err.Error())
if err != nil {
- environment.Zap.Error("failed to send a message to discord", zap.Error(err))
+ logger.Error("failed to send a message to discord", zap.Error(err))
}
}
}
@@ -85,7 +81,7 @@ func InitDscHandlers(environment *common.DiscordBotEnvironment, requestType chan
if url == "" {
_, err := s.ChannelMessageSend(environment.ChatID, "Please provide a URL to remove")
if err != nil {
- environment.Zap.Error("failed to send a message to discord", zap.Error(err))
+ logger.Error("failed to send a message to discord", zap.Error(err))
}
return
}
@@ -93,7 +89,7 @@ func InitDscHandlers(environment *common.DiscordBotEnvironment, requestType chan
if err != nil {
_, err := s.ChannelMessageSend(environment.ChatID, "Failed to remove a node, "+err.Error())
if err != nil {
- environment.Zap.Error("failed to send a message to discord", zap.Error(err))
+ logger.Error("failed to send a message to discord", zap.Error(err))
}
}
}
diff --git a/cmd/bots/internal/telegram/handlers/handlers.go b/cmd/bots/internal/telegram/handlers/handlers.go
index 088bc573..a036bdff 100644
--- a/cmd/bots/internal/telegram/handlers/handlers.go
+++ b/cmd/bots/internal/telegram/handlers/handlers.go
@@ -119,7 +119,7 @@ func InitTgHandlers(environment *common.TelegramBotEnvironment, requestType chan
if err != nil {
return nil
}
- urls, err := messaging.RequestNodesList(requestType, responsePairType, false)
+ urls, err := messaging.RequestNodesUrls(requestType, responsePairType, false)
if err != nil {
return errors.Wrap(err, "failed to request nodes list buttons")
}
@@ -154,6 +154,44 @@ func InitTgHandlers(environment *common.TelegramBotEnvironment, requestType chan
)
}
return RemoveNodeHandler(c, environment, requestType, responsePairType, args[0])
+
+ })
+
+ environment.Bot.Handle("/add_alias", func(c tele.Context) error {
+ args := c.Args()
+ if len(args) != 2 {
+ return c.Send(
+ messages.AliasWrongFormat,
+ &tele.SendOptions{
+ ParseMode: tele.ModeDefault,
+ },
+ )
+ }
+ return UpdateAliasHandler(c, environment, requestType, args[0], args[1])
+ })
+
+ environment.Bot.Handle("/aliases", func(c tele.Context) error {
+ nodes, err := messaging.RequestAllNodes(requestType, responsePairType)
+ if err != nil {
+ environment.Zap.Error("failed to request nodes list", zap.Error(err))
+ return errors.Wrap(err, "failed to get nodes list")
+ }
+ var msg string
+ for _, n := range nodes {
+ if n.Alias != "" {
+ msg += fmt.Sprintf("Node: %s\nAlias: %s\n\n", n.URL, n.Alias)
+ }
+ }
+ if msg == "" {
+ msg = "No aliases have been found"
+ }
+
+ return c.Send(
+ msg,
+ &tele.SendOptions{
+ ParseMode: tele.ModeHTML,
+ },
+ )
})
environment.Bot.Handle("/subscribe", func(c tele.Context) error {
@@ -276,17 +314,11 @@ func InitTgHandlers(environment *common.TelegramBotEnvironment, requestType chan
})
environment.Bot.Handle("/status", func(c tele.Context) error {
- urls, err := messaging.RequestNodesList(requestType, responsePairType, false)
+ nodes, err := messaging.RequestAllNodes(requestType, responsePairType)
if err != nil {
- environment.Zap.Error("failed to request nodes list buttons", zap.Error(err))
- return err
- }
- additionalUrls, err := messaging.RequestNodesList(requestType, responsePairType, true)
- if err != nil {
- environment.Zap.Error("failed to request list of specific nodes", zap.Error(err))
- return err
+ environment.Zap.Error("failed to get nodes list", zap.Error(err))
}
- urls = append(urls, additionalUrls...)
+ urls := messaging.NodesToUrls(nodes)
nodesStatus, err := messaging.RequestNodesStatus(requestType, responsePairType, urls)
if err != nil {
@@ -294,7 +326,7 @@ func InitTgHandlers(environment *common.TelegramBotEnvironment, requestType chan
return err
}
- msg, statusCondition, err := common.HandleNodesStatus(nodesStatus, common.Html)
+ msg, statusCondition, err := common.HandleNodesStatus(nodesStatus, common.Html, nodes)
if err != nil {
environment.Zap.Error("failed to handle status of nodes", zap.Error(err))
return err
@@ -320,7 +352,7 @@ func EditPool(
requestType chan<- pair.RequestPair,
responsePairType <-chan pair.ResponsePair) error {
- urls, err := messaging.RequestNodesList(requestType, responsePairType, false)
+ urls, err := messaging.RequestNodesUrls(requestType, responsePairType, false)
if err != nil {
return errors.Wrap(err, "failed to request nodes list buttons")
}
diff --git a/cmd/bots/internal/telegram/handlers/text.go b/cmd/bots/internal/telegram/handlers/text.go
index d5fd3b68..5bf3a891 100644
--- a/cmd/bots/internal/telegram/handlers/text.go
+++ b/cmd/bots/internal/telegram/handlers/text.go
@@ -34,7 +34,7 @@ func AddNewNodeHandler(
if err != nil {
return nil
}
- urls, err := messaging.RequestNodesList(requestType, responsePairType, false)
+ urls, err := messaging.RequestNodesUrls(requestType, responsePairType, false)
if err != nil {
return errors.Wrap(err, "failed to request nodes list buttons")
}
@@ -73,7 +73,7 @@ func RemoveNodeHandler(
return err
}
- urls, err := messaging.RequestNodesList(requestType, responsePairType, false)
+ urls, err := messaging.RequestNodesUrls(requestType, responsePairType, false)
if err != nil {
return errors.Wrap(err, "failed to request nodes list buttons")
}
@@ -89,6 +89,28 @@ func RemoveNodeHandler(
)
}
+func UpdateAliasHandler(
+ c tele.Context,
+ environment *common.TelegramBotEnvironment,
+ requestType chan<- pair.RequestPair,
+ url string,
+ alias string) error {
+
+ response, err := messaging.UpdateAliasHandler(strconv.FormatInt(c.Chat().ID, 10), environment, requestType, url, alias)
+ if err != nil {
+ if errors.Is(err, messaging.IncorrectUrlError) || errors.Is(err, messaging.InsufficientPermissionsError) {
+ return c.Send(
+ response,
+ &tele.SendOptions{ParseMode: tele.ModeDefault},
+ )
+ }
+ return errors.Wrap(err, "failed to update a node")
+ }
+ return c.Send(
+ response,
+ &tele.SendOptions{ParseMode: tele.ModeHTML})
+}
+
func SubscribeHandler(
c tele.Context,
environment *common.TelegramBotEnvironment,
diff --git a/cmd/bots/internal/telegram/messages/base_messages.go b/cmd/bots/internal/telegram/messages/base_messages.go
index f5d10f94..60261e11 100644
--- a/cmd/bots/internal/telegram/messages/base_messages.go
+++ b/cmd/bots/internal/telegram/messages/base_messages.go
@@ -16,7 +16,9 @@ const (
"/add node - to add a node to the list\n" +
"/remove node - to remove a node from the list\n" +
"/subscribe alert name - to subscribe to a specific alert\n" +
- "/unsubscribe alert name - to unsubscribe from a specific alert"
+ "/unsubscribe alert name - to unsubscribe from a specific alert" +
+ "/add_alias node alias" +
+ "/aliases - to see the matching list with aliases"
MuteText = "Say no more..." + messages.SleepingMsg
PongText = "Pong!" + messages.PongMsg
diff --git a/cmd/bots/internal/telegram/messages/error_messages.go b/cmd/bots/internal/telegram/messages/error_messages.go
index 42a9b3e2..650175d4 100644
--- a/cmd/bots/internal/telegram/messages/error_messages.go
+++ b/cmd/bots/internal/telegram/messages/error_messages.go
@@ -5,6 +5,7 @@ const (
AddedLessThanOne = "You should add a node"
RemovedMoreThanOne = "You can remove only one node at a time"
RemovedLessThanOne = "You should remove a node"
+ AliasWrongFormat = "Format: /add_alias "
SubscribedToMoreThanOne = "You can subscribe to only one node at a time"
SubscribedToLessThanOne = "You should subscribe to a node"
UnsubscribedFromMoreThanOne = "You can unsubscribe from only one node at a time"
diff --git a/cmd/bots/telegram/telegram.go b/cmd/bots/telegram/telegram.go
index 71155f3d..e4e73fa5 100644
--- a/cmd/bots/telegram/telegram.go
+++ b/cmd/bots/telegram/telegram.go
@@ -76,13 +76,14 @@ func runTelegramBot() error {
ctx, done := signal.NotifyContext(context.Background(), os.Interrupt)
defer done()
- tgBotEnv, err := initial.InitTgBot(behavior, webhookLocalAddress, publicURL, tgBotToken, tgChatID, zap)
+ pairRequest := make(chan pairResponses.RequestPair)
+ pairResponse := make(chan pairResponses.ResponsePair)
+
+ tgBotEnv, err := initial.InitTgBot(behavior, webhookLocalAddress, publicURL, tgBotToken, tgChatID, zap, pairRequest, pairResponse)
if err != nil {
zap.Fatal("failed to initialize telegram bot", zapLogger.Error(err))
}
- pairRequest := make(chan pairResponses.RequestPair)
- pairResponse := make(chan pairResponses.ResponsePair)
handlers.InitTgHandlers(tgBotEnv, pairRequest, pairResponse)
go func() {
diff --git a/pkg/entities/node.go b/pkg/entities/node.go
index 96153a48..004f83a7 100644
--- a/pkg/entities/node.go
+++ b/pkg/entities/node.go
@@ -15,6 +15,7 @@ const (
type Node struct {
URL string `json:"url"`
Enabled bool `json:"enabled"`
+ Alias string `json:"alias"`
}
func CheckAndUpdateURL(s string) (string, error) {
diff --git a/pkg/messaging/pair/request_types.go b/pkg/messaging/pair/request_types.go
index 5f37d585..13d59e8e 100644
--- a/pkg/messaging/pair/request_types.go
+++ b/pkg/messaging/pair/request_types.go
@@ -7,6 +7,7 @@ const (
RequestSpecificNodeListT
RequestInsertNewNodeT
RequestInsertSpecificNewNodeT
+ RequestUpdateNode
RequestDeleteNodeT
RequestNodesStatus
RequestNodeStatement
diff --git a/pkg/messaging/pair/requests.go b/pkg/messaging/pair/requests.go
index ca4b1898..ec9be8e7 100644
--- a/pkg/messaging/pair/requests.go
+++ b/pkg/messaging/pair/requests.go
@@ -15,6 +15,11 @@ type InsertNewNodeRequest struct {
Specific bool
}
+type UpdateNodeRequest struct {
+ Url string
+ Alias string
+}
+
type DeleteNodeRequest struct {
Url string
}
@@ -28,6 +33,8 @@ func (nl *NodesListRequest) requestMarker() {}
func (nl *InsertNewNodeRequest) requestMarker() {}
+func (nl *UpdateNodeRequest) requestMarker() {}
+
func (nl *DeleteNodeRequest) requestMarker() {}
func (nl *NodesStatusRequest) requestMarker() {}
diff --git a/pkg/messaging/pair/responses.go b/pkg/messaging/pair/responses.go
index 8566132d..4408d68a 100644
--- a/pkg/messaging/pair/responses.go
+++ b/pkg/messaging/pair/responses.go
@@ -8,7 +8,7 @@ import (
type ResponsePair interface{ responseMarker() }
type NodesListResponse struct {
- Urls []string `json:"urls"`
+ Nodes []entities.Node `json:"nodes"`
}
type NodesStatusResponse struct {
diff --git a/pkg/messaging/pair/server.go b/pkg/messaging/pair/server.go
index aea4698b..89d028e5 100644
--- a/pkg/messaging/pair/server.go
+++ b/pkg/messaging/pair/server.go
@@ -59,12 +59,7 @@ func StartPairMessagingServer(ctx context.Context, nanomsgURL string, ns *nodes.
return err
}
}
- var nodeList NodesListResponse
-
- nodeList.Urls = make([]string, len(nodes))
- for i, node := range nodes {
- nodeList.Urls[i] = node.URL
- }
+ nodeList := NodesListResponse{Nodes: nodes}
response, err := json.Marshal(nodeList)
if err != nil {
logger.Error("Failed to marshal node list to json", zap.Error(err))
@@ -76,16 +71,27 @@ func StartPairMessagingServer(ctx context.Context, nanomsgURL string, ns *nodes.
case RequestInsertNewNodeT:
url := msg[1:]
- err := ns.InsertIfNew(string(url))
+ err := ns.InsertIfNew(string(url), false)
if err != nil {
logger.Error("Failed to insert a new node to storage", zap.Error(err))
}
case RequestInsertSpecificNewNodeT:
url := msg[1:]
- err := ns.InsertSpecificIfNew(string(url))
+ err := ns.InsertIfNew(string(url), true)
+ if err != nil {
+ logger.Error("Failed to insert a new specific node to storage", zap.Error(err))
+ }
+ case RequestUpdateNode:
+ node := entities.Node{}
+ err := json.Unmarshal(msg[1:], &node)
+ if err != nil {
+ logger.Error("Failed to update a specific node", zap.Error(err))
+ }
+ err = ns.Update(node)
if err != nil {
logger.Error("Failed to insert a new specific node to storage", zap.Error(err))
}
+
case RequestDeleteNodeT:
url := msg[1:]
err := ns.Delete(string(url))
diff --git a/pkg/messaging/pubsub/server.go b/pkg/messaging/pubsub/server.go
index 05375637..c83ad42d 100644
--- a/pkg/messaging/pubsub/server.go
+++ b/pkg/messaging/pubsub/server.go
@@ -12,7 +12,6 @@ import (
_ "go.nanomsg.org/mangos/v3/transport/all"
"go.uber.org/zap"
"nodemon/pkg/entities"
- "nodemon/pkg/messaging"
)
func StartPubMessagingServer(ctx context.Context, nanomsgURL string, alerts <-chan entities.Alert, logger *zap.Logger) error {
@@ -41,14 +40,9 @@ func StartPubMessagingServer(ctx context.Context, nanomsgURL string, alerts <-ch
case alert := <-alerts:
logger.Sugar().Infof("Alert has been generated: %v", alert)
- jsonAlert, err := json.Marshal(
- messaging.Alert{
- AlertDescription: alert.ShortDescription(),
- Level: alert.Level(),
- Details: alert.Message(),
- })
+ jsonAlert, err := json.Marshal(alert)
if err != nil {
- logger.Error("Failed to marshal alert to json", zap.Error(err))
+ logger.Error("Failed to marshal an alert to json", zap.Error(err))
}
message := &bytes.Buffer{}
diff --git a/pkg/storing/nodes/nodes.go b/pkg/storing/nodes/nodes.go
index 7ac06673..ce0560d4 100644
--- a/pkg/storing/nodes/nodes.go
+++ b/pkg/storing/nodes/nodes.go
@@ -16,21 +16,21 @@ const (
specificNodesTableName = "specific_nodes"
)
-type node struct {
+type nodeRecord struct {
ID int `json:"id"`
entities.Node
}
-func (n *node) GetID() int {
+func (n *nodeRecord) GetID() int {
return n.ID
}
-func (n *node) SetID(id int) {
+func (n *nodeRecord) SetID(id int) {
n.ID = id
}
// AfterFind required by Hare function. Don't ask why.
-func (n *node) AfterFind(_ *hare.Database) error {
+func (n *nodeRecord) AfterFind(_ *hare.Database) error {
return nil
}
@@ -71,49 +71,86 @@ func (cs *Storage) Close() error {
}
func (cs *Storage) Nodes(specific bool) ([]entities.Node, error) {
- return cs.queryNodes(func(_ node) bool { return true }, 0, specific)
+ nodesRecord, err := cs.queryNodes(func(_ nodeRecord) bool { return true }, 0, specific)
+ if err != nil {
+ return nil, err
+ }
+ return nodesFromRecords(nodesRecord), nil
}
func (cs *Storage) EnabledNodes() ([]entities.Node, error) {
- return cs.queryNodes(func(n node) bool { return n.Enabled }, 0, false)
+ nodesRecords, err := cs.queryNodes(func(n nodeRecord) bool { return n.Enabled }, 0, false)
+ if err != nil {
+ return nil, err
+ }
+ return nodesFromRecords(nodesRecords), nil
}
func (cs *Storage) EnabledSpecificNodes() ([]entities.Node, error) {
- return cs.queryNodes(func(n node) bool { return n.Enabled }, 0, true)
+ nodesRecords, err := cs.queryNodes(func(n nodeRecord) bool { return n.Enabled }, 0, true)
+ if err != nil {
+ return nil, err
+ }
+ return nodesFromRecords(nodesRecords), nil
}
-func (cs *Storage) InsertIfNew(url string) error {
- ids, err := cs.queryNodes(func(n node) bool { return n.URL == url }, 0, false)
+// Update handles both specific and non-specific nodes
+func (cs *Storage) Update(nodeToUpdate entities.Node) error {
+ specific := false
+ ids, err := cs.queryNodes(func(n nodeRecord) bool { return n.URL == nodeToUpdate.URL }, 0, false)
if err != nil {
return err
}
if len(ids) == 0 {
- id, err := cs.db.Insert(nodesTableName, &node{Node: entities.Node{
- URL: url,
- Enabled: true,
- }})
+ // look for the url in the specific nodes table
+ ids, err = cs.queryNodes(func(n nodeRecord) bool { return n.URL == nodeToUpdate.URL }, 0, true)
if err != nil {
return err
}
- cs.zap.Sugar().Infof("New node #%d at '%s' was stored", id, url)
+ specific = true
+ if len(ids) == 0 {
+ return errors.Errorf("nodeRecord %s was not found in the storage", nodeToUpdate.URL)
+ }
+ }
+ if len(ids) != 1 {
+ return errors.Errorf("failed to update a nodeRecord in the storage, multiple nodes were found")
+ }
+ tableName := nodesTableName
+ if specific {
+ tableName = specificNodesTableName
+ }
+
+ pulledRecord := ids[0]
+ err = cs.db.Update(tableName, &nodeRecord{Node: entities.Node{
+ URL: pulledRecord.URL,
+ Enabled: pulledRecord.Enabled,
+ Alias: nodeToUpdate.Alias,
+ }, ID: pulledRecord.ID})
+ if err != nil {
+ return err
}
+ cs.zap.Sugar().Infof("New nodeRecord '%s' was updated with alias %s", pulledRecord.URL, nodeToUpdate.Alias)
return nil
}
-func (cs *Storage) InsertSpecificIfNew(url string) error {
- ids, err := cs.queryNodes(func(n node) bool { return n.URL == url }, 0, true)
+func (cs *Storage) InsertIfNew(url string, specific bool) error {
+ ids, err := cs.queryNodes(func(n nodeRecord) bool { return n.URL == url }, 0, false)
if err != nil {
return err
}
+ tableName := nodesTableName
+ if specific {
+ tableName = specificNodesTableName
+ }
if len(ids) == 0 {
- id, err := cs.db.Insert(specificNodesTableName, &node{Node: entities.Node{
+ id, err := cs.db.Insert(tableName, &nodeRecord{Node: entities.Node{
URL: url,
Enabled: true,
}})
if err != nil {
return err
}
- cs.zap.Sugar().Infof("New node #%d at '%s' was stored", id, url)
+ cs.zap.Sugar().Infof("New nodeRecord #%d at '%s' was stored", id, url)
}
return nil
}
@@ -124,7 +161,7 @@ func (cs *Storage) Delete(url string) error {
return err
}
for _, id := range ids {
- var n node
+ var n nodeRecord
if err := cs.db.Find(nodesTableName, id, &n); err != nil {
return err
}
@@ -141,23 +178,46 @@ func (cs *Storage) Delete(url string) error {
return nil
}
-func (cs *Storage) queryNodes(queryFn func(n node) bool, limit int, specific bool) ([]entities.Node, error) {
+func (cs *Storage) FindAlias(url string) (string, error) {
+ ids, err := cs.queryNodes(func(n nodeRecord) bool { return n.URL == url }, 0, false)
+ if err != nil {
+ return "", err
+ }
+ if len(ids) == 0 {
+ // look for the url in the specific nodes table
+ ids, err = cs.queryNodes(func(n nodeRecord) bool { return n.URL == url }, 0, true)
+ if err != nil {
+ return "", err
+ }
+ if len(ids) == 0 {
+ return "", errors.Errorf("nodeRecord %s was not found in the storage", url)
+ }
+ }
+ if len(ids) != 1 {
+ return "", errors.Errorf("failed to update a nodeRecord in the storage, multiple nodes were found")
+ }
+
+ return ids[0].Alias, nil
+}
+
+func (cs *Storage) queryNodes(queryFn func(n nodeRecord) bool, limit int, specific bool) ([]nodeRecord, error) {
table := nodesTableName
if specific {
table = specificNodesTableName
}
- var results []entities.Node
+ var results []nodeRecord
ids, err := cs.db.IDs(table)
if err != nil {
return nil, err
}
for _, id := range ids {
- var n node
+ var n nodeRecord
if err := cs.db.Find(table, id, &n); err != nil {
return nil, err
}
if queryFn(n) {
- results = append(results, n.Node)
+ n.ID = id
+ results = append(results, n)
}
if limit != 0 && limit == len(results) {
break
@@ -171,10 +231,18 @@ func (cs *Storage) populate(nodes string) error {
if err != nil {
return err
}
- err = cs.InsertIfNew(url)
+ err = cs.InsertIfNew(url, false)
if err != nil {
return err
}
}
return nil
}
+
+func nodesFromRecords(records []nodeRecord) []entities.Node {
+ var nodes []entities.Node
+ for _, r := range records {
+ nodes = append(nodes, r.Node)
+ }
+ return nodes
+}