refactor: rewrite, a lot

The last implementation was sketchy, and since I have been weeks without
interacting with the codebase, I don't have the pacience to know what
the fuck my mind was thinking before
This commit is contained in:
Guz
2024-11-04 19:21:01 -03:00
parent daa63f5d64
commit 0566b1624a
25 changed files with 725 additions and 219 deletions

View File

@@ -1,61 +1,49 @@
package bot
import (
"database/sql"
"log/slog"
"forge.capytal.company/capytal/dislate/db"
"forge.capytal.company/capytal/dislate/translator"
"forge.capytal.company/capytal/dislate/bot/gconf"
dgo "github.com/bwmarrin/discordgo"
"github.com/bwmarrin/discordgo"
)
type Bot struct {
token string
db gconf.DB
translator translator.Translator
session *dgo.Session
db *db.Queries
logger *slog.Logger
session *discordgo.Session
}
func NewBot(
token string,
db gconf.DB,
database *sql.DB,
translator translator.Translator,
logger *slog.Logger,
log *slog.Logger,
) (*Bot, error) {
discord, err := dgo.New("Bot " + token)
s, err := discordgo.New(token)
if err != nil {
return &Bot{}, err
return nil, err
}
db, err := db.Prepare(database)
if err != nil {
return nil, err
}
return &Bot{
token: token,
session: s,
db: db,
translator: translator,
session: discord,
logger: logger,
logger: log,
}, nil
}
func (b *Bot) Start() error {
b.registerEventHandlers()
b.session.Identify.Intents = dgo.MakeIntent(dgo.IntentsAllWithoutPrivileged)
if err := b.session.Open(); err != nil {
return err
}
if err := b.registerCommands(); err != nil {
return err
}
return nil
return b.session.Open()
}
func (b *Bot) Stop() error {
if err := b.removeCommands(); err != nil {
return err
}
return b.session.Close()
}

View File

@@ -1,180 +1 @@
package bot
import (
"encoding/json"
"errors"
"fmt"
"log/slog"
"slices"
"forge.capytal.company/capytal/dislate/bot/commands"
dgo "github.com/bwmarrin/discordgo"
)
func (b *Bot) registerCommands() error {
cs := []commands.Command{
commands.NewMagageConfig(b.db),
commands.NewManageChannel(b.db),
}
handlers := make(map[string]func(*dgo.Session, *dgo.InteractionCreate), len(cs))
componentsHandlers := make(map[string]func(*dgo.Session, *dgo.InteractionCreate))
for _, v := range cs {
var cmd *dgo.ApplicationCommand
var err error
subCmds := make(map[string]commands.Command)
sb := v.Subcommands()
if len(sb) == 0 {
cmd, err = b.session.ApplicationCommandCreate(b.session.State.User.ID, "", v.Info())
if err != nil {
return err
}
} else {
subCmdsOpts := make([]*dgo.ApplicationCommandOption, len(sb))
for i, sb := range sb {
subCmds[sb.Info().Name] = sb
subCmdsOpts[i] = &dgo.ApplicationCommandOption{
Type: dgo.ApplicationCommandOptionSubCommand,
Name: sb.Info().Name,
Description: sb.Info().Description,
Options: sb.Info().Options,
}
}
info := v.Info()
info.Options = subCmdsOpts
cmd, err = b.session.ApplicationCommandCreate(b.session.State.User.ID, "", info)
if err != nil {
return err
}
}
for _, c := range v.Components() {
cj, err := c.Info().MarshalJSON()
if err != nil {
return errors.Join(fmt.Errorf("Failed to marshal command"), err)
}
var v struct {
CustomID string `json:"custom_id"`
}
if err := json.Unmarshal(cj, &v); err != nil {
return errors.Join(fmt.Errorf("Failed to unmarshal command"), err)
}
componentsHandlers[v.CustomID] = func(s *dgo.Session, ic *dgo.InteractionCreate) {
b.logger.Debug("Handling message component",
slog.String("id", ic.Interaction.ID),
slog.String("custom_id", ic.Interaction.MessageComponentData().CustomID),
)
err := c.Handle(s, ic)
if err != nil {
b.logger.Error("Failed to handle message component",
slog.String("custom_id", ic.Interaction.MessageComponentData().CustomID),
slog.String("err", err.Error()),
)
}
}
}
handlers[cmd.Name] = func(s *dgo.Session, ic *dgo.InteractionCreate) {
b.logger.Debug("Handling command",
slog.String("id", ic.Interaction.ID),
slog.String("name", ic.Interaction.ApplicationCommandData().Name),
)
opts := ic.Interaction.ApplicationCommandData().Options
isSub := slices.IndexFunc(
opts,
func(o *dgo.ApplicationCommandInteractionDataOption) bool {
return o.Type == dgo.ApplicationCommandOptionSubCommand
},
)
if isSub != -1 {
sc := opts[isSub]
err := subCmds[sc.Name].Handle(s, ic)
if err != nil {
_ = s.InteractionRespond(ic.Interaction, &dgo.InteractionResponse{
Type: dgo.InteractionResponseDeferredChannelMessageWithSource,
Data: &dgo.InteractionResponseData{
Content: fmt.Sprintf(
"Error while trying to handle sub command: %s",
err.Error(),
),
Flags: dgo.MessageFlagsEphemeral,
},
})
b.logger.Error("Failed to handle sub command",
slog.String("name", sc.Name),
slog.String("err", err.Error()),
)
}
return
}
err := v.Handle(s, ic)
if err != nil {
_ = s.InteractionRespond(ic.Interaction, &dgo.InteractionResponse{
Type: dgo.InteractionResponseDeferredChannelMessageWithSource,
Data: &dgo.InteractionResponseData{
Content: fmt.Sprintf(
"Error while trying to handle command: %s",
err.Error(),
),
Flags: dgo.MessageFlagsEphemeral,
},
})
b.logger.Error("Failed to handle command",
slog.String("name", cmd.Name),
slog.String("id", cmd.ID),
slog.String("err", err.Error()),
)
}
}
b.logger.Info("Registered command",
slog.String("name", cmd.Name),
slog.String("id", cmd.ID),
)
}
b.session.AddHandler(func(s *dgo.Session, i *dgo.InteractionCreate) {
switch i.Interaction.Type {
case dgo.InteractionApplicationCommand:
if h, ok := handlers[i.ApplicationCommandData().Name]; ok {
h(s, i)
}
case dgo.InteractionMessageComponent:
if h, ok := componentsHandlers[i.MessageComponentData().CustomID]; ok {
h(s, i)
}
}
})
return nil
}
func (b *Bot) removeCommands() error {
cmds, err := b.session.ApplicationCommands(b.session.State.Application.ID, "")
if err != nil {
return err
}
for _, v := range cmds {
err := b.session.ApplicationCommandDelete(b.session.State.User.ID, "", v.ID)
if err != nil {
return err
}
b.logger.Info("Removed command",
slog.String("name", v.Name),
slog.String("id", v.ID),
)
}
return nil
}

61
botv1/bot.go Normal file
View File

@@ -0,0 +1,61 @@
package bot
import (
"log/slog"
"forge.capytal.company/capytal/dislate/translator"
"forge.capytal.company/capytal/dislate/bot/gconf"
dgo "github.com/bwmarrin/discordgo"
)
type Bot struct {
token string
db gconf.DB
translator translator.Translator
session *dgo.Session
logger *slog.Logger
}
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
}
return &Bot{
token: token,
db: db,
translator: translator,
session: discord,
logger: logger,
}, nil
}
func (b *Bot) Start() error {
b.registerEventHandlers()
b.session.Identify.Intents = dgo.MakeIntent(dgo.IntentsAllWithoutPrivileged)
if err := b.session.Open(); err != nil {
return err
}
if err := b.registerCommands(); err != nil {
return err
}
return nil
}
func (b *Bot) Stop() error {
if err := b.removeCommands(); err != nil {
return err
}
return b.session.Close()
}

180
botv1/commands.go Normal file
View File

@@ -0,0 +1,180 @@
package bot
import (
"encoding/json"
"errors"
"fmt"
"log/slog"
"slices"
"forge.capytal.company/capytal/dislate/bot/commands"
dgo "github.com/bwmarrin/discordgo"
)
func (b *Bot) registerCommands() error {
cs := []commands.Command{
commands.NewMagageConfig(b.db),
commands.NewManageChannel(b.db),
}
handlers := make(map[string]func(*dgo.Session, *dgo.InteractionCreate), len(cs))
componentsHandlers := make(map[string]func(*dgo.Session, *dgo.InteractionCreate))
for _, v := range cs {
var cmd *dgo.ApplicationCommand
var err error
subCmds := make(map[string]commands.Command)
sb := v.Subcommands()
if len(sb) == 0 {
cmd, err = b.session.ApplicationCommandCreate(b.session.State.User.ID, "", v.Info())
if err != nil {
return err
}
} else {
subCmdsOpts := make([]*dgo.ApplicationCommandOption, len(sb))
for i, sb := range sb {
subCmds[sb.Info().Name] = sb
subCmdsOpts[i] = &dgo.ApplicationCommandOption{
Type: dgo.ApplicationCommandOptionSubCommand,
Name: sb.Info().Name,
Description: sb.Info().Description,
Options: sb.Info().Options,
}
}
info := v.Info()
info.Options = subCmdsOpts
cmd, err = b.session.ApplicationCommandCreate(b.session.State.User.ID, "", info)
if err != nil {
return err
}
}
for _, c := range v.Components() {
cj, err := c.Info().MarshalJSON()
if err != nil {
return errors.Join(fmt.Errorf("Failed to marshal command"), err)
}
var v struct {
CustomID string `json:"custom_id"`
}
if err := json.Unmarshal(cj, &v); err != nil {
return errors.Join(fmt.Errorf("Failed to unmarshal command"), err)
}
componentsHandlers[v.CustomID] = func(s *dgo.Session, ic *dgo.InteractionCreate) {
b.logger.Debug("Handling message component",
slog.String("id", ic.Interaction.ID),
slog.String("custom_id", ic.Interaction.MessageComponentData().CustomID),
)
err := c.Handle(s, ic)
if err != nil {
b.logger.Error("Failed to handle message component",
slog.String("custom_id", ic.Interaction.MessageComponentData().CustomID),
slog.String("err", err.Error()),
)
}
}
}
handlers[cmd.Name] = func(s *dgo.Session, ic *dgo.InteractionCreate) {
b.logger.Debug("Handling command",
slog.String("id", ic.Interaction.ID),
slog.String("name", ic.Interaction.ApplicationCommandData().Name),
)
opts := ic.Interaction.ApplicationCommandData().Options
isSub := slices.IndexFunc(
opts,
func(o *dgo.ApplicationCommandInteractionDataOption) bool {
return o.Type == dgo.ApplicationCommandOptionSubCommand
},
)
if isSub != -1 {
sc := opts[isSub]
err := subCmds[sc.Name].Handle(s, ic)
if err != nil {
_ = s.InteractionRespond(ic.Interaction, &dgo.InteractionResponse{
Type: dgo.InteractionResponseDeferredChannelMessageWithSource,
Data: &dgo.InteractionResponseData{
Content: fmt.Sprintf(
"Error while trying to handle sub command: %s",
err.Error(),
),
Flags: dgo.MessageFlagsEphemeral,
},
})
b.logger.Error("Failed to handle sub command",
slog.String("name", sc.Name),
slog.String("err", err.Error()),
)
}
return
}
err := v.Handle(s, ic)
if err != nil {
_ = s.InteractionRespond(ic.Interaction, &dgo.InteractionResponse{
Type: dgo.InteractionResponseDeferredChannelMessageWithSource,
Data: &dgo.InteractionResponseData{
Content: fmt.Sprintf(
"Error while trying to handle command: %s",
err.Error(),
),
Flags: dgo.MessageFlagsEphemeral,
},
})
b.logger.Error("Failed to handle command",
slog.String("name", cmd.Name),
slog.String("id", cmd.ID),
slog.String("err", err.Error()),
)
}
}
b.logger.Info("Registered command",
slog.String("name", cmd.Name),
slog.String("id", cmd.ID),
)
}
b.session.AddHandler(func(s *dgo.Session, i *dgo.InteractionCreate) {
switch i.Interaction.Type {
case dgo.InteractionApplicationCommand:
if h, ok := handlers[i.ApplicationCommandData().Name]; ok {
h(s, i)
}
case dgo.InteractionMessageComponent:
if h, ok := componentsHandlers[i.MessageComponentData().CustomID]; ok {
h(s, i)
}
}
})
return nil
}
func (b *Bot) removeCommands() error {
cmds, err := b.session.ApplicationCommands(b.session.State.Application.ID, "")
if err != nil {
return err
}
for _, v := range cmds {
err := b.session.ApplicationCommandDelete(b.session.State.User.ID, "", v.ID)
if err != nil {
return err
}
b.logger.Info("Removed command",
slog.String("name", v.Name),
slog.String("id", v.ID),
)
}
return nil
}

View File

@@ -10,7 +10,7 @@ import (
"forge.capytal.company/capytal/dislate/bot/gconf"
"forge.capytal.company/capytal/dislate/translator"
gdb "dislate/internals/guilddb"
gdb "forge.capytal.company/capytal/dislate/guilddb"
dgo "github.com/bwmarrin/discordgo"
)

123
db/channel.go Normal file
View File

@@ -0,0 +1,123 @@
package db
import (
"encoding/json"
"forge.capytal.company/capytal/dislate/translator"
)
type Channel struct {
GuildID string
ID string
Language translator.Language
LinkedChannels []string
}
const channelCreate = `
CREATE IF NOT EXISTS channels (
GuildID text NOT NULL,
ID text NOT NULL,
Language text NOT NULL,
LinkedChannels text NOT NULL,
PRIMARY KEY(GuildID, ID),
FOREIGN KEY(GuildID) REFERENCES guilds(ID)
);
`
const createChannel = `
INSERT INTO channels (
GuildID,
ID,
Language,
LinkedChannels,
) VALUES (?, ?, ?, ?);
`
func (q *Queries) CreateChannel(
GuildID, ID string,
Language translator.Translator,
LinkedChannels []string,
) error {
j, err := json.Marshal(LinkedChannels)
if err != nil {
return err
}
_, err = q.exec(createChannel, GuildID, ID, Language, string(j))
return err
}
const updateChannel = `
UPDATE channels
SET GuildID = ?, ID = ?, Language = ?, LinkedChannels = json(?)
WHERE GuildID = ? AND ID = ?;
`
func (q *Queries) UpdateChannel(
GuildID, ID string,
Language translator.Translator,
LinkedChannels []string,
) error {
j, err := json.Marshal(LinkedChannels)
if err != nil {
return err
}
_, err = q.exec(updateChannel, GuildID, ID, Language, string(j), GuildID, ID)
return err
}
const getChannel = `
SELECT (GuildID, ID, Language, LinkedChannels) FROM channels
WHERE GuildID = ? AND ID = ?;
`
func (q *Queries) GetChannel(GuildID, ID string) (Channel, error) {
row := q.queryRow(getChannel, GuildID, ID)
var c Channel
var lc string
if err := row.Scan(&c.GuildID, &c.ID, &c.Language, &lc); err != nil {
return c, err
}
if err := json.Unmarshal([]byte(lc), &c.LinkedChannels); err != nil {
return c, err
}
return c, nil
}
const listGuildChannels = `
SELECT (GuildID, ID, Language, LinkedChannels) FROM channels
WHERE GuildID = ?;
`
func (q *Queries) ListGuildChannels(GuildID string) ([]Channel, error) {
rows, err := q.query(listGuildChannels, GuildID)
if err != nil {
return []Channel{}, err
}
cs := []Channel{}
for rows.Next() {
var m Channel
var lc string
if err := rows.Scan(&m.GuildID, &m.ID, &m.Language, &lc); err != nil {
return cs, err
}
if err := json.Unmarshal([]byte(lc), &m.LinkedChannels); err != nil {
return cs, err
}
cs = append(cs, m)
}
return cs, err
}

64
db/db.go Normal file
View File

@@ -0,0 +1,64 @@
package db
import (
"context"
"database/sql"
)
type DBTX interface {
ExecContext(context.Context, string, ...any) (sql.Result, error)
QueryContext(context.Context, string, ...any) (*sql.Rows, error)
QueryRowContext(context.Context, string, ...any) *sql.Row
}
type Queries struct {
db DBTX
ctx context.Context
}
func New(db DBTX, ctx ...context.Context) *Queries {
var c context.Context
if len(ctx) > 0 {
c = ctx[0]
} else {
c = context.Background()
}
return &Queries{db, c}
}
func Prepare(db DBTX, ctx ...context.Context) (*Queries, error) {
q := New(db, ctx...)
if _, err := q.exec(guildCreate); err != nil {
return nil, err
}
if _, err := q.exec(channelCreate); err != nil {
return nil, err
}
if _, err := q.exec(messageCreate); err != nil {
return nil, err
}
return q, nil
}
func (q *Queries) WithTx(tx *sql.Tx, ctx ...context.Context) *Queries {
return New(tx, ctx...)
}
func (q *Queries) WithContext(ctx context.Context) *Queries {
return New(q.db, ctx)
}
func (q *Queries) exec(query string, args ...any) (sql.Result, error) {
return q.db.ExecContext(q.ctx, query, args...)
}
func (q *Queries) query(query string, args ...any) (*sql.Rows, error) {
return q.db.QueryContext(q.ctx, query, args...)
}
func (q *Queries) queryRow(query string, args ...any) *sql.Row {
return q.db.QueryRowContext(q.ctx, query, args...)
}

45
db/guild.go Normal file
View File

@@ -0,0 +1,45 @@
package db
type Guild struct {
ID string
}
const guildCreate = `
CREATE IF NOT EXISTS guilds (
ID text NOT NULL,
PRIMARY KEY(ID)
);
`
const createGuild = `
INSERT INTO guilds (ID) VALUES (?);
`
func (q *Queries) CreateGuild(id string) error {
_, err := q.exec(createGuild, id)
return err
}
const updateGuild = `
UPDATE guilds SET ID = ? WHERE ID = ?;
`
func (q *Queries) UpdateGuild(id string) error {
_, err := q.exec(updateGuild, id, id)
return err
}
const getGuild = `
SELECT (ID) FROM guilds WHERE ID = ?;
`
func (q *Queries) GetGuild(id string) (Guild, error) {
row := q.queryRow(getGuild, id)
var g Guild
if err := row.Scan(&g.ID); err != nil {
return Guild{}, err
}
return g, nil
}

137
db/message.go Normal file
View File

@@ -0,0 +1,137 @@
package db
import (
"encoding/json"
"forge.capytal.company/capytal/dislate/translator"
)
type Message struct {
GuildID string
ChannelID string
ID string
Language translator.Language
LinkedMessages []string
}
const messageCreate = `
CREATE IF NOT EXISTS messages (
GuildID text NOT NULL,
ChannelID text NOT NULL,
ID text NOT NULL,
Language text NOT NULL,
LinkedMessages text NOT NULL,
PRIMARY KEY(GuildID, ChannelID, ID),
FOREIGN KEY(GuildID) REFERENCES guilds(ID),
FOREIGN KEY(ChannelID) REFERENCES channels(ID)
);
`
const createMessage = `
INSERT INTO messages (
GuildID,
ChannelID,
ID,
Language,
LinkedMessages,
) VALUES (?, ?, ?, ?, ?);
`
func (q *Queries) CreateMessage(
GuildID, ChannelID, ID string,
Language translator.Translator,
LinkedMessages []string,
) error {
j, err := json.Marshal(LinkedMessages)
if err != nil {
return err
}
_, err = q.exec(createMessage, GuildID, ChannelID, ID, Language, string(j))
return err
}
const updateMessage = `
UPDATE messages
SET GuildID = ?, ChannelID = ?, ID = ?, Language = ?, LinkedMessages = json(?)
WHERE GuildID = ? AND ChannelID = ? AND ID = ?;
`
func (q *Queries) UpdateMessage(
GuildID, ChannelID, ID string,
Language translator.Translator,
LinkedMessages []string,
) error {
j, err := json.Marshal(LinkedMessages)
if err != nil {
return err
}
_, err = q.exec(
updateMessage,
GuildID,
ChannelID,
ID,
Language,
string(j),
GuildID,
ChannelID,
ID,
)
return err
}
const getMessage = `
SELECT (GuildID, ChannelID, ID, Language, LinkedMessages) FROM messages
WHERE GuildID = ? AND ChannelID = ? AND ID = ?;
`
func (q *Queries) GetMessage(GuildID, ChannelID, ID string) (Message, error) {
row := q.queryRow(getMessage, GuildID, ChannelID, ID)
var m Message
var lm string
if err := row.Scan(&m.GuildID, &m.ChannelID, &m.ID, &m.Language, &lm); err != nil {
return m, err
}
if err := json.Unmarshal([]byte(lm), &m.LinkedMessages); err != nil {
return m, err
}
return m, nil
}
const listChannelMessages = `
SELECT (GuildID, ChannelID, ID, Language, LinkedMessages) FROM messages
WHERE GuildID = ? AND ChannelID = ?;
`
func (q *Queries) ListChannelMessages(GuildID, ChannelID string) ([]Message, error) {
rows, err := q.query(listChannelMessages, GuildID, ChannelID)
if err != nil {
return []Message{}, err
}
ms := []Message{}
for rows.Next() {
var m Message
var lm string
if err := rows.Scan(&m.GuildID, &m.ChannelID, &m.ID, &m.Language, &lm); err != nil {
return ms, err
}
if err := json.Unmarshal([]byte(lm), &m.LinkedMessages); err != nil {
return ms, err
}
ms = append(ms, m)
}
return ms, err
}

19
main.go
View File

@@ -1,6 +1,7 @@
package main
import (
"database/sql"
"flag"
"log/slog"
"os"
@@ -9,10 +10,10 @@ import (
"time"
"forge.capytal.company/capytal/dislate/bot"
"forge.capytal.company/capytal/dislate/bot/gconf"
"forge.capytal.company/capytal/dislate/guilddb"
"forge.capytal.company/capytal/dislate/translator"
_ "github.com/tursodatabase/go-libsql"
"github.com/charmbracelet/log"
)
@@ -43,12 +44,14 @@ func main() {
ReportCaller: true,
}))
db, err := guilddb.NewSQLiteDB[gconf.ConfigString](*database_file + "?_busy_timeout=5000")
db, err := sql.Open("libsql", "file://sqlite.db")
if err != nil {
logger.Error("Failed to open database connection", slog.String("err", err.Error()))
logger.Error("Failed to start SQLite database", slog.String("error", err.Error()))
return
}
logger.Info("Connection to database started", slog.String("file", *database_file))
defer func() {
err := db.Close()
if err != nil {
@@ -58,12 +61,6 @@ func main() {
logger.Info("Connection to database closed", slog.String("file", *database_file))
}()
if err := db.Prepare(); err != nil {
logger.Error("Failed to prepare database", slog.String("err", err.Error()))
return
}
logger.Info("Database ready to be used")
bot, err := bot.NewBot(*discord_token, db, translator.NewMockTranslator(), logger)
if err != nil {
logger.Error("Failed to create discord bot", slog.String("err", err.Error()))
@@ -73,7 +70,9 @@ func main() {
logger.Error("Failed to start discord bot", slog.String("err", err.Error()))
return
}
logger.Info("Discord bot started")
defer func() {
if err := bot.Stop(); err != nil {
logger.Error("Failed to stop discord bot", slog.String("err", err.Error()))

88
v1/mainv1.go Normal file
View File

@@ -0,0 +1,88 @@
package main
import (
"flag"
"log/slog"
"os"
"os/signal"
"syscall"
"time"
"forge.capytal.company/capytal/dislate/bot"
"forge.capytal.company/capytal/dislate/botv1/gconf"
"forge.capytal.company/capytal/dislate/guilddb"
"forge.capytal.company/capytal/dislate/translator"
"github.com/charmbracelet/log"
)
type TranslationProvider string
const (
GOOGLE_TRANSLATE TranslationProvider = "google-translate"
)
// var translation_provider = flag.String("tprovider", string(GOOGLE_TRANSLATE), "Translation provider")
var (
database_file = flag.String("db", "file:./guild.db", "SQLite database file/location")
discord_token = flag.String(
"token",
os.Getenv("DISCORD_TOKEN"),
"Discord bot authentication token",
)
)
func init() {
flag.Parse()
}
func main() {
logger := slog.New(log.NewWithOptions(os.Stderr, log.Options{
TimeFormat: time.DateTime,
ReportTimestamp: true,
ReportCaller: true,
}))
db, err := guilddb.NewSQLiteDB[gconf.ConfigString](*database_file + "?_busy_timeout=5000")
if err != nil {
logger.Error("Failed to open database connection", slog.String("err", err.Error()))
return
}
logger.Info("Connection to database started", slog.String("file", *database_file))
defer func() {
err := db.Close()
if err != nil {
logger.Error("Failed to close database connection", slog.String("err", err.Error()))
return
}
logger.Info("Connection to database closed", slog.String("file", *database_file))
}()
if err := db.Prepare(); err != nil {
logger.Error("Failed to prepare database", slog.String("err", err.Error()))
return
}
logger.Info("Database ready to be used")
bot, err := bot.NewBot(*discord_token, db, translator.NewMockTranslator(), logger)
if err != nil {
logger.Error("Failed to create discord bot", slog.String("err", err.Error()))
return
}
if err := bot.Start(); err != nil {
logger.Error("Failed to start discord bot", slog.String("err", err.Error()))
return
}
logger.Info("Discord bot started")
defer func() {
if err := bot.Stop(); err != nil {
logger.Error("Failed to stop discord bot", slog.String("err", err.Error()))
return
}
logger.Info("Discord bot stopped")
}()
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt, syscall.SIGINT)
<-sig
}