feat(bot,events,commands): guild configuration and logging
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,3 +9,4 @@ dist
|
||||
tmp
|
||||
bin
|
||||
static/uno.css
|
||||
guild.db
|
||||
|
||||
56
flake.nix
56
flake.nix
@@ -4,33 +4,33 @@
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
templ.url = "github:a-h/templ?ref=v0.2.707";
|
||||
};
|
||||
outputs = { nixpkgs, ... } @ inputs:
|
||||
let
|
||||
systems = [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
"x86_64-darwin"
|
||||
"aarch64-darwin"
|
||||
];
|
||||
forAllSystems = f: nixpkgs.lib.genAttrs systems (system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
in
|
||||
outputs = {nixpkgs, ...} @ inputs: let
|
||||
systems = [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
"x86_64-darwin"
|
||||
"aarch64-darwin"
|
||||
];
|
||||
forAllSystems = f:
|
||||
nixpkgs.lib.genAttrs systems (system: let
|
||||
pkgs = import nixpkgs {inherit system;};
|
||||
in
|
||||
f system pkgs);
|
||||
templ = system: inputs.templ.packages.${system}.templ;
|
||||
in
|
||||
{
|
||||
devShells = forAllSystems (system: pkgs: {
|
||||
default = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
sqlite
|
||||
sqlitebrowser
|
||||
go
|
||||
golangci-lint
|
||||
docker-compose
|
||||
(templ system)
|
||||
];
|
||||
};
|
||||
});
|
||||
};
|
||||
templ = system: inputs.templ.packages.${system}.templ;
|
||||
in {
|
||||
devShells = forAllSystems (system: pkgs: {
|
||||
default = pkgs.mkShell {
|
||||
hardeningDisable = ["fortify"];
|
||||
buildInputs = with pkgs; [
|
||||
sqlite
|
||||
sqlitebrowser
|
||||
go
|
||||
golangci-lint
|
||||
delve
|
||||
docker-compose
|
||||
(templ system)
|
||||
];
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
package bot
|
||||
|
||||
import (
|
||||
"dislate/internals/guilddb"
|
||||
"dislate/internals/translator"
|
||||
"log/slog"
|
||||
|
||||
"dislate/internals/discord/bot/gconf"
|
||||
"dislate/internals/translator"
|
||||
|
||||
dgo "github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
type Bot struct {
|
||||
token string
|
||||
db guilddb.GuildDB
|
||||
db gconf.DB
|
||||
translator translator.Translator
|
||||
session *dgo.Session
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewBot(token string, db guilddb.GuildDB, translator translator.Translator, logger *slog.Logger) (*Bot, error) {
|
||||
func NewBot(token string, db gconf.DB, translator translator.Translator, logger *slog.Logger) (*Bot, error) {
|
||||
discord, err := dgo.New("Bot " + token)
|
||||
if err != nil {
|
||||
return &Bot{}, err
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
func (b *Bot) registerCommands() error {
|
||||
cs := []commands.Command{
|
||||
commands.NewMagageConfig(b.db),
|
||||
commands.NewManageChannel(b.db),
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"dislate/internals/discord/bot/gconf"
|
||||
"dislate/internals/guilddb"
|
||||
gdb "dislate/internals/guilddb"
|
||||
"dislate/internals/translator/lang"
|
||||
@@ -13,10 +14,10 @@ import (
|
||||
)
|
||||
|
||||
type ManageChannel struct {
|
||||
db gdb.GuildDB
|
||||
db gconf.DB
|
||||
}
|
||||
|
||||
func NewManageChannel(db gdb.GuildDB) ManageChannel {
|
||||
func NewManageChannel(db gconf.DB) ManageChannel {
|
||||
return ManageChannel{db}
|
||||
}
|
||||
func (c ManageChannel) Info() *dgo.ApplicationCommand {
|
||||
@@ -43,7 +44,7 @@ func (c ManageChannel) Components() []Component {
|
||||
}
|
||||
|
||||
type ChannelsInfo struct {
|
||||
db gdb.GuildDB
|
||||
db gconf.DB
|
||||
}
|
||||
|
||||
func (c ChannelsInfo) Info() *dgo.ApplicationCommand {
|
||||
@@ -110,7 +111,7 @@ func (c ChannelsInfo) Subcommands() []Command {
|
||||
}
|
||||
|
||||
type ChannelsLink struct {
|
||||
db guilddb.GuildDB
|
||||
db gconf.DB
|
||||
}
|
||||
|
||||
func (c ChannelsLink) Info() *dgo.ApplicationCommand {
|
||||
@@ -223,7 +224,7 @@ func (c ChannelsLink) Subcommands() []Command {
|
||||
}
|
||||
|
||||
type ChannelsSetLang struct {
|
||||
db guilddb.GuildDB
|
||||
db gconf.DB
|
||||
}
|
||||
|
||||
func (c ChannelsSetLang) Info() *dgo.ApplicationCommand {
|
||||
@@ -315,7 +316,7 @@ func (c ChannelsSetLang) Subcommands() []Command {
|
||||
return []Command{}
|
||||
}
|
||||
|
||||
func getChannel(db guilddb.GuildDB, guildID, channelID string) (gdb.Channel, error) {
|
||||
func getChannel(db gconf.DB, guildID, channelID string) (gdb.Channel, error) {
|
||||
ch, err := db.Channel(guildID, channelID)
|
||||
if errors.Is(err, gdb.ErrNotFound) {
|
||||
if err := db.ChannelInsert(gdb.NewChannel(guildID, channelID, lang.EN)); err != nil {
|
||||
@@ -332,7 +333,7 @@ func getChannel(db guilddb.GuildDB, guildID, channelID string) (gdb.Channel, err
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
func getChannelInfo(db guilddb.GuildDB, ch gdb.Channel) (*dgo.MessageEmbed, error) {
|
||||
func getChannelInfo(db gconf.DB, ch gdb.Channel) (*dgo.MessageEmbed, error) {
|
||||
group, err := db.ChannelGroup(ch.GuildID, ch.ID)
|
||||
if !errors.Is(err, gdb.ErrNotFound) {
|
||||
return nil, err
|
||||
|
||||
102
internals/discord/bot/commands/config.go
Normal file
102
internals/discord/bot/commands/config.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"dislate/internals/discord/bot/gconf"
|
||||
"fmt"
|
||||
|
||||
dgo "github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
type ManageConfig struct {
|
||||
db gconf.DB
|
||||
}
|
||||
|
||||
func NewMagageConfig(db gconf.DB) ManageConfig {
|
||||
return ManageConfig{db}
|
||||
}
|
||||
func (c ManageConfig) Info() *dgo.ApplicationCommand {
|
||||
var permissions int64 = dgo.PermissionAdministrator
|
||||
|
||||
return &dgo.ApplicationCommand{
|
||||
Name: "config",
|
||||
Description: "Manages the guild's configuration",
|
||||
DefaultMemberPermissions: &permissions,
|
||||
}
|
||||
}
|
||||
func (c ManageConfig) Handle(s *dgo.Session, ic *dgo.InteractionCreate) error {
|
||||
return nil
|
||||
}
|
||||
func (c ManageConfig) Components() []Component {
|
||||
return []Component{}
|
||||
}
|
||||
func (c ManageConfig) Subcommands() []Command {
|
||||
return []Command{
|
||||
loggerConfigChannel(c),
|
||||
}
|
||||
}
|
||||
|
||||
type loggerConfigChannel struct {
|
||||
db gconf.DB
|
||||
}
|
||||
func (c loggerConfigChannel) Info() *dgo.ApplicationCommand {
|
||||
var permissions int64 = dgo.PermissionAdministrator
|
||||
return &dgo.ApplicationCommand{
|
||||
Name: "log-channel",
|
||||
Description: "Change logging channel",
|
||||
DefaultMemberPermissions: &permissions,
|
||||
Options: []*dgo.ApplicationCommandOption{{
|
||||
Type: dgo.ApplicationCommandOptionChannel,
|
||||
Required: true,
|
||||
Name: "log-channel",
|
||||
Description: "The channel to send log messages and errors to",
|
||||
ChannelTypes: []dgo.ChannelType{
|
||||
dgo.ChannelTypeGuildText,
|
||||
},
|
||||
}},
|
||||
}
|
||||
}
|
||||
func (c loggerConfigChannel) Handle(s *dgo.Session, ic *dgo.InteractionCreate) error {
|
||||
opts := getOptions(ic.ApplicationCommandData().Options)
|
||||
|
||||
var err error
|
||||
var dch *dgo.Channel
|
||||
if c, ok := opts["log-channel"]; ok {
|
||||
dch = c.ChannelValue(s)
|
||||
} else {
|
||||
dch, err = s.Channel(ic.ChannelID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
guild, err := c.db.Guild(ic.GuildID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conf := guild.Config
|
||||
conf.LoggingChannel = &dch.ID
|
||||
guild.Config = conf
|
||||
|
||||
err = c.db.GuildUpdate(guild)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// FIXME: response message continuously on "thinking..."
|
||||
err = s.InteractionRespond(ic.Interaction, &dgo.InteractionResponse{
|
||||
Type: dgo.InteractionResponseDeferredChannelMessageWithSource,
|
||||
Data: &dgo.InteractionResponseData{
|
||||
Content: fmt.Sprintf("Logging channel changed to %s", *guild.Config.LoggingChannel),
|
||||
Flags: dgo.MessageFlagsEphemeral,
|
||||
},
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
func (c loggerConfigChannel) Components() []Component {
|
||||
return []Component{}
|
||||
}
|
||||
func (c loggerConfigChannel) Subcommands() []Command {
|
||||
return []Command{}
|
||||
}
|
||||
86
internals/discord/bot/errors/errors.go
Normal file
86
internals/discord/bot/errors/errors.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
dgo "github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
type Error interface {
|
||||
Log(*slog.Logger)
|
||||
Reply(*dgo.Session, *dgo.Message)
|
||||
LogReply(*slog.Logger, *dgo.Session, *dgo.Message)
|
||||
Error() string
|
||||
}
|
||||
|
||||
type defaultError struct {
|
||||
err string
|
||||
args []slog.Attr
|
||||
}
|
||||
|
||||
func NewError(err string, args ...slog.Attr) defaultError {
|
||||
return defaultError{err, args}
|
||||
}
|
||||
func New(err string, args ...slog.Attr) defaultError {
|
||||
return NewError(err, args...)
|
||||
}
|
||||
|
||||
func (err defaultError) Log(l *slog.Logger) {
|
||||
args := make([]any, len(err.args))
|
||||
for i, a := range err.args {
|
||||
args[i] = any(a)
|
||||
}
|
||||
l.Error(err.err, args...)
|
||||
}
|
||||
|
||||
func (err defaultError) Reply(s *dgo.Session, m *dgo.Message) {
|
||||
_, erro := s.ChannelMessageSendReply(
|
||||
m.ChannelID,
|
||||
fmt.Sprintf("Error: %s\nSee logs for more details", err.err),
|
||||
m.Reference(),
|
||||
)
|
||||
if erro != nil {
|
||||
_, _ = s.ChannelMessageSendReply(
|
||||
m.ChannelID,
|
||||
fmt.Sprintf("Failed to send error message (somehow), due to:\n%s", erro.Error()),
|
||||
m.Reference(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (err defaultError) LogReply(l *slog.Logger, s *dgo.Session, m *dgo.Message) {
|
||||
err.Reply(s,m)
|
||||
err.Log(l)
|
||||
}
|
||||
|
||||
func (err defaultError) Error() string {
|
||||
s := make([]string, len(err.args))
|
||||
for i, a := range err.args {
|
||||
s[i] = fmt.Sprintf("%s=%s", a.Key, a.Value)
|
||||
}
|
||||
return fmt.Sprintf("%s\n%s", err.err, strings.Join(s, " "))
|
||||
}
|
||||
|
||||
type ErrDatabase struct {
|
||||
defaultError
|
||||
}
|
||||
|
||||
func NewErrDatabase(args ...slog.Attr) ErrDatabase {
|
||||
return ErrDatabase{defaultError{
|
||||
"Error while trying to talk to the database.",
|
||||
args,
|
||||
}}
|
||||
}
|
||||
|
||||
type ErrUserWebhook struct {
|
||||
defaultError
|
||||
}
|
||||
|
||||
func NewErrUserWebhook(args ...slog.Attr) ErrUserWebhook {
|
||||
return ErrUserWebhook{defaultError{
|
||||
"Error while trying to access/execute the user webhook",
|
||||
args,
|
||||
}}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import "dislate/internals/discord/bot/events"
|
||||
func (b *Bot) registerEventHandlers() {
|
||||
ehs := []any{
|
||||
events.NewGuildCreate(b.logger, b.db).Serve,
|
||||
events.NewMessageCreate(b.logger, b.db, b.translator).Serve,
|
||||
events.NewMessageCreate(b.db, b.translator).Serve,
|
||||
events.NewReady(b.logger, b.db).Serve,
|
||||
}
|
||||
for _, h := range ehs {
|
||||
|
||||
9
internals/discord/bot/events/events.go
Normal file
9
internals/discord/bot/events/events.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
dgo "github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
type EventHandler[E any] interface {
|
||||
Serve(*dgo.Session, E)
|
||||
}
|
||||
@@ -4,23 +4,24 @@ import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
|
||||
"dislate/internals/guilddb"
|
||||
"dislate/internals/discord/bot/gconf"
|
||||
gdb "dislate/internals/guilddb"
|
||||
|
||||
dgo "github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
type GuildCreate struct {
|
||||
log *slog.Logger
|
||||
db guilddb.GuildDB
|
||||
db gconf.DB
|
||||
}
|
||||
|
||||
func NewGuildCreate(log *slog.Logger, db guilddb.GuildDB) GuildCreate {
|
||||
func NewGuildCreate(log *slog.Logger, db gconf.DB) GuildCreate {
|
||||
return GuildCreate{log, db}
|
||||
}
|
||||
func (h GuildCreate) Serve(s *dgo.Session, e *dgo.GuildCreate) {
|
||||
err := h.db.GuildInsert(guilddb.Guild{ID: e.Guild.ID})
|
||||
err := h.db.GuildInsert(gdb.Guild[gconf.ConfigString]{ID: e.Guild.ID})
|
||||
|
||||
if err != nil && !errors.Is(err, guilddb.ErrNoAffect) {
|
||||
if err != nil && !errors.Is(err, gdb.ErrNoAffect) {
|
||||
h.log.Error("Failed to add guild to database",
|
||||
slog.String("id", e.Guild.ID),
|
||||
slog.String("err", err.Error()),
|
||||
@@ -34,17 +35,17 @@ func (h GuildCreate) Serve(s *dgo.Session, e *dgo.GuildCreate) {
|
||||
|
||||
type Ready struct {
|
||||
log *slog.Logger
|
||||
db guilddb.GuildDB
|
||||
db gconf.DB
|
||||
}
|
||||
|
||||
func NewReady(log *slog.Logger, db guilddb.GuildDB) EventHandler[*dgo.Ready] {
|
||||
func NewReady(log *slog.Logger, db gconf.DB) EventHandler[*dgo.Ready] {
|
||||
return Ready{log, db}
|
||||
}
|
||||
func (h Ready) Serve(s *dgo.Session, e *dgo.Ready) {
|
||||
for _, g := range e.Guilds {
|
||||
err := h.db.GuildInsert(guilddb.Guild{ID: g.ID})
|
||||
err := h.db.GuildInsert(gdb.Guild[gconf.ConfigString]{ID: g.ID})
|
||||
|
||||
if err != nil && !errors.Is(err, guilddb.ErrNoAffect) {
|
||||
if err != nil && !errors.Is(err, gdb.ErrNoAffect) {
|
||||
h.log.Error("Failed to add guild to database",
|
||||
slog.String("id", g.ID),
|
||||
slog.String("err", err.Error()),
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
e "errors"
|
||||
"log/slog"
|
||||
"slices"
|
||||
|
||||
"dislate/internals/discord/bot/errors"
|
||||
"dislate/internals/discord/bot/gconf"
|
||||
"dislate/internals/guilddb"
|
||||
"dislate/internals/translator"
|
||||
"dislate/internals/translator/lang"
|
||||
@@ -13,71 +14,60 @@ import (
|
||||
dgo "github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
type EventHandler[E any] interface {
|
||||
Serve(*dgo.Session, E)
|
||||
}
|
||||
|
||||
type MessageCreate struct {
|
||||
log *slog.Logger
|
||||
db guilddb.GuildDB
|
||||
db gconf.DB
|
||||
translator translator.Translator
|
||||
}
|
||||
|
||||
func NewMessageCreate(log *slog.Logger, db guilddb.GuildDB, t translator.Translator) MessageCreate {
|
||||
return MessageCreate{log, db, t}
|
||||
func NewMessageCreate(db gconf.DB, t translator.Translator) MessageCreate {
|
||||
return MessageCreate{db, t}
|
||||
}
|
||||
func (h MessageCreate) Serve(s *dgo.Session, e *dgo.MessageCreate) {
|
||||
if e.Message.Author.Bot {
|
||||
func (h MessageCreate) Serve(s *dgo.Session, ev *dgo.MessageCreate) {
|
||||
log := gconf.GetLogger(ev.GuildID, s, h.db)
|
||||
if ev.Message.Author.Bot {
|
||||
return
|
||||
}
|
||||
|
||||
ch, err := h.db.Channel(e.GuildID, e.ChannelID)
|
||||
if errors.Is(err, guilddb.ErrNotFound) {
|
||||
h.log.Debug("Channel is not in database, ignoring.", slog.String("guild", e.GuildID), slog.String("channel", e.ChannelID))
|
||||
ch, err := h.db.Channel(ev.GuildID, ev.ChannelID)
|
||||
if e.Is(err, guilddb.ErrNotFound) {
|
||||
log.Debug("Channel is not in database, ignoring.",
|
||||
slog.String("guild", ev.GuildID),
|
||||
slog.String("channel", ev.ChannelID),
|
||||
)
|
||||
return
|
||||
} else if err != nil {
|
||||
h.log.Error("Error while trying to get channel from database",
|
||||
slog.String("guild", e.GuildID),
|
||||
slog.String("channel", e.ChannelID),
|
||||
errors.NewErrDatabase(
|
||||
slog.String("guild", ev.GuildID),
|
||||
slog.String("channel", ev.ChannelID),
|
||||
slog.String("err", err.Error()),
|
||||
)
|
||||
).LogReply(log, s, ev.Message)
|
||||
return
|
||||
}
|
||||
|
||||
gc, err := h.db.ChannelGroup(ch.GuildID, ch.ID)
|
||||
if errors.Is(err, guilddb.ErrNotFound) {
|
||||
h.log.Debug("Channel is not in a group, ignoring.", slog.String("guild", e.GuildID), slog.String("channel", e.ChannelID))
|
||||
if e.Is(err, guilddb.ErrNotFound) {
|
||||
log.Debug("Channel is not in a group, ignoring.",
|
||||
slog.String("guild", ev.GuildID),
|
||||
slog.String("channel", ev.ChannelID),
|
||||
)
|
||||
return
|
||||
} else if err != nil {
|
||||
h.log.Error("Error while trying to get channel group from database",
|
||||
slog.String("guild", e.GuildID),
|
||||
slog.String("channel", e.ChannelID),
|
||||
errors.NewErrDatabase(
|
||||
slog.String("guild", ev.GuildID),
|
||||
slog.String("channel", ev.ChannelID),
|
||||
slog.String("err", err.Error()),
|
||||
)
|
||||
).LogReply(log, s, ev.Message)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = h.getMessage(e.Message, ch.Language)
|
||||
_, err = h.getMessage(ev.Message, ch.Language)
|
||||
if err != nil {
|
||||
h.log.Error("Error while trying to get/set message to database",
|
||||
slog.String("guild", e.Message.GuildID),
|
||||
slog.String("channel", e.Message.ChannelID),
|
||||
slog.String("message", e.Message.ID),
|
||||
errors.NewErrDatabase(
|
||||
slog.String("guild", ev.Message.GuildID),
|
||||
slog.String("channel", ev.Message.ChannelID),
|
||||
slog.String("message", ev.Message.ID),
|
||||
slog.String("err", err.Error()),
|
||||
)
|
||||
_, err := s.ChannelMessageSendReply(
|
||||
e.Message.ChannelID,
|
||||
fmt.Sprintf("Error while trying to send message to database. %s", err.Error()),
|
||||
e.Message.Reference(),
|
||||
)
|
||||
if err != nil {
|
||||
h.log.Error("Error while trying to send error message",
|
||||
slog.String("guild", e.Message.GuildID),
|
||||
slog.String("channel", e.Message.ChannelID),
|
||||
slog.String("message", e.Message.ID),
|
||||
slog.String("err", err.Error()),
|
||||
)
|
||||
}
|
||||
).LogReply(log, s, ev.Message)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -86,106 +76,53 @@ func (h MessageCreate) Serve(s *dgo.Session, e *dgo.MessageCreate) {
|
||||
continue
|
||||
}
|
||||
go func(c guilddb.Channel) {
|
||||
uw, err := h.getUserWebhook(s, c.ID, e.Message.Author)
|
||||
uw, err := h.getUserWebhook(s, c.ID, ev.Message.Author)
|
||||
if err != nil {
|
||||
h.log.Error("Error while trying to create user webhook",
|
||||
slog.String("guild", e.Message.GuildID),
|
||||
slog.String("channel", e.Message.ChannelID),
|
||||
slog.Any("user", e.Message.Author),
|
||||
)
|
||||
_, err := s.ChannelMessageSendReply(
|
||||
e.Message.ChannelID,
|
||||
fmt.Sprintf("Error while trying to create user webhook %s", err.Error()),
|
||||
e.Message.Reference(),
|
||||
)
|
||||
if err != nil {
|
||||
h.log.Error("Error while trying to send error message",
|
||||
slog.String("guild", e.Message.GuildID),
|
||||
slog.String("channel", e.Message.ChannelID),
|
||||
slog.String("message", e.Message.ID),
|
||||
slog.String("err", err.Error()),
|
||||
)
|
||||
}
|
||||
errors.NewErrUserWebhook(
|
||||
slog.String("guild", ev.Message.GuildID),
|
||||
slog.String("channel", ev.Message.ChannelID),
|
||||
slog.Any("user", ev.Message.Author),
|
||||
).LogReply(log, s, ev.Message)
|
||||
}
|
||||
|
||||
t, err := h.translator.Translate(ch.Language, c.Language, e.Message.Content)
|
||||
t, err := h.translator.Translate(ch.Language, c.Language, ev.Message.Content)
|
||||
if err != nil {
|
||||
h.log.Error("Error while trying to translate message",
|
||||
slog.String("guild", e.Message.GuildID),
|
||||
slog.String("channel", e.Message.ChannelID),
|
||||
slog.String("message", e.Message.ID),
|
||||
slog.String("content", e.Message.Content),
|
||||
errors.New("Error while trying to translate message",
|
||||
slog.String("guild", ev.Message.GuildID),
|
||||
slog.String("channel", ev.Message.ChannelID),
|
||||
slog.String("message", ev.Message.ID),
|
||||
slog.String("content", ev.Message.Content),
|
||||
slog.String("err", err.Error()),
|
||||
)
|
||||
_, err := s.ChannelMessageSendReply(
|
||||
e.Message.ChannelID,
|
||||
fmt.Sprintf("Error while trying to translate message. %s", err.Error()),
|
||||
e.Message.Reference(),
|
||||
)
|
||||
if err != nil {
|
||||
h.log.Error("Error while trying to send error message",
|
||||
slog.String("guild", e.Message.GuildID),
|
||||
slog.String("channel", e.Message.ChannelID),
|
||||
slog.String("message", e.Message.ID),
|
||||
slog.String("err", err.Error()),
|
||||
)
|
||||
}
|
||||
).LogReply(log, s, ev.Message)
|
||||
}
|
||||
|
||||
tdm, err := s.WebhookExecute(uw.ID, uw.Token, true, &dgo.WebhookParams{
|
||||
AvatarURL: e.Message.Author.AvatarURL(""),
|
||||
Username: e.Message.Author.GlobalName,
|
||||
AvatarURL: ev.Message.Author.AvatarURL(""),
|
||||
Username: ev.Message.Author.GlobalName,
|
||||
Content: t,
|
||||
})
|
||||
// tdm, err := s.ChannelMessageSend(c.ID, t)
|
||||
if err != nil {
|
||||
h.log.Error("Error while trying to send translated message",
|
||||
slog.String("guild", e.Message.GuildID),
|
||||
slog.String("channel", e.Message.ChannelID),
|
||||
slog.String("message", e.Message.ID),
|
||||
slog.String("content", e.Message.Content),
|
||||
errors.NewErrUserWebhook(
|
||||
slog.String("guild", ev.Message.GuildID),
|
||||
slog.String("channel", ev.Message.ChannelID),
|
||||
slog.String("message", ev.Message.ID),
|
||||
slog.String("content", ev.Message.Content),
|
||||
slog.String("err", err.Error()),
|
||||
)
|
||||
_, err := s.ChannelMessageSendReply(
|
||||
e.Message.ChannelID,
|
||||
fmt.Sprintf("Error while trying to send translated message. %s", err.Error()),
|
||||
e.Message.Reference(),
|
||||
)
|
||||
if err != nil {
|
||||
h.log.Error("Error while trying to send error message",
|
||||
slog.String("guild", e.Message.GuildID),
|
||||
slog.String("channel", e.Message.ChannelID),
|
||||
slog.String("message", e.Message.ID),
|
||||
slog.String("err", err.Error()),
|
||||
)
|
||||
}
|
||||
).LogReply(log, s, ev.Message)
|
||||
}
|
||||
|
||||
if tdm.GuildID == "" {
|
||||
tdm.GuildID = e.Message.GuildID
|
||||
tdm.GuildID = ev.Message.GuildID
|
||||
}
|
||||
|
||||
_, err = h.getTranslatedMessage(tdm, e.Message, c.Language)
|
||||
_, err = h.getTranslatedMessage(tdm, ev.Message, c.Language)
|
||||
if err != nil {
|
||||
h.log.Error("Error while trying to get/set translated message to database",
|
||||
slog.String("guild", e.Message.GuildID),
|
||||
slog.String("channel", e.Message.ChannelID),
|
||||
slog.String("message", e.Message.ID),
|
||||
errors.NewErrDatabase(
|
||||
slog.String("guild", ev.Message.GuildID),
|
||||
slog.String("channel", ev.Message.ChannelID),
|
||||
slog.String("message", ev.Message.ID),
|
||||
slog.String("err", err.Error()),
|
||||
)
|
||||
_, err := s.ChannelMessageSendReply(
|
||||
e.Message.ChannelID,
|
||||
fmt.Sprintf("Error while trying to send translated message to database. %s", err.Error()),
|
||||
e.Message.Reference(),
|
||||
)
|
||||
if err != nil {
|
||||
h.log.Error("Error while trying to send error message",
|
||||
slog.String("guild", e.Message.GuildID),
|
||||
slog.String("channel", e.Message.ChannelID),
|
||||
slog.String("message", e.Message.ID),
|
||||
slog.String("err", err.Error()),
|
||||
)
|
||||
}
|
||||
).LogReply(log, s, ev.Message)
|
||||
}
|
||||
}(c)
|
||||
|
||||
@@ -219,7 +156,7 @@ func (h MessageCreate) getUserWebhook(s *dgo.Session, channelID string, user *dg
|
||||
func (h MessageCreate) getMessage(m *dgo.Message, lang lang.Language) (guilddb.Message, error) {
|
||||
msg, err := h.db.Message(m.GuildID, m.ChannelID, m.ID)
|
||||
|
||||
if errors.Is(err, guilddb.ErrNotFound) {
|
||||
if e.Is(err, guilddb.ErrNotFound) {
|
||||
if err := h.db.MessageInsert(guilddb.NewMessage(m.GuildID, m.ChannelID, m.ID, lang)); err != nil {
|
||||
return guilddb.Message{}, err
|
||||
}
|
||||
@@ -236,7 +173,7 @@ func (h MessageCreate) getMessage(m *dgo.Message, lang lang.Language) (guilddb.M
|
||||
func (h MessageCreate) getTranslatedMessage(m, original *dgo.Message, lang lang.Language) (guilddb.Message, error) {
|
||||
msg, err := h.db.Message(m.GuildID, m.ChannelID, m.ID)
|
||||
|
||||
if errors.Is(err, guilddb.ErrNotFound) {
|
||||
if e.Is(err, guilddb.ErrNotFound) {
|
||||
if err := h.db.MessageInsert(guilddb.NewTranslatedMessage(
|
||||
m.GuildID,
|
||||
m.ChannelID,
|
||||
|
||||
61
internals/discord/bot/gconf/config.go
Normal file
61
internals/discord/bot/gconf/config.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package gconf
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
gdb "dislate/internals/guilddb"
|
||||
|
||||
dgo "github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Logger *slog.Logger
|
||||
}
|
||||
|
||||
type ConfigString struct {
|
||||
LoggingChannel *string `json:"logging_channel"`
|
||||
LoggingLevel *slog.Level `json:"logging_level"`
|
||||
}
|
||||
|
||||
type Guild gdb.Guild[ConfigString]
|
||||
type DB gdb.GuildDB[ConfigString]
|
||||
|
||||
func (g Guild) GetConfig(s *dgo.Session) (*Config, error) {
|
||||
var l *slog.Logger
|
||||
var err error
|
||||
|
||||
if g.Config.LoggingChannel != nil {
|
||||
c, err := s.Channel(*g.Config.LoggingChannel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var lv slog.Level
|
||||
if g.Config.LoggingLevel != nil {
|
||||
lv = *g.Config.LoggingLevel
|
||||
} else {
|
||||
lv = slog.LevelInfo
|
||||
}
|
||||
l = slog.New(NewGuildHandler(s,c, &slog.HandlerOptions{
|
||||
Level: lv,
|
||||
}))
|
||||
} else {
|
||||
l = slog.New(disabledHandler{})
|
||||
}
|
||||
|
||||
return &Config{l}, err
|
||||
}
|
||||
|
||||
func GetLogger(guildID string, s *dgo.Session, db DB) *slog.Logger {
|
||||
g, err := db.Guild(guildID)
|
||||
if err != nil {
|
||||
return slog.New(disabledHandler{})
|
||||
}
|
||||
|
||||
c, err := Guild(g).GetConfig(s)
|
||||
if err != nil {
|
||||
return slog.New(disabledHandler{})
|
||||
}
|
||||
|
||||
return c.Logger
|
||||
}
|
||||
45
internals/discord/bot/gconf/logger.go
Normal file
45
internals/discord/bot/gconf/logger.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package gconf
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
|
||||
dgo "github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
type guildHandler struct {
|
||||
*slog.TextHandler
|
||||
}
|
||||
|
||||
func NewGuildHandler(s *dgo.Session, c *dgo.Channel, opts *slog.HandlerOptions) guildHandler {
|
||||
w := NewChannelWriter(s, c)
|
||||
h := slog.NewTextHandler(w, opts)
|
||||
return guildHandler{h}
|
||||
}
|
||||
|
||||
type disabledHandler struct {
|
||||
*slog.TextHandler
|
||||
}
|
||||
func (_ disabledHandler) Enabled(_ context.Context,_ slog.Level) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type channelWriter struct {
|
||||
session *dgo.Session
|
||||
channel *dgo.Channel
|
||||
}
|
||||
|
||||
func NewChannelWriter(s *dgo.Session, c *dgo.Channel) channelWriter {
|
||||
w := channelWriter{s, c}
|
||||
|
||||
return w
|
||||
}
|
||||
|
||||
func (w channelWriter) Write(p []byte) (int, error) {
|
||||
m, err := w.session.ChannelMessageSend(w.channel.ID, string(p))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return len(m.Content), nil
|
||||
}
|
||||
14
main.go
14
main.go
@@ -1,9 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"dislate/internals/discord/bot"
|
||||
"dislate/internals/guilddb"
|
||||
"dislate/internals/translator"
|
||||
"flag"
|
||||
"log/slog"
|
||||
"os"
|
||||
@@ -11,6 +8,11 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"dislate/internals/discord/bot"
|
||||
"dislate/internals/discord/bot/gconf"
|
||||
"dislate/internals/guilddb"
|
||||
"dislate/internals/translator"
|
||||
|
||||
"github.com/charmbracelet/log"
|
||||
)
|
||||
|
||||
@@ -30,12 +32,12 @@ func init() {
|
||||
|
||||
func main() {
|
||||
logger := slog.New(log.NewWithOptions(os.Stderr, log.Options{
|
||||
TimeFormat: time.DateTime,
|
||||
TimeFormat: time.DateTime,
|
||||
ReportTimestamp: true,
|
||||
ReportCaller: true,
|
||||
ReportCaller: true,
|
||||
}))
|
||||
|
||||
db, err := guilddb.NewSQLiteDB(*database_file)
|
||||
db, err := guilddb.NewSQLiteDB[gconf.ConfigString](*database_file)
|
||||
if err != nil {
|
||||
logger.Error("Failed to open database connection", slog.String("err", err.Error()))
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user