Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add feature to search through factoids within Discord. #23

Merged
merged 17 commits into from
Sep 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ FROM alpine
COPY --from=builder /build/absol /go/bin/absol

ENTRYPOINT ["/go/bin/absol"]
CMD ["alert", "cleaner", "factoids", "log", "twitch", "hjt"]
CMD ["alert", "cleaner", "factoids", "log", "twitch", "hjt", "search", "mcping"]
6 changes: 3 additions & 3 deletions api/intents.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ func RegisterIntentNeed(neededIntents ...discordgo.Intent) {
}
}

func GetIntent() *discordgo.Intent {
func GetIntent() discordgo.Intent {
var intent discordgo.Intent

for _, v := range intents {
intent = intent | v
}

return discordgo.MakeIntent(intent)
}
return intent
}
6 changes: 6 additions & 0 deletions core/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"github.com/lordralex/absol/modules/factoids"
"github.com/lordralex/absol/modules/hjt"
"github.com/lordralex/absol/modules/log"
"github.com/lordralex/absol/modules/mcping"
"github.com/lordralex/absol/modules/search"
"github.com/lordralex/absol/modules/twitch"
"strings"
)
Expand All @@ -31,6 +33,10 @@ func LoadModule(ds *discordgo.Session, modules []string) {
loadedModules["factoids"] = &factoids.Module{}
case "hjt":
loadedModules["hjt"] = &hjt.Module{}
case "search":
loadedModules["search"] = &search.Module{}
case "mcping":
loadedModules["mcping"] = &mcping.Module{}
default:
logger.Err().Printf("No module with name %s\n", v)
}
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ module github.com/lordralex/absol
go 1.15

require (
github.com/bwmarrin/discordgo v0.22.1-0.20201217190221-8d6815dde7ed
github.com/bwmarrin/discordgo v0.23.1
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/golang/protobuf v1.4.3 // indirect
github.com/iverly/go-mcping v1.0.0
github.com/magiconair/properties v1.8.4 // indirect
github.com/mitchellh/mapstructure v1.4.0 // indirect
github.com/pelletier/go-toml v1.8.1 // indirect
Expand Down
31 changes: 6 additions & 25 deletions go.sum

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions modules/factoids/factoids.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func RunCommand(ds *discordgo.Session, mc *discordgo.MessageCreate, cmd string,
return
}

var data []factoid
var data []Factoid
err = db.Where("name IN (?)", factoids).Find(&data).Error

if gorm.ErrRecordNotFound == err || (err == nil && len(data) == 0) {
Expand Down Expand Up @@ -105,7 +105,7 @@ func RunCommand(ds *discordgo.Session, mc *discordgo.MessageCreate, cmd string,
for i, v := range factoids {
for _, o := range data {
if o.Name == v {
msg += cleanupFactoid(o.Content)
msg += CleanupFactoid(o.Content)
if i+1 != len(factoids) {
msg += "\n\n"
}
Expand All @@ -114,7 +114,7 @@ func RunCommand(ds *discordgo.Session, mc *discordgo.MessageCreate, cmd string,
}

header := ""
if len(mc.Message.Mentions) > 0 || mc.MessageReference != nil {
if len(mc.Message.Mentions) > 0 || mc.MessageReference != nil {
//the golang set
mentions := make(map[string]bool, 0)

Expand All @@ -131,7 +131,7 @@ func RunCommand(ds *discordgo.Session, mc *discordgo.MessageCreate, cmd string,
}
}

for k, _ := range mentions {
for k := range mentions {
header += "<@" + k + "> " + " "
}

Expand Down Expand Up @@ -173,7 +173,7 @@ func SendWithSelfDelete(ds *discordgo.Session, channelId, message string) error
return nil
}

func cleanupFactoid(msg string) string {
func CleanupFactoid(msg string) string {
msg = strings.Replace(msg, "[b]", "**", -1)
msg = strings.Replace(msg, "[/b]", "**", -1)
msg = strings.Replace(msg, "[u]", "__", -1)
Expand All @@ -195,7 +195,7 @@ func cleanupFactoid(msg string) string {
return msg
}

type factoid struct {
type Factoid struct {
Name string `gorm:"name"`
Content string `gorm:"content"`
}
114 changes: 114 additions & 0 deletions modules/mcping/mcping.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package mcping

import (
"bytes"
"encoding/base64"
"github.com/bwmarrin/discordgo"
"github.com/iverly/go-mcping/mcping"
"github.com/lordralex/absol/api"
"github.com/lordralex/absol/api/logger"
"regexp"
"strconv"
"strings"
"time"
)

type Module struct {
api.Module
}

func (*Module) Load(ds *discordgo.Session) {
api.RegisterCommand("mcping", RunCommand)

api.RegisterIntentNeed(discordgo.IntentsGuildMessages, discordgo.IntentsDirectMessages)
}

func RunCommand(ds *discordgo.Session, mc *discordgo.MessageCreate, _ string, args []string) {
if len(args) == 0 {
randomairborne marked this conversation as resolved.
Show resolved Hide resolved
_, _ = ds.ChannelMessageSendReply(mc.ChannelID, "Usage: `!?mcping (address)`", mc.MessageReference)
return
}
_ = ds.ChannelTyping(mc.ChannelID)

connectionSlice := strings.Split(args[0], ":")

port := 25565

var err error // prevent shadowing

if len(connectionSlice) == 2 {
port, err = strconv.Atoi(connectionSlice[1])
if err != nil {
_, _ = ds.ChannelMessageSendReply(mc.ChannelID, "That's not a valid port!", mc.Reference())
return
}
}

// set up pinger
pinger := mcping.NewPinger()
response, err := pinger.PingWithTimeout(connectionSlice[0], uint16(port), 5*time.Second)
if err != nil {
// if it takes more than five seconds to ping, then the server is probably down
_, _ = ds.ChannelMessageSendReply(mc.ChannelID, "Connecting to the server failed.", mc.Reference())
return
}

// set up the embed
var fields []*discordgo.MessageEmbedField
fields = append(fields, &discordgo.MessageEmbedField{
Name: "Latency",
Value: strconv.Itoa(int(response.Latency)) + "ms",
Inline: false,
})
fields = append(fields, &discordgo.MessageEmbedField{
Name: "Player Count",
Value: strconv.Itoa(response.PlayerCount.Online) + "/" + strconv.Itoa(response.PlayerCount.Max) + " players",
Inline: false,
})
motdRegex := regexp.MustCompile(`§.`)
if response.Motd == "" {
response.Motd = "No motd detected for some reason."
}
fields = append(fields, &discordgo.MessageEmbedField{
Name: "Message Of The Day",
Value: motdRegex.ReplaceAllString(response.Motd, ""),
Inline: false,
})
fields = append(fields, &discordgo.MessageEmbedField{
Name: "Version",
Value: response.Version,
Inline: false,
})

// add image to embed
embed := &discordgo.MessageEmbed{
Title: "Ping response from `" + strings.Join(args, "") + "`",
Image: &discordgo.MessageEmbedImage{
URL: "attachment://favicon.png",
},
Fields: fields,
}

// add server favicon to the message
var files []*discordgo.File
if response.Favicon != "" {
files = append(files, &discordgo.File{
Name: "favicon.png",
ContentType: "image/png",
Reader: base64.NewDecoder(base64.StdEncoding, bytes.NewReader([]byte(strings.Split(response.Favicon, ",")[1]))),
})
} else {
files = nil
}

send := &discordgo.MessageSend{
Embed: embed,
Files: files,
Reference: mc.Reference(),
}

_, err = ds.ChannelMessageSendComplex(mc.ChannelID, send)
if err != nil {
logger.Err().Printf("Failed to send message\n%s", err)
}
}
104 changes: 104 additions & 0 deletions modules/search/search.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package search

import (
"github.com/bwmarrin/discordgo"
"github.com/lordralex/absol/api"
"github.com/lordralex/absol/api/database"
"github.com/lordralex/absol/api/logger"
"github.com/lordralex/absol/modules/factoids"
"github.com/spf13/viper"
"strconv"
"strings"
)

type Module struct {
api.Module
}

// Load absol commands API
func (*Module) Load(ds *discordgo.Session) {
api.RegisterCommand("search", RunCommand)

api.RegisterIntentNeed(discordgo.IntentsGuildMessages, discordgo.IntentsDirectMessages)
}

func RunCommand(ds *discordgo.Session, mc *discordgo.MessageCreate, _ string, args []string) {
if mc.GuildID != "" {
_ = factoids.SendWithSelfDelete(ds, mc.ChannelID, "This command may only be used in DMs.")
return
}

if len(args) == 0 {
randomairborne marked this conversation as resolved.
Show resolved Hide resolved
_, _ = ds.ChannelMessageSend(mc.ChannelID, "You must specify a search string!")
return
} else if len(strings.Join(args, "")) < 3 {
_, _ = ds.ChannelMessageSend(mc.ChannelID, "Your search is too short!")
return
}

max := viper.GetInt("factoids.max")
if max == 0 {
max = 5
}

db, err := database.Get()
if err != nil {
_, _ = ds.ChannelMessageSend(mc.ChannelID, "Failed to connect to database")
logger.Err().Printf("Failed to connect to database\n%s", err)
return
}

pageNumber := 0
pageNumber, err = strconv.Atoi(args[len(args)-1]) // if the last arg is a number use it as the page number
randomairborne marked this conversation as resolved.
Show resolved Hide resolved

// if the page number was specified then we subtract one from it to make the page index start at 1, then
// cut the last argument out if it's a number
if _, err := strconv.Atoi(args[len(args)-1]); err == nil {
pageNumber = pageNumber - 1
args = args[:len(args)-1]
}

message := ""
// gets how many rows there are
var rows int64
db.Where("content LIKE ? OR name LIKE ?", "%"+strings.Join(args, " ")+"%", "%"+strings.Join(args, " ")+"%").Table("factoids").Count(&rows)

// ensures that page number is valid
if pageNumber < 0 || pageNumber > int(rows)/max+1 {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just puke the last page, we already did heavy work on this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how this would be done, as it would rely on knowing the count before the find, and that's not possible without doing the aforementioned heavy computing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's very simple to just throw the first page back at them, i'll add something to do that

pageNumber = 0
}

// searches through results for a match
// gets the factoids table
var factoidsList []factoids.Factoid
db.Where("content LIKE ? OR name LIKE ?", "%"+strings.Join(args, " ")+"%", "%"+strings.Join(args, " ")+"%").Order("name").Offset(pageNumber * max).Limit(max).Find(&factoidsList)
// actually put the factoids into one string
for _, factoid := range factoidsList {
message += "**" + factoid.Name + "**" + "```" + factoids.CleanupFactoid(factoid.Content) + "```\n"
}

// add footer with page numbers
footer := ""
footer = "Page " + strconv.Itoa(pageNumber+1) + "/" + strconv.Itoa(int(rows)/max+1) + ". "
if pageNumber+1 < int(rows)/max+1 {
footer += "Type !?search " + strings.Join(args, " ") + " " + strconv.Itoa(pageNumber+2) + " to see the next page."
}

// prepare embed
embed := &discordgo.MessageEmbed{
Description: message,
Footer: &discordgo.MessageEmbedFooter{
Text: footer,
},
}

send := &discordgo.MessageSend{
Embed: embed,
}

_, err = ds.ChannelMessageSendComplex(mc.ChannelID, send)
if err != nil {
logger.Err().Printf("Failed to send message\n%s", err)
}

}