package discord import ( "fmt" "log" "os" "os/signal" "ticket-pimp/client/discord/discord_handler" "ticket-pimp/client/discord/discord_router" "ticket-pimp/internal/controller" "ticket-pimp/internal/domain" "ticket-pimp/internal/external" "github.com/bwmarrin/discordgo" ) var ( // minLength int = 3 // repoType string = "repo_type" // projectRepo string = "project_repo" // buildRepo string = "build_repo" // nameOption string = "name" tagsPreset = [3]discordgo.ForumTag{ { Name: "В работе", Moderated: true, EmojiName: "👩‍🍳", }, { Name: "Готово", Moderated: true, EmojiName: "✅", }, { Name: "Не начат", Moderated: true, EmojiName: "🚧", }, } // commands = []discordgo.ApplicationCommand{ // { // Name: "ping", // Description: "pongs in a reply", // }, // { // Name: "coda_ticket", // Description: "Creates ticket in Coda.io w/ provided info", // }, // { // Name: "init_project", // Description: "Connect project with Coda ID", // Options: []*discordgo.ApplicationCommandOption{ // { // Type: discordgo.ApplicationCommandOptionString, // Name: "key", // Description: "Project's key from Coda.io", // Required: true, // MinLength: &minLength, // }, // }, // }, // { // Name: "project", // Description: "Create new development ticket", // Options: []*discordgo.ApplicationCommandOption{ // { // Type: discordgo.ApplicationCommandOptionString, // Name: "project_name", // Description: "Temporary project name", // Required: true, // MinLength: &minLength, // }, // }, // }, // { // Name: "info", // Description: "Get project's info", // }, // { // Name: "repo", // Description: "Creates repository of selected type. Name used for projects channels only", // Options: []*discordgo.ApplicationCommandOption{ // { // Type: discordgo.ApplicationCommandOptionString, // Name: repoType, // Description: "The type of repo", // Required: true, // Choices: []*discordgo.ApplicationCommandOptionChoice{ // { // Name: "Unity project repo", // Value: projectRepo, // }, // { // Name: "XCode build repo", // Value: buildRepo, // }, // }, // }, // { // Type: discordgo.ApplicationCommandOptionString, // Name: nameOption, // Description: "Type the repository's name", // Required: false, // MinLength: &minLength, // }, // }, // }, // { // Name: "folder", // Description: "Command for cloud folder creation", // Options: []*discordgo.ApplicationCommandOption{ // { // Type: discordgo.ApplicationCommandOptionString, // Name: nameOption, // Description: "Type the folder's name", // Required: false, // MinLength: &minLength, // }, // }, // }, // } ) func initBotWith(token string) *discordgo.Session { discord, err := discordgo.New("Bot " + token) if err != nil { log.Fatalf("unable to create discord session: %v", err) } return discord } type DiscordOptions struct { Config *domain.Config Controller *controller.WorkflowController } func updateForum(conf *domain.Config, s *discordgo.Session) ([]discordgo.ForumTag, error) { log.Println("Updating forum chan...") // Get tasks channel instance: forum, err := s.Channel(conf.Discord.IsTaskForum) if err != nil { return nil, err } // Map all pre-set tags var tagsMap = map[string]discordgo.ForumTag{} for _, t := range forum.AvailableTags { tagsMap[t.Name] = t } // Result tags array tags := forum.AvailableTags // Check if preset tag exists into current channel.. for i := 0; i < len(tagsPreset); i++ { _, ok := tagsMap[tagsPreset[i].Name] if !ok { // .. and append them if they aren't tags = append(tags, tagsPreset[i]) } } dchan, err := s.ChannelEditComplex(forum.ID, &discordgo.ChannelEdit{ AvailableTags: &tags, }) if err != nil { return nil, err } log.Printf("Channel %s with ID %s propagated by tags:", dchan.Name, dchan.ID) for _, t := range dchan.AvailableTags { fmt.Printf("N: %s, ID: %s", t.Name, t.ID) } // Update config w/ tags: confTags := make(map[domain.TaskState]string) for _, tag := range dchan.AvailableTags { switch tag.Name { case "Не начат": confTags[domain.State(0)] = tag.ID case "В работе": confTags[domain.State(1)] = tag.ID case "Готово": confTags[domain.State(2)] = tag.ID } } conf.Discord.Tags = confTags return dchan.AvailableTags, nil } // commandRegistration // unused, cause method was deprecated; // func commandRegistration(s *discordgo.Session, commands []discordgo.ApplicationCommand) []*discordgo.ApplicationCommand { // oldCommands, err := s.ApplicationCommands(s.State.User.ID, s.State.Application.GuildID) // if err != nil { // log.Panicf("Cannot get old commands: %v", err) // } // var removedCommands []string // for _, cmd := range oldCommands { // err := s.ApplicationCommandDelete(s.State.User.ID, s.State.Application.GuildID, cmd.ID) // if err != nil { // log.Printf("commands removed: %v", removedCommands) // log.Panicf("removing old comands failed with %s command, err: %v", cmd.Name, err) // } // removedCommands = append(removedCommands, cmd.Name) // } // log.Printf("commands removed: %v", removedCommands) // log.Println("Adding commands...") // var cmds []*discordgo.ApplicationCommand // for _, cmd := range commands { // cmd, err := s.ApplicationCommandCreate(s.State.User.ID, "", &cmd) // if err != nil { // log.Panicf("Cannot create '%v' command: %v", cmd.Name, err) // } // cmds = append(cmds, cmd) // log.Println(cmd.Name + " command added") // } // return cmds // } // Run - метод // // 1. Инициализирует бота Discord, создаёт некоторое подобие роутера // 2. Объявляет доступные роутинги // 3. Объявляет доступные для роутов хэндлеры func Run(conf *domain.Config, opts DiscordOptions) error { // bot init s := initBotWith(conf.Discord.Token) // Init new handler h := discord_handler.New( opts.Controller, &conf.Discord, external.NewDummyClient(conf.Telegram), ) r := discord_router.NewApp(s) // Depretated // var commonMw = []discord_router.Middleware{ // h.WithInitialResponse, // h.RejectPM, // } // Depretated // r.Use(commonMw...). // Route("ping", h.Ping). // Route("project", h.CreateProject). // Route("info", h.ProjectInfo). // Route("repo", h.CreateGit). // Route("folder", h.CreateFolder). // Route("init_project", h.InitChannelAsProject). // Route("coda_ticket", h.CreateCoda) // and components r. /*Use().*/ // Combining into group duplicates replies Route("task_start", h.HandleTaskButtons). Route("task_close", h.HandleTaskButtons) // Add posts listener s.AddHandler(h.ListenPosts) // Add interactions handlers s.AddHandler(r.Serve) // session opening if err := s.Open(); err != nil { return fmt.Errorf("cannot open the session: %v", err) } // Forum update tags, err := updateForum(conf, s) if err != nil { log.Println(err.Error()) } //Update handler with tags: h.SetAvailableTags(tags) // commands registration // cmds := commandRegistration(s, commands) // gracefull shutdown defer s.Close() stop := make(chan os.Signal, 1) signal.Notify(stop, os.Interrupt) <-stop log.Println("Graceful shutdown") // log.Println("Removing commands...") // for _, h := range cmds { // err := s.ApplicationCommandDelete(s.State.User.ID, "", h.ID) // if err != nil { // log.Panicf("Cannot delete '%v' command: %v", h.Name, err) // } // } return nil }