feat(messages,threads): support for thread syncronization between channels
This commit is contained in:
@@ -17,7 +17,12 @@ type Bot struct {
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewBot(token string, db gconf.DB, 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
|
||||
|
||||
@@ -7,9 +7,10 @@ import (
|
||||
|
||||
"dislate/internals/discord/bot/gconf"
|
||||
"dislate/internals/guilddb"
|
||||
gdb "dislate/internals/guilddb"
|
||||
"dislate/internals/translator/lang"
|
||||
|
||||
gdb "dislate/internals/guilddb"
|
||||
|
||||
dgo "github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
@@ -134,6 +135,9 @@ func (c channelsLink) Info() *dgo.ApplicationCommand {
|
||||
Required: true,
|
||||
ChannelTypes: []dgo.ChannelType{
|
||||
dgo.ChannelTypeGuildText,
|
||||
dgo.ChannelTypeGuildForum,
|
||||
dgo.ChannelTypeGuildPublicThread,
|
||||
dgo.ChannelTypeGuildPrivateThread,
|
||||
},
|
||||
}, {
|
||||
Type: dgo.ApplicationCommandOptionChannel,
|
||||
@@ -141,6 +145,9 @@ func (c channelsLink) Info() *dgo.ApplicationCommand {
|
||||
Description: "The channel to link",
|
||||
ChannelTypes: []dgo.ChannelType{
|
||||
dgo.ChannelTypeGuildText,
|
||||
dgo.ChannelTypeGuildForum,
|
||||
dgo.ChannelTypeGuildPublicThread,
|
||||
dgo.ChannelTypeGuildPrivateThread,
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
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"
|
||||
e "errors"
|
||||
"log/slog"
|
||||
"slices"
|
||||
|
||||
dgo "github.com/bwmarrin/discordgo"
|
||||
)
|
||||
@@ -24,51 +23,59 @@ func NewMessageCreate(db gconf.DB, t translator.Translator) MessageCreate {
|
||||
}
|
||||
|
||||
func (h MessageCreate) Serve(s *dgo.Session, ev *dgo.MessageCreate) {
|
||||
if ev.Message.Author.Bot {
|
||||
if ev.Message.Author.Bot || ev.Type != dgo.MessageTypeDefault {
|
||||
return
|
||||
}
|
||||
log := gconf.GetLogger(ev.GuildID, s, h.db)
|
||||
|
||||
ch, err := h.db.Channel(ev.GuildID, ev.ChannelID)
|
||||
log := gconf.GetLogger(ev.Message.GuildID, s, h.db)
|
||||
h.sendMessage(log, s, ev.Message)
|
||||
}
|
||||
|
||||
func (h MessageCreate) sendMessage(log *slog.Logger, s *dgo.Session, msg *dgo.Message) {
|
||||
ch, err := h.db.Channel(msg.GuildID, msg.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),
|
||||
slog.String("guild", msg.GuildID),
|
||||
slog.String("channel", msg.ChannelID),
|
||||
slog.String("message", msg.ID),
|
||||
)
|
||||
return
|
||||
} else if err != nil {
|
||||
errors.NewErrDatabase(
|
||||
slog.String("guild", ev.GuildID),
|
||||
slog.String("channel", ev.ChannelID),
|
||||
slog.String("guild", msg.GuildID),
|
||||
slog.String("channel", msg.ChannelID),
|
||||
slog.String("message", msg.ID),
|
||||
slog.String("err", err.Error()),
|
||||
).LogReply(log, s, ev.Message)
|
||||
).LogReply(log, s, msg)
|
||||
return
|
||||
}
|
||||
|
||||
gc, err := h.db.ChannelGroup(ch.GuildID, ch.ID)
|
||||
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),
|
||||
slog.String("guild", msg.GuildID),
|
||||
slog.String("channel", msg.ChannelID),
|
||||
slog.String("message", msg.ID),
|
||||
)
|
||||
return
|
||||
} else if err != nil {
|
||||
errors.NewErrDatabase(
|
||||
slog.String("guild", ev.GuildID),
|
||||
slog.String("channel", ev.ChannelID),
|
||||
slog.String("guild", msg.GuildID),
|
||||
slog.String("channel", msg.ChannelID),
|
||||
slog.String("message", msg.ID),
|
||||
slog.String("err", err.Error()),
|
||||
).LogReply(log, s, ev.Message)
|
||||
).LogReply(log, s, msg)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = getMessage(h.db, ev.Message, ch.Language)
|
||||
_, err = getMessage(h.db, msg, ch.Language)
|
||||
if err != nil {
|
||||
errors.NewErrDatabase(
|
||||
slog.String("guild", ev.Message.GuildID),
|
||||
slog.String("channel", ev.Message.ChannelID),
|
||||
slog.String("message", ev.Message.ID),
|
||||
slog.String("guild", msg.GuildID),
|
||||
slog.String("channel", msg.ChannelID),
|
||||
slog.String("message", msg.ID),
|
||||
slog.String("err", err.Error()),
|
||||
).LogReply(log, s, ev.Message)
|
||||
).LogReply(log, s, msg)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -77,53 +84,77 @@ func (h MessageCreate) Serve(s *dgo.Session, ev *dgo.MessageCreate) {
|
||||
continue
|
||||
}
|
||||
go func(c guilddb.Channel) {
|
||||
uw, err := getUserWebhook(s, c.ID, ev.Message.Author)
|
||||
dch, err := s.Channel(c.ID)
|
||||
|
||||
var channelID string
|
||||
if err != nil {
|
||||
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)
|
||||
errors.New("Failed to get information about channel",
|
||||
slog.String("guild", msg.GuildID),
|
||||
slog.String("channel", msg.ChannelID),
|
||||
slog.String("err", err.Error()),
|
||||
).LogReply(log, s, msg)
|
||||
} else if dch.IsThread() {
|
||||
channelID = dch.ParentID
|
||||
} else {
|
||||
channelID = dch.ID
|
||||
}
|
||||
|
||||
t, err := h.translator.Translate(ch.Language, c.Language, ev.Message.Content)
|
||||
uw, err := getUserWebhook(s, channelID, msg.Author)
|
||||
if err != nil {
|
||||
errors.NewErrUserWebhook(
|
||||
slog.String("guild", msg.GuildID),
|
||||
slog.String("channel", msg.ChannelID),
|
||||
slog.Any("user", msg.Author),
|
||||
).LogReply(log, s, msg)
|
||||
}
|
||||
|
||||
t, err := h.translator.Translate(ch.Language, c.Language, msg.Content)
|
||||
if err != nil {
|
||||
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("guild", msg.GuildID),
|
||||
slog.String("channel", msg.ChannelID),
|
||||
slog.String("message", msg.ID),
|
||||
slog.String("content", msg.Content),
|
||||
slog.String("err", err.Error()),
|
||||
).LogReply(log, s, ev.Message)
|
||||
).LogReply(log, s, msg)
|
||||
}
|
||||
|
||||
tdm, err := s.WebhookExecute(uw.ID, uw.Token, true, &dgo.WebhookParams{
|
||||
AvatarURL: ev.Message.Author.AvatarURL(""),
|
||||
Username: ev.Message.Author.GlobalName,
|
||||
Content: t,
|
||||
})
|
||||
var tdm *dgo.Message
|
||||
if dch.IsThread() {
|
||||
tdm, err = s.WebhookThreadExecute(uw.ID, uw.Token, true, dch.ID, &dgo.WebhookParams{
|
||||
AvatarURL: msg.Author.AvatarURL(""),
|
||||
Username: msg.Author.GlobalName,
|
||||
Content: t,
|
||||
})
|
||||
} else {
|
||||
tdm, err = s.WebhookExecute(uw.ID, uw.Token, true, &dgo.WebhookParams{
|
||||
AvatarURL: msg.Author.AvatarURL(""),
|
||||
Username: msg.Author.GlobalName,
|
||||
Content: t,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
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("guild", msg.GuildID),
|
||||
slog.String("channel", msg.ChannelID),
|
||||
slog.String("message", msg.ID),
|
||||
slog.String("content", msg.Content),
|
||||
slog.String("err", err.Error()),
|
||||
).LogReply(log, s, ev.Message)
|
||||
).LogReply(log, s, msg)
|
||||
}
|
||||
|
||||
if tdm.GuildID == "" {
|
||||
tdm.GuildID = ev.Message.GuildID
|
||||
tdm.GuildID = msg.GuildID
|
||||
}
|
||||
|
||||
_, err = getTranslatedMessage(h.db, tdm, ev.Message, c.Language)
|
||||
_, err = getTranslatedMessage(h.db, tdm, msg, c.Language)
|
||||
if err != nil {
|
||||
errors.NewErrDatabase(
|
||||
slog.String("guild", ev.Message.GuildID),
|
||||
slog.String("channel", ev.Message.ChannelID),
|
||||
slog.String("message", ev.Message.ID),
|
||||
slog.String("guild", msg.GuildID),
|
||||
slog.String("channel", msg.ChannelID),
|
||||
slog.String("message", msg.ID),
|
||||
slog.String("err", err.Error()),
|
||||
).LogReply(log, s, ev.Message)
|
||||
).LogReply(log, s, msg)
|
||||
}
|
||||
}(c)
|
||||
|
||||
@@ -140,7 +171,7 @@ func NewMessageEdit(db gconf.DB, t translator.Translator) MessageEdit {
|
||||
}
|
||||
|
||||
func (h MessageEdit) Serve(s *dgo.Session, ev *dgo.MessageUpdate) {
|
||||
if ev.Message.Author.Bot {
|
||||
if ev.Message.Author.Bot || ev.Type != dgo.MessageTypeDefault {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -183,7 +214,20 @@ func (h MessageEdit) Serve(s *dgo.Session, ev *dgo.MessageUpdate) {
|
||||
continue
|
||||
}
|
||||
go func(m guilddb.Message) {
|
||||
uw, err := getUserWebhook(s, m.ChannelID, ev.Message.Author)
|
||||
var channelID string
|
||||
if dch, err := s.Channel(m.ChannelID); err != nil {
|
||||
errors.New("Failed to get information about channel",
|
||||
slog.String("guild", ev.Message.GuildID),
|
||||
slog.String("channel", ev.Message.ChannelID),
|
||||
slog.String("err", err.Error()),
|
||||
).LogReply(log, s, ev.Message)
|
||||
} else if dch.IsThread() {
|
||||
channelID = dch.ParentID
|
||||
} else {
|
||||
channelID = dch.ID
|
||||
}
|
||||
|
||||
uw, err := getUserWebhook(s, channelID, ev.Message.Author)
|
||||
if err != nil {
|
||||
errors.NewErrUserWebhook(
|
||||
slog.String("guild", ev.Message.GuildID),
|
||||
@@ -233,6 +277,9 @@ func NewMessageDelete(db gconf.DB) MessageDelete {
|
||||
}
|
||||
|
||||
func (h MessageDelete) Serve(s *dgo.Session, ev *dgo.MessageDelete) {
|
||||
if ev.Type != dgo.MessageTypeDefault {
|
||||
return
|
||||
}
|
||||
log := gconf.GetLogger(ev.Message.GuildID, s, h.db)
|
||||
|
||||
msg, err := h.db.Message(ev.Message.GuildID, ev.Message.ChannelID, ev.Message.ID)
|
||||
@@ -280,7 +327,7 @@ func (h MessageDelete) Serve(s *dgo.Session, ev *dgo.MessageDelete) {
|
||||
}
|
||||
|
||||
for _, m := range tmsgs {
|
||||
if m.ID == msg.ID && m.GuildID == msg.GuildID {
|
||||
if m.ID == msg.ID && m.ChannelID == msg.ChannelID && m.GuildID == msg.GuildID {
|
||||
continue
|
||||
}
|
||||
go func(m guilddb.Message) {
|
||||
|
||||
192
internals/discord/bot/events/threads.go
Normal file
192
internals/discord/bot/events/threads.go
Normal file
@@ -0,0 +1,192 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"dislate/internals/discord/bot/errors"
|
||||
"dislate/internals/discord/bot/gconf"
|
||||
gdb "dislate/internals/guilddb"
|
||||
"dislate/internals/translator"
|
||||
e "errors"
|
||||
"log/slog"
|
||||
"sync"
|
||||
|
||||
dgo "github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
type ThreadCreate struct {
|
||||
db gconf.DB
|
||||
translator translator.Translator
|
||||
}
|
||||
|
||||
func NewThreadCreate(db gconf.DB, t translator.Translator) ThreadCreate {
|
||||
return ThreadCreate{db, t}
|
||||
}
|
||||
|
||||
func (h ThreadCreate) Serve(s *dgo.Session, ev *dgo.ThreadCreate) {
|
||||
log := gconf.GetLogger(ev.GuildID, s, h.db)
|
||||
log.Debug("Thread created!", slog.String("parent", ev.ParentID), slog.String("thread", ev.ID))
|
||||
|
||||
if len(ev.AppliedTags) > 0 {
|
||||
log.Debug("New thread is in forum, unimplemented, ignoring")
|
||||
return
|
||||
}
|
||||
|
||||
// INFO: Threads have the same ID as the origin message of them
|
||||
threadMsg, err := h.db.Message(ev.GuildID, ev.ParentID, ev.ID)
|
||||
if e.Is(err, gdb.ErrNotFound) {
|
||||
log.Debug("Parent message of thread not in database, ignoring",
|
||||
slog.String("thread", ev.ID),
|
||||
slog.String("parent", ev.ParentID),
|
||||
slog.String("error", err.Error()),
|
||||
)
|
||||
return
|
||||
} else if err != nil {
|
||||
errors.New("Unable to get thread parent message from database",
|
||||
slog.String("thread", ev.ID),
|
||||
slog.String("parent", ev.ParentID),
|
||||
slog.String("error", err.Error()),
|
||||
).Log(log)
|
||||
return
|
||||
}
|
||||
|
||||
var originMsg gdb.Message
|
||||
if threadMsg.OriginID != nil && threadMsg.OriginChannelID != nil {
|
||||
oMsg, err := h.db.Message(ev.GuildID, *threadMsg.OriginChannelID, *threadMsg.OriginID)
|
||||
if err != nil {
|
||||
originMsg = threadMsg
|
||||
} else {
|
||||
originMsg = oMsg
|
||||
}
|
||||
} else {
|
||||
originMsg = threadMsg
|
||||
}
|
||||
|
||||
msgs, err := h.db.MessagesWithOrigin(ev.GuildID, originMsg.ChannelID, originMsg.ID)
|
||||
if e.Is(err, gdb.ErrNotFound) {
|
||||
log.Debug("No translated messages for thread parent message found, ignoring",
|
||||
slog.String("thread message", ev.ID),
|
||||
slog.String("parent channel", ev.ParentID),
|
||||
)
|
||||
return
|
||||
} else if err != nil {
|
||||
errors.NewErrDatabase(
|
||||
slog.String("thread message", ev.ID),
|
||||
slog.String("parent channel", ev.ParentID),
|
||||
slog.String("error", err.Error()),
|
||||
).LogSend(log, s, ev.ParentID)
|
||||
return
|
||||
}
|
||||
msgs = append(msgs, originMsg)
|
||||
|
||||
dth, err := s.Channel(ev.ID)
|
||||
if err != nil {
|
||||
errors.New("Failed to get message thread object",
|
||||
slog.String("thread", ev.ID),
|
||||
slog.String("parent", ev.ParentID),
|
||||
slog.String("error", err.Error()),
|
||||
).LogSend(log, s, ev.ParentID)
|
||||
return
|
||||
} else if !dth.IsThread() {
|
||||
errors.New("Channel is not a thread for some reason",
|
||||
slog.String("channel", ev.ID),
|
||||
slog.String("parent", ev.ParentID),
|
||||
).LogSend(log, s, ev.ID)
|
||||
return
|
||||
}
|
||||
|
||||
th := gdb.NewChannel(dth.GuildID, dth.ID, threadMsg.Language)
|
||||
if err := h.db.ChannelInsert(th); e.Is(err, gdb.ErrNoAffect) {
|
||||
log.Info("Thread already in database, probably created by bot",
|
||||
slog.String("thread", dth.ID),
|
||||
slog.String("parent", dth.ParentID),
|
||||
)
|
||||
return
|
||||
} else if err != nil {
|
||||
errors.New("Failed to add origin thread channel to database",
|
||||
slog.String("thread", dth.ID),
|
||||
slog.String("parent", dth.ParentID),
|
||||
slog.String("err", err.Error()),
|
||||
).LogSend(log, s, ev.ID)
|
||||
return
|
||||
}
|
||||
|
||||
threadGroup := make([]gdb.Channel, len(msgs))
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for i, m := range msgs {
|
||||
|
||||
threadGroup[i] = gdb.NewChannel(m.GuildID, m.ID, m.Language)
|
||||
|
||||
if m.ID == th.ID {
|
||||
continue
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
|
||||
go func(m gdb.Message) {
|
||||
defer wg.Done()
|
||||
|
||||
dtth, err := s.MessageThreadStartComplex(
|
||||
m.ChannelID,
|
||||
m.ID,
|
||||
&dgo.ThreadStart{
|
||||
Name: dth.Name,
|
||||
AutoArchiveDuration: dth.ThreadMetadata.AutoArchiveDuration,
|
||||
Type: dth.Type,
|
||||
Invitable: dth.ThreadMetadata.Invitable,
|
||||
RateLimitPerUser: dth.RateLimitPerUser,
|
||||
AppliedTags: dth.AppliedTags,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
errors.New("Failed to create translated thread",
|
||||
slog.String("origin thread", dth.ID),
|
||||
slog.String("origin thread parent", dth.ParentID),
|
||||
slog.String("error", err.Error()),
|
||||
).LogSend(log, s, ev.ID)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.db.ChannelInsert(gdb.NewChannel(dtth.GuildID, dtth.ID, m.Language)); err != nil &&
|
||||
!e.Is(err, gdb.ErrNoAffect) {
|
||||
errors.New("Failed to add thread channel to database",
|
||||
slog.String("thread", dth.ID),
|
||||
slog.String("parent", dth.ParentID),
|
||||
slog.String("origin thread", dth.ID),
|
||||
slog.String("origin thread parent", dth.ParentID),
|
||||
slog.String("err", err.Error()),
|
||||
).LogSend(log, s, dtth.ParentID)
|
||||
return
|
||||
}
|
||||
}(m)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if err := h.db.ChannelGroupInsert(threadGroup); err != nil {
|
||||
errors.New("Failed to insert group of threads in database",
|
||||
slog.String("origin thread", dth.ID),
|
||||
slog.String("origin thread parent", dth.ParentID),
|
||||
slog.Any("thread group", threadGroup),
|
||||
slog.String("error", err.Error()),
|
||||
).LogSend(log, s, ev.ID)
|
||||
return
|
||||
}
|
||||
|
||||
thMsgs, err := s.ChannelMessages(th.ID, 10, "", "", "")
|
||||
if err != nil {
|
||||
errors.New("Failed to get thread messages",
|
||||
slog.String("thread", dth.ID),
|
||||
slog.String("parent", dth.ParentID),
|
||||
slog.String("err", err.Error()),
|
||||
).LogSend(log, s, ev.ID)
|
||||
return
|
||||
}
|
||||
|
||||
for _, m := range thMsgs {
|
||||
if m.Content != "" {
|
||||
m.GuildID = th.GuildID
|
||||
NewMessageCreate(h.db, h.translator).sendMessage(log, s, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user