discord kommunikation
This commit is contained in:
190
cmd/discord/main.go
Normal file
190
cmd/discord/main.go
Normal 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()
|
||||
}
|
||||
Reference in New Issue
Block a user