ticket-pimp/client/discord/handler/discord_handler.go

542 lines
15 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package handler
import (
"context"
"fmt"
"log"
"ticket-pimp/adapters"
"ticket-pimp/internal/controller"
"ticket-pimp/internal/domain"
"github.com/bwmarrin/discordgo"
)
type Handler struct {
controller *controller.WorkflowController
conf *domain.DiscordConfig
tg adapters.IDummyTelegram
}
func NewHandler(
controller *controller.WorkflowController,
conf *domain.DiscordConfig,
tg adapters.IDummyTelegram,
) *Handler {
return &Handler{
controller: controller,
conf: conf,
tg: tg,
}
}
func (h *Handler) 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)
}
}
// 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
func (h *Handler) 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 != h.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 := h.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 = h.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
}
}
// handleTaskBurrons
// .. handler function to work with the Action Buttons over a task
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{},
})
// 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 := h.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 != "" {
h.tg.DummyNotification(
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 = h.setFlag(s, i, &h.Tags[opt])
// if err != nil {
// log.Printf("error while `start` tag setting: %v", err)
// }
}
func (h *Handler) CreateFolder(s *discordgo.Session, i *discordgo.InteractionCreate) {
const (
nameOption string = "folder_name"
)
// Моментальный ответ для избежания столкновения с протуханием токена
initialResponse := discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Flags: discordgo.MessageFlagsEphemeral,
Content: "👩‍🍳 Cooking your query..",
},
}
s.InteractionRespond(i.Interaction, &initialResponse)
// Определение переменной для ответа
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 == h.conf.IsProjectChannel {
req.ChannelID = dchan.ID
if insertedValueNotNil {
req.InsertedName = name.StringValue()
}
} else {
req.ChannelID = ""
if insertedValueNotNil {
req.InsertedName = name.StringValue()
}
}
}
// Making request:
resp := h.controller.CreateFolder(context.TODO(), req)
if resp.Project == nil {
result = "Надо написать имя для папки, или создать папку из проекта!"
} else {
result = resp.Project.DiscordString()
if resp.Message != nil {
result += "Errors: " + resp.Message.Error()
}
}
h.defaultFollowUp(result, s, i)
}
func (h *Handler) CreateGit(s *discordgo.Session, i *discordgo.InteractionCreate) {
const (
repoType = "repo_type"
projectRepo = "project_repo"
buildRepo = "build_repo"
nameOption = "repo_name"
)
// Моментальный ответ для избежания столкновения с протуханием токена
initialResponse := discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Flags: discordgo.MessageFlagsEphemeral,
Content: "👩‍🍳 Cooking your query..",
},
}
s.InteractionRespond(i.Interaction, &initialResponse)
// Определение переменной для ответа
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 == h.conf.IsProjectChannel {
req.ChannelID = dchan.ID
if insertedValueNotNil {
req.InsertedName = name.StringValue()
}
} else {
req.ChannelID = ""
if insertedValueNotNil {
req.InsertedName = name.StringValue()
}
}
}
// Making request:
resp := h.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()
}
}
h.defaultFollowUp(result, s, i)
}
// PROJECT
func (h *Handler) ProjectInfo(s *discordgo.Session, i *discordgo.InteractionCreate) {
// Моментальный ответ для избежания столкновения с протуханием токена
initialResponse := discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Flags: discordgo.MessageFlagsEphemeral,
Content: "👩‍🍳 Cooking your query..",
},
}
s.InteractionRespond(i.Interaction, &initialResponse)
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 := h.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"
}
}
}
h.defaultFollowUp(result, s, i)
}
func (h *Handler) InitChannelAsProject(s *discordgo.Session, i *discordgo.InteractionCreate) {
// Моментальный ответ для избежания столкновения с протуханием токена
initialResponse := discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Flags: discordgo.MessageFlagsEphemeral,
Content: "👩‍🍳 Cooking your query..",
},
}
s.InteractionRespond(i.Interaction, &initialResponse)
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 != h.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 := h.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()
}
}
}
}
h.defaultFollowUp(result, s, i)
}
func (h *Handler) CreateTicket(s *discordgo.Session, i *discordgo.InteractionCreate) {
// Моментальный ответ для избежания столкновения с протуханием токена
initialResponse := discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Flags: discordgo.MessageFlagsEphemeral,
Content: "👩‍🍳 Cooking your query..",
},
}
s.InteractionRespond(i.Interaction, &initialResponse)
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 := h.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,
ParentID: "1150719794853716028",
}
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"
}
}
}
}
h.defaultFollowUp(result, s, i)
}