- router implementation
This commit is contained in:
parent
19f6064066
commit
2be4b1dfd4
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"ticket-pimp/client/discord/discord_handler"
|
"ticket-pimp/client/discord/discord_handler"
|
||||||
|
"ticket-pimp/client/discord/discord_router"
|
||||||
|
|
||||||
"ticket-pimp/internal/controller"
|
"ticket-pimp/internal/controller"
|
||||||
"ticket-pimp/internal/domain"
|
"ticket-pimp/internal/domain"
|
||||||
|
|
@ -19,7 +20,7 @@ var (
|
||||||
repoType string = "repo_type"
|
repoType string = "repo_type"
|
||||||
projectRepo string = "project_repo"
|
projectRepo string = "project_repo"
|
||||||
buildRepo string = "build_repo"
|
buildRepo string = "build_repo"
|
||||||
nameOption string = "repo_name"
|
nameOption string = "name"
|
||||||
tagsPreset = [3]discordgo.ForumTag{
|
tagsPreset = [3]discordgo.ForumTag{
|
||||||
{
|
{
|
||||||
Name: "В работе",
|
Name: "В работе",
|
||||||
|
|
@ -135,79 +136,6 @@ type DiscordOptions struct {
|
||||||
Controller *controller.WorkflowController
|
Controller *controller.WorkflowController
|
||||||
}
|
}
|
||||||
|
|
||||||
// Моментальный ответ для избежания столкновения с протуханием токена
|
|
||||||
func initialResponse(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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Определяем канал и реджектим запрос, если пишут в лс:
|
|
||||||
func isRejected(s *discordgo.Session, i *discordgo.InteractionCreate) bool {
|
|
||||||
|
|
||||||
dchan, err := s.Channel(i.ChannelID)
|
|
||||||
if err != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
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 true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func route(s *discordgo.Session, i *discordgo.InteractionCreate, h discord_handler.Handler) {
|
|
||||||
// initialResponse(s, i)
|
|
||||||
|
|
||||||
if isRejected(s, i) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Определяем тип взаимодействия и хэндлим правильной функцией:
|
|
||||||
switch i.Type {
|
|
||||||
case discordgo.InteractionApplicationCommand:
|
|
||||||
|
|
||||||
switch i.ApplicationCommandData().Name {
|
|
||||||
case "ping":
|
|
||||||
h.Ping(s, i)
|
|
||||||
case "project":
|
|
||||||
h.CreateProject(s, i)
|
|
||||||
case "info":
|
|
||||||
h.ProjectInfo(s, i)
|
|
||||||
case "repo":
|
|
||||||
h.CreateGit(s, i)
|
|
||||||
case "folder":
|
|
||||||
h.CreateFolder(s, i)
|
|
||||||
case "init_project":
|
|
||||||
h.InitChannelAsProject(s, i)
|
|
||||||
case "coda_ticket":
|
|
||||||
h.CreateCoda(s, i)
|
|
||||||
}
|
|
||||||
|
|
||||||
case discordgo.InteractionMessageComponent:
|
|
||||||
|
|
||||||
switch i.MessageComponentData().CustomID {
|
|
||||||
case "task_start":
|
|
||||||
h.HandleTaskButtons(s, i)
|
|
||||||
case "task_close":
|
|
||||||
h.HandleTaskButtons(s, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateForum(conf *domain.Config, s *discordgo.Session) ([]discordgo.ForumTag, error) {
|
func updateForum(conf *domain.Config, s *discordgo.Session) ([]discordgo.ForumTag, error) {
|
||||||
|
|
||||||
log.Println("Updating forum chan...")
|
log.Println("Updating forum chan...")
|
||||||
|
|
@ -293,14 +221,33 @@ func Run(conf *domain.Config, opts DiscordOptions) error {
|
||||||
external.NewDummyClient(conf.Telegram),
|
external.NewDummyClient(conf.Telegram),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
r := discord_router.NewApp(s)
|
||||||
|
|
||||||
|
var commonMw = []discord_router.Middleware{
|
||||||
|
h.WithInitialResponse,
|
||||||
|
h.RejectPM,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle commands
|
||||||
|
r.
|
||||||
|
Route("ping", r.Wrapped(h.Ping, commonMw...)).
|
||||||
|
Route("project", r.Wrapped(h.CreateProject, commonMw...)).
|
||||||
|
Route("info", r.Wrapped(h.ProjectInfo, commonMw...)).
|
||||||
|
Route("repo", r.Wrapped(h.CreateGit, commonMw...)).
|
||||||
|
Route("folder", r.Wrapped(h.CreateFolder, commonMw...)).
|
||||||
|
Route("init_project", r.Wrapped(h.InitChannelAsProject, commonMw...)).
|
||||||
|
Route("coda_ticket", r.Wrapped(h.CreateCoda, commonMw...))
|
||||||
|
|
||||||
|
// Handle components
|
||||||
|
r.
|
||||||
|
Route("task_start", h.HandleTaskButtons).
|
||||||
|
Route("task_close", h.HandleTaskButtons)
|
||||||
|
|
||||||
// Add posts listener
|
// Add posts listener
|
||||||
s.AddHandler(h.ListenPosts)
|
s.AddHandler(h.ListenPosts)
|
||||||
|
|
||||||
// Add interactions handlers
|
// Add interactions handlers
|
||||||
s.AddHandler(func(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
s.AddHandler(r.Serve)
|
||||||
|
|
||||||
route(s, i, *h)
|
|
||||||
})
|
|
||||||
|
|
||||||
// session opening
|
// session opening
|
||||||
if err := s.Open(); err != nil {
|
if err := s.Open(); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"ticket-pimp/adapters"
|
"ticket-pimp/adapters"
|
||||||
|
"ticket-pimp/client/discord/discord_router"
|
||||||
"ticket-pimp/internal/controller"
|
"ticket-pimp/internal/controller"
|
||||||
"ticket-pimp/internal/domain"
|
"ticket-pimp/internal/domain"
|
||||||
"ticket-pimp/internal/helpers"
|
"ticket-pimp/internal/helpers"
|
||||||
|
|
@ -31,6 +32,69 @@ func New(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handler) RejectPM(f discord_router.HandlerFunc) discord_router.HandlerFunc {
|
||||||
|
return func(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||||
|
|
||||||
|
dchan, err := s.Channel(i.ChannelID)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if dchan.Type != discordgo.ChannelTypeDM {
|
||||||
|
f(s, i)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
respondWithReject(s, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func respondWithReject(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||||
|
|
||||||
|
var content = "Yo, fella! I'm not working in private!"
|
||||||
|
|
||||||
|
err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
Data: &discordgo.InteractionResponseData{
|
||||||
|
Content: content,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = s.InteractionResponseEdit(i.Interaction, &discordgo.WebhookEdit{
|
||||||
|
Content: &content,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("unable to edit answer with unknown error %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Моментальный ответ для избежания столкновения с протуханием токена
|
||||||
|
func (h *Handler) WithInitialResponse(f discord_router.HandlerFunc) discord_router.HandlerFunc {
|
||||||
|
return func(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||||
|
|
||||||
|
initialResponse := discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
Data: &discordgo.InteractionResponseData{
|
||||||
|
Flags: discordgo.MessageFlagsEphemeral,
|
||||||
|
Content: "👩🍳 Cooking your query..",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.InteractionRespond(i.Interaction, &initialResponse)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f(s, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Handler) SetAvailableTags(tags []discordgo.ForumTag) {
|
func (h *Handler) SetAvailableTags(tags []discordgo.ForumTag) {
|
||||||
h.tags = append(h.tags, tags...)
|
h.tags = append(h.tags, tags...)
|
||||||
}
|
}
|
||||||
|
|
@ -45,9 +109,10 @@ func (h *Handler) Ping(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||||
i.ChannelID,
|
i.ChannelID,
|
||||||
)
|
)
|
||||||
|
|
||||||
_, err := s.FollowupMessageCreate(i.Interaction, false, &discordgo.WebhookParams{
|
_, err := s.InteractionResponseEdit(i.Interaction, &discordgo.WebhookEdit{
|
||||||
Content: content,
|
Content: &content,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
}
|
}
|
||||||
|
|
@ -65,8 +130,10 @@ func (h *Handler) ListenPosts(s *discordgo.Session, th *discordgo.ThreadCreate)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get all messages from the channel:
|
||||||
msgs, _ := s.ChannelMessages(th.ID, 1, "", "", "")
|
msgs, _ := s.ChannelMessages(th.ID, 1, "", "", "")
|
||||||
|
|
||||||
|
// Take the first one:
|
||||||
msg, _ := s.ChannelMessage(th.ID, msgs[0].ID)
|
msg, _ := s.ChannelMessage(th.ID, msgs[0].ID)
|
||||||
|
|
||||||
if msg.Author.ID == s.State.User.ID {
|
if msg.Author.ID == s.State.User.ID {
|
||||||
|
|
@ -88,8 +155,14 @@ func (h *Handler) ListenPosts(s *discordgo.Session, th *discordgo.ThreadCreate)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Отредактировать Thread name как для задачи
|
// Отредактировать Thread name как для задачи
|
||||||
|
appliedTags := []string{
|
||||||
|
h.conf.Tags[domain.NewTaskState()],
|
||||||
|
// h.tags[2].ID,
|
||||||
|
}
|
||||||
|
|
||||||
_, err = s.ChannelEditComplex(th.ID, &discordgo.ChannelEdit{
|
_, err = s.ChannelEditComplex(th.ID, &discordgo.ChannelEdit{
|
||||||
Name: fmt.Sprintf("Task ID: %d, by %s", t.ID, t.Creator),
|
Name: fmt.Sprintf("Task ID: %d, by %s", t.ID, t.Creator),
|
||||||
|
AppliedTags: &appliedTags,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("th edition is not complete: %v", err)
|
log.Printf("th edition is not complete: %v", err)
|
||||||
|
|
@ -132,6 +205,12 @@ func (h *Handler) ListenPosts(s *discordgo.Session, th *discordgo.ThreadCreate)
|
||||||
// .. handler function to work with the Action Buttons over a task
|
// .. handler function to work with the Action Buttons over a task
|
||||||
func (h *Handler) HandleTaskButtons(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
func (h *Handler) HandleTaskButtons(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||||
|
|
||||||
|
err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
Data: &discordgo.InteractionResponseData{},
|
||||||
|
})
|
||||||
|
_ = err
|
||||||
|
|
||||||
// Get assignee value; ---------------------------------------------------------------------------------
|
// Get assignee value; ---------------------------------------------------------------------------------
|
||||||
user := i.Member.User.Mention()
|
user := i.Member.User.Mention()
|
||||||
|
|
||||||
|
|
@ -209,7 +288,6 @@ func (h *Handler) HandleTaskButtons(s *discordgo.Session, i *discordgo.Interacti
|
||||||
log.Printf("th start message edition is not complete: %v", err)
|
log.Printf("th start message edition is not complete: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// [ ] Устанавливаем тэги статуса на тред ---------------------------------------------------------------------
|
|
||||||
err = h.setFlag(s, i, &tag)
|
err = h.setFlag(s, i, &tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error while `start` tag setting: %v", err)
|
log.Printf("error while `start` tag setting: %v", err)
|
||||||
|
|
@ -291,12 +369,14 @@ func (h *Handler) CreateFolder(s *discordgo.Session, i *discordgo.InteractionCre
|
||||||
- writed git link to db;
|
- writed git link to db;
|
||||||
*/
|
*/
|
||||||
func (h *Handler) CreateGit(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
func (h *Handler) CreateGit(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||||
|
|
||||||
const (
|
const (
|
||||||
repoType = "repo_type"
|
typeOfRepo = "repo_type"
|
||||||
projectRepo = "project_repo"
|
projectRepoType = "project_repo"
|
||||||
buildRepo = "build_repo"
|
buildRepoType = "build_repo"
|
||||||
nameOption = "repo_name"
|
nameOption = "repo_name"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Моментальный ответ для избежания столкновения с протуханием токена
|
// Моментальный ответ для избежания столкновения с протуханием токена
|
||||||
initialResponse := discordgo.InteractionResponse{
|
initialResponse := discordgo.InteractionResponse{
|
||||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
|
@ -322,18 +402,22 @@ func (h *Handler) CreateGit(s *discordgo.Session, i *discordgo.InteractionCreate
|
||||||
|
|
||||||
// Creating request:
|
// Creating request:
|
||||||
var req controller.GitRequest
|
var req controller.GitRequest
|
||||||
|
|
||||||
name, insertedValueNotNil := optionMap[nameOption]
|
name, insertedValueNotNil := optionMap[nameOption]
|
||||||
isBuild := optionMap[repoType]
|
|
||||||
|
isBuild := optionMap[typeOfRepo]
|
||||||
|
|
||||||
switch isBuild.StringValue() {
|
switch isBuild.StringValue() {
|
||||||
case buildRepo:
|
case buildRepoType:
|
||||||
req.IsBuildGit = true
|
req.IsBuildGit = true
|
||||||
case projectRepo:
|
case projectRepoType:
|
||||||
req.IsBuildGit = false
|
req.IsBuildGit = false
|
||||||
}
|
}
|
||||||
dchan, err := s.Channel(i.ChannelID)
|
|
||||||
|
|
||||||
|
dchan, err := s.Channel(i.ChannelID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error while identifying channel: %v", err)
|
h.defaultFollowUp("error while identifying channel: %v", s, i)
|
||||||
|
return
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if dchan.ParentID == h.conf.IsProjectChannel {
|
if dchan.ParentID == h.conf.IsProjectChannel {
|
||||||
|
|
@ -530,3 +614,35 @@ func (h *Handler) CreateProject(s *discordgo.Session, i *discordgo.InteractionCr
|
||||||
|
|
||||||
h.defaultFollowUp(result, s, i)
|
h.defaultFollowUp(result, s, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateCoda
|
||||||
|
/*
|
||||||
|
- sends request to Coda.io;
|
||||||
|
*/
|
||||||
|
func (h *Handler) CreateCoda(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||||
|
|
||||||
|
// Get channel from the request
|
||||||
|
dchan, err := s.Channel(i.ChannelID)
|
||||||
|
if err != nil {
|
||||||
|
h.defaultFollowUp("unable to get channel from the message", s, i)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if dchan.ParentID != h.conf.IsProjectChannel {
|
||||||
|
h.defaultFollowUp("This channel is not at the project's group", s, i)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//[ ] достать проект из базы и послать в коду
|
||||||
|
result, err := h.controller.CreateCoda(i.GuildID, dchan.ID)
|
||||||
|
if err != nil {
|
||||||
|
h.defaultFollowUp(fmt.Sprintf("unable to create coda: %v", err), s, i)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
result += fmt.Sprintf("\nexecuted w/ error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
h.defaultFollowUp(result, s, i)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
package discord_router
|
||||||
|
|
||||||
|
import "github.com/bwmarrin/discordgo"
|
||||||
|
|
||||||
|
type HandlerFunc func(*discordgo.Session, *discordgo.InteractionCreate)
|
||||||
|
|
||||||
|
type RouteEntry struct {
|
||||||
|
CommandName string
|
||||||
|
Handler HandlerFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re *RouteEntry) Match(i *discordgo.InteractionCreate) bool {
|
||||||
|
switch i.Type {
|
||||||
|
case discordgo.InteractionApplicationCommand:
|
||||||
|
if i.ApplicationCommandData().Name != re.CommandName {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case discordgo.InteractionMessageComponent:
|
||||||
|
if i.MessageComponentData().CustomID != re.CommandName {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type Router struct {
|
||||||
|
session *discordgo.Session
|
||||||
|
routes []RouteEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewApp(s *discordgo.Session) *Router {
|
||||||
|
return &Router{
|
||||||
|
session: s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Router) Route(cmd string, handlerFunc HandlerFunc) *Router {
|
||||||
|
|
||||||
|
r.routes = append(r.routes, RouteEntry{
|
||||||
|
CommandName: cmd,
|
||||||
|
Handler: handlerFunc,
|
||||||
|
})
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Router) Serve(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||||
|
for _, e := range r.routes {
|
||||||
|
ok := e.Match(i)
|
||||||
|
if ok {
|
||||||
|
e.Handler(s, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//[ ] Is there something like 404?!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Middleware func(HandlerFunc) HandlerFunc
|
||||||
|
|
||||||
|
func (r *Router) Wrapped(f HandlerFunc, m ...Middleware) HandlerFunc {
|
||||||
|
|
||||||
|
if len(m) < 1 {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapped := f
|
||||||
|
|
||||||
|
// loop in reverse to preserve middleware order
|
||||||
|
for i := len(m) - 1; i >= 0; i-- {
|
||||||
|
wrapped = m[i](wrapped)
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrapped
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue