ticket-pimp/client/discord/discord.go

285 lines
6.5 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 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
}
func commandRegistration(s *discordgo.Session, commands []discordgo.ApplicationCommand) []*discordgo.ApplicationCommand {
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
}
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)
var commonMw = []discord_router.Middleware{
h.WithInitialResponse,
h.RejectPM,
}
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
}