multiple discord senders

This commit is contained in:
2026-01-20 20:58:18 +01:00
parent 76933a04d8
commit 53e6eecb3f
9 changed files with 100 additions and 69 deletions

View File

@@ -10,19 +10,22 @@ import (
)
type Config struct {
WikiBaseURL string
WikiToken string
WikiContentLimit int
RedmineBaseURL string
RedmineKey string
RedmineContentLimit int
GiteaToken string
GiteaBaseURL string
GiteaRepos []string
GiteaContentLimit int
DiscordWebhook string
DiscordFake bool
Interval time.Duration
WikiBaseURL string
WikiToken string
WikiContentLimit int
WikiDiscordWebhook string
RedmineBaseURL string
RedmineKey string
RedmineContentLimit int
RedmineDiscordWebhook string
GiteaToken string
GiteaBaseURL string
GiteaRepos []string
GiteaContentLimit int
GiteaDiscordWebhook string
DiscordWebhook string
DiscordFake bool
Interval time.Duration
}
func envToInteger(key string, defaultValue int) int {
@@ -40,6 +43,6 @@ func envToInteger(key string, defaultValue int) int {
func loadEnv() {
if err := godotenv.Load(); err != nil {
log.Println("Warning: .env file not found, using environment variables")
log.Println("[BOOT] .env file not found")
}
}

View File

@@ -24,8 +24,8 @@ func (f *GiteaFetcher) Name() string {
return "Gitea"
}
func (f *GiteaFetcher) Fetch() []Entry {
var entries []Entry
func (f *GiteaFetcher) Fetch() []Message {
var messages []Message
for _, repo := range f.Repos {
repo = strings.TrimSpace(repo)
@@ -54,18 +54,19 @@ func (f *GiteaFetcher) Fetch() []Entry {
for _, content := range response {
cacheKey := "gitea_commit_" + url.QueryEscape(f.BaseURL+repo+"_"+content.Sha)
entry := f.TryCreateEntry(
message := f.TryCreateMessage(
"gitea",
cacheKey,
content.Sha,
fmt.Sprintf("📝 [%s] - (%s) %s", f.Name(), repo, content.Commit.Message),
content.HTMLURL,
)
if entry != nil {
entries = append(entries, *entry)
if message != nil {
messages = append(messages, *message)
}
}
}
return entries
return messages
}

View File

@@ -7,16 +7,10 @@ import (
"net/http"
)
// Entry represents a single fetched item.
type Entry struct {
Title string
URL string
}
// Fetcher is the interface for a fetcher.
type Fetcher interface {
Name() string
Fetch() []Entry
Fetch() []Message
}
// BaseFetcher contains common fields for all fetchers.
@@ -27,10 +21,14 @@ type BaseFetcher struct {
ContentLimit int
}
// TryCreateEntry checks the cache and creates an Entry if the value has changed.
func (b *BaseFetcher) TryCreateEntry(cacheKey, cacheValue, title, url string) *Entry {
// TryCreateMessage checks the cache and creates a Message if the value has changed.
func (b *BaseFetcher) TryCreateMessage(channel, cacheKey, cacheValue, title, url string) *Message {
if b.Cache.TryUpdate(cacheKey, cacheValue) {
return &Entry{Title: title, URL: url}
return &Message{
Channel: channel,
Title: title,
URL: url,
}
}
return nil
}

View File

@@ -21,8 +21,8 @@ func (f *RedmineFetcher) Name() string {
return "Redmine"
}
func (f *RedmineFetcher) Fetch() []Entry {
var entries []Entry
func (f *RedmineFetcher) Fetch() []Message {
var messages []Message
path := fmt.Sprintf("/issues.json?limit=%d&sort=updated_on:desc", f.ContentLimit)
req := FetcherRequest{
BaseURL: f.BaseURL,
@@ -43,16 +43,17 @@ func (f *RedmineFetcher) Fetch() []Entry {
}
for _, content := range response.Issues {
entry := f.TryCreateEntry(
message := f.TryCreateMessage(
"redmine",
fmt.Sprintf("redmine_%d", content.ID),
content.UpdatedOn,
fmt.Sprintf("🎫 [%s] - #%d %s", f.Name(), content.ID, content.Subject),
fmt.Sprintf("%s/issues/%d", f.BaseURL, content.ID),
)
if entry != nil {
entries = append(entries, *entry)
if message != nil {
messages = append(messages, *message)
}
}
return entries
return messages
}

View File

@@ -25,8 +25,8 @@ func (f *WikiFetcher) Name() string {
return "WikiJS"
}
func (f *WikiFetcher) Fetch() []Entry {
var entries []Entry
func (f *WikiFetcher) Fetch() []Message {
var messages []Message
query := fmt.Sprintf(`{"query":"{ pages { list(orderBy: UPDATED, orderByDirection: DESC, limit: %d){ path, updatedAt, title }}}"}`, f.ContentLimit)
req := FetcherRequest{
@@ -50,17 +50,18 @@ func (f *WikiFetcher) Fetch() []Entry {
}
for _, content := range response.Data.Pages.List {
entry := f.TryCreateEntry(
message := f.TryCreateMessage(
"wiki",
"wiki_"+content.Path,
content.UpdatedAt,
fmt.Sprintf("📖 [%s] - %s", f.Name(), content.Title),
fmt.Sprintf("%s/%s", f.BaseURL, content.Path),
)
if entry != nil {
entries = append(entries, *entry)
if message != nil {
messages = append(messages, *message)
}
}
return entries
return messages
}

13
lib/message.go Normal file
View File

@@ -0,0 +1,13 @@
package lib
import "fmt"
type Message struct {
Channel string
Title string
URL string
}
func (m Message) ToDiscord() string {
return fmt.Sprintf("[%s](%s)", m.Title, m.URL)
}

View File

@@ -1,7 +1,6 @@
package lib
import (
"fmt"
"log"
"os"
"strings"
@@ -62,34 +61,50 @@ func getCache() Cache {
return cache
}
func getDiscordSender(config Config) DiscordSender {
return DiscordSender{
Webhook: config.DiscordWebhook,
Fake: config.DiscordFake,
}
}
func getMessages(fetchers []Fetcher) []string {
messages := []string{}
func getMessages(fetchers []Fetcher) []Message {
messages := []Message{}
for _, fetcher := range fetchers {
entries := fetcher.Fetch()
for _, entry := range entries {
messages = append(messages, fmt.Sprintf("[%s](%s)", entry.Title, entry.URL))
fetchedMessages := fetcher.Fetch()
for _, message := range fetchedMessages {
messages = append(messages, message)
}
}
return messages
}
func sendMessageToDiscord(sender DiscordSender, message Message) {
log.Printf("[SCHEDULED] [SEND] (%s) %s\n", message.Channel, message.ToDiscord())
if sender.Fake {
return
}
sender.Send(message.ToDiscord())
}
func Runner() {
loadEnv()
config := getConfig()
cache := getCache()
discord_sender := getDiscordSender(config)
fetchers := getFetchers(&config, &cache)
default_discord_sender := NewDiscordSender(config.DiscordWebhook, config.DiscordFake)
wiki_discord_sender := NewDiscordSender(config.WikiDiscordWebhook, config.DiscordFake)
redmine_discord_sender := NewDiscordSender(config.RedmineDiscordWebhook, config.DiscordFake)
gita_discord_sender := NewDiscordSender(config.GiteaDiscordWebhook, config.DiscordFake)
for {
log.Println("Run updater...")
log.Println("[SCHEDULED] Run updater...")
messages := getMessages(fetchers)
discord_sender.SendBatch(messages)
for _, message := range messages {
switch message.Channel {
case "wiki":
sendMessageToDiscord(wiki_discord_sender, message)
case "redmine":
sendMessageToDiscord(redmine_discord_sender, message)
case "gitea":
sendMessageToDiscord(gita_discord_sender, message)
default:
sendMessageToDiscord(default_discord_sender, message)
}
}
cache.Save()
time.Sleep(config.Interval)
}

View File

@@ -3,10 +3,16 @@ package lib
import (
"bytes"
"encoding/json"
"log"
"net/http"
)
func NewDiscordSender(webhook string, fake bool) DiscordSender {
return DiscordSender{
Webhook: webhook,
Fake: fake,
}
}
type DiscordSender struct {
Webhook string
Fake bool
@@ -19,13 +25,3 @@ func (d DiscordSender) Send(msg string) {
}
http.Post(d.Webhook, "application/json", bytes.NewBuffer(b))
}
func (d DiscordSender) SendBatch(msgs []string) {
for _, msg := range msgs {
log.Println("Sending to Discord:", msg)
if d.Fake {
continue
}
d.Send(msg)
}
}