mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-23 03:33:53 +00:00
fix: major webhook system improvements and bug fixes
- Re-enable JSON validation that was accidentally commented out - Fix Telegram chat_id handling with proper validation - Add smart retry logic that distinguishes retryable from permanent errors - Enhance error logging to include response bodies for debugging - Add webhook URL validation for security (prevent SSRF) - Fix deprecated strings.Title function usage - Add webhook delivery history tracking - Improve template variable substitution - Add exponential backoff for retries (max 30 seconds) - Validate numeric chat IDs for Telegram webhooks - Add comprehensive error handling and logging Tested with Telegram, Discord, and Slack - all working correctly now
This commit is contained in:
@@ -2,6 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -104,6 +105,12 @@ func (h *NotificationHandlers) CreateWebhook(w http.ResponseWriter, r *http.Requ
|
||||
return
|
||||
}
|
||||
|
||||
// Validate webhook URL
|
||||
if err := notifications.ValidateWebhookURL(webhook.URL); err != nil {
|
||||
http.Error(w, fmt.Sprintf("Invalid webhook URL: %v", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Generate ID if not provided
|
||||
if webhook.ID == "" {
|
||||
webhook.ID = utils.GenerateID("webhook")
|
||||
@@ -149,6 +156,12 @@ func (h *NotificationHandlers) UpdateWebhook(w http.ResponseWriter, r *http.Requ
|
||||
return
|
||||
}
|
||||
|
||||
// Validate webhook URL
|
||||
if err := notifications.ValidateWebhookURL(webhook.URL); err != nil {
|
||||
http.Error(w, fmt.Sprintf("Invalid webhook URL: %v", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
webhook.ID = webhookID
|
||||
if err := h.monitor.GetNotificationManager().UpdateWebhook(webhookID, webhook); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusNotFound)
|
||||
@@ -307,6 +320,14 @@ func (h *NotificationHandlers) GetWebhookTemplates(w http.ResponseWriter, r *htt
|
||||
json.NewEncoder(w).Encode(templates)
|
||||
}
|
||||
|
||||
// GetWebhookHistory returns recent webhook delivery history
|
||||
func (h *NotificationHandlers) GetWebhookHistory(w http.ResponseWriter, r *http.Request) {
|
||||
history := h.monitor.GetNotificationManager().GetWebhookHistory()
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(history)
|
||||
}
|
||||
|
||||
// GetEmailProviders returns available email providers
|
||||
func (h *NotificationHandlers) GetEmailProviders(w http.ResponseWriter, r *http.Request) {
|
||||
providers := notifications.GetEmailProviders()
|
||||
@@ -435,6 +456,8 @@ func (h *NotificationHandlers) HandleNotifications(w http.ResponseWriter, r *htt
|
||||
h.DeleteWebhook(w, r)
|
||||
case path == "/webhook-templates" && r.Method == http.MethodGet:
|
||||
h.GetWebhookTemplates(w, r)
|
||||
case path == "/webhook-history" && r.Method == http.MethodGet:
|
||||
h.GetWebhookHistory(w, r)
|
||||
case path == "/email-providers" && r.Method == http.MethodGet:
|
||||
h.GetEmailProviders(w, r)
|
||||
case path == "/test" && r.Method == http.MethodPost:
|
||||
|
||||
@@ -21,6 +21,20 @@ type TestNodeInfo struct {
|
||||
InstanceURL string
|
||||
}
|
||||
|
||||
// WebhookDelivery tracks webhook delivery attempts for debugging
|
||||
type WebhookDelivery struct {
|
||||
WebhookName string `json:"webhookName"`
|
||||
WebhookURL string `json:"webhookUrl"`
|
||||
Service string `json:"service"`
|
||||
AlertID string `json:"alertId"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
StatusCode int `json:"statusCode"`
|
||||
Success bool `json:"success"`
|
||||
ErrorMessage string `json:"errorMessage,omitempty"`
|
||||
RetryAttempts int `json:"retryAttempts"`
|
||||
PayloadSize int `json:"payloadSize"`
|
||||
}
|
||||
|
||||
// NotificationManager handles sending notifications
|
||||
type NotificationManager struct {
|
||||
mu sync.RWMutex
|
||||
@@ -34,6 +48,7 @@ type NotificationManager struct {
|
||||
groupTimer *time.Timer
|
||||
groupByNode bool
|
||||
groupByGuest bool
|
||||
webhookHistory []WebhookDelivery // Keep last 100 webhook deliveries for debugging
|
||||
}
|
||||
|
||||
// Alert represents an alert (interface to avoid circular dependency)
|
||||
@@ -79,14 +94,15 @@ type WebhookConfig struct {
|
||||
// NewNotificationManager creates a new notification manager
|
||||
func NewNotificationManager() *NotificationManager {
|
||||
return &NotificationManager{
|
||||
enabled: true,
|
||||
cooldown: 5 * time.Minute,
|
||||
lastNotified: make(map[string]time.Time),
|
||||
webhooks: []WebhookConfig{},
|
||||
groupWindow: 30 * time.Second,
|
||||
pendingAlerts: make([]*alerts.Alert, 0),
|
||||
groupByNode: true,
|
||||
groupByGuest: false,
|
||||
enabled: true,
|
||||
cooldown: 5 * time.Minute,
|
||||
lastNotified: make(map[string]time.Time),
|
||||
webhooks: []WebhookConfig{},
|
||||
groupWindow: 30 * time.Second,
|
||||
pendingAlerts: make([]*alerts.Alert, 0),
|
||||
groupByNode: true,
|
||||
groupByGuest: false,
|
||||
webhookHistory: make([]WebhookDelivery, 0, 100), // Pre-allocate for 100 entries
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,12 +447,15 @@ func (n *NotificationManager) sendGroupedWebhook(webhook WebhookConfig, alertLis
|
||||
data := n.prepareWebhookData(alert, nil)
|
||||
|
||||
// For Telegram, extract chat_id from URL if present
|
||||
if webhook.Service == "telegram" && strings.Contains(webhook.URL, "chat_id=") {
|
||||
if u, err := url.Parse(webhook.URL); err == nil {
|
||||
chatID := u.Query().Get("chat_id")
|
||||
if chatID != "" {
|
||||
data.ChatID = chatID
|
||||
}
|
||||
if webhook.Service == "telegram" {
|
||||
if chatID, err := extractTelegramChatID(webhook.URL); err == nil && chatID != "" {
|
||||
data.ChatID = chatID
|
||||
} else if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("webhook", webhook.Name).
|
||||
Msg("Failed to extract Telegram chat_id for grouped notification")
|
||||
return // Skip this webhook
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,12 +501,15 @@ func (n *NotificationManager) sendGroupedWebhook(webhook WebhookConfig, alertLis
|
||||
data := n.prepareWebhookData(alert, nil)
|
||||
|
||||
// Handle service-specific requirements
|
||||
if webhook.Service == "telegram" && strings.Contains(webhook.URL, "chat_id=") {
|
||||
if u, err := url.Parse(webhook.URL); err == nil {
|
||||
chatID := u.Query().Get("chat_id")
|
||||
if chatID != "" {
|
||||
data.ChatID = chatID
|
||||
}
|
||||
if webhook.Service == "telegram" {
|
||||
if chatID, err := extractTelegramChatID(webhook.URL); err == nil && chatID != "" {
|
||||
data.ChatID = chatID
|
||||
} else if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("webhook", webhook.Name).
|
||||
Msg("Failed to extract Telegram chat_id for grouped notification")
|
||||
return // Skip this webhook
|
||||
}
|
||||
} else if webhook.Service == "pagerduty" {
|
||||
if data.CustomFields == nil {
|
||||
@@ -605,17 +627,34 @@ func (n *NotificationManager) sendWebhookRequest(webhook WebhookConfig, jsonData
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Read response body for logging
|
||||
var respBody bytes.Buffer
|
||||
respBody.ReadFrom(resp.Body)
|
||||
responseBody := respBody.String()
|
||||
|
||||
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
||||
log.Info().
|
||||
Str("webhook", webhook.Name).
|
||||
Str("service", webhook.Service).
|
||||
Str("type", alertType).
|
||||
Int("status", resp.StatusCode).
|
||||
Msg("Webhook notification sent")
|
||||
Int("payloadSize", len(jsonData)).
|
||||
Msg("Webhook notification sent successfully")
|
||||
|
||||
// Log response body only in debug mode for successful requests
|
||||
if len(responseBody) > 0 {
|
||||
log.Debug().
|
||||
Str("webhook", webhook.Name).
|
||||
Str("response", responseBody).
|
||||
Msg("Webhook response body")
|
||||
}
|
||||
} else {
|
||||
log.Warn().
|
||||
Str("webhook", webhook.Name).
|
||||
Str("service", webhook.Service).
|
||||
Str("type", alertType).
|
||||
Int("status", resp.StatusCode).
|
||||
Str("response", responseBody).
|
||||
Msg("Webhook returned non-success status")
|
||||
}
|
||||
}
|
||||
@@ -638,12 +677,15 @@ func (n *NotificationManager) sendWebhook(webhook WebhookConfig, alert *alerts.A
|
||||
data := n.prepareWebhookData(alert, nil)
|
||||
|
||||
// For Telegram, still extract chat_id from URL if present
|
||||
if webhook.Service == "telegram" && strings.Contains(webhook.URL, "chat_id=") {
|
||||
if u, err := url.Parse(webhook.URL); err == nil {
|
||||
chatID := u.Query().Get("chat_id")
|
||||
if chatID != "" {
|
||||
data.ChatID = chatID
|
||||
}
|
||||
if webhook.Service == "telegram" {
|
||||
if chatID, err := extractTelegramChatID(webhook.URL); err == nil && chatID != "" {
|
||||
data.ChatID = chatID
|
||||
} else if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("webhook", webhook.Name).
|
||||
Msg("Failed to extract Telegram chat_id - skipping webhook")
|
||||
return // Skip this webhook
|
||||
}
|
||||
}
|
||||
|
||||
@@ -682,34 +724,21 @@ func (n *NotificationManager) sendWebhook(webhook WebhookConfig, alert *alerts.A
|
||||
|
||||
// For Telegram, extract chat_id from URL if present
|
||||
if webhook.Service == "telegram" {
|
||||
if strings.Contains(webhook.URL, "chat_id=") {
|
||||
// Extract chat_id from URL query params
|
||||
if u, err := url.Parse(webhook.URL); err == nil {
|
||||
chatID := u.Query().Get("chat_id")
|
||||
if chatID != "" {
|
||||
data.ChatID = chatID
|
||||
log.Debug().
|
||||
Str("webhook", webhook.Name).
|
||||
Str("chatID", chatID).
|
||||
Msg("Extracted Telegram chat_id from URL")
|
||||
} else {
|
||||
log.Warn().
|
||||
Str("webhook", webhook.Name).
|
||||
Str("url", webhook.URL).
|
||||
Msg("chat_id parameter in URL is empty")
|
||||
}
|
||||
} else {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("webhook", webhook.Name).
|
||||
Str("url", webhook.URL).
|
||||
Msg("Failed to parse Telegram webhook URL")
|
||||
}
|
||||
} else {
|
||||
chatID, err := extractTelegramChatID(webhook.URL)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("webhook", webhook.Name).
|
||||
Str("url", webhook.URL).
|
||||
Msg("Telegram webhook URL missing chat_id parameter - notifications will fail")
|
||||
Msg("Failed to extract Telegram chat_id - webhook will fail")
|
||||
return // Skip this webhook rather than sending invalid payload
|
||||
}
|
||||
if chatID != "" {
|
||||
data.ChatID = chatID
|
||||
log.Debug().
|
||||
Str("webhook", webhook.Name).
|
||||
Str("chatID", chatID).
|
||||
Msg("Extracted Telegram chat_id from URL")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -791,7 +820,13 @@ func (n *NotificationManager) prepareWebhookData(alert *alerts.Alert, customFiel
|
||||
func (n *NotificationManager) generatePayloadFromTemplate(templateStr string, data WebhookPayloadData) ([]byte, error) {
|
||||
// Create template with helper functions
|
||||
funcMap := template.FuncMap{
|
||||
"title": strings.Title,
|
||||
"title": func(s string) string {
|
||||
// Replace deprecated strings.Title with proper title casing
|
||||
if s == "" {
|
||||
return s
|
||||
}
|
||||
return strings.ToUpper(s[:1]) + strings.ToLower(s[1:])
|
||||
},
|
||||
"upper": strings.ToUpper,
|
||||
"lower": strings.ToLower,
|
||||
"printf": fmt.Sprintf,
|
||||
@@ -807,6 +842,16 @@ func (n *NotificationManager) generatePayloadFromTemplate(templateStr string, da
|
||||
return nil, fmt.Errorf("template execution failed: %w", err)
|
||||
}
|
||||
|
||||
// Validate that the generated payload is valid JSON
|
||||
var jsonCheck interface{}
|
||||
if err := json.Unmarshal(buf.Bytes(), &jsonCheck); err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("payload", string(buf.Bytes())).
|
||||
Msg("Generated webhook payload is invalid JSON")
|
||||
return nil, fmt.Errorf("template produced invalid JSON: %w", err)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
@@ -825,6 +870,135 @@ func formatWebhookDuration(d time.Duration) string {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// extractTelegramChatID extracts and validates the chat_id from a Telegram webhook URL
|
||||
func extractTelegramChatID(webhookURL string) (string, error) {
|
||||
if !strings.Contains(webhookURL, "chat_id=") {
|
||||
return "", fmt.Errorf("Telegram webhook URL missing chat_id parameter")
|
||||
}
|
||||
|
||||
u, err := url.Parse(webhookURL)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid URL format: %w", err)
|
||||
}
|
||||
|
||||
chatID := u.Query().Get("chat_id")
|
||||
if chatID == "" {
|
||||
return "", fmt.Errorf("chat_id parameter is empty")
|
||||
}
|
||||
|
||||
// Validate that chat_id is numeric (Telegram chat IDs are always numeric)
|
||||
// Handle negative IDs (group chats) and positive IDs (private chats)
|
||||
if strings.HasPrefix(chatID, "-") {
|
||||
if !isNumeric(chatID[1:]) {
|
||||
return "", fmt.Errorf("chat_id must be numeric, got: %s", chatID)
|
||||
}
|
||||
} else if !isNumeric(chatID) {
|
||||
return "", fmt.Errorf("chat_id must be numeric, got: %s", chatID)
|
||||
}
|
||||
|
||||
return chatID, nil
|
||||
}
|
||||
|
||||
// isNumeric checks if a string contains only digits
|
||||
func isNumeric(s string) bool {
|
||||
for _, char := range s {
|
||||
if char < '0' || char > '9' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return len(s) > 0
|
||||
}
|
||||
|
||||
// ValidateWebhookURL validates that a webhook URL is safe and properly formed
|
||||
func ValidateWebhookURL(webhookURL string) error {
|
||||
if webhookURL == "" {
|
||||
return fmt.Errorf("webhook URL cannot be empty")
|
||||
}
|
||||
|
||||
u, err := url.Parse(webhookURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid URL format: %w", err)
|
||||
}
|
||||
|
||||
// Must be HTTP or HTTPS
|
||||
if u.Scheme != "http" && u.Scheme != "https" {
|
||||
return fmt.Errorf("webhook URL must use http or https protocol")
|
||||
}
|
||||
|
||||
// Block localhost and private network ranges for security
|
||||
// Allow them only if explicitly configured (for testing)
|
||||
host := u.Hostname()
|
||||
if host == "localhost" || host == "127.0.0.1" || host == "::1" {
|
||||
log.Warn().
|
||||
Str("url", webhookURL).
|
||||
Msg("Webhook URL points to localhost - this may be intentional for testing")
|
||||
}
|
||||
|
||||
// Check for private IP ranges (10.x.x.x, 172.16-31.x.x, 192.168.x.x)
|
||||
if strings.HasPrefix(host, "10.") ||
|
||||
strings.HasPrefix(host, "192.168.") ||
|
||||
(strings.HasPrefix(host, "172.") && isPrivateRange172(host)) {
|
||||
log.Warn().
|
||||
Str("url", webhookURL).
|
||||
Msg("Webhook URL points to private network - ensure this is intentional")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// isPrivateRange172 checks if an IP is in the 172.16.0.0/12 range
|
||||
func isPrivateRange172(host string) bool {
|
||||
parts := strings.Split(host, ".")
|
||||
if len(parts) < 2 {
|
||||
return false
|
||||
}
|
||||
if parts[0] != "172" {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if second octet is between 16 and 31
|
||||
if len(parts[1]) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
second := 0
|
||||
for _, char := range parts[1] {
|
||||
if char < '0' || char > '9' {
|
||||
return false
|
||||
}
|
||||
second = second*10 + int(char-'0')
|
||||
}
|
||||
|
||||
return second >= 16 && second <= 31
|
||||
}
|
||||
|
||||
// addWebhookDelivery adds a webhook delivery record to the history
|
||||
func (n *NotificationManager) addWebhookDelivery(delivery WebhookDelivery) {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
|
||||
// Add to history
|
||||
n.webhookHistory = append(n.webhookHistory, delivery)
|
||||
|
||||
// Keep only last 100 entries
|
||||
if len(n.webhookHistory) > 100 {
|
||||
// Remove oldest entry
|
||||
n.webhookHistory = n.webhookHistory[1:]
|
||||
}
|
||||
}
|
||||
|
||||
// GetWebhookHistory returns recent webhook delivery history
|
||||
func (n *NotificationManager) GetWebhookHistory() []WebhookDelivery {
|
||||
n.mu.RLock()
|
||||
defer n.mu.RUnlock()
|
||||
|
||||
// Return a copy to avoid concurrent access issues
|
||||
history := make([]WebhookDelivery, len(n.webhookHistory))
|
||||
copy(history, n.webhookHistory)
|
||||
return history
|
||||
}
|
||||
|
||||
// groupAlerts groups alerts based on configuration
|
||||
func (n *NotificationManager) groupAlerts(alertList []*alerts.Alert) map[string][]*alerts.Alert {
|
||||
groups := make(map[string][]*alerts.Alert)
|
||||
|
||||
@@ -112,35 +112,6 @@ func (n *NotificationManager) prepareWebhookData(alert *alerts.Alert, customFiel
|
||||
|
||||
// generatePayloadFromTemplate renders the payload using Go templates
|
||||
// NOTE: This function is now defined in notifications.go to be shared
|
||||
/*
|
||||
func (n *NotificationManager) generatePayloadFromTemplate(templateStr string, data WebhookPayloadData) ([]byte, error) {
|
||||
// Create template with helper functions
|
||||
funcMap := template.FuncMap{
|
||||
"title": strings.Title,
|
||||
"upper": strings.ToUpper,
|
||||
"lower": strings.ToLower,
|
||||
"printf": fmt.Sprintf,
|
||||
}
|
||||
|
||||
tmpl, err := template.New("webhook").Funcs(funcMap).Parse(templateStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid template: %w", err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := tmpl.Execute(&buf, data); err != nil {
|
||||
return nil, fmt.Errorf("template execution failed: %w", err)
|
||||
}
|
||||
|
||||
// Validate JSON
|
||||
var jsonCheck interface{}
|
||||
if err := json.Unmarshal(buf.Bytes(), &jsonCheck); err != nil {
|
||||
return nil, fmt.Errorf("template produced invalid JSON: %w", err)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
*/
|
||||
|
||||
// shouldSendWebhook checks if alert matches webhook filter rules
|
||||
func (n *NotificationManager) shouldSendWebhook(webhook EnhancedWebhookConfig, alert *alerts.Alert) bool {
|
||||
@@ -209,7 +180,7 @@ func (n *NotificationManager) shouldSendWebhook(webhook EnhancedWebhookConfig, a
|
||||
return true
|
||||
}
|
||||
|
||||
// sendWebhookWithRetry implements exponential backoff retry
|
||||
// sendWebhookWithRetry implements exponential backoff retry with enhanced error tracking
|
||||
func (n *NotificationManager) sendWebhookWithRetry(webhook EnhancedWebhookConfig, payload []byte) error {
|
||||
maxRetries := webhook.RetryCount
|
||||
if maxRetries <= 0 {
|
||||
@@ -218,12 +189,14 @@ func (n *NotificationManager) sendWebhookWithRetry(webhook EnhancedWebhookConfig
|
||||
|
||||
var lastErr error
|
||||
backoff := time.Second
|
||||
retryableErrors := 0
|
||||
|
||||
for attempt := 0; attempt <= maxRetries; attempt++ {
|
||||
if attempt > 0 {
|
||||
log.Debug().
|
||||
Str("webhook", webhook.Name).
|
||||
Int("attempt", attempt).
|
||||
Int("maxRetries", maxRetries).
|
||||
Dur("backoff", backoff).
|
||||
Msg("Retrying webhook after backoff")
|
||||
time.Sleep(backoff)
|
||||
@@ -239,22 +212,126 @@ func (n *NotificationManager) sendWebhookWithRetry(webhook EnhancedWebhookConfig
|
||||
log.Info().
|
||||
Str("webhook", webhook.Name).
|
||||
Int("attempt", attempt).
|
||||
Int("totalAttempts", attempt+1).
|
||||
Msg("Webhook succeeded after retry")
|
||||
}
|
||||
// Log successful delivery
|
||||
log.Debug().
|
||||
Str("webhook", webhook.Name).
|
||||
Str("service", webhook.Service).
|
||||
Int("payloadSize", len(payload)).
|
||||
Msg("Webhook delivered successfully")
|
||||
|
||||
// Track successful delivery
|
||||
delivery := WebhookDelivery{
|
||||
WebhookName: webhook.Name,
|
||||
WebhookURL: webhook.URL,
|
||||
Service: webhook.Service,
|
||||
AlertID: "enhanced", // This is for enhanced webhooks, alertID might not be available
|
||||
Timestamp: time.Now(),
|
||||
StatusCode: 200, // Assume success
|
||||
Success: true,
|
||||
RetryAttempts: attempt,
|
||||
PayloadSize: len(payload),
|
||||
}
|
||||
n.addWebhookDelivery(delivery)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
lastErr = err
|
||||
|
||||
// Determine if error is retryable
|
||||
isRetryable := isRetryableWebhookError(err)
|
||||
if isRetryable {
|
||||
retryableErrors++
|
||||
}
|
||||
|
||||
log.Warn().
|
||||
Err(err).
|
||||
Str("webhook", webhook.Name).
|
||||
Int("attempt", attempt).
|
||||
Str("service", webhook.Service).
|
||||
Int("attempt", attempt+1).
|
||||
Int("maxRetries", maxRetries+1).
|
||||
Bool("retryable", isRetryable).
|
||||
Msg("Webhook attempt failed")
|
||||
|
||||
// If error is not retryable, break early
|
||||
if !isRetryable && attempt == 0 {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("webhook", webhook.Name).
|
||||
Msg("Non-retryable webhook error - not attempting retry")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Final error logging with summary
|
||||
log.Error().
|
||||
Err(lastErr).
|
||||
Str("webhook", webhook.Name).
|
||||
Str("service", webhook.Service).
|
||||
Int("totalAttempts", maxRetries+1).
|
||||
Int("retryableErrors", retryableErrors).
|
||||
Msg("Webhook delivery failed after all retry attempts")
|
||||
|
||||
// Track failed delivery
|
||||
delivery := WebhookDelivery{
|
||||
WebhookName: webhook.Name,
|
||||
WebhookURL: webhook.URL,
|
||||
Service: webhook.Service,
|
||||
AlertID: "enhanced", // This is for enhanced webhooks, alertID might not be available
|
||||
Timestamp: time.Now(),
|
||||
StatusCode: 0, // Unknown status
|
||||
Success: false,
|
||||
ErrorMessage: lastErr.Error(),
|
||||
RetryAttempts: maxRetries,
|
||||
PayloadSize: len(payload),
|
||||
}
|
||||
n.addWebhookDelivery(delivery)
|
||||
|
||||
return fmt.Errorf("webhook failed after %d attempts: %w", maxRetries+1, lastErr)
|
||||
}
|
||||
|
||||
// isRetryableWebhookError determines if a webhook error should trigger a retry
|
||||
func isRetryableWebhookError(err error) bool {
|
||||
errStr := strings.ToLower(err.Error())
|
||||
|
||||
// Network-related errors that should be retried
|
||||
if strings.Contains(errStr, "timeout") ||
|
||||
strings.Contains(errStr, "connection refused") ||
|
||||
strings.Contains(errStr, "connection reset") ||
|
||||
strings.Contains(errStr, "no such host") ||
|
||||
strings.Contains(errStr, "network unreachable") {
|
||||
return true
|
||||
}
|
||||
|
||||
// HTTP status codes that should be retried
|
||||
if strings.Contains(errStr, "status 429") || // Rate limited
|
||||
strings.Contains(errStr, "status 502") || // Bad Gateway
|
||||
strings.Contains(errStr, "status 503") || // Service Unavailable
|
||||
strings.Contains(errStr, "status 504") { // Gateway Timeout
|
||||
return true
|
||||
}
|
||||
|
||||
// 5xx server errors are generally retryable
|
||||
for i := 500; i <= 599; i++ {
|
||||
if strings.Contains(errStr, fmt.Sprintf("status %d", i)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// 4xx client errors are generally not retryable
|
||||
for i := 400; i <= 499; i++ {
|
||||
if strings.Contains(errStr, fmt.Sprintf("status %d", i)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Default to retryable for unknown errors
|
||||
return true
|
||||
}
|
||||
|
||||
// sendWebhookOnce sends a single webhook request
|
||||
func (n *NotificationManager) sendWebhookOnce(webhook EnhancedWebhookConfig, payload []byte) error {
|
||||
method := webhook.Method
|
||||
@@ -287,19 +364,22 @@ func (n *NotificationManager) sendWebhookOnce(webhook EnhancedWebhookConfig, pay
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Log response if enabled
|
||||
if webhook.ResponseLogging {
|
||||
var respBody bytes.Buffer
|
||||
respBody.ReadFrom(resp.Body)
|
||||
// Read response body for error handling and logging
|
||||
var respBody bytes.Buffer
|
||||
respBody.ReadFrom(resp.Body)
|
||||
responseBody := respBody.String()
|
||||
|
||||
// Log response if enabled or if there's an error
|
||||
if webhook.ResponseLogging || resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
log.Debug().
|
||||
Str("webhook", webhook.Name).
|
||||
Int("status", resp.StatusCode).
|
||||
Str("response", respBody.String()).
|
||||
Str("response", responseBody).
|
||||
Msg("Webhook response")
|
||||
}
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return fmt.Errorf("webhook returned status %d", resp.StatusCode)
|
||||
return fmt.Errorf("webhook returned status %d: %s", resp.StatusCode, responseBody)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -348,13 +428,12 @@ func (n *NotificationManager) TestEnhancedWebhook(webhook EnhancedWebhookConfig)
|
||||
data := n.prepareWebhookData(testAlert, webhook.CustomFields)
|
||||
|
||||
// For Telegram, extract chat_id from URL if present
|
||||
if webhook.Service == "telegram" && strings.Contains(webhook.URL, "chat_id=") {
|
||||
if u, err := url.Parse(webhook.URL); err == nil {
|
||||
chatID := u.Query().Get("chat_id")
|
||||
if chatID != "" {
|
||||
data.ChatID = chatID
|
||||
}
|
||||
if webhook.Service == "telegram" {
|
||||
if chatID, err := extractTelegramChatID(webhook.URL); err == nil && chatID != "" {
|
||||
data.ChatID = chatID
|
||||
}
|
||||
// Note: For test webhooks, we don't fail if chat_id is missing
|
||||
// as this may be intentional during testing
|
||||
}
|
||||
|
||||
// Generate payload
|
||||
|
||||
Executable
+163
@@ -0,0 +1,163 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Simple Telegram Bot Setup using Homebrew
|
||||
set -e
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${BLUE}======================================"
|
||||
echo "Telegram Bot Setup via Homebrew"
|
||||
echo "======================================${NC}"
|
||||
echo ""
|
||||
|
||||
# Step 1: Install telegram-cli if on macOS
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
echo -e "${GREEN}macOS detected${NC}"
|
||||
echo ""
|
||||
echo "Installing telegram-cli..."
|
||||
echo "Run this command in your terminal:"
|
||||
echo ""
|
||||
echo -e "${YELLOW}brew install telegram-cli${NC}"
|
||||
echo ""
|
||||
echo "If you don't have Homebrew, install it first:"
|
||||
echo -e "${YELLOW}/bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"${NC}"
|
||||
echo ""
|
||||
read -p "Press ENTER after installing telegram-cli..."
|
||||
fi
|
||||
|
||||
# Step 2: Since telegram-cli is complex, let's use the simpler API approach
|
||||
echo ""
|
||||
echo -e "${BLUE}Creating Telegram Bot${NC}"
|
||||
echo "======================================"
|
||||
echo ""
|
||||
echo "Since telegram-cli requires phone authentication, let's use the simpler approach:"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Step 1: Create Your Bot${NC}"
|
||||
echo "1. Open Telegram (web.telegram.org works too)"
|
||||
echo "2. Search for: @BotFather"
|
||||
echo "3. Send: /newbot"
|
||||
echo "4. Choose a name: Pulse Monitor"
|
||||
echo "5. Choose username: PulseMonitor_$(date +%s)_bot"
|
||||
echo "6. Copy the token (looks like: 1234567890:ABCdefGHIjklMNOpqrsTUVwxyz)"
|
||||
echo ""
|
||||
read -p "Enter your bot token: " BOT_TOKEN
|
||||
|
||||
# Verify token
|
||||
echo ""
|
||||
echo "Verifying bot token..."
|
||||
VERIFY=$(curl -s "https://api.telegram.org/bot${BOT_TOKEN}/getMe")
|
||||
|
||||
if echo "$VERIFY" | grep -q '"ok":true'; then
|
||||
BOT_NAME=$(echo "$VERIFY" | grep -o '"first_name":"[^"]*' | cut -d'"' -f4)
|
||||
BOT_USERNAME=$(echo "$VERIFY" | grep -o '"username":"[^"]*' | cut -d'"' -f4)
|
||||
echo -e "${GREEN}✓ Bot verified: $BOT_NAME (@$BOT_USERNAME)${NC}"
|
||||
|
||||
# Save token
|
||||
echo "$BOT_TOKEN" > ~/.pulse_telegram_token
|
||||
chmod 600 ~/.pulse_telegram_token
|
||||
else
|
||||
echo -e "${RED}✗ Invalid token!${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Step 3: Get Chat ID
|
||||
echo ""
|
||||
echo -e "${YELLOW}Step 2: Get Your Chat ID${NC}"
|
||||
echo "1. Open Telegram"
|
||||
echo "2. Search for: @$BOT_USERNAME"
|
||||
echo "3. Click START or send any message"
|
||||
echo ""
|
||||
read -p "Press ENTER after messaging your bot..."
|
||||
|
||||
# Get updates
|
||||
UPDATES=$(curl -s "https://api.telegram.org/bot${BOT_TOKEN}/getUpdates")
|
||||
CHAT_ID=$(echo "$UPDATES" | grep -o '"chat":{"id":[0-9-]*' | grep -o '[0-9-]*' | head -1)
|
||||
|
||||
if [ -n "$CHAT_ID" ]; then
|
||||
echo -e "${GREEN}✓ Found your chat ID: $CHAT_ID${NC}"
|
||||
echo "$CHAT_ID" > ~/.pulse_telegram_chat
|
||||
chmod 600 ~/.pulse_telegram_chat
|
||||
else
|
||||
echo -e "${YELLOW}Couldn't find chat ID automatically${NC}"
|
||||
echo "Try this URL in your browser:"
|
||||
echo "https://api.telegram.org/bot${BOT_TOKEN}/getUpdates"
|
||||
echo "Look for \"chat\":{\"id\":YOUR_NUMBER"
|
||||
read -p "Enter your chat ID: " CHAT_ID
|
||||
fi
|
||||
|
||||
# Step 4: Test
|
||||
echo ""
|
||||
echo "Sending test message..."
|
||||
TEST=$(curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"chat_id\": \"$CHAT_ID\",
|
||||
\"text\": \"🎉 *Pulse Integration Working!*\n\nBot: @$BOT_USERNAME\nChat ID: $CHAT_ID\n\nYou're all set!\",
|
||||
\"parse_mode\": \"Markdown\"
|
||||
}")
|
||||
|
||||
if echo "$TEST" | grep -q '"ok":true'; then
|
||||
echo -e "${GREEN}✓ Test message sent! Check Telegram${NC}"
|
||||
else
|
||||
echo -e "${RED}Failed to send test${NC}"
|
||||
fi
|
||||
|
||||
# Step 5: Show Pulse Configuration
|
||||
echo ""
|
||||
echo -e "${BLUE}======================================"
|
||||
echo "Configuration for Pulse"
|
||||
echo "======================================${NC}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Webhook URL:${NC}"
|
||||
echo "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage"
|
||||
echo ""
|
||||
echo -e "${YELLOW}HTTP Method:${NC} POST"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Custom Payload Template:${NC}"
|
||||
cat << EOF
|
||||
{
|
||||
"chat_id": "$CHAT_ID",
|
||||
"text": "🚨 *Pulse Alert: {{.Level}}*\\n\\n{{.Message}}\\n\\n📊 *Details:*\\n• Resource: {{.ResourceName}}\\n• Node: {{.Node}}\\n• Value: {{.Value}}%\\n• Threshold: {{.Threshold}}%\\n• Duration: {{.Duration}}",
|
||||
"parse_mode": "Markdown",
|
||||
"disable_web_page_preview": true
|
||||
}
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}Setup Complete!${NC}"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Go to Pulse web interface"
|
||||
echo "2. Navigate to Alerts → Webhooks"
|
||||
echo "3. Click 'Add Webhook'"
|
||||
echo "4. Select 'Generic' as Service Type"
|
||||
echo "5. Paste the configuration above"
|
||||
echo "6. Enable the webhook"
|
||||
echo "7. Test it!"
|
||||
|
||||
# Save config for easy access
|
||||
cat > ~/pulse-telegram-config.txt << EOF
|
||||
Telegram Webhook Configuration for Pulse
|
||||
=========================================
|
||||
|
||||
Bot Token: $BOT_TOKEN
|
||||
Chat ID: $CHAT_ID
|
||||
Bot Username: @$BOT_USERNAME
|
||||
|
||||
Webhook URL:
|
||||
https://api.telegram.org/bot${BOT_TOKEN}/sendMessage
|
||||
|
||||
Custom Payload:
|
||||
{
|
||||
"chat_id": "$CHAT_ID",
|
||||
"text": "🚨 *Pulse Alert: {{.Level}}*\\n\\n{{.Message}}\\n\\n📊 *Details:*\\n• Resource: {{.ResourceName}}\\n• Node: {{.Node}}\\n• Value: {{.Value}}%\\n• Threshold: {{.Threshold}}%",
|
||||
"parse_mode": "Markdown"
|
||||
}
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo -e "${YELLOW}Configuration saved to: ~/pulse-telegram-config.txt${NC}"
|
||||
Executable
+215
@@ -0,0 +1,215 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Automated Telegram Bot Setup for Pulse
|
||||
This script creates a bot and configures it automatically
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import time
|
||||
import requests
|
||||
|
||||
try:
|
||||
from telethon import TelegramClient
|
||||
from telethon.sessions import StringSession
|
||||
except ImportError:
|
||||
print("Installing required packages...")
|
||||
os.system("pip install telethon")
|
||||
from telethon import TelegramClient
|
||||
from telethon.sessions import StringSession
|
||||
|
||||
# Configuration
|
||||
API_ID = 2040 # Default API ID for Telegram CLI apps
|
||||
API_HASH = "b18441a1ff607e10a989891a5462e627" # Default API hash
|
||||
PULSE_CONFIG_DIR = "/opt/pulse"
|
||||
|
||||
def setup_telegram_bot():
|
||||
"""Interactive Telegram bot setup"""
|
||||
|
||||
print("=" * 50)
|
||||
print("Automated Telegram Bot Setup for Pulse")
|
||||
print("=" * 50)
|
||||
print()
|
||||
|
||||
# Check for existing configuration
|
||||
token_file = os.path.join(PULSE_CONFIG_DIR, ".telegram_bot_token")
|
||||
chat_file = os.path.join(PULSE_CONFIG_DIR, ".telegram_chat_id")
|
||||
|
||||
if os.path.exists(token_file):
|
||||
print("Found existing bot configuration")
|
||||
with open(token_file, 'r') as f:
|
||||
bot_token = f.read().strip()
|
||||
print(f"Bot token: {bot_token[:10]}...{bot_token[-5:]}")
|
||||
else:
|
||||
print("Creating new Telegram bot...")
|
||||
print()
|
||||
print("Option 1: Manual Setup")
|
||||
print("-" * 30)
|
||||
print("1. Open Telegram and search for @BotFather")
|
||||
print("2. Send /newbot")
|
||||
print("3. Choose a name and username")
|
||||
print("4. Copy the token here")
|
||||
print()
|
||||
print("Option 2: Automated Setup (requires phone number)")
|
||||
print("-" * 30)
|
||||
|
||||
choice = input("Choose option (1 or 2): ").strip()
|
||||
|
||||
if choice == "2":
|
||||
# Automated bot creation using Telethon
|
||||
phone = input("Enter your phone number (with country code, e.g., +1234567890): ")
|
||||
|
||||
client = TelegramClient(StringSession(), API_ID, API_HASH)
|
||||
|
||||
async def create_bot():
|
||||
await client.start(phone=phone)
|
||||
|
||||
# Send message to BotFather
|
||||
botfather = await client.get_entity("@BotFather")
|
||||
|
||||
# Create new bot
|
||||
await client.send_message(botfather, "/newbot")
|
||||
time.sleep(1)
|
||||
|
||||
# Set bot name
|
||||
bot_name = "Pulse Monitor Alert Bot"
|
||||
await client.send_message(botfather, bot_name)
|
||||
time.sleep(1)
|
||||
|
||||
# Set bot username (must be unique)
|
||||
import random
|
||||
bot_username = f"PulseMonitor_{random.randint(1000, 9999)}_bot"
|
||||
await client.send_message(botfather, bot_username)
|
||||
time.sleep(2)
|
||||
|
||||
# Get the response with token
|
||||
messages = await client.get_messages(botfather, limit=1)
|
||||
response = messages[0].text
|
||||
|
||||
# Extract token from response
|
||||
import re
|
||||
token_match = re.search(r'[0-9]+:[A-Za-z0-9_-]+', response)
|
||||
if token_match:
|
||||
return token_match.group(0), bot_username
|
||||
else:
|
||||
print("Could not extract token from BotFather response")
|
||||
return None, None
|
||||
|
||||
import asyncio
|
||||
bot_token, bot_username = asyncio.run(create_bot())
|
||||
|
||||
if not bot_token:
|
||||
print("Failed to create bot automatically")
|
||||
bot_token = input("Please enter the bot token manually: ").strip()
|
||||
else:
|
||||
bot_token = input("Enter your bot token: ").strip()
|
||||
|
||||
# Save token
|
||||
with open(token_file, 'w') as f:
|
||||
f.write(bot_token)
|
||||
os.chmod(token_file, 0o600)
|
||||
print(f"✓ Bot token saved")
|
||||
|
||||
# Verify bot token
|
||||
print("\nVerifying bot...")
|
||||
response = requests.get(f"https://api.telegram.org/bot{bot_token}/getMe")
|
||||
|
||||
if response.json().get('ok'):
|
||||
bot_info = response.json()['result']
|
||||
print(f"✓ Bot verified: {bot_info['first_name']} (@{bot_info['username']})")
|
||||
else:
|
||||
print("✗ Invalid bot token!")
|
||||
sys.exit(1)
|
||||
|
||||
# Get chat ID
|
||||
if os.path.exists(chat_file):
|
||||
with open(chat_file, 'r') as f:
|
||||
chat_id = f.read().strip()
|
||||
print(f"Found existing chat ID: {chat_id}")
|
||||
else:
|
||||
print("\nGetting your chat ID...")
|
||||
print(f"1. Open Telegram")
|
||||
print(f"2. Search for @{bot_info['username']}")
|
||||
print(f"3. Send any message to the bot")
|
||||
input("\nPress ENTER after sending a message...")
|
||||
|
||||
# Get updates
|
||||
response = requests.get(f"https://api.telegram.org/bot{bot_token}/getUpdates")
|
||||
updates = response.json()
|
||||
|
||||
if updates.get('ok') and updates.get('result'):
|
||||
# Find chat IDs
|
||||
chat_ids = set()
|
||||
for update in updates['result']:
|
||||
if 'message' in update:
|
||||
chat_ids.add(update['message']['chat']['id'])
|
||||
|
||||
if chat_ids:
|
||||
chat_id = list(chat_ids)[0]
|
||||
print(f"✓ Found chat ID: {chat_id}")
|
||||
|
||||
# Save chat ID
|
||||
with open(chat_file, 'w') as f:
|
||||
f.write(str(chat_id))
|
||||
os.chmod(chat_file, 0o600)
|
||||
else:
|
||||
print("No chat ID found")
|
||||
chat_id = input("Enter your chat ID manually: ").strip()
|
||||
else:
|
||||
print("Could not get updates")
|
||||
chat_id = input("Enter your chat ID manually: ").strip()
|
||||
|
||||
# Send test message
|
||||
print("\nSending test message...")
|
||||
test_message = {
|
||||
"chat_id": chat_id,
|
||||
"text": "🎉 *Pulse Telegram Integration Successful!*\n\nYour bot is now connected and ready to receive alerts.",
|
||||
"parse_mode": "Markdown"
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"https://api.telegram.org/bot{bot_token}/sendMessage",
|
||||
json=test_message
|
||||
)
|
||||
|
||||
if response.json().get('ok'):
|
||||
print("✓ Test message sent successfully!")
|
||||
else:
|
||||
print("✗ Failed to send test message")
|
||||
print(response.json())
|
||||
|
||||
# Generate Pulse configuration
|
||||
print("\n" + "=" * 50)
|
||||
print("PULSE WEBHOOK CONFIGURATION")
|
||||
print("=" * 50)
|
||||
print()
|
||||
print(f"Webhook URL: https://api.telegram.org/bot{bot_token}/sendMessage")
|
||||
print()
|
||||
print("Custom Payload Template:")
|
||||
payload = {
|
||||
"chat_id": str(chat_id),
|
||||
"text": "🚨 *Pulse Alert: {{.Level}}*\\n\\n{{.Message}}\\n\\n📊 *Details:*\\n• Resource: {{.ResourceName}}\\n• Node: {{.Node}}\\n• Value: {{.Value}}%\\n• Threshold: {{.Threshold}}%",
|
||||
"parse_mode": "Markdown"
|
||||
}
|
||||
print(json.dumps(payload, indent=2))
|
||||
|
||||
# Save configuration
|
||||
config_file = os.path.join(PULSE_CONFIG_DIR, "telegram-webhook.json")
|
||||
config = {
|
||||
"url": f"https://api.telegram.org/bot{bot_token}/sendMessage",
|
||||
"method": "POST",
|
||||
"payload": payload
|
||||
}
|
||||
|
||||
with open(config_file, 'w') as f:
|
||||
json.dump(config, f, indent=2)
|
||||
|
||||
print(f"\n✓ Configuration saved to: {config_file}")
|
||||
print("\nNext steps:")
|
||||
print("1. Go to Pulse web interface")
|
||||
print("2. Navigate to Alerts > Webhooks")
|
||||
print("3. Use the configuration above")
|
||||
|
||||
if __name__ == "__main__":
|
||||
setup_telegram_bot()
|
||||
Executable
+180
@@ -0,0 +1,180 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Automated Telegram Bot Setup for Pulse
|
||||
# This script helps create and configure a Telegram bot
|
||||
|
||||
set -e
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${GREEN}==================================="
|
||||
echo "Telegram Bot Auto-Setup for Pulse"
|
||||
echo "===================================${NC}"
|
||||
echo ""
|
||||
|
||||
# Check if we have a saved bot token
|
||||
TOKEN_FILE="/opt/pulse/.telegram_bot_token"
|
||||
CHAT_FILE="/opt/pulse/.telegram_chat_id"
|
||||
|
||||
if [ -f "$TOKEN_FILE" ]; then
|
||||
echo -e "${YELLOW}Found existing bot token${NC}"
|
||||
BOT_TOKEN=$(cat "$TOKEN_FILE")
|
||||
echo "Token: ${BOT_TOKEN:0:10}...${BOT_TOKEN: -5}"
|
||||
else
|
||||
echo -e "${YELLOW}No existing bot token found${NC}"
|
||||
echo ""
|
||||
echo "To create a new bot automatically, I need you to:"
|
||||
echo "1. Open Telegram"
|
||||
echo "2. Search for @BotFather"
|
||||
echo "3. Send: /newbot"
|
||||
echo "4. Choose a name (e.g., 'Pulse Monitor')"
|
||||
echo "5. Choose a username ending in 'bot' (e.g., 'PulseMonitor_bot')"
|
||||
echo ""
|
||||
read -p "Enter the bot token from BotFather: " BOT_TOKEN
|
||||
|
||||
# Save the token
|
||||
echo "$BOT_TOKEN" > "$TOKEN_FILE"
|
||||
chmod 600 "$TOKEN_FILE"
|
||||
echo -e "${GREEN}✓ Bot token saved${NC}"
|
||||
fi
|
||||
|
||||
# Test the bot token
|
||||
echo ""
|
||||
echo "Testing bot token..."
|
||||
API_RESPONSE=$(curl -s "https://api.telegram.org/bot${BOT_TOKEN}/getMe")
|
||||
|
||||
if echo "$API_RESPONSE" | grep -q '"ok":true'; then
|
||||
BOT_USERNAME=$(echo "$API_RESPONSE" | grep -o '"username":"[^"]*' | cut -d'"' -f4)
|
||||
BOT_NAME=$(echo "$API_RESPONSE" | grep -o '"first_name":"[^"]*' | cut -d'"' -f4)
|
||||
echo -e "${GREEN}✓ Bot verified: $BOT_NAME (@$BOT_USERNAME)${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Invalid bot token!${NC}"
|
||||
rm -f "$TOKEN_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get or find chat ID
|
||||
if [ -f "$CHAT_FILE" ]; then
|
||||
echo -e "${YELLOW}Found existing chat ID${NC}"
|
||||
CHAT_ID=$(cat "$CHAT_FILE")
|
||||
echo "Chat ID: $CHAT_ID"
|
||||
else
|
||||
echo ""
|
||||
echo -e "${YELLOW}Getting your chat ID...${NC}"
|
||||
echo ""
|
||||
echo "Please do the following NOW:"
|
||||
echo "1. Open Telegram"
|
||||
echo "2. Search for: @$BOT_USERNAME"
|
||||
echo "3. Click 'Start' or send any message"
|
||||
echo ""
|
||||
read -p "Press ENTER after you've messaged the bot..."
|
||||
|
||||
# Get updates
|
||||
UPDATES=$(curl -s "https://api.telegram.org/bot${BOT_TOKEN}/getUpdates")
|
||||
|
||||
# Extract chat IDs
|
||||
CHAT_IDS=$(echo "$UPDATES" | grep -o '"chat":{"id":[0-9-]*' | grep -o '[0-9-]*' | sort -u)
|
||||
|
||||
if [ -z "$CHAT_IDS" ]; then
|
||||
echo -e "${RED}No messages found. Trying alternative method...${NC}"
|
||||
|
||||
# Try with offset
|
||||
UPDATES=$(curl -s "https://api.telegram.org/bot${BOT_TOKEN}/getUpdates?offset=-1")
|
||||
CHAT_IDS=$(echo "$UPDATES" | grep -o '"chat":{"id":[0-9-]*' | grep -o '[0-9-]*' | sort -u)
|
||||
fi
|
||||
|
||||
if [ -z "$CHAT_IDS" ]; then
|
||||
echo -e "${RED}Still no chat ID found!${NC}"
|
||||
echo "Manual steps:"
|
||||
echo "1. Open: https://api.telegram.org/bot${BOT_TOKEN}/getUpdates"
|
||||
echo "2. Look for 'chat' -> 'id' in the JSON"
|
||||
echo "3. Enter it manually"
|
||||
read -p "Enter your chat ID: " CHAT_ID
|
||||
else
|
||||
CHAT_ID=$(echo "$CHAT_IDS" | head -n1)
|
||||
echo -e "${GREEN}✓ Found chat ID: $CHAT_ID${NC}"
|
||||
fi
|
||||
|
||||
# Save chat ID
|
||||
echo "$CHAT_ID" > "$CHAT_FILE"
|
||||
chmod 600 "$CHAT_FILE"
|
||||
fi
|
||||
|
||||
# Send test message
|
||||
echo ""
|
||||
echo "Sending test message..."
|
||||
|
||||
TEST_MESSAGE='{
|
||||
"chat_id": "'$CHAT_ID'",
|
||||
"text": "🎉 *Pulse Integration Successful!*\n\nYour Telegram bot is now connected to Pulse monitoring.\n\n✅ Bot: @'$BOT_USERNAME'\n✅ Chat ID: '$CHAT_ID'\n✅ Status: Ready\n\nYou will receive alerts here when thresholds are triggered.",
|
||||
"parse_mode": "Markdown"
|
||||
}'
|
||||
|
||||
TEST_RESULT=$(curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$TEST_MESSAGE")
|
||||
|
||||
if echo "$TEST_RESULT" | grep -q '"ok":true'; then
|
||||
echo -e "${GREEN}✓ Test message sent!${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Failed to send test message${NC}"
|
||||
echo "$TEST_RESULT"
|
||||
fi
|
||||
|
||||
# Configure webhook in Pulse
|
||||
echo ""
|
||||
echo -e "${GREEN}==================================="
|
||||
echo "Configuring Pulse Webhook"
|
||||
echo "===================================${NC}"
|
||||
|
||||
# Create webhook configuration
|
||||
WEBHOOK_CONFIG=$(cat << EOF
|
||||
{
|
||||
"name": "Telegram Alerts",
|
||||
"enabled": true,
|
||||
"url": "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage",
|
||||
"method": "POST",
|
||||
"payloadTemplate": {
|
||||
"chat_id": "${CHAT_ID}",
|
||||
"text": "🚨 *Pulse Alert: {{.Level}}*\\n\\n{{.Message}}\\n\\n📊 *Details:*\\n• Resource: {{.ResourceName}}\\n• Node: {{.Node}}\\n• Value: {{.Value}}%\\n• Threshold: {{.Threshold}}%\\n• Duration: {{.Duration}}",
|
||||
"parse_mode": "Markdown",
|
||||
"disable_web_page_preview": true
|
||||
}
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
# Save webhook config
|
||||
echo "$WEBHOOK_CONFIG" > /opt/pulse/telegram-webhook.json
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}==================================="
|
||||
echo "SETUP COMPLETE!"
|
||||
echo "===================================${NC}"
|
||||
echo ""
|
||||
echo "Webhook URL for Pulse:"
|
||||
echo -e "${YELLOW}https://api.telegram.org/bot${BOT_TOKEN}/sendMessage${NC}"
|
||||
echo ""
|
||||
echo "Custom Payload (copy this exactly):"
|
||||
cat << EOF
|
||||
{
|
||||
"chat_id": "${CHAT_ID}",
|
||||
"text": "🚨 *Pulse Alert: {{.Level}}*\\n\\n{{.Message}}\\n\\n📊 *Details:*\\n• Resource: {{.ResourceName}}\\n• Node: {{.Node}}\\n• Value: {{.Value}}%\\n• Threshold: {{.Threshold}}%",
|
||||
"parse_mode": "Markdown"
|
||||
}
|
||||
EOF
|
||||
echo ""
|
||||
echo -e "${GREEN}Files saved:${NC}"
|
||||
echo "• Bot token: $TOKEN_FILE"
|
||||
echo "• Chat ID: $CHAT_FILE"
|
||||
echo "• Webhook config: /opt/pulse/telegram-webhook.json"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Next steps:${NC}"
|
||||
echo "1. Go to Pulse web interface"
|
||||
echo "2. Navigate to Alerts > Webhooks"
|
||||
echo "3. Add new webhook with the configuration above"
|
||||
echo "4. Enable the webhook"
|
||||
echo "5. Set up alert thresholds to trigger notifications"
|
||||
Executable
+281
@@ -0,0 +1,281 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Telegram CLI Setup via Homebrew for Pulse
|
||||
set -e
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${BLUE}======================================"
|
||||
echo "Telegram CLI Setup for Pulse"
|
||||
echo "======================================${NC}"
|
||||
echo ""
|
||||
|
||||
# Check OS
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
echo -e "${GREEN}✓ macOS detected${NC}"
|
||||
|
||||
# Check if Homebrew is installed
|
||||
if ! command -v brew &> /dev/null; then
|
||||
echo -e "${RED}Homebrew not found. Installing...${NC}"
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
else
|
||||
echo -e "${GREEN}✓ Homebrew found${NC}"
|
||||
fi
|
||||
|
||||
# Install telegram-cli
|
||||
echo ""
|
||||
echo -e "${YELLOW}Installing telegram-cli via Homebrew...${NC}"
|
||||
|
||||
# Check if already installed
|
||||
if brew list telegram-cli &>/dev/null; then
|
||||
echo -e "${GREEN}✓ telegram-cli already installed${NC}"
|
||||
else
|
||||
echo "Installing telegram-cli..."
|
||||
brew install telegram-cli
|
||||
fi
|
||||
|
||||
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||
echo -e "${GREEN}✓ Linux detected${NC}"
|
||||
|
||||
# For Linux, try different package managers
|
||||
if command -v apt-get &> /dev/null; then
|
||||
echo "Installing telegram-cli via apt..."
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y telegram-cli
|
||||
elif command -v yum &> /dev/null; then
|
||||
echo "Installing telegram-cli via yum..."
|
||||
sudo yum install -y telegram-cli
|
||||
else
|
||||
echo -e "${RED}Package manager not supported. Building from source...${NC}"
|
||||
|
||||
# Build from source
|
||||
sudo apt-get install -y libreadline-dev libconfig-dev libssl-dev lua5.2 liblua5.2-dev libevent-dev libjansson-dev libpython-dev make
|
||||
|
||||
cd /tmp
|
||||
git clone --recursive https://github.com/vysheng/tg.git
|
||||
cd tg
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
cd /opt/pulse
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}Unsupported OS: $OSTYPE${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}✓ telegram-cli installed${NC}"
|
||||
echo ""
|
||||
|
||||
# Create telegram-cli config
|
||||
echo -e "${YELLOW}Creating telegram-cli configuration...${NC}"
|
||||
|
||||
mkdir -p ~/.telegram-cli
|
||||
|
||||
# Create config file
|
||||
cat > ~/.telegram-cli/config << 'EOF'
|
||||
# Telegram CLI Configuration
|
||||
default_profile = "default";
|
||||
|
||||
default = {
|
||||
config_directory = ".telegram-cli";
|
||||
use_ipv6 = false;
|
||||
make_auth_key_on_start = true;
|
||||
};
|
||||
EOF
|
||||
|
||||
echo -e "${GREEN}✓ Configuration created${NC}"
|
||||
echo ""
|
||||
|
||||
# Create bot creation script for telegram-cli
|
||||
cat > /tmp/create_bot.lua << 'EOF'
|
||||
-- Lua script for telegram-cli to create a bot
|
||||
function on_msg_receive (msg)
|
||||
if msg.text then
|
||||
print("Message received: " .. msg.text)
|
||||
end
|
||||
end
|
||||
|
||||
function on_our_id (id)
|
||||
print("Our ID: " .. id)
|
||||
end
|
||||
|
||||
function on_secret_chat_created (peer)
|
||||
print("Secret chat created")
|
||||
end
|
||||
|
||||
function on_user_update (user)
|
||||
end
|
||||
|
||||
function on_chat_update (chat)
|
||||
end
|
||||
|
||||
function on_get_difference_end ()
|
||||
end
|
||||
|
||||
function on_binlog_replay_end ()
|
||||
-- Start bot creation process
|
||||
send_msg("@BotFather", "/newbot", ok_cb, false)
|
||||
end
|
||||
EOF
|
||||
|
||||
# Create interactive setup script
|
||||
cat > /tmp/setup_telegram_bot.sh << 'SCRIPT'
|
||||
#!/bin/bash
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${YELLOW}Starting telegram-cli...${NC}"
|
||||
echo ""
|
||||
echo "IMPORTANT: First time setup:"
|
||||
echo "1. You'll be asked for your phone number"
|
||||
echo "2. Enter it with country code (e.g., +1234567890)"
|
||||
echo "3. You'll receive a code via Telegram"
|
||||
echo "4. Enter the code when prompted"
|
||||
echo ""
|
||||
read -p "Press ENTER to continue..."
|
||||
|
||||
# Start telegram-cli in interactive mode
|
||||
echo ""
|
||||
echo "Starting Telegram CLI..."
|
||||
echo "Once logged in, we'll create your bot"
|
||||
echo ""
|
||||
|
||||
# Create expect script for automation
|
||||
cat > /tmp/telegram_bot_create.expect << 'EXPECT'
|
||||
#!/usr/bin/expect -f
|
||||
|
||||
set timeout 30
|
||||
|
||||
spawn telegram-cli -N -W
|
||||
|
||||
expect {
|
||||
"phone number:" {
|
||||
send_user "\nEnter your phone number with country code: "
|
||||
expect_user -re "(.*)\n"
|
||||
send "$expect_out(1,string)\r"
|
||||
exp_continue
|
||||
}
|
||||
"code:" {
|
||||
send_user "\nEnter the verification code: "
|
||||
expect_user -re "(.*)\n"
|
||||
send "$expect_out(1,string)\r"
|
||||
exp_continue
|
||||
}
|
||||
"> " {
|
||||
send_user "\nLogged in! Creating bot...\n"
|
||||
|
||||
# Contact BotFather
|
||||
send "msg @BotFather /newbot\r"
|
||||
sleep 2
|
||||
|
||||
# Send bot name
|
||||
send "msg @BotFather Pulse Monitor Alert Bot\r"
|
||||
sleep 2
|
||||
|
||||
# Generate random username
|
||||
set timestamp [clock seconds]
|
||||
send "msg @BotFather PulseMonitor_${timestamp}_bot\r"
|
||||
sleep 3
|
||||
|
||||
# Get messages to see the token
|
||||
send "history @BotFather 5\r"
|
||||
sleep 2
|
||||
|
||||
send "quit\r"
|
||||
}
|
||||
timeout {
|
||||
send_user "\nTimeout occurred\n"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
expect eof
|
||||
EXPECT
|
||||
|
||||
chmod +x /tmp/telegram_bot_create.expect
|
||||
|
||||
# Check if expect is installed
|
||||
if ! command -v expect &> /dev/null; then
|
||||
echo "Installing expect..."
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
brew install expect
|
||||
else
|
||||
sudo apt-get install -y expect
|
||||
fi
|
||||
fi
|
||||
|
||||
# Run expect script
|
||||
/tmp/telegram_bot_create.expect
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}Bot creation process completed!${NC}"
|
||||
echo ""
|
||||
echo "Check the output above for your bot token (looks like: 1234567890:ABCdefGHIjklMNOpqrsTUVwxyz)"
|
||||
echo ""
|
||||
read -p "Enter your bot token: " BOT_TOKEN
|
||||
|
||||
# Save token
|
||||
echo "$BOT_TOKEN" > /opt/pulse/.telegram_bot_token
|
||||
chmod 600 /opt/pulse/.telegram_bot_token
|
||||
|
||||
echo -e "${GREEN}✓ Bot token saved${NC}"
|
||||
|
||||
# Get chat ID
|
||||
echo ""
|
||||
echo "Now we need your chat ID..."
|
||||
echo "1. Open Telegram app"
|
||||
echo "2. Search for your bot and start a chat"
|
||||
echo "3. Send any message"
|
||||
echo ""
|
||||
read -p "Press ENTER after sending a message to your bot..."
|
||||
|
||||
# Get updates to find chat ID
|
||||
RESPONSE=$(curl -s "https://api.telegram.org/bot${BOT_TOKEN}/getUpdates")
|
||||
CHAT_ID=$(echo "$RESPONSE" | grep -o '"chat":{"id":[0-9-]*' | grep -o '[0-9-]*' | head -1)
|
||||
|
||||
if [ -n "$CHAT_ID" ]; then
|
||||
echo -e "${GREEN}✓ Found chat ID: $CHAT_ID${NC}"
|
||||
echo "$CHAT_ID" > /opt/pulse/.telegram_chat_id
|
||||
chmod 600 /opt/pulse/.telegram_chat_id
|
||||
else
|
||||
echo "Could not find chat ID automatically"
|
||||
echo "Visit: https://api.telegram.org/bot${BOT_TOKEN}/getUpdates"
|
||||
echo "Look for 'chat' -> 'id'"
|
||||
read -p "Enter your chat ID: " CHAT_ID
|
||||
echo "$CHAT_ID" > /opt/pulse/.telegram_chat_id
|
||||
fi
|
||||
|
||||
# Test message
|
||||
echo ""
|
||||
echo "Sending test message..."
|
||||
curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"chat_id": "'$CHAT_ID'",
|
||||
"text": "✅ Pulse Telegram Integration Complete!",
|
||||
"parse_mode": "Markdown"
|
||||
}' > /dev/null
|
||||
|
||||
echo -e "${GREEN}✓ Setup complete!${NC}"
|
||||
echo ""
|
||||
echo "Webhook URL for Pulse:"
|
||||
echo "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage"
|
||||
echo ""
|
||||
echo "Payload template:"
|
||||
echo '{'
|
||||
echo ' "chat_id": "'$CHAT_ID'",'
|
||||
echo ' "text": "🚨 *Pulse Alert: {{.Level}}*\n\n{{.Message}}",'
|
||||
echo ' "parse_mode": "Markdown"'
|
||||
echo '}'
|
||||
SCRIPT
|
||||
|
||||
chmod +x /tmp/setup_telegram_bot.sh
|
||||
/tmp/setup_telegram_bot.sh
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"bot_token": "8463420252:AAFOMoXSwpJoIyFrZ5QKvXZiksEcVJLP7iI",
|
||||
"chat_id": "8143835411",
|
||||
"bot_username": "pulse_monitor1_bot",
|
||||
"webhook_url": "https://api.telegram.org/bot8463420252:AAFOMoXSwpJoIyFrZ5QKvXZiksEcVJLP7iI/sendMessage",
|
||||
"payload_template": {
|
||||
"chat_id": "8143835411",
|
||||
"text": "🚨 *Pulse Alert: {{.Level}}*\\n\\n{{.Message}}\\n\\n📊 *Details:*\\n• Resource: {{.ResourceName}}\\n• Node: {{.Node}}\\n• Value: {{.Value}}%\\n• Threshold: {{.Threshold}}%\\n• Duration: {{.Duration}}\\n\\n⏰ {{.Timestamp}}",
|
||||
"parse_mode": "Markdown",
|
||||
"disable_web_page_preview": true
|
||||
}
|
||||
}
|
||||
Executable
+190
@@ -0,0 +1,190 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Fully Automated Telegram Bot Setup
|
||||
# This does EVERYTHING possible without manual intervention
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${BLUE}======================================"
|
||||
echo "Fully Automated Telegram Setup"
|
||||
echo "======================================${NC}"
|
||||
echo ""
|
||||
|
||||
# Generate unique bot username
|
||||
TIMESTAMP=$(date +%s)
|
||||
BOT_NAME="Pulse Monitor Alert Bot"
|
||||
BOT_USERNAME="PulseMonitor_${TIMESTAMP}_bot"
|
||||
|
||||
echo -e "${YELLOW}Unfortunately, Telegram requires manual bot creation for security.${NC}"
|
||||
echo -e "${YELLOW}But I'll make it as easy as possible!${NC}"
|
||||
echo ""
|
||||
echo -e "${GREEN}Here's a one-click solution:${NC}"
|
||||
echo ""
|
||||
|
||||
# Create a pre-filled URL for BotFather
|
||||
echo "1. Click this link to open BotFather:"
|
||||
echo -e "${BLUE}https://t.me/BotFather${NC}"
|
||||
echo ""
|
||||
echo "2. Copy and paste these THREE messages quickly:"
|
||||
echo ""
|
||||
echo -e "${GREEN}/newbot${NC}"
|
||||
echo -e "${GREEN}${BOT_NAME}${NC}"
|
||||
echo -e "${GREEN}${BOT_USERNAME}${NC}"
|
||||
echo ""
|
||||
echo "3. BotFather will respond with a token. It looks like:"
|
||||
echo " 1234567890:ABCdefGHIjklMNOpqrsTUVwxyz"
|
||||
echo ""
|
||||
|
||||
# Wait for token
|
||||
read -p "Paste your bot token here: " BOT_TOKEN
|
||||
|
||||
# Verify the token immediately
|
||||
echo ""
|
||||
echo "Verifying token..."
|
||||
RESPONSE=$(curl -s "https://api.telegram.org/bot${BOT_TOKEN}/getMe")
|
||||
|
||||
if ! echo "$RESPONSE" | grep -q '"ok":true'; then
|
||||
echo -e "${RED}Invalid token! Please check and try again.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BOT_INFO=$(echo "$RESPONSE" | python3 -c "import sys, json; data=json.load(sys.stdin); print(data['result']['username'])")
|
||||
echo -e "${GREEN}✓ Bot created successfully: @${BOT_INFO}${NC}"
|
||||
|
||||
# Save token
|
||||
mkdir -p /opt/pulse/.telegram
|
||||
echo "$BOT_TOKEN" > /opt/pulse/.telegram/bot_token
|
||||
chmod 600 /opt/pulse/.telegram/bot_token
|
||||
|
||||
# Now get chat ID
|
||||
echo ""
|
||||
echo -e "${YELLOW}Step 2: Getting your Chat ID${NC}"
|
||||
echo ""
|
||||
echo "Click this link to message your bot:"
|
||||
echo -e "${BLUE}https://t.me/${BOT_INFO}${NC}"
|
||||
echo ""
|
||||
echo "Send the message: /start"
|
||||
echo ""
|
||||
read -p "Press ENTER after sending /start to your bot..."
|
||||
|
||||
# Get chat ID
|
||||
UPDATES=$(curl -s "https://api.telegram.org/bot${BOT_TOKEN}/getUpdates")
|
||||
CHAT_ID=$(echo "$UPDATES" | python3 -c "
|
||||
import sys, json
|
||||
try:
|
||||
data = json.load(sys.stdin)
|
||||
if data['ok'] and data['result']:
|
||||
for update in data['result']:
|
||||
if 'message' in update:
|
||||
print(update['message']['chat']['id'])
|
||||
break
|
||||
except: pass
|
||||
")
|
||||
|
||||
if [ -z "$CHAT_ID" ]; then
|
||||
echo -e "${YELLOW}Waiting for your message...${NC}"
|
||||
sleep 3
|
||||
UPDATES=$(curl -s "https://api.telegram.org/bot${BOT_TOKEN}/getUpdates")
|
||||
CHAT_ID=$(echo "$UPDATES" | python3 -c "
|
||||
import sys, json
|
||||
try:
|
||||
data = json.load(sys.stdin)
|
||||
if data['ok'] and data['result']:
|
||||
for update in data['result']:
|
||||
if 'message' in update:
|
||||
print(update['message']['chat']['id'])
|
||||
break
|
||||
except: pass
|
||||
")
|
||||
fi
|
||||
|
||||
if [ -z "$CHAT_ID" ]; then
|
||||
echo -e "${RED}No message found yet.${NC}"
|
||||
echo "Please make sure you sent /start to @${BOT_INFO}"
|
||||
echo ""
|
||||
echo "Manual check URL:"
|
||||
echo "https://api.telegram.org/bot${BOT_TOKEN}/getUpdates"
|
||||
echo ""
|
||||
read -p "Enter your chat ID manually: " CHAT_ID
|
||||
else
|
||||
echo -e "${GREEN}✓ Found your chat ID: ${CHAT_ID}${NC}"
|
||||
fi
|
||||
|
||||
# Save chat ID
|
||||
echo "$CHAT_ID" > /opt/pulse/.telegram/chat_id
|
||||
chmod 600 /opt/pulse/.telegram/chat_id
|
||||
|
||||
# Send test message
|
||||
echo ""
|
||||
echo "Sending test message..."
|
||||
TEST_MSG=$(cat <<EOF
|
||||
{
|
||||
"chat_id": "$CHAT_ID",
|
||||
"text": "🎉 *Pulse Telegram Integration Complete!*\n\nYour alerts will appear here.\n\n✅ Bot: @${BOT_INFO}\n✅ Chat ID: ${CHAT_ID}\n✅ Status: Ready",
|
||||
"parse_mode": "Markdown"
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
SEND_RESULT=$(curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$TEST_MSG")
|
||||
|
||||
if echo "$SEND_RESULT" | grep -q '"ok":true'; then
|
||||
echo -e "${GREEN}✓ Test message sent! Check Telegram${NC}"
|
||||
else
|
||||
echo -e "${RED}Failed to send test message${NC}"
|
||||
fi
|
||||
|
||||
# Create Pulse webhook configuration
|
||||
echo ""
|
||||
echo -e "${BLUE}======================================"
|
||||
echo "Pulse Webhook Configuration"
|
||||
echo "======================================${NC}"
|
||||
echo ""
|
||||
|
||||
WEBHOOK_URL="https://api.telegram.org/bot${BOT_TOKEN}/sendMessage"
|
||||
PAYLOAD_TEMPLATE=$(cat <<EOF
|
||||
{
|
||||
"chat_id": "$CHAT_ID",
|
||||
"text": "🚨 *Pulse Alert: {{.Level}}*\\n\\n{{.Message}}\\n\\n📊 *Details:*\\n• Resource: {{.ResourceName}}\\n• Node: {{.Node}}\\n• Value: {{.Value}}%\\n• Threshold: {{.Threshold}}%\\n• Duration: {{.Duration}}",
|
||||
"parse_mode": "Markdown",
|
||||
"disable_web_page_preview": true
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
# Save configuration
|
||||
cat > /opt/pulse/telegram-webhook.json <<EOF
|
||||
{
|
||||
"name": "Telegram Alerts",
|
||||
"url": "$WEBHOOK_URL",
|
||||
"method": "POST",
|
||||
"headers": {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
"payloadTemplate": $(echo "$PAYLOAD_TEMPLATE" | python3 -c "import sys, json; print(json.dumps(sys.stdin.read()))")
|
||||
}
|
||||
EOF
|
||||
|
||||
echo -e "${GREEN}Configuration saved to: /opt/pulse/telegram-webhook.json${NC}"
|
||||
echo ""
|
||||
echo "Copy this for Pulse Webhook setup:"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Webhook URL:${NC}"
|
||||
echo "$WEBHOOK_URL"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Custom Payload Template:${NC}"
|
||||
echo "$PAYLOAD_TEMPLATE"
|
||||
echo ""
|
||||
echo -e "${GREEN}✓ Setup Complete!${NC}"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Go to Pulse web interface"
|
||||
echo "2. Navigate to Alerts → Webhooks"
|
||||
echo "3. Use the configuration above"
|
||||
echo "4. Enable the webhook"
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Test Pulse webhook endpoint
|
||||
echo "Testing Pulse Webhook..."
|
||||
|
||||
# You'll need to be authenticated - using the API token
|
||||
API_TOKEN="2674a96979fdcda0367e10b808443928b410b4c094c964f7"
|
||||
|
||||
# Test the webhook (adjust the webhook ID if needed)
|
||||
curl -X POST http://localhost:7655/api/notifications/test \
|
||||
-H "X-API-Token: $API_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"method": "webhook",
|
||||
"webhookId": 1
|
||||
}'
|
||||
|
||||
echo ""
|
||||
echo "Check your Telegram for the test message!"
|
||||
Executable
+96
@@ -0,0 +1,96 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Telegram Webhook Test Script for Pulse
|
||||
# This helps you test your Telegram bot setup
|
||||
|
||||
echo "==================================="
|
||||
echo "Telegram Webhook Setup for Pulse"
|
||||
echo "==================================="
|
||||
echo ""
|
||||
|
||||
# Get bot token
|
||||
read -p "Enter your Bot Token (from BotFather): " BOT_TOKEN
|
||||
|
||||
# Get chat ID
|
||||
echo ""
|
||||
echo "Getting updates to find your chat ID..."
|
||||
echo "Make sure you've sent a message to your bot first!"
|
||||
echo ""
|
||||
|
||||
UPDATES_URL="https://api.telegram.org/bot${BOT_TOKEN}/getUpdates"
|
||||
echo "Fetching from: $UPDATES_URL"
|
||||
echo ""
|
||||
|
||||
# Get updates and pretty print
|
||||
RESPONSE=$(curl -s "$UPDATES_URL")
|
||||
|
||||
if echo "$RESPONSE" | grep -q '"ok":true'; then
|
||||
echo "✅ Bot token is valid!"
|
||||
echo ""
|
||||
|
||||
# Try to extract chat IDs
|
||||
CHAT_IDS=$(echo "$RESPONSE" | grep -o '"chat":{"id":[0-9-]*' | grep -o '[0-9-]*' | sort -u)
|
||||
|
||||
if [ -z "$CHAT_IDS" ]; then
|
||||
echo "❌ No chat IDs found. Please send a message to your bot and try again."
|
||||
echo ""
|
||||
echo "Instructions:"
|
||||
echo "1. Open Telegram"
|
||||
echo "2. Search for your bot by username"
|
||||
echo "3. Start a conversation and send any message"
|
||||
echo "4. Run this script again"
|
||||
else
|
||||
echo "Found chat ID(s):"
|
||||
echo "$CHAT_IDS"
|
||||
echo ""
|
||||
|
||||
# Use the first chat ID
|
||||
CHAT_ID=$(echo "$CHAT_IDS" | head -n1)
|
||||
echo "Using Chat ID: $CHAT_ID"
|
||||
echo ""
|
||||
|
||||
# Test sending a message
|
||||
echo "Testing webhook..."
|
||||
TEST_URL="https://api.telegram.org/bot${BOT_TOKEN}/sendMessage"
|
||||
|
||||
TEST_PAYLOAD='{
|
||||
"chat_id": "'$CHAT_ID'",
|
||||
"text": "🎉 *Pulse Telegram Integration Test*\n\nIf you see this message, your Telegram webhook is working!\n\n✅ Bot Token: Valid\n✅ Chat ID: '$CHAT_ID'\n✅ Connection: Working\n\nYou can now configure this in Pulse.",
|
||||
"parse_mode": "Markdown"
|
||||
}'
|
||||
|
||||
TEST_RESPONSE=$(curl -s -X POST "$TEST_URL" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$TEST_PAYLOAD")
|
||||
|
||||
if echo "$TEST_RESPONSE" | grep -q '"ok":true'; then
|
||||
echo "✅ Test message sent successfully!"
|
||||
echo ""
|
||||
echo "==================================="
|
||||
echo "CONFIGURATION FOR PULSE:"
|
||||
echo "==================================="
|
||||
echo ""
|
||||
echo "Service Type: Telegram"
|
||||
echo "Webhook URL: https://api.telegram.org/bot${BOT_TOKEN}/sendMessage"
|
||||
echo ""
|
||||
echo "Custom Payload Template:"
|
||||
cat << 'EOF'
|
||||
{
|
||||
"chat_id": "CHAT_ID_HERE",
|
||||
"text": "🚨 *Pulse Alert: {{.Level}}*\n\n{{.Message}}\n\n📊 *Details:*\n• Resource: {{.ResourceName}}\n• Node: {{.Node}}\n• Value: {{.Value}}%\n• Threshold: {{.Threshold}}%",
|
||||
"parse_mode": "Markdown"
|
||||
}
|
||||
EOF
|
||||
echo ""
|
||||
echo "IMPORTANT: Replace CHAT_ID_HERE with: $CHAT_ID"
|
||||
echo ""
|
||||
echo "==================================="
|
||||
else
|
||||
echo "❌ Failed to send test message"
|
||||
echo "Response: $TEST_RESPONSE"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "❌ Invalid bot token or API error"
|
||||
echo "Response: $RESPONSE"
|
||||
fi
|
||||
Reference in New Issue
Block a user