Skip to content

Commit

Permalink
Reactions (#2)
Browse files Browse the repository at this point in the history
* refactor: commands to applications and type masking

* feat: Built a reaction handler through the rules system, also added an example through autopin
  • Loading branch information
lcox74 authored Apr 4, 2024
1 parent 8a2819f commit 9f67f75
Show file tree
Hide file tree
Showing 12 changed files with 368 additions and 77 deletions.
66 changes: 66 additions & 0 deletions applicationRules/AutoPinRule.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package applicationrules

import (
"fmt"

"github.com/aussiebroadwan/tony/database"
"github.com/aussiebroadwan/tony/framework"
)

const (
autoPinThreshold = 5
)

// AutoPinRule is a rule that automatically pins messages that are reacted to
// with a pin emoji 📌 at least 5 times.
type AutoPinRule struct {
framework.ApplicationRule
}

func (r *AutoPinRule) Name() string {
return "auto-pin"
}

func (r *AutoPinRule) GetType() framework.ApplicationRuleType {
return framework.ApplicationRuleTypeReactions
}

// Test tests the rule against the content
func (r *AutoPinRule) Test(content string) error {
if content != "📌" {
return nil
}
return fmt.Errorf("pin emoji found in message")
}

// Action takes action if the rule is violated
func (r *AutoPinRule) Action(ctx *framework.Context, violation error) {
db := ctx.Database()
reaction, add := ctx.Reaction()

// Increment or decrement the count
if add {
database.IncrementAutoPin(db, reaction.ChannelID, reaction.MessageID)
} else {
database.DecrementAutoPin(db, reaction.ChannelID, reaction.MessageID)
}

// Get the count
count, pinned, err := database.GetAutoPin(db, reaction.ChannelID, reaction.MessageID)
if err != nil {
return
}

// Check if the message should be pinned
if count >= autoPinThreshold && !pinned.Valid {
// Pin the message
if err := ctx.Session().ChannelMessagePin(reaction.ChannelID, reaction.MessageID); err == nil {
database.SetAutoPinPinned(db, reaction.ChannelID, reaction.MessageID, true)
}
} else if count < autoPinThreshold && pinned.Valid {
// Unpin the message
if err := ctx.Session().ChannelMessageUnpin(reaction.ChannelID, reaction.MessageID); err != nil {
database.SetAutoPinPinned(db, reaction.ChannelID, reaction.MessageID, false)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package moderation
package applicationrules

import (
"errors"
Expand Down Expand Up @@ -29,7 +29,7 @@ import (
// send a message to the user to let them know that the message was deleted and
// why.
type ModerateNewsRule struct {
framework.ModerateRule
framework.ApplicationRule
}

var (
Expand All @@ -42,6 +42,10 @@ func (r *ModerateNewsRule) Name() string {
return "tech-news"
}

func (r *ModerateNewsRule) GetType() framework.ApplicationRuleType {
return framework.ApplicationRuleTypeModeration
}

// Test tests the rule against the content
func (r *ModerateNewsRule) Test(content string) error {
// Split the message into lines
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package moderation
package applicationrules

import (
"errors"
Expand Down Expand Up @@ -30,7 +30,7 @@ import (
// send a message to the user to let them know that the message was deleted and
// why.
type ModerateRSSRule struct {
framework.ModerateRule
framework.ApplicationRule
}

var (
Expand All @@ -42,6 +42,10 @@ func (r *ModerateRSSRule) Name() string {
return "rss"
}

func (r *ModerateRSSRule) GetType() framework.ApplicationRuleType {
return framework.ApplicationRuleTypeModeration
}

// Test tests the rule against the content
func (r *ModerateRSSRule) Test(content string) error {
// Check if the title is in bold and ends with a colon and a link
Expand Down
18 changes: 9 additions & 9 deletions applications/ping.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
// with a simple "Pong!" message. This command demonstrates a basic
// interaction within Discord using the discordgo package.
type PingCommand struct {
framework.Command
framework.AppCommand
}

// Register is responsible for registering the "ping" command with
Expand All @@ -24,14 +24,14 @@ func (pc *PingCommand) Register(s *discordgo.Session) *discordgo.ApplicationComm
}
}

func (pc *PingCommand) GetType() framework.CommandType {
return framework.CommandTypeApp
func (pc *PingCommand) GetType() framework.AppType {
return framework.AppTypeCommand
}

// Execute handles the execution logic for the "ping" command. When a user
// OnCommand handles the execution logic for the "ping" command. When a user
// invokes this command, Discord triggers this method, allowing the bot to
// respond appropriately.
func (pc *PingCommand) Execute(ctx *framework.Context) {
func (pc *PingCommand) OnCommand(ctx *framework.Context) {
interaction := ctx.Interaction()

user := interaction.Member.User
Expand All @@ -55,8 +55,8 @@ type PingButtonCommand struct {
framework.SubCommand
}

func (pc *PingButtonCommand) GetType() framework.CommandType {
return framework.CommandTypeAppAndEvent
func (pc *PingButtonCommand) GetType() framework.AppType {
return framework.AppTypeCommand | framework.AppTypeEvent
}

// Register is responsible for registering the "ping.button" command with
Expand All @@ -70,10 +70,10 @@ func (pc *PingButtonCommand) Register(s *discordgo.Session) *discordgo.Applicati
}
}

// Execute handles the execution logic for the "ping.button" command. When a
// OnCommand handles the execution logic for the "ping.button" command. When a
// user invokes this command, Discord triggers this method, allowing the bot
// to post a button that the user can interact with.
func (pc *PingButtonCommand) Execute(ctx *framework.Context) {
func (pc *PingButtonCommand) OnCommand(ctx *framework.Context) {
err := ctx.Session().InteractionRespond(ctx.Interaction(), &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Expand Down
30 changes: 15 additions & 15 deletions applications/remind.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
)

type RemindCommand struct {
framework.Command
framework.Application
}

// Register is responsible for registering the "remind" command with
Expand All @@ -24,8 +24,8 @@ func (c *RemindCommand) Register(s *discordgo.Session) *discordgo.ApplicationCom
}
}

func (c *RemindCommand) GetType() framework.CommandType {
return framework.CommandTypeNOP
func (c *RemindCommand) GetType() framework.AppType {
return framework.AppTypeNOP
}

// This is the subcommand for adding a reminder to the bot. The user can specify
Expand Down Expand Up @@ -62,11 +62,11 @@ func (c *RemindAddSubCommand) Register(s *discordgo.Session) *discordgo.Applicat
}
}

func (c *RemindAddSubCommand) GetType() framework.CommandType {
return framework.CommandTypeApp
func (c *RemindAddSubCommand) GetType() framework.AppType {
return framework.AppTypeCommand
}

func (c *RemindAddSubCommand) Execute(ctx *framework.Context) {
func (c *RemindAddSubCommand) OnCommand(ctx *framework.Context) {
interaction := ctx.Interaction()
db := ctx.Database()
commandOptions := interaction.ApplicationCommandData().Options[0].Options
Expand Down Expand Up @@ -174,11 +174,11 @@ func (c *RemindDeleteSubCommand) Register(s *discordgo.Session) *discordgo.Appli
}
}

func (c *RemindDeleteSubCommand) GetType() framework.CommandType {
return framework.CommandTypeApp
func (c *RemindDeleteSubCommand) GetType() framework.AppType {
return framework.AppTypeCommand
}

func (c *RemindDeleteSubCommand) Execute(ctx *framework.Context) {
func (c *RemindDeleteSubCommand) OnCommand(ctx *framework.Context) {
interaction := ctx.Interaction()
db := ctx.Database()
commandOptions := interaction.ApplicationCommandData().Options[0].Options
Expand Down Expand Up @@ -242,11 +242,11 @@ func (c *RemindListSubCommand) Register(s *discordgo.Session) *discordgo.Applica
}
}

func (c *RemindListSubCommand) GetType() framework.CommandType {
return framework.CommandTypeApp
func (c *RemindListSubCommand) GetType() framework.AppType {
return framework.AppTypeCommand
}

func (c *RemindListSubCommand) Execute(ctx *framework.Context) {
func (c *RemindListSubCommand) OnCommand(ctx *framework.Context) {
interaction := ctx.Interaction()

// Get all reminders
Expand Down Expand Up @@ -321,11 +321,11 @@ func (c *RemindStatusSubCommand) Register(s *discordgo.Session) *discordgo.Appli
}
}

func (c *RemindStatusSubCommand) GetType() framework.CommandType {
return framework.CommandTypeApp
func (c *RemindStatusSubCommand) GetType() framework.AppType {
return framework.AppTypeCommand
}

func (c *RemindStatusSubCommand) Execute(ctx *framework.Context) {
func (c *RemindStatusSubCommand) OnCommand(ctx *framework.Context) {
interaction := ctx.Interaction()
commandOptions := interaction.ApplicationCommandData().Options[0].Options

Expand Down
110 changes: 110 additions & 0 deletions database/autopin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package database

import (
"database/sql"
"time"

log "github.com/sirupsen/logrus"
)

func SetupAutoPinDB(db *sql.DB) {
_, err := db.Exec(`CREATE TABLE IF NOT EXISTS autopin (
id INTEGER PRIMARY KEY AUTOINCREMENT,
channel_id TEXT NOT NULL,
message_id TEXT NOT NULL,
reacts INTEGER NOT NULL,
pinned TEXT
)`)
if err != nil {
log.WithField("src", "database.SetupAutoPinDB").WithError(err).Fatal("Failed to create autopin table")
}
}

func GetAutoPin(db *sql.DB, channelId string, messageId string) (int, sql.NullString, error) {
var reacts int
var pinned sql.NullString
err := db.QueryRow(`SELECT reacts, pinned FROM autopin WHERE channel_id = ? AND message_id = ?`, channelId, messageId).Scan(&reacts, &pinned)
if err != nil {
return 0, sql.NullString{Valid: false}, err
}
return reacts, pinned, nil
}

func IncrementAutoPin(db *sql.DB, channelId string, messageId string) error {

// Check if autopin already exists
reacts, _, err := GetAutoPin(db, channelId, messageId)
if err == nil {
// Update autopin
_, err := db.Exec(`UPDATE autopin SET reacts = ? WHERE channel_id = ? AND message_id = ?`, reacts+1, channelId, messageId)
if err != nil {
log.WithField("src", "database.IncrementAutoPin").WithError(err).Error("Failed to update autopin")
return err
}

log.WithField("src", "database.IncrementAutoPin").Info("Updated autopin")
return nil
}

_, err = db.Exec(`INSERT INTO autopin (channel_id, message_id, reacts) VALUES (?, ?, ?)`, channelId, messageId, 1)
if err != nil {
log.WithField("src", "database.IncrementAutoPin").WithError(err).Error("Failed to add autopin")
return err
}

log.WithField("src", "database.IncrementAutoPin").Info("Added autopin")
return nil
}

func DecrementAutoPin(db *sql.DB, channelId string, messageId string) error {
// Check if autopin already exists
reacts, _, err := GetAutoPin(db, channelId, messageId)
if err != nil {
return err
}

newReacts := reacts - 1

// If the new reacts is 0, delete the autopin
if newReacts == 0 {
_, err = db.Exec(`DELETE FROM autopin WHERE channel_id = ? AND message_id = ?`, channelId, messageId)
if err != nil {
log.WithField("src", "database.DecrementAutoPin").WithError(err).Error("Failed to delete autopin")
return err
}

log.WithField("src", "database.DecrementAutoPin").Info("Deleted autopin")
return nil
}

// Update autopin
_, err = db.Exec(`UPDATE autopin SET reacts = ? WHERE channel_id = ? AND message_id = ?`, newReacts, channelId, messageId)
if err != nil {
log.WithField("src", "database.DecrementAutoPin").WithError(err).Error("Failed to update autopin")
return err
}

log.WithField("src", "database.DecrementAutoPin").Info("Updated autopin")
return nil
}

func SetAutoPinPinned(db *sql.DB, channelId string, messageId string, pinned bool) error {

if pinned {
_, err := db.Exec(`UPDATE autopin SET pinned = ? WHERE channel_id = ? AND message_id = ?`, time.Now().Format(time.DateTime), channelId, messageId)
if err != nil {
log.WithField("src", "database.SetAutoPinPinned").WithError(err).Error("Failed to update autopin")
return err
}
} else {
_, err := db.Exec(`UPDATE autopin SET pinned = NULL WHERE channel_id = ? AND message_id = ?`, channelId, messageId)
if err != nil {
log.WithField("src", "database.SetAutoPinPinned").WithError(err).Error("Failed to update autopin")
return err
}

}

log.WithField("src", "database.SetAutoPinPinned").Info("Updated autopin")
return nil
}
Loading

0 comments on commit 9f67f75

Please sign in to comment.