- router implementation
This commit is contained in:
parent
19f6064066
commit
2be4b1dfd4
|
|
@ -6,6 +6,7 @@ import (
|
|||
"os"
|
||||
"os/signal"
|
||||
"ticket-pimp/client/discord/discord_handler"
|
||||
"ticket-pimp/client/discord/discord_router"
|
||||
|
||||
"ticket-pimp/internal/controller"
|
||||
"ticket-pimp/internal/domain"
|
||||
|
|
@ -19,7 +20,7 @@ var (
|
|||
repoType string = "repo_type"
|
||||
projectRepo string = "project_repo"
|
||||
buildRepo string = "build_repo"
|
||||
nameOption string = "repo_name"
|
||||
nameOption string = "name"
|
||||
tagsPreset = [3]discordgo.ForumTag{
|
||||
{
|
||||
Name: "В работе",
|
||||
|
|
@ -135,79 +136,6 @@ type DiscordOptions struct {
|
|||
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) {
|
||||
|
||||
log.Println("Updating forum chan...")
|
||||
|
|
@ -293,14 +221,33 @@ func Run(conf *domain.Config, opts DiscordOptions) error {
|
|||
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
|
||||
s.AddHandler(h.ListenPosts)
|
||||
|
||||
// Add interactions handlers
|
||||
s.AddHandler(func(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
|
||||
route(s, i, *h)
|
||||
})
|
||||
s.AddHandler(r.Serve)
|
||||
|
||||
// session opening
|
||||
if err := s.Open(); err != nil {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"ticket-pimp/adapters"
|
||||
"ticket-pimp/client/discord/discord_router"
|
||||
"ticket-pimp/internal/controller"
|
||||
"ticket-pimp/internal/domain"
|
||||
"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) {
|
||||
h.tags = append(h.tags, tags...)
|
||||
}
|
||||
|
|
@ -45,9 +109,10 @@ func (h *Handler) Ping(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
|||
i.ChannelID,
|
||||
)
|
||||
|
||||
_, err := s.FollowupMessageCreate(i.Interaction, false, &discordgo.WebhookParams{
|
||||
Content: content,
|
||||
_, err := s.InteractionResponseEdit(i.Interaction, &discordgo.WebhookEdit{
|
||||
Content: &content,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
|
@ -65,8 +130,10 @@ func (h *Handler) ListenPosts(s *discordgo.Session, th *discordgo.ThreadCreate)
|
|||
return
|
||||
}
|
||||
|
||||
// Get all messages from the channel:
|
||||
msgs, _ := s.ChannelMessages(th.ID, 1, "", "", "")
|
||||
|
||||
// Take the first one:
|
||||
msg, _ := s.ChannelMessage(th.ID, msgs[0].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 как для задачи
|
||||
appliedTags := []string{
|
||||
h.conf.Tags[domain.NewTaskState()],
|
||||
// h.tags[2].ID,
|
||||
}
|
||||
|
||||
_, 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 {
|
||||
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
|
||||
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; ---------------------------------------------------------------------------------
|
||||
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)
|
||||
}
|
||||
|
||||
// [ ] Устанавливаем тэги статуса на тред ---------------------------------------------------------------------
|
||||
err = h.setFlag(s, i, &tag)
|
||||
if err != nil {
|
||||
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;
|
||||
*/
|
||||
func (h *Handler) CreateGit(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
|
||||
const (
|
||||
repoType = "repo_type"
|
||||
projectRepo = "project_repo"
|
||||
buildRepo = "build_repo"
|
||||
nameOption = "repo_name"
|
||||
typeOfRepo = "repo_type"
|
||||
projectRepoType = "project_repo"
|
||||
buildRepoType = "build_repo"
|
||||
nameOption = "repo_name"
|
||||
)
|
||||
|
||||
// Моментальный ответ для избежания столкновения с протуханием токена
|
||||
initialResponse := discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
|
|
@ -322,18 +402,22 @@ func (h *Handler) CreateGit(s *discordgo.Session, i *discordgo.InteractionCreate
|
|||
|
||||
// Creating request:
|
||||
var req controller.GitRequest
|
||||
|
||||
name, insertedValueNotNil := optionMap[nameOption]
|
||||
isBuild := optionMap[repoType]
|
||||
|
||||
isBuild := optionMap[typeOfRepo]
|
||||
|
||||
switch isBuild.StringValue() {
|
||||
case buildRepo:
|
||||
case buildRepoType:
|
||||
req.IsBuildGit = true
|
||||
case projectRepo:
|
||||
case projectRepoType:
|
||||
req.IsBuildGit = false
|
||||
}
|
||||
dchan, err := s.Channel(i.ChannelID)
|
||||
|
||||
dchan, err := s.Channel(i.ChannelID)
|
||||
if err != nil {
|
||||
log.Printf("error while identifying channel: %v", err)
|
||||
h.defaultFollowUp("error while identifying channel: %v", s, i)
|
||||
return
|
||||
} else {
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// 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