Files
patchpinglite/cmd/releasewatcher/main.go
jbergner 270c13af5b
All checks were successful
release-tag / release-image (push) Successful in 2m3s
init
2026-05-04 22:25:50 +02:00

94 lines
3.6 KiB
Go

package main
import (
"flag"
"log"
"log/slog"
"net/http"
"os"
"releasewatcher/internal/auth"
"releasewatcher/internal/discordbot"
"releasewatcher/internal/notify"
"releasewatcher/internal/store"
"releasewatcher/internal/web"
)
func main() {
addr := flag.String("addr", env("RW_ADDR", ":8080"), "HTTP listen address")
dataFile := flag.String("data", env("RW_DATA", "/data/releasewatcher.json"), "JSON data file")
adminEmail := flag.String("admin-email", env("RW_ADMIN_EMAIL", "admin@example.local"), "initial admin email")
adminPass := flag.String("admin-pass", env("RW_ADMIN_PASSWORD", "admin12345"), "initial admin password")
secret := flag.String("secret", env("RW_SECRET", "change-me-in-production"), "session secret")
discordToken := flag.String("discord-token", env("RW_DISCORD_TOKEN", ""), "Discord bot token; empty disables Discord")
discordAppID := flag.String("discord-app-id", env("RW_DISCORD_APP_ID", ""), "Discord application ID for command registration")
discordGuildID := flag.String("discord-guild-id", env("RW_DISCORD_GUILD_ID", ""), "Discord guild/server ID for release channels")
discordCategoryID := flag.String("discord-category-id", env("RW_DISCORD_CATEGORY_ID", ""), "Discord category ID for software release channels; empty creates/uses category name")
discordCategoryName := flag.String("discord-category-name", env("RW_DISCORD_CATEGORY_NAME", "ReleaseWatcher"), "Discord category name for software release channels")
discordReleaseMention := flag.String("discord-release-mention", env("RW_DISCORD_RELEASE_MENTION", "@here"), "Mention text for release channel pings, e.g. @here or <@&ROLE_ID>; empty disables pings")
discordSendDMs := flag.Bool("discord-send-dms", envBool("RW_DISCORD_SEND_DMS", true), "also send release notifications as DMs to subscribers")
flag.Parse()
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
st, err := store.Open(*dataFile)
if err != nil {
log.Fatal(err)
}
if err := st.EnsureUser(store.User{Email: *adminEmail, DisplayName: "Administrator", Role: store.RoleAdmin, PasswordHash: auth.HashPassword(*adminPass)}); err != nil {
log.Fatal(err)
}
am := auth.New(*secret, st)
var notifier notify.ReleaseNotifier = notify.LoggingNotifier{Log: logger}
if *discordToken != "" {
dg, err := discordbot.NewDiscordSession(*discordToken)
if err != nil {
log.Fatal(err)
}
discordServices := discordbot.NewServices(st, dg, logger, discordbot.Config{
GuildID: *discordGuildID,
CategoryID: *discordCategoryID,
CategoryName: *discordCategoryName,
ReleaseMention: *discordReleaseMention,
SendDMs: *discordSendDMs,
})
discordbot.AttachDiscordHandlers(dg, discordServices)
if err := dg.Open(); err != nil {
log.Fatal(err)
}
defer dg.Close()
if *discordAppID != "" {
if err := discordbot.UpsertCommands(dg, *discordAppID); err != nil {
logger.Warn("Discord commands konnten nicht registriert werden", "error", err)
}
} else {
logger.Warn("RW_DISCORD_APP_ID fehlt; Slash-Commands werden nicht registriert")
}
notifier = discordServices
logger.Info("Discord-Bot gestartet")
}
srv, err := web.New(st, am, notifier, logger)
if err != nil {
log.Fatal(err)
}
logger.Info("ReleaseWatcher gestartet", "addr", *addr, "admin", *adminEmail)
log.Fatal(http.ListenAndServe(*addr, srv.Routes()))
}
func env(key, fallback string) string {
if v := os.Getenv(key); v != "" {
return v
}
return fallback
}
func envBool(key string, fallback bool) bool {
switch os.Getenv(key) {
case "1", "true", "TRUE", "yes", "YES", "on", "ON":
return true
case "0", "false", "FALSE", "no", "NO", "off", "OFF":
return false
default:
return fallback
}
}