Implement attachments for Discord -> Matrix
This commit is contained in:
60
bridge/attachments.go
Normal file
60
bridge/attachments.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package bridge
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
|
||||
"maunium.net/go/mautrix/appservice"
|
||||
"maunium.net/go/mautrix/event"
|
||||
)
|
||||
|
||||
func (p *Portal) downloadDiscordAttachment(url string) ([]byte, error) {
|
||||
// We might want to make this save to disk in the future. Discord defaults
|
||||
// to 8mb for all attachments to a messages for non-nitro users and
|
||||
// non-boosted servers.
|
||||
//
|
||||
// If the user has nitro classic, their limit goes up to 50mb but if a user
|
||||
// has regular nitro the limit is increased to 100mb.
|
||||
//
|
||||
// Servers boosted to level 2 will have the limit bumped to 50mb.
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", discordgo.DroidBrowserUserAgent)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ioutil.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
func (p *Portal) uploadMatrixAttachment(intent *appservice.IntentAPI, data []byte, content *event.MessageEventContent) error {
|
||||
uploaded, err := intent.UploadBytes(data, content.Info.MimeType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
content.URL = uploaded.ContentURI.CUString()
|
||||
|
||||
content.Info.Size = len(data)
|
||||
|
||||
if content.Info.Width == 0 && content.Info.Height == 0 && strings.HasPrefix(content.Info.MimeType, "image/") {
|
||||
cfg, _, _ := image.DecodeConfig(bytes.NewReader(data))
|
||||
content.Info.Width = cfg.Width
|
||||
content.Info.Height = cfg.Height
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
208
bridge/portal.go
208
bridge/portal.go
@@ -2,6 +2,7 @@ package bridge
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -303,6 +304,79 @@ func (p *Portal) markMessageHandled(msg *database.Message, discordID string, mxi
|
||||
return msg
|
||||
}
|
||||
|
||||
func (p *Portal) sendMediaFailedMessage(intent *appservice.IntentAPI, bridgeErr error) {
|
||||
content := &event.MessageEventContent{
|
||||
Body: fmt.Sprintf("Failed to bridge media: %v", bridgeErr),
|
||||
MsgType: event.MsgNotice,
|
||||
}
|
||||
|
||||
_, err := intent.SendMessageEvent(p.MXID, event.EventMessage, content)
|
||||
if err != nil {
|
||||
p.log.Warnfln("failed to send error message to matrix: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Portal) handleDiscordAttachment(intent *appservice.IntentAPI, msgID string, attachment *discordgo.MessageAttachment) {
|
||||
// var captionContent *event.MessageEventContent
|
||||
|
||||
// if attachment.Description != "" {
|
||||
// captionContent = &event.MessageEventContent{
|
||||
// Body: attachment.Description,
|
||||
// MsgType: event.MsgNotice,
|
||||
// }
|
||||
// }
|
||||
// p.log.Debugfln("captionContent: %#v", captionContent)
|
||||
|
||||
content := &event.MessageEventContent{
|
||||
Body: attachment.Filename,
|
||||
Info: &event.FileInfo{
|
||||
Height: attachment.Height,
|
||||
MimeType: attachment.ContentType,
|
||||
Width: attachment.Width,
|
||||
|
||||
// This gets overwritten later after the file is uploaded to the homeserver
|
||||
Size: attachment.Size,
|
||||
},
|
||||
}
|
||||
|
||||
switch strings.ToLower(strings.Split(attachment.ContentType, "/")[0]) {
|
||||
case "audio":
|
||||
content.MsgType = event.MsgAudio
|
||||
case "image":
|
||||
content.MsgType = event.MsgImage
|
||||
case "video":
|
||||
content.MsgType = event.MsgVideo
|
||||
default:
|
||||
content.MsgType = event.MsgFile
|
||||
}
|
||||
|
||||
data, err := p.downloadDiscordAttachment(attachment.URL)
|
||||
if err != nil {
|
||||
p.sendMediaFailedMessage(intent, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
err = p.uploadMatrixAttachment(intent, data, content)
|
||||
if err != nil {
|
||||
p.sendMediaFailedMessage(intent, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := intent.SendMessageEvent(p.MXID, event.EventMessage, content)
|
||||
if err != nil {
|
||||
p.log.Warnfln("failed to send media message to matrix: %v", err)
|
||||
}
|
||||
|
||||
dbAttachment := p.bridge.db.Attachment.New()
|
||||
dbAttachment.Channel = p.Key
|
||||
dbAttachment.DiscordMessageID = msgID
|
||||
dbAttachment.DiscordAttachmentID = attachment.ID
|
||||
dbAttachment.MatrixEventID = resp.EventID
|
||||
dbAttachment.Insert()
|
||||
}
|
||||
|
||||
func (p *Portal) handleDiscordMessageCreate(user *User, msg *discordgo.Message) {
|
||||
if msg.Author != nil && user.ID == msg.Author.ID {
|
||||
return
|
||||
@@ -321,22 +395,29 @@ func (p *Portal) handleDiscordMessageCreate(user *User, msg *discordgo.Message)
|
||||
return
|
||||
}
|
||||
|
||||
content := &event.MessageEventContent{
|
||||
Body: msg.Content,
|
||||
MsgType: event.MsgText,
|
||||
}
|
||||
|
||||
intent := p.bridge.GetPuppetByID(msg.Author.ID).IntentFor(p)
|
||||
|
||||
resp, err := intent.SendMessageEvent(p.MXID, event.EventMessage, content)
|
||||
if err != nil {
|
||||
p.log.Warnfln("failed to send message %q to matrix: %v", msg.ID, err)
|
||||
if msg.Content != "" {
|
||||
content := &event.MessageEventContent{
|
||||
Body: msg.Content,
|
||||
MsgType: event.MsgText,
|
||||
}
|
||||
|
||||
return
|
||||
resp, err := intent.SendMessageEvent(p.MXID, event.EventMessage, content)
|
||||
if err != nil {
|
||||
p.log.Warnfln("failed to send message %q to matrix: %v", msg.ID, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
ts, _ := msg.Timestamp.Parse()
|
||||
p.markMessageHandled(existing, msg.ID, resp.EventID, msg.Author.ID, ts)
|
||||
}
|
||||
|
||||
ts, _ := msg.Timestamp.Parse()
|
||||
p.markMessageHandled(existing, msg.ID, resp.EventID, msg.Author.ID, ts)
|
||||
// now run through any attachments the message has
|
||||
for _, attachment := range msg.Attachments {
|
||||
p.handleDiscordAttachment(intent, msg.ID, attachment)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Portal) handleDiscordMessagesUpdate(user *User, msg *discordgo.Message) {
|
||||
@@ -350,9 +431,45 @@ func (p *Portal) handleDiscordMessagesUpdate(user *User, msg *discordgo.Message)
|
||||
return
|
||||
}
|
||||
|
||||
intent := p.bridge.GetPuppetByID(msg.Author.ID).IntentFor(p)
|
||||
|
||||
existing := p.bridge.db.Message.GetByDiscordID(p.Key, msg.ID)
|
||||
if existing == nil {
|
||||
p.log.Debugln("failed to find previous message to update", msg.ID)
|
||||
// Due to the differences in Discord and Matrix attachment handling,
|
||||
// existing will return nil if the original message was empty as we
|
||||
// don't store/save those messages so we can determine when we're
|
||||
// working against an attachment and do the attachment lookup instead.
|
||||
|
||||
// Find all the existing attachments and drop them in a map so we can
|
||||
// figure out which, if any have been deleted and clean them up on the
|
||||
// matrix side.
|
||||
attachmentMap := map[string]*database.Attachment{}
|
||||
attachments := p.bridge.db.Attachment.GetAllByDiscordMessageID(p.Key, msg.ID)
|
||||
|
||||
for _, attachment := range attachments {
|
||||
attachmentMap[attachment.DiscordAttachmentID] = attachment
|
||||
}
|
||||
|
||||
// Now run through the list of attachments on this message and remove
|
||||
// them from the map.
|
||||
for _, attachment := range msg.Attachments {
|
||||
if _, found := attachmentMap[attachment.ID]; found {
|
||||
delete(attachmentMap, attachment.ID)
|
||||
}
|
||||
}
|
||||
|
||||
// Finally run through any attachments still in the map and delete them
|
||||
// on the matrix side and our database.
|
||||
for _, attachment := range attachmentMap {
|
||||
_, err := intent.RedactEvent(p.MXID, attachment.MatrixEventID)
|
||||
if err != nil {
|
||||
p.log.Warnfln("Failed to remove attachment %s: %v", attachment.MatrixEventID, err)
|
||||
}
|
||||
|
||||
attachment.Delete()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
content := &event.MessageEventContent{
|
||||
@@ -362,8 +479,6 @@ func (p *Portal) handleDiscordMessagesUpdate(user *User, msg *discordgo.Message)
|
||||
|
||||
content.SetEdit(existing.MatrixID)
|
||||
|
||||
intent := p.bridge.GetPuppetByID(msg.Author.ID).IntentFor(p)
|
||||
|
||||
_, err := intent.SendMessageEvent(p.MXID, event.EventMessage, content)
|
||||
if err != nil {
|
||||
p.log.Warnfln("failed to send message %q to matrix: %v", msg.ID, err)
|
||||
@@ -384,13 +499,9 @@ func (p *Portal) handleDiscordMessageDelete(user *User, msg *discordgo.Message)
|
||||
// add guild message support, but we'll cross that bridge when we get
|
||||
// there.
|
||||
|
||||
// Find the message that we're working with.
|
||||
// Find the message that we're working with. This could correctly return
|
||||
// nil if the message was just one or more attachments.
|
||||
existing := p.bridge.db.Message.GetByDiscordID(p.Key, msg.ID)
|
||||
if existing == nil {
|
||||
p.log.Debugfln("failed to find message", msg.ID)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var intent *appservice.IntentAPI
|
||||
|
||||
@@ -400,12 +511,25 @@ func (p *Portal) handleDiscordMessageDelete(user *User, msg *discordgo.Message)
|
||||
p.log.Errorfln("no guilds yet...")
|
||||
}
|
||||
|
||||
_, err := intent.RedactEvent(p.MXID, existing.MatrixID)
|
||||
if err != nil {
|
||||
p.log.Warnfln("Failed to remove message %s: %v", existing.MatrixID, err)
|
||||
if existing != nil {
|
||||
_, err := intent.RedactEvent(p.MXID, existing.MatrixID)
|
||||
if err != nil {
|
||||
p.log.Warnfln("Failed to remove message %s: %v", existing.MatrixID, err)
|
||||
}
|
||||
|
||||
existing.Delete()
|
||||
}
|
||||
|
||||
existing.Delete()
|
||||
// Now delete all of the existing attachments.
|
||||
attachments := p.bridge.db.Attachment.GetAllByDiscordMessageID(p.Key, msg.ID)
|
||||
for _, attachment := range attachments {
|
||||
_, err := intent.RedactEvent(p.MXID, attachment.MatrixEventID)
|
||||
if err != nil {
|
||||
p.log.Warnfln("Failed to remove attachment %s: %v", attachment.MatrixEventID, err)
|
||||
}
|
||||
|
||||
attachment.Delete()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Portal) syncParticipants(source *User, participants []*discordgo.User) {
|
||||
@@ -615,16 +739,38 @@ func (p *Portal) handleMatrixReaction(evt *event.Event) {
|
||||
return
|
||||
}
|
||||
|
||||
msg := p.bridge.db.Message.GetByMatrixID(p.Key, reaction.RelatesTo.EventID)
|
||||
if msg.DiscordID == "" {
|
||||
p.log.Debugf("Message %s has not yet been sent to discord", reaction.RelatesTo.EventID)
|
||||
var discordID string
|
||||
|
||||
return
|
||||
msg := p.bridge.db.Message.GetByMatrixID(p.Key, reaction.RelatesTo.EventID)
|
||||
|
||||
// Due to the differences in attachments between Discord and Matrix, if a
|
||||
// user reacts to a media message on discord our lookup above will fail
|
||||
// because the relation of matrix media messages to attachments in handled
|
||||
// in the attachments table instead of messages so we need to check that
|
||||
// before continuing.
|
||||
//
|
||||
// This also leads to interesting problems when a Discord message comes in
|
||||
// with multiple attachments. A user can react to each one individually on
|
||||
// Matrix, which will cause us to send it twice. Discord tends to ignore
|
||||
// this, but if the user removes one of them, discord removes it and now
|
||||
// they're out of sync. Perhaps we should add a counter to the reactions
|
||||
// table to keep them in sync and to avoid sending duplicates to Discord.
|
||||
if msg == nil {
|
||||
attachment := p.bridge.db.Attachment.GetByMatrixID(p.Key, reaction.RelatesTo.EventID)
|
||||
discordID = attachment.DiscordMessageID
|
||||
} else {
|
||||
if msg.DiscordID == "" {
|
||||
p.log.Debugf("Message %s has not yet been sent to discord", reaction.RelatesTo.EventID)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
discordID = msg.DiscordID
|
||||
}
|
||||
|
||||
err := user.Session.MessageReactionAdd(p.Key.ChannelID, msg.DiscordID, reaction.RelatesTo.Key)
|
||||
err := user.Session.MessageReactionAdd(p.Key.ChannelID, discordID, reaction.RelatesTo.Key)
|
||||
if err != nil {
|
||||
p.log.Debugf("Failed to send reaction %s@%s: %v", p.Key, msg.DiscordID, err)
|
||||
p.log.Debugf("Failed to send reaction %s@%s: %v", p.Key, discordID, err)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -633,7 +779,7 @@ func (p *Portal) handleMatrixReaction(evt *event.Event) {
|
||||
dbReaction.Channel.ChannelID = p.Key.ChannelID
|
||||
dbReaction.Channel.Receiver = p.Key.Receiver
|
||||
dbReaction.MatrixEventID = evt.ID
|
||||
dbReaction.DiscordMessageID = msg.DiscordID
|
||||
dbReaction.DiscordMessageID = discordID
|
||||
dbReaction.AuthorID = user.ID
|
||||
dbReaction.MatrixName = reaction.RelatesTo.Key
|
||||
dbReaction.DiscordID = reaction.RelatesTo.Key
|
||||
|
||||
70
database/attachment.go
Normal file
70
database/attachment.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
log "maunium.net/go/maulogger/v2"
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
type Attachment struct {
|
||||
db *Database
|
||||
log log.Logger
|
||||
|
||||
Channel PortalKey
|
||||
|
||||
DiscordMessageID string
|
||||
DiscordAttachmentID string
|
||||
MatrixEventID id.EventID
|
||||
}
|
||||
|
||||
func (a *Attachment) Scan(row Scannable) *Attachment {
|
||||
err := row.Scan(
|
||||
&a.Channel.ChannelID, &a.Channel.Receiver,
|
||||
&a.DiscordMessageID, &a.DiscordAttachmentID,
|
||||
&a.MatrixEventID)
|
||||
|
||||
if err != nil {
|
||||
if !errors.Is(err, sql.ErrNoRows) {
|
||||
a.log.Errorln("Database scan failed:", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *Attachment) Insert() {
|
||||
query := "INSERT INTO attachment" +
|
||||
" (channel_id, receiver, discord_message_id, discord_attachment_id, " +
|
||||
" matrix_event_id) VALUES ($1, $2, $3, $4, $5);"
|
||||
|
||||
_, err := a.db.Exec(
|
||||
query,
|
||||
a.Channel.ChannelID, a.Channel.Receiver,
|
||||
a.DiscordMessageID, a.DiscordAttachmentID,
|
||||
a.MatrixEventID,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
a.log.Warnfln("Failed to insert attachment for %s@%s: %v", a.Channel, a.DiscordMessageID, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Attachment) Delete() {
|
||||
query := "DELETE FROM attachment WHERE" +
|
||||
" channel_id=$1 AND receiver=$2 AND discord_attachment_id=$3 AND" +
|
||||
" matrix_event_id=$4"
|
||||
|
||||
_, err := a.db.Exec(
|
||||
query,
|
||||
a.Channel.ChannelID, a.Channel.Receiver,
|
||||
a.DiscordAttachmentID, a.MatrixEventID,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
a.log.Warnfln("Failed to delete attachment for %s@%s: %v", a.Channel, a.DiscordAttachmentID, err)
|
||||
}
|
||||
}
|
||||
73
database/attachmentquery.go
Normal file
73
database/attachmentquery.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
log "maunium.net/go/maulogger/v2"
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
type AttachmentQuery struct {
|
||||
db *Database
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
const (
|
||||
attachmentSelect = "SELECT channel_id, receiver, discord_message_id," +
|
||||
" discord_attachment_id, matrix_event_id FROM attachment"
|
||||
)
|
||||
|
||||
func (aq *AttachmentQuery) New() *Attachment {
|
||||
return &Attachment{
|
||||
db: aq.db,
|
||||
log: aq.log,
|
||||
}
|
||||
}
|
||||
|
||||
func (aq *AttachmentQuery) GetAllByDiscordMessageID(key PortalKey, discordMessageID string) []*Attachment {
|
||||
query := attachmentSelect + " WHERE channel_id=$1 AND receiver=$2 AND" +
|
||||
" discord_message_id=$3"
|
||||
|
||||
return aq.getAll(query, key.ChannelID, key.Receiver, discordMessageID)
|
||||
}
|
||||
|
||||
func (aq *AttachmentQuery) getAll(query string, args ...interface{}) []*Attachment {
|
||||
rows, err := aq.db.Query(query, args...)
|
||||
if err != nil {
|
||||
aq.log.Debugfln("getAll failed: %v", err)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if rows == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
attachments := []*Attachment{}
|
||||
for rows.Next() {
|
||||
attachments = append(attachments, aq.New().Scan(rows))
|
||||
}
|
||||
|
||||
return attachments
|
||||
}
|
||||
|
||||
func (aq *AttachmentQuery) GetByDiscordAttachmentID(key PortalKey, discordMessageID, discordID string) *Attachment {
|
||||
query := attachmentSelect + " WHERE channel_id=$1 AND receiver=$2" +
|
||||
" AND discord_message_id=$3 AND discord_id=$4"
|
||||
|
||||
return aq.get(query, key.ChannelID, key.Receiver, discordMessageID, discordID)
|
||||
}
|
||||
|
||||
func (aq *AttachmentQuery) GetByMatrixID(key PortalKey, matrixEventID id.EventID) *Attachment {
|
||||
query := attachmentSelect + " WHERE channel_id=$1 AND receiver=$2" +
|
||||
" AND matrix_event_id=$3"
|
||||
|
||||
return aq.get(query, key.ChannelID, key.Receiver, matrixEventID)
|
||||
}
|
||||
|
||||
func (aq *AttachmentQuery) get(query string, args ...interface{}) *Attachment {
|
||||
row := aq.db.QueryRow(query, args...)
|
||||
if row == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return aq.New().Scan(row)
|
||||
}
|
||||
@@ -16,11 +16,12 @@ type Database struct {
|
||||
log log.Logger
|
||||
dialect string
|
||||
|
||||
User *UserQuery
|
||||
Portal *PortalQuery
|
||||
Puppet *PuppetQuery
|
||||
Message *MessageQuery
|
||||
Reaction *ReactionQuery
|
||||
User *UserQuery
|
||||
Portal *PortalQuery
|
||||
Puppet *PuppetQuery
|
||||
Message *MessageQuery
|
||||
Reaction *ReactionQuery
|
||||
Attachment *AttachmentQuery
|
||||
}
|
||||
|
||||
func New(dbType, uri string, maxOpenConns, maxIdleConns int, baseLog log.Logger) (*Database, error) {
|
||||
@@ -73,5 +74,10 @@ func New(dbType, uri string, maxOpenConns, maxIdleConns int, baseLog log.Logger)
|
||||
log: db.log.Sub("Reaction"),
|
||||
}
|
||||
|
||||
db.Attachment = &AttachmentQuery{
|
||||
db: db,
|
||||
log: db.log.Sub("Attachment"),
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
12
database/migrations/02-attachments.sql
Normal file
12
database/migrations/02-attachments.sql
Normal file
@@ -0,0 +1,12 @@
|
||||
CREATE TABLE attachment (
|
||||
channel_id TEXT NOT NULL,
|
||||
receiver TEXT NOT NULL,
|
||||
|
||||
discord_message_id TEXT NOT NULL,
|
||||
discord_attachment_id TEXT NOT NULL,
|
||||
|
||||
matrix_event_id TEXT NOT NULL UNIQUE,
|
||||
|
||||
PRIMARY KEY(discord_attachment_id, matrix_event_id),
|
||||
FOREIGN KEY(channel_id, receiver) REFERENCES portal(channel_id, receiver) ON DELETE CASCADE
|
||||
);
|
||||
@@ -40,6 +40,7 @@ func Run(db *sql.DB, baseLog log.Logger) error {
|
||||
migrator.WithLogger(logger),
|
||||
migrator.Migrations(
|
||||
migrationFromFile("01-initial.sql"),
|
||||
migrationFromFile("02-attachments.sql"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
6
go.mod
6
go.mod
@@ -6,7 +6,7 @@ require (
|
||||
github.com/alecthomas/kong v0.2.18
|
||||
github.com/bwmarrin/discordgo v0.23.2
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/lib/pq v1.9.0
|
||||
github.com/lopezator/migrator v0.3.0
|
||||
github.com/mattn/go-sqlite3 v1.14.10
|
||||
@@ -21,9 +21,9 @@ require (
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220209195652-db638375bc3a // indirect
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect
|
||||
)
|
||||
|
||||
replace github.com/bwmarrin/discordgo v0.23.2 => gitlab.com/beeper/discordgo v0.23.3-0.20220210113317-784a5c1cfaa2
|
||||
replace github.com/bwmarrin/discordgo v0.23.2 => gitlab.com/beeper/discordgo v0.23.3-0.20220219094025-13ff4cc63da7
|
||||
|
||||
6
go.sum
6
go.sum
@@ -26,6 +26,8 @@ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
@@ -57,6 +59,8 @@ github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhso
|
||||
github.com/tidwall/sjson v1.2.3/go.mod h1:5WdjKx3AQMvCJ4RG6/2UYT7dLrGvJUV1x4jdTAyGvZs=
|
||||
gitlab.com/beeper/discordgo v0.23.3-0.20220210113317-784a5c1cfaa2 h1:CK9faDZlCY4rbxpqPArNdMy1kOsIrVHDEAVJcgarnrg=
|
||||
gitlab.com/beeper/discordgo v0.23.3-0.20220210113317-784a5c1cfaa2/go.mod h1:Hwfv4M8yP/MDh47BN+4Z1WItJ1umLKUyplCH5KcQPgE=
|
||||
gitlab.com/beeper/discordgo v0.23.3-0.20220219094025-13ff4cc63da7 h1:8ieR27GadHnShqhsvPrDzL1/ZOntavGGt4TXqafncYE=
|
||||
gitlab.com/beeper/discordgo v0.23.3-0.20220219094025-13ff4cc63da7/go.mod h1:Hwfv4M8yP/MDh47BN+4Z1WItJ1umLKUyplCH5KcQPgE=
|
||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
@@ -64,6 +68,8 @@ golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220209195652-db638375bc3a h1:atOEWVSedO4ksXBe/UrlbSLVxQQ9RxM/tT2Jy10IaHo=
|
||||
golang.org/x/crypto v0.0.0-20220209195652-db638375bc3a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
|
||||
Reference in New Issue
Block a user