diff --git a/client/discord/handler/handler.go b/client/discord/handler/handler.go index d6b2266..c6dc733 100644 --- a/client/discord/handler/handler.go +++ b/client/discord/handler/handler.go @@ -8,34 +8,35 @@ import ( "github.com/bwmarrin/discordgo" ) -type router struct { - Commands []CommandRoute - Components []ComponentRoute - Tags []discordgo.ForumTag +type client struct { + Commands []Command + Components []Component + ListenPostsHandler func(s *discordgo.Session, th *discordgo.ThreadCreate) + Tags []discordgo.ForumTag controller controller.WorkflowController conf *domain.DiscordConfig } // Подключение роутов к Discord боту -func InitRouter(wc controller.WorkflowController, conf *domain.DiscordConfig) *router { +func InitRouter(wc controller.WorkflowController, conf *domain.DiscordConfig) *client { - var r router - r.Commands = append(r.Commands, - // r.CreateRepoHandler(3), - // r.CreateFolderHandler(3), - r.Ping(), - // r.CreateTicketHandler(3), - // r.InitProjectFromChannel(3), - // r.GetInfo(), - r.CreateExternalTask(), - ) - r.Components = append(r.Components, - r.StartTask(), - r.CloseTask(), - ) + 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.Tags = append( r.Tags, discordgo.ForumTag{ @@ -49,21 +50,25 @@ func InitRouter(wc controller.WorkflowController, conf *domain.DiscordConfig) *r Moderated: true, EmojiName: "✅", }) + r.ListenPostsHandler = r.ListenPosts return &r } -type CommandRoute struct { +// +// Подключение роутов к Discord боту + +type Command struct { Command discordgo.ApplicationCommand Handler func(s *discordgo.Session, i *discordgo.InteractionCreate) } -type ComponentRoute struct { +type Component struct { Component discordgo.MessageComponent Handler func(s *discordgo.Session, i *discordgo.InteractionCreate) } -func (h *router) defaultFollowUp(answer string, 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{ diff --git a/client/telegram/handler/handler_test.go b/client/telegram/handler/handler_test.go deleted file mode 100644 index d871058..0000000 --- a/client/telegram/handler/handler_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package handler - -import ( - "testing" - "ticket-pimp/internal/domain" -) - -type test struct { - arg domain.Git - expected string -} - -var tests = []test{ - {domain.Git{ - Name: "text", - FullName: "", - Private: false, - Url: "", - CloneUrl: "", - HtmlUrl: "https://reddit.com/", - SshUrl: "", - }, "Repo text has been created!"}, -} - -func TestPrepareAnswer(t *testing.T) { - - for _, test := range tests { - g := newGit(&test.arg) - - if output := g.PrepareAnswer(); output != test.expected { - t.Errorf("Output %q not equal to expected %q", output, test.expected) - } - } -} diff --git a/cmd/main.go b/cmd/main.go index 0ae0db8..b8ad6ac 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -55,7 +55,7 @@ func run(conf domain.Config) { go func() { opts := discord.DiscordOptions{ Controller: controller, - AppConfig: &conf, + Config: &conf, } if err := discord.Run(conf, opts); err != nil { log.Fatalf("discord bot cannot be runned: %v", err) diff --git a/internal/controller/control_git.go b/internal/controller/control_git.go index c9e1f0d..077a509 100644 --- a/internal/controller/control_git.go +++ b/internal/controller/control_git.go @@ -92,8 +92,13 @@ func (wc *WorkflowController) createGitForExistingProject(ctx context.Context, r } func (wc *WorkflowController) CreateGit(ctx context.Context, req GitRequest) *ProjectResponse { + if req.ChannelID == "" { + return &ProjectResponse{ + Project: nil, + Message: errors.New("empty channel string"), + } + } - // [ ] Валидация на пустой канал? p, err := wc.GetProjectByChannelID(ctx, req.ChannelID) if err != nil { return &ProjectResponse{ @@ -116,7 +121,6 @@ func (wc *WorkflowController) CreateGit(ctx context.Context, req GitRequest) *Pr Message: errors.New("build git already exists"), } } else { - // [x] return wc.createGitForExistingProject(ctx, req, p) } case p != nil && !req.IsBuildGit: @@ -126,7 +130,6 @@ func (wc *WorkflowController) CreateGit(ctx context.Context, req GitRequest) *Pr Message: errors.New("project git already exists"), } } else { - // [x] return wc.createGitForExistingProject(ctx, req, p) } default: diff --git a/internal/controller/control_project.go b/internal/controller/control_project.go index bc95ff6..d04cbb3 100644 --- a/internal/controller/control_project.go +++ b/internal/controller/control_project.go @@ -86,10 +86,10 @@ func (wc *WorkflowController) GetProjectByChannelID(ctx context.Context, id stri return &proj, nil } +// Saves current channel as project's channel; func (wc *WorkflowController) InitProjectInChannel(ctx context.Context, channelID string, key string) (*domain.Project, error) { dbTicket, err := wc.q.GetTicketByChannelID(ctx, pgtype.Text{String: channelID, Valid: true}) if err == pgx.ErrNoRows { - // [ ] Логика инициализации проекта dbTicket, err = wc.q.CreateTicket( ctx, db.CreateTicketParams{ diff --git a/internal/controller/control_task.go b/internal/controller/control_task.go index 032537d..b992ca7 100644 --- a/internal/controller/control_task.go +++ b/internal/controller/control_task.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "os" - "strconv" "ticket-pimp/internal/domain" "ticket-pimp/internal/storage/db" "time" @@ -13,9 +12,14 @@ import ( "github.com/jackc/pgx/v5/pgtype" ) -func (wc *WorkflowController) InitTask(t *domain.Task) (*domain.Task, error) { - - // Записываем в базу созданную задачу ------------------------------------------ +// WriteTaskToDB +/* +Makes an SQL query to create new tasks row from domain.Task entity + - Creator field: telegram nickname or Discord's Mention(); + - Creator link (tg ID) in telegram case; + - Description from telegram/discord message bodies; +*/ +func (wc *WorkflowController) WriteTaskToDB(t *domain.Task) (*domain.Task, error) { dbtask, err := wc.q.InsertTask(context.TODO(), db.InsertTaskParams{ Creator: pgtype.Text{String: t.Creator, Valid: true}, CreatorLink: pgtype.Text{ @@ -33,6 +37,29 @@ func (wc *WorkflowController) InitTask(t *domain.Task) (*domain.Task, error) { // ------------------------------------------------------------------------------------ task := newConvertable(&dbtask).ExtractDomain() + return task, nil +} + +// InitTask +/* +Runs the following: + - Use WriteTaskToDB method to make a new task row in the db; + - init new discord bot instance; + - + +Possible errors: + - db record couldn't be created; + - bot couldn't be inited; + - bot session couldn't be started; + - thread couldn't be started; + - first task message couldn't be edited; +*/ +func (wc *WorkflowController) InitTask(t *domain.Task) (*domain.Task, error) { + + task, err := wc.WriteTaskToDB(t) + if err != nil { + return nil, fmt.Errorf("unable to create task at the db: %v", err) + } // Инициализируем новый клиент дискорда // [ ] Нездоровое получение параметров клиента из os.. @@ -44,16 +71,14 @@ func (wc *WorkflowController) InitTask(t *domain.Task) (*domain.Task, error) { s, err := discordgo.New("Bot " + token) if err != nil { return task, fmt.Errorf("unable to create discord session: %v", err) - // [ ] Что делать, если не получилось создать задачу? } if err := s.Open(); err != nil { return task, fmt.Errorf("cannot open the session: %v", err) - // [ ] Что делать, если не получилось создать задачу? } msg := discordgo.MessageSend{ - Content: task.NotStartedMessage(), + Content: task.DiscordMessage(domain.NewTaskState()), Components: []discordgo.MessageComponent{ discordgo.ActionsRow{ Components: []discordgo.MessageComponent{ @@ -77,7 +102,7 @@ func (wc *WorkflowController) InitTask(t *domain.Task) (*domain.Task, error) { th, err := s.ForumThreadStartComplex( forumChannelID, &discordgo.ThreadStart{ - Name: "Task ID:" + strconv.Itoa(int(task.ID)), + Name: fmt.Sprintf("Task ID: %d, by %s", task.ID, task.Creator), }, &msg, ) @@ -85,10 +110,7 @@ func (wc *WorkflowController) InitTask(t *domain.Task) (*domain.Task, error) { return task, fmt.Errorf("unable to update channel: %v", err) } - err = wc.q.UpdateTaskWithMessageID(context.TODO(), db.UpdateTaskWithMessageIDParams{ - Messageid: pgtype.Text{String: th.ID, Valid: true}, - ID: dbtask.ID, - }) + err = wc.UpdateTasksMessageID(context.TODO(), th.ID, task.ID) if err != nil { return task, fmt.Errorf("unable to set discord message to task: %v", err) } @@ -96,6 +118,14 @@ func (wc *WorkflowController) InitTask(t *domain.Task) (*domain.Task, error) { return task, nil } +func (wc *WorkflowController) UpdateTasksMessageID(ctx context.Context, msgID string, taskID int32) error { + err := wc.q.UpdateTaskWithMessageID(context.TODO(), db.UpdateTaskWithMessageIDParams{ + Messageid: pgtype.Text{String: msgID, Valid: true}, + ID: taskID, + }) + return err +} + func (wc *WorkflowController) UpdateTask(id string, opt int, user string) (*TaskConvertable, error) { var ( err error diff --git a/internal/controller/control_workflow.go b/internal/controller/control_workflow.go index c24fdbb..d1fd033 100644 --- a/internal/controller/control_workflow.go +++ b/internal/controller/control_workflow.go @@ -12,6 +12,13 @@ import ( "github.com/jackc/pgx/v5/pgtype" ) +// FullProjectInit +/* + Deprecated method to create a project with all related data: + - git; + - git for the project's build; + - cloud folder; +*/ func (wc *WorkflowController) FullProjectInit(name, key, id string) (string, error) { appKey := fmt.Sprintf("%s-%s", key, id) diff --git a/internal/domain/config.go b/internal/domain/config.go index fb02231..b2f891a 100644 --- a/internal/domain/config.go +++ b/internal/domain/config.go @@ -50,6 +50,7 @@ type TelegramConfig struct { type DiscordConfig struct { Token string IsProjectChannel string + IsTaskForum string } type ApplicationConfig struct { @@ -95,6 +96,7 @@ func InitConfig(envFilePath string) Config { Discord: DiscordConfig{ Token: os.Getenv("DISCORD_TOKEN"), IsProjectChannel: os.Getenv("PROJECTS_CHANNEL_GROUP"), + IsTaskForum: os.Getenv("TASKS_CHANNEL"), }, } } diff --git a/internal/domain/models.go b/internal/domain/models.go index 5cc59e1..48ec870 100644 --- a/internal/domain/models.go +++ b/internal/domain/models.go @@ -69,38 +69,52 @@ type Task struct { URL string } -func (t *Task) NotStartedMessage() string { +type TaskState int - return fmt.Sprintf( - "## TaskID: %d\nCreated by: %s\n>>> %s\n", - t.ID, - t.Creator, - t.Description, - ) +const ( + new TaskState = iota + inprogress + done +) + +func NewTaskState() TaskState { + return TaskState(0) } -func (t *Task) StartedMessage() string { - - return fmt.Sprintf( - "## TaskID: %d\nCreated by: %s\n Assignee: %s\n🚀 Started at: %s\n>>> %s\n", - t.ID, - t.Creator, - t.Assignee, - t.UpdatedAt, - t.Description, - ) +func InrpogressTaskState() TaskState { + return TaskState(1) } -func (t *Task) ClosedMessage() string { +func DoneTaskState() TaskState { + return TaskState(2) +} - return fmt.Sprintf( - "## TaskID: %d\nCreated by: %s\n Assignee: %s\n✅ Closed at: %s\n>>> %s\n", - t.ID, - t.Creator, - t.Assignee, - t.DeletedAt, - t.Description, - ) +// Creates a string for discordgo.DiscordMessage.Content +// State: New task; +func (t *Task) DiscordMessage(ts TaskState) string { + switch ts { + case new: + return fmt.Sprintf( + "Created at: %s \n>>> %s\n", + t.CreatedAt, + t.Description, + ) + case inprogress: + return fmt.Sprintf( + "**TaskID: %d** Started by: %s\n🚀 Started at: %s\n", + t.ID, + t.Assignee, + t.UpdatedAt, + ) + case done: + return fmt.Sprintf( + "**TaskID: %d** Closed by: %s\n✅ Closed at: %s\n", + t.ID, + t.Assignee, + t.DeletedAt, + ) + } + return "task state not provided" } func NewTask(summ, desc, c, cLink string) *Task {