Skip to content

Commit 1443d70

Browse files
committed
fix: Add missing webhook.go file to repository
- webhook.go was missing from git tracking (was ignored by .gitignore) - Contains SendWebhookLogAsync and other webhook functions - Required for discord.go to compile - Force added to override .gitignore
1 parent 07da533 commit 1443d70

1 file changed

Lines changed: 197 additions & 0 deletions

File tree

internal/modules/utils/webhook.go

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
package utils
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
"log"
9+
"mime/multipart"
10+
"net/http"
11+
"os"
12+
"path/filepath"
13+
"time"
14+
)
15+
16+
// SendWebhookLog sends a log message to Discord webhook
17+
func SendWebhookLog(message string) error {
18+
webhookURL := os.Getenv("DISCORD_WEBHOOK")
19+
if webhookURL == "" {
20+
// No webhook configured, skip silently
21+
return nil
22+
}
23+
24+
// Create webhook payload
25+
payload := map[string]interface{}{
26+
"content": message,
27+
}
28+
29+
jsonData, err := json.Marshal(payload)
30+
if err != nil {
31+
return fmt.Errorf("failed to marshal webhook payload: %w", err)
32+
}
33+
34+
// Send HTTP request
35+
req, err := http.NewRequest("POST", webhookURL, bytes.NewBuffer(jsonData))
36+
if err != nil {
37+
return fmt.Errorf("failed to create webhook request: %w", err)
38+
}
39+
req.Header.Set("Content-Type", "application/json")
40+
41+
client := &http.Client{Timeout: 10 * time.Second}
42+
resp, err := client.Do(req)
43+
if err != nil {
44+
log.Printf("[WEBHOOK] Failed to send webhook: %v", err)
45+
return fmt.Errorf("failed to send webhook request: %w", err)
46+
}
47+
defer resp.Body.Close()
48+
49+
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
50+
bodyBytes, _ := io.ReadAll(resp.Body)
51+
log.Printf("[WEBHOOK] Webhook returned status %d: %s", resp.StatusCode, string(bodyBytes))
52+
return fmt.Errorf("webhook returned status %d", resp.StatusCode)
53+
}
54+
55+
return nil
56+
}
57+
58+
// SendWebhookLogAsync sends a log message to Discord webhook asynchronously (non-blocking)
59+
func SendWebhookLogAsync(message string) {
60+
go func() {
61+
if err := SendWebhookLog(message); err != nil {
62+
// Log error but don't block
63+
log.Printf("[WEBHOOK] [ERROR] Failed to send async webhook: %v", err)
64+
}
65+
}()
66+
}
67+
68+
// SendWebhookEmbed sends a formatted embed message to Discord webhook
69+
func SendWebhookEmbed(title, description string, color int, fields []map[string]interface{}) error {
70+
webhookURL := os.Getenv("DISCORD_WEBHOOK")
71+
if webhookURL == "" {
72+
return nil
73+
}
74+
75+
embed := map[string]interface{}{
76+
"title": title,
77+
"description": description,
78+
"color": color,
79+
"timestamp": time.Now().Format(time.RFC3339),
80+
}
81+
82+
if len(fields) > 0 {
83+
embed["fields"] = fields
84+
}
85+
86+
payload := map[string]interface{}{
87+
"embeds": []map[string]interface{}{embed},
88+
}
89+
90+
jsonData, err := json.Marshal(payload)
91+
if err != nil {
92+
return fmt.Errorf("failed to marshal webhook payload: %w", err)
93+
}
94+
95+
req, err := http.NewRequest("POST", webhookURL, bytes.NewBuffer(jsonData))
96+
if err != nil {
97+
return fmt.Errorf("failed to create webhook request: %w", err)
98+
}
99+
req.Header.Set("Content-Type", "application/json")
100+
101+
client := &http.Client{Timeout: 10 * time.Second}
102+
resp, err := client.Do(req)
103+
if err != nil {
104+
return fmt.Errorf("failed to send webhook request: %w", err)
105+
}
106+
defer resp.Body.Close()
107+
108+
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
109+
bodyBytes, _ := io.ReadAll(resp.Body)
110+
return fmt.Errorf("webhook returned status %d: %s", resp.StatusCode, string(bodyBytes))
111+
}
112+
113+
return nil
114+
}
115+
116+
// SendWebhookFile sends a file to Discord webhook
117+
func SendWebhookFile(filePath, description string) error {
118+
webhookURL := os.Getenv("DISCORD_WEBHOOK")
119+
if webhookURL == "" {
120+
// No webhook configured, skip silently
121+
return nil
122+
}
123+
124+
// Check if file exists
125+
fileInfo, err := os.Stat(filePath)
126+
if os.IsNotExist(err) {
127+
return fmt.Errorf("file not found: %s", filePath)
128+
} else if err != nil {
129+
return fmt.Errorf("failed to stat file: %w", err)
130+
} else if fileInfo.Size() == 0 {
131+
return fmt.Errorf("file is empty: %s", filePath)
132+
}
133+
134+
// Read file
135+
fileData, err := os.ReadFile(filePath)
136+
if err != nil {
137+
return fmt.Errorf("failed to read file: %w", err)
138+
}
139+
140+
// Create multipart form
141+
body := &bytes.Buffer{}
142+
writer := multipart.NewWriter(body)
143+
144+
// Add file
145+
fileName := filepath.Base(filePath)
146+
filePart, err := writer.CreateFormFile("file", fileName)
147+
if err != nil {
148+
return fmt.Errorf("failed to create form file: %w", err)
149+
}
150+
if _, err := filePart.Write(fileData); err != nil {
151+
return fmt.Errorf("failed to write file data: %w", err)
152+
}
153+
154+
// Add description/content
155+
if description == "" {
156+
description = fmt.Sprintf("📁 %s", fileName)
157+
}
158+
if err := writer.WriteField("content", description); err != nil {
159+
return fmt.Errorf("failed to write content field: %w", err)
160+
}
161+
162+
writer.Close()
163+
164+
// Send HTTP request
165+
req, err := http.NewRequest("POST", webhookURL, body)
166+
if err != nil {
167+
return fmt.Errorf("failed to create webhook request: %w", err)
168+
}
169+
req.Header.Set("Content-Type", writer.FormDataContentType())
170+
171+
client := &http.Client{Timeout: 30 * time.Second}
172+
resp, err := client.Do(req)
173+
if err != nil {
174+
log.Printf("[WEBHOOK] Failed to send webhook file: %v", err)
175+
return fmt.Errorf("failed to send webhook request: %w", err)
176+
}
177+
defer resp.Body.Close()
178+
179+
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
180+
bodyBytes, _ := io.ReadAll(resp.Body)
181+
log.Printf("[WEBHOOK] Webhook file returned status %d: %s", resp.StatusCode, string(bodyBytes))
182+
return fmt.Errorf("webhook returned status %d", resp.StatusCode)
183+
}
184+
185+
return nil
186+
}
187+
188+
// SendWebhookFileAsync sends a file to Discord webhook asynchronously (non-blocking)
189+
func SendWebhookFileAsync(filePath, description string) {
190+
go func() {
191+
if err := SendWebhookFile(filePath, description); err != nil {
192+
// Log error but don't block
193+
log.Printf("[WEBHOOK] [ERROR] Failed to send async webhook file: %v", err)
194+
}
195+
}()
196+
}
197+

0 commit comments

Comments
 (0)