diff --git a/add.go b/add.go new file mode 100644 index 0000000..c74ab85 --- /dev/null +++ b/add.go @@ -0,0 +1,25 @@ +package main + +import ( + "strings" + + "github.com/bwmarrin/discordgo" + + "github.com/nint8835/scribe/database" +) + +type AddArgs struct { + Text string `description:"Text for the quote to add. To insert a new line, insert \\n."` + Author discordgo.User `description:"Author of the quote."` + Source *string `description:"Link to a source for the quote, if available (such as a Discord message, screenshot, etc.)"` +} + +func AddQuoteCommand(_ *discordgo.Session, interaction *discordgo.InteractionCreate, args AddArgs) { + quote := database.Quote{ + Text: strings.Replace(args.Text, "\\n", "\n", -1), + Authors: []*database.Author{{ID: args.Author.ID}}, + Source: args.Source, + } + + addQuote(quote, interaction) +} diff --git a/add_message.go b/add_message.go new file mode 100644 index 0000000..8692fd8 --- /dev/null +++ b/add_message.go @@ -0,0 +1,38 @@ +package main + +import ( + "fmt" + + "github.com/bwmarrin/discordgo" + + "github.com/nint8835/scribe/database" +) + +func AddQuoteMessageCommand(_ *discordgo.Session, interaction *discordgo.InteractionCreate, message *discordgo.Message) { + if message.Content == "" { + Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Embeds: []*discordgo.MessageEmbed{ + { + Color: (240 << 16) + (85 << 8) + (125), + Title: "Error adding quote.", + Description: "You cannot quote an empty message.", + }, + }, + Flags: discordgo.MessageFlagsEphemeral, + }, + }) + return + } + + quoteUrl := fmt.Sprintf("https://discord.com/channels/%s/%s/%s", message.GuildID, message.ChannelID, message.ID) + + quote := database.Quote{ + Text: message.Content, + Authors: []*database.Author{{ID: message.Author.ID}}, + Source: "eUrl, + } + + addQuote(quote, interaction) +} diff --git a/bot.go b/bot.go index a830b7a..70f99ac 100644 --- a/bot.go +++ b/bot.go @@ -70,6 +70,63 @@ func Run() error { return nil } +func RegisterCommands(parser *switchboard.Switchboard) { + _ = parser.AddCommand(&switchboard.Command{ + Name: "add", + Description: "Add a new quote.", + Handler: AddQuoteCommand, + GuildID: config.GuildId, + }) + _ = parser.AddCommand(&switchboard.Command{ + Name: "Quote Message", + Handler: AddQuoteMessageCommand, + GuildID: config.GuildId, + Type: switchboard.MessageCommand, + }) + _ = parser.AddCommand(&switchboard.Command{ + Name: "get", + Description: "Display an individual quote by ID.", + Handler: GetQuoteCommand, + GuildID: config.GuildId, + }) + _ = parser.AddCommand(&switchboard.Command{ + Name: "random", + Description: "Get a random quote.", + Handler: RandomQuoteCommand, + GuildID: config.GuildId, + }) + _ = parser.AddCommand(&switchboard.Command{ + Name: "list", + Description: "List quotes.", + Handler: ListQuotesCommand, + GuildID: config.GuildId, + }) + _ = parser.AddCommand(&switchboard.Command{ + Name: "remove", + Description: "Remove a quote.", + Handler: RemoveQuoteCommand, + GuildID: config.GuildId, + }) + _ = parser.AddCommand(&switchboard.Command{ + Name: "edit", + Description: "Edit a quote.", + Handler: EditQuoteCommand, + GuildID: config.GuildId, + }) + _ = parser.AddCommand(&switchboard.Command{ + Name: "search", + Description: "Search for quotes.", + Handler: SearchQuotesCommand, + GuildID: config.GuildId, + }) + _ = parser.AddCommand(&switchboard.Command{ + Name: "db", + Description: "Get a copy of the Scribe database.", + Handler: DbCommand, + GuildID: config.GuildId, + }) +} + func main() { err := Run() if err != nil { diff --git a/commands.go b/commands.go deleted file mode 100644 index 2d99133..0000000 --- a/commands.go +++ /dev/null @@ -1,459 +0,0 @@ -package main - -import ( - "fmt" - "math/rand" - "os" - "strings" - - "github.com/bwmarrin/discordgo" - "gorm.io/gorm/clause" - "pkg.nit.so/switchboard" - - "github.com/nint8835/scribe/database" -) - -type AddArgs struct { - Text string `description:"Text for the quote to add. To insert a new line, insert \\n."` - Author discordgo.User `description:"Author of the quote."` - Source *string `description:"Link to a source for the quote, if available (such as a Discord message, screenshot, etc.)"` -} - -func AddQuoteCommand(_ *discordgo.Session, interaction *discordgo.InteractionCreate, args AddArgs) { - quote := database.Quote{ - Text: strings.Replace(args.Text, "\\n", "\n", -1), - Authors: []*database.Author{{ID: args.Author.ID}}, - Source: args.Source, - } - - addQuote(quote, interaction) -} - -func AddQuoteMessageCommand(_ *discordgo.Session, interaction *discordgo.InteractionCreate, message *discordgo.Message) { - if message.Content == "" { - Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Embeds: []*discordgo.MessageEmbed{ - { - Color: (240 << 16) + (85 << 8) + (125), - Title: "Error adding quote.", - Description: "You cannot quote an empty message.", - }, - }, - Flags: discordgo.MessageFlagsEphemeral, - }, - }) - return - } - - quoteUrl := fmt.Sprintf("https://discord.com/channels/%s/%s/%s", message.GuildID, message.ChannelID, message.ID) - - quote := database.Quote{ - Text: message.Content, - Authors: []*database.Author{{ID: message.Author.ID}}, - Source: "eUrl, - } - - addQuote(quote, interaction) -} - -type GetArgs struct { - ID int `description:"ID of the quote to display."` -} - -func GetQuoteCommand(_ *discordgo.Session, interaction *discordgo.InteractionCreate, args GetArgs) { - var quote database.Quote - - result := database.Instance.Model(&database.Quote{}).Preload(clause.Associations).First("e, args.ID) - if result.Error != nil { - Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Content: fmt.Sprintf("Error getting quote.\n```\n%s\n```", result.Error), - }, - }) - return - } - - embed, err := MakeQuoteEmbed("e, interaction.GuildID) - if err != nil { - Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Content: fmt.Sprintf("Error getting quote.\n```\n%s\n```", err), - }, - }) - return - } - - Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Embeds: []*discordgo.MessageEmbed{embed}, - }, - }) -} - -func RandomQuoteCommand(_ *discordgo.Session, interaction *discordgo.InteractionCreate, _ struct{}) { - var quotes []database.Quote - - result := database.Instance.Model(&database.Quote{}).Preload(clause.Associations).Find("es) - if result.Error != nil { - Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Content: fmt.Sprintf("Error getting quotes.\n```\n%s\n```", result.Error), - }, - }) - return - } - - quote := quotes[rand.Intn(len(quotes))] - - embed, err := MakeQuoteEmbed("e, interaction.GuildID) - if err != nil { - Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Content: fmt.Sprintf("Error getting quote.\n```\n%s\n```", err), - }, - }) - return - } - - Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Embeds: []*discordgo.MessageEmbed{embed}, - }, - }) -} - -type ListArgs struct { - Author *discordgo.User `description:"Author to display quotes for. Omit to display quotes from all users."` - Page int `default:"1" description:"Page of quotes to display."` -} - -func ListQuotesCommand(_ *discordgo.Session, interaction *discordgo.InteractionCreate, args ListArgs) { - var quotes []database.Quote - - query := database.Instance.Model(&database.Quote{}).Preload(clause.Associations) - - if args.Author != nil { - query = query. - Joins("INNER JOIN quote_authors ON quote_authors.quote_id = quotes.id"). - Where(map[string]interface{}{"quote_authors.author_id": args.Author.ID}) - } - - result := query.Limit(5).Offset(5 * (args.Page - 1)).Find("es) - if result.Error != nil { - Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Content: fmt.Sprintf("Error getting quotes.\n```\n%s\n```", result.Error), - }, - }) - return - } - - embed := discordgo.MessageEmbed{ - Title: "Quotes", - Color: (40 << 16) + (120 << 8) + (120), - Fields: []*discordgo.MessageEmbedField{}, - } - - for _, quote := range quotes { - authors, _, err := GenerateAuthorString(quote.Authors, interaction.GuildID) - if err != nil { - Bot.ChannelMessageSend(interaction.ChannelID, fmt.Sprintf("Error getting quote authors.\n```\n%s\n```", result.Error)) - } - - quoteText := quote.Text - if len(quoteText) >= 900 { - quoteText = quoteText[:900] + "..." - } - - quoteBody := fmt.Sprintf("%s\n\n__", quoteText, quote.Meta.CreatedAt.UTC().Unix()) - if quote.Source != nil { - quoteBody += fmt.Sprintf(" - [Source](%s)", *quote.Source) - } - embed.Fields = append(embed.Fields, &discordgo.MessageEmbedField{ - Name: fmt.Sprintf("%d - %s", quote.Meta.ID, authors), - Value: quoteBody, - }) - } - - Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Embeds: []*discordgo.MessageEmbed{&embed}, - }, - }) -} - -type RemoveArgs struct { - ID int `description:"ID of the quote to remove."` -} - -func RemoveQuoteCommand(_ *discordgo.Session, interaction *discordgo.InteractionCreate, args RemoveArgs) { - if interaction.Member.User.ID != config.OwnerId { - Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Content: "You do not have access to that command.", - }, - }) - return - } - - var quote database.Quote - result := database.Instance.Model(&database.Quote{}).Preload(clause.Associations).First("e, args.ID) - if result.Error != nil { - Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Content: fmt.Sprintf("Error getting quote.\n```\n%s\n```", result.Error), - }, - }) - return - } - - result = database.Instance.Delete("e) - if result.Error != nil { - Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Content: fmt.Sprintf("Error deleting quote.\n```\n%s\n```", result.Error), - }, - }) - return - } - - embed := discordgo.MessageEmbed{ - Title: "Quote deleted!", - Description: fmt.Sprintf("Quote %d has been deleted succesfully.", args.ID), - Color: (240 << 16) + (85 << 8) + (125), - } - Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Embeds: []*discordgo.MessageEmbed{&embed}, - }, - }) -} - -type EditArgs struct { - ID int `description:"ID of the quote to edit."` - Text string `description:"New text for the quote."` -} - -func EditQuoteCommand(_ *discordgo.Session, interaction *discordgo.InteractionCreate, args EditArgs) { - if interaction.Member.User.ID != config.OwnerId { - Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Content: "You do not have access to that command.", - }, - }) - return - } - - var quote database.Quote - result := database.Instance.Model(&database.Quote{}).Preload(clause.Associations).First("e, args.ID) - if result.Error != nil { - Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Content: fmt.Sprintf("Error getting quote.\n```\n%s\n```", result.Error), - }, - }) - return - } - - quote.Text = strings.Replace(args.Text, "\\n", "\n", -1) - - result = database.Instance.Save("e) - if result.Error != nil { - Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Content: fmt.Sprintf("Error editing quote.\n```\n%s\n```", result.Error), - }, - }) - return - } - - Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Embeds: []*discordgo.MessageEmbed{ - { - Title: "Quote edited!", - Color: (45 << 16) + (200 << 8) + (95), - Description: "The quote has been edited successfully.", - }, - }, - }, - }) -} - -type SearchArgs struct { - Query string `description:"Keyword / phrase to search for."` - Page int `default:"1" description:"Page of results to display."` -} - -func SearchQuotesCommand(_ *discordgo.Session, interaction *discordgo.InteractionCreate, args SearchArgs) { - if result := database.Instance.Exec("PRAGMA case_sensitive_like = OFF", nil); result.Error != nil { - Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Content: fmt.Sprintf("Error enabling case-insensitive like.\n```\n%s\n```", result.Error), - }, - }) - return - } - - var quotes []database.Quote - - query := database.Instance.Model(&database.Quote{}). - Preload(clause.Associations) - - if strings.Contains(args.Query, "%") { - query = query.Where("text LIKE ?", args.Query) - } else { - query = query.Where("text LIKE ?", "% "+args.Query+" %"). - Or("text LIKE ?", "% "+args.Query). - Or("text LIKE ?", args.Query+" %") - } - - result := query. - Limit(5). - Offset(int(5 * (args.Page - 1))). - Find("es) - if result.Error != nil { - Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Content: fmt.Sprintf("Error getting quotes.\n```\n%s\n```", result.Error), - }, - }) - return - } - - embed := discordgo.MessageEmbed{ - Title: "Quotes", - Color: (40 << 16) + (120 << 8) + (120), - Fields: []*discordgo.MessageEmbedField{}, - } - - for _, quote := range quotes { - authors, _, err := GenerateAuthorString(quote.Authors, interaction.GuildID) - if err != nil { - Bot.ChannelMessageSend(interaction.ChannelID, fmt.Sprintf("Error getting quote authors.\n```\n%s\n```", result.Error)) - } - - quoteText := quote.Text - if len(quoteText) >= 900 { - quoteText = quoteText[:900] + "..." - } - - quoteBody := fmt.Sprintf("%s\n\n__", quoteText, quote.Meta.CreatedAt.UTC().Unix()) - if quote.Source != nil { - quoteBody += fmt.Sprintf(" - [Source](%s)", *quote.Source) - } - embed.Fields = append(embed.Fields, &discordgo.MessageEmbedField{ - Name: fmt.Sprintf("%d - %s", quote.Meta.ID, authors), - Value: quoteBody, - }) - } - - Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Embeds: []*discordgo.MessageEmbed{&embed}, - }, - }) -} - -func DbCommand(_ *discordgo.Session, interaction *discordgo.InteractionCreate, args struct{}) { - dbFile, err := os.Open(config.DBPath) - if err != nil { - Bot.ChannelMessageSend(interaction.ChannelID, fmt.Sprintf("Error opening database.\n```\n%s\n```", err)) - return - } - - err = Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Files: []*discordgo.File{ - { - Name: "quotes.sqlite", - ContentType: "application/x-sqlite3", - Reader: dbFile, - }, - }, - }, - }) - - if err != nil { - Bot.ChannelMessageSend(interaction.ChannelID, fmt.Sprintf("Error sending database.\n```\n%s\n```", err)) - } -} - -func RegisterCommands(parser *switchboard.Switchboard) { - _ = parser.AddCommand(&switchboard.Command{ - Name: "add", - Description: "Add a new quote.", - Handler: AddQuoteCommand, - GuildID: config.GuildId, - }) - _ = parser.AddCommand(&switchboard.Command{ - Name: "Quote Message", - Handler: AddQuoteMessageCommand, - GuildID: config.GuildId, - Type: switchboard.MessageCommand, - }) - _ = parser.AddCommand(&switchboard.Command{ - Name: "get", - Description: "Display an individual quote by ID.", - Handler: GetQuoteCommand, - GuildID: config.GuildId, - }) - _ = parser.AddCommand(&switchboard.Command{ - Name: "random", - Description: "Get a random quote.", - Handler: RandomQuoteCommand, - GuildID: config.GuildId, - }) - _ = parser.AddCommand(&switchboard.Command{ - Name: "list", - Description: "List quotes.", - Handler: ListQuotesCommand, - GuildID: config.GuildId, - }) - _ = parser.AddCommand(&switchboard.Command{ - Name: "remove", - Description: "Remove a quote.", - Handler: RemoveQuoteCommand, - GuildID: config.GuildId, - }) - _ = parser.AddCommand(&switchboard.Command{ - Name: "edit", - Description: "Edit a quote.", - Handler: EditQuoteCommand, - GuildID: config.GuildId, - }) - _ = parser.AddCommand(&switchboard.Command{ - Name: "search", - Description: "Search for quotes.", - Handler: SearchQuotesCommand, - GuildID: config.GuildId, - }) - _ = parser.AddCommand(&switchboard.Command{ - Name: "db", - Description: "Get a copy of the Scribe database.", - Handler: DbCommand, - GuildID: config.GuildId, - }) -} diff --git a/db.go b/db.go new file mode 100644 index 0000000..082ae26 --- /dev/null +++ b/db.go @@ -0,0 +1,33 @@ +package main + +import ( + "fmt" + "os" + + "github.com/bwmarrin/discordgo" +) + +func DbCommand(_ *discordgo.Session, interaction *discordgo.InteractionCreate, args struct{}) { + dbFile, err := os.Open(config.DBPath) + if err != nil { + Bot.ChannelMessageSend(interaction.ChannelID, fmt.Sprintf("Error opening database.\n```\n%s\n```", err)) + return + } + + err = Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Files: []*discordgo.File{ + { + Name: "quotes.sqlite", + ContentType: "application/x-sqlite3", + Reader: dbFile, + }, + }, + }, + }) + + if err != nil { + Bot.ChannelMessageSend(interaction.ChannelID, fmt.Sprintf("Error sending database.\n```\n%s\n```", err)) + } +} diff --git a/edit.go b/edit.go new file mode 100644 index 0000000..2f6b695 --- /dev/null +++ b/edit.go @@ -0,0 +1,66 @@ +package main + +import ( + "fmt" + "strings" + + "github.com/bwmarrin/discordgo" + "gorm.io/gorm/clause" + + "github.com/nint8835/scribe/database" +) + +type EditArgs struct { + ID int `description:"ID of the quote to edit."` + Text string `description:"New text for the quote."` +} + +func EditQuoteCommand(_ *discordgo.Session, interaction *discordgo.InteractionCreate, args EditArgs) { + if interaction.Member.User.ID != config.OwnerId { + Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: "You do not have access to that command.", + }, + }) + return + } + + var quote database.Quote + result := database.Instance.Model(&database.Quote{}).Preload(clause.Associations).First("e, args.ID) + if result.Error != nil { + Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: fmt.Sprintf("Error getting quote.\n```\n%s\n```", result.Error), + }, + }) + return + } + + quote.Text = strings.Replace(args.Text, "\\n", "\n", -1) + + result = database.Instance.Save("e) + if result.Error != nil { + Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: fmt.Sprintf("Error editing quote.\n```\n%s\n```", result.Error), + }, + }) + return + } + + Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Embeds: []*discordgo.MessageEmbed{ + { + Title: "Quote edited!", + Color: (45 << 16) + (200 << 8) + (95), + Description: "The quote has been edited successfully.", + }, + }, + }, + }) +} diff --git a/get.go b/get.go new file mode 100644 index 0000000..d1c382d --- /dev/null +++ b/get.go @@ -0,0 +1,47 @@ +package main + +import ( + "fmt" + + "github.com/bwmarrin/discordgo" + "gorm.io/gorm/clause" + + "github.com/nint8835/scribe/database" +) + +type GetArgs struct { + ID int `description:"ID of the quote to display."` +} + +func GetQuoteCommand(_ *discordgo.Session, interaction *discordgo.InteractionCreate, args GetArgs) { + var quote database.Quote + + result := database.Instance.Model(&database.Quote{}).Preload(clause.Associations).First("e, args.ID) + if result.Error != nil { + Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: fmt.Sprintf("Error getting quote.\n```\n%s\n```", result.Error), + }, + }) + return + } + + embed, err := MakeQuoteEmbed("e, interaction.GuildID) + if err != nil { + Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: fmt.Sprintf("Error getting quote.\n```\n%s\n```", err), + }, + }) + return + } + + Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Embeds: []*discordgo.MessageEmbed{embed}, + }, + }) +} diff --git a/list.go b/list.go new file mode 100644 index 0000000..6a48891 --- /dev/null +++ b/list.go @@ -0,0 +1,72 @@ +package main + +import ( + "fmt" + + "github.com/bwmarrin/discordgo" + "gorm.io/gorm/clause" + + "github.com/nint8835/scribe/database" +) + +type ListArgs struct { + Author *discordgo.User `description:"Author to display quotes for. Omit to display quotes from all users."` + Page int `default:"1" description:"Page of quotes to display."` +} + +func ListQuotesCommand(_ *discordgo.Session, interaction *discordgo.InteractionCreate, args ListArgs) { + var quotes []database.Quote + + query := database.Instance.Model(&database.Quote{}).Preload(clause.Associations) + + if args.Author != nil { + query = query. + Joins("INNER JOIN quote_authors ON quote_authors.quote_id = quotes.id"). + Where(map[string]interface{}{"quote_authors.author_id": args.Author.ID}) + } + + result := query.Limit(5).Offset(5 * (args.Page - 1)).Find("es) + if result.Error != nil { + Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: fmt.Sprintf("Error getting quotes.\n```\n%s\n```", result.Error), + }, + }) + return + } + + embed := discordgo.MessageEmbed{ + Title: "Quotes", + Color: (40 << 16) + (120 << 8) + (120), + Fields: []*discordgo.MessageEmbedField{}, + } + + for _, quote := range quotes { + authors, _, err := GenerateAuthorString(quote.Authors, interaction.GuildID) + if err != nil { + Bot.ChannelMessageSend(interaction.ChannelID, fmt.Sprintf("Error getting quote authors.\n```\n%s\n```", result.Error)) + } + + quoteText := quote.Text + if len(quoteText) >= 900 { + quoteText = quoteText[:900] + "..." + } + + quoteBody := fmt.Sprintf("%s\n\n__", quoteText, quote.Meta.CreatedAt.UTC().Unix()) + if quote.Source != nil { + quoteBody += fmt.Sprintf(" - [Source](%s)", *quote.Source) + } + embed.Fields = append(embed.Fields, &discordgo.MessageEmbedField{ + Name: fmt.Sprintf("%d - %s", quote.Meta.ID, authors), + Value: quoteBody, + }) + } + + Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Embeds: []*discordgo.MessageEmbed{&embed}, + }, + }) +} diff --git a/random.go b/random.go new file mode 100644 index 0000000..32266cb --- /dev/null +++ b/random.go @@ -0,0 +1,46 @@ +package main + +import ( + "fmt" + "math/rand" + + "github.com/bwmarrin/discordgo" + "gorm.io/gorm/clause" + + "github.com/nint8835/scribe/database" +) + +func RandomQuoteCommand(_ *discordgo.Session, interaction *discordgo.InteractionCreate, _ struct{}) { + var quotes []database.Quote + + result := database.Instance.Model(&database.Quote{}).Preload(clause.Associations).Find("es) + if result.Error != nil { + Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: fmt.Sprintf("Error getting quotes.\n```\n%s\n```", result.Error), + }, + }) + return + } + + quote := quotes[rand.Intn(len(quotes))] + + embed, err := MakeQuoteEmbed("e, interaction.GuildID) + if err != nil { + Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: fmt.Sprintf("Error getting quote.\n```\n%s\n```", err), + }, + }) + return + } + + Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Embeds: []*discordgo.MessageEmbed{embed}, + }, + }) +} diff --git a/remove.go b/remove.go new file mode 100644 index 0000000..f8ccca0 --- /dev/null +++ b/remove.go @@ -0,0 +1,61 @@ +package main + +import ( + "fmt" + + "github.com/bwmarrin/discordgo" + "gorm.io/gorm/clause" + + "github.com/nint8835/scribe/database" +) + +type RemoveArgs struct { + ID int `description:"ID of the quote to remove."` +} + +func RemoveQuoteCommand(_ *discordgo.Session, interaction *discordgo.InteractionCreate, args RemoveArgs) { + if interaction.Member.User.ID != config.OwnerId { + Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: "You do not have access to that command.", + }, + }) + return + } + + var quote database.Quote + result := database.Instance.Model(&database.Quote{}).Preload(clause.Associations).First("e, args.ID) + if result.Error != nil { + Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: fmt.Sprintf("Error getting quote.\n```\n%s\n```", result.Error), + }, + }) + return + } + + result = database.Instance.Delete("e) + if result.Error != nil { + Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: fmt.Sprintf("Error deleting quote.\n```\n%s\n```", result.Error), + }, + }) + return + } + + embed := discordgo.MessageEmbed{ + Title: "Quote deleted!", + Description: fmt.Sprintf("Quote %d has been deleted succesfully.", args.ID), + Color: (240 << 16) + (85 << 8) + (125), + } + Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Embeds: []*discordgo.MessageEmbed{&embed}, + }, + }) +} diff --git a/search.go b/search.go new file mode 100644 index 0000000..96407da --- /dev/null +++ b/search.go @@ -0,0 +1,89 @@ +package main + +import ( + "fmt" + "strings" + + "github.com/bwmarrin/discordgo" + "gorm.io/gorm/clause" + + "github.com/nint8835/scribe/database" +) + +type SearchArgs struct { + Query string `description:"Keyword / phrase to search for."` + Page int `default:"1" description:"Page of results to display."` +} + +func SearchQuotesCommand(_ *discordgo.Session, interaction *discordgo.InteractionCreate, args SearchArgs) { + if result := database.Instance.Exec("PRAGMA case_sensitive_like = OFF", nil); result.Error != nil { + Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: fmt.Sprintf("Error enabling case-insensitive like.\n```\n%s\n```", result.Error), + }, + }) + return + } + + var quotes []database.Quote + + query := database.Instance.Model(&database.Quote{}). + Preload(clause.Associations) + + if strings.Contains(args.Query, "%") { + query = query.Where("text LIKE ?", args.Query) + } else { + query = query.Where("text LIKE ?", "% "+args.Query+" %"). + Or("text LIKE ?", "% "+args.Query). + Or("text LIKE ?", args.Query+" %") + } + + result := query. + Limit(5). + Offset(int(5 * (args.Page - 1))). + Find("es) + if result.Error != nil { + Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: fmt.Sprintf("Error getting quotes.\n```\n%s\n```", result.Error), + }, + }) + return + } + + embed := discordgo.MessageEmbed{ + Title: "Quotes", + Color: (40 << 16) + (120 << 8) + (120), + Fields: []*discordgo.MessageEmbedField{}, + } + + for _, quote := range quotes { + authors, _, err := GenerateAuthorString(quote.Authors, interaction.GuildID) + if err != nil { + Bot.ChannelMessageSend(interaction.ChannelID, fmt.Sprintf("Error getting quote authors.\n```\n%s\n```", result.Error)) + } + + quoteText := quote.Text + if len(quoteText) >= 900 { + quoteText = quoteText[:900] + "..." + } + + quoteBody := fmt.Sprintf("%s\n\n__", quoteText, quote.Meta.CreatedAt.UTC().Unix()) + if quote.Source != nil { + quoteBody += fmt.Sprintf(" - [Source](%s)", *quote.Source) + } + embed.Fields = append(embed.Fields, &discordgo.MessageEmbedField{ + Name: fmt.Sprintf("%d - %s", quote.Meta.ID, authors), + Value: quoteBody, + }) + } + + Bot.InteractionRespond(interaction.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Embeds: []*discordgo.MessageEmbed{&embed}, + }, + }) +}