mirror Discord deletions to X and bluesky
Deleting a message on the watched channel now deletes the posts it
produced. Making that possible reshapes how a message reaches an output:
- OutputInterface takes an output.Post and returns an output.Ref, the
identifier the platform gave the new post (tweet ID for X, URI and
CID for bluesky). It also gained Delete(Ref).
- The discord package no longer forwards raw MessageCreate values but
a discord.Event carrying the kind of change, so the MessageDelete
handler can use the same channel. Deleted messages arrive without an
author, so only the channel ID can be filtered on.
- The new store package remembers Discord message ID -> per output Ref.
Set POST_STORE to a path to keep that mapping across restarts;
without it the mapping is memory only and a restart makes older
messages undeletable. It holds the most recent 1000 messages.
main.go grows a distributor type to hold the outputs and the store
rather than threading them through the event loop.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,34 +6,69 @@ import (
|
||||
"github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
type discord struct {
|
||||
// EventKind tells apart the things that can happen to a watched message.
|
||||
type EventKind int
|
||||
|
||||
const (
|
||||
MessageCreated EventKind = iota
|
||||
MessageDeleted
|
||||
)
|
||||
|
||||
// Event is a change on the watched channel that the distributor has to mirror.
|
||||
type Event struct {
|
||||
Kind EventKind
|
||||
MessageID string
|
||||
Content string
|
||||
Attachments []*discordgo.MessageAttachment
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
Token string
|
||||
ChannelID string
|
||||
dgsession *discordgo.Session
|
||||
}
|
||||
|
||||
func Discord(token string, channelId string) *discord {
|
||||
return &discord{
|
||||
func Discord(token string, channelId string) *Client {
|
||||
return &Client{
|
||||
Token: token,
|
||||
ChannelID: channelId,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *discord) BeginRead(tweetchannel chan<- *discordgo.MessageCreate) {
|
||||
d.read(tweetchannel)
|
||||
func (d *Client) BeginRead(eventchannel chan<- Event) {
|
||||
d.read(eventchannel)
|
||||
}
|
||||
|
||||
func (d *discord) read(tweetchannel chan<- *discordgo.MessageCreate) {
|
||||
func (d *Client) read(eventchannel chan<- Event) {
|
||||
dgsession, err := discordgo.New("Bot " + d.Token)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dgsession.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||
if m.ChannelID == d.ChannelID && m.Author.ID != s.State.User.ID {
|
||||
tweetchannel <- m
|
||||
if m.ChannelID != d.ChannelID || m.Author.ID == s.State.User.ID {
|
||||
return
|
||||
}
|
||||
eventchannel <- Event{
|
||||
Kind: MessageCreated,
|
||||
MessageID: m.ID,
|
||||
Content: m.Content,
|
||||
Attachments: m.Attachments,
|
||||
}
|
||||
})
|
||||
|
||||
// Deleted messages arrive without an author, so the channel is the only
|
||||
// thing to filter on; messages we never distributed are dropped later.
|
||||
dgsession.AddHandler(func(s *discordgo.Session, m *discordgo.MessageDelete) {
|
||||
if m.ChannelID != d.ChannelID {
|
||||
return
|
||||
}
|
||||
eventchannel <- Event{
|
||||
Kind: MessageDeleted,
|
||||
MessageID: m.ID,
|
||||
}
|
||||
})
|
||||
|
||||
d.dgsession = dgsession
|
||||
|
||||
errOpen := dgsession.Open()
|
||||
@@ -45,6 +80,6 @@ func Read() string {
|
||||
return "discord"
|
||||
}
|
||||
|
||||
func (d *discord) Write(str string) {
|
||||
func (d *Client) Write(str string) {
|
||||
d.dgsession.ChannelMessageSend(d.ChannelID, str)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user