Skip to content

Commit

Permalink
feat(FAQ): added faq functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
Kesuaheli committed Jan 5, 2025
1 parent 328c7a0 commit 761c976
Show file tree
Hide file tree
Showing 15 changed files with 385 additions and 6 deletions.
17 changes: 13 additions & 4 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,22 @@ event:
name: 🔁
#id:
#animated: true
generic.back:
name: ↩️
#id:
#animated: true
generic.delete:
name: 🗑️
#id:
#animated: true

adventcalendar:
# Emoji for entering the advent calendar giveaway
enter: vote.check

faq:
all_questions: generic.back

random.coin:
heads:
name: 👤
Expand Down Expand Up @@ -129,10 +141,7 @@ event:
name: 🏠
#id:
#animated: true
invite.delete:
name: 🗑️
#id:
#animated: true
invite.delete: generic.delete
invite.nudge_match:
name: 👉
#id:
Expand Down
13 changes: 13 additions & 0 deletions data/lang/de.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ discord.command:
msg.self_hidden: Warum ist das unsichtbar?
msg.self_hidden.desc: Da du deinen Geburtstag als nicht sichtbar eingetragen hast, kannst diese Nachricht nur du sehen. Du kannst diese Nachricht nun schließen.
msg.page: Seite %d/%d
msg.button.delete: Schließen
msg.error.not_author: Das ist die Nachricht von %s. Du kannst das nicht verwenden!

birthday:
base: geburtstag
Expand Down Expand Up @@ -108,6 +110,17 @@ discord.command:
base.description: Häufig gestellte Fragen
display: FAQ

option.question: frage
option.question.description: Die Frage, nach der gesucht werden soll

msg.button.all_questions: Alle Fragen
msg.no_questions: Es hier noch keine häufig gestellten Fragen.
#TODO: Link slash command
msg.question_not_found: |-
Die Frage, nach der du gesucht hast, konnte nicht gefunden werden.
> %s
Versuche es erneut oder schau dir alle Fragen an indem du keine Frage in der suche angibst.
info:
base: info
base.description: Zeigt ein paar Infos über den Bot
Expand Down
13 changes: 13 additions & 0 deletions data/lang/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ discord.command:
msg.self_hidden: Why is this invisible?
msg.self_hidden.desc: Since you've set your birthday to not be visible, this message is also only visible to you. You can close this message now.
msg.page: Page %d/%d
msg.button.delete: Close
msg.error.not_author: This is the message of %s. You can't use this here!

birthday:
base: birthday
Expand Down Expand Up @@ -108,6 +110,17 @@ discord.command:
base.description: Frequently Asked Questions
display: FAQ

option.question: question
option.question.description: The question you want to ask

msg.button.all_questions: All questions
msg.no_questions: There are no frequently asked questions yet
#TODO: Link slash command
msg.question_not_found: |-
The question you searched for was not found.
> %s
Check again or list all questions by not searching anything.
info:
base: info
base.description: Displays some infos about the bot
Expand Down
3 changes: 3 additions & 0 deletions event/component/componentBase.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/bwmarrin/discordgo"
"github.com/cake4everyone/cake4everybot/logger"
"github.com/cake4everyone/cake4everybot/modules/adventcalendar"
"github.com/cake4everyone/cake4everybot/modules/faq"
"github.com/cake4everyone/cake4everybot/modules/random"
"github.com/cake4everyone/cake4everybot/modules/secretsanta"
)
Expand Down Expand Up @@ -34,6 +35,8 @@ func Register() {
var componentList []Component

componentList = append(componentList, adventcalendar.Component{})
componentList = append(componentList, faq.Component{})
componentList = append(componentList, GenericComponents{})
componentList = append(componentList, random.Component{})
componentList = append(componentList, secretsanta.Component{})

Expand Down
64 changes: 64 additions & 0 deletions event/component/handleGenericComponents.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package component

import (
"strings"

"github.com/bwmarrin/discordgo"
"github.com/cake4everyone/cake4everybot/util"
)

// GenericComponents is the Component handler for generic components
type GenericComponents struct {
util.InteractionUtil
member *discordgo.Member
user *discordgo.User
originalAuthor *discordgo.User
data discordgo.MessageComponentInteractionData
}

// Handle handles the functionality of a component interaction
func (gc GenericComponents) Handle(s *discordgo.Session, i *discordgo.InteractionCreate) {
gc.InteractionUtil = util.InteractionUtil{Session: s, Interaction: i}
gc.member = i.Member
gc.user = i.User
if i.Member != nil {
gc.user = i.Member.User
} else if i.User != nil {
gc.member = &discordgo.Member{User: i.User}
}
gc.data = i.MessageComponentData()

if gc.Interaction.Message.Type == discordgo.MessageTypeChatInputCommand {
gc.originalAuthor = gc.Interaction.Message.Interaction.User
} else {
gc.originalAuthor = gc.Interaction.Message.Author
}

ids := strings.Split(gc.data.CustomID, ".")
// pop the first level identifier
util.ShiftL(ids)

switch util.ShiftL(ids) {
case "delete":
if gc.RequireOriginalAuthor() {
gc.handleDelete()
}
return
default:
log.Printf("Unknown component interaction ID: %s", gc.data.CustomID)
gc.ReplyError()
}
}

// ID returns the component ID to identify the module
func (gc GenericComponents) ID() string {
return "generic"
}

func (gc GenericComponents) handleDelete() {
err := gc.Session.ChannelMessageDelete(gc.Interaction.ChannelID, gc.Interaction.Message.ID)
if err != nil {
log.Printf("ERROR: could not delete message %s/%s: %+v", gc.Interaction.ChannelID, gc.Interaction.Message.ID, err)
gc.ReplyError()
}
}
24 changes: 24 additions & 0 deletions modules/faq/chatCommand.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ func (cmd Chat) AppCmd() *discordgo.ApplicationCommand {
NameLocalizations: util.TranslateLocalization(tp + "base"),
Description: lang.GetDefault(tp + "base.description"),
DescriptionLocalizations: util.TranslateLocalization(tp + "base.description"),
Options: []*discordgo.ApplicationCommandOption{
{
Type: discordgo.ApplicationCommandOptionString,
Name: lang.GetDefault(tp + "option.question"),
Description: lang.GetDefault(tp + "option.question.description"),
Required: false,
Autocomplete: true,
},
},
}
}

Expand All @@ -38,6 +47,21 @@ func (cmd Chat) Handle(s *discordgo.Session, i *discordgo.InteractionCreate) {
} else if i.User != nil {
cmd.member = &discordgo.Member{User: i.User}
}

data := i.ApplicationCommandData()
var question string
for _, option := range data.Options {
switch option.Name {
case lang.GetDefault(tp + "option.question"):
question = option.StringValue()
}
}

if i.Type == discordgo.InteractionApplicationCommandAutocomplete {
cmd.handleAutocomplete(question)
} else {
cmd.handleCommand(question)
}
}

// SetID sets the registered command ID for internal uses after uploading to discord
Expand Down
12 changes: 11 additions & 1 deletion modules/faq/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,24 @@ func (c Component) Handle(s *discordgo.Session, i *discordgo.InteractionCreate)
} else if i.User != nil {
c.member = &discordgo.Member{User: i.User}
}
//lint:ignore SA4005 currently not used but will be when implementing the component
c.data = i.MessageComponentData()

ids := strings.Split(c.data.CustomID, ".")
// pop the first level identifier
util.ShiftL(ids)

switch util.ShiftL(ids) {
case "show_question":
if c.RequireOriginalAuthor() {
question := strings.Join(ids[:len(ids)-2], ".")
c.handleShowQuestion(question)
}
return
case "all_questions":
if c.RequireOriginalAuthor() {
c.handleAllQuestions()
}
return
default:
log.Printf("Unknown component interaction ID: %s", c.data.CustomID)
}
Expand Down
40 changes: 40 additions & 0 deletions modules/faq/faqBase.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,55 @@
package faq

import (
"database/sql"
"errors"
"fmt"
"time"

"github.com/bwmarrin/discordgo"
"github.com/cake4everyone/cake4everybot/database"
"github.com/cake4everyone/cake4everybot/logger"
"github.com/cake4everyone/cake4everybot/util"
)

var log = logger.New("FAQ")

var (
// lastFAQs is a cache for all the FAQs
lastFAQs = make(map[string]map[string]string)
// lastFAQTime is the time when the lastFAQs map was updated
lastFAQTime time.Time
)

type faqBase struct {
util.InteractionUtil
member *discordgo.Member
user *discordgo.User
}

func (faq faqBase) getAllFAQs() (map[string]string, error) {
if time.Since(lastFAQTime) < 2*time.Minute {
return lastFAQs[faq.Interaction.GuildID], nil
}
delete(lastFAQs, faq.Interaction.GuildID)
lastFAQs[faq.Interaction.GuildID] = make(map[string]string)

row, err := database.Query("SELECT question, answer FROM faq WHERE guild_id=?", faq.Interaction.GuildID)
if errors.Is(err, sql.ErrNoRows) {
return lastFAQs[faq.Interaction.GuildID], nil
} else if err != nil {
return lastFAQs[faq.Interaction.GuildID], fmt.Errorf("getting all FAQs from guild %s: %w", faq.Interaction.GuildID, err)
}
defer row.Close()

for row.Next() {
var question, answer string
if err := row.Scan(&question, &answer); err != nil {
return lastFAQs[faq.Interaction.GuildID], fmt.Errorf("scanning row: %w", err)
}
lastFAQs[faq.Interaction.GuildID][question] = answer
}

lastFAQTime = time.Now()
return lastFAQs[faq.Interaction.GuildID], nil
}
34 changes: 34 additions & 0 deletions modules/faq/handleAllQuestions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package faq

import (
"fmt"

"github.com/bwmarrin/discordgo"
"github.com/cake4everyone/cake4everybot/util"
)

func (c Component) handleAllQuestions() {
faqs, err := c.getAllFAQs()
if err != nil {
log.Printf("ERROR: getting all FAQs: %v", err)
c.ReplyError()
return
}

e := &discordgo.MessageEmbed{
Color: 0xFAB1FD,
Title: "FAQs",
}
util.SetEmbedFooter(c.Session, tp+"display", e)

var components []discordgo.MessageComponent
var i int
for question := range faqs {
i++
util.AddEmbedField(e, fmt.Sprintf("%d", i), question, true)
components = append(components, util.CreateButtonComponent(fmt.Sprintf("faq.show_question.%s", question), fmt.Sprint(i), discordgo.PrimaryButton, nil))
}
components = []discordgo.MessageComponent{discordgo.ActionsRow{Components: components}}

c.ReplyComponentsEmbedUpdate(components, e)
}
27 changes: 27 additions & 0 deletions modules/faq/handleAutocomplete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package faq

import (
"strings"

"github.com/bwmarrin/discordgo"
)

func (cmd Chat) handleAutocomplete(input string) {
faqs, err := cmd.getAllFAQs()
if err != nil {
log.Printf("ERROR: getting all FAQs: %v", err)
cmd.ReplyError()
return
}

options := make([]*discordgo.ApplicationCommandOptionChoice, 0, len(faqs))
for question := range faqs {
if input == "" || strings.Contains(strings.ToLower(question), strings.ToLower(input)) {
options = append(options, &discordgo.ApplicationCommandOptionChoice{
Name: question,
Value: question,
})
}
}
cmd.ReplyAutocomplete(options)
}
Loading

0 comments on commit 761c976

Please sign in to comment.