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

Birthday Feature #67

Merged
merged 33 commits into from
Dec 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
565112f
Merge pull request #63 from ritsec/staging
1nv8rzim Oct 14, 2023
2c99224
ent birthday placeholder
1nv8rzim Oct 18, 2023
ee83c42
data birthday placeholder
1nv8rzim Oct 18, 2023
9b9fc7d
added birthday slash command placeholder
1nv8rzim Oct 18, 2023
4ae8058
place holder for birthday scheduled event
1nv8rzim Oct 18, 2023
ddc71e1
add span for birthday slash command
1nv8rzim Oct 18, 2023
6ef64d9
ent schema
1nv8rzim Oct 18, 2023
4b6cfb1
generate ent schema
1nv8rzim Oct 18, 2023
a84d745
added started code for data.Birthday
1nv8rzim Oct 18, 2023
9cec6b1
started code for scheduled eent
1nv8rzim Oct 18, 2023
9bafc14
starter code for birthday slash command
1nv8rzim Oct 18, 2023
c45fb34
Merge pull request #1 from 1nv8rzim/ent-schema
1nv8rzim Oct 18, 2023
ccc7c0e
Midnight loop
hfw8271 Oct 19, 2023
cdfb724
Cron-ed - still working on it
hfw8271 Oct 30, 2023
22aaad6
birthday abstractions
1nv8rzim Nov 8, 2023
ac5f264
Merge pull request #65 from 1nv8rzim/data-abstraction
1nv8rzim Nov 8, 2023
c53ca66
Merge pull request #2 from 1nv8rzim/data-abstraction
1nv8rzim Nov 8, 2023
e882bd4
Merge branch 'ritsec:main' into scheduled-event
1nv8rzim Nov 8, 2023
3929c03
bday add/remove
hfw8271 Nov 8, 2023
3e07fe5
updated slash command for birthday-add
praneeth919 Nov 8, 2023
a7faff3
remove unnessary comments
1nv8rzim Nov 15, 2023
e45ffc3
added removal functionality
hfw8271 Nov 15, 2023
192d02c
added comments
1nv8rzim Nov 15, 2023
06d8635
added error checking
1nv8rzim Nov 15, 2023
e3cc964
Merge pull request #3 from 1nv8rzim/slash-command
1nv8rzim Nov 15, 2023
79089e5
replaced birthday role ID with reference to config file
1nv8rzim Nov 15, 2023
427886a
fixed errors and cleaned up code
1nv8rzim Nov 15, 2023
5bb29dd
logging and logic clean up
1nv8rzim Nov 15, 2023
c8350dc
Merge pull request #4 from 1nv8rzim/scheduled-event
1nv8rzim Nov 15, 2023
c963c21
commited wacky code birthday.go
sophduke Dec 3, 2023
38e5daa
Clean Up Implementation
1nv8rzim Dec 3, 2023
48d6694
Merge pull request #5 from 1nv8rzim/sophduke-patch-1
1nv8rzim Dec 3, 2023
2c93a88
Add birthday date validation
1nv8rzim Dec 3, 2023
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: 2 additions & 0 deletions commands/enabled.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func populateSlashCommands(ctx ddtrace.SpanContext) {
SlashCommands["update"] = slash.Update
SlashCommands["query"] = slash.Query
SlashCommands["scoreboard"] = slash.Scoreboard
SlashCommands["birthday"] = slash.Birthday
}

// populateHandlers populates the Handlers map with all of the handlers
Expand Down Expand Up @@ -68,4 +69,5 @@ func populateScheduledEvents(ctx ddtrace.SpanContext) {
ScheduledEvents["heartbeat"] = scheduled.Heartbeat
ScheduledEvents["status"] = scheduled.Status
ScheduledEvents["update"] = scheduled.Update
ScheduledEvents["birthday"] = scheduled.Birthday
}
116 changes: 116 additions & 0 deletions commands/scheduled/birthday.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package scheduled

import (
"time"

"github.com/bwmarrin/discordgo"
"github.com/ritsec/ops-bot-iii/config"
"github.com/ritsec/ops-bot-iii/data"
"github.com/ritsec/ops-bot-iii/helpers"
"github.com/ritsec/ops-bot-iii/logging"
"github.com/robfig/cron"
"github.com/sirupsen/logrus"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
)

var (
birthdayRoleID string = config.GetString("commands.birthday.role_id")
)

// removeBirthday removes the birthday role from a user
func removeBirthday(s *discordgo.Session, UserID string, ctx ddtrace.SpanContext) error {
span := tracer.StartSpan(
"commands.scheduled.birthday:removeBirthday",
tracer.ResourceName("Scheduled.Birthday.removeBirthday"),
tracer.ChildOf(ctx),
)
defer span.Finish()

return s.GuildMemberRoleRemove(config.GuildID, UserID, birthdayRoleID)
}

// addBirthday adds the birthday role to a user
func addBirthday(s *discordgo.Session, UserID string, ctx ddtrace.SpanContext) error {
span := tracer.StartSpan(
"commands.scheduled.birthday:addBirthday",
tracer.ResourceName("Scheduled.Birthday.addBirthday"),
tracer.ChildOf(ctx),
)
defer span.Finish()

return s.GuildMemberRoleAdd(config.GuildID, UserID, birthdayRoleID)
}

// Birthday is a scheduled event that runs at midnight to remove existing birthday roles and add new ones
func Birthday(s *discordgo.Session, quit chan interface{}) error {
span := tracer.StartSpan(
"commands.scheduled.birthday:Birthday",
tracer.ResourceName("Scheduled.Birthday"),
)
defer span.Finish()

// Set the cron job to run at EST
est, err := time.LoadLocation("America/New_York")
if err != nil {
logging.Error(s, err.Error(), nil, span)
return err
}

c := cron.NewWithLocation(est)

// Run at midnight
err = c.AddFunc("0 0 0 * * *", func() {
internalSpan := tracer.StartSpan(
"commands.scheduled.birthday:Birthday.Cron",
tracer.ResourceName("Scheduled.Birthday.Cron"),
tracer.ChildOf(span.Context()),
)
defer internalSpan.Finish()

today := time.Now()
yesterday := today.Add(-24 * time.Hour)

// Get yesterday's birthdays
entRemoveBirthdays, err := data.Birthday.GetBirthdays(yesterday.Day(), int(yesterday.Month()), internalSpan.Context())
if err != nil {
logging.Error(s, "failed to get yesterday's birthdays", nil, span, logrus.Fields{"error": err})
return
}

// Remove yesterday's birthdays
for _, entRemoveBirthday := range entRemoveBirthdays {
err = removeBirthday(s, entRemoveBirthday.Edges.User.ID, internalSpan.Context())
if err != nil {
logging.Error(s, "failed to remove birthday for "+helpers.AtUser(entRemoveBirthday.Edges.User.ID), nil, span, logrus.Fields{"error": err})
return
}
}

// Get today's birthdays
entAddBirthday, err := data.Birthday.GetBirthdays(today.Day(), int(today.Month()), internalSpan.Context())
if err != nil {
logging.Error(s, "failed to get today's birthdays", nil, span, logrus.Fields{"error": err})
return
}

// Add today's birthdays
for _, entAddBirthday := range entAddBirthday {
err = addBirthday(s, entAddBirthday.Edges.User.ID, internalSpan.Context())
if err != nil {
logging.Error(s, "failed to add birthday for "+helpers.AtUser(entAddBirthday.Edges.User.ID), nil, span, logrus.Fields{"error": err})
return
}
}
})
if err != nil {
logging.Error(s, "failed to create cron job", nil, span, logrus.Fields{"error": err})
return err
}

c.Start()
<-quit
c.Stop()

return nil
}
235 changes: 235 additions & 0 deletions commands/slash/birthday.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
package slash

import (
"fmt"

"github.com/bwmarrin/discordgo"
"github.com/ritsec/ops-bot-iii/commands/slash/permission"
"github.com/ritsec/ops-bot-iii/data"
"github.com/ritsec/ops-bot-iii/logging"
"github.com/sirupsen/logrus"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
)

// Birthday is a slash command that allows users to add or edit their birthday
func Birthday() (*discordgo.ApplicationCommand, func(s *discordgo.Session, i *discordgo.InteractionCreate)) {
var minValue float64 = 1
return &discordgo.ApplicationCommand{
Name: "birthday",
Description: "Add or edit birthday",
DefaultMemberPermissions: &permission.Member,
Options: []*discordgo.ApplicationCommandOption{
{
Type: discordgo.ApplicationCommandOptionInteger,
Name: "month",
Required: true,
Choices: []*discordgo.ApplicationCommandOptionChoice{
{
Name: "January",
Value: 1,
},
{
Name: "February",
Value: 2,
},
{
Name: "March",
Value: 3,
},
{
Name: "April",
Value: 4,
},
{
Name: "May",
Value: 5,
},
{
Name: "June",
Value: 6,
},
{
Name: "July",
Value: 7,
},
{
Name: "August",
Value: 8,
},
{
Name: "September",
Value: 9,
},
{
Name: "October",
Value: 10,
},
{
Name: "November",
Value: 11,
},
{
Name: "December",
Value: 12,
},
},
},
{
Type: discordgo.ApplicationCommandOptionInteger,
Name: "day",
Required: true,
MinValue: &minValue,
MaxValue: 31,
},
},
},
func(s *discordgo.Session, i *discordgo.InteractionCreate) {
span := tracer.StartSpan(
"commands.slash.birthday:Birthday",
tracer.ResourceName("/birthday"),
)
defer span.Finish()

// get user parameters
month := int(i.ApplicationCommandData().Options[0].IntValue())
day := int(i.ApplicationCommandData().Options[1].IntValue())

// TODO: check if birthday is current date and prevent edit

// check is birthday already exists
exists, err := data.Birthday.Exists(i.Member.User.ID, span.Context())
if err != nil {
logging.Error(s, "encounted error when checking if birthday exists", i.Member.User, span, logrus.Fields{"err": err.Error()})
}

if exists {
// birthday exists, update existing one

// get current birthday
entBirthday, err := data.Birthday.Get(i.Member.User.ID, span.Context())
if err != nil {
logging.Error(s, "encounted error when getting birthday from user", i.Member.User, span, logrus.Fields{"err": err.Error()})
}

// update birthday
_, err = entBirthday.Update().SetDay(day).SetMonth(month).Save(data.Ctx)
if err != nil {
logging.Error(s, "encounted error when updating birthday", i.Member.User, span, logrus.Fields{"err": err.Error()})
}

// log that birthday has been updated
logging.Debug(s, "Updated birthday for "+i.Member.User.Username, i.Member.User, span)

// send user message that birthday was successfully updated
err = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Content: fmt.Sprintf("Changed birtday to %d/%d", month, day),
Flags: discordgo.MessageFlagsEphemeral,
},
})
if err != nil {
logging.Error(s, "encounted error when responding to user", i.Member.User, span, logrus.Fields{"err": err.Error()})
}

} else {
// birthday does not exist create new one

// create new birthday for user
_, err = data.Birthday.Create(i.Member.User.ID, day, month, span.Context())
if err != nil {
logging.Error(s, "encounted error when creating birthday", i.Member.User, span, logrus.Fields{"err": err.Error()})
}

// log that birthday has been updated
logging.Debug(s, "Created birthday for "+i.Member.User.Username, i.Member.User, span)

// send user message that birthday was successfully updated
err = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Content: "Created birthday for " + i.Member.User.Username + " ",
Flags: discordgo.MessageFlagsEphemeral,
},
})
if err != nil {
logging.Error(s, "encounted error when responding to user", i.Member.User, span, logrus.Fields{"err": err.Error()})
}
}
}
}

// BirthdayRemove is a slash command that allows users to remove their birthday
func BirthdayRemove() (*discordgo.ApplicationCommand, func(s *discordgo.Session, i *discordgo.InteractionCreate)) {
return &discordgo.ApplicationCommand{
Name: "birthday_remove",
Description: "Remove a birthday",
DefaultMemberPermissions: &permission.Member,
},
func(s *discordgo.Session, i *discordgo.InteractionCreate) {
span := tracer.StartSpan(
"commands.slash.birthday:BirthdayRemove",
tracer.ResourceName("/birthday_remove"),
)
defer span.Finish()

// TODO: check if birthday is current date and prevent removal

// check if birthday exists
exists, err := data.Birthday.Exists(i.Member.User.ID, span.Context())
if err != nil {
logging.Error(s, "Birthday has been removed", i.Member.User, span)
}

if exists {
// birthday exists, remove it

// remove birthday
_, err = data.Birthday.Delete(i.Member.User.ID, span.Context())
if err != nil {
// respond to user that error occured when removing birthday
err = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Content: "Error occured when removing birthday",
Flags: discordgo.MessageFlagsEphemeral,
},
})
if err != nil {
logging.Error(s, "encounted error when responding to user", i.Member.User, span, logrus.Fields{"err": err.Error()})
}

logging.Error(s, "Birthday has been removed", i.Member.User, span)

}

// respond to user that birthday has been removed
err = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Content: "Birthday has been removed",
Flags: discordgo.MessageFlagsEphemeral,
},
})
if err != nil {
logging.Error(s, "encounted error when responding to user", i.Member.User, span, logrus.Fields{"err": err.Error()})
}

} else {
// birthday does not exist

// respond to user that birthday does not exist
err = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Content: "Birthday does not exist",
Flags: discordgo.MessageFlagsEphemeral,
},
})
if err != nil {
logging.Error(s, "encounted error when responding to user", i.Member.User, span, logrus.Fields{"err": err.Error()})
}
}

}
}
Loading