diff --git a/client/discord/discord.go b/client/discord/discord.go index 48db455..66964b9 100644 --- a/client/discord/discord.go +++ b/client/discord/discord.go @@ -1,18 +1,119 @@ package discord import ( - "errors" "fmt" "log" "os" "os/signal" - "ticket-pimp/client/discord/router" + "ticket-pimp/client/discord/discord_handler" + "ticket-pimp/internal/controller" "ticket-pimp/internal/domain" + "ticket-pimp/internal/services" "github.com/bwmarrin/discordgo" ) +var ( + minLength int = 3 + repoType string = "repo_type" + projectRepo string = "project_repo" + buildRepo string = "build_repo" + nameOption string = "repo_name" +) + +var tags = []discordgo.ForumTag{ + { + Name: "В работе", + Moderated: true, + EmojiName: "👩‍🍳", + }, + { + Name: "Готово", + Moderated: true, + EmojiName: "✅", + }, +} + +var commands = []discordgo.ApplicationCommand{ + { + Name: "ping", + Description: "pongs in a reply", + }, + { + 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 { @@ -27,60 +128,48 @@ type DiscordOptions struct { Controller *controller.WorkflowController } -func checkPrivateMessaging(s *discordgo.Session, i *discordgo.InteractionCreate) error { - // Моментальный ответ для избежания столкновения с протуханием токена - initialResponse := discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Flags: discordgo.MessageFlagsEphemeral, - Content: "👩‍🍳 Cooking your query..", - }, - } - - s.InteractionRespond(i.Interaction, &initialResponse) - - dchan, err := s.Channel(i.ChannelID) - if err != nil { - return err - } - - if dchan.Type == discordgo.ChannelTypeDM { - s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Content: "Yo, fella! I'm not working in private!", - }, - }) - return errors.New("no private messages! lol") - } - - return nil -} - func Run(conf domain.Config, opts DiscordOptions) error { - token := conf.Discord.Token - s := initBotWith(token) + s := initBotWith(conf.Discord.Token) - router := router.InitRouter(*opts.Controller, &conf.Discord, &conf.Telegram) + h := discord_handler.New( + opts.Controller, + &conf.Discord, + services.NewDummyClient(conf.Telegram), + tags, + ) commandHandlers := map[string]func(s *discordgo.Session, i *discordgo.InteractionCreate){} - for _, handler := range router.Commands { - commandHandlers[handler.Command.Name] = handler.Handler + + for _, cmd := range commands { + var f func(s *discordgo.Session, i *discordgo.InteractionCreate) + + switch cmd.Name { + case "ping": + f = h.Ping + case "project": + f = h.CreateTicket + case "info": + f = h.ProjectInfo + case "repo": + f = h.CreateGit + case "folder": + f = h.CreateFolder + case "init_project": + f = h.InitChannelAsProject + } + commandHandlers[cmd.Name] = f } componentsHandlers := map[string]func(s *discordgo.Session, i *discordgo.InteractionCreate){ - "task_start": router.Components[0].Handler, - "task_close": router.Components[0].Handler, + "task_start": h.HandleTaskButtons, + "task_close": h.HandleTaskButtons, } - s.AddHandler(router.ListenPosts) + s.AddHandler(h.ListenPosts) s.AddHandler(func(s *discordgo.Session, i *discordgo.InteractionCreate) { - err := checkPrivateMessaging(s, i) - if err != nil { - return - } + h.AllInteractions(s, i) switch i.Type { case discordgo.InteractionApplicationCommand: @@ -105,20 +194,8 @@ func Run(conf domain.Config, opts DiscordOptions) error { log.Print(err) } - tagsAvailable := []discordgo.ForumTag{ - { - Name: "В работе", - Moderated: true, - EmojiName: "👩‍🍳", - }, - { - Name: "Готово", - Moderated: true, - EmojiName: "✅", - }, - } dchan, err := s.ChannelEditComplex(forum.ID, &discordgo.ChannelEdit{ - AvailableTags: &tagsAvailable, + AvailableTags: &tags, }) if err != nil { log.Fatal(err) @@ -131,19 +208,16 @@ func Run(conf domain.Config, opts DiscordOptions) error { log.Println("Adding commands...") var cmds []*discordgo.ApplicationCommand - var logString []string - for _, h := range router.Commands { - cmd, err := s.ApplicationCommandCreate(s.State.User.ID, "", &h.Command) + + for _, cmd := range commands { + cmd, err := s.ApplicationCommandCreate(s.State.User.ID, "", &cmd) if err != nil { - log.Panicf("Cannot create '%v' command: %v", h.Command.Name, err) + log.Panicf("Cannot create '%v' command: %v", cmd.Name, err) } cmds = append(cmds, cmd) - logString = append(logString, cmd.Name) + log.Println(cmd.Name + " command added") } - log.Println("Following commands added:") - log.Println(logString) - defer s.Close() stop := make(chan os.Signal, 1) signal.Notify(stop, os.Interrupt) diff --git a/client/discord/discord_handler/common.go b/client/discord/discord_handler/common.go new file mode 100644 index 0000000..913d954 --- /dev/null +++ b/client/discord/discord_handler/common.go @@ -0,0 +1,82 @@ +package discord_handler + +import ( + "fmt" + + "github.com/bwmarrin/discordgo" +) + +func (h *Handler) defaultFollowUp(answer string, s *discordgo.Session, i *discordgo.InteractionCreate) { + + // Sending result: + _, err := s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{ + Content: answer, + }) + + if err != nil { + s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{ + Content: fmt.Sprintf("Something went wrong: %v", err), + }) + return + } +} + +func (h *Handler) AllInteractions(s *discordgo.Session, i *discordgo.InteractionCreate) { + + dchan, err := s.Channel(i.ChannelID) + if err != nil { + return + } + + if dchan.Type == discordgo.ChannelTypeDM { + s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: "Yo, fella! I'm not working in private!", + }, + }) + return + } + + // Моментальный ответ для избежания столкновения с протуханием токена + initialResponse := discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Flags: discordgo.MessageFlagsEphemeral, + Content: "👩‍🍳 Cooking your query..", + }, + } + + s.InteractionRespond(i.Interaction, &initialResponse) +} + +// setFlag +// sets tag with In progress and Done text to discords channel; +func (h *Handler) setFlag(s *discordgo.Session, i *discordgo.InteractionCreate, tag *discordgo.ForumTag) error { + + th, err := s.Channel(i.ChannelID) + if err != nil { + return err + } + + forum, err := s.Channel(th.ParentID) + if err != nil { + return err + } + + // Проверка на существование тега в списке тегов: + if len(forum.AvailableTags) != 0 { + for _, some := range forum.AvailableTags { + if some.Name == tag.Name { + _, err := s.ChannelEditComplex(i.ChannelID, &discordgo.ChannelEdit{ + AppliedTags: &[]string{some.ID}, + }) + if err != nil { + return err + } + } + } + } + + return nil +} diff --git a/client/discord/handler/discord_handler.go b/client/discord/discord_handler/discord_handler.go similarity index 92% rename from client/discord/handler/discord_handler.go rename to client/discord/discord_handler/discord_handler.go index 5556b17..dd9e8fe 100644 --- a/client/discord/handler/discord_handler.go +++ b/client/discord/discord_handler/discord_handler.go @@ -1,4 +1,4 @@ -package handler +package discord_handler import ( "context" @@ -12,20 +12,23 @@ import ( ) type Handler struct { - controller *controller.WorkflowController - conf *domain.DiscordConfig - tg adapters.IDummyTelegram + controller *controller.WorkflowController + conf *domain.DiscordConfig + telegramDummyClient adapters.IDummyTelegram + tags []discordgo.ForumTag } -func NewHandler( +func New( controller *controller.WorkflowController, conf *domain.DiscordConfig, tg adapters.IDummyTelegram, + tags []discordgo.ForumTag, ) *Handler { return &Handler{ - controller: controller, - conf: conf, - tg: tg, + controller: controller, + conf: conf, + telegramDummyClient: tg, + tags: tags, } } @@ -42,37 +45,6 @@ func (h *Handler) Ping(s *discordgo.Session, i *discordgo.InteractionCreate) { } } -// setFlag -// sets tag with In progress and Done text to discords channel; -func (h *Handler) setFlag(s *discordgo.Session, i *discordgo.InteractionCreate, tag *discordgo.ForumTag) error { - - th, err := s.Channel(i.ChannelID) - if err != nil { - return err - } - - forum, err := s.Channel(th.ParentID) - if err != nil { - return err - } - - // Проверка на существование тега в списке тегов: - if len(forum.AvailableTags) != 0 { - for _, some := range forum.AvailableTags { - if some.Name == tag.Name { - _, err := s.ChannelEditComplex(i.ChannelID, &discordgo.ChannelEdit{ - AppliedTags: &[]string{some.ID}, - }) - if err != nil { - return err - } - } - } - } - - return nil -} - // ListenPosts // ..listens to new posts in specific channel // to act them like a task @@ -151,10 +123,10 @@ func (h *Handler) ListenPosts(s *discordgo.Session, th *discordgo.ThreadCreate) func (h *Handler) HandleTaskButtons(s *discordgo.Session, i *discordgo.InteractionCreate) { // Send an empty interaction response; ---------------------------------------------------------------- - s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseUpdateMessage, - Data: &discordgo.InteractionResponseData{}, - }) + // s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + // Type: discordgo.InteractionResponseUpdateMessage, + // Data: &discordgo.InteractionResponseData{}, + // }) // Get assignee value; --------------------------------------------------------------------------------- user := i.Member.User.Mention() @@ -164,6 +136,7 @@ func (h *Handler) HandleTaskButtons(s *discordgo.Session, i *discordgo.Interacti doneButtonIsDisabled bool = false state domain.TaskState = domain.NewTaskState() message string + tag discordgo.ForumTag ) // Check what flow was touched: ------------------------------------------------------------------------- @@ -173,11 +146,13 @@ func (h *Handler) HandleTaskButtons(s *discordgo.Session, i *discordgo.Interacti doneButtonIsDisabled = false state = domain.InrpogressTaskState() message = "взята в работу" + tag = h.tags[0] case "task_close": opt = 1 doneButtonIsDisabled = true state = domain.DoneTaskState() message = "выполнена" + tag = h.tags[1] } // Send the task update to db -------------------------------------------------------------------------- @@ -196,7 +171,7 @@ func (h *Handler) HandleTaskButtons(s *discordgo.Session, i *discordgo.Interacti // Send message to the creator in Telegram: ------------------------------------------------------------- if task.CreatorLink != "" { - h.tg.DummyNotification( + h.telegramDummyClient.DummyNotification( task.CreatorLink, fmt.Sprintf("Task ID: %d %s", task.ID, message)) } @@ -231,10 +206,10 @@ func (h *Handler) HandleTaskButtons(s *discordgo.Session, i *discordgo.Interacti } // [ ] Устанавливаем тэги статуса на тред --------------------------------------------------------------------- - // err = h.setFlag(s, i, &h.Tags[opt]) - // if err != nil { - // log.Printf("error while `start` tag setting: %v", err) - // } + err = h.setFlag(s, i, &tag) + if err != nil { + log.Printf("error while `start` tag setting: %v", err) + } } func (h *Handler) CreateFolder(s *discordgo.Session, i *discordgo.InteractionCreate) { diff --git a/client/discord/handler/common.go b/client/discord/handler/common.go deleted file mode 100644 index 2dfe4d2..0000000 --- a/client/discord/handler/common.go +++ /dev/null @@ -1,22 +0,0 @@ -package handler - -import ( - "fmt" - - "github.com/bwmarrin/discordgo" -) - -func (h *Handler) defaultFollowUp(answer string, s *discordgo.Session, i *discordgo.InteractionCreate) { - - // Sending result: - _, err := s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{ - Content: answer, - }) - - if err != nil { - s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{ - Content: fmt.Sprintf("Something went wrong: %v", err), - }) - return - } -} diff --git a/client/discord/router/handle_external_task.go b/client/discord/router/handle_external_task.go deleted file mode 100644 index 25e2a1f..0000000 --- a/client/discord/router/handle_external_task.go +++ /dev/null @@ -1,222 +0,0 @@ -package router - -import ( - "context" - "fmt" - "log" - "ticket-pimp/internal/domain" - - "github.com/bwmarrin/discordgo" - "github.com/imroc/req/v3" -) - -func (c *client) setFlag(s *discordgo.Session, i *discordgo.InteractionCreate, tag *discordgo.ForumTag) error { - - th, err := s.Channel(i.ChannelID) - if err != nil { - return err - } - - forum, err := s.Channel(th.ParentID) - if err != nil { - return err - } - - // Проверка на существование тега в списке тегов: - if len(forum.AvailableTags) != 0 { - for _, some := range forum.AvailableTags { - if some.Name == tag.Name { - _, err := s.ChannelEditComplex(i.ChannelID, &discordgo.ChannelEdit{ - AppliedTags: &[]string{some.ID}, - }) - if err != nil { - return err - } - } - } - } - - return nil -} - -func (c *client) ListenPosts(s *discordgo.Session, th *discordgo.ThreadCreate) { - - // Check if thread starter is not a bot, and thread started at the tasks channel; - if th.ParentID != c.conf.IsTaskForum || th.OwnerID == s.State.User.ID { - return - } - - msgs, _ := s.ChannelMessages(th.ID, 1, "", "", "") - - msg, _ := s.ChannelMessage(th.ID, msgs[0].ID) - - if msg.Author.ID == s.State.User.ID { - return - } - - content := th.Name - content += "\n" + msg.Content - - user, _ := s.GuildMember(th.GuildID, msg.Author.ID) - - t, err := c.controller.WriteTaskToDB(&domain.Task{ - Description: content, - Creator: user.User.Mention(), - }) - if err != nil { - s.ChannelMessageSend(th.ID, fmt.Sprintf("unable to write task to db, %v", err)) - return - } - - // [x] -- Отредактировать Thread name как для задачи - _, err = s.ChannelEditComplex(th.ID, &discordgo.ChannelEdit{ - Name: fmt.Sprintf("Task ID: %d, by %s", t.ID, t.Creator), - }) - if err != nil { - log.Printf("th edition is not complete: %v", err) - } - - // Fix the original task message: - taskMessage, err := s.ChannelMessageSendComplex(th.ID, &discordgo.MessageSend{ - Content: content, - Components: []discordgo.MessageComponent{ - discordgo.ActionsRow{ - Components: []discordgo.MessageComponent{ - discordgo.Button{ - Label: "Start", - Style: discordgo.SuccessButton, - Disabled: false, - CustomID: "task_start", - }, - discordgo.Button{ - Label: "Close", - Style: discordgo.DangerButton, - Disabled: true, - CustomID: "task_close", - }, - }, - }, - }, - }) - if err != nil { - log.Printf("th start message edition is not complete: %v", err) - } - - err = c.controller.UpdateTasksMessageID(context.TODO(), taskMessage.ID, t.ID) - if err != nil { - s.ChannelMessageSend(th.ID, fmt.Sprintf("unable to update task at the db, %v", err)) - return - } -} - -func (c *client) HandleTaskButtons() Component { - return Component{ - Handler: c.handleTaskButton, - } -} - -func (c *client) handleTaskButton(s *discordgo.Session, i *discordgo.InteractionCreate) { - - // Get assignee value; --------------------------------------------------------------------------------- - user := i.Member.User.Mention() - - var ( - opt int = -1 - doneButtonIsDisabled bool = false - state domain.TaskState = domain.NewTaskState() - message string - ) - - // Check what flow was touched: ------------------------------------------------------------------------- - switch i.Interaction.MessageComponentData().CustomID { - case "task_start": - opt = 0 - doneButtonIsDisabled = false - state = domain.InrpogressTaskState() - message = "взята в работу" - case "task_close": - opt = 1 - doneButtonIsDisabled = true - state = domain.DoneTaskState() - message = "выполнена" - } - - // Send the task update to db -------------------------------------------------------------------------- - convertable, err := c.controller.UpdateTask(i.Message.ID, opt, user) - if err != nil { - s.ChannelMessageSend(i.ChannelID, fmt.Sprintf("Unable to update task at the db w/ error: %v", err)) - return - } - - // Map DB's response to domain.Task: ------------------------------------------------------------------- - task := convertable. - ExtractDomain() - - newContent := task.DiscordMessage(state) - - // Send message to the creator in Telegram: ------------------------------------------------------------- - - if task.CreatorLink != "" { - c.sendTelegramMessageToCreator( - task.CreatorLink, - fmt.Sprintf("Task ID: %d %s", task.ID, message)) - } - - // Send a message to the thread about the task was started: --------------------------------------------- - _, err = s.ChannelMessageSendComplex(i.ChannelID, &discordgo.MessageSend{ - Content: newContent, - }) - if err != nil { - log.Printf("error while sending start task message: %v", err) - } - - // Fix the original task message: ---------------------------------------------------------------------- - _, err = s.ChannelMessageEditComplex(&discordgo.MessageEdit{ - Channel: i.ChannelID, - ID: i.Message.ID, - Components: []discordgo.MessageComponent{ - discordgo.ActionsRow{ - Components: []discordgo.MessageComponent{ - discordgo.Button{ - Label: "Close", - Style: discordgo.DangerButton, - Disabled: doneButtonIsDisabled, - CustomID: "task_close", - }, - }, - }, - }, - }) - if err != nil { - log.Printf("th start message edition is not complete: %v", err) - } - - // Устанавливаем тэги статуса на тред --------------------------------------------------------------------- - // err = c.setFlag(s, i, &c.Tags[opt]) - // if err != nil { - // log.Printf("error while `start` tag setting: %v", err) - // } -} - -type TelegramMessage struct { - ChatID string `json:"chat_id"` - Text string `json:"text"` - DisableNotification bool `json:"disable_notification"` - ParseMode string `json:"parse_mode"` - DisablePreview bool `json:"disable_web_page_preview"` -} - -// [ ] As a separate service? -func (c *client) sendTelegramMessageToCreator(tgChatID string, text string) { - - http := req.C() - http.R(). - SetBody(&TelegramMessage{ - ChatID: tgChatID, - Text: text, - DisableNotification: true, - ParseMode: "HTML", - DisablePreview: true, - }). - Post("https://api.telegram.org/bot" + c.tgConf.Token + "/sendMessage") -} diff --git a/client/discord/router/handle_folder.go b/client/discord/router/handle_folder.go deleted file mode 100644 index 0efed58..0000000 --- a/client/discord/router/handle_folder.go +++ /dev/null @@ -1,86 +0,0 @@ -package router - -import ( - "context" - "log" - "ticket-pimp/internal/controller" - - "github.com/bwmarrin/discordgo" -) - -func (c *client) CreateFolderHandler(nameMinLenght int) Command { - const ( - nameOption string = "folder_name" - ) - return Command{ - - Command: discordgo.ApplicationCommand{ - 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: &nameMinLenght, - }, - }, - }, - - Handler: c.createFolderHandler, - } -} - -func (c *client) createFolderHandler(s *discordgo.Session, i *discordgo.InteractionCreate) { - const ( - nameOption string = "folder_name" - ) - - // Определение переменной для ответа - var result string = "unexpected result" - - // Определение выбранных вариантов ответа - options := i.ApplicationCommandData().Options - - optionMap := make(map[string]*discordgo.ApplicationCommandInteractionDataOption, len(options)) - for _, opt := range options { - optionMap[opt.Name] = opt - } - - // Creating request: - var req controller.FolderRequest - name, insertedValueNotNil := optionMap[nameOption] - dchan, err := s.Channel(i.ChannelID) - - if err != nil { - log.Printf("error while identifying channel: %v", err) - } else { - - if dchan.ParentID == c.conf.IsProjectChannel { - req.ChannelID = dchan.ID - if insertedValueNotNil { - req.InsertedName = name.StringValue() - } - - } else { - req.ChannelID = "" - if insertedValueNotNil { - req.InsertedName = name.StringValue() - } - } - } - - // Making request: - resp := c.controller.CreateFolder(context.TODO(), req) - if resp.Project == nil { - result = "Надо написать имя для папки, или создать папку из проекта!" - } else { - result = resp.Project.DiscordString() - if resp.Message != nil { - result += "Errors: " + resp.Message.Error() - } - } - - c.defaultFollowUp(result, s, i) -} diff --git a/client/discord/router/handle_git.go b/client/discord/router/handle_git.go deleted file mode 100644 index 0d71953..0000000 --- a/client/discord/router/handle_git.go +++ /dev/null @@ -1,117 +0,0 @@ -package router - -import ( - "context" - "log" - "ticket-pimp/internal/controller" - - "github.com/bwmarrin/discordgo" -) - -func (c *client) CreateRepoHandler(repoNameMinLength int) Command { - const ( - repoType = "repo_type" - projectRepo = "project_repo" - buildRepo = "build_repo" - nameOption = "repo_name" - ) - - return Command{ - Command: discordgo.ApplicationCommand{ - 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: &repoNameMinLength, - }, - }, - }, - Handler: c.createRepoHandler, - } -} - -func (c *client) createRepoHandler(s *discordgo.Session, i *discordgo.InteractionCreate) { - const ( - repoType = "repo_type" - projectRepo = "project_repo" - buildRepo = "build_repo" - nameOption = "repo_name" - ) - - // Определение переменной для ответа - var result string = "unexpected result" - - // Access options in the order provided by the user. - options := i.ApplicationCommandData().Options - - // Or convert the slice into a map - optionMap := make(map[string]*discordgo.ApplicationCommandInteractionDataOption, len(options)) - for _, opt := range options { - optionMap[opt.Name] = opt - } - - // Creating request: - var req controller.GitRequest - name, insertedValueNotNil := optionMap[nameOption] - isBuild := optionMap[repoType] - switch isBuild.StringValue() { - case buildRepo: - req.IsBuildGit = true - case projectRepo: - req.IsBuildGit = false - } - dchan, err := s.Channel(i.ChannelID) - - if err != nil { - log.Printf("error while identifying channel: %v", err) - } else { - - if dchan.ParentID == c.conf.IsProjectChannel { - req.ChannelID = dchan.ID - if insertedValueNotNil { - req.InsertedName = name.StringValue() - } - } else { - req.ChannelID = "" - if insertedValueNotNil { - req.InsertedName = name.StringValue() - } - } - } - - // Making request: - resp := c.controller.CreateGit(context.TODO(), req) - if resp.Project == nil { - if resp.Message != nil { - result = resp.Message.Error() - } - } else { - - result = resp.Project.DiscordString() - if resp.Message != nil { - result += "Errors: " + resp.Message.Error() - } - - } - c.defaultFollowUp(result, s, i) -} diff --git a/client/discord/router/handle_ping.go b/client/discord/router/handle_ping.go deleted file mode 100644 index 9257715..0000000 --- a/client/discord/router/handle_ping.go +++ /dev/null @@ -1,30 +0,0 @@ -package router - -import ( - "log" - - "github.com/bwmarrin/discordgo" -) - -func (c *client) Ping() Command { - return Command{ - Command: discordgo.ApplicationCommand{ - Name: "ping", - Description: "pongs in a reply", - }, - Handler: c.ping, - } -} - -func (c *client) ping(s *discordgo.Session, i *discordgo.InteractionCreate) { - - err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Content: "Pong to: " + i.Member.User.Mention(), - }, - }) - if err != nil { - log.Println(err) - } -} diff --git a/client/discord/router/handle_ticket.go b/client/discord/router/handle_ticket.go deleted file mode 100644 index 85eef5c..0000000 --- a/client/discord/router/handle_ticket.go +++ /dev/null @@ -1,188 +0,0 @@ -package router - -import ( - "context" - "fmt" - "log" - "ticket-pimp/internal/domain" - "ticket-pimp/internal/helpers" - - "github.com/bwmarrin/discordgo" -) - -func (c *client) GetInfo() Command { - return Command{ - Command: discordgo.ApplicationCommand{ - Name: "info", - Description: "Get project's info", - }, - Handler: c.getInfo, - } -} - -func (c *client) getInfo(s *discordgo.Session, i *discordgo.InteractionCreate) { - - var result string - - // Get channel from the request - dchan, err := s.Channel(i.ChannelID) - if err != nil { - result = "unable to get channel from the message" - } else { - project, err := c.controller.GetProjectByChannelID(context.TODO(), dchan.ID) - if err != nil { - result = err.Error() - } else { - if project != nil { - result = project.DiscordString() - if err != nil { - result += "Errors: " + err.Error() - } - } else { - result = "Something wrong with retrieving project from db" - } - - } - } - - c.defaultFollowUp(result, s, i) -} - -func (c *client) InitProjectFromChannel(minLength int) Command { - return Command{ - Command: discordgo.ApplicationCommand{ - 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, - }, - }, - }, - Handler: c.initProjectFromChannel, - } -} - -func (c *client) initProjectFromChannel(s *discordgo.Session, i *discordgo.InteractionCreate) { - - var result string - - // Get channel from the request - dchan, err := s.Channel(i.ChannelID) - if err != nil { - result = "unable to get channel from the message" - } else { - if dchan.ParentID != c.conf.IsProjectChannel { - // Sending result: - _, err := s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{ - Content: "This channel is not at the project's group", - }) - - if err != nil { - s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{ - Content: fmt.Sprintf("Something went wrong: %v", err), - }) - return - } - return - } - - // Access options in the order provided by the user. - options := i.ApplicationCommandData().Options - - // Or convert the slice into a map - optionMap := make(map[string]*discordgo.ApplicationCommandInteractionDataOption, len(options)) - for _, opt := range options { - optionMap[opt.Name] = opt - } - - if option, ok := optionMap["key"]; ok { - var errMsg error = nil - - project, err := c.controller.InitProjectInChannel(context.TODO(), i.ChannelID, option.StringValue()) - if err != nil { - result = fmt.Sprintf("unable to init project: %v", err) - } else { - result = project.DiscordString() - if errMsg != nil { - result += "Errors: " + errMsg.Error() - } - } - } - } - - c.defaultFollowUp(result, s, i) -} - -func (c *client) CreateTicketHandler(repoNameMinLength int) Command { - return Command{ - Command: discordgo.ApplicationCommand{ - Name: "project", - Description: "Create new development ticket", - Options: []*discordgo.ApplicationCommandOption{ - { - Type: discordgo.ApplicationCommandOptionString, - Name: "project_name", - Description: "Temporary project name", - Required: true, - MinLength: &repoNameMinLength, - }, - }, - }, - Handler: c.createTicketHandler, - } -} - -func (c *client) createTicketHandler(s *discordgo.Session, i *discordgo.InteractionCreate) { - - var result string - // Access options in the order provided by the user. - options := i.ApplicationCommandData().Options - - // Or convert the slice into a map - optionMap := make(map[string]*discordgo.ApplicationCommandInteractionDataOption, len(options)) - for _, opt := range options { - optionMap[opt.Name] = opt - } - - if option, ok := optionMap["project_name"]; ok { - dchan, err := s.GuildChannelCreate(i.GuildID, option.StringValue(), discordgo.ChannelTypeGuildText) - if err != nil { - result = fmt.Sprintf("chan creation problem: %v\n", err) - } else { - p, err := c.controller.ProjectCreate(context.TODO(), domain.Project{ - ChannelID: dchan.ID, - }) - if err != nil { - result = fmt.Sprintf("unable to create project: %v\n", err) - _, err := s.ChannelDelete(dchan.ID) - if err != nil { - result += fmt.Sprintf("\nunable to clean channel: %v\n", err) - } - } else { - edit := discordgo.ChannelEdit{ - Name: p.ShortName + "-" + helpers.Cut(option.StringValue()), - ParentID: c.conf.IsProjectChannel, - } - - dchan, err = s.ChannelEdit(dchan.ID, &edit) - if err != nil { - result = fmt.Sprintf("channel %s created, but unable to edit follow up message: %v\n", p.ShortName, err) - - } else { - _, err = s.ChannelMessageSend(dchan.ID, "Hello!") - if err != nil { - log.Printf("message send problem: %v\n", err) - } - result = "Project " + p.ShortName + " was created" - } - } - } - } - - c.defaultFollowUp(result, s, i) -} diff --git a/client/discord/router/handler.go b/client/discord/router/handler.go deleted file mode 100644 index 27f3097..0000000 --- a/client/discord/router/handler.go +++ /dev/null @@ -1,71 +0,0 @@ -package router - -import ( - "fmt" - "ticket-pimp/internal/controller" - "ticket-pimp/internal/domain" - - "github.com/bwmarrin/discordgo" -) - -type client struct { - Commands []Command - Components []Component - - // Tags []discordgo.ForumTag - - controller controller.WorkflowController - conf *domain.DiscordConfig - tgConf *domain.TelegramConfig -} - -// Подключение роутов к Discord боту -func InitRouter(wc controller.WorkflowController, conf *domain.DiscordConfig, tgConf *domain.TelegramConfig) *client { - - var r client - r.controller = wc - r.conf = conf - - r.Commands = append(r.Commands, - r.CreateRepoHandler(3), - r.CreateFolderHandler(3), - r.Ping(), - r.CreateTicketHandler(3), - r.InitProjectFromChannel(3), - r.GetInfo(), - ) - r.Components = append(r.Components, - r.HandleTaskButtons(), - ) - r.tgConf = tgConf - - return &r -} - -// -// Подключение роутов к Discord боту - -type Command struct { - Command discordgo.ApplicationCommand - Handler func(s *discordgo.Session, i *discordgo.InteractionCreate) -} - -type Component struct { - Component discordgo.MessageComponent - Handler func(s *discordgo.Session, i *discordgo.InteractionCreate) -} - -func (h *client) defaultFollowUp(answer string, s *discordgo.Session, i *discordgo.InteractionCreate) { - - // Sending result: - _, err := s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{ - Content: answer, - }) - - if err != nil { - s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{ - Content: fmt.Sprintf("Something went wrong: %v", err), - }) - return - } -} diff --git a/client/telegram/telegram.go b/client/telegram/telegram.go index 63fb7d8..6f81531 100644 --- a/client/telegram/telegram.go +++ b/client/telegram/telegram.go @@ -3,7 +3,7 @@ package telegram import ( "context" "log" - "ticket-pimp/client/telegram/handler" + "ticket-pimp/client/telegram/telegram_handler" "ticket-pimp/internal/controller" "ticket-pimp/internal/domain" "ticket-pimp/internal/services" @@ -30,7 +30,7 @@ func Run(ctx context.Context, opts TelegramOptions) error { log.Print("Start telegram bot init..") client := tg.New(opts.AppConfig.Telegram.Token) - h := handler.NewHandler( + h := telegram_handler.NewHandler( opts.GitService, opts.CloudService, opts.Coda, diff --git a/client/telegram/handler/handle_application.go b/client/telegram/telegram_handler/handle_application.go similarity index 97% rename from client/telegram/handler/handle_application.go rename to client/telegram/telegram_handler/handle_application.go index a24f673..38cba20 100644 --- a/client/telegram/handler/handle_application.go +++ b/client/telegram/telegram_handler/handle_application.go @@ -1,4 +1,4 @@ -package handler +package telegram_handler // func (h *Handler) DevelopmentTaskHandler(ctx context.Context, mu *tgb.MessageUpdate) error { diff --git a/client/telegram/handler/handle_farmtask.go b/client/telegram/telegram_handler/handle_farmtask.go similarity index 98% rename from client/telegram/handler/handle_farmtask.go rename to client/telegram/telegram_handler/handle_farmtask.go index 128c120..6442dc6 100644 --- a/client/telegram/handler/handle_farmtask.go +++ b/client/telegram/telegram_handler/handle_farmtask.go @@ -1,4 +1,4 @@ -package handler +package telegram_handler import ( "context" diff --git a/client/telegram/handler/handle_folder.go b/client/telegram/telegram_handler/handle_folder.go similarity index 97% rename from client/telegram/handler/handle_folder.go rename to client/telegram/telegram_handler/handle_folder.go index fa88377..0a903f9 100644 --- a/client/telegram/handler/handle_folder.go +++ b/client/telegram/telegram_handler/handle_folder.go @@ -1,4 +1,4 @@ -package handler +package telegram_handler import ( "context" diff --git a/client/telegram/handler/handle_git.go b/client/telegram/telegram_handler/handle_git.go similarity index 97% rename from client/telegram/handler/handle_git.go rename to client/telegram/telegram_handler/handle_git.go index 52e0a76..d53240d 100644 --- a/client/telegram/handler/handle_git.go +++ b/client/telegram/telegram_handler/handle_git.go @@ -1,4 +1,4 @@ -package handler +package telegram_handler import ( "context" diff --git a/client/telegram/handler/handle_init.go b/client/telegram/telegram_handler/handle_init.go similarity index 98% rename from client/telegram/handler/handle_init.go rename to client/telegram/telegram_handler/handle_init.go index c01ad4c..266e7d2 100644 --- a/client/telegram/handler/handle_init.go +++ b/client/telegram/telegram_handler/handle_init.go @@ -1,4 +1,4 @@ -package handler +package telegram_handler import ( "context" diff --git a/client/telegram/handler/handler.go b/client/telegram/telegram_handler/handler.go similarity index 95% rename from client/telegram/handler/handler.go rename to client/telegram/telegram_handler/handler.go index 283cb8d..1b91fa4 100644 --- a/client/telegram/handler/handler.go +++ b/client/telegram/telegram_handler/handler.go @@ -1,4 +1,4 @@ -package handler +package telegram_handler import ( "ticket-pimp/adapters" diff --git a/internal/services/dummy_telegram.go b/internal/services/dummy_telegram.go index dca909f..79ace7e 100644 --- a/internal/services/dummy_telegram.go +++ b/internal/services/dummy_telegram.go @@ -1,12 +1,28 @@ package services -import "ticket-pimp/internal/domain" +import ( + "ticket-pimp/internal/domain" + "time" +) type DummyTelegram struct { *CommonClient config domain.TelegramConfig } +func NewDummyClient(conf domain.TelegramConfig) *DummyTelegram { + + client := NewClient(). + SetTimeout(5 * time.Second) + + return &DummyTelegram{ + CommonClient: &CommonClient{ + Client: client, + }, + config: conf, + } +} + type TelegramMessage struct { ChatID string `json:"chat_id"` Text string `json:"text"` diff --git a/internal/services/git.go b/internal/services/git.go index 1d9800f..4671cc6 100644 --- a/internal/services/git.go +++ b/internal/services/git.go @@ -86,7 +86,6 @@ func (gb *Git) defaultGroupAsCollaborator(git *domain.Git) (*domain.Git, error) Perm: "push", } - // respURL := "/orgs/mobilerino/teams/devs/repos/mobilerino/" + git.Name respURL := fmt.Sprintf("/orgs/%s/teams/devs/repos/%s/%s", gb.conf.OrgName, gb.conf.OrgName, git.Name) resp, _ := gb.R().