-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
226 lines (192 loc) · 5.83 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
package main
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"os"
"os/signal"
"strings"
"syscall"
"github.com/bwmarrin/discordgo"
"github.com/joho/godotenv"
)
// Define the structure for the Vikunja webhook payload
type VikunjaWebhook struct {
EventName string `json:"event_name"`
Time string `json:"time"`
Data struct {
Task struct {
Title string `json:"title"`
Description string `json:"description"`
DueDate string `json:"due_date"`
Priority int `json:"priority"`
Identifier string `json:"identifier"`
} `json:"task"`
Doer struct {
Name string `json:"name"`
} `json:"doer"`
} `json:"data"`
}
// Define a struct to hold both the channel ID and the role ID
type ChannelInfo struct {
ChannelID string `json:"channel_id"`
RoleID string `json:"role_id"`
}
// Handler for incoming webhook requests
func webhookHandler(dg *discordgo.Session, w http.ResponseWriter, r *http.Request, channelIDs *map[string]ChannelInfo) {
fmt.Println("Yo, at the webhook handler")
if r.Method != http.MethodPost {
http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
return
}
// Read and parse the JSON body
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Failed to read request body", http.StatusInternalServerError)
return
}
defer r.Body.Close()
var webhook VikunjaWebhook
if err := json.Unmarshal(body, &webhook); err != nil {
// http.Error(w, "Failed to parse JSON", http.StatusBadRequest)
// return
}
switch webhook.EventName {
case "task.created":
if err := sendTaskCreated(dg, webhook, channelIDs); err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
}
default:
// http.Error(w, "Not Implemented", http.StatusInternalServerError)
// return
}
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "Message sent to Discord")
}
func sendTaskCreated(dg *discordgo.Session, webhook VikunjaWebhook, channelIDs *map[string]ChannelInfo) error {
message, chanID, err := formatMessage(dg, webhook, channelIDs)
if err != nil {
return fmt.Errorf("Error reading channel IDs")
}
fmt.Println(chanID)
_, err = dg.ChannelMessageSend(chanID, message)
if err != nil {
return errors.New("Failed to send message to Discord")
}
return nil
}
func formatMessage(dg *discordgo.Session, webhook VikunjaWebhook, channelIDs *map[string]ChannelInfo) (string, string, error) {
var project string
index := strings.Index(webhook.Data.Task.Identifier, "-")
if index != -1 {
// Get the substring up to the found index
project = webhook.Data.Task.Identifier[:index]
} else {
return "", "", errors.New("Not from known project")
}
fmt.Println(project)
// Send message to a specific Discord channel
chanID, exists := (*channelIDs)[project]
if !exists {
return "", "", fmt.Errorf("No project id found")
}
// Format the message for Discord
message := fmt.Sprintf(
"**New Task Created <@&%s>**\n\n**Title:** %s\n**Created By:** %s ",
chanID.RoleID,
webhook.Data.Task.Title,
webhook.Data.Doer.Name,
)
return message, chanID.ChannelID, nil
}
// Function to send a message to Discord
func sendToDiscord(webhookURL, message string) error {
payload := map[string]string{"content": message}
data, err := json.Marshal(payload)
if err != nil {
return err
}
resp, err := http.Post(webhookURL, "application/json", bytes.NewBuffer(data))
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to send message, status code: %d", resp.StatusCode)
}
return nil
}
// Function to load the channel information from a JSON file and return a map of structs
func loadChannelIDs(filename string) (map[string]ChannelInfo, error) {
// Open the file
file, err := os.Open(filename)
if err != nil {
return nil, fmt.Errorf("failed to open file: %v", err)
}
defer file.Close()
// Read the file contents
byteValue, err := io.ReadAll(file)
if err != nil {
return nil, fmt.Errorf("failed to read file: %v", err)
}
// Declare the map to hold the channel information (channel_id and role_id)
channelMap := make(map[string]ChannelInfo)
// Unmarshal the JSON into the map
err = json.Unmarshal(byteValue, &channelMap)
if err != nil {
return nil, fmt.Errorf("failed to parse JSON: %v", err)
}
// Return the map and nil error if everything is successful
return channelMap, nil
}
func main() {
// Call the function to load channel IDs
channelIDs, err := loadChannelIDs("channels.json")
if err != nil {
fmt.Println("Error loading channel IDs:", err)
return
}
err = godotenv.Load()
if err != nil {
fmt.Println("Error loading .env file")
return
}
token := os.Getenv("DISCORD_BOT_TOKEN")
if token == "" {
fmt.Println("No token provided. Please set DISCORD_BOT_TOKEN in your .env file")
return
}
dg, err := discordgo.New("Bot " + token)
if err != nil {
fmt.Println("error creating Discord session,", err)
return
}
// Open a websocket connection to Discord and begin listening.
err = dg.Open()
if err != nil {
fmt.Println("error opening connection,", err)
return
}
defer dg.Close() // Ensure Discord session is closed at the end
// Start the HTTP server for handling webhooks
go func() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
webhookHandler(dg, w, r, &channelIDs) // Pass dg to the handler
})
fmt.Println("Server is running on port 4030")
if err := http.ListenAndServe(":4030", nil); err != nil {
fmt.Printf("Server failed: %v\n", err)
}
}()
// Send a test message to a specific channel
dg.ChannelMessageSend("1280543736199123068", "Pong!")
// Wait here until CTRL-C or other term signal is received.
fmt.Println("Bot is now running. Press CTRL-C to exit.")
sc := make(chan os.Signal, 1)
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
<-sc
}