From 680f7bdbea270f2cf944a430899b33f87f508db2 Mon Sep 17 00:00:00 2001 From: Gary Kramlich Date: Tue, 25 Jan 2022 23:22:20 -0600 Subject: [PATCH] A ton of work getting towards dms. This commit also points to my fork of discordgo which makes it look like the official client which is the only way to get the actually contents of a dm when not authorized as a bot. --- bridge/bot.go | 2 +- bridge/bridge.go | 15 ++- bridge/matrix.go | 12 +- bridge/portal.go | 183 ++++++++++++++++++++++++++++- bridge/puppet.go | 27 ++++- bridge/user.go | 128 +++++++++++++++++++- config/bridge.go | 55 +++++++-- database/migrations/01-initial.sql | 44 +++++-- database/portal.go | 29 ++++- database/portalkey.go | 15 ++- database/portalquery.go | 6 +- database/puppet.go | 13 ++ database/puppetquery.go | 2 +- go.mod | 2 + go.sum | 2 + 15 files changed, 478 insertions(+), 57 deletions(-) diff --git a/bridge/bot.go b/bridge/bot.go index 0239412..665bbea 100644 --- a/bridge/bot.go +++ b/bridge/bot.go @@ -5,7 +5,7 @@ import ( ) func (b *Bridge) updateBotProfile() { - cfg := b.config.Appservice.Bot + cfg := b.Config.Appservice.Bot // Set the bot's avatar. if cfg.Avatar != "" { diff --git a/bridge/bridge.go b/bridge/bridge.go index c3d08a9..d5d8980 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -21,7 +21,7 @@ const ( ) type Bridge struct { - config *config.Config + Config *config.Config log log.Logger @@ -44,6 +44,8 @@ type Bridge struct { puppets map[string]*Puppet puppetsLock sync.Mutex + + StateStore *database.SQLStateStore } func New(cfg *config.Config) (*Bridge, error) { @@ -73,12 +75,17 @@ func New(cfg *config.Config) (*Bridge, error) { return nil, err } + // Create the state store + logger.Debugln("Initializing state store") + stateStore := database.NewSQLStateStore(db) + appservice.StateStore = stateStore + // Create the bridge. bridge := &Bridge{ as: appservice, db: db, bot: bot, - config: cfg, + Config: cfg, log: logger, usersByMXID: make(map[id.UserID]*User), @@ -88,6 +95,10 @@ func New(cfg *config.Config) (*Bridge, error) { portalsByMXID: make(map[id.RoomID]*Portal), portalsByID: make(map[database.PortalKey]*Portal), + + puppets: make(map[string]*Puppet), + + StateStore: stateStore, } // Setup the event processors diff --git a/bridge/matrix.go b/bridge/matrix.go index 238b8ea..b05de42 100644 --- a/bridge/matrix.go +++ b/bridge/matrix.go @@ -78,7 +78,7 @@ func (mh *matrixHandler) handleMessage(evt *event.Event) { content.RemoveReplyFallback() if content.MsgType == event.MsgText { - prefix := mh.bridge.config.Bridge.CommandPrefix + prefix := mh.bridge.Config.Bridge.CommandPrefix hasPrefix := strings.HasPrefix(content.Body, prefix) if hasPrefix { @@ -150,16 +150,16 @@ func (mh *matrixHandler) handleBotInvite(evt *event.Event) { // Wait to send the welcome message until we're sure we're not in an empty // room. - mh.sendNoticeWithmarkdown(evt.RoomID, mh.bridge.config.Bridge.ManagementRoomText.Welcome) + mh.sendNoticeWithmarkdown(evt.RoomID, mh.bridge.Config.Bridge.ManagementRoomText.Welcome) if evt.RoomID == user.ManagementRoom { if user.HasSession() { - mh.sendNoticeWithmarkdown(evt.RoomID, mh.bridge.config.Bridge.ManagementRoomText.Connected) + mh.sendNoticeWithmarkdown(evt.RoomID, mh.bridge.Config.Bridge.ManagementRoomText.Connected) } else { - mh.sendNoticeWithmarkdown(evt.RoomID, mh.bridge.config.Bridge.ManagementRoomText.NotConnected) + mh.sendNoticeWithmarkdown(evt.RoomID, mh.bridge.Config.Bridge.ManagementRoomText.NotConnected) } - additionalHelp := mh.bridge.config.Bridge.ManagementRoomText.AdditionalHelp + additionalHelp := mh.bridge.Config.Bridge.ManagementRoomText.AdditionalHelp if additionalHelp != "" { mh.sendNoticeWithmarkdown(evt.RoomID, additionalHelp) } @@ -201,8 +201,6 @@ func (mh *matrixHandler) handleMembership(evt *event.Event) { mh.handlePuppetInvite(evt, user, puppet) } - mh.log.Warnln("no existing portal for", evt.RoomID) - return } diff --git a/bridge/portal.go b/bridge/portal.go index 570aaec..8de21b7 100644 --- a/bridge/portal.go +++ b/bridge/portal.go @@ -2,9 +2,12 @@ package bridge import ( "fmt" + "sync" + + "github.com/bwmarrin/discordgo" log "maunium.net/go/maulogger/v2" - + "maunium.net/go/mautrix" "maunium.net/go/mautrix/appservice" "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/id" @@ -12,7 +15,12 @@ import ( "gitlab.com/beeper/discord/database" ) -type PortalMatrixMessage struct { +type portalDiscordMessage struct { + msg interface{} + user *User +} + +type portalMatrixMessage struct { evt *event.Event user *User } @@ -23,9 +31,18 @@ type Portal struct { bridge *Bridge log log.Logger - matrixMessages chan PortalMatrixMessage + channelType discordgo.ChannelType + + roomCreateLock sync.Mutex + + discordMessages chan portalDiscordMessage + matrixMessages chan portalMatrixMessage } +var ( + portalCreationDummyEvent = event.Type{Type: "fi.mau.dummy.portal_created", Class: event.MessageEventType} +) + func (b *Bridge) loadPortal(dbPortal *database.Portal, key *database.PortalKey) *Portal { // If we weren't given a portal we'll attempt to create it if a key was // provided. @@ -63,13 +80,26 @@ func (b *Bridge) GetPortalByMXID(mxid id.RoomID) *Portal { return portal } +func (b *Bridge) GetPortalByID(key database.PortalKey) *Portal { + b.portalsLock.Lock() + defer b.portalsLock.Unlock() + + portal, ok := b.portalsByID[key] + if !ok { + return b.loadPortal(b.db.Portal.GetByID(key), &key) + } + + return portal +} + func (b *Bridge) NewPortal(dbPortal *database.Portal) *Portal { portal := &Portal{ Portal: dbPortal, bridge: b, log: b.log.Sub(fmt.Sprintf("Portal/%s", dbPortal.Key)), - matrixMessages: make(chan PortalMatrixMessage, b.config.Bridge.PortalMessageBuffer), + discordMessages: make(chan portalDiscordMessage, b.Config.Bridge.PortalMessageBuffer), + matrixMessages: make(chan portalMatrixMessage, b.Config.Bridge.PortalMessageBuffer), } go portal.messageLoop() @@ -91,13 +121,15 @@ func (p *Portal) messageLoop() { for { select { case msg := <-p.matrixMessages: - p.log.Infoln("got message", msg) + p.log.Infoln("got matrix message", msg) + case msg := <-p.discordMessages: + p.handleDiscordMessage(msg) } } } func (p *Portal) IsPrivateChat() bool { - return false + return (p.channelType == discordgo.ChannelTypeDM || p.channelType == discordgo.ChannelTypeGroupDM) } func (p *Portal) MainIntent() *appservice.IntentAPI { @@ -107,3 +139,142 @@ func (p *Portal) MainIntent() *appservice.IntentAPI { return p.bridge.bot } + +func (p *Portal) createMatrixRoom(user *User, channel *discordgo.Channel) error { + p.roomCreateLock.Lock() + defer p.roomCreateLock.Unlock() + + // If we have a matrix id the room should exist so we have nothing to do. + if p.MXID != "" { + return nil + } + + p.channelType = channel.Type + + intent := p.MainIntent() + if err := intent.EnsureRegistered(); err != nil { + return err + } + + if p.IsPrivateChat() { + puppet := p.bridge.GetPuppetByID(p.Key.ID) + puppet.SyncContact(user) + + p.Name = puppet.DisplayName + p.Avatar = puppet.Avatar + p.AvatarURL = puppet.AvatarURL + } + + p.log.Infoln("Creating Matrix room. Info source:", p.Portal.Key.ID) + + initialState := []*event.Event{} + + creationContent := make(map[string]interface{}) + // if !portal.bridge.Config.Bridge.FederateRooms { + creationContent["m.federate"] = false + // } + + var invite []id.UserID + + if p.IsPrivateChat() { + invite = append(invite, p.bridge.bot.UserID) + } + + resp, err := intent.CreateRoom(&mautrix.ReqCreateRoom{ + Visibility: "private", + Name: p.Name, + Topic: p.Topic, + Preset: "private_chat", + IsDirect: p.IsPrivateChat(), + InitialState: initialState, + CreationContent: creationContent, + }) + if err != nil { + return err + } + + p.MXID = resp.RoomID + p.Update() + p.bridge.portalsLock.Lock() + p.bridge.portalsByMXID[p.MXID] = p + p.bridge.portalsLock.Unlock() + + p.ensureUserInvited(user) + + // if p.IsPrivateChat() { + // puppet := user.bridge.GetPuppetByID(p.Key.ID) + + // if p.bridge.Config.Bridge.Encryption.Default { + // err = portal.bridge.Bot.EnsureJoined(portal.MXID) + // if err != nil { + // portal.log.Errorln("Failed to join created portal with bridge bot for e2be:", err) + // } + // } + + // user.UpdateDirectChats(map[id.UserID][]id.RoomID{puppet.MXID: {portal.MXID}}) + // } + + firstEventResp, err := p.MainIntent().SendMessageEvent(p.MXID, portalCreationDummyEvent, struct{}{}) + if err != nil { + p.log.Errorln("Failed to send dummy event to mark portal creation:", err) + } else { + p.FirstEventID = firstEventResp.EventID + p.Update() + } + + return nil +} + +func (p *Portal) handleDiscordMessage(msg portalDiscordMessage) { + if p.MXID == "" { + p.log.Debugln("Creating Matrix room from incoming message") + + discordMsg := msg.msg.(*discordgo.MessageCreate) + channel, err := msg.user.Session.Channel(discordMsg.ChannelID) + if err != nil { + p.log.Errorln("Failed to find channel for message:", err) + + return + } + + if err := p.createMatrixRoom(msg.user, channel); err != nil { + p.log.Errorln("Failed to create portal room:", err) + + return + } + } + + switch msg.msg.(type) { + case *discordgo.MessageCreate: + p.handleMessage(msg.msg.(*discordgo.MessageCreate).Message) + default: + p.log.Warnln("unknown message type") + } +} + +func (p *Portal) ensureUserInvited(user *User) bool { + return user.ensureInvited(p.MainIntent(), p.MXID, p.IsPrivateChat()) +} + +func (p *Portal) handleMessage(msg *discordgo.Message) { + if p.MXID == "" { + p.log.Warnln("handle message called without a valid portal") + + return + } + + // TODO: Check if we already got the message + + p.log.Debugln("content", msg.Content) + p.log.Debugln("embeds", msg.Embeds) + p.log.Debugln("msg", msg) + + content := &event.MessageEventContent{ + Body: msg.Content, + MsgType: event.MsgText, + } + + resp, err := p.MainIntent().SendMessageEvent(p.MXID, event.EventMessage, content) + p.log.Warnln("response:", resp) + p.log.Warnln("error:", err) +} diff --git a/bridge/puppet.go b/bridge/puppet.go index 20f7fa1..17674c1 100644 --- a/bridge/puppet.go +++ b/bridge/puppet.go @@ -3,6 +3,7 @@ package bridge import ( "fmt" "regexp" + "sync" log "maunium.net/go/maulogger/v2" "maunium.net/go/mautrix/appservice" @@ -18,6 +19,8 @@ type Puppet struct { log log.Logger MXID id.UserID + + syncLock sync.Mutex } var userIDRegex *regexp.Regexp @@ -36,8 +39,8 @@ func (b *Bridge) ParsePuppetMXID(mxid id.UserID) (string, bool) { if userIDRegex == nil { pattern := fmt.Sprintf( "^@%s:%s$", - b.config.Bridge.FormatUsername("([0-9]+)"), - b.config.Homeserver.Domain, + b.Config.Bridge.FormatUsername("([0-9]+)"), + b.Config.Homeserver.Domain, ) userIDRegex = regexp.MustCompile(pattern) @@ -82,11 +85,27 @@ func (b *Bridge) GetPuppetByID(id string) *Puppet { func (b *Bridge) FormatPuppetMXID(did string) id.UserID { return id.NewUserID( - b.config.Bridge.FormatUsername(did), - b.config.Homeserver.Domain, + b.Config.Bridge.FormatUsername(did), + b.Config.Homeserver.Domain, ) } func (p *Puppet) DefaultIntent() *appservice.IntentAPI { return p.bridge.as.Intent(p.MXID) } + +func (p *Puppet) SyncContact(user *User) { + p.syncLock.Lock() + defer p.syncLock.Unlock() + + dUser, err := user.Session.User(p.ID) + if err != nil { + p.log.Warnfln("failed to sync puppet %s: %v", p.ID, err) + + return + } + + p.DisplayName = p.bridge.Config.Bridge.FormatDisplayname(dUser) + + p.Update() +} diff --git a/bridge/user.go b/bridge/user.go index e7456ef..84ac991 100644 --- a/bridge/user.go +++ b/bridge/user.go @@ -1,10 +1,14 @@ package bridge import ( + "errors" + "strings" + "github.com/bwmarrin/discordgo" "github.com/skip2/go-qrcode" log "maunium.net/go/maulogger/v2" + "maunium.net/go/mautrix" "maunium.net/go/mautrix/appservice" "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/id" @@ -177,13 +181,131 @@ func (u *User) Login(token string) error { } func (u *User) Connect() error { + u.log.Debugln("connecting to discord") + + // get our user info + user, err := u.User.Session.User("@me") + if err != nil { + return err + } + + u.User.ID = user.ID + + // Add our event handlers + u.User.Session.AddHandler(u.connectedHandler) + u.User.Session.AddHandler(u.disconnectedHandler) + + u.User.Session.AddHandler(u.channelCreateHandler) + u.User.Session.AddHandler(u.channelDeleteHandler) + u.User.Session.AddHandler(u.channelPinsUpdateHandler) + u.User.Session.AddHandler(u.channelUpdateHandler) + u.User.Session.AddHandler(u.messageHandler) - u.log.Warnln("logged in, opening websocket") + // u.User.Session.Identify.Capabilities = 125 + // // Setup our properties + // u.User.Session.Identify.Properties = discordgo.IdentifyProperties{ + // OS: "Windows", + // OSVersion: "10", + // Browser: "Chrome", + // BrowserUserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36", + // BrowserVersion: "92.0.4515.159", + // Referrer: "https://discord.com/channels/@me", + // ReferringDomain: "discord.com", + // ClientBuildNumber: "83364", + // ReleaseChannel: "stable", + // } + + u.User.Session.Identify.Presence.Status = "online" return u.User.Session.Open() } -func (u *User) messageHandler(s *discordgo.Session, m *discordgo.MessageCreate) { - u.log.Warnln("received message", m) +func (u *User) connectedHandler(s *discordgo.Session, c *discordgo.Connect) { + u.log.Debugln("connected to discord") +} + +func (u *User) disconnectedHandler(s *discordgo.Session, d *discordgo.Disconnect) { + u.log.Debugln("disconnected from discord") +} + +func (u *User) channelCreateHandler(s *discordgo.Session, c *discordgo.ChannelCreate) { + key := database.NewPortalKey(u.User.ID, c.ID) + portal := u.bridge.GetPortalByID(key) + + portal.Name = c.Name + portal.Topic = c.Topic + + if c.Icon != "" { + u.log.Debugln("channel icon", c.Icon) + } + + portal.Update() + + portal.createMatrixRoom(u, c.Channel) +} + +func (u *User) channelDeleteHandler(s *discordgo.Session, c *discordgo.ChannelDelete) { + u.log.Debugln("channel delete handler") +} + +func (u *User) channelPinsUpdateHandler(s *discordgo.Session, c *discordgo.ChannelPinsUpdate) { + u.log.Debugln("channel pins update") +} + +func (u *User) channelUpdateHandler(s *discordgo.Session, c *discordgo.ChannelUpdate) { + key := database.NewPortalKey(u.User.ID, c.ID) + portal := u.bridge.GetPortalByID(key) + + portal.Name = c.Name + portal.Topic = c.Topic + u.log.Debugln("channel icon", c.Icon) + portal.Update() + + u.log.Debugln("channel update") +} + +func (u *User) messageHandler(s *discordgo.Session, m *discordgo.MessageCreate) { + if m.GuildID != "" { + u.log.Debugln("ignoring guild build messaged") + + return + } + + key := database.NewPortalKey(u.User.ID, m.ChannelID) + portal := u.bridge.GetPortalByID(key) + + msg := portalDiscordMessage{ + msg: m, + user: u, + } + + portal.discordMessages <- msg +} + +func (u *User) ensureInvited(intent *appservice.IntentAPI, roomID id.RoomID, isDirect bool) bool { + ret := false + + inviteContent := event.Content{ + Parsed: &event.MemberEventContent{ + Membership: event.MembershipInvite, + IsDirect: isDirect, + }, + Raw: map[string]interface{}{}, + } + + resp, err := intent.SendStateEvent(roomID, event.StateMember, u.MXID.String(), &inviteContent) + u.log.Warnfln("resp: %#v", resp) + + var httpErr mautrix.HTTPError + if err != nil && errors.As(err, &httpErr) && httpErr.RespError != nil && strings.Contains(httpErr.RespError.Err, "is already in the room") { + u.bridge.StateStore.SetMembership(roomID, u.MXID, event.MembershipJoin) + ret = true + } else if err != nil { + u.log.Warnfln("Failed to invite user to %s: %v", roomID, err) + } else { + ret = true + } + + return ret } diff --git a/config/bridge.go b/config/bridge.go index f057dad..fbf8873 100644 --- a/config/bridge.go +++ b/config/bridge.go @@ -1,12 +1,15 @@ package config import ( - "bytes" + "strings" "text/template" + + "github.com/bwmarrin/discordgo" ) type bridge struct { - UsernameTemplate string `yaml:"username_template"` + UsernameTemplate string `yaml:"username_template"` + DisplaynameTemplate string `yaml:"displayname_template"` CommandPrefix string `yaml:"command_prefix"` @@ -14,7 +17,8 @@ type bridge struct { PortalMessageBuffer int `yaml:"portal_message_buffer"` - usernameTemplate *template.Template `yaml:"-"` + usernameTemplate *template.Template `yaml:"-"` + displaynameTemplate *template.Template `yaml:"-"` } func (b *bridge) validate() error { @@ -24,15 +28,24 @@ func (b *bridge) validate() error { b.UsernameTemplate = "Discord_{{.}}" } - if b.PortalMessageBuffer <= 0 { - b.PortalMessageBuffer = 128 - } - b.usernameTemplate, err = template.New("username").Parse(b.UsernameTemplate) if err != nil { return err } + if b.DisplaynameTemplate == "" { + b.DisplaynameTemplate = "{{.Username}}#{{.Discriminator}} (D){{if .Bot}} (bot){{end}}" + } + + b.displaynameTemplate, err = template.New("displayname").Parse(b.DisplaynameTemplate) + if err != nil { + return err + } + + if b.PortalMessageBuffer <= 0 { + b.PortalMessageBuffer = 128 + } + if b.CommandPrefix == "" { b.CommandPrefix = "!dis" } @@ -60,9 +73,35 @@ func (b *bridge) UnmarshalYAML(unmarshal func(interface{}) error) error { } func (b bridge) FormatUsername(userid string) string { - var buffer bytes.Buffer + var buffer strings.Builder b.usernameTemplate.Execute(&buffer, userid) return buffer.String() } + +type simplfiedUser struct { + Username string + Discriminator string + Locale string + Verified bool + MFAEnabled bool + Bot bool + System bool +} + +func (b bridge) FormatDisplayname(user *discordgo.User) string { + var buffer strings.Builder + + b.displaynameTemplate.Execute(&buffer, simplfiedUser{ + Username: user.Username, + Discriminator: user.Discriminator, + Locale: user.Locale, + Verified: user.Verified, + MFAEnabled: user.MFAEnabled, + Bot: user.Bot, + System: user.System, + }) + + return buffer.String() +} diff --git a/database/migrations/01-initial.sql b/database/migrations/01-initial.sql index d0a325f..22e484c 100644 --- a/database/migrations/01-initial.sql +++ b/database/migrations/01-initial.sql @@ -1,20 +1,22 @@ -CREATE TABLE IF NOT EXISTS portal ( - id TEXT, - receiver TEXT, - mxid TEXT UNIQUE, +CREATE TABLE portal ( + id TEXT, + channel_id TEXT, + mxid TEXT UNIQUE, - name TEXT NOT NULL, - topic TEXT NOT NULL, + name TEXT NOT NULL, + topic TEXT NOT NULL, avatar TEXT NOT NULL, - avatar_url TEXT NOT NULL, + avatar_url TEXT, - PRIMARY KEY (id, receiver) + first_event_id TEXT, + + PRIMARY KEY (id, channel_id) ); -CREATE TABLE IF NOT EXISTS puppet ( - id TEXT PRIMARY KEY, - displayname TEXT, +CREATE TABLE puppet ( + id TEXT PRIMARY KEY, + display_name TEXT, avatar TEXT, avatar_url TEXT, @@ -22,7 +24,7 @@ CREATE TABLE IF NOT EXISTS puppet ( enable_presence BOOLEAN NOT NULL DEFAULT true ); -CREATE TABLE IF NOT EXISTS user ( +CREATE TABLE user ( mxid TEXT PRIMARY KEY, id TEXT UNIQUE, @@ -30,3 +32,21 @@ CREATE TABLE IF NOT EXISTS user ( token TEXT ); + +CREATE TABLE mx_user_profile ( + room_id TEXT, + user_id TEXT, + membership TEXT NOT NULL, + displayname TEXT, + avatar_url TEXT, + PRIMARY KEY (room_id, user_id) +); + +CREATE TABLE mx_registrations ( + user_id TEXT PRIMARY KEY +); + +CREATE TABLE mx_room_state ( + room_id TEXT PRIMARY KEY, + power_levels TEXT +); diff --git a/database/portal.go b/database/portal.go index 9e60c08..cf8be1e 100644 --- a/database/portal.go +++ b/database/portal.go @@ -19,12 +19,14 @@ type Portal struct { Avatar string AvatarURL id.ContentURI + + FirstEventID id.EventID } func (p *Portal) Scan(row Scannable) *Portal { - var mxid, avatarURL sql.NullString + var mxid, avatarURL, firstEventID sql.NullString - err := row.Scan(&p.Key.ID, &p.Key.Receiver, &mxid, &p.Name, &p.Topic, &p.Avatar, &avatarURL) + err := row.Scan(&p.Key.ID, &p.Key.ChannelID, &mxid, &p.Name, &p.Topic, &p.Avatar, &avatarURL, &firstEventID) if err != nil { if err != sql.ErrNoRows { p.log.Errorln("Database scan failed:", err) @@ -35,19 +37,34 @@ func (p *Portal) Scan(row Scannable) *Portal { p.MXID = id.RoomID(mxid.String) p.AvatarURL, _ = id.ParseContentURI(avatarURL.String) + p.FirstEventID = id.EventID(firstEventID.String) return p } func (p *Portal) Insert() { query := "INSERT INTO portal" + - " (id, receiver, mxid, name, topic, avatar, avatar_url)" + - " VALUES ($1, $2, $3, $4, $5, $6, $7)" + " (id, mxid, channel_id, name, topic, avatar, avatar_url, first_event_id)" + + " VALUES ($1, $2, $3, $4, $5, $6, $7, $8)" - _, err := p.db.Exec(query, p.Key.ID, p.Key.Receiver, p.MXID, - p.Name, p.Topic, p.Avatar, p.AvatarURL.String()) + _, err := p.db.Exec(query, p.Key.ID, p.MXID, p.Key.ChannelID, + p.Name, p.Topic, p.Avatar, p.AvatarURL.String(), p.FirstEventID.String()) if err != nil { p.log.Warnfln("Failed to insert %s: %v", p.Key, err) } } + +func (p *Portal) Update() { + query := "UPDATE portal SET" + + " mxid=$1, name=$2, topic=$3, avatar=$4, avatar_url=$5, first_event_id=$6" + + " WHERE id=$7 AND channel_id=$8" + + _, err := p.db.Exec(query, p.MXID, p.Name, p.Topic, p.Avatar, + p.AvatarURL.String(), p.FirstEventID.String(), p.Key.ID, + p.Key.ChannelID) + + if err != nil { + p.log.Warnfln("Failed to update %s: %v", p.Key, err) + } +} diff --git a/database/portalkey.go b/database/portalkey.go index a4252b9..59f8328 100644 --- a/database/portalkey.go +++ b/database/portalkey.go @@ -1,13 +1,20 @@ package database type PortalKey struct { - ID string - Receiver string + ID string + ChannelID string +} + +func NewPortalKey(id, channelID string) PortalKey { + return PortalKey{ + ID: id, + ChannelID: channelID, + } } func (key PortalKey) String() string { - if key.Receiver == key.ID { + if key.ChannelID == key.ID { return key.ID } - return key.ID + "-" + key.Receiver + return key.ID + "-" + key.ChannelID } diff --git a/database/portalquery.go b/database/portalquery.go index a1fd52f..650d6ee 100644 --- a/database/portalquery.go +++ b/database/portalquery.go @@ -21,8 +21,8 @@ func (pq *PortalQuery) GetAll() []*Portal { return pq.getAll("SELECT * FROM portal") } -func (pq *PortalQuery) GetByDID(key PortalKey) *Portal { - return pq.get("SELECT * FROM portal WHERE did=$1 AND receiver=$2", key.ID, key.Receiver) +func (pq *PortalQuery) GetByID(key PortalKey) *Portal { + return pq.get("SELECT * FROM portal WHERE id=$1 AND channel_id=$2", key.ID, key.ChannelID) } func (pq *PortalQuery) GetByMXID(mxid id.RoomID) *Portal { @@ -30,7 +30,7 @@ func (pq *PortalQuery) GetByMXID(mxid id.RoomID) *Portal { } func (pq *PortalQuery) GetAllByDID(did string) []*Portal { - return pq.getAll("SELECT * FROM portal WHERE did=$1", did) + return pq.getAll("SELECT * FROM portal WHERE id=$1", did) } func (pq *PortalQuery) getAll(query string, args ...interface{}) []*Portal { diff --git a/database/puppet.go b/database/puppet.go index 8603c7b..33af833 100644 --- a/database/puppet.go +++ b/database/puppet.go @@ -54,3 +54,16 @@ func (p *Puppet) Insert() { p.log.Warnfln("Failed to insert %s: %v", p.ID, err) } } + +func (p *Puppet) Update() { + query := "UPDATE puppet" + + " SET display_name=$1, avatar=$2, avatar_url=$3, enable_presence=$4" + + " WHERE id=$5" + + _, err := p.db.Exec(query, p.DisplayName, p.Avatar, p.AvatarURL.String(), + p.EnablePresence, p.ID) + + if err != nil { + p.log.Warnfln("Failed to update %s: %v", p.ID, err) + } +} diff --git a/database/puppetquery.go b/database/puppetquery.go index 1b318b0..ae61178 100644 --- a/database/puppetquery.go +++ b/database/puppetquery.go @@ -19,7 +19,7 @@ func (pq *PuppetQuery) New() *Puppet { } func (pq *PuppetQuery) Get(id string) *Puppet { - row := pq.db.QueryRow("SELECT id, displayname, avatar, avatar_url, enable_presence FROM puppet WHERE id=$1", id) + row := pq.db.QueryRow("SELECT id, display_name, avatar, avatar_url, enable_presence FROM puppet WHERE id=$1", id) if row == nil { return nil } diff --git a/go.mod b/go.mod index 8ec9ef7..0523050 100644 --- a/go.mod +++ b/go.mod @@ -25,3 +25,5 @@ require ( golang.org/x/net v0.0.0-20211216030914-fe4d6282115f // indirect golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect ) + +replace github.com/bwmarrin/discordgo => github.com/grimmy/discordgo v0.23.3-0.20220126043435-7470d1aacd64 diff --git a/go.sum b/go.sum index 5643c23..a348998 100644 --- a/go.sum +++ b/go.sum @@ -28,6 +28,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/grimmy/discordgo v0.23.3-0.20220126043435-7470d1aacd64 h1:KrZb8UPGlmlnUx+eLQDHWnt1uPkdCUeVgUgFRFFdR1o= +github.com/grimmy/discordgo v0.23.3-0.20220126043435-7470d1aacd64/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M= 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=