Merge branch 'release/5.1.0-gopher'
This commit is contained in:
commit
e5090fa35d
33 changed files with 1294 additions and 151 deletions
|
@ -7,4 +7,3 @@ README.md
|
||||||
update.sh
|
update.sh
|
||||||
compose.yml
|
compose.yml
|
||||||
Dockerfile
|
Dockerfile
|
||||||
script/
|
|
19
.env.example
19
.env.example
|
@ -1,11 +1,24 @@
|
||||||
|
# Database (URL 형식과 Non-URL 형식 중 하나를 선택)
|
||||||
|
## URL 형식
|
||||||
DATABASE_URL=
|
DATABASE_URL=
|
||||||
|
|
||||||
|
## Non-URL 형식
|
||||||
|
DATABASE_USERNAME=
|
||||||
|
DATABASE_PASSWORD=
|
||||||
|
DATABASE_HOSTNAME= # 해당 값은 도커로 실행할 때 필요하지 않음.
|
||||||
|
DATABASE_PORT=
|
||||||
|
DATABASE_AUTH_SOURCE= # 기본 값: admin
|
||||||
|
|
||||||
|
## 필수 값
|
||||||
DATABASE_NAME=
|
DATABASE_NAME=
|
||||||
|
|
||||||
|
## 데이터베이스 마이그레이션용
|
||||||
|
PREVIOUS_DATABASE_URL=
|
||||||
|
|
||||||
|
# 봇
|
||||||
BOT_TOKEN=
|
BOT_TOKEN=
|
||||||
BOT_PREFIX=
|
BOT_PREFIX=
|
||||||
BOT_OWNER_ID=
|
BOT_OWNER_ID=
|
||||||
|
|
||||||
# If you need
|
# 학습 (필수 아님)
|
||||||
PREVIOUS_DATABASE_URL=
|
|
||||||
|
|
||||||
TRAIN_USER_ID=
|
TRAIN_USER_ID=
|
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -124,4 +124,7 @@ $RECYCLE.BIN/
|
||||||
# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option)
|
# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option)
|
||||||
# Config files
|
# Config files
|
||||||
*.env
|
*.env
|
||||||
!.env.example
|
!.env.example
|
||||||
|
|
||||||
|
export/
|
||||||
|
data/
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"makefile.configureOnOpen": false
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
FROM golang:1.24.2
|
FROM golang:1.24.3
|
||||||
|
|
||||||
|
ENV DATABASE_NAME=muffin_ai
|
||||||
|
|
||||||
RUN mkdir /app
|
RUN mkdir /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
|
@ -12,8 +12,10 @@
|
||||||
|
|
||||||
## 실행
|
## 실행
|
||||||
|
|
||||||
|
- 아래의 모든 명령어는 make를 사용합니다.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
go run main.go
|
make run
|
||||||
```
|
```
|
||||||
|
|
||||||
### 빌드
|
### 빌드
|
||||||
|
@ -21,7 +23,7 @@ go run main.go
|
||||||
1. 빌드
|
1. 빌드
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
go build -o ./build/goMuffin git.wh64.net/muffin/goMuffin # 윈도우면 ./build/goMuffin을 .\build\goMuffin.exe으로 변경
|
make #또는 make build
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 실행
|
2. 실행
|
||||||
|
|
|
@ -89,9 +89,9 @@ func dataLengthRun(s *discordgo.Session, m any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
dataLengthWg.Add(5)
|
dataLengthWg.Add(5)
|
||||||
go getLength(text, databases.Texts, bson.D{{}})
|
go getLength(text, databases.Database.Texts, bson.D{{}})
|
||||||
go getLength(muffin, databases.Texts, bson.D{{Key: "persona", Value: "muffin"}})
|
go getLength(muffin, databases.Database.Texts, bson.D{{Key: "persona", Value: "muffin"}})
|
||||||
go getLength(nsfw, databases.Texts, bson.D{
|
go getLength(nsfw, databases.Database.Texts, bson.D{
|
||||||
{
|
{
|
||||||
Key: "persona",
|
Key: "persona",
|
||||||
Value: bson.M{
|
Value: bson.M{
|
||||||
|
@ -99,8 +99,8 @@ func dataLengthRun(s *discordgo.Session, m any) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
go getLength(learn, databases.Learns, bson.D{{}})
|
go getLength(learn, databases.Database.Learns, bson.D{{}})
|
||||||
go getLength(userLearn, databases.Learns, bson.D{{Key: "user_id", Value: userId}})
|
go getLength(userLearn, databases.Database.Learns, bson.D{{Key: "user_id", Value: userId}})
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
dataLengthWg.Wait()
|
dataLengthWg.Wait()
|
||||||
|
|
|
@ -31,7 +31,7 @@ var DeleteLearnedDataCommand *Command = &Command{
|
||||||
},
|
},
|
||||||
Category: Chatting,
|
Category: Chatting,
|
||||||
MessageRun: func(ctx *MsgContext) {
|
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) {
|
ChatInputRun: func(ctx *ChatInputContext) {
|
||||||
deleteLearnedDataRun(ctx.Command, ctx.Session, ctx.Inter, nil)
|
deleteLearnedDataRun(ctx.Command, ctx.Session, ctx.Inter, nil)
|
||||||
|
@ -74,7 +74,7 @@ func deleteLearnedDataRun(c *Command, s *discordgo.Session, m any, args *[]strin
|
||||||
userId = m.Member.User.ID
|
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 {
|
if err != nil {
|
||||||
embed := &discordgo.MessageEmbed{
|
embed := &discordgo.MessageEmbed{
|
||||||
Title: "❌ 오류",
|
Title: "❌ 오류",
|
||||||
|
@ -119,7 +119,7 @@ func deleteLearnedDataRun(c *Command, s *discordgo.Session, m any, args *[]strin
|
||||||
options = append(options, discordgo.SelectMenuOption{
|
options = append(options, discordgo.SelectMenuOption{
|
||||||
Label: fmt.Sprintf("%d번 지식", i+1),
|
Label: fmt.Sprintf("%d번 지식", i+1),
|
||||||
Description: data.Result,
|
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)
|
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{
|
Components: []discordgo.MessageComponent{
|
||||||
discordgo.SelectMenu{
|
discordgo.SelectMenu{
|
||||||
MenuType: discordgo.StringSelectMenu,
|
MenuType: discordgo.StringSelectMenu,
|
||||||
CustomID: utils.DeleteLearnedDataUserId + userId,
|
CustomID: utils.MakeDeleteLearnedDataUserId(userId),
|
||||||
Options: options,
|
Options: options,
|
||||||
Placeholder: "ㅈ지울 응답을 선택해주세요.",
|
Placeholder: "ㅈ지울 응답을 선택해주세요.",
|
||||||
},
|
},
|
||||||
|
@ -144,7 +144,7 @@ func deleteLearnedDataRun(c *Command, s *discordgo.Session, m any, args *[]strin
|
||||||
discordgo.ActionsRow{
|
discordgo.ActionsRow{
|
||||||
Components: []discordgo.MessageComponent{
|
Components: []discordgo.MessageComponent{
|
||||||
discordgo.Button{
|
discordgo.Button{
|
||||||
CustomID: utils.DeleteLearnedDataCancel + userId,
|
CustomID: utils.MakeDeleteLearnedDataCancel(userId),
|
||||||
Label: "취소하기",
|
Label: "취소하기",
|
||||||
Style: discordgo.DangerButton,
|
Style: discordgo.DangerButton,
|
||||||
Disabled: false,
|
Disabled: false,
|
||||||
|
|
|
@ -7,9 +7,12 @@ import (
|
||||||
"github.com/bwmarrin/discordgo"
|
"github.com/bwmarrin/discordgo"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type modalRun func(ctx *ModalContext)
|
||||||
type messageRun func(ctx *MsgContext)
|
type messageRun func(ctx *MsgContext)
|
||||||
type chatInputRun func(ctx *ChatInputContext)
|
type chatInputRun func(ctx *ChatInputContext)
|
||||||
type componentRun func(ctx *ComponentContext)
|
type componentRun func(ctx *ComponentContext)
|
||||||
|
|
||||||
|
type modalParse func(ctx *ModalContext) bool
|
||||||
type componentParse func(ctx *ComponentContext) bool
|
type componentParse func(ctx *ComponentContext) bool
|
||||||
|
|
||||||
type Category string
|
type Category string
|
||||||
|
@ -32,12 +35,13 @@ type DiscommandStruct struct {
|
||||||
Commands map[string]*Command
|
Commands map[string]*Command
|
||||||
Components []*Component
|
Components []*Component
|
||||||
Aliases map[string]string
|
Aliases map[string]string
|
||||||
|
Modals []*Modal
|
||||||
}
|
}
|
||||||
|
|
||||||
type MsgContext struct {
|
type MsgContext struct {
|
||||||
Session *discordgo.Session
|
Session *discordgo.Session
|
||||||
Msg *discordgo.MessageCreate
|
Msg *discordgo.MessageCreate
|
||||||
Args []string
|
Args *[]string
|
||||||
Command *Command
|
Command *Command
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,24 +57,38 @@ type ComponentContext struct {
|
||||||
Component *Component
|
Component *Component
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ModalContext struct {
|
||||||
|
Inter *utils.InteractionCreate
|
||||||
|
Modal *Modal
|
||||||
|
}
|
||||||
|
|
||||||
type Component struct {
|
type Component struct {
|
||||||
Parse componentParse
|
Parse componentParse
|
||||||
Run componentRun
|
Run componentRun
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Modal struct {
|
||||||
|
Parse modalParse
|
||||||
|
Run modalRun
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Chatting Category = "채팅"
|
Chatting Category = "채팅"
|
||||||
General Category = "일반"
|
General Category = "일반"
|
||||||
)
|
)
|
||||||
|
|
||||||
var commandMutex sync.Mutex
|
var (
|
||||||
var componentMutex sync.Mutex
|
commandMutex sync.Mutex
|
||||||
|
componentMutex sync.Mutex
|
||||||
|
modalMutex sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
func new() *DiscommandStruct {
|
func new() *DiscommandStruct {
|
||||||
discommand := DiscommandStruct{
|
discommand := DiscommandStruct{
|
||||||
Commands: map[string]*Command{},
|
Commands: map[string]*Command{},
|
||||||
Aliases: map[string]string{},
|
Aliases: map[string]string{},
|
||||||
Components: []*Component{},
|
Components: []*Component{},
|
||||||
|
Modals: []*Modal{},
|
||||||
}
|
}
|
||||||
return &discommand
|
return &discommand
|
||||||
}
|
}
|
||||||
|
@ -92,9 +110,15 @@ func (d *DiscommandStruct) LoadComponent(c *Component) {
|
||||||
d.Components = append(d.Components, c)
|
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) {
|
func (d *DiscommandStruct) MessageRun(name string, s *discordgo.Session, m *discordgo.MessageCreate, args []string) {
|
||||||
if command, ok := d.Commands[name]; ok {
|
if command, ok := d.Commands[name]; ok {
|
||||||
command.MessageRun(&MsgContext{s, m, args, command})
|
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) {
|
func (d *DiscommandStruct) ComponentRun(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||||
for _, c := range d.Components {
|
data := &ComponentContext{
|
||||||
if (!c.Parse(&ComponentContext{s, &utils.InteractionCreate{
|
Session: s,
|
||||||
|
Inter: &utils.InteractionCreate{
|
||||||
InteractionCreate: i,
|
InteractionCreate: i,
|
||||||
Session: s,
|
Session: s,
|
||||||
}, c})) {
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range d.Components {
|
||||||
|
data.Component = c
|
||||||
|
|
||||||
|
if !c.Parse(data) {
|
||||||
continue
|
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,
|
InteractionCreate: i,
|
||||||
Session: s,
|
Session: s,
|
||||||
}, c})
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range d.Modals {
|
||||||
|
data.Modal = m
|
||||||
|
|
||||||
|
if !m.Parse(data) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Run(data)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,10 +30,10 @@ var HelpCommand *Command = &Command{
|
||||||
},
|
},
|
||||||
Category: General,
|
Category: General,
|
||||||
MessageRun: func(ctx *MsgContext) {
|
MessageRun: func(ctx *MsgContext) {
|
||||||
helpRun(ctx.Command, ctx.Session, ctx.Msg, &ctx.Args)
|
helpRun(ctx.Session, ctx.Msg, ctx.Args)
|
||||||
},
|
},
|
||||||
ChatInputRun: func(ctx *ChatInputContext) {
|
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
|
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
|
var commandName string
|
||||||
embed := &discordgo.MessageEmbed{
|
embed := &discordgo.MessageEmbed{
|
||||||
Color: utils.EmbedDefault,
|
Color: utils.EmbedDefault,
|
||||||
|
@ -106,6 +106,13 @@ func helpRun(c *Command, 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 {
|
if command.Aliases != nil {
|
||||||
embed.Fields = append(embed.Fields, &discordgo.MessageEmbedField{
|
embed.Fields = append(embed.Fields, &discordgo.MessageEmbedField{
|
||||||
Name: "별칭",
|
Name: "별칭",
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
"github.com/bwmarrin/discordgo"
|
"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.mention}") + "\n" +
|
||||||
utils.InlineCode("{user.globalName}") + "\n" +
|
utils.InlineCode("{user.globalName}") + "\n" +
|
||||||
utils.InlineCode("{user.id}") + "\n" +
|
utils.InlineCode("{user.id}") + "\n" +
|
||||||
|
@ -57,7 +57,7 @@ var LearnCommand *Command = &Command{
|
||||||
},
|
},
|
||||||
Category: Chatting,
|
Category: Chatting,
|
||||||
MessageRun: func(ctx *MsgContext) {
|
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) {
|
ChatInputRun: func(ctx *ChatInputContext) {
|
||||||
learnRun(ctx.Command, ctx.Session, ctx.Inter, nil)
|
learnRun(ctx.Command, ctx.Session, ctx.Inter, nil)
|
||||||
|
@ -91,7 +91,7 @@ func learnRun(c *Command, s *discordgo.Session, m any, args *[]string) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "사용 가능한 인자",
|
Name: "사용 가능한 인자",
|
||||||
Value: arguments,
|
Value: learnArguments,
|
||||||
Inline: true,
|
Inline: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -173,7 +173,24 @@ func learnRun(c *Command, s *discordgo.Session, m any, args *[]string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := databases.Learns.InsertOne(context.TODO(), databases.InsertLearn{
|
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,
|
Command: command,
|
||||||
Result: result,
|
Result: result,
|
||||||
UserId: userId,
|
UserId: userId,
|
||||||
|
|
|
@ -3,6 +3,7 @@ package commands
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.wh64.net/muffin/goMuffin/configs"
|
"git.wh64.net/muffin/goMuffin/configs"
|
||||||
|
@ -13,50 +14,191 @@ import (
|
||||||
"go.mongodb.org/mongo-driver/v2/mongo"
|
"go.mongodb.org/mongo-driver/v2/mongo"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
LIST_MIN_VALUE float64 = 10.0
|
||||||
|
LIST_MAX_VALUE float64 = 100.0
|
||||||
|
)
|
||||||
|
|
||||||
var LearnedDataListCommand *Command = &Command{
|
var LearnedDataListCommand *Command = &Command{
|
||||||
ApplicationCommand: &discordgo.ApplicationCommand{
|
ApplicationCommand: &discordgo.ApplicationCommand{
|
||||||
Type: discordgo.ChatApplicationCommand,
|
Type: discordgo.ChatApplicationCommand,
|
||||||
Name: "리스트",
|
Name: "리스트",
|
||||||
Description: "당신이 가ㄹ르쳐준 지식을 나열해요.",
|
Description: "당신이 가ㄹ르쳐준 지식을 나열해요.",
|
||||||
|
Options: []*discordgo.ApplicationCommandOption{
|
||||||
|
{
|
||||||
|
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", "목록", "지식목록"},
|
Aliases: []string{"list", "목록", "지식목록"},
|
||||||
DetailedDescription: &DetailedDescription{
|
DetailedDescription: &DetailedDescription{
|
||||||
Usage: fmt.Sprintf("%s리스트", configs.Config.Bot.Prefix),
|
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,
|
Category: Chatting,
|
||||||
MessageRun: func(ctx *MsgContext) {
|
MessageRun: func(ctx *MsgContext) {
|
||||||
learnedDataListRun(ctx.Session, ctx.Msg)
|
learnedDataListRun(ctx.Session, ctx.Msg, ctx.Args)
|
||||||
},
|
},
|
||||||
ChatInputRun: func(ctx *ChatInputContext) {
|
ChatInputRun: func(ctx *ChatInputContext) {
|
||||||
learnedDataListRun(ctx.Session, ctx.Inter)
|
learnedDataListRun(ctx.Session, ctx.Inter, nil)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDescriptions(data *[]databases.Learn) (descriptions []string) {
|
func getDescriptions(data *[]databases.Learn, length int) (descriptions []string) {
|
||||||
|
var builder strings.Builder
|
||||||
|
MAX_LENGTH := 100
|
||||||
|
|
||||||
|
if length == 0 {
|
||||||
|
length = 25
|
||||||
|
}
|
||||||
|
|
||||||
|
tempDesc := []string{}
|
||||||
|
|
||||||
for _, data := range *data {
|
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]) + "..."
|
||||||
|
}
|
||||||
|
|
||||||
|
tempDesc = append(tempDesc, fmt.Sprintf("- %s: %s\n", command, result))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, s := range tempDesc {
|
||||||
|
builder.WriteString(s)
|
||||||
|
|
||||||
|
if (i+1)%length == 0 {
|
||||||
|
descriptions = append(descriptions, builder.String())
|
||||||
|
builder.Reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if builder.Len() > 0 {
|
||||||
|
descriptions = append(descriptions, builder.String())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func learnedDataListRun(s *discordgo.Session, m any) {
|
func learnedDataListRun(s *discordgo.Session, m any, args *[]string) {
|
||||||
var userId, globalName, avatarUrl string
|
var globalName, avatarUrl string
|
||||||
var data []databases.Learn
|
var data []databases.Learn
|
||||||
|
var filter bson.D
|
||||||
|
var length int
|
||||||
|
|
||||||
switch m := m.(type) {
|
switch m := m.(type) {
|
||||||
case *discordgo.MessageCreate:
|
case *discordgo.MessageCreate:
|
||||||
userId = m.Author.ID
|
filter = bson.D{{Key: "user_id", Value: m.Author.ID}}
|
||||||
globalName = m.Author.GlobalName
|
globalName = m.Author.GlobalName
|
||||||
avatarUrl = m.Author.AvatarURL("512")
|
avatarUrl = m.Author.AvatarURL("512")
|
||||||
|
|
||||||
|
query := strings.Join(*args, " ")
|
||||||
|
|
||||||
|
if match := utils.RegexpLearnQueryCommand.FindStringSubmatch(query); match != nil {
|
||||||
|
filter = append(filter, bson.E{
|
||||||
|
Key: "command",
|
||||||
|
Value: bson.M{
|
||||||
|
"$regex": match[1],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if match := utils.RegexpLearnQueryResult.FindStringSubmatch(query); match != nil {
|
||||||
|
filter = append(filter, bson.E{
|
||||||
|
Key: "result",
|
||||||
|
Value: bson.M{
|
||||||
|
"$regex": match[1],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if match := utils.RegexpLearnQueryLength.FindStringSubmatch(query); match != nil {
|
||||||
|
var err error
|
||||||
|
length, err = strconv.Atoi(match[1])
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
s.ChannelMessageSendEmbedReply(m.ChannelID, &discordgo.MessageEmbed{
|
||||||
|
Title: "❌ 오류",
|
||||||
|
Description: "개수의 값은 숫자여야해요.",
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
case *utils.InteractionCreate:
|
case *utils.InteractionCreate:
|
||||||
m.DeferReply(true)
|
m.DeferReply(true)
|
||||||
|
|
||||||
userId = m.Member.User.ID
|
filter = bson.D{{Key: "user_id", Value: m.Member.User.ID}}
|
||||||
globalName = m.Member.User.GlobalName
|
globalName = m.Member.User.GlobalName
|
||||||
avatarUrl = m.Member.User.AvatarURL("512")
|
avatarUrl = m.Member.User.AvatarURL("512")
|
||||||
|
|
||||||
|
if opt, ok := m.Options["단어"]; ok {
|
||||||
|
filter = append(filter, bson.E{
|
||||||
|
Key: "command",
|
||||||
|
Value: bson.M{
|
||||||
|
"$regex": opt.StringValue(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Learns.Find(context.TODO(), bson.D{{Key: "user_id", Value: userId}})
|
cur, err := databases.Database.Learns.Find(context.TODO(), filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == mongo.ErrNoDocuments {
|
if err == mongo.ErrNoDocuments {
|
||||||
embed := &discordgo.MessageEmbed{
|
embed := &discordgo.MessageEmbed{
|
||||||
|
@ -99,20 +241,12 @@ func learnedDataListRun(s *discordgo.Session, m any) {
|
||||||
cur.All(context.TODO(), &data)
|
cur.All(context.TODO(), &data)
|
||||||
|
|
||||||
embed := &discordgo.MessageEmbed{
|
embed := &discordgo.MessageEmbed{
|
||||||
Title: fmt.Sprintf("%s님이 알려주신 지식", globalName),
|
Title: fmt.Sprintf("%s님이 알려주신 지식", globalName),
|
||||||
Description: utils.CodeBlock("md", fmt.Sprintf("# 총 %d개에요.\n%s", len(data), strings.Join(getDescriptions(&data), "\n"))),
|
Color: utils.EmbedDefault,
|
||||||
Color: utils.EmbedDefault,
|
|
||||||
Thumbnail: &discordgo.MessageEmbedThumbnail{
|
Thumbnail: &discordgo.MessageEmbedThumbnail{
|
||||||
URL: avatarUrl,
|
URL: avatarUrl,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
switch m := m.(type) {
|
utils.StartPaginationEmbed(s, m, embed, getDescriptions(&data, length), utils.CodeBlock("md", fmt.Sprintf("# 총 %d개에요.\n", len(data))+"%s"))
|
||||||
case *discordgo.MessageCreate:
|
|
||||||
s.ChannelMessageSendEmbedReply(m.ChannelID, embed, m.Reference())
|
|
||||||
case *utils.InteractionCreate:
|
|
||||||
m.EditReply(&discordgo.WebhookEdit{
|
|
||||||
Embeds: &[]*discordgo.MessageEmbed{embed},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ var DeleteLearnedDataComponent *commands.Component = &commands.Component{
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
userId = customId[len(utils.DeleteLearnedDataCancel):]
|
userId = utils.GetDeleteLearnedDataUserId(customId)
|
||||||
if i.Member.User.ID == userId {
|
if i.Member.User.ID == userId {
|
||||||
i.Update(&discordgo.InteractionResponseData{
|
i.Update(&discordgo.InteractionResponseData{
|
||||||
Embeds: []*discordgo.MessageEmbed{
|
Embeds: []*discordgo.MessageEmbed{
|
||||||
|
@ -41,7 +41,7 @@ var DeleteLearnedDataComponent *commands.Component = &commands.Component{
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
userId = customId[len(utils.DeleteLearnedDataUserId):]
|
userId = utils.GetDeleteLearnedDataUserId(customId)
|
||||||
}
|
}
|
||||||
|
|
||||||
if i.Member.User.ID != userId {
|
if i.Member.User.ID != userId {
|
||||||
|
@ -66,16 +66,15 @@ var DeleteLearnedDataComponent *commands.Component = &commands.Component{
|
||||||
|
|
||||||
i.DeferUpdate()
|
i.DeferUpdate()
|
||||||
|
|
||||||
id, _ := bson.ObjectIDFromHex(strings.ReplaceAll(utils.ItemIdRegexp.ReplaceAllString(i.MessageComponentData().Values[0][len(utils.DeleteLearnedData):], ""), "&", ""))
|
id, itemId := utils.GetDeleteLearnedDataId(i.MessageComponentData().Values[0])
|
||||||
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{
|
i.EditReply(&discordgo.WebhookEdit{
|
||||||
Embeds: &[]*discordgo.MessageEmbed{
|
Embeds: &[]*discordgo.MessageEmbed{
|
||||||
{
|
{
|
||||||
Title: "✅ 삭제 완료",
|
Title: "✅ 삭제 완료",
|
||||||
Description: fmt.Sprintf("%s번을 삭ㅈ제했어요.", itemId),
|
Description: fmt.Sprintf("%d번을 삭ㅈ제했어요.", itemId),
|
||||||
Color: utils.EmbedSuccess,
|
Color: utils.EmbedSuccess,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
49
components/paginationEmbed.go
Normal file
49
components/paginationEmbed.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
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) && !strings.HasPrefix(customId, utils.PaginationEmbedPages) {
|
||||||
|
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 if strings.HasPrefix(customId, utils.PaginationEmbedNext) {
|
||||||
|
p.Next(ctx.Inter)
|
||||||
|
} else {
|
||||||
|
p.ShowModal(ctx.Inter)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
18
compose.yml
18
compose.yml
|
@ -5,4 +5,20 @@ services:
|
||||||
env_file:
|
env_file:
|
||||||
- "./.env"
|
- "./.env"
|
||||||
volumes:
|
volumes:
|
||||||
- "/etc/localtime:/etc/localtime"
|
- "/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}"
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
)
|
)
|
||||||
|
@ -18,26 +19,41 @@ type trainConfig struct {
|
||||||
UserID string
|
UserID string
|
||||||
}
|
}
|
||||||
|
|
||||||
// MuffinConfig for Muffin bot
|
type databaseConfig struct {
|
||||||
type MuffinConfig struct {
|
Name string
|
||||||
Bot botConfig
|
URL string
|
||||||
Train trainConfig
|
HostName string
|
||||||
DatabaseURL string
|
Username string
|
||||||
DBName string
|
Password string
|
||||||
|
AuthSource string
|
||||||
|
Port int
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadConfig() *MuffinConfig {
|
// MuffinConfig for Muffin bot
|
||||||
godotenv.Load()
|
type MuffinConfig struct {
|
||||||
config := &MuffinConfig{Bot: botConfig{}, Train: trainConfig{}}
|
Bot botConfig
|
||||||
setConfig(config)
|
Train trainConfig
|
||||||
|
Database databaseConfig
|
||||||
|
|
||||||
return config
|
// Deprecated: Use Database.URL
|
||||||
|
DatabaseURL string
|
||||||
|
|
||||||
|
// Deprecated: Use Database.Name
|
||||||
|
DatabaseName string
|
||||||
|
}
|
||||||
|
|
||||||
|
var Config *MuffinConfig
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
godotenv.Load()
|
||||||
|
Config = &MuffinConfig{Bot: botConfig{}, Train: trainConfig{}, Database: databaseConfig{}}
|
||||||
|
setConfig(Config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRequiredValue(key string) string {
|
func getRequiredValue(key string) string {
|
||||||
value := os.Getenv(key)
|
value := os.Getenv(key)
|
||||||
if value == "" {
|
if value == "" {
|
||||||
log.Fatalln(fmt.Sprintf("[goMuffin] .env 파일에서 필요한 %s값이 없어요.", key))
|
log.Fatalln(fmt.Sprintf("[goMuffin] .env 파일에서 필요한 '%s'값이 없어요.", key))
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
@ -53,8 +69,29 @@ func setConfig(config *MuffinConfig) {
|
||||||
|
|
||||||
config.Train.UserID = getValue("TRAIN_USER_ID")
|
config.Train.UserID = getValue("TRAIN_USER_ID")
|
||||||
|
|
||||||
config.DatabaseURL = getRequiredValue("DATABASE_URL")
|
config.Database.URL = getValue("DATABASE_URL")
|
||||||
config.DBName = getRequiredValue("DATABASE_NAME")
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
var Config *MuffinConfig = loadConfig()
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated된 Value
|
||||||
|
config.DatabaseURL = config.Database.URL
|
||||||
|
config.DatabaseName = config.Database.Name
|
||||||
|
}
|
||||||
|
|
|
@ -7,9 +7,9 @@ import (
|
||||||
"git.wh64.net/muffin/goMuffin/utils"
|
"git.wh64.net/muffin/goMuffin/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
const MUFFIN_VERSION = "5.0.2-gopher_release.250511a"
|
const MUFFIN_VERSION = "5.1.0-gopher_release.250524a"
|
||||||
|
|
||||||
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 {
|
var UpdatedAt *time.Time = func() *time.Time {
|
||||||
year, _ := strconv.Atoi("20" + updatedString[0:2])
|
year, _ := strconv.Atoi("20" + updatedString[0:2])
|
||||||
|
|
|
@ -3,24 +3,20 @@ package databases
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.wh64.net/muffin/goMuffin/configs"
|
|
||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
"go.mongodb.org/mongo-driver/v2/mongo"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type InsertLearn struct {
|
type InsertLearn struct {
|
||||||
Command string
|
Command string `bson:"command"`
|
||||||
Result string
|
Result string `bson:"result"`
|
||||||
UserId string `bson:"user_id"`
|
UserId string `bson:"user_id"`
|
||||||
CreatedAt time.Time `bson:"created_at"`
|
CreatedAt time.Time `bson:"created_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Learn struct {
|
type Learn struct {
|
||||||
Id bson.ObjectID `bson:"_id"`
|
Id bson.ObjectID `bson:"_id" json:"id"`
|
||||||
Command string
|
Command string `bson:"command" json:"command"`
|
||||||
Result string
|
Result string `bson:"result" json:"result"`
|
||||||
UserId string `bson:"user_id"`
|
UserId string `bson:"user_id" json:"user_id"`
|
||||||
CreatedAt time.Time `bson:"created_at"`
|
CreatedAt time.Time `bson:"created_at" json:"created_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var Learns *mongo.Collection = Client.Database(configs.Config.DBName).Collection("learn")
|
|
||||||
|
|
|
@ -3,22 +3,18 @@ package databases
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.wh64.net/muffin/goMuffin/configs"
|
|
||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
"go.mongodb.org/mongo-driver/v2/mongo"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type InsertText struct {
|
type InsertText struct {
|
||||||
Text string
|
Text string `bson:"text" json:"text"`
|
||||||
Persona string
|
Persona string `bson:"persona" json:"persona"`
|
||||||
CreatedAt time.Time `bson:"created_at"`
|
CreatedAt time.Time `bson:"created_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Text struct {
|
type Text struct {
|
||||||
Id bson.ObjectID `bson:"_id"`
|
Id bson.ObjectID `bson:"_id" json:"id"`
|
||||||
Text string
|
Text string `bson:"text" json:"text"`
|
||||||
Persona string
|
Persona string `bson:"persona" json:"persona"`
|
||||||
CreatedAt time.Time `bson:"created_at"`
|
CreatedAt time.Time `bson:"created_at" json:"created_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var Texts *mongo.Collection = Client.Database(configs.Config.DBName).Collection("text")
|
|
||||||
|
|
|
@ -8,13 +8,31 @@ import (
|
||||||
"go.mongodb.org/mongo-driver/v2/mongo/options"
|
"go.mongodb.org/mongo-driver/v2/mongo/options"
|
||||||
)
|
)
|
||||||
|
|
||||||
func connect() *mongo.Client {
|
type MuffinDatabase struct {
|
||||||
client, err := mongo.Connect(options.Client().ApplyURI(configs.Config.DatabaseURL))
|
Client *mongo.Client
|
||||||
|
Learns *mongo.Collection
|
||||||
|
Texts *mongo.Collection
|
||||||
|
}
|
||||||
|
|
||||||
|
var Database *MuffinDatabase
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
Database, err = Connect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln(err)
|
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.DatabaseName).Collection("learn"),
|
||||||
|
Texts: client.Database(configs.Config.DatabaseName).Collection("text"),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -5,6 +5,7 @@ go 1.24.1
|
||||||
require (
|
require (
|
||||||
github.com/LoperLee/golang-hangul-toolkit v1.1.0
|
github.com/LoperLee/golang-hangul-toolkit v1.1.0
|
||||||
github.com/bwmarrin/discordgo v0.28.1
|
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/go-sql-driver/mysql v1.9.2
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
go.mongodb.org/mongo-driver/v2 v2.1.0
|
go.mongodb.org/mongo-driver/v2 v2.1.0
|
||||||
|
|
2
go.sum
2
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/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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
|
||||||
github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||||
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
|
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
|
||||||
|
|
|
@ -11,5 +11,7 @@ func InteractionCreate(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||||
return
|
return
|
||||||
} else if i.Type == discordgo.InteractionMessageComponent {
|
} else if i.Type == discordgo.InteractionMessageComponent {
|
||||||
commands.Discommand.ComponentRun(s, i)
|
commands.Discommand.ComponentRun(s, i)
|
||||||
|
} else if i.Type == discordgo.InteractionModalSubmit {
|
||||||
|
commands.Discommand.ModalRun(s, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func argParser(content string) (args []string) {
|
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] != "" {
|
if arg[1] != "" {
|
||||||
args = append(args, arg[1])
|
args = append(args, arg[1])
|
||||||
} else {
|
} else {
|
||||||
|
@ -60,7 +60,7 @@ func MessageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||||
command := commands.Discommand.Aliases[args[0]]
|
command := commands.Discommand.Aliases[args[0]]
|
||||||
|
|
||||||
if m.Author.ID == config.Train.UserID {
|
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,
|
Text: content,
|
||||||
Persona: "muffin",
|
Persona: "muffin",
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: time.Now(),
|
||||||
|
@ -77,13 +77,13 @@ func MessageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||||
var filter bson.D
|
var filter bson.D
|
||||||
|
|
||||||
ch := make(chan int)
|
ch := make(chan int)
|
||||||
x := rand.Intn(5)
|
x := rand.Intn(10)
|
||||||
|
|
||||||
channel, _ := s.Channel(m.ChannelID)
|
channel, _ := s.Channel(m.ChannelID)
|
||||||
if channel.NSFW {
|
if channel.NSFW {
|
||||||
filter = bson.D{{}}
|
filter = bson.D{{}}
|
||||||
|
|
||||||
if _, err := databases.Texts.InsertOne(context.TODO(), databases.InsertText{
|
if _, err := databases.Database.Texts.InsertOne(context.TODO(), databases.InsertText{
|
||||||
Text: content,
|
Text: content,
|
||||||
Persona: fmt.Sprintf("user:%s", m.Author.Username),
|
Persona: fmt.Sprintf("user:%s", m.Author.Username),
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: time.Now(),
|
||||||
|
@ -96,7 +96,7 @@ func MessageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
cur, err := databases.Texts.Find(context.TODO(), filter)
|
cur, err := databases.Database.Texts.Find(context.TODO(), filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
|
@ -107,7 +107,7 @@ func MessageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||||
ch <- 1
|
ch <- 1
|
||||||
}()
|
}()
|
||||||
go func() {
|
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 != nil {
|
||||||
if err == mongo.ErrNilDocument {
|
if err == mongo.ErrNilDocument {
|
||||||
learnData = []databases.Learn{}
|
learnData = []databases.Learn{}
|
||||||
|
@ -131,7 +131,6 @@ func MessageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||||
user, _ := s.User(data.UserId)
|
user, _ := s.User(data.UserId)
|
||||||
result := resultParser(data.Result, s, m)
|
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{
|
s.ChannelMessageSendComplex(m.ChannelID, &discordgo.MessageSend{
|
||||||
Reference: m.Reference(),
|
Reference: m.Reference(),
|
||||||
Content: fmt.Sprintf("%s\n%s", result, utils.InlineCode(fmt.Sprintf("%s님이 알려주셨어요.", user.Username))),
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// s.ChannelMessageSendReply(m.ChannelID, data[rand.Intn(len(data))].Text, m.Reference())
|
|
||||||
s.ChannelMessageSendComplex(m.ChannelID, &discordgo.MessageSend{
|
s.ChannelMessageSendComplex(m.ChannelID, &discordgo.MessageSend{
|
||||||
Reference: m.Reference(),
|
Reference: m.Reference(),
|
||||||
Content: data[rand.Intn(len(data))].Text,
|
Content: data[rand.Intn(len(data))].Text,
|
||||||
|
|
69
main.go
69
main.go
|
@ -6,7 +6,6 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strings"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -15,30 +14,59 @@ import (
|
||||||
"git.wh64.net/muffin/goMuffin/configs"
|
"git.wh64.net/muffin/goMuffin/configs"
|
||||||
"git.wh64.net/muffin/goMuffin/databases"
|
"git.wh64.net/muffin/goMuffin/databases"
|
||||||
"git.wh64.net/muffin/goMuffin/handler"
|
"git.wh64.net/muffin/goMuffin/handler"
|
||||||
|
"git.wh64.net/muffin/goMuffin/modals"
|
||||||
"git.wh64.net/muffin/goMuffin/scripts"
|
"git.wh64.net/muffin/goMuffin/scripts"
|
||||||
"github.com/bwmarrin/discordgo"
|
"github.com/bwmarrin/discordgo"
|
||||||
|
"github.com/devproje/commando"
|
||||||
|
"github.com/devproje/commando/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
command := commando.NewCommando(os.Args[1:])
|
||||||
config := configs.Config
|
config := configs.Config
|
||||||
|
|
||||||
if len(os.Args) > 1 {
|
if len(os.Args) > 1 {
|
||||||
switch strings.ToLower(os.Args[1]) {
|
command.Root("db-migrate", "봇의 데이터를 MariaDB에서 MongoDB로 옮깁니다.", scripts.DBMigrate)
|
||||||
case "dbmigrate":
|
command.Root("delete-all-commands", "봇의 모든 슬래시 커맨드를 삭제합니다.", scripts.DeleteAllCommands,
|
||||||
scripts.DBMigrate()
|
types.OptionData{
|
||||||
case "deleteallcommands":
|
Name: "id",
|
||||||
scripts.DeleteAllCommands()
|
Desc: "봇의 디스코드 아이디",
|
||||||
default:
|
Type: types.STRING,
|
||||||
log.Fatalln(fmt.Errorf("[goMuffin] 명령어 인자에는 dbmigrate나 deleteallcommands만 올 수 있어요"))
|
},
|
||||||
|
types.OptionData{
|
||||||
|
Name: "isYes",
|
||||||
|
Short: []string{"y"},
|
||||||
|
Type: types.BOOLEAN,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dg, err := discordgo.New("Bot " + config.Bot.Token)
|
dg, _ := discordgo.New("Bot " + config.Bot.Token)
|
||||||
if err != nil {
|
|
||||||
log.Println("[goMuffin] 봇의 세션을 만들수가 없어요.")
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
go commands.Discommand.LoadCommand(commands.HelpCommand)
|
go commands.Discommand.LoadCommand(commands.HelpCommand)
|
||||||
go commands.Discommand.LoadCommand(commands.DataLengthCommand)
|
go commands.Discommand.LoadCommand(commands.DataLengthCommand)
|
||||||
|
@ -48,13 +76,22 @@ func main() {
|
||||||
go commands.Discommand.LoadCommand(commands.DeleteLearnedDataCommand)
|
go commands.Discommand.LoadCommand(commands.DeleteLearnedDataCommand)
|
||||||
|
|
||||||
go commands.Discommand.LoadComponent(components.DeleteLearnedDataComponent)
|
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.MessageCreate)
|
||||||
go dg.AddHandler(handler.InteractionCreate)
|
go dg.AddHandler(handler.InteractionCreate)
|
||||||
|
|
||||||
dg.Open()
|
err := dg.Open()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("[goMuffin] 봇을 시작할 수 없어요.")
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
defer dg.Close()
|
defer dg.Close()
|
||||||
|
|
||||||
|
// 봇의 상태메세지 변경
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
dg.UpdateCustomStatus("ㅅ살려주세요..!")
|
dg.UpdateCustomStatus("ㅅ살려주세요..!")
|
||||||
|
@ -63,7 +100,7 @@ func main() {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for _, cmd := range commands.Discommand.Commands {
|
for _, cmd := range commands.Discommand.Commands {
|
||||||
if cmd.Name == "도움말" {
|
if cmd.Name == commands.HelpCommand.Name {
|
||||||
// 극한의 성능 똥망 코드 탄생!
|
// 극한의 성능 똥망 코드 탄생!
|
||||||
// 무려 똑같은 걸 반복해서 돌리는!
|
// 무려 똑같은 걸 반복해서 돌리는!
|
||||||
for _, a := range commands.Discommand.Commands {
|
for _, a := range commands.Discommand.Commands {
|
||||||
|
@ -77,7 +114,7 @@ func main() {
|
||||||
go dg.ApplicationCommandCreate(dg.State.User.ID, "", cmd.ApplicationCommand)
|
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)
|
log.Println("[goMuffin] 봇이 실행되고 있어요. 버전:", configs.MUFFIN_VERSION)
|
||||||
sc := make(chan os.Signal, 1)
|
sc := make(chan os.Signal, 1)
|
||||||
|
|
66
modals/paginationEmbed.go
Normal file
66
modals/paginationEmbed.go
Normal file
|
@ -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)
|
||||||
|
},
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
"git.wh64.net/muffin/goMuffin/configs"
|
"git.wh64.net/muffin/goMuffin/configs"
|
||||||
|
|
||||||
|
"github.com/devproje/commando"
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
"go.mongodb.org/mongo-driver/v2/mongo"
|
"go.mongodb.org/mongo-driver/v2/mongo"
|
||||||
|
@ -19,15 +20,17 @@ import (
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
// 이 스크립트는 MariaDB -> MongoDB로의 전환을 위해 만들었음.
|
// 이 스크립트는 MariaDB -> MongoDB로의 전환을 위해 만들었음.
|
||||||
func DBMigrate() {
|
func DBMigrate(n *commando.Node) error {
|
||||||
mariaURL := os.Getenv("PREVIOUS_DATABASE_URL")
|
mariaURL := os.Getenv("PREVIOUS_DATABASE_URL")
|
||||||
mongoURL := configs.Config.DatabaseURL
|
mongoURL := configs.Config.DatabaseURL
|
||||||
dbName := configs.Config.DBName
|
dbName := configs.Config.DatabaseName
|
||||||
|
|
||||||
dbConnectionQuery := "?parseTime=true"
|
dbConnectionQuery := "?parseTime=true"
|
||||||
|
|
||||||
wg.Add(3)
|
wg.Add(3)
|
||||||
|
|
||||||
|
fmt.Println("[경고] 해당 명령어는 다음 버전에서 사라져요.")
|
||||||
|
|
||||||
// statement -> text
|
// statement -> text
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
@ -189,4 +192,5 @@ func DBMigrate() {
|
||||||
// 모든 고루틴이 끝날 떄 까지 대기
|
// 모든 고루틴이 끝날 떄 까지 대기
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
fmt.Println("데이터 마이그레이션이 끝났어요.")
|
fmt.Println("데이터 마이그레이션이 끝났어요.")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package scripts
|
package scripts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -10,40 +9,45 @@ import (
|
||||||
|
|
||||||
"git.wh64.net/muffin/goMuffin/configs"
|
"git.wh64.net/muffin/goMuffin/configs"
|
||||||
"github.com/bwmarrin/discordgo"
|
"github.com/bwmarrin/discordgo"
|
||||||
|
"github.com/devproje/commando"
|
||||||
|
"github.com/devproje/commando/option"
|
||||||
)
|
)
|
||||||
|
|
||||||
func DeleteAllCommands() {
|
func DeleteAllCommands(n *commando.Node) error {
|
||||||
var answer string
|
var answer string
|
||||||
id := flag.String("id", "", "디스코드 봇의 토큰")
|
|
||||||
|
|
||||||
flag.Parse()
|
id, err := option.ParseString(*n.MustGetOpt("id"), n)
|
||||||
|
if err != nil {
|
||||||
fmt.Printf("정말로 모든 명령어를 삭제하시겠어요? [y/N]: ")
|
return err
|
||||||
fmt.Scanf("%s", &answer)
|
|
||||||
if strings.ToLower(answer) != "y" && strings.ToLower(answer) != "yes" {
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if *id == "" {
|
yes, _ := option.ParseBool(*n.MustGetOpt("isYes"), n)
|
||||||
panic(fmt.Errorf("--id 플래그의 값이 필요해요"))
|
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{}
|
c := http.Client{}
|
||||||
req, err := http.NewRequest("PUT", discordgo.EndpointApplicationGlobalCommands(*id), nil)
|
req, err := http.NewRequest("PUT", discordgo.EndpointApplicationGlobalCommands(id), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Add("Authorization", "Bot "+configs.Config.Bot.Token)
|
req.Header.Add("Authorization", "Bot "+configs.Config.Bot.Token)
|
||||||
|
|
||||||
resp, err := c.Do(req)
|
resp, err := c.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes, err := io.ReadAll(resp.Body)
|
bytes, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println(string(bytes))
|
fmt.Println(string(bytes))
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
321
scripts/export.go
Normal file
321
scripts/export.go
Normal file
|
@ -0,0 +1,321 @@
|
||||||
|
package scripts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"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()
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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 saveFileToJSONL(path, name string, data any) error {
|
||||||
|
var content string
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
defer databases.Database.Client.Disconnect(context.TODO())
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
ch := make(chan error, 3)
|
||||||
|
|
||||||
|
fileType, err := option.ParseString(*n.MustGetOpt("type"), n)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileType != "json" && fileType != "jsonl" {
|
||||||
|
return fmt.Errorf("파일 형식은 txt또는 json또는 jsonl이여야 해요")
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
path += "/" + getDate()
|
||||||
|
|
||||||
|
err = checkDir(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Add(3)
|
||||||
|
|
||||||
|
if fileType == "jsonl" {
|
||||||
|
fmt.Println("NOTE: 파일 형식이 'jsonl'인 경우 일부데이터만 추출 됩니다.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 머핀 데이터 추출
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
var data []databases.Text
|
||||||
|
|
||||||
|
cur, err := databases.Database.Texts.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.RegexpEmoji.Match([]byte(text.Text)) {
|
||||||
|
data = append(data[:i], data[i+1:]...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
text.Text = strings.TrimPrefix(text.Text, "머핀아 ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileType == "json" {
|
||||||
|
err = saveFileToJSON(path, "muffin", data)
|
||||||
|
if err != nil {
|
||||||
|
ch <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if fileType == "jsonl" {
|
||||||
|
var newData []textJSONLData
|
||||||
|
|
||||||
|
for _, data := range data {
|
||||||
|
newData = append(newData, textJSONLData{data.Text, ""})
|
||||||
|
}
|
||||||
|
|
||||||
|
err = saveFileToJSONL(path, "muffin", newData)
|
||||||
|
if err != nil {
|
||||||
|
ch <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("머핀 데이터 추출 완료")
|
||||||
|
}()
|
||||||
|
|
||||||
|
// nsfw 데이터 추출
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
var data []databases.Text
|
||||||
|
|
||||||
|
cur, err := databases.Database.Texts.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
|
||||||
|
}
|
||||||
|
|
||||||
|
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 데이터 추출 완료")
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 지식 데이터 추출
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
var data []databases.Learn
|
||||||
|
|
||||||
|
cur, err := databases.Database.Learns.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
|
||||||
|
}
|
||||||
|
|
||||||
|
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("지식 데이터 추출 완료")
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
close(ch)
|
||||||
|
|
||||||
|
for err = range ch {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,7 +1,89 @@
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DeleteLearnedData = "#muffin/deleteLearnedData$"
|
DeleteLearnedData = "#muffin/deleteLearnedData@"
|
||||||
DeleteLearnedDataUserId = "#muffin/deleteLearnedData@"
|
DeleteLearnedDataUserId = "#muffin/deleteLearnedData@"
|
||||||
DeleteLearnedDataCancel = "#muffin/deleteLearnedData/cancel@"
|
DeleteLearnedDataCancel = "#muffin/deleteLearnedData/cancel@"
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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(RegexpItemId.ReplaceAllString(customId[len(DeleteLearnedData):], ""), "&", ""))
|
||||||
|
stringItemId := strings.ReplaceAll(RegexpItemId.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) 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 {
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(customId, PaginationEmbedPrev):
|
||||||
|
return customId[len(PaginationEmbedPrev):]
|
||||||
|
case strings.HasPrefix(customId, PaginationEmbedPages):
|
||||||
|
return customId[len(PaginationEmbedPages):]
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPaginationEmbedUserId(id string) string {
|
||||||
|
return RegexpPaginationEmbedId.FindAllStringSubmatch(id, 1)[0][1]
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,20 @@
|
||||||
package utils
|
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
|
// InteractionCreate custom data of discordgo.InteractionCreate
|
||||||
type InteractionCreate struct {
|
type InteractionCreate struct {
|
||||||
|
@ -62,3 +76,44 @@ func (i *InteractionCreate) Update(data *discordgo.InteractionResponseData) {
|
||||||
Data: data,
|
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
|
||||||
|
}
|
||||||
|
|
225
utils/paginationEmbed.go
Normal file
225
utils/paginationEmbed.go
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
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
|
||||||
|
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{
|
||||||
|
discordgo.Button{
|
||||||
|
Style: discordgo.PrimaryButton,
|
||||||
|
Label: "이전",
|
||||||
|
CustomID: MakePaginationEmbedPrev(id),
|
||||||
|
Disabled: disabled,
|
||||||
|
},
|
||||||
|
discordgo.Button{
|
||||||
|
Style: discordgo.SecondaryButton,
|
||||||
|
Label: fmt.Sprintf("(%d/%d)", current, total),
|
||||||
|
CustomID: MakePaginationEmbedPages(id),
|
||||||
|
Disabled: disabled,
|
||||||
|
},
|
||||||
|
discordgo.Button{
|
||||||
|
Style: discordgo.PrimaryButton,
|
||||||
|
Label: "다음",
|
||||||
|
CustomID: MakePaginationEmbedNext(id),
|
||||||
|
Disabled: disabled,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, defaultDesc string) {
|
||||||
|
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,
|
||||||
|
desc: defaultDesc,
|
||||||
|
}
|
||||||
|
|
||||||
|
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, p.Current, p.Total),
|
||||||
|
})
|
||||||
|
case *InteractionCreate:
|
||||||
|
m.EditReply(&discordgo.WebhookEdit{
|
||||||
|
Embeds: &[]*discordgo.MessageEmbed{p.Embed},
|
||||||
|
Components: makeComponents(id, p.Current, p.Total),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Flags: discordgo.MessageFlagsEphemeral,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PaginationEmbed) Next(i *InteractionCreate) {
|
||||||
|
if p.Current >= p.Total {
|
||||||
|
i.Reply(&discordgo.InteractionResponseData{
|
||||||
|
Embeds: []*discordgo.MessageEmbed{
|
||||||
|
{
|
||||||
|
Title: "❌ 오류",
|
||||||
|
Description: "해당 페이지가 마지막ㅇ이에요.",
|
||||||
|
Color: EmbedFail,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Flags: discordgo.MessageFlagsEphemeral,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
|
@ -2,6 +2,13 @@ package utils
|
||||||
|
|
||||||
import "regexp"
|
import "regexp"
|
||||||
|
|
||||||
var FlexibleStringParser *regexp.Regexp = regexp.MustCompile("[^\\s\"'「」«»]+|\"([^\"]*)\"|'([^']*)'|「([^」]*)」|«([^»]*)»")
|
var (
|
||||||
var Decimals *regexp.Regexp = regexp.MustCompile(`\d+`)
|
RegexpFlexibleString = regexp.MustCompile(`[^\s"'「」«»]+|"([^"]*)"|'([^']*)'|「([^」]*)」|«([^»]*)»`)
|
||||||
var ItemIdRegexp *regexp.Regexp = regexp.MustCompile(`No.\d+`)
|
RegexpDecimals = regexp.MustCompile(`\d+`)
|
||||||
|
RegexpItemId = regexp.MustCompile(`No.\d+`)
|
||||||
|
RegexpEmoji = regexp.MustCompile(`<a?:\w+:\d+>`)
|
||||||
|
RegexpLearnQueryCommand = regexp.MustCompile(`단어:([^\n대답개수:]*)`)
|
||||||
|
RegexpLearnQueryResult = regexp.MustCompile(`대답:([^\n단어개수:]*)`)
|
||||||
|
RegexpLearnQueryLength = regexp.MustCompile(`개수:(\d+)`)
|
||||||
|
RegexpPaginationEmbedId = regexp.MustCompile(`^(\d+)/(\d+)$`)
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in a new issue