discord kommunikation

This commit is contained in:
Christoph K.
2026-03-12 19:16:29 +01:00
parent 92f520101a
commit 5337e7af6f
7 changed files with 249 additions and 16 deletions

190
cmd/discord/main.go Normal file
View File

@@ -0,0 +1,190 @@
// discord Discord-Bot für my-brain-importer
// Unterstützt /ask, /ingest und @Mention
package main
import (
"fmt"
"log"
"os"
"os/signal"
"strings"
"syscall"
"github.com/bwmarrin/discordgo"
"my-brain-importer/internal/brain"
"my-brain-importer/internal/config"
)
var (
dg *discordgo.Session
botUser *discordgo.User
commands = []*discordgo.ApplicationCommand{
{
Name: "ask",
Description: "Stelle eine Frage an deinen AI Brain",
Options: []*discordgo.ApplicationCommandOption{
{
Type: discordgo.ApplicationCommandOptionString,
Name: "frage",
Description: "Die Frage, die du stellen möchtest",
Required: true,
},
},
},
{
Name: "ingest",
Description: "Importiert Markdown-Notizen aus brain_root in die Wissensdatenbank",
},
}
)
func main() {
config.LoadConfig()
token := config.Cfg.Discord.Token
if token == "" || token == "dein-discord-bot-token" {
log.Fatal("❌ Kein Discord-Token in config.yml konfiguriert (discord.token)")
}
var err error
dg, err = discordgo.New("Bot " + token)
if err != nil {
log.Fatalf("❌ Discord-Session konnte nicht erstellt werden: %v", err)
}
dg.AddHandler(onReady)
dg.AddHandler(onInteraction)
dg.AddHandler(onMessage)
dg.Identify.Intents = discordgo.IntentsGuilds |
discordgo.IntentsGuildMessages |
discordgo.IntentMessageContent
if err = dg.Open(); err != nil {
log.Fatalf("❌ Verbindung zu Discord fehlgeschlagen: %v", err)
}
defer dg.Close()
registerCommands()
fmt.Println("✅ Bot läuft. Drücke CTRL+C zum Beenden.")
stop := make(chan os.Signal, 1)
signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
<-stop
fmt.Println("\n👋 Bot wird beendet...")
}
func onReady(s *discordgo.Session, r *discordgo.Ready) {
botUser = r.User
fmt.Printf("✅ Eingeloggt als %s#%s\n", r.User.Username, r.User.Discriminator)
}
func registerCommands() {
guildID := config.Cfg.Discord.GuildID
for _, cmd := range commands {
_, err := dg.ApplicationCommandCreate(dg.State.User.ID, guildID, cmd)
if err != nil {
log.Printf("⚠️ Slash-Command /%s konnte nicht registriert werden: %v", cmd.Name, err)
} else {
scope := "global"
if guildID != "" {
scope = "guild " + guildID
}
fmt.Printf("📝 Slash-Command /%s registriert (%s)\n", cmd.Name, scope)
}
}
}
func onInteraction(s *discordgo.Session, i *discordgo.InteractionCreate) {
if i.Type != discordgo.InteractionApplicationCommand {
return
}
switch i.ApplicationCommandData().Name {
case "ask":
handleAsk(s, i, i.ApplicationCommandData().Options[0].StringValue())
case "ingest":
handleIngest(s, i)
}
}
func onMessage(s *discordgo.Session, m *discordgo.MessageCreate) {
if m.Author.Bot {
return
}
if botUser == nil {
return
}
mentioned := false
for _, u := range m.Mentions {
if u.ID == botUser.ID {
mentioned = true
break
}
}
if !mentioned {
return
}
// Mention aus der Nachricht entfernen
question := strings.TrimSpace(
strings.ReplaceAll(m.Content, "<@"+botUser.ID+">", ""),
)
if question == "" {
s.ChannelMessageSend(m.ChannelID, "Stell mir eine Frage! Beispiel: @Brain Was sind meine TODOs?")
return
}
s.ChannelTyping(m.ChannelID)
reply := queryAndFormat(question)
s.ChannelMessageSendReply(m.ChannelID, reply, m.Reference())
}
func handleAsk(s *discordgo.Session, i *discordgo.InteractionCreate, question string) {
// Sofort mit "Denke nach..." antworten (Discord-Timeout: 3s)
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseDeferredChannelMessageWithSource,
})
reply := queryAndFormat(question)
s.InteractionResponseEdit(i.Interaction, &discordgo.WebhookEdit{
Content: &reply,
})
}
func handleIngest(s *discordgo.Session, i *discordgo.InteractionCreate) {
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseDeferredChannelMessageWithSource,
})
fmt.Println("📥 Ingest gestartet via Discord...")
brain.RunIngest(config.Cfg.BrainRoot)
msg := fmt.Sprintf("✅ Ingest abgeschlossen! Quelle: `%s`", config.Cfg.BrainRoot)
s.InteractionResponseEdit(i.Interaction, &discordgo.WebhookEdit{
Content: &msg,
})
}
func queryAndFormat(question string) string {
answer, chunks, err := brain.AskQuery(question)
if err != nil {
return fmt.Sprintf("❌ Fehler: %v", err)
}
if len(chunks) == 0 {
return "❌ Keine relevanten Informationen in der Datenbank gefunden.\nFüge mehr Daten mit `/ingest` hinzu."
}
var sb strings.Builder
fmt.Fprintf(&sb, "💬 **Antwort auf:** _%s_\n\n", question)
sb.WriteString(answer)
sb.WriteString("\n\n📚 **Quellen:**\n")
for _, chunk := range chunks {
fmt.Fprintf(&sb, "• %.1f%% %s\n", chunk.Score*100, chunk.Source)
}
return sb.String()
}