From 77d41bb945b0b3c01a0c99b07d8eeca5b62e3100 Mon Sep 17 00:00:00 2001 From: Layla <layla@layla.gg> Date: Sat, 17 Jun 2023 22:27:14 +0000 Subject: [PATCH] Recieving messages via the external link --- app/bot.go | 30 ++++++++++++++--- app/component_loader.go | 2 +- common/chat_sync.go | 6 ---- common/external_chat.go | 11 +++++++ common/module.go | 2 +- discord/discord.go | 18 ++++++++++- discord/message.go | 45 ++++++++++++++++++++++++++ main.go | 2 +- persistence/database.go | 8 +++++ persistence/sql/20230617-webhooks.sql | 9 ++++++ persistence/sqlite3.go | 46 +++++++++++++++++++++++++++ 11 files changed, 164 insertions(+), 15 deletions(-) delete mode 100644 common/chat_sync.go create mode 100644 common/external_chat.go create mode 100644 discord/message.go create mode 100644 persistence/sql/20230617-webhooks.sql diff --git a/app/bot.go b/app/bot.go index 1f76d81..445df2a 100644 --- a/app/bot.go +++ b/app/bot.go @@ -8,6 +8,7 @@ import ( "github.com/yeslayla/birdbot/core" "github.com/yeslayla/birdbot/discord" "github.com/yeslayla/birdbot/mastodon" + "github.com/yeslayla/birdbot/persistence" ) var Version string @@ -17,6 +18,8 @@ type Bot struct { Session *discord.Discord Mastodon *mastodon.Mastodon + Database persistence.Database + // Discord Objects guildID string eventCategoryID string @@ -31,7 +34,7 @@ type Bot struct { onEventUpdatedHandlers [](func(common.Event) error) onEventCompletedHandlers [](func(common.Event) error) - gameModules []common.ChatSyncModule + channelChats map[string][]common.ExternalChatModule } // Initalize creates the discord session and registers handlers @@ -57,7 +60,7 @@ func (app *Bot) Initialize(cfg *core.Config) error { cfg.Mastodon.Username, cfg.Mastodon.Password) } - app.Session = discord.New(cfg.Discord.ApplicationID, app.guildID, cfg.Discord.Token) + app.Session = discord.New(cfg.Discord.ApplicationID, app.guildID, cfg.Discord.Token, app.Database) // Register Event Handlers app.Session.OnReady(app.onReady) @@ -65,6 +68,10 @@ func (app *Bot) Initialize(cfg *core.Config) error { app.Session.OnEventDelete(app.onEventDelete) app.Session.OnEventUpdate(app.onEventUpdate) + if len(app.channelChats) > 0 { + app.Session.OnMessageRecieved(app.onMessageRecieved) + } + return nil } @@ -160,7 +167,20 @@ func (app *Bot) onEventComplete(d *discord.Discord, event common.Event) { } -// NewBot creates a new bot instance -func NewBot() *Bot { - return &Bot{} +func (app *Bot) onMessageRecieved(d *discord.Discord, channel string, user common.User, message string) { + chats, ok := app.channelChats[channel] + if !ok { + return + } + + for _, chat := range chats { + chat.RecieveMessage(user, message) + } +} + +// NewBot creates a new bot instance +func NewBot(db persistence.Database) *Bot { + return &Bot{ + Database: db, + } } diff --git a/app/component_loader.go b/app/component_loader.go index 476ef1e..3c77cc7 100644 --- a/app/component_loader.go +++ b/app/component_loader.go @@ -50,7 +50,7 @@ func (loader *ComponentLoader) OnEventComplete(handler func(common.Event) error) return nil } -func (loader *ComponentLoader) RegisterChatSyncModule(ID string, plugin common.ChatSyncModule) error { +func (loader *ComponentLoader) RegisterExternalChat(ID string, plugin common.ExternalChatModule) error { return fmt.Errorf("unimplemented") } diff --git a/common/chat_sync.go b/common/chat_sync.go deleted file mode 100644 index e51f897..0000000 --- a/common/chat_sync.go +++ /dev/null @@ -1,6 +0,0 @@ -package common - -type ChatSyncModule interface { - SendMessage(user string, message string) - RecieveMessage(user User, message string) -} diff --git a/common/external_chat.go b/common/external_chat.go new file mode 100644 index 0000000..e191107 --- /dev/null +++ b/common/external_chat.go @@ -0,0 +1,11 @@ +package common + +type ExternalChatManager interface { + SendMessage(user string, message string) +} + +type ExternalChatModule interface { + Initialize(ExternalChatManager) + + RecieveMessage(user User, message string) +} diff --git a/common/module.go b/common/module.go index caed7a8..bb95b54 100644 --- a/common/module.go +++ b/common/module.go @@ -24,5 +24,5 @@ type ModuleManager interface { // Commands RegisterCommand(string, ChatCommandConfiguration, func(User, map[string]any) string) - RegisterChatSyncModule(ID string, plugin ChatSyncModule) error + RegisterExternalChat(ID string, chat ExternalChatModule) error } diff --git a/discord/discord.go b/discord/discord.go index 7025fad..3b292a3 100644 --- a/discord/discord.go +++ b/discord/discord.go @@ -9,6 +9,7 @@ import ( "github.com/bwmarrin/discordgo" "github.com/stretchr/testify/mock" "github.com/yeslayla/birdbot/common" + "github.com/yeslayla/birdbot/persistence" ) type Discord struct { @@ -21,12 +22,14 @@ type Discord struct { commands map[string]*discordgo.ApplicationCommand commandHandlers map[string]func(session *discordgo.Session, i *discordgo.InteractionCreate) + db persistence.Database + // Signal for shutdown stop chan os.Signal } // New creates a new Discord session -func New(applicationID string, guildID string, token string) *Discord { +func New(applicationID string, guildID string, token string, db persistence.Database) *Discord { // Create Discord Session session, err := discordgo.New(fmt.Sprint("Bot ", token)) @@ -35,6 +38,8 @@ func New(applicationID string, guildID string, token string) *Discord { } session.ShouldReconnectOnError = true return &Discord{ + db: db, + session: session, applicationID: applicationID, guildID: guildID, @@ -117,6 +122,17 @@ func (discord *Discord) OnEventUpdate(handler func(*Discord, common.Event)) { }) } +// OnMessageRecieved registers a handler when a message is recieved +func (discord *Discord) OnMessageRecieved(handler func(*Discord, string, common.User, string)) { + discord.session.AddHandler(func(s *discordgo.Session, r *discordgo.MessageCreate) { + if r.GuildID != discord.guildID { + return + } + + handler(discord, r.ChannelID, NewUser(r.Author), r.Content) + }) +} + func (discord *Discord) SetStatus(status string) { if err := discord.session.UpdateGameStatus(0, status); err != nil { log.Fatal("Failed to update status: ", err) diff --git a/discord/message.go b/discord/message.go new file mode 100644 index 0000000..5f54f2a --- /dev/null +++ b/discord/message.go @@ -0,0 +1,45 @@ +package discord + +import ( + "log" + + "github.com/bwmarrin/discordgo" + "github.com/yeslayla/birdbot/core" + "github.com/yeslayla/birdbot/persistence" +) + +func (discord *Discord) WebhookSendMessage(channel *core.Channel, displayName string, message string) { + + webhookData, err := discord.db.GetDiscordWebhook(channel.ID) + if err != nil { + log.Printf("Error getting webhook from DB: %s", err) + return + } + + if webhookData == nil { + webhook, err := discord.session.WebhookCreate(channel.ID, "BirdBot", "") + if err != nil { + log.Printf("Error creating webhook: %s", err) + return + } + + webhookData = &persistence.DBDiscordWebhook{ + ID: webhook.ID, + Token: webhook.Token, + } + + if err := discord.db.SetDiscordWebhook(channel.ID, webhookData); err != nil { + log.Fatalf("Error failed to store webhook in DB: %s", err) + return + } + + } + + if _, err = discord.session.WebhookExecute(webhookData.ID, webhookData.Token, false, &discordgo.WebhookParams{ + Content: message, + Username: displayName, + }); err != nil { + log.Printf("Failed to send message over webhook: %s", err) + } + +} diff --git a/main.go b/main.go index 98c59dc..1794121 100644 --- a/main.go +++ b/main.go @@ -59,7 +59,7 @@ func main() { log.Fatal("Failed to migrate db: ", err) } - bot := app.NewBot() + bot := app.NewBot(db) if err := bot.Initialize(cfg); err != nil { log.Fatal("Failed to initialize: ", err) diff --git a/persistence/database.go b/persistence/database.go index df6aabf..0ac111c 100644 --- a/persistence/database.go +++ b/persistence/database.go @@ -4,4 +4,12 @@ package persistence type Database interface { GetDiscordMessage(id string) (string, error) SetDiscordMessage(id string, messageID string) error + + GetDiscordWebhook(id string) (*DBDiscordWebhook, error) + SetDiscordWebhook(id string, data *DBDiscordWebhook) error +} + +type DBDiscordWebhook struct { + ID string + Token string } diff --git a/persistence/sql/20230617-webhooks.sql b/persistence/sql/20230617-webhooks.sql new file mode 100644 index 0000000..1095992 --- /dev/null +++ b/persistence/sql/20230617-webhooks.sql @@ -0,0 +1,9 @@ +-- +migrate Up +CREATE TABLE IF NOT EXISTS discord_webhooks ( + id TEXT NOT NULL PRIMARY KEY, + webhook_id TEXT NOT NULL, + webhook_token TEXT NOT NULL +); + +-- +migrate Down +DROP TABLE discord_webhooks; \ No newline at end of file diff --git a/persistence/sqlite3.go b/persistence/sqlite3.go index 3b4db49..de70444 100644 --- a/persistence/sqlite3.go +++ b/persistence/sqlite3.go @@ -120,3 +120,49 @@ func (db *Sqlite3Database) SetDiscordMessage(id string, messageID string) error return nil } + +// GetDiscordWebhook finds a discord webhook based on a given local id +func (db *Sqlite3Database) GetDiscordWebhook(id string) (*DBDiscordWebhook, error) { + + var data DBDiscordWebhook = DBDiscordWebhook{} + row := db.db.QueryRow("SELECT webhook_id, webhook_token FROM discord_webhooks WHERE id = $1", id) + + if err := row.Scan(&data.ID, &data.Token); err != nil { + if err == sql.ErrNoRows { + return nil, nil + } + return nil, fmt.Errorf("failed to get discord webhook from sqlite3: %s", err) + } + + return &data, nil +} + +// SetDiscordWebhook stores a discord webhook based on a given local id +func (db *Sqlite3Database) SetDiscordWebhook(id string, data *DBDiscordWebhook) error { + + statement, err := db.db.Prepare("INSERT OR IGNORE INTO discord_webhooks (id, webhook_id, webhook_token) VALUES (?, ?)") + if err != nil { + return err + } + + result, err := statement.Exec(id, data.ID, data.Token) + if err != nil { + return err + } + + n, _ := result.RowsAffected() + + if n == 0 { + statement, err := db.db.Prepare("UPDATE discord_webhooks SET webhook_id = (?), webhook_token = (?) WHERE id = (?)") + if err != nil { + return err + } + + if _, err := statement.Exec(data.ID, data.Token, id); err != nil { + return err + } + + } + + return nil +}