From 7cf40088348cecaba9f17517915a5c8257534acf Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Tue, 13 May 2025 19:23:06 +0900 Subject: [PATCH 1/3] chore: edit customIds --- commands/deleteLearnedData.go | 6 +-- commands/learnedDataList.go | 6 +-- components/deleteLearnedData.go | 9 ++--- utils/customIds.go | 67 ++++++++++++++++++++++++++++++++- 4 files changed, 76 insertions(+), 12 deletions(-) diff --git a/commands/deleteLearnedData.go b/commands/deleteLearnedData.go index 13eaeb4..3ce337d 100644 --- a/commands/deleteLearnedData.go +++ b/commands/deleteLearnedData.go @@ -119,7 +119,7 @@ func deleteLearnedDataRun(c *Command, s *discordgo.Session, m any, args *[]strin options = append(options, discordgo.SelectMenuOption{ Label: fmt.Sprintf("%d번 지식", i+1), Description: data.Result, - Value: fmt.Sprintf("%s%s&No.%d", utils.DeleteLearnedData, data.Id.Hex(), i+1), + Value: utils.MakeDeleteLearnedData(data.Id.Hex(), i+1), }) description += fmt.Sprintf("%d. %s\n", i+1, data.Result) } @@ -135,7 +135,7 @@ func deleteLearnedDataRun(c *Command, s *discordgo.Session, m any, args *[]strin Components: []discordgo.MessageComponent{ discordgo.SelectMenu{ MenuType: discordgo.StringSelectMenu, - CustomID: utils.DeleteLearnedDataUserId + userId, + CustomID: utils.MakeDeleteLearnedDataUserId(userId), Options: options, Placeholder: "ㅈ지울 응답을 선택해주세요.", }, @@ -144,7 +144,7 @@ func deleteLearnedDataRun(c *Command, s *discordgo.Session, m any, args *[]strin discordgo.ActionsRow{ Components: []discordgo.MessageComponent{ discordgo.Button{ - CustomID: utils.DeleteLearnedDataCancel + userId, + CustomID: utils.MakeDeleteLearnedDataCancel(userId), Label: "취소하기", Style: discordgo.DangerButton, Disabled: false, diff --git a/commands/learnedDataList.go b/commands/learnedDataList.go index 3601713..4bd222e 100644 --- a/commands/learnedDataList.go +++ b/commands/learnedDataList.go @@ -171,9 +171,9 @@ func learnedDataListRun(s *discordgo.Session, m any, args *[]string) { cur.All(context.TODO(), &data) embed := &discordgo.MessageEmbed{ - Title: fmt.Sprintf("%s님이 알려주신 지식", globalName), - Description: utils.CodeBlock("md", fmt.Sprintf("# 총 %d개에요.\n%s", len(data), strings.Join(getDescriptions(&data), "\n"))), - Color: utils.EmbedDefault, + Title: fmt.Sprintf("%s님이 알려주신 지식", globalName), + Color: utils.EmbedDefault, + // Description: utils.CodeBlock("md", fmt.Sprintf("# 총 %d개에요.\n%s", len(data), strings.Join(getDescriptions(&data), "\n"))), Thumbnail: &discordgo.MessageEmbedThumbnail{ URL: avatarUrl, }, diff --git a/components/deleteLearnedData.go b/components/deleteLearnedData.go index 84311a4..2902e25 100644 --- a/components/deleteLearnedData.go +++ b/components/deleteLearnedData.go @@ -23,7 +23,7 @@ var DeleteLearnedDataComponent *commands.Component = &commands.Component{ return false } - userId = customId[len(utils.DeleteLearnedDataCancel):] + userId = utils.GetDeleteLearnedDataUserId(customId) if i.Member.User.ID == userId { i.Update(&discordgo.InteractionResponseData{ Embeds: []*discordgo.MessageEmbed{ @@ -41,7 +41,7 @@ var DeleteLearnedDataComponent *commands.Component = &commands.Component{ return false } - userId = customId[len(utils.DeleteLearnedDataUserId):] + userId = utils.GetDeleteLearnedDataUserId(customId) } if i.Member.User.ID != userId { @@ -66,8 +66,7 @@ var DeleteLearnedDataComponent *commands.Component = &commands.Component{ i.DeferUpdate() - id, _ := bson.ObjectIDFromHex(strings.ReplaceAll(utils.ItemIdRegexp.ReplaceAllString(i.MessageComponentData().Values[0][len(utils.DeleteLearnedData):], ""), "&", "")) - itemId := strings.ReplaceAll(utils.ItemIdRegexp.FindAllString(i.MessageComponentData().Values[0], 1)[0], "No.", "") + id, itemId := utils.GetDeleteLearnedDataId(i.MessageComponentData().Values[0]) databases.Database.Learns.DeleteOne(context.TODO(), bson.D{{Key: "_id", Value: id}}) @@ -75,7 +74,7 @@ var DeleteLearnedDataComponent *commands.Component = &commands.Component{ Embeds: &[]*discordgo.MessageEmbed{ { Title: "✅ 삭제 완료", - Description: fmt.Sprintf("%s번을 삭ㅈ제했어요.", itemId), + Description: fmt.Sprintf("%d번을 삭ㅈ제했어요.", itemId), Color: utils.EmbedSuccess, }, }, diff --git a/utils/customIds.go b/utils/customIds.go index 6544d26..0c3f2ac 100644 --- a/utils/customIds.go +++ b/utils/customIds.go @@ -1,7 +1,72 @@ package utils +import ( + "fmt" + "strconv" + "strings" + + "go.mongodb.org/mongo-driver/v2/bson" +) + const ( - DeleteLearnedData = "#muffin/deleteLearnedData$" + DeleteLearnedData = "#muffin/deleteLearnedData@" DeleteLearnedDataUserId = "#muffin/deleteLearnedData@" DeleteLearnedDataCancel = "#muffin/deleteLearnedData/cancel@" + + PaginationEmbedPrev = "#muffin-pages/prev$" + PaginationEmbedPages = "#muffin-pages/pages$" + PaginationEmbedNext = "#muffin-pages/next$" ) + +func MakeDeleteLearnedData(id string, number int) string { + return fmt.Sprintf("%s%s&No.%d", DeleteLearnedData, id, number) +} + +func MakeDeleteLearnedDataUserId(userId string) string { + return fmt.Sprintf("%s%s", DeleteLearnedDataUserId, userId) +} + +func MakeDeleteLearnedDataCancel(id string) string { + return fmt.Sprintf("%s%s", DeleteLearnedDataCancel, id) +} + +func GetDeleteLearnedDataId(customId string) (id bson.ObjectID, itemId int) { + id, _ = bson.ObjectIDFromHex(strings.ReplaceAll(ItemIdRegexp.ReplaceAllString(customId[len(DeleteLearnedData):], ""), "&", "")) + stringItemId := strings.ReplaceAll(ItemIdRegexp.FindAllString(customId, 1)[0], "No.", "") + itemId, _ = strconv.Atoi(stringItemId) + return +} + +func GetDeleteLearnedDataUserId(customId string) string { + if strings.HasPrefix(customId, DeleteLearnedDataCancel) { + return customId[len(DeleteLearnedDataCancel):] + } else { + return customId[len(DeleteLearnedDataUserId):] + } +} + +func MakePaginationEmbedPrev(id string) string { + return fmt.Sprintf("%s%s", PaginationEmbedPrev, id) +} + +func MakePaginationEmbedPages(id string, total, current int) string { + return fmt.Sprintf("%s%s/%d/%d", PaginationEmbedPages, id, total, current) +} + +func MakePaginationEmbedNext(id string) string { + return fmt.Sprintf("%s%s", PaginationEmbedNext, id) +} + +func GetPaginationEmbedId(customId string) string { + if strings.HasPrefix(customId, PaginationEmbedPrev) { + return customId[len(PaginationEmbedPrev):] + } else if strings.HasPrefix(customId, PaginationEmbedPages) { + return customId[len(PaginationEmbedPages):] + } else { + return customId[len(PaginationEmbedNext):] + } +} + +func GetPaginationEmbedUserId(id string) string { + return PaginationEmbedId.FindAllStringSubmatch(id, 1)[0][1] +} From f66b1cd4041b5fadc689d754d0854dae0411e0c7 Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Tue, 13 May 2025 19:25:02 +0900 Subject: [PATCH 2/3] feat: pagination embed I'll replace embed to ComponentsV2's container --- commands/learnedDataList.go | 19 +++-- components/paginationEmbed.go | 48 ++++++++++++ configs/version.go | 2 +- main.go | 1 + utils/paginationEmbed.go | 143 ++++++++++++++++++++++++++++++++++ utils/regexp.go | 1 + 6 files changed, 205 insertions(+), 9 deletions(-) create mode 100644 components/paginationEmbed.go create mode 100644 utils/paginationEmbed.go diff --git a/commands/learnedDataList.go b/commands/learnedDataList.go index 4bd222e..dd29270 100644 --- a/commands/learnedDataList.go +++ b/commands/learnedDataList.go @@ -179,12 +179,15 @@ func learnedDataListRun(s *discordgo.Session, m any, args *[]string) { }, } - switch m := m.(type) { - case *discordgo.MessageCreate: - s.ChannelMessageSendEmbedReply(m.ChannelID, embed, m.Reference()) - case *utils.InteractionCreate: - m.EditReply(&discordgo.WebhookEdit{ - Embeds: &[]*discordgo.MessageEmbed{embed}, - }) - } + // 실험용 데이터 + utils.StartPaginationEmbed(s, m, embed, []string{"asdf", "fdsa"}, 10) + + // switch m := m.(type) { + // case *discordgo.MessageCreate: + // s.ChannelMessageSendEmbedReply(m.ChannelID, embed, m.Reference()) + // case *utils.InteractionCreate: + // m.EditReply(&discordgo.WebhookEdit{ + // Embeds: &[]*discordgo.MessageEmbed{embed}, + // }) + // } } diff --git a/components/paginationEmbed.go b/components/paginationEmbed.go new file mode 100644 index 0000000..1442349 --- /dev/null +++ b/components/paginationEmbed.go @@ -0,0 +1,48 @@ +package components + +import ( + "strings" + + "git.wh64.net/muffin/goMuffin/commands" + "git.wh64.net/muffin/goMuffin/utils" + "github.com/bwmarrin/discordgo" +) + +var PaginationEmbedComponent *commands.Component = &commands.Component{ + Parse: func(ctx *commands.ComponentContext) bool { + i := ctx.Inter + + if i.MessageComponentData().ComponentType == discordgo.ButtonComponent { + customId := i.MessageComponentData().CustomID + + if !strings.HasPrefix(customId, utils.PaginationEmbedPrev) && !strings.HasPrefix(customId, utils.PaginationEmbedNext) { + return false + } + + id := utils.GetPaginationEmbedId(customId) + userId := utils.GetPaginationEmbedUserId(id) + + if i.Member.User.ID != userId { + return false + } + + if utils.GetPaginationEmbed(id) == nil { + return false + } + } else { + return false + } + return true + }, + Run: func(ctx *commands.ComponentContext) { + customId := ctx.Inter.MessageComponentData().CustomID + id := utils.GetPaginationEmbedId(customId) + p := utils.GetPaginationEmbed(id) + + if strings.HasPrefix(customId, utils.PaginationEmbedPrev) { + p.Prev(ctx.Inter) + } else { + p.Next(ctx.Inter) + } + }, +} diff --git a/configs/version.go b/configs/version.go index 69b38c7..e9728e7 100644 --- a/configs/version.go +++ b/configs/version.go @@ -7,7 +7,7 @@ import ( "git.wh64.net/muffin/goMuffin/utils" ) -const MUFFIN_VERSION = "5.1.0-gopher_dev.250512a" +const MUFFIN_VERSION = "5.1.0-gopher_dev.250513a-paginated_embed" var updatedString string = utils.Decimals.FindAllStringSubmatch(MUFFIN_VERSION, -1)[3][0] diff --git a/main.go b/main.go index e526d86..9ada644 100644 --- a/main.go +++ b/main.go @@ -75,6 +75,7 @@ func main() { go commands.Discommand.LoadCommand(commands.DeleteLearnedDataCommand) go commands.Discommand.LoadComponent(components.DeleteLearnedDataComponent) + go commands.Discommand.LoadComponent(components.PaginationEmbedComponent) go dg.AddHandler(handler.MessageCreate) go dg.AddHandler(handler.InteractionCreate) diff --git a/utils/paginationEmbed.go b/utils/paginationEmbed.go new file mode 100644 index 0000000..5331afb --- /dev/null +++ b/utils/paginationEmbed.go @@ -0,0 +1,143 @@ +package utils + +import ( + "fmt" + "math/rand" + + "github.com/bwmarrin/discordgo" +) + +// PaginationEmbed is embed with page +type PaginationEmbed struct { + Embed *discordgo.MessageEmbed + Data []string + Current int + Total int + id string + s *discordgo.Session + m any +} + +var PaginationEmbeds = make(map[string]*PaginationEmbed) + +func makeComponents(id string, current, total int) *[]discordgo.MessageComponent { + + return &[]discordgo.MessageComponent{ + discordgo.ActionsRow{ + Components: []discordgo.MessageComponent{ + discordgo.Button{ + Style: discordgo.PrimaryButton, + Label: "이전", + CustomID: MakePaginationEmbedPrev(id), + Disabled: false, + }, + discordgo.Button{ + Style: discordgo.SecondaryButton, + Label: fmt.Sprintf("(%d/%d)", current, total), + CustomID: MakePaginationEmbedPages(id, current, total), + Disabled: true, + }, + discordgo.Button{ + Style: discordgo.PrimaryButton, + Label: "다음", + CustomID: MakePaginationEmbedNext(id), + Disabled: false, + }, + }, + }, + } +} + +// StartPaginationEmbed starts new PaginationEmbed struct +func StartPaginationEmbed(s *discordgo.Session, m any, e *discordgo.MessageEmbed, data []string, length int) { + var userId string + + switch m := m.(type) { + case *discordgo.MessageCreate: + userId = m.Author.ID + case *InteractionCreate: + userId = m.Member.User.ID + } + + id := fmt.Sprintf("%s/%d", userId, rand.Intn(12)) + p := &PaginationEmbed{ + Embed: e, + Data: data, + Current: 1, + Total: len(data), + id: id, + s: s, + m: m, + } + + p.Embed.Description = p.Data[0] + + switch m := m.(type) { + case *discordgo.MessageCreate: + s.ChannelMessageSendComplex(m.ChannelID, &discordgo.MessageSend{ + Reference: m.Reference(), + Embeds: []*discordgo.MessageEmbed{p.Embed}, + Components: *makeComponents(id, 1, len(data)), + }) + case *InteractionCreate: + m.Reply(&discordgo.InteractionResponseData{ + Embeds: []*discordgo.MessageEmbed{p.Embed}, + Components: *makeComponents(id, 1, len(data)), + }) + } + + PaginationEmbeds[id] = p +} + +func GetPaginationEmbed(id string) *PaginationEmbed { + if p, ok := PaginationEmbeds[id]; ok { + return p + } + return nil +} + +func (p *PaginationEmbed) Prev(i *InteractionCreate) { + if p.Current == 1 { + i.Reply(&discordgo.InteractionResponseData{ + Embeds: []*discordgo.MessageEmbed{ + { + Title: "❌ 오류", + Description: "해당 페이자가 처음ㅇ이에요.", + Color: EmbedFail, + }, + }, + }) + return + } + + p.Current -= 1 + + p.Embed.Description = p.Data[p.Current-1] + i.Update(&discordgo.InteractionResponseData{ + Embeds: []*discordgo.MessageEmbed{p.Embed}, + Components: *makeComponents(p.id, p.Current, p.Total), + }) +} + +func (p *PaginationEmbed) Next(i *InteractionCreate) { + if p.Current >= p.Total { + i.Reply(&discordgo.InteractionResponseData{ + Embeds: []*discordgo.MessageEmbed{ + { + Title: "❌ 오류", + Description: "해당 페이자가 마지막ㅇ이에요.", + Color: EmbedFail, + }, + }, + }) + return + } + + p.Current += 1 + + p.Embed.Description = p.Data[p.Current-1] + i.Update(&discordgo.InteractionResponseData{ + Embeds: []*discordgo.MessageEmbed{p.Embed}, + Components: *makeComponents(p.id, p.Current, p.Total), + }) +} diff --git a/utils/regexp.go b/utils/regexp.go index f180d6e..0e920b8 100644 --- a/utils/regexp.go +++ b/utils/regexp.go @@ -9,4 +9,5 @@ var ( EmojiRegexp = regexp.MustCompile(``) LearnQueryCommand = regexp.MustCompile(`^단어:`) LearnQueryResult = regexp.MustCompile(`^대답:`) + PaginationEmbedId = regexp.MustCompile(`^(\d+)/(\d+)$`) ) From e8f4bc9c21cee350e006196d6be722e10c1a8674 Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Tue, 13 May 2025 22:05:43 +0900 Subject: [PATCH 3/3] feat: add pagination embed in learnedDataList --- commands/learnedDataList.go | 33 ++++++++++++++++++++------------- configs/version.go | 2 +- utils/paginationEmbed.go | 31 +++++++++++++++++++++++-------- 3 files changed, 44 insertions(+), 22 deletions(-) diff --git a/commands/learnedDataList.go b/commands/learnedDataList.go index dd29270..7099822 100644 --- a/commands/learnedDataList.go +++ b/commands/learnedDataList.go @@ -51,6 +51,9 @@ var LearnedDataListCommand *Command = &Command{ func getDescriptions(data *[]databases.Learn) (descriptions []string) { MAX_LENGTH := 100 + MAX_ITEM_LENGTH := 25 + + tempDesc := []string{} for _, data := range *data { command := data.Command @@ -64,7 +67,22 @@ func getDescriptions(data *[]databases.Learn) (descriptions []string) { result = string(runeResult[:MAX_LENGTH]) + "..." } - descriptions = append(descriptions, fmt.Sprintf("- %s: %s", command, result)) + tempDesc = append(tempDesc, fmt.Sprintf("- %s: %s\n", command, result)) + } + + var builder strings.Builder + + for i, s := range tempDesc { + builder.WriteString(s) + + if (i+1)%MAX_ITEM_LENGTH == 0 { + descriptions = append(descriptions, builder.String()) + builder.Reset() + } + } + + if builder.Len() > 0 { + descriptions = append(descriptions, builder.String()) } return } @@ -173,21 +191,10 @@ func learnedDataListRun(s *discordgo.Session, m any, args *[]string) { embed := &discordgo.MessageEmbed{ Title: fmt.Sprintf("%s님이 알려주신 지식", globalName), Color: utils.EmbedDefault, - // Description: utils.CodeBlock("md", fmt.Sprintf("# 총 %d개에요.\n%s", len(data), strings.Join(getDescriptions(&data), "\n"))), Thumbnail: &discordgo.MessageEmbedThumbnail{ URL: avatarUrl, }, } - // 실험용 데이터 - utils.StartPaginationEmbed(s, m, embed, []string{"asdf", "fdsa"}, 10) - - // switch m := m.(type) { - // case *discordgo.MessageCreate: - // s.ChannelMessageSendEmbedReply(m.ChannelID, embed, m.Reference()) - // case *utils.InteractionCreate: - // m.EditReply(&discordgo.WebhookEdit{ - // Embeds: &[]*discordgo.MessageEmbed{embed}, - // }) - // } + utils.StartPaginationEmbed(s, m, embed, getDescriptions(&data), utils.CodeBlock("md", fmt.Sprintf("# 총 %d개에요.\n", len(data))+"%s")) } diff --git a/configs/version.go b/configs/version.go index e9728e7..93d3fb7 100644 --- a/configs/version.go +++ b/configs/version.go @@ -7,7 +7,7 @@ import ( "git.wh64.net/muffin/goMuffin/utils" ) -const MUFFIN_VERSION = "5.1.0-gopher_dev.250513a-paginated_embed" +const MUFFIN_VERSION = "5.1.0-gopher_dev.250513b-paginated_embed" var updatedString string = utils.Decimals.FindAllStringSubmatch(MUFFIN_VERSION, -1)[3][0] diff --git a/utils/paginationEmbed.go b/utils/paginationEmbed.go index 5331afb..cdab278 100644 --- a/utils/paginationEmbed.go +++ b/utils/paginationEmbed.go @@ -15,7 +15,7 @@ type PaginationEmbed struct { Total int id string s *discordgo.Session - m any + desc string } var PaginationEmbeds = make(map[string]*PaginationEmbed) @@ -48,8 +48,19 @@ func makeComponents(id string, current, total int) *[]discordgo.MessageComponent } } +func makeDesc(desc, item string) string { + var newDesc string + + if desc == "" { + newDesc = item + } else { + newDesc = fmt.Sprintf(desc, item) + } + return newDesc +} + // StartPaginationEmbed starts new PaginationEmbed struct -func StartPaginationEmbed(s *discordgo.Session, m any, e *discordgo.MessageEmbed, data []string, length int) { +func StartPaginationEmbed(s *discordgo.Session, m any, e *discordgo.MessageEmbed, data []string, defaultDesc string) { var userId string switch m := m.(type) { @@ -67,10 +78,10 @@ func StartPaginationEmbed(s *discordgo.Session, m any, e *discordgo.MessageEmbed Total: len(data), id: id, s: s, - m: m, + desc: defaultDesc, } - p.Embed.Description = p.Data[0] + p.Embed.Description = makeDesc(p.desc, data[0]) switch m := m.(type) { case *discordgo.MessageCreate: @@ -102,17 +113,19 @@ func (p *PaginationEmbed) Prev(i *InteractionCreate) { Embeds: []*discordgo.MessageEmbed{ { Title: "❌ 오류", - Description: "해당 페이자가 처음ㅇ이에요.", + Description: "해당 페이지가 처음ㅇ이에요.", Color: EmbedFail, }, }, + Flags: discordgo.MessageFlagsEphemeral, }) return } p.Current -= 1 - p.Embed.Description = p.Data[p.Current-1] + p.Embed.Description = makeDesc(p.desc, p.Data[p.Current-1]) + i.Update(&discordgo.InteractionResponseData{ Embeds: []*discordgo.MessageEmbed{p.Embed}, Components: *makeComponents(p.id, p.Current, p.Total), @@ -125,17 +138,19 @@ func (p *PaginationEmbed) Next(i *InteractionCreate) { Embeds: []*discordgo.MessageEmbed{ { Title: "❌ 오류", - Description: "해당 페이자가 마지막ㅇ이에요.", + Description: "해당 페이지가 마지막ㅇ이에요.", Color: EmbedFail, }, }, + Flags: discordgo.MessageFlagsEphemeral, }) return } p.Current += 1 - p.Embed.Description = p.Data[p.Current-1] + p.Embed.Description = makeDesc(p.desc, p.Data[p.Current-1]) + i.Update(&discordgo.InteractionResponseData{ Embeds: []*discordgo.MessageEmbed{p.Embed}, Components: *makeComponents(p.id, p.Current, p.Total),