Recieving messages via the external link
This commit is contained in:
		
							
								
								
									
										30
									
								
								app/bot.go
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								app/bot.go
									
									
									
									
									
								
							@ -8,6 +8,7 @@ import (
 | 
				
			|||||||
	"github.com/yeslayla/birdbot/core"
 | 
						"github.com/yeslayla/birdbot/core"
 | 
				
			||||||
	"github.com/yeslayla/birdbot/discord"
 | 
						"github.com/yeslayla/birdbot/discord"
 | 
				
			||||||
	"github.com/yeslayla/birdbot/mastodon"
 | 
						"github.com/yeslayla/birdbot/mastodon"
 | 
				
			||||||
 | 
						"github.com/yeslayla/birdbot/persistence"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var Version string
 | 
					var Version string
 | 
				
			||||||
@ -17,6 +18,8 @@ type Bot struct {
 | 
				
			|||||||
	Session  *discord.Discord
 | 
						Session  *discord.Discord
 | 
				
			||||||
	Mastodon *mastodon.Mastodon
 | 
						Mastodon *mastodon.Mastodon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Database persistence.Database
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Discord Objects
 | 
						// Discord Objects
 | 
				
			||||||
	guildID               string
 | 
						guildID               string
 | 
				
			||||||
	eventCategoryID       string
 | 
						eventCategoryID       string
 | 
				
			||||||
@ -31,7 +34,7 @@ type Bot struct {
 | 
				
			|||||||
	onEventUpdatedHandlers   [](func(common.Event) error)
 | 
						onEventUpdatedHandlers   [](func(common.Event) error)
 | 
				
			||||||
	onEventCompletedHandlers [](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
 | 
					// 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)
 | 
								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
 | 
						// Register Event Handlers
 | 
				
			||||||
	app.Session.OnReady(app.onReady)
 | 
						app.Session.OnReady(app.onReady)
 | 
				
			||||||
@ -65,6 +68,10 @@ func (app *Bot) Initialize(cfg *core.Config) error {
 | 
				
			|||||||
	app.Session.OnEventDelete(app.onEventDelete)
 | 
						app.Session.OnEventDelete(app.onEventDelete)
 | 
				
			||||||
	app.Session.OnEventUpdate(app.onEventUpdate)
 | 
						app.Session.OnEventUpdate(app.onEventUpdate)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(app.channelChats) > 0 {
 | 
				
			||||||
 | 
							app.Session.OnMessageRecieved(app.onMessageRecieved)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -160,7 +167,20 @@ func (app *Bot) onEventComplete(d *discord.Discord, event common.Event) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewBot creates a new bot instance
 | 
					func (app *Bot) onMessageRecieved(d *discord.Discord, channel string, user common.User, message string) {
 | 
				
			||||||
func NewBot() *Bot {
 | 
						chats, ok := app.channelChats[channel]
 | 
				
			||||||
	return &Bot{}
 | 
						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,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -50,7 +50,7 @@ func (loader *ComponentLoader) OnEventComplete(handler func(common.Event) error)
 | 
				
			|||||||
	return nil
 | 
						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")
 | 
						return fmt.Errorf("unimplemented")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +0,0 @@
 | 
				
			|||||||
package common
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type ChatSyncModule interface {
 | 
					 | 
				
			||||||
	SendMessage(user string, message string)
 | 
					 | 
				
			||||||
	RecieveMessage(user User, message string)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										11
									
								
								common/external_chat.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								common/external_chat.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					package common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ExternalChatManager interface {
 | 
				
			||||||
 | 
						SendMessage(user string, message string)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ExternalChatModule interface {
 | 
				
			||||||
 | 
						Initialize(ExternalChatManager)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						RecieveMessage(user User, message string)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -24,5 +24,5 @@ type ModuleManager interface {
 | 
				
			|||||||
	// Commands
 | 
						// Commands
 | 
				
			||||||
	RegisterCommand(string, ChatCommandConfiguration, func(User, map[string]any) string)
 | 
						RegisterCommand(string, ChatCommandConfiguration, func(User, map[string]any) string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	RegisterChatSyncModule(ID string, plugin ChatSyncModule) error
 | 
						RegisterExternalChat(ID string, chat ExternalChatModule) error
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -9,6 +9,7 @@ import (
 | 
				
			|||||||
	"github.com/bwmarrin/discordgo"
 | 
						"github.com/bwmarrin/discordgo"
 | 
				
			||||||
	"github.com/stretchr/testify/mock"
 | 
						"github.com/stretchr/testify/mock"
 | 
				
			||||||
	"github.com/yeslayla/birdbot/common"
 | 
						"github.com/yeslayla/birdbot/common"
 | 
				
			||||||
 | 
						"github.com/yeslayla/birdbot/persistence"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Discord struct {
 | 
					type Discord struct {
 | 
				
			||||||
@ -21,12 +22,14 @@ type Discord struct {
 | 
				
			|||||||
	commands        map[string]*discordgo.ApplicationCommand
 | 
						commands        map[string]*discordgo.ApplicationCommand
 | 
				
			||||||
	commandHandlers map[string]func(session *discordgo.Session, i *discordgo.InteractionCreate)
 | 
						commandHandlers map[string]func(session *discordgo.Session, i *discordgo.InteractionCreate)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						db persistence.Database
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Signal for shutdown
 | 
						// Signal for shutdown
 | 
				
			||||||
	stop chan os.Signal
 | 
						stop chan os.Signal
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// New creates a new Discord session
 | 
					// 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
 | 
						// Create Discord Session
 | 
				
			||||||
	session, err := discordgo.New(fmt.Sprint("Bot ", token))
 | 
						session, err := discordgo.New(fmt.Sprint("Bot ", token))
 | 
				
			||||||
@ -35,6 +38,8 @@ func New(applicationID string, guildID string, token string) *Discord {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	session.ShouldReconnectOnError = true
 | 
						session.ShouldReconnectOnError = true
 | 
				
			||||||
	return &Discord{
 | 
						return &Discord{
 | 
				
			||||||
 | 
							db: db,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		session:         session,
 | 
							session:         session,
 | 
				
			||||||
		applicationID:   applicationID,
 | 
							applicationID:   applicationID,
 | 
				
			||||||
		guildID:         guildID,
 | 
							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) {
 | 
					func (discord *Discord) SetStatus(status string) {
 | 
				
			||||||
	if err := discord.session.UpdateGameStatus(0, status); err != nil {
 | 
						if err := discord.session.UpdateGameStatus(0, status); err != nil {
 | 
				
			||||||
		log.Fatal("Failed to update status: ", err)
 | 
							log.Fatal("Failed to update status: ", err)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										45
									
								
								discord/message.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								discord/message.go
									
									
									
									
									
										Normal file
									
								
							@ -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)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										2
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								main.go
									
									
									
									
									
								
							@ -59,7 +59,7 @@ func main() {
 | 
				
			|||||||
		log.Fatal("Failed to migrate db: ", err)
 | 
							log.Fatal("Failed to migrate db: ", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bot := app.NewBot()
 | 
						bot := app.NewBot(db)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := bot.Initialize(cfg); err != nil {
 | 
						if err := bot.Initialize(cfg); err != nil {
 | 
				
			||||||
		log.Fatal("Failed to initialize: ", err)
 | 
							log.Fatal("Failed to initialize: ", err)
 | 
				
			||||||
 | 
				
			|||||||
@ -4,4 +4,12 @@ package persistence
 | 
				
			|||||||
type Database interface {
 | 
					type Database interface {
 | 
				
			||||||
	GetDiscordMessage(id string) (string, error)
 | 
						GetDiscordMessage(id string) (string, error)
 | 
				
			||||||
	SetDiscordMessage(id string, messageID 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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										9
									
								
								persistence/sql/20230617-webhooks.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								persistence/sql/20230617-webhooks.sql
									
									
									
									
									
										Normal file
									
								
							@ -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;
 | 
				
			||||||
@ -120,3 +120,49 @@ func (db *Sqlite3Database) SetDiscordMessage(id string, messageID string) error
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user