Add customizable embed timestamps
This commit is contained in:
parent
20c6771e7b
commit
dd550b10cf
|
@ -75,6 +75,7 @@ The eventlog listens for important events such as kicks, bans, role changes, etc
|
|||
|
||||
- `/eventlog channel` can be used by anyone with the `Manage Server` permission to set the channel for the event log
|
||||
- `/eventlog ticket_channel` can be used by anyone with the `Manage Server` permission to set the channel in which ticket conversations logs will be sent
|
||||
- `/eventlog time_format` can be used by anyone with the `Manage Server` permission to set the time format for embeds. You can either use `discord` for a timezone-agnostic timestamp or a [strftime string](https://github.com/lestrrat-go/strftime#supported-conversion-specifications), which will be in UTC.
|
||||
|
||||
### Reactions
|
||||
|
||||
|
|
|
@ -48,7 +48,6 @@ func loadConfig() (*Config, error) {
|
|||
}
|
||||
fl.Close()
|
||||
}
|
||||
|
||||
|
||||
return cfg, env.ParseWithOptions(cfg, env.Options{Prefix: "OWOBOT_"})
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -6,6 +6,7 @@ require (
|
|||
github.com/bwmarrin/discordgo v0.27.1
|
||||
github.com/caarlos0/env/v10 v10.0.0
|
||||
github.com/jmoiron/sqlx v1.3.5
|
||||
github.com/lestrrat-go/strftime v1.0.6
|
||||
github.com/pelletier/go-toml/v2 v2.1.0
|
||||
github.com/rivo/uniseg v0.4.4
|
||||
github.com/valyala/fasttemplate v1.2.2
|
||||
|
@ -22,6 +23,7 @@ require (
|
|||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mvdan/xurls v1.1.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
|
||||
|
|
7
go.sum
7
go.sum
|
@ -23,6 +23,10 @@ github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
|||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
|
||||
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
|
||||
github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205AhTIGQQ=
|
||||
github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw=
|
||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
|
@ -34,6 +38,8 @@ github.com/mvdan/xurls v1.1.0 h1:OpuDelGQ1R1ueQ6sSryzi6P+1RtBpfQHM8fJwlE45ww=
|
|||
github.com/mvdan/xurls v1.1.0/go.mod h1:tQlNn3BED8bE/15hnSL2HLkDeLWpNPAwtw7wkEq44oU=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
|
@ -43,6 +49,7 @@ github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc
|
|||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
|
|
|
@ -32,6 +32,7 @@ type Guild struct {
|
|||
TicketCategoryID string `db:"ticket_category_id"`
|
||||
VettingReqChanID string `db:"vetting_req_chan_id"`
|
||||
VettingRoleID string `db:"vetting_role_id"`
|
||||
TimeFormat string `db:"time_format"`
|
||||
}
|
||||
|
||||
func AllGuilds() ([]Guild, error) {
|
||||
|
@ -86,6 +87,11 @@ func SetVettingRoleID(guildID, roleID string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func SetTimeFormat(guildID, timeFmt string) error {
|
||||
_, err := db.Exec("UPDATE guilds SET time_format = ? WHERE id = ?", timeFmt, guildID)
|
||||
return err
|
||||
}
|
||||
|
||||
func IsVettingMsg(msgID string) (bool, error) {
|
||||
var out bool
|
||||
err := db.QueryRow("SELECT 1 FROM guild WHERE vetting_msg_id = ?", msgID).Scan(&out)
|
||||
|
|
|
@ -12,7 +12,8 @@ CREATE TABLE guilds (
|
|||
ticket_log_chan_id TEXT NOT NULL DEFAULT '',
|
||||
ticket_category_id TEXT NOT NULL DEFAULT '',
|
||||
vetting_req_chan_id TEXT NOT NULL DEFAULT '',
|
||||
vetting_role_id TEXT NOT NULL DEFAULT ''
|
||||
vetting_role_id TEXT NOT NULL DEFAULT '',
|
||||
time_format TEXT NOT NULL DEFAULT 'discord'
|
||||
);
|
||||
|
||||
/* tickets holds all open tickets */
|
||||
|
|
|
@ -21,7 +21,6 @@ package eventlog
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"go.elara.ws/owobot/internal/db"
|
||||
|
@ -63,6 +62,19 @@ func Init(s *discordgo.Session) error {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "time_format",
|
||||
Description: "Set the time format for embeds",
|
||||
Type: discordgo.ApplicationCommandOptionSubCommand,
|
||||
Options: []*discordgo.ApplicationCommandOption{
|
||||
{
|
||||
Name: "format",
|
||||
Description: "The time format to use",
|
||||
Type: discordgo.ApplicationCommandOptionString,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -76,6 +88,8 @@ func eventlogCmd(s *discordgo.Session, i *discordgo.InteractionCreate) error {
|
|||
return channelCmd(s, i)
|
||||
case "ticket_channel":
|
||||
return ticketChannelCmd(s, i)
|
||||
case "time_format":
|
||||
return timeFormatCmd(s, i)
|
||||
default:
|
||||
return fmt.Errorf("unknown eventlog subcommand: %s", name)
|
||||
}
|
||||
|
@ -107,6 +121,19 @@ func ticketChannelCmd(s *discordgo.Session, i *discordgo.InteractionCreate) erro
|
|||
return util.RespondEphemeral(s, i.Interaction, fmt.Sprintf("Successfully set ticket log channel to <#%s>!", c.ID))
|
||||
}
|
||||
|
||||
func timeFormatCmd(s *discordgo.Session, i *discordgo.InteractionCreate) error {
|
||||
// Get the subcommand options
|
||||
args := i.ApplicationCommandData().Options[0].Options
|
||||
timeFmt := args[0].StringValue()
|
||||
|
||||
err := db.SetTimeFormat(i.GuildID, timeFmt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return util.RespondEphemeral(s, i.Interaction, "Successfully set the time format!")
|
||||
}
|
||||
|
||||
type Entry struct {
|
||||
Title string
|
||||
Description string
|
||||
|
@ -127,9 +154,6 @@ func Log(s *discordgo.Session, guildID string, e Entry) error {
|
|||
embed := &discordgo.MessageEmbed{
|
||||
Title: e.Title,
|
||||
Description: e.Description,
|
||||
Footer: &discordgo.MessageEmbedFooter{
|
||||
Text: util.FormatJucheTime(time.Now()),
|
||||
},
|
||||
}
|
||||
|
||||
if e.Author != nil {
|
||||
|
@ -143,6 +167,8 @@ func Log(s *discordgo.Session, guildID string, e Entry) error {
|
|||
embed.Image = &discordgo.MessageEmbedImage{URL: e.ImageURL}
|
||||
}
|
||||
|
||||
AddTimeToEmbed(guild.TimeFormat, embed)
|
||||
|
||||
_, err = s.ChannelMessageSendEmbed(guild.LogChanID, embed)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package eventlog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/lestrrat-go/strftime"
|
||||
)
|
||||
|
||||
// AddTimeToEmbed formats the current time using timeFmt and adds it to e.
|
||||
// The timeFmt can either be "discord", "juche", or a strftime string.
|
||||
func AddTimeToEmbed(timeFmt string, e *discordgo.MessageEmbed) *discordgo.MessageEmbed {
|
||||
t := time.Now().In(time.UTC)
|
||||
switch timeFmt {
|
||||
case "discord":
|
||||
e.Timestamp = t.Format(time.RFC3339)
|
||||
case "juche":
|
||||
e.Footer = &discordgo.MessageEmbedFooter{Text: formatJuche(t)}
|
||||
default:
|
||||
e.Footer = &discordgo.MessageEmbedFooter{Text: format(timeFmt, t)}
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// formatJuche formats the given time in Juche calendar format
|
||||
func formatJuche(t time.Time) string {
|
||||
return fmt.Sprintf("%02d:%02d %02d-%02d Juche %d", t.Hour(), t.Minute(), t.Day(), t.Month(), t.Year()-1911)
|
||||
}
|
||||
|
||||
// format formats t using timeFmt
|
||||
func format(timeFmt string, t time.Time) string {
|
||||
timeStr, err := strftime.Format(timeFmt, t)
|
||||
if err != nil {
|
||||
return "ERROR: " + err.Error()
|
||||
}
|
||||
return timeStr
|
||||
}
|
|
@ -25,7 +25,6 @@ import (
|
|||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"mvdan.cc/xurls"
|
||||
|
||||
|
@ -33,6 +32,7 @@ import (
|
|||
"go.elara.ws/logger/log"
|
||||
"go.elara.ws/owobot/internal/db"
|
||||
"go.elara.ws/owobot/internal/systems/commands"
|
||||
"go.elara.ws/owobot/internal/systems/eventlog"
|
||||
"go.elara.ws/owobot/internal/util"
|
||||
)
|
||||
|
||||
|
@ -200,11 +200,10 @@ func onReaction(s *discordgo.Session, mra *discordgo.MessageReactionAdd) {
|
|||
msg.ID,
|
||||
),
|
||||
Color: embedColor,
|
||||
Footer: &discordgo.MessageEmbedFooter{
|
||||
Text: util.FormatJucheTime(time.Now()),
|
||||
},
|
||||
}
|
||||
|
||||
eventlog.AddTimeToEmbed(guild.TimeFormat, embed)
|
||||
|
||||
if imageURL := getImageURL(msg); imageURL != "" {
|
||||
// If the message has an image, add it to the embed
|
||||
embed.Image = &discordgo.MessageEmbedImage{URL: imageURL}
|
||||
|
|
|
@ -24,7 +24,6 @@ import (
|
|||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"go.elara.ws/logger/log"
|
||||
|
@ -151,18 +150,19 @@ func onVettingRequest(s *discordgo.Session, i *discordgo.InteractionCreate) erro
|
|||
return errors.New("you do not have the vetting role")
|
||||
}
|
||||
|
||||
_, err = s.ChannelMessageSendComplex(guild.VettingReqChanID, &discordgo.MessageSend{
|
||||
Embed: &discordgo.MessageEmbed{
|
||||
Title: "Vetting Request",
|
||||
Author: &discordgo.MessageEmbedAuthor{
|
||||
Name: i.Member.User.Username,
|
||||
IconURL: i.Member.User.AvatarURL(""),
|
||||
},
|
||||
Description: "Accept the vetting request to create a ticket, or reject it to kick the user.",
|
||||
Footer: &discordgo.MessageEmbedFooter{
|
||||
Text: util.FormatJucheTime(time.Now()),
|
||||
},
|
||||
embed := &discordgo.MessageEmbed{
|
||||
Title: "Vetting Request",
|
||||
Author: &discordgo.MessageEmbedAuthor{
|
||||
Name: i.Member.User.Username,
|
||||
IconURL: i.Member.User.AvatarURL(""),
|
||||
},
|
||||
Description: "Accept the vetting request to create a ticket, or reject it to kick the user.",
|
||||
}
|
||||
|
||||
eventlog.AddTimeToEmbed(guild.TimeFormat, embed)
|
||||
|
||||
_, err = s.ChannelMessageSendComplex(guild.VettingReqChanID, &discordgo.MessageSend{
|
||||
Embeds: []*discordgo.MessageEmbed{embed},
|
||||
Components: []discordgo.MessageComponent{
|
||||
discordgo.ActionsRow{Components: []discordgo.MessageComponent{
|
||||
discordgo.Button{
|
||||
|
@ -270,6 +270,11 @@ func onVettingResponse(s *discordgo.Session, i *discordgo.InteractionCreate) err
|
|||
return nil
|
||||
}
|
||||
|
||||
guild, err := db.GuildByID(i.GuildID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
executor := i.Member
|
||||
member, err := cache.Member(s, i.GuildID, userID)
|
||||
if err != nil {
|
||||
|
@ -283,22 +288,21 @@ func onVettingResponse(s *discordgo.Session, i *discordgo.InteractionCreate) err
|
|||
return err
|
||||
}
|
||||
|
||||
embed := &discordgo.MessageEmbed{
|
||||
Title: "Vetting Request Accepted!",
|
||||
Description: fmt.Sprintf("This vetting request has been accepted and a vetting ticket has been created at <#%s>.\n\n**Accepted By:** <@%s>", channelID, executor.User.ID),
|
||||
Author: &discordgo.MessageEmbedAuthor{
|
||||
Name: member.User.Username,
|
||||
IconURL: member.User.AvatarURL(""),
|
||||
},
|
||||
}
|
||||
|
||||
eventlog.AddTimeToEmbed(guild.TimeFormat, embed)
|
||||
|
||||
err = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseUpdateMessage,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Embeds: []*discordgo.MessageEmbed{
|
||||
{
|
||||
Title: "Vetting Request Accepted!",
|
||||
Description: fmt.Sprintf("This vetting request has been accepted and a vetting ticket has been created at <#%s>.\n\n**Accepted By:** <@%s>", channelID, executor.User.ID),
|
||||
Author: &discordgo.MessageEmbedAuthor{
|
||||
Name: member.User.Username,
|
||||
IconURL: member.User.AvatarURL(""),
|
||||
},
|
||||
Footer: &discordgo.MessageEmbedFooter{
|
||||
Text: util.FormatJucheTime(time.Now()),
|
||||
},
|
||||
},
|
||||
},
|
||||
Embeds: []*discordgo.MessageEmbed{embed},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -310,22 +314,21 @@ func onVettingResponse(s *discordgo.Session, i *discordgo.InteractionCreate) err
|
|||
return err
|
||||
}
|
||||
|
||||
embed := &discordgo.MessageEmbed{
|
||||
Title: "Vetting Request Rejected",
|
||||
Description: fmt.Sprintf("This vetting request has been rejected and <@%s> has been kicked from the server.\n\n**Rejected By:** <@%s>", member.User.ID, executor.User.ID),
|
||||
Author: &discordgo.MessageEmbedAuthor{
|
||||
Name: member.User.Username,
|
||||
IconURL: member.User.AvatarURL(""),
|
||||
},
|
||||
}
|
||||
|
||||
eventlog.AddTimeToEmbed(guild.TimeFormat, embed)
|
||||
|
||||
err = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseUpdateMessage,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Embeds: []*discordgo.MessageEmbed{
|
||||
{
|
||||
Title: "Vetting Request Rejected",
|
||||
Description: fmt.Sprintf("This vetting request has been rejected and <@%s> has been kicked from the server.\n\n**Rejected By:** <@%s>", member.User.ID, executor.User.ID),
|
||||
Author: &discordgo.MessageEmbedAuthor{
|
||||
Name: member.User.Username,
|
||||
IconURL: member.User.AvatarURL(""),
|
||||
},
|
||||
Footer: &discordgo.MessageEmbedFooter{
|
||||
Text: util.FormatJucheTime(time.Now()),
|
||||
},
|
||||
},
|
||||
},
|
||||
Embeds: []*discordgo.MessageEmbed{embed},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
|
|
|
@ -19,9 +19,6 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"go.elara.ws/logger/log"
|
||||
)
|
||||
|
@ -33,17 +30,6 @@ func Pointer[T any](v T) *T {
|
|||
return &v
|
||||
}
|
||||
|
||||
// FormatJucheTime formats the given time in Juche calendar format,
|
||||
// using pyongyang time if it's available, and otherwise UTC.
|
||||
func FormatJucheTime(t time.Time) string {
|
||||
tz, err := time.LoadLocation("Asia/Pyongyang")
|
||||
if err != nil {
|
||||
tz = time.UTC
|
||||
}
|
||||
t = t.In(tz)
|
||||
return fmt.Sprintf("%02d:%02d %02d-%02d Juche %d", t.Hour(), t.Minute(), t.Day(), t.Month(), t.Year()-1911)
|
||||
}
|
||||
|
||||
// RespondEphemeral responds to an interaction with an ephemeral message.
|
||||
func RespondEphemeral(s *discordgo.Session, i *discordgo.Interaction, content string) error {
|
||||
return s.InteractionRespond(i, &discordgo.InteractionResponse{
|
||||
|
|
Loading…
Reference in New Issue