From ba5b45d5ebd8b33846ea6716184af5e2dd998eda Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Mon, 5 May 2025 10:21:07 +0900 Subject: [PATCH 01/34] fix: remove unused parameter --- commands/help.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/commands/help.go b/commands/help.go index 4d1d3df..f948776 100644 --- a/commands/help.go +++ b/commands/help.go @@ -30,10 +30,10 @@ var HelpCommand *Command = &Command{ }, Category: General, MessageRun: func(ctx *MsgContext) { - helpRun(ctx.Command, ctx.Session, ctx.Msg, &ctx.Args) + helpRun(ctx.Session, ctx.Msg, &ctx.Args) }, ChatInputRun: func(ctx *ChatInputContext) { - helpRun(ctx.Command, ctx.Session, ctx.Inter, nil) + helpRun(ctx.Session, ctx.Inter, nil) }, } @@ -47,7 +47,7 @@ func getCommandsByCategory(d *DiscommandStruct, category Category) []string { return commands } -func helpRun(c *Command, s *discordgo.Session, m any, args *[]string) { +func helpRun(s *discordgo.Session, m any, args *[]string) { var commandName string embed := &discordgo.MessageEmbed{ Color: utils.EmbedDefault, From 7127a57eb091ae5042f74ced83864dac6e6308bb Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Mon, 5 May 2025 10:29:35 +0900 Subject: [PATCH 02/34] feat: add argument description in learn command --- commands/help.go | 7 +++++++ commands/learn.go | 4 ++-- main.go | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/commands/help.go b/commands/help.go index f948776..307a839 100644 --- a/commands/help.go +++ b/commands/help.go @@ -106,6 +106,13 @@ func helpRun(s *discordgo.Session, m any, args *[]string) { }, } + if command.Name == LearnCommand.Name { + embed.Fields = append(embed.Fields, &discordgo.MessageEmbedField{ + Name: "대답에 쓸 수 있는 인자", + Value: learnArguments, + }) + } + if command.Aliases != nil { embed.Fields = append(embed.Fields, &discordgo.MessageEmbedField{ Name: "별칭", diff --git a/commands/learn.go b/commands/learn.go index 483c7b6..12af361 100644 --- a/commands/learn.go +++ b/commands/learn.go @@ -13,7 +13,7 @@ import ( "github.com/bwmarrin/discordgo" ) -var arguments = utils.InlineCode("{user.name}") + "\n" + +var learnArguments = utils.InlineCode("{user.name}") + "\n" + utils.InlineCode("{user.mention}") + "\n" + utils.InlineCode("{user.globalName}") + "\n" + utils.InlineCode("{user.id}") + "\n" + @@ -91,7 +91,7 @@ func learnRun(c *Command, s *discordgo.Session, m any, args *[]string) { }, { Name: "사용 가능한 인자", - Value: arguments, + Value: learnArguments, Inline: true, }, { diff --git a/main.go b/main.go index 20a8878..7534680 100644 --- a/main.go +++ b/main.go @@ -63,7 +63,7 @@ func main() { }() for _, cmd := range commands.Discommand.Commands { - if cmd.Name == "도움말" { + if cmd.Name == commands.HelpCommand.Name { // 극한의 성능 똥망 코드 탄생! // 무려 똑같은 걸 반복해서 돌리는! for _, a := range commands.Discommand.Commands { From 8b55a8328746d400ba75da88ba742ed5d2b4e82b Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Mon, 5 May 2025 14:10:24 +0900 Subject: [PATCH 03/34] feat: add devproje/commando --- go.mod | 1 + go.sum | 2 ++ main.go | 29 +++++++++++++++++++++-------- scripts/dbMigrate.go | 4 +++- scripts/deleteAllCommands.go | 34 +++++++++++++++++++--------------- 5 files changed, 46 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index ba7f7b8..9cbb709 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( require ( filippo.io/edwards25519 v1.1.0 // indirect + github.com/devproje/commando v0.1.0-alpha.1 // indirect github.com/golang/snappy v1.0.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/klauspost/compress v1.18.0 // indirect diff --git a/go.sum b/go.sum index ac1c0fd..c976b9b 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/bwmarrin/discordgo v0.28.1 h1:gXsuo2GBO7NbR6uqmrrBDplPUx2T3nzu775q/Rd github.com/bwmarrin/discordgo v0.28.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/devproje/commando v0.1.0-alpha.1 h1:JU6CKIdt1otjUKh+asCJC0yTzwVj+4Yh8KoTdzaKAkU= +github.com/devproje/commando v0.1.0-alpha.1/go.mod h1:OhrPX3mZUGSyEX/E7d1o0vaQIYkjG/N5rk6Nqwgyc7k= github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU= github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= diff --git a/main.go b/main.go index 7534680..4f36f25 100644 --- a/main.go +++ b/main.go @@ -6,7 +6,6 @@ import ( "log" "os" "os/signal" - "strings" "syscall" "time" @@ -17,19 +16,33 @@ import ( "git.wh64.net/muffin/goMuffin/handler" "git.wh64.net/muffin/goMuffin/scripts" "github.com/bwmarrin/discordgo" + "github.com/devproje/commando" + "github.com/devproje/commando/types" ) func main() { + command := commando.NewCommando(os.Args[1:]) config := configs.Config if len(os.Args) > 1 { - switch strings.ToLower(os.Args[1]) { - case "dbmigrate": - scripts.DBMigrate() - case "deleteallcommands": - scripts.DeleteAllCommands() - default: - log.Fatalln(fmt.Errorf("[goMuffin] 명령어 인자에는 dbmigrate나 deleteallcommands만 올 수 있어요")) + command.Root("db-migrate", "봇의 데이터를 MariaDB에서 MongoDB로 옮깁니다.", scripts.DBMigrate) + command.Root("delete-all-commands", "봇의 모든 슬래시 커맨드를 삭제합니다.", scripts.DeleteAllCommands, + types.OptionData{ + Name: "id", + Desc: "봇의 디스코드 아이디", + Type: types.STRING, + }, + types.OptionData{ + Name: "isYes", + Short: []string{"y"}, + Type: types.BOOLEAN, + }, + ) + + err := command.Execute() + if err != nil { + _, _ = fmt.Fprintln(os.Stderr, err) + os.Exit(1) } return } diff --git a/scripts/dbMigrate.go b/scripts/dbMigrate.go index 22fde88..60f8192 100644 --- a/scripts/dbMigrate.go +++ b/scripts/dbMigrate.go @@ -10,6 +10,7 @@ import ( "git.wh64.net/muffin/goMuffin/configs" + "github.com/devproje/commando" _ "github.com/go-sql-driver/mysql" "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/mongo" @@ -19,7 +20,7 @@ import ( var wg sync.WaitGroup // 이 스크립트는 MariaDB -> MongoDB로의 전환을 위해 만들었음. -func DBMigrate() { +func DBMigrate(n *commando.Node) error { mariaURL := os.Getenv("PREVIOUS_DATABASE_URL") mongoURL := configs.Config.DatabaseURL dbName := configs.Config.DBName @@ -189,4 +190,5 @@ func DBMigrate() { // 모든 고루틴이 끝날 떄 까지 대기 wg.Wait() fmt.Println("데이터 마이그레이션이 끝났어요.") + return nil } diff --git a/scripts/deleteAllCommands.go b/scripts/deleteAllCommands.go index 6e325df..af496c9 100644 --- a/scripts/deleteAllCommands.go +++ b/scripts/deleteAllCommands.go @@ -1,7 +1,6 @@ package scripts import ( - "flag" "fmt" "io" "net/http" @@ -10,40 +9,45 @@ import ( "git.wh64.net/muffin/goMuffin/configs" "github.com/bwmarrin/discordgo" + "github.com/devproje/commando" + "github.com/devproje/commando/option" ) -func DeleteAllCommands() { +func DeleteAllCommands(n *commando.Node) error { var answer string - id := flag.String("id", "", "디스코드 봇의 토큰") - flag.Parse() - - fmt.Printf("정말로 모든 명령어를 삭제하시겠어요? [y/N]: ") - fmt.Scanf("%s", &answer) - if strings.ToLower(answer) != "y" && strings.ToLower(answer) != "yes" { - os.Exit(1) + id, err := option.ParseString(*n.MustGetOpt("id"), n) + if err != nil { + return err } - if *id == "" { - panic(fmt.Errorf("--id 플래그의 값이 필요해요")) + yes, _ := option.ParseBool(*n.MustGetOpt("isYes"), n) + if !yes { + fmt.Printf("정말로 모든 명령어를 삭제하시겠어요? [y/N]: ") + fmt.Scanf("%s", &answer) + if strings.ToLower(answer) != "y" && strings.ToLower(answer) != "yes" { + fmt.Println("모든 명령어 삭제를 취소했어요.") + os.Exit(1) + } } c := http.Client{} - req, err := http.NewRequest("PUT", discordgo.EndpointApplicationGlobalCommands(*id), nil) + req, err := http.NewRequest("PUT", discordgo.EndpointApplicationGlobalCommands(id), nil) if err != nil { - panic(err) + return err } req.Header.Add("Authorization", "Bot "+configs.Config.Bot.Token) resp, err := c.Do(req) if err != nil { - panic(err) + return err } bytes, err := io.ReadAll(resp.Body) if err != nil { - panic(err) + return err } fmt.Println(string(bytes)) + return nil } From 216264be9ccb2c0ebcddf8fff6727c2b8f7def8d Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Mon, 5 May 2025 15:41:31 +0900 Subject: [PATCH 04/34] fix: dependencies --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 9cbb709..814397b 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.24.1 require ( github.com/LoperLee/golang-hangul-toolkit v1.1.0 github.com/bwmarrin/discordgo v0.28.1 + github.com/devproje/commando v0.1.0-alpha.1 github.com/go-sql-driver/mysql v1.9.2 github.com/joho/godotenv v1.5.1 go.mongodb.org/mongo-driver/v2 v2.1.0 @@ -12,7 +13,6 @@ require ( require ( filippo.io/edwards25519 v1.1.0 // indirect - github.com/devproje/commando v0.1.0-alpha.1 // indirect github.com/golang/snappy v1.0.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/klauspost/compress v1.18.0 // indirect From 25af28e5ffe091345dfa0875087ca3662061f6e8 Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Wed, 7 May 2025 19:43:26 +0900 Subject: [PATCH 05/34] feat: add export muffin data --- .gitignore | 4 +- databases/Learn.go | 14 ++-- databases/Text.go | 12 ++-- main.go | 18 +++++ scripts/export.go | 169 +++++++++++++++++++++++++++++++++++++++++++++ utils/regexp.go | 1 + 6 files changed, 204 insertions(+), 14 deletions(-) create mode 100644 scripts/export.go diff --git a/.gitignore b/.gitignore index b8b14c5..9c95d85 100644 --- a/.gitignore +++ b/.gitignore @@ -124,4 +124,6 @@ $RECYCLE.BIN/ # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) # Config files *.env -!.env.example \ No newline at end of file +!.env.example + +export/ \ No newline at end of file diff --git a/databases/Learn.go b/databases/Learn.go index f0d25aa..b89efee 100644 --- a/databases/Learn.go +++ b/databases/Learn.go @@ -9,18 +9,18 @@ import ( ) type InsertLearn struct { - Command string - Result string + Command string `bson:"command"` + Result string `bson:"result"` UserId string `bson:"user_id"` CreatedAt time.Time `bson:"created_at"` } type Learn struct { - Id bson.ObjectID `bson:"_id"` - Command string - Result string - UserId string `bson:"user_id"` - CreatedAt time.Time `bson:"created_at"` + Id bson.ObjectID `bson:"_id" json:"id"` + Command string `bson:"command" json:"command"` + Result string `bson:"Result" json:"result"` + UserId string `bson:"user_id" json:"user_id"` + CreatedAt time.Time `bson:"created_at" json:"created_at"` } var Learns *mongo.Collection = Client.Database(configs.Config.DBName).Collection("learn") diff --git a/databases/Text.go b/databases/Text.go index 06dc147..fbff6b2 100644 --- a/databases/Text.go +++ b/databases/Text.go @@ -9,16 +9,16 @@ import ( ) type InsertText struct { - Text string - Persona string + Text string `bson:"text" json:"text"` + Persona string `bson:"persona" json:"persona"` CreatedAt time.Time `bson:"created_at"` } type Text struct { - Id bson.ObjectID `bson:"_id"` - Text string - Persona string - CreatedAt time.Time `bson:"created_at"` + Id bson.ObjectID `bson:"_id" json:"id"` + Text string `bson:"text" json:"text"` + Persona string `bson:"persona" json:"persona"` + CreatedAt time.Time `bson:"created_at" json:"created_at"` } var Texts *mongo.Collection = Client.Database(configs.Config.DBName).Collection("text") diff --git a/main.go b/main.go index 4f36f25..e53f1f5 100644 --- a/main.go +++ b/main.go @@ -39,6 +39,24 @@ func main() { }, ) + command.Root("export", "머핀봇의 데이터를 추출합니다.", scripts.ExportData, + types.OptionData{ + Name: "type", + Desc: "파일형식을 지정합니다. (json, txt(txt는 머핀 데이터만 적용))", + Type: types.STRING, + }, + types.OptionData{ + Name: "export-path", + Desc: "데이터를 저장할 위치를 지정합니다.", + Type: types.STRING, + }, + types.OptionData{ + Name: "refined", + Desc: "머핀 데이터를 있는 그대로 추출할 지, 가려내서 추출할 지를 지정합니다.", + Type: types.BOOLEAN, + }, + ) + err := command.Execute() if err != nil { _, _ = fmt.Fprintln(os.Stderr, err) diff --git a/scripts/export.go b/scripts/export.go new file mode 100644 index 0000000..79f7676 --- /dev/null +++ b/scripts/export.go @@ -0,0 +1,169 @@ +package scripts + +import ( + "context" + "encoding/json" + "fmt" + "os" + "strconv" + "strings" + "time" + + "git.wh64.net/muffin/goMuffin/databases" + "git.wh64.net/muffin/goMuffin/utils" + "github.com/devproje/commando" + "github.com/devproje/commando/option" + "go.mongodb.org/mongo-driver/v2/bson" +) + +var date time.Time = time.Now() + +func getDate() string { + year := strconv.Itoa(date.Year()) + month := strconv.Itoa(int(date.Month())) + day := strconv.Itoa(date.Day()) + hour := strconv.Itoa(date.Hour()) + minute := strconv.Itoa(date.Minute()) + sec := strconv.Itoa(date.Second()) + + if len(month) < 2 { + month = "0" + month + } + + if len(day) < 2 { + day = "0" + day + } + + if len(hour) < 2 { + hour = "0" + hour + } + + if len(minute) < 2 { + minute = "0" + minute + } + + if len(sec) < 2 { + sec = "0" + sec + } + return year + month + day + hour + minute + sec +} + +func checkDir(path string) error { + _, err := os.ReadDir(path) + if err != nil { + err = os.MkdirAll(path, os.ModePerm) + if err != nil { + return err + } + } + return nil +} + +func saveFileToJSON(path, name string, data any) error { + bytes, err := json.MarshalIndent(data, "", " ") + if err != nil { + return err + } + + path += getDate() + err = checkDir(path) + if err != nil { + return err + } + + f, err := os.Create(fmt.Sprintf("%s/%s.json", path, name)) + if err != nil { + return err + } + + defer f.Close() + + _, err = f.Write(bytes) + if err != nil { + return err + } + return nil +} + +func saveFileToTXT(path, name string, data []databases.Text) error { + var content string + + for _, data := range data { + content += data.Text + "\n" + } + + path += getDate() + err := checkDir(path) + if err != nil { + return err + } + + f, err := os.Create(fmt.Sprintf("%s/%s.txt", path, name)) + if err != nil { + return err + } + + defer f.Close() + + _, err = f.WriteString(content) + if err != nil { + return err + } + return nil +} + +func ExportData(n *commando.Node) error { + ch := make(chan error) + defer databases.Client.Disconnect(context.TODO()) + fileType, err := option.ParseString(*n.MustGetOpt("type"), n) + if err != nil { + return err + } + + if fileType != "txt" && fileType != "json" { + return fmt.Errorf("파일 형식은 txt또는 json이여야 해요") + } + + refined, err := option.ParseBool(*n.MustGetOpt("refined"), n) + if err != nil { + return err + } + + path, err := option.ParseString(*n.MustGetOpt("export-path"), n) + if err != nil { + return err + } + + texts := databases.Texts + // learns := databases.Learns + + // 머핀 데이터 추출 + go func() { + var data []databases.Text + + cur, err := texts.Find(context.TODO(), bson.D{{Key: "persona", Value: "muffin"}}) + if err != nil { + ch <- err + } + + cur.All(context.TODO(), &data) + + if refined { + for i, text := range data { + if utils.EmojiRegexp.Match([]byte(text.Text)) { + data = append(data[:i], data[i+1:]...) + } + + text.Text = strings.TrimPrefix(text.Text, "머핀아 ") + } + } + + if fileType == "json" { + ch <- saveFileToJSON(path, "muffin", data) + } else { + fmt.Println("NOTE: 파일 형식이 'txt'인 경우 머핀 데이터만 txt형식으로 저장되고, 나머지는 json으로 저장됩니다.") + ch <- saveFileToTXT(path, "muffin", data) + } + }() + return <-ch +} diff --git a/utils/regexp.go b/utils/regexp.go index d2279e3..f162478 100644 --- a/utils/regexp.go +++ b/utils/regexp.go @@ -5,3 +5,4 @@ import "regexp" var FlexibleStringParser *regexp.Regexp = regexp.MustCompile("[^\\s\"'「」«»]+|\"([^\"]*)\"|'([^']*)'|「([^」]*)」|«([^»]*)»") var Decimals *regexp.Regexp = regexp.MustCompile(`\d+`) var ItemIdRegexp *regexp.Regexp = regexp.MustCompile(`No.\d+`) +var EmojiRegexp *regexp.Regexp = regexp.MustCompile(``) From 0897e8046516a9c627407ea95a4d14316103287f Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Wed, 7 May 2025 21:11:52 +0900 Subject: [PATCH 06/34] feat: add export data nsfw --- scripts/export.go | 121 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 100 insertions(+), 21 deletions(-) diff --git a/scripts/export.go b/scripts/export.go index 79f7676..c91d51a 100644 --- a/scripts/export.go +++ b/scripts/export.go @@ -7,13 +7,17 @@ import ( "os" "strconv" "strings" + "sync" "time" + "git.wh64.net/muffin/goMuffin/configs" "git.wh64.net/muffin/goMuffin/databases" "git.wh64.net/muffin/goMuffin/utils" "github.com/devproje/commando" "github.com/devproje/commando/option" "go.mongodb.org/mongo-driver/v2/bson" + "go.mongodb.org/mongo-driver/v2/mongo" + "go.mongodb.org/mongo-driver/v2/mongo/options" ) var date time.Time = time.Now() @@ -65,12 +69,6 @@ func saveFileToJSON(path, name string, data any) error { return err } - path += getDate() - err = checkDir(path) - if err != nil { - return err - } - f, err := os.Create(fmt.Sprintf("%s/%s.json", path, name)) if err != nil { return err @@ -92,12 +90,6 @@ func saveFileToTXT(path, name string, data []databases.Text) error { content += data.Text + "\n" } - path += getDate() - err := checkDir(path) - if err != nil { - return err - } - f, err := os.Create(fmt.Sprintf("%s/%s.txt", path, name)) if err != nil { return err @@ -113,8 +105,10 @@ func saveFileToTXT(path, name string, data []databases.Text) error { } func ExportData(n *commando.Node) error { - ch := make(chan error) - defer databases.Client.Disconnect(context.TODO()) + var wg sync.WaitGroup + ch := make(chan error, 2) + + databases.Client.Disconnect(context.TODO()) // databases 패키지의 DB 연결은 필요 없음 (나중에 수정 예정) fileType, err := option.ParseString(*n.MustGetOpt("type"), n) if err != nil { return err @@ -134,24 +128,48 @@ func ExportData(n *commando.Node) error { return err } - texts := databases.Texts - // learns := databases.Learns + path += "/" + getDate() + + err = checkDir(path) + if err != nil { + return err + } + + wg.Add(2) // 머핀 데이터 추출 go func() { + defer wg.Done() + var data []databases.Text - cur, err := texts.Find(context.TODO(), bson.D{{Key: "persona", Value: "muffin"}}) + conn, err := mongo.Connect(options.Client().ApplyURI(configs.Config.DatabaseURL)) if err != nil { ch <- err + return } - cur.All(context.TODO(), &data) + defer conn.Disconnect(context.TODO()) + + cur, err := conn.Database(configs.Config.DBName).Collection("text").Find(context.TODO(), bson.D{{Key: "persona", Value: "muffin"}}) + if err != nil { + ch <- err + return + } + + defer cur.Close(context.TODO()) + + err = cur.All(context.TODO(), &data) + if err != nil { + ch <- err + return + } if refined { for i, text := range data { if utils.EmojiRegexp.Match([]byte(text.Text)) { data = append(data[:i], data[i+1:]...) + return } text.Text = strings.TrimPrefix(text.Text, "머핀아 ") @@ -159,11 +177,72 @@ func ExportData(n *commando.Node) error { } if fileType == "json" { - ch <- saveFileToJSON(path, "muffin", data) + err = saveFileToJSON(path, "muffin", data) + if err != nil { + ch <- err + return + } } else { fmt.Println("NOTE: 파일 형식이 'txt'인 경우 머핀 데이터만 txt형식으로 저장되고, 나머지는 json으로 저장됩니다.") - ch <- saveFileToTXT(path, "muffin", data) + err = saveFileToTXT(path, "muffin", data) + if err != nil { + ch <- err + return + } } + + fmt.Println("머핀 데이터 추출 완료") }() - return <-ch + + // nsfw 데이터 추출 + go func() { + defer wg.Done() + + var data []databases.Text + + conn, err := mongo.Connect(options.Client().ApplyURI(configs.Config.DatabaseURL)) + if err != nil { + ch <- err + return + } + + defer conn.Disconnect(context.TODO()) + + cur, err := conn.Database(configs.Config.DBName).Collection("text").Find(context.TODO(), bson.D{ + { + Key: "persona", + Value: bson.M{ + "$regex": "^user", + }, + }, + }) + if err != nil { + ch <- err + return + } + + defer cur.Close(context.TODO()) + + err = cur.All(context.TODO(), &data) + if err != nil { + ch <- err + return + } + + err = saveFileToJSON(path, "nsfw", data) + if err != nil { + ch <- err + return + } + + fmt.Println("nsfw 데이터 추출 완료") + }() + + wg.Wait() + close(ch) + + for err = range ch { + fmt.Println(err) + } + return nil } From 7d19bd66dff0d7cd05648924b64cb860dcf8e5dc Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Wed, 7 May 2025 21:17:07 +0900 Subject: [PATCH 07/34] fix: wrong tag --- databases/Learn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/databases/Learn.go b/databases/Learn.go index b89efee..81ce270 100644 --- a/databases/Learn.go +++ b/databases/Learn.go @@ -18,7 +18,7 @@ type InsertLearn struct { type Learn struct { Id bson.ObjectID `bson:"_id" json:"id"` Command string `bson:"command" json:"command"` - Result string `bson:"Result" json:"result"` + Result string `bson:"result" json:"result"` UserId string `bson:"user_id" json:"user_id"` CreatedAt time.Time `bson:"created_at" json:"created_at"` } From a739938229710162b234f18ecc862e9cbfc7774e Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Wed, 7 May 2025 21:17:21 +0900 Subject: [PATCH 08/34] feat: add export learn data --- scripts/export.go | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/scripts/export.go b/scripts/export.go index c91d51a..e2bd766 100644 --- a/scripts/export.go +++ b/scripts/export.go @@ -106,7 +106,7 @@ func saveFileToTXT(path, name string, data []databases.Text) error { func ExportData(n *commando.Node) error { var wg sync.WaitGroup - ch := make(chan error, 2) + ch := make(chan error, 3) databases.Client.Disconnect(context.TODO()) // databases 패키지의 DB 연결은 필요 없음 (나중에 수정 예정) fileType, err := option.ParseString(*n.MustGetOpt("type"), n) @@ -135,7 +135,7 @@ func ExportData(n *commando.Node) error { return err } - wg.Add(2) + wg.Add(3) // 머핀 데이터 추출 go func() { @@ -238,6 +238,43 @@ func ExportData(n *commando.Node) error { fmt.Println("nsfw 데이터 추출 완료") }() + // 지식 데이터 추출 + go func() { + defer wg.Done() + + var data []databases.Learn + + conn, err := mongo.Connect(options.Client().ApplyURI(configs.Config.DatabaseURL)) + if err != nil { + ch <- err + return + } + + defer conn.Disconnect(context.TODO()) + + cur, err := conn.Database(configs.Config.DBName).Collection("learn").Find(context.TODO(), bson.D{{}}) + if err != nil { + ch <- err + return + } + + defer cur.Close(context.TODO()) + + err = cur.All(context.TODO(), &data) + if err != nil { + ch <- err + return + } + + err = saveFileToJSON(path, "learn", data) + if err != nil { + ch <- err + return + } + + fmt.Println("지식 데이터 추출 완료") + }() + wg.Wait() close(ch) From 8be039e8cf3a6eac2fc19e0d5b59ea0cd5b359f2 Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Thu, 8 May 2025 15:58:44 +0900 Subject: [PATCH 09/34] feat: add export to jsonl --- scripts/export.go | 52 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/scripts/export.go b/scripts/export.go index e2bd766..5f26bd6 100644 --- a/scripts/export.go +++ b/scripts/export.go @@ -22,6 +22,10 @@ import ( var date time.Time = time.Now() +type jsonlData struct { + Text string `json:"text"` +} + func getDate() string { year := strconv.Itoa(date.Year()) month := strconv.Itoa(int(date.Month())) @@ -104,6 +108,31 @@ func saveFileToTXT(path, name string, data []databases.Text) error { return nil } +func saveFileToJSONL(path, name string, data []jsonlData) error { + var content string + + for _, data := range data { + bytes, err := json.Marshal(data) + if err != nil { + return err + } + content += string(bytes) + "\n" + } + + f, err := os.Create(fmt.Sprintf("%s/%s.jsonl", path, name)) + if err != nil { + return err + } + + defer f.Close() + + _, err = f.WriteString(content) + if err != nil { + return err + } + return nil +} + func ExportData(n *commando.Node) error { var wg sync.WaitGroup ch := make(chan error, 3) @@ -114,8 +143,8 @@ func ExportData(n *commando.Node) error { return err } - if fileType != "txt" && fileType != "json" { - return fmt.Errorf("파일 형식은 txt또는 json이여야 해요") + if fileType != "txt" && fileType != "json" && fileType != "jsonl" { + return fmt.Errorf("파일 형식은 txt또는 json또는 jsonl이여야 해요") } refined, err := option.ParseBool(*n.MustGetOpt("refined"), n) @@ -182,13 +211,28 @@ func ExportData(n *commando.Node) error { ch <- err return } - } else { - fmt.Println("NOTE: 파일 형식이 'txt'인 경우 머핀 데이터만 txt형식으로 저장되고, 나머지는 json으로 저장됩니다.") + } else if fileType == "txt" { + fmt.Println("NOTE: 파일 형식이 'txt'인 경우 머핀 데이터만 txt형식으로 추출되고, 나머지는 json으로 추출됩니다.") err = saveFileToTXT(path, "muffin", data) if err != nil { ch <- err return } + } else if fileType == "jsonl" { + var newData []jsonlData + + fmt.Println("NOTE: 파일 형식이 'jsonl'인 경우 머핀 데이터만 jsonl형식으로 추출되고, 나머지는 json으로 추출됩니다.") + fmt.Println("NOTE: 파일 형식이 'jsonl'인 경우 일부데이터만 추출 됩니다.") + + for _, data := range data { + newData = append(newData, jsonlData{data.Text}) + } + + err = saveFileToJSONL(path, "muffin", newData) + if err != nil { + ch <- err + return + } } fmt.Println("머핀 데이터 추출 완료") From df693bfa43a3066e2a4630564d82826c3090ec92 Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Fri, 9 May 2025 19:31:56 +0900 Subject: [PATCH 10/34] feat: add jsonl export learn, nsfw. delete txt export --- scripts/export.go | 118 ++++++++++++++++++++++++++-------------------- 1 file changed, 68 insertions(+), 50 deletions(-) diff --git a/scripts/export.go b/scripts/export.go index 5f26bd6..c8269c6 100644 --- a/scripts/export.go +++ b/scripts/export.go @@ -22,8 +22,14 @@ import ( var date time.Time = time.Now() -type jsonlData struct { - Text string `json:"text"` +type textJSONLData struct { + Text string `json:"text"` + Persona string `json:"persona,omitempty"` +} + +type learnJSONLData struct { + Command string `json:"command"` + Result string `json:"result"` } func getDate() string { @@ -87,36 +93,26 @@ func saveFileToJSON(path, name string, data any) error { return nil } -func saveFileToTXT(path, name string, data []databases.Text) error { +func saveFileToJSONL(path, name string, data any) error { var content string - for _, data := range data { - content += data.Text + "\n" - } - - f, err := os.Create(fmt.Sprintf("%s/%s.txt", path, name)) - if err != nil { - return err - } - - defer f.Close() - - _, err = f.WriteString(content) - if err != nil { - return err - } - return nil -} - -func saveFileToJSONL(path, name string, data []jsonlData) error { - var content string - - for _, data := range data { - bytes, err := json.Marshal(data) - if err != nil { - return err + switch data := data.(type) { + case []textJSONLData: + for _, data := range data { + bytes, err := json.Marshal(data) + if err != nil { + return err + } + content += string(bytes) + "\n" + } + case []learnJSONLData: + for _, data := range data { + bytes, err := json.Marshal(data) + if err != nil { + return err + } + content += string(bytes) + "\n" } - content += string(bytes) + "\n" } f, err := os.Create(fmt.Sprintf("%s/%s.jsonl", path, name)) @@ -143,7 +139,7 @@ func ExportData(n *commando.Node) error { return err } - if fileType != "txt" && fileType != "json" && fileType != "jsonl" { + if fileType != "json" && fileType != "jsonl" { return fmt.Errorf("파일 형식은 txt또는 json또는 jsonl이여야 해요") } @@ -166,6 +162,10 @@ func ExportData(n *commando.Node) error { wg.Add(3) + if fileType == "jsonl" { + fmt.Println("NOTE: 파일 형식이 'jsonl'인 경우 일부데이터만 추출 됩니다.") + } + // 머핀 데이터 추출 go func() { defer wg.Done() @@ -211,21 +211,11 @@ func ExportData(n *commando.Node) error { ch <- err return } - } else if fileType == "txt" { - fmt.Println("NOTE: 파일 형식이 'txt'인 경우 머핀 데이터만 txt형식으로 추출되고, 나머지는 json으로 추출됩니다.") - err = saveFileToTXT(path, "muffin", data) - if err != nil { - ch <- err - return - } } else if fileType == "jsonl" { - var newData []jsonlData - - fmt.Println("NOTE: 파일 형식이 'jsonl'인 경우 머핀 데이터만 jsonl형식으로 추출되고, 나머지는 json으로 추출됩니다.") - fmt.Println("NOTE: 파일 형식이 'jsonl'인 경우 일부데이터만 추출 됩니다.") + var newData []textJSONLData for _, data := range data { - newData = append(newData, jsonlData{data.Text}) + newData = append(newData, textJSONLData{data.Text, ""}) } err = saveFileToJSONL(path, "muffin", newData) @@ -273,10 +263,24 @@ func ExportData(n *commando.Node) error { return } - err = saveFileToJSON(path, "nsfw", data) - if err != nil { - ch <- err - return + if fileType == "json" { + err = saveFileToJSON(path, "nsfw", data) + if err != nil { + ch <- err + return + } + } else if fileType == "jsonl" { + var newData []textJSONLData + + for _, data := range data { + newData = append(newData, textJSONLData{data.Text, data.Persona}) + } + + err = saveFileToJSONL(path, "nsfw", newData) + if err != nil { + ch <- err + return + } } fmt.Println("nsfw 데이터 추출 완료") @@ -310,10 +314,24 @@ func ExportData(n *commando.Node) error { return } - err = saveFileToJSON(path, "learn", data) - if err != nil { - ch <- err - return + if fileType == "json" { + err = saveFileToJSON(path, "learn", data) + if err != nil { + ch <- err + return + } + } else if fileType == "jsonl" { + var newData []learnJSONLData + + for _, data := range data { + newData = append(newData, learnJSONLData{data.Command, data.Result}) + } + + err = saveFileToJSONL(path, "learn", newData) + if err != nil { + ch <- err + return + } } fmt.Println("지식 데이터 추출 완료") From 302fa9d3d4d576f9e3505f3fafae77794b8ede3e Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Sat, 10 May 2025 17:59:06 +0900 Subject: [PATCH 11/34] readme: edit script --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b83ff8e..0ef3522 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,10 @@ ## 실행 +- 아래의 모든 명령어는 make를 사용합니다. + ```sh -go run main.go +make run ``` ### 빌드 @@ -21,7 +23,7 @@ go run main.go 1. 빌드 ```sh -go build -o ./build/goMuffin git.wh64.net/muffin/goMuffin # 윈도우면 ./build/goMuffin을 .\build\goMuffin.exe으로 변경 +make #또는 make build ``` 2. 실행 From 052924fcb537d455ebf0bdbf03ba9271f0577081 Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Sat, 10 May 2025 19:34:02 +0900 Subject: [PATCH 12/34] feat: add search in learnedDataList --- commands/learnedDataList.go | 67 ++++++++++++++++++++++++++++++++++--- configs/version.go | 2 +- utils/regexp.go | 12 ++++--- 3 files changed, 72 insertions(+), 9 deletions(-) diff --git a/commands/learnedDataList.go b/commands/learnedDataList.go index 9c3554c..be71433 100644 --- a/commands/learnedDataList.go +++ b/commands/learnedDataList.go @@ -13,22 +13,39 @@ import ( "go.mongodb.org/mongo-driver/v2/mongo" ) +const ( + learnArgsCommand = "단어:" + learnArgsResult = "대답:" +) + var LearnedDataListCommand *Command = &Command{ ApplicationCommand: &discordgo.ApplicationCommand{ Type: discordgo.ChatApplicationCommand, Name: "리스트", Description: "당신이 가ㄹ르쳐준 지식을 나열해요.", + Options: []*discordgo.ApplicationCommandOption{ + { + Name: "쿼리", + Description: "해당 단어가 포함된 결과만 찾아요.", + Required: false, + }, + }, }, Aliases: []string{"list", "목록", "지식목록"}, DetailedDescription: &DetailedDescription{ Usage: fmt.Sprintf("%s리스트", configs.Config.Bot.Prefix), + Examples: []string{ + fmt.Sprintf("%s리스트 ㅁㄴㅇㄹ", configs.Config.Bot.Prefix), + fmt.Sprintf("%s리스트 단어:안녕", configs.Config.Bot.Prefix), + fmt.Sprintf("%s리스트 대답:머핀", configs.Config.Bot.Prefix), + }, }, Category: Chatting, MessageRun: func(ctx *MsgContext) { - learnedDataListRun(ctx.Session, ctx.Msg) + learnedDataListRun(ctx.Session, ctx.Msg, &ctx.Args) }, ChatInputRun: func(ctx *ChatInputContext) { - learnedDataListRun(ctx.Session, ctx.Inter) + learnedDataListRun(ctx.Session, ctx.Inter, nil) }, } @@ -39,24 +56,66 @@ func getDescriptions(data *[]databases.Learn) (descriptions []string) { return } -func learnedDataListRun(s *discordgo.Session, m any) { +func learnedDataListRun(s *discordgo.Session, m any, args *[]string) { var userId, globalName, avatarUrl string var data []databases.Learn + var filter bson.E switch m := m.(type) { case *discordgo.MessageCreate: userId = m.Author.ID globalName = m.Author.GlobalName avatarUrl = m.Author.AvatarURL("512") + + query := strings.Join(*args, " ") + if strings.HasPrefix(query, learnArgsResult) { + query, _ = strings.CutPrefix(query, learnArgsResult) + filter = bson.E{ + Key: "result", + Value: bson.M{ + "$regex": query, + }, + } + } else { + query, _ = strings.CutPrefix(query, learnArgsCommand) + filter = bson.E{ + Key: "command", + Value: bson.M{ + "$regex": query, + }, + } + } case *utils.InteractionCreate: m.DeferReply(true) userId = m.Member.User.ID globalName = m.Member.User.GlobalName avatarUrl = m.Member.User.AvatarURL("512") + + if opt, ok := m.Options["쿼리"]; ok { + query := opt.StringValue() + + if strings.HasPrefix(query, learnArgsResult) { + query, _ = strings.CutPrefix(query, learnArgsResult) + filter = bson.E{ + Key: "result", + Value: bson.M{ + "$regex": query, + }, + } + } else { + query, _ = strings.CutPrefix(query, learnArgsCommand) + filter = bson.E{ + Key: "command", + Value: bson.M{ + "$regex": query, + }, + } + } + } } - cur, err := databases.Learns.Find(context.TODO(), bson.D{{Key: "user_id", Value: userId}}) + cur, err := databases.Learns.Find(context.TODO(), bson.D{{Key: "user_id", Value: userId}, filter}) if err != nil { if err == mongo.ErrNoDocuments { embed := &discordgo.MessageEmbed{ diff --git a/configs/version.go b/configs/version.go index dcb0b48..6df6301 100644 --- a/configs/version.go +++ b/configs/version.go @@ -7,7 +7,7 @@ import ( "git.wh64.net/muffin/goMuffin/utils" ) -const MUFFIN_VERSION = "5.0.1-gopher_release.250505a" +const MUFFIN_VERSION = "5.1.0-gopher_dev.250510a" var updatedString string = utils.Decimals.FindAllStringSubmatch(MUFFIN_VERSION, -1)[3][0] diff --git a/utils/regexp.go b/utils/regexp.go index f162478..f180d6e 100644 --- a/utils/regexp.go +++ b/utils/regexp.go @@ -2,7 +2,11 @@ package utils import "regexp" -var FlexibleStringParser *regexp.Regexp = regexp.MustCompile("[^\\s\"'「」«»]+|\"([^\"]*)\"|'([^']*)'|「([^」]*)」|«([^»]*)»") -var Decimals *regexp.Regexp = regexp.MustCompile(`\d+`) -var ItemIdRegexp *regexp.Regexp = regexp.MustCompile(`No.\d+`) -var EmojiRegexp *regexp.Regexp = regexp.MustCompile(``) +var ( + FlexibleStringParser = regexp.MustCompile("[^\\s\"'「」«»]+|\"([^\"]*)\"|'([^']*)'|「([^」]*)」|«([^»]*)»") + Decimals = regexp.MustCompile(`\d+`) + ItemIdRegexp = regexp.MustCompile(`No.\d+`) + EmojiRegexp = regexp.MustCompile(``) + LearnQueryCommand = regexp.MustCompile(`^단어:`) + LearnQueryResult = regexp.MustCompile(`^대답:`) +) From f69b3f13a3472c6d59558f67a8e17f19791d5773 Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Sun, 11 May 2025 13:48:53 +0900 Subject: [PATCH 13/34] chore: edit database struct --- commands/dataLength.go | 10 ++++----- commands/deleteLearnedData.go | 2 +- commands/learn.go | 2 +- commands/learnedDataList.go | 2 +- components/deleteLearnedData.go | 2 +- databases/Learn.go | 4 ---- databases/Text.go | 4 ---- databases/database.go | 28 ++++++++++++++++++++----- handler/messageCreate.go | 8 ++++---- main.go | 2 +- scripts/export.go | 36 +++++---------------------------- 11 files changed, 42 insertions(+), 58 deletions(-) diff --git a/commands/dataLength.go b/commands/dataLength.go index e14ffaa..6ee92eb 100644 --- a/commands/dataLength.go +++ b/commands/dataLength.go @@ -89,9 +89,9 @@ func dataLengthRun(s *discordgo.Session, m any) { } dataLengthWg.Add(5) - go getLength(text, databases.Texts, bson.D{{}}) - go getLength(muffin, databases.Texts, bson.D{{Key: "persona", Value: "muffin"}}) - go getLength(nsfw, databases.Texts, bson.D{ + go getLength(text, databases.Database.Texts, bson.D{{}}) + go getLength(muffin, databases.Database.Texts, bson.D{{Key: "persona", Value: "muffin"}}) + go getLength(nsfw, databases.Database.Texts, bson.D{ { Key: "persona", Value: bson.M{ @@ -99,8 +99,8 @@ func dataLengthRun(s *discordgo.Session, m any) { }, }, }) - go getLength(learn, databases.Learns, bson.D{{}}) - go getLength(userLearn, databases.Learns, bson.D{{Key: "user_id", Value: userId}}) + go getLength(learn, databases.Database.Learns, bson.D{{}}) + go getLength(userLearn, databases.Database.Learns, bson.D{{Key: "user_id", Value: userId}}) go func() { dataLengthWg.Wait() diff --git a/commands/deleteLearnedData.go b/commands/deleteLearnedData.go index 105c928..aaa31e0 100644 --- a/commands/deleteLearnedData.go +++ b/commands/deleteLearnedData.go @@ -74,7 +74,7 @@ func deleteLearnedDataRun(c *Command, s *discordgo.Session, m any, args *[]strin userId = m.Member.User.ID } - cur, err := databases.Learns.Find(context.TODO(), bson.M{"user_id": userId, "command": command}) + cur, err := databases.Database.Learns.Find(context.TODO(), bson.M{"user_id": userId, "command": command}) if err != nil { embed := &discordgo.MessageEmbed{ Title: "❌ 오류", diff --git a/commands/learn.go b/commands/learn.go index 12af361..be0fe30 100644 --- a/commands/learn.go +++ b/commands/learn.go @@ -173,7 +173,7 @@ func learnRun(c *Command, s *discordgo.Session, m any, args *[]string) { } } - _, err := databases.Learns.InsertOne(context.TODO(), databases.InsertLearn{ + _, err := databases.Database.Learns.InsertOne(context.TODO(), databases.InsertLearn{ Command: command, Result: result, UserId: userId, diff --git a/commands/learnedDataList.go b/commands/learnedDataList.go index be71433..dc7b486 100644 --- a/commands/learnedDataList.go +++ b/commands/learnedDataList.go @@ -115,7 +115,7 @@ func learnedDataListRun(s *discordgo.Session, m any, args *[]string) { } } - cur, err := databases.Learns.Find(context.TODO(), bson.D{{Key: "user_id", Value: userId}, filter}) + cur, err := databases.Database.Learns.Find(context.TODO(), bson.D{{Key: "user_id", Value: userId}, filter}) if err != nil { if err == mongo.ErrNoDocuments { embed := &discordgo.MessageEmbed{ diff --git a/components/deleteLearnedData.go b/components/deleteLearnedData.go index 4737d11..84311a4 100644 --- a/components/deleteLearnedData.go +++ b/components/deleteLearnedData.go @@ -69,7 +69,7 @@ var DeleteLearnedDataComponent *commands.Component = &commands.Component{ 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.", "") - databases.Learns.DeleteOne(context.TODO(), bson.D{{Key: "_id", Value: id}}) + databases.Database.Learns.DeleteOne(context.TODO(), bson.D{{Key: "_id", Value: id}}) i.EditReply(&discordgo.WebhookEdit{ Embeds: &[]*discordgo.MessageEmbed{ diff --git a/databases/Learn.go b/databases/Learn.go index 81ce270..cf0c265 100644 --- a/databases/Learn.go +++ b/databases/Learn.go @@ -3,9 +3,7 @@ package databases import ( "time" - "git.wh64.net/muffin/goMuffin/configs" "go.mongodb.org/mongo-driver/v2/bson" - "go.mongodb.org/mongo-driver/v2/mongo" ) type InsertLearn struct { @@ -22,5 +20,3 @@ type Learn struct { UserId string `bson:"user_id" json:"user_id"` CreatedAt time.Time `bson:"created_at" json:"created_at"` } - -var Learns *mongo.Collection = Client.Database(configs.Config.DBName).Collection("learn") diff --git a/databases/Text.go b/databases/Text.go index fbff6b2..7bacbbe 100644 --- a/databases/Text.go +++ b/databases/Text.go @@ -3,9 +3,7 @@ package databases import ( "time" - "git.wh64.net/muffin/goMuffin/configs" "go.mongodb.org/mongo-driver/v2/bson" - "go.mongodb.org/mongo-driver/v2/mongo" ) type InsertText struct { @@ -20,5 +18,3 @@ type Text struct { Persona string `bson:"persona" json:"persona"` CreatedAt time.Time `bson:"created_at" json:"created_at"` } - -var Texts *mongo.Collection = Client.Database(configs.Config.DBName).Collection("text") diff --git a/databases/database.go b/databases/database.go index 81115f2..7928bdc 100644 --- a/databases/database.go +++ b/databases/database.go @@ -8,13 +8,31 @@ import ( "go.mongodb.org/mongo-driver/v2/mongo/options" ) -func connect() *mongo.Client { - client, err := mongo.Connect(options.Client().ApplyURI(configs.Config.DatabaseURL)) +type MuffinDatabase struct { + Client *mongo.Client + Learns *mongo.Collection + Texts *mongo.Collection +} + +var Database *MuffinDatabase + +func init() { + var err error + + Database, err = Connect() if err != nil { log.Fatalln(err) } - - return client } -var Client *mongo.Client = connect() +func Connect() (*MuffinDatabase, error) { + client, err := mongo.Connect(options.Client().ApplyURI(configs.Config.DatabaseURL)) + if err != nil { + return nil, err + } + return &MuffinDatabase{ + Client: client, + Learns: client.Database(configs.Config.DBName).Collection("learn"), + Texts: client.Database(configs.Config.DBName).Collection("text"), + }, nil +} diff --git a/handler/messageCreate.go b/handler/messageCreate.go index d9d98bb..6832252 100644 --- a/handler/messageCreate.go +++ b/handler/messageCreate.go @@ -60,7 +60,7 @@ func MessageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { command := commands.Discommand.Aliases[args[0]] if m.Author.ID == config.Train.UserID { - if _, err := databases.Texts.InsertOne(context.TODO(), databases.InsertText{ + if _, err := databases.Database.Texts.InsertOne(context.TODO(), databases.InsertText{ Text: content, Persona: "muffin", CreatedAt: time.Now(), @@ -83,7 +83,7 @@ func MessageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { if channel.NSFW { filter = bson.D{{}} - if _, err := databases.Texts.InsertOne(context.TODO(), databases.InsertText{ + if _, err := databases.Database.Texts.InsertOne(context.TODO(), databases.InsertText{ Text: content, Persona: fmt.Sprintf("user:%s", m.Author.Username), CreatedAt: time.Now(), @@ -96,7 +96,7 @@ func MessageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { } go func() { - cur, err := databases.Texts.Find(context.TODO(), filter) + cur, err := databases.Database.Texts.Find(context.TODO(), filter) if err != nil { log.Fatalln(err) } @@ -107,7 +107,7 @@ func MessageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { ch <- 1 }() go func() { - cur, err := databases.Learns.Find(context.TODO(), bson.D{{Key: "command", Value: content}}) + cur, err := databases.Database.Learns.Find(context.TODO(), bson.D{{Key: "command", Value: content}}) if err != nil { if err == mongo.ErrNilDocument { learnData = []databases.Learn{} diff --git a/main.go b/main.go index e53f1f5..0ce9876 100644 --- a/main.go +++ b/main.go @@ -108,7 +108,7 @@ func main() { go dg.ApplicationCommandCreate(dg.State.User.ID, "", cmd.ApplicationCommand) } - defer databases.Client.Disconnect(context.TODO()) + defer databases.Database.Client.Disconnect(context.TODO()) log.Println("[goMuffin] 봇이 실행되고 있어요. 버전:", configs.MUFFIN_VERSION) sc := make(chan os.Signal, 1) diff --git a/scripts/export.go b/scripts/export.go index c8269c6..24c26b0 100644 --- a/scripts/export.go +++ b/scripts/export.go @@ -10,14 +10,11 @@ import ( "sync" "time" - "git.wh64.net/muffin/goMuffin/configs" "git.wh64.net/muffin/goMuffin/databases" "git.wh64.net/muffin/goMuffin/utils" "github.com/devproje/commando" "github.com/devproje/commando/option" "go.mongodb.org/mongo-driver/v2/bson" - "go.mongodb.org/mongo-driver/v2/mongo" - "go.mongodb.org/mongo-driver/v2/mongo/options" ) var date time.Time = time.Now() @@ -130,10 +127,11 @@ func saveFileToJSONL(path, name string, data any) error { } func ExportData(n *commando.Node) error { + defer databases.Database.Client.Disconnect(context.TODO()) + var wg sync.WaitGroup ch := make(chan error, 3) - databases.Client.Disconnect(context.TODO()) // databases 패키지의 DB 연결은 필요 없음 (나중에 수정 예정) fileType, err := option.ParseString(*n.MustGetOpt("type"), n) if err != nil { return err @@ -172,15 +170,7 @@ func ExportData(n *commando.Node) error { var data []databases.Text - conn, err := mongo.Connect(options.Client().ApplyURI(configs.Config.DatabaseURL)) - if err != nil { - ch <- err - return - } - - defer conn.Disconnect(context.TODO()) - - cur, err := conn.Database(configs.Config.DBName).Collection("text").Find(context.TODO(), bson.D{{Key: "persona", Value: "muffin"}}) + cur, err := databases.Database.Texts.Find(context.TODO(), bson.D{{Key: "persona", Value: "muffin"}}) if err != nil { ch <- err return @@ -234,15 +224,7 @@ func ExportData(n *commando.Node) error { var data []databases.Text - conn, err := mongo.Connect(options.Client().ApplyURI(configs.Config.DatabaseURL)) - if err != nil { - ch <- err - return - } - - defer conn.Disconnect(context.TODO()) - - cur, err := conn.Database(configs.Config.DBName).Collection("text").Find(context.TODO(), bson.D{ + cur, err := databases.Database.Texts.Find(context.TODO(), bson.D{ { Key: "persona", Value: bson.M{ @@ -292,15 +274,7 @@ func ExportData(n *commando.Node) error { var data []databases.Learn - conn, err := mongo.Connect(options.Client().ApplyURI(configs.Config.DatabaseURL)) - if err != nil { - ch <- err - return - } - - defer conn.Disconnect(context.TODO()) - - cur, err := conn.Database(configs.Config.DBName).Collection("learn").Find(context.TODO(), bson.D{{}}) + cur, err := databases.Database.Learns.Find(context.TODO(), bson.D{{}}) if err != nil { ch <- err return From 5f881b4ec5a8f7e3ab1da9f6062fb9ea96cedcd9 Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Sun, 11 May 2025 13:51:26 +0900 Subject: [PATCH 14/34] chore: edit config struct --- configs/config.go | 24 +++++++++++------------- databases/database.go | 4 ++-- scripts/dbMigrate.go | 2 +- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/configs/config.go b/configs/config.go index a10ecef..3dc46d5 100644 --- a/configs/config.go +++ b/configs/config.go @@ -20,24 +20,24 @@ type trainConfig struct { // MuffinConfig for Muffin bot type MuffinConfig struct { - Bot botConfig - Train trainConfig - DatabaseURL string - DBName string + Bot botConfig + Train trainConfig + DatabaseURL string + DatabaseName string } -func loadConfig() *MuffinConfig { - godotenv.Load() - config := &MuffinConfig{Bot: botConfig{}, Train: trainConfig{}} - setConfig(config) +var Config *MuffinConfig - return config +func init() { + godotenv.Load() + Config = &MuffinConfig{Bot: botConfig{}, Train: trainConfig{}} + setConfig(Config) } func getRequiredValue(key string) string { value := os.Getenv(key) if value == "" { - log.Fatalln(fmt.Sprintf("[goMuffin] .env 파일에서 필요한 %s값이 없어요.", key)) + log.Fatalln(fmt.Sprintf("[goMuffin] .env 파일에서 필요한 '%s'값이 없어요.", key)) } return value } @@ -54,7 +54,5 @@ func setConfig(config *MuffinConfig) { config.Train.UserID = getValue("TRAIN_USER_ID") config.DatabaseURL = getRequiredValue("DATABASE_URL") - config.DBName = getRequiredValue("DATABASE_NAME") + config.DatabaseName = getRequiredValue("DATABASE_NAME") } - -var Config *MuffinConfig = loadConfig() diff --git a/databases/database.go b/databases/database.go index 7928bdc..26a6607 100644 --- a/databases/database.go +++ b/databases/database.go @@ -32,7 +32,7 @@ func Connect() (*MuffinDatabase, error) { } return &MuffinDatabase{ Client: client, - Learns: client.Database(configs.Config.DBName).Collection("learn"), - Texts: client.Database(configs.Config.DBName).Collection("text"), + Learns: client.Database(configs.Config.DatabaseName).Collection("learn"), + Texts: client.Database(configs.Config.DatabaseName).Collection("text"), }, nil } diff --git a/scripts/dbMigrate.go b/scripts/dbMigrate.go index 60f8192..f06569c 100644 --- a/scripts/dbMigrate.go +++ b/scripts/dbMigrate.go @@ -23,7 +23,7 @@ var wg sync.WaitGroup func DBMigrate(n *commando.Node) error { mariaURL := os.Getenv("PREVIOUS_DATABASE_URL") mongoURL := configs.Config.DatabaseURL - dbName := configs.Config.DBName + dbName := configs.Config.DatabaseName dbConnectionQuery := "?parseTime=true" From c56e2818720986efca260709671b7ecfdd990c06 Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Sun, 11 May 2025 13:57:36 +0900 Subject: [PATCH 15/34] chore: edit discommand struct --- commands/deleteLearnedData.go | 2 +- commands/discommand.go | 4 ++-- commands/help.go | 2 +- commands/learn.go | 2 +- commands/learnedDataList.go | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/commands/deleteLearnedData.go b/commands/deleteLearnedData.go index aaa31e0..13eaeb4 100644 --- a/commands/deleteLearnedData.go +++ b/commands/deleteLearnedData.go @@ -31,7 +31,7 @@ var DeleteLearnedDataCommand *Command = &Command{ }, Category: Chatting, MessageRun: func(ctx *MsgContext) { - deleteLearnedDataRun(ctx.Command, ctx.Session, ctx.Msg, &ctx.Args) + deleteLearnedDataRun(ctx.Command, ctx.Session, ctx.Msg, ctx.Args) }, ChatInputRun: func(ctx *ChatInputContext) { deleteLearnedDataRun(ctx.Command, ctx.Session, ctx.Inter, nil) diff --git a/commands/discommand.go b/commands/discommand.go index 3221306..6d627d2 100644 --- a/commands/discommand.go +++ b/commands/discommand.go @@ -37,7 +37,7 @@ type DiscommandStruct struct { type MsgContext struct { Session *discordgo.Session Msg *discordgo.MessageCreate - Args []string + Args *[]string Command *Command } @@ -94,7 +94,7 @@ func (d *DiscommandStruct) LoadComponent(c *Component) { func (d *DiscommandStruct) MessageRun(name string, s *discordgo.Session, m *discordgo.MessageCreate, args []string) { if command, ok := d.Commands[name]; ok { - command.MessageRun(&MsgContext{s, m, args, command}) + command.MessageRun(&MsgContext{s, m, &args, command}) } } diff --git a/commands/help.go b/commands/help.go index 307a839..7de4052 100644 --- a/commands/help.go +++ b/commands/help.go @@ -30,7 +30,7 @@ var HelpCommand *Command = &Command{ }, Category: General, MessageRun: func(ctx *MsgContext) { - helpRun(ctx.Session, ctx.Msg, &ctx.Args) + helpRun(ctx.Session, ctx.Msg, ctx.Args) }, ChatInputRun: func(ctx *ChatInputContext) { helpRun(ctx.Session, ctx.Inter, nil) diff --git a/commands/learn.go b/commands/learn.go index be0fe30..d3bf3bc 100644 --- a/commands/learn.go +++ b/commands/learn.go @@ -57,7 +57,7 @@ var LearnCommand *Command = &Command{ }, Category: Chatting, MessageRun: func(ctx *MsgContext) { - learnRun(ctx.Command, ctx.Session, ctx.Msg, &ctx.Args) + learnRun(ctx.Command, ctx.Session, ctx.Msg, ctx.Args) }, ChatInputRun: func(ctx *ChatInputContext) { learnRun(ctx.Command, ctx.Session, ctx.Inter, nil) diff --git a/commands/learnedDataList.go b/commands/learnedDataList.go index dc7b486..ccd9333 100644 --- a/commands/learnedDataList.go +++ b/commands/learnedDataList.go @@ -42,7 +42,7 @@ var LearnedDataListCommand *Command = &Command{ }, Category: Chatting, MessageRun: func(ctx *MsgContext) { - learnedDataListRun(ctx.Session, ctx.Msg, &ctx.Args) + learnedDataListRun(ctx.Session, ctx.Msg, ctx.Args) }, ChatInputRun: func(ctx *ChatInputContext) { learnedDataListRun(ctx.Session, ctx.Inter, nil) From 1b715daf8fcd5bb2970be806e1f14a1f7bcc5b63 Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Sun, 11 May 2025 14:06:04 +0900 Subject: [PATCH 16/34] chore: edit error handle --- main.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index 0ce9876..e526d86 100644 --- a/main.go +++ b/main.go @@ -65,11 +65,7 @@ func main() { return } - dg, err := discordgo.New("Bot " + config.Bot.Token) - if err != nil { - log.Println("[goMuffin] 봇의 세션을 만들수가 없어요.") - log.Fatalln(err) - } + dg, _ := discordgo.New("Bot " + config.Bot.Token) go commands.Discommand.LoadCommand(commands.HelpCommand) go commands.Discommand.LoadCommand(commands.DataLengthCommand) @@ -83,9 +79,15 @@ func main() { go dg.AddHandler(handler.MessageCreate) go dg.AddHandler(handler.InteractionCreate) - dg.Open() + err := dg.Open() + if err != nil { + log.Println("[goMuffin] 봇을 시작할 수 없어요.") + log.Fatalln(err) + } + defer dg.Close() + // 봇의 상태메세지 변경 go func() { for { dg.UpdateCustomStatus("ㅅ살려주세요..!") From b2b45866bc2724362138ced6df98763785f99a39 Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Sun, 11 May 2025 18:20:12 +0900 Subject: [PATCH 17/34] feat: add connect Non-URL --- .env.example | 19 +++++++++++++--- .vscode/settings.json | 3 +++ configs/config.go | 52 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.env.example b/.env.example index 093837c..7652c7a 100644 --- a/.env.example +++ b/.env.example @@ -1,11 +1,24 @@ +# Database (URL 형식과 Non-URL 형식 중 하나를 선택) +## URL 형식 DATABASE_URL= + +## Non-URL 형식 +DATABASE_USERNAME= +DATABASE_PASSWORD= +DATABASE_HOSTNAME= # 해당 값은 도커로 실행할 때 필요하지 않음. +DATABASE_PORT= +DATABASE_AUTH_SOURCE= # 기본 값: admin + +## 필수 값 DATABASE_NAME= +## 데이터베이스 마이그레이션용 +PREVIOUS_DATABASE_URL= + +# 봇 BOT_TOKEN= BOT_PREFIX= BOT_OWNER_ID= -# If you need -PREVIOUS_DATABASE_URL= - +# 학습 (필수 아님) TRAIN_USER_ID= \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..082b194 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "makefile.configureOnOpen": false +} \ No newline at end of file diff --git a/configs/config.go b/configs/config.go index 3dc46d5..a1499dc 100644 --- a/configs/config.go +++ b/configs/config.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "os" + "strconv" "github.com/joho/godotenv" ) @@ -18,11 +19,26 @@ type trainConfig struct { UserID string } +type databaseConfig struct { + Name string + URL string + HostName string + Username string + Password string + AuthSource string + Port int +} + // MuffinConfig for Muffin bot type MuffinConfig struct { - Bot botConfig - Train trainConfig - DatabaseURL string + Bot botConfig + Train trainConfig + Database databaseConfig + + // Deprecated: Use Database.URL + DatabaseURL string + + // Deprecated: Use Database.Name DatabaseName string } @@ -30,7 +46,7 @@ var Config *MuffinConfig func init() { godotenv.Load() - Config = &MuffinConfig{Bot: botConfig{}, Train: trainConfig{}} + Config = &MuffinConfig{Bot: botConfig{}, Train: trainConfig{}, Database: databaseConfig{}} setConfig(Config) } @@ -53,6 +69,30 @@ func setConfig(config *MuffinConfig) { config.Train.UserID = getValue("TRAIN_USER_ID") - config.DatabaseURL = getRequiredValue("DATABASE_URL") - config.DatabaseName = getRequiredValue("DATABASE_NAME") + config.Database.URL = getValue("DATABASE_URL") + config.Database.HostName = getValue("DATABASE_HOSTNAME") + config.Database.Password = getValue("DATABASE_PASSWORD") + config.Database.Username = getValue("DATABASE_USERNAME") + config.Database.AuthSource = getValue("DATABASE_AUTH_SOURCE") + config.Database.Name = getRequiredValue("DATABASE_NAME") + port, err := strconv.Atoi(getValue("DATABASE_PORT")) + if err != nil { + log.Println("[goMuffin] 'DATABASE_PORT'값을 int로 파싱할 수 없어요.") + log.Fatalln(err) + } + + config.Database.Port = port + + if config.Database.AuthSource == "" { + config.Database.AuthSource = "admin" + } + + if config.Database.URL == "" { + config.Database.URL = fmt.Sprintf("mongodb://%s:%s@%s:%d/?authSource=%s", config.Database.Username, config.Database.Password, config.Database.HostName, config.Database.Port, config.Database.AuthSource) + } + fmt.Println(config.Database.URL) + + // Deprecated된 Value + config.DatabaseURL = config.Database.URL + config.DatabaseName = config.Database.Name } From 8a39830712152a325b715d1c88a8ca14927c716f Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Sun, 11 May 2025 18:20:34 +0900 Subject: [PATCH 18/34] feat: docker database --- .dockerignore | 1 - .gitignore | 3 ++- Dockerfile | 4 +++- compose.yml | 18 +++++++++++++++++- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/.dockerignore b/.dockerignore index cde229b..10f229a 100644 --- a/.dockerignore +++ b/.dockerignore @@ -7,4 +7,3 @@ README.md update.sh compose.yml Dockerfile -script/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index 9c95d85..a61dd0c 100644 --- a/.gitignore +++ b/.gitignore @@ -126,4 +126,5 @@ $RECYCLE.BIN/ *.env !.env.example -export/ \ No newline at end of file +export/ +data/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index bcb44f5..47da25d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,6 @@ -FROM golang:1.24.2 +FROM golang:1.24.3 + +ENV DATABASE_NAME=muffin_ai RUN mkdir /app WORKDIR /app diff --git a/compose.yml b/compose.yml index 45aaf66..074d58a 100644 --- a/compose.yml +++ b/compose.yml @@ -5,4 +5,20 @@ services: env_file: - "./.env" volumes: - - "/etc/localtime:/etc/localtime" \ No newline at end of file + - "/etc/localtime:/etc/localtime" + depends_on: + - database + environment: + - "DATABASE_HOSTNAME=database" + database: + container_name: "goMuffin_database" + image: "mongo:7.0.17" + ports: + - "${DATABASE_PORT}:27017" + volumes: + - "./data:/data/db" + - "/etc/localtime:/etc/localtime" + environment: + - "MONGO_INITDB_ROOT_USERNAME=${DATABASE_USERNAME}" + - "MONGO_INITDB_ROOT_PASSWORD=${DATABASE_PASSWORD}" + \ No newline at end of file From e3722101b13f10922f974790f855ebcdbc2dbaec Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Mon, 12 May 2025 19:30:14 +0900 Subject: [PATCH 19/34] fix: remove db url print --- configs/config.go | 1 - 1 file changed, 1 deletion(-) diff --git a/configs/config.go b/configs/config.go index a1499dc..0e51efe 100644 --- a/configs/config.go +++ b/configs/config.go @@ -90,7 +90,6 @@ func setConfig(config *MuffinConfig) { if config.Database.URL == "" { config.Database.URL = fmt.Sprintf("mongodb://%s:%s@%s:%d/?authSource=%s", config.Database.Username, config.Database.Password, config.Database.HostName, config.Database.Port, config.Database.AuthSource) } - fmt.Println(config.Database.URL) // Deprecated된 Value config.DatabaseURL = config.Database.URL From 351bf86908a9a71a738033b5f9f29786fa3286be Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Mon, 12 May 2025 19:34:56 +0900 Subject: [PATCH 20/34] feat: add length limit in learnedDataList command --- commands/learnedDataList.go | 15 ++++++++++++++- configs/version.go | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/commands/learnedDataList.go b/commands/learnedDataList.go index ccd9333..3601713 100644 --- a/commands/learnedDataList.go +++ b/commands/learnedDataList.go @@ -50,8 +50,21 @@ var LearnedDataListCommand *Command = &Command{ } func getDescriptions(data *[]databases.Learn) (descriptions []string) { + MAX_LENGTH := 100 + for _, data := range *data { - descriptions = append(descriptions, fmt.Sprintf("- %s: %s", data.Command, data.Result)) + command := data.Command + result := data.Result + + if runeCommand := []rune(command); len(runeCommand) >= MAX_LENGTH { + command = string(runeCommand)[:MAX_LENGTH] + "..." + } + + if runeResult := []rune(result); len(runeResult) >= MAX_LENGTH { + result = string(runeResult[:MAX_LENGTH]) + "..." + } + + descriptions = append(descriptions, fmt.Sprintf("- %s: %s", command, result)) } return } diff --git a/configs/version.go b/configs/version.go index 6df6301..69b38c7 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.250510a" +const MUFFIN_VERSION = "5.1.0-gopher_dev.250512a" var updatedString string = utils.Decimals.FindAllStringSubmatch(MUFFIN_VERSION, -1)[3][0] From 7cf40088348cecaba9f17517915a5c8257534acf Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Tue, 13 May 2025 19:23:06 +0900 Subject: [PATCH 21/34] 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 22/34] 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 23/34] 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), From 0a8e1fd6735460be63f581e5c8e2b54a72d81156 Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Wed, 14 May 2025 22:05:32 +0900 Subject: [PATCH 24/34] chore: edit percentage of learned data & muffin data --- configs/version.go | 2 +- handler/messageCreate.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/version.go b/configs/version.go index 93d3fb7..aee267f 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.250513b-paginated_embed" +const MUFFIN_VERSION = "5.1.0-gopher_dev.250514a" var updatedString string = utils.Decimals.FindAllStringSubmatch(MUFFIN_VERSION, -1)[3][0] diff --git a/handler/messageCreate.go b/handler/messageCreate.go index 6832252..55f8d36 100644 --- a/handler/messageCreate.go +++ b/handler/messageCreate.go @@ -77,7 +77,7 @@ func MessageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { var filter bson.D ch := make(chan int) - x := rand.Intn(5) + x := rand.Intn(10) channel, _ := s.Channel(m.ChannelID) if channel.NSFW { @@ -126,7 +126,7 @@ func MessageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { } close(ch) - if x > 2 && len(learnData) != 0 { + if x > 6 && len(learnData) != 0 { data := learnData[rand.Intn(len(learnData))] user, _ := s.User(data.UserId) result := resultParser(data.Result, s, m) From 2d0e3e5b18f216d3c9fb8f7d3ce68d29d2d88ee4 Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Wed, 14 May 2025 22:11:31 +0900 Subject: [PATCH 25/34] chore: regexp --- utils/regexp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/regexp.go b/utils/regexp.go index 0e920b8..da7774c 100644 --- a/utils/regexp.go +++ b/utils/regexp.go @@ -3,7 +3,7 @@ package utils import "regexp" var ( - FlexibleStringParser = regexp.MustCompile("[^\\s\"'「」«»]+|\"([^\"]*)\"|'([^']*)'|「([^」]*)」|«([^»]*)»") + FlexibleStringParser = regexp.MustCompile(`[^\s"'「」«»]+|"([^"]*)"|'([^']*)'|「([^」]*)」|«([^»]*)»`) Decimals = regexp.MustCompile(`\d+`) ItemIdRegexp = regexp.MustCompile(`No.\d+`) EmojiRegexp = regexp.MustCompile(``) From e9f6929ee59d14f90d6a9f28236b73eb4783af6d Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Wed, 14 May 2025 22:17:14 +0900 Subject: [PATCH 26/34] fix: percentage --- configs/version.go | 2 +- handler/messageCreate.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/version.go b/configs/version.go index aee267f..3b04fbd 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.250514a" +const MUFFIN_VERSION = "5.1.0-gopher_dev.250514b" var updatedString string = utils.Decimals.FindAllStringSubmatch(MUFFIN_VERSION, -1)[3][0] diff --git a/handler/messageCreate.go b/handler/messageCreate.go index 55f8d36..1a25e92 100644 --- a/handler/messageCreate.go +++ b/handler/messageCreate.go @@ -126,7 +126,7 @@ func MessageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { } close(ch) - if x > 6 && len(learnData) != 0 { + if x > 2 && len(learnData) != 0 { data := learnData[rand.Intn(len(learnData))] user, _ := s.User(data.UserId) result := resultParser(data.Result, s, m) From c3955f213bee7157851be205238dc3314f563570 Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Fri, 16 May 2025 18:46:28 +0900 Subject: [PATCH 27/34] fix: pagination embed in chatInput, list query --- commands/learnedDataList.go | 129 +++++++++++++++++++++++------------- configs/version.go | 4 +- handler/messageCreate.go | 4 +- scripts/export.go | 2 +- utils/customIds.go | 6 +- utils/paginationEmbed.go | 16 +++-- utils/regexp.go | 15 +++-- 7 files changed, 108 insertions(+), 68 deletions(-) diff --git a/commands/learnedDataList.go b/commands/learnedDataList.go index 7099822..e925f81 100644 --- a/commands/learnedDataList.go +++ b/commands/learnedDataList.go @@ -3,6 +3,7 @@ package commands import ( "context" "fmt" + "strconv" "strings" "git.wh64.net/muffin/goMuffin/configs" @@ -13,9 +14,9 @@ import ( "go.mongodb.org/mongo-driver/v2/mongo" ) -const ( - learnArgsCommand = "단어:" - learnArgsResult = "대답:" +var ( + LIST_MIN_VALUE float64 = 10.0 + LIST_MAX_VALUE float64 = 100.0 ) var LearnedDataListCommand *Command = &Command{ @@ -25,10 +26,25 @@ var LearnedDataListCommand *Command = &Command{ Description: "당신이 가ㄹ르쳐준 지식을 나열해요.", Options: []*discordgo.ApplicationCommandOption{ { - Name: "쿼리", + Type: discordgo.ApplicationCommandOptionString, + Name: "단어", Description: "해당 단어가 포함된 결과만 찾아요.", Required: false, }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "대답", + Description: "해당 대답이 포함된 결과만 찾아요.", + Required: false, + }, + { + Type: discordgo.ApplicationCommandOptionInteger, + Name: "개수", + Description: "한 페이지당 보여줄 지식의 데이터 양을 정해요.", + MinValue: &LIST_MIN_VALUE, + MaxValue: LIST_MAX_VALUE, + Required: false, + }, }, }, Aliases: []string{"list", "목록", "지식목록"}, @@ -49,9 +65,13 @@ var LearnedDataListCommand *Command = &Command{ }, } -func getDescriptions(data *[]databases.Learn) (descriptions []string) { +func getDescriptions(data *[]databases.Learn, length int) (descriptions []string) { + var builder strings.Builder MAX_LENGTH := 100 - MAX_ITEM_LENGTH := 25 + + if length == 0 { + length = 25 + } tempDesc := []string{} @@ -70,12 +90,10 @@ func getDescriptions(data *[]databases.Learn) (descriptions []string) { 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 { + if (i+1)%length == 0 { descriptions = append(descriptions, builder.String()) builder.Reset() } @@ -88,65 +106,84 @@ func getDescriptions(data *[]databases.Learn) (descriptions []string) { } func learnedDataListRun(s *discordgo.Session, m any, args *[]string) { - var userId, globalName, avatarUrl string + var globalName, avatarUrl string var data []databases.Learn - var filter bson.E + var filter bson.D + var length int switch m := m.(type) { case *discordgo.MessageCreate: - userId = m.Author.ID + filter = bson.D{{Key: "user_id", Value: m.Author.ID}} globalName = m.Author.GlobalName avatarUrl = m.Author.AvatarURL("512") query := strings.Join(*args, " ") - if strings.HasPrefix(query, learnArgsResult) { - query, _ = strings.CutPrefix(query, learnArgsResult) - filter = bson.E{ - Key: "result", - Value: bson.M{ - "$regex": query, - }, - } - } else { - query, _ = strings.CutPrefix(query, learnArgsCommand) - filter = bson.E{ + + if match := utils.RegexpLearnQueryCommand.FindStringSubmatch(query); match != nil { + filter = append(filter, bson.E{ Key: "command", Value: bson.M{ - "$regex": query, + "$regex": match[1], }, + }) + } + + if match := utils.RegexpLearnQueryResult.FindStringSubmatch(query); match != nil { + fmt.Println(match[1]) + filter = append(filter, bson.E{ + Key: "result", + Value: bson.M{ + "$regex": match[1], + }, + }) + } + + if match := utils.RegexpLearnQueryLength.FindStringSubmatch(query); match != nil { + fmt.Println(1) + var err error + length, err = strconv.Atoi(match[1]) + fmt.Printf("err: %v\n", err) + + if err != nil { + s.ChannelMessageSendEmbedReply(m.ChannelID, &discordgo.MessageEmbed{ + Title: "❌ 오류", + Description: "개수의 값은 숫자여야해요.", + Color: utils.EmbedFail, + }, m.Reference()) + return } } case *utils.InteractionCreate: m.DeferReply(true) - userId = m.Member.User.ID + filter = bson.D{{Key: "user_id", Value: m.Member.User.ID}} globalName = m.Member.User.GlobalName avatarUrl = m.Member.User.AvatarURL("512") - if opt, ok := m.Options["쿼리"]; ok { - query := opt.StringValue() + if opt, ok := m.Options["단어"]; ok { + filter = append(filter, bson.E{ + Key: "command", + Value: bson.M{ + "$regex": opt.StringValue(), + }, + }) + } - if strings.HasPrefix(query, learnArgsResult) { - query, _ = strings.CutPrefix(query, learnArgsResult) - filter = bson.E{ - Key: "result", - Value: bson.M{ - "$regex": query, - }, - } - } else { - query, _ = strings.CutPrefix(query, learnArgsCommand) - filter = bson.E{ - Key: "command", - Value: bson.M{ - "$regex": query, - }, - } - } + if opt, ok := m.Options["대답"]; ok { + filter = append(filter, bson.E{ + Key: "result", + Value: bson.M{ + "$regex": opt.StringValue(), + }, + }) + } + + if opt, ok := m.Options["개수"]; ok { + length = int(opt.IntValue()) } } - cur, err := databases.Database.Learns.Find(context.TODO(), bson.D{{Key: "user_id", Value: userId}, filter}) + cur, err := databases.Database.Learns.Find(context.TODO(), filter) if err != nil { if err == mongo.ErrNoDocuments { embed := &discordgo.MessageEmbed{ @@ -196,5 +233,5 @@ func learnedDataListRun(s *discordgo.Session, m any, args *[]string) { }, } - utils.StartPaginationEmbed(s, m, embed, getDescriptions(&data), utils.CodeBlock("md", fmt.Sprintf("# 총 %d개에요.\n", len(data))+"%s")) + utils.StartPaginationEmbed(s, m, embed, getDescriptions(&data, length), utils.CodeBlock("md", fmt.Sprintf("# 총 %d개에요.\n", len(data))+"%s")) } diff --git a/configs/version.go b/configs/version.go index 3b04fbd..389ea8c 100644 --- a/configs/version.go +++ b/configs/version.go @@ -7,9 +7,9 @@ import ( "git.wh64.net/muffin/goMuffin/utils" ) -const MUFFIN_VERSION = "5.1.0-gopher_dev.250514b" +const MUFFIN_VERSION = "5.1.0-gopher_dev.250516a" -var updatedString string = utils.Decimals.FindAllStringSubmatch(MUFFIN_VERSION, -1)[3][0] +var updatedString string = utils.RegexpDecimals.FindAllStringSubmatch(MUFFIN_VERSION, -1)[3][0] var UpdatedAt *time.Time = func() *time.Time { year, _ := strconv.Atoi("20" + updatedString[0:2]) diff --git a/handler/messageCreate.go b/handler/messageCreate.go index 1a25e92..99fda87 100644 --- a/handler/messageCreate.go +++ b/handler/messageCreate.go @@ -18,7 +18,7 @@ import ( ) func argParser(content string) (args []string) { - for _, arg := range utils.FlexibleStringParser.FindAllStringSubmatch(content, -1) { + for _, arg := range utils.RegexpFlexibleString.FindAllStringSubmatch(content, -1) { if arg[1] != "" { args = append(args, arg[1]) } else { @@ -131,7 +131,6 @@ func MessageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { user, _ := s.User(data.UserId) result := resultParser(data.Result, s, m) - // s.ChannelMessageSendReply(m.ChannelID, fmt.Sprintf("%s\n%s", result, utils.InlineCode(fmt.Sprintf("%s님이 알려주셨어요.", user.Username))), m.Reference()) s.ChannelMessageSendComplex(m.ChannelID, &discordgo.MessageSend{ Reference: m.Reference(), Content: fmt.Sprintf("%s\n%s", result, utils.InlineCode(fmt.Sprintf("%s님이 알려주셨어요.", user.Username))), @@ -144,7 +143,6 @@ func MessageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { return } - // s.ChannelMessageSendReply(m.ChannelID, data[rand.Intn(len(data))].Text, m.Reference()) s.ChannelMessageSendComplex(m.ChannelID, &discordgo.MessageSend{ Reference: m.Reference(), Content: data[rand.Intn(len(data))].Text, diff --git a/scripts/export.go b/scripts/export.go index 24c26b0..3fb2b33 100644 --- a/scripts/export.go +++ b/scripts/export.go @@ -186,7 +186,7 @@ func ExportData(n *commando.Node) error { if refined { for i, text := range data { - if utils.EmojiRegexp.Match([]byte(text.Text)) { + if utils.RegexpEmoji.Match([]byte(text.Text)) { data = append(data[:i], data[i+1:]...) return } diff --git a/utils/customIds.go b/utils/customIds.go index 0c3f2ac..27b160c 100644 --- a/utils/customIds.go +++ b/utils/customIds.go @@ -31,8 +31,8 @@ func MakeDeleteLearnedDataCancel(id string) string { } 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.", "") + id, _ = bson.ObjectIDFromHex(strings.ReplaceAll(RegexpItemId.ReplaceAllString(customId[len(DeleteLearnedData):], ""), "&", "")) + stringItemId := strings.ReplaceAll(RegexpItemId.FindAllString(customId, 1)[0], "No.", "") itemId, _ = strconv.Atoi(stringItemId) return } @@ -68,5 +68,5 @@ func GetPaginationEmbedId(customId string) string { } func GetPaginationEmbedUserId(id string) string { - return PaginationEmbedId.FindAllStringSubmatch(id, 1)[0][1] + return RegexpPaginationEmbedId.FindAllStringSubmatch(id, 1)[0][1] } diff --git a/utils/paginationEmbed.go b/utils/paginationEmbed.go index cdab278..9231ae4 100644 --- a/utils/paginationEmbed.go +++ b/utils/paginationEmbed.go @@ -21,7 +21,6 @@ type PaginationEmbed struct { var PaginationEmbeds = make(map[string]*PaginationEmbed) func makeComponents(id string, current, total int) *[]discordgo.MessageComponent { - return &[]discordgo.MessageComponent{ discordgo.ActionsRow{ Components: []discordgo.MessageComponent{ @@ -81,19 +80,24 @@ func StartPaginationEmbed(s *discordgo.Session, m any, e *discordgo.MessageEmbed desc: defaultDesc, } - p.Embed.Description = makeDesc(p.desc, data[0]) + if len(data) <= 0 { + p.Embed.Description = makeDesc(p.desc, "없음") + p.Total = 1 + } else { + p.Embed.Description = makeDesc(p.desc, 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)), + Components: *makeComponents(id, p.Current, p.Total), }) case *InteractionCreate: - m.Reply(&discordgo.InteractionResponseData{ - Embeds: []*discordgo.MessageEmbed{p.Embed}, - Components: *makeComponents(id, 1, len(data)), + m.EditReply(&discordgo.WebhookEdit{ + Embeds: &[]*discordgo.MessageEmbed{p.Embed}, + Components: makeComponents(id, p.Current, p.Total), }) } diff --git a/utils/regexp.go b/utils/regexp.go index da7774c..a5c7e0f 100644 --- a/utils/regexp.go +++ b/utils/regexp.go @@ -3,11 +3,12 @@ package utils import "regexp" var ( - FlexibleStringParser = regexp.MustCompile(`[^\s"'「」«»]+|"([^"]*)"|'([^']*)'|「([^」]*)」|«([^»]*)»`) - Decimals = regexp.MustCompile(`\d+`) - ItemIdRegexp = regexp.MustCompile(`No.\d+`) - EmojiRegexp = regexp.MustCompile(``) - LearnQueryCommand = regexp.MustCompile(`^단어:`) - LearnQueryResult = regexp.MustCompile(`^대답:`) - PaginationEmbedId = regexp.MustCompile(`^(\d+)/(\d+)$`) + RegexpFlexibleString = regexp.MustCompile(`[^\s"'「」«»]+|"([^"]*)"|'([^']*)'|「([^」]*)」|«([^»]*)»`) + RegexpDecimals = regexp.MustCompile(`\d+`) + RegexpItemId = regexp.MustCompile(`No.\d+`) + RegexpEmoji = regexp.MustCompile(``) + RegexpLearnQueryCommand = regexp.MustCompile(`단어:([^\n대답개수:]*)`) + RegexpLearnQueryResult = regexp.MustCompile(`대답:([^\n단어개수:]*)`) + RegexpLearnQueryLength = regexp.MustCompile(`개수:(\d+)`) + RegexpPaginationEmbedId = regexp.MustCompile(`^(\d+)/(\d+)$`) ) From 2e90e14116817e06226b29be4f08c4a8a1480a95 Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Sat, 17 May 2025 11:33:43 +0900 Subject: [PATCH 28/34] feat: set page in list --- commands/discommand.go | 62 ++++++++++++++++++++++++---- commands/learnedDataList.go | 2 - components/paginationEmbed.go | 7 ++-- configs/version.go | 2 +- handler/interactionCreate.go | 2 + main.go | 3 ++ modals/paginationEmbed.go | 66 ++++++++++++++++++++++++++++++ utils/customIds.go | 33 +++++++++++---- utils/interactions.go | 57 +++++++++++++++++++++++++- utils/paginationEmbed.go | 76 +++++++++++++++++++++++++++++++---- 10 files changed, 281 insertions(+), 29 deletions(-) create mode 100644 modals/paginationEmbed.go diff --git a/commands/discommand.go b/commands/discommand.go index 6d627d2..53bafba 100644 --- a/commands/discommand.go +++ b/commands/discommand.go @@ -7,9 +7,12 @@ import ( "github.com/bwmarrin/discordgo" ) +type modalRun func(ctx *ModalContext) type messageRun func(ctx *MsgContext) type chatInputRun func(ctx *ChatInputContext) type componentRun func(ctx *ComponentContext) + +type modalParse func(ctx *ModalContext) bool type componentParse func(ctx *ComponentContext) bool type Category string @@ -32,6 +35,7 @@ type DiscommandStruct struct { Commands map[string]*Command Components []*Component Aliases map[string]string + Modals []*Modal } type MsgContext struct { @@ -53,24 +57,38 @@ type ComponentContext struct { Component *Component } +type ModalContext struct { + Inter *utils.InteractionCreate + Modal *Modal +} + type Component struct { Parse componentParse Run componentRun } +type Modal struct { + Parse modalParse + Run modalRun +} + const ( Chatting Category = "채팅" General Category = "일반" ) -var commandMutex sync.Mutex -var componentMutex sync.Mutex +var ( + commandMutex sync.Mutex + componentMutex sync.Mutex + modalMutex sync.Mutex +) func new() *DiscommandStruct { discommand := DiscommandStruct{ Commands: map[string]*Command{}, Aliases: map[string]string{}, Components: []*Component{}, + Modals: []*Modal{}, } return &discommand } @@ -92,6 +110,12 @@ func (d *DiscommandStruct) LoadComponent(c *Component) { d.Components = append(d.Components, c) } +func (d *DiscommandStruct) LoadModal(m *Modal) { + defer modalMutex.Unlock() + modalMutex.Lock() + d.Modals = append(d.Modals, m) +} + func (d *DiscommandStruct) MessageRun(name string, s *discordgo.Session, m *discordgo.MessageCreate, args []string) { if command, ok := d.Commands[name]; ok { command.MessageRun(&MsgContext{s, m, &args, command}) @@ -109,18 +133,42 @@ func (d *DiscommandStruct) ChatInputRun(name string, s *discordgo.Session, i *di } func (d *DiscommandStruct) ComponentRun(s *discordgo.Session, i *discordgo.InteractionCreate) { - for _, c := range d.Components { - if (!c.Parse(&ComponentContext{s, &utils.InteractionCreate{ + data := &ComponentContext{ + Session: s, + Inter: &utils.InteractionCreate{ InteractionCreate: i, Session: s, - }, c})) { + }, + } + + for _, c := range d.Components { + data.Component = c + + if !c.Parse(data) { continue } - c.Run(&ComponentContext{s, &utils.InteractionCreate{ + c.Run(data) + break + } +} + +func (d *DiscommandStruct) ModalRun(s *discordgo.Session, i *discordgo.InteractionCreate) { + data := &ModalContext{ + Inter: &utils.InteractionCreate{ InteractionCreate: i, Session: s, - }, c}) + }, + } + + for _, m := range d.Modals { + data.Modal = m + + if !m.Parse(data) { + continue + } + + m.Run(data) break } } diff --git a/commands/learnedDataList.go b/commands/learnedDataList.go index e925f81..86aa4e9 100644 --- a/commands/learnedDataList.go +++ b/commands/learnedDataList.go @@ -129,7 +129,6 @@ func learnedDataListRun(s *discordgo.Session, m any, args *[]string) { } if match := utils.RegexpLearnQueryResult.FindStringSubmatch(query); match != nil { - fmt.Println(match[1]) filter = append(filter, bson.E{ Key: "result", Value: bson.M{ @@ -139,7 +138,6 @@ func learnedDataListRun(s *discordgo.Session, m any, args *[]string) { } if match := utils.RegexpLearnQueryLength.FindStringSubmatch(query); match != nil { - fmt.Println(1) var err error length, err = strconv.Atoi(match[1]) fmt.Printf("err: %v\n", err) diff --git a/components/paginationEmbed.go b/components/paginationEmbed.go index 1442349..f2175aa 100644 --- a/components/paginationEmbed.go +++ b/components/paginationEmbed.go @@ -15,13 +15,12 @@ var PaginationEmbedComponent *commands.Component = &commands.Component{ if i.MessageComponentData().ComponentType == discordgo.ButtonComponent { customId := i.MessageComponentData().CustomID - if !strings.HasPrefix(customId, utils.PaginationEmbedPrev) && !strings.HasPrefix(customId, utils.PaginationEmbedNext) { + if !strings.HasPrefix(customId, utils.PaginationEmbedPrev) && !strings.HasPrefix(customId, utils.PaginationEmbedNext) && !strings.HasPrefix(customId, utils.PaginationEmbedPages) { return false } id := utils.GetPaginationEmbedId(customId) userId := utils.GetPaginationEmbedUserId(id) - if i.Member.User.ID != userId { return false } @@ -41,8 +40,10 @@ var PaginationEmbedComponent *commands.Component = &commands.Component{ if strings.HasPrefix(customId, utils.PaginationEmbedPrev) { p.Prev(ctx.Inter) - } else { + } else if strings.HasPrefix(customId, utils.PaginationEmbedNext) { p.Next(ctx.Inter) + } else { + p.ShowModal(ctx.Inter) } }, } diff --git a/configs/version.go b/configs/version.go index 389ea8c..d3306be 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.250516a" +const MUFFIN_VERSION = "5.1.0-gopher_dev.250517a" var updatedString string = utils.RegexpDecimals.FindAllStringSubmatch(MUFFIN_VERSION, -1)[3][0] diff --git a/handler/interactionCreate.go b/handler/interactionCreate.go index 2152bd8..4dfbbbc 100644 --- a/handler/interactionCreate.go +++ b/handler/interactionCreate.go @@ -11,5 +11,7 @@ func InteractionCreate(s *discordgo.Session, i *discordgo.InteractionCreate) { return } else if i.Type == discordgo.InteractionMessageComponent { commands.Discommand.ComponentRun(s, i) + } else if i.Type == discordgo.InteractionModalSubmit { + commands.Discommand.ModalRun(s, i) } } diff --git a/main.go b/main.go index 9ada644..5422466 100644 --- a/main.go +++ b/main.go @@ -14,6 +14,7 @@ import ( "git.wh64.net/muffin/goMuffin/configs" "git.wh64.net/muffin/goMuffin/databases" "git.wh64.net/muffin/goMuffin/handler" + "git.wh64.net/muffin/goMuffin/modals" "git.wh64.net/muffin/goMuffin/scripts" "github.com/bwmarrin/discordgo" "github.com/devproje/commando" @@ -77,6 +78,8 @@ func main() { go commands.Discommand.LoadComponent(components.DeleteLearnedDataComponent) go commands.Discommand.LoadComponent(components.PaginationEmbedComponent) + go commands.Discommand.LoadModal(modals.PaginationEmbedModal) + go dg.AddHandler(handler.MessageCreate) go dg.AddHandler(handler.InteractionCreate) diff --git a/modals/paginationEmbed.go b/modals/paginationEmbed.go new file mode 100644 index 0000000..3f30342 --- /dev/null +++ b/modals/paginationEmbed.go @@ -0,0 +1,66 @@ +package modals + +import ( + "strconv" + "strings" + + "git.wh64.net/muffin/goMuffin/commands" + "git.wh64.net/muffin/goMuffin/utils" + "github.com/bwmarrin/discordgo" +) + +var PaginationEmbedModal *commands.Modal = &commands.Modal{ + Parse: func(ctx *commands.ModalContext) bool { + i := ctx.Inter + data := i.ModalSubmitData() + customId := data.CustomID + + if data.Components[0].Type() != discordgo.ActionsRowComponent { + return false + } + + if !strings.HasPrefix(customId, utils.PaginationEmbedModal) { + 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 + } + + cmp := data.Components[0].(*discordgo.ActionsRow).Components[0].(*discordgo.TextInput) + + if _, err := strconv.Atoi(cmp.Value); err != nil { + i.Reply(&discordgo.InteractionResponseData{ + Embeds: []*discordgo.MessageEmbed{ + { + Title: "❌ 오류", + Description: "해당 값은 숫자여야해요.", + Color: utils.EmbedFail, + }, + }, + Flags: discordgo.MessageFlagsEphemeral, + }) + return false + } + + return true + }, + Run: func(ctx *commands.ModalContext) { + data := ctx.Inter.ModalSubmitData() + customId := data.CustomID + id := utils.GetPaginationEmbedId(customId) + p := utils.GetPaginationEmbed(id) + cmp := data.Components[0].(*discordgo.ActionsRow).Components[0].(*discordgo.TextInput) + + page, _ := strconv.Atoi(cmp.Value) + + p.Set(ctx.Inter, page) + }, +} diff --git a/utils/customIds.go b/utils/customIds.go index 27b160c..a37a55b 100644 --- a/utils/customIds.go +++ b/utils/customIds.go @@ -13,9 +13,11 @@ const ( DeleteLearnedDataUserId = "#muffin/deleteLearnedData@" DeleteLearnedDataCancel = "#muffin/deleteLearnedData/cancel@" - PaginationEmbedPrev = "#muffin-pages/prev$" - PaginationEmbedPages = "#muffin-pages/pages$" - PaginationEmbedNext = "#muffin-pages/next$" + PaginationEmbedPrev = "#muffin-pages/prev$" + PaginationEmbedPages = "#muffin-pages/pages$" + PaginationEmbedNext = "#muffin-pages/next$" + PaginationEmbedModal = "#muffin-pages/modal$" + PaginationEmbedSetPage = "#muffin-pages/modal/set$" ) func MakeDeleteLearnedData(id string, number int) string { @@ -49,21 +51,36 @@ 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 MakePaginationEmbedPages(id string) string { + return fmt.Sprintf("%s%s", PaginationEmbedPages, id) } func MakePaginationEmbedNext(id string) string { return fmt.Sprintf("%s%s", PaginationEmbedNext, id) } +func MakePaginationEmbedModal(id string) string { + return fmt.Sprintf("%s%s", PaginationEmbedModal, id) +} + +func MakePaginationEmbedSetPage(id string) string { + return fmt.Sprintf("%s%s", PaginationEmbedSetPage, id) +} + func GetPaginationEmbedId(customId string) string { - if strings.HasPrefix(customId, PaginationEmbedPrev) { + switch { + case strings.HasPrefix(customId, PaginationEmbedPrev): return customId[len(PaginationEmbedPrev):] - } else if strings.HasPrefix(customId, PaginationEmbedPages) { + case strings.HasPrefix(customId, PaginationEmbedPages): return customId[len(PaginationEmbedPages):] - } else { + case strings.HasPrefix(customId, PaginationEmbedNext): return customId[len(PaginationEmbedNext):] + case strings.HasPrefix(customId, PaginationEmbedModal): + return customId[len(PaginationEmbedModal):] + case strings.HasPrefix(customId, PaginationEmbedSetPage): + return customId[len(PaginationEmbedSetPage):] + default: + return customId } } diff --git a/utils/interactions.go b/utils/interactions.go index a2245be..199779e 100644 --- a/utils/interactions.go +++ b/utils/interactions.go @@ -1,6 +1,20 @@ package utils -import "github.com/bwmarrin/discordgo" +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + + "github.com/bwmarrin/discordgo" +) + +type ModalData struct { + CustomId string `json:"custom_id"` + Title string `json:"title"` + Components []discordgo.MessageComponent `json:"components"` +} // InteractionCreate custom data of discordgo.InteractionCreate type InteractionCreate struct { @@ -62,3 +76,44 @@ func (i *InteractionCreate) Update(data *discordgo.InteractionResponseData) { Data: data, }) } + +func (i *InteractionCreate) ShowModal(data *ModalData) error { + var reqData struct { + Type discordgo.InteractionResponseType `json:"type"` + Data ModalData `json:"data"` + } + + reqData.Type = discordgo.InteractionResponseModal + reqData.Data = *data + bin, err := json.Marshal(reqData) + if err != nil { + return err + } + + buf := bytes.NewBuffer(bin) + + req, err := http.NewRequest("POST", discordgo.EndpointInteractionResponse(i.ID, i.Token), buf) + if err != nil { + return err + } + + req.Header.Add("Authorization", i.Session.Identify.Token) + req.Header.Add("Content-Type", "application/json") + + resp, err := i.Session.Client.Do(req) + if err != nil { + return err + } + + respBin, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + if resp.StatusCode != 200 { + return fmt.Errorf("%s", string(respBin)) + } + + defer resp.Body.Close() + return nil +} diff --git a/utils/paginationEmbed.go b/utils/paginationEmbed.go index 9231ae4..7dba36f 100644 --- a/utils/paginationEmbed.go +++ b/utils/paginationEmbed.go @@ -14,13 +14,18 @@ type PaginationEmbed struct { Current int Total int id string - s *discordgo.Session desc string } var PaginationEmbeds = make(map[string]*PaginationEmbed) func makeComponents(id string, current, total int) *[]discordgo.MessageComponent { + disabled := false + + if total == 1 { + disabled = true + } + return &[]discordgo.MessageComponent{ discordgo.ActionsRow{ Components: []discordgo.MessageComponent{ @@ -28,19 +33,19 @@ func makeComponents(id string, current, total int) *[]discordgo.MessageComponent Style: discordgo.PrimaryButton, Label: "이전", CustomID: MakePaginationEmbedPrev(id), - Disabled: false, + Disabled: disabled, }, discordgo.Button{ Style: discordgo.SecondaryButton, Label: fmt.Sprintf("(%d/%d)", current, total), - CustomID: MakePaginationEmbedPages(id, current, total), - Disabled: true, + CustomID: MakePaginationEmbedPages(id), + Disabled: disabled, }, discordgo.Button{ Style: discordgo.PrimaryButton, Label: "다음", CustomID: MakePaginationEmbedNext(id), - Disabled: false, + Disabled: disabled, }, }, }, @@ -76,8 +81,6 @@ func StartPaginationEmbed(s *discordgo.Session, m any, e *discordgo.MessageEmbed Current: 1, Total: len(data), id: id, - s: s, - desc: defaultDesc, } if len(data) <= 0 { @@ -160,3 +163,62 @@ func (p *PaginationEmbed) Next(i *InteractionCreate) { Components: *makeComponents(p.id, p.Current, p.Total), }) } + +func (p *PaginationEmbed) Set(i *InteractionCreate, page int) { + if page <= 0 { + i.Reply(&discordgo.InteractionResponseData{ + Embeds: []*discordgo.MessageEmbed{ + { + Title: "❌ 오류", + Description: "해당 값은 0보다 커야해요.", + Color: EmbedFail, + }, + }, + Flags: discordgo.MessageFlagsEphemeral, + }) + return + } + + if page >= p.Total { + i.Reply(&discordgo.InteractionResponseData{ + Embeds: []*discordgo.MessageEmbed{ + { + Title: "❌ 오류", + Description: "해당 값은 총 페이지의 수보다 작아야해요.", + Color: EmbedFail, + }, + }, + Flags: discordgo.MessageFlagsEphemeral, + }) + return + } + + p.Current = page + + 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), + }) +} + +func (p *PaginationEmbed) ShowModal(i *InteractionCreate) { + i.ShowModal(&ModalData{ + CustomId: MakePaginationEmbedModal(p.id), + Title: fmt.Sprintf("%s의 리스트", i.Session.State.User.Username), + Components: []discordgo.MessageComponent{ + discordgo.ActionsRow{ + Components: []discordgo.MessageComponent{ + discordgo.TextInput{ + CustomID: MakePaginationEmbedSetPage(p.id), + Label: "페이지", + Style: discordgo.TextInputShort, + Placeholder: "이동할 페이지를 여기에 적어주세요.", + Required: true, + }, + }, + }, + }, + }) +} From 67b8c0ee86eef7c47f8bb71cebdba73e4ac5c80f Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Sat, 17 May 2025 11:45:07 +0900 Subject: [PATCH 29/34] fix: pagination default description --- utils/paginationEmbed.go | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/paginationEmbed.go b/utils/paginationEmbed.go index 7dba36f..4e9d4b0 100644 --- a/utils/paginationEmbed.go +++ b/utils/paginationEmbed.go @@ -81,6 +81,7 @@ func StartPaginationEmbed(s *discordgo.Session, m any, e *discordgo.MessageEmbed Current: 1, Total: len(data), id: id, + desc: defaultDesc, } if len(data) <= 0 { From dfd0b74d76adef523cb7596d3dd08e54f84a8fb4 Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Sat, 17 May 2025 11:49:03 +0900 Subject: [PATCH 30/34] fix: max min value --- commands/learnedDataList.go | 18 ++++++++++++++++++ configs/version.go | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/commands/learnedDataList.go b/commands/learnedDataList.go index 86aa4e9..c8710bd 100644 --- a/commands/learnedDataList.go +++ b/commands/learnedDataList.go @@ -151,6 +151,24 @@ func learnedDataListRun(s *discordgo.Session, m any, args *[]string) { return } } + + if float64(length) < LIST_MIN_VALUE { + s.ChannelMessageSendEmbedReply(m.ChannelID, &discordgo.MessageEmbed{ + Title: "❌ 오류", + Description: fmt.Sprintf("개수의 값은 %d보다 커야해요.", int(LIST_MIN_VALUE)), + Color: utils.EmbedFail, + }, m.Reference()) + return + } + + if float64(length) > LIST_MAX_VALUE { + s.ChannelMessageSendEmbedReply(m.ChannelID, &discordgo.MessageEmbed{ + Title: "❌ 오류", + Description: fmt.Sprintf("개수의 값은 %d보다 작아야해요.", int(LIST_MAX_VALUE)), + Color: utils.EmbedFail, + }, m.Reference()) + return + } case *utils.InteractionCreate: m.DeferReply(true) diff --git a/configs/version.go b/configs/version.go index d3306be..06b02ca 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.250517a" +const MUFFIN_VERSION = "5.1.0-gopher_dev.250517b" var updatedString string = utils.RegexpDecimals.FindAllStringSubmatch(MUFFIN_VERSION, -1)[3][0] From 24254f0b6dbd2664738109fd7b319cab7017872d Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Sat, 17 May 2025 11:53:38 +0900 Subject: [PATCH 31/34] fix: length --- commands/learnedDataList.go | 33 ++++++++++++++++----------------- configs/version.go | 2 +- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/commands/learnedDataList.go b/commands/learnedDataList.go index c8710bd..7647859 100644 --- a/commands/learnedDataList.go +++ b/commands/learnedDataList.go @@ -140,7 +140,6 @@ func learnedDataListRun(s *discordgo.Session, m any, args *[]string) { if match := utils.RegexpLearnQueryLength.FindStringSubmatch(query); match != nil { var err error length, err = strconv.Atoi(match[1]) - fmt.Printf("err: %v\n", err) if err != nil { s.ChannelMessageSendEmbedReply(m.ChannelID, &discordgo.MessageEmbed{ @@ -150,24 +149,24 @@ func learnedDataListRun(s *discordgo.Session, m any, args *[]string) { }, m.Reference()) return } - } - if float64(length) < LIST_MIN_VALUE { - s.ChannelMessageSendEmbedReply(m.ChannelID, &discordgo.MessageEmbed{ - Title: "❌ 오류", - Description: fmt.Sprintf("개수의 값은 %d보다 커야해요.", int(LIST_MIN_VALUE)), - Color: utils.EmbedFail, - }, m.Reference()) - return - } + if float64(length) < LIST_MIN_VALUE { + s.ChannelMessageSendEmbedReply(m.ChannelID, &discordgo.MessageEmbed{ + Title: "❌ 오류", + Description: fmt.Sprintf("개수의 값은 %d보다 커야해요.", int(LIST_MIN_VALUE)), + Color: utils.EmbedFail, + }, m.Reference()) + return + } - if float64(length) > LIST_MAX_VALUE { - s.ChannelMessageSendEmbedReply(m.ChannelID, &discordgo.MessageEmbed{ - Title: "❌ 오류", - Description: fmt.Sprintf("개수의 값은 %d보다 작아야해요.", int(LIST_MAX_VALUE)), - Color: utils.EmbedFail, - }, m.Reference()) - return + if float64(length) > LIST_MAX_VALUE { + s.ChannelMessageSendEmbedReply(m.ChannelID, &discordgo.MessageEmbed{ + Title: "❌ 오류", + Description: fmt.Sprintf("개수의 값은 %d보다 작아야해요.", int(LIST_MAX_VALUE)), + Color: utils.EmbedFail, + }, m.Reference()) + return + } } case *utils.InteractionCreate: m.DeferReply(true) diff --git a/configs/version.go b/configs/version.go index 06b02ca..dc93921 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.250517b" +const MUFFIN_VERSION = "5.1.0-gopher_dev.250517c" var updatedString string = utils.RegexpDecimals.FindAllStringSubmatch(MUFFIN_VERSION, -1)[3][0] From fce4dc2e7326741e40f2a996b7218adca009118c Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Sun, 18 May 2025 15:09:50 +0900 Subject: [PATCH 32/34] chore: edit version --- configs/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/version.go b/configs/version.go index dc93921..dac476b 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.250517c" +const MUFFIN_VERSION = "5.1.0-gopher_preview.250518a" var updatedString string = utils.RegexpDecimals.FindAllStringSubmatch(MUFFIN_VERSION, -1)[3][0] From b573016d0ef62d47a2fec97a0892786a82a75888 Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Mon, 19 May 2025 19:51:49 +0900 Subject: [PATCH 33/34] chore: Add deprecated alert --- scripts/dbMigrate.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/dbMigrate.go b/scripts/dbMigrate.go index f06569c..fbd4a60 100644 --- a/scripts/dbMigrate.go +++ b/scripts/dbMigrate.go @@ -29,6 +29,8 @@ func DBMigrate(n *commando.Node) error { wg.Add(3) + fmt.Println("[경고] 해당 명령어는 다음 버전에서 사라져요.") + // statement -> text go func() { defer wg.Done() From dc57303dfa5e781b3c77a90eb65d5f8bec18c7eb Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Thu, 22 May 2025 19:07:39 +0900 Subject: [PATCH 34/34] feat: add knowledge length limit --- commands/learn.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/commands/learn.go b/commands/learn.go index d3bf3bc..0515cd4 100644 --- a/commands/learn.go +++ b/commands/learn.go @@ -173,6 +173,23 @@ func learnRun(c *Command, s *discordgo.Session, m any, args *[]string) { } } + if len([]rune(command)) > 100 { + embed := &discordgo.MessageEmbed{ + Title: "❌ 오류", + Description: "단어는 100글자를 못 넘ㅇ어가요.", + Color: utils.EmbedFail, + } + + 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}, + }) + } + } + _, err := databases.Database.Learns.InsertOne(context.TODO(), databases.InsertLearn{ Command: command, Result: result,