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=