Bridge friend nicks as DM room name

This commit is contained in:
Tulir Asokan
2023-04-22 02:50:14 +03:00
parent 049ef48fb0
commit 3f3c86754d
8 changed files with 124 additions and 31 deletions

View File

@@ -15,7 +15,7 @@ import (
const (
portalSelect = `
SELECT dcid, receiver, type, other_user_id, dc_guild_id, dc_parent_id, mxid,
plain_name, name, name_set, topic, topic_set, avatar, avatar_url, avatar_set,
plain_name, name, name_set, friend_nick, topic, topic_set, avatar, avatar_url, avatar_set,
encrypted, in_space, first_event_id, relay_webhook_id, relay_webhook_secret
FROM portal
`
@@ -68,6 +68,10 @@ func (pq *PortalQuery) GetByMXID(mxid id.RoomID) *Portal {
return pq.get(portalSelect+" WHERE mxid=$1", mxid)
}
func (pq *PortalQuery) FindPrivateChatBetween(id, receiver string) *Portal {
return pq.get(portalSelect+" WHERE other_user_id=$1 AND receiver=$2 AND type=$3", id, receiver, discordgo.ChannelTypeDM)
}
func (pq *PortalQuery) FindPrivateChatsWith(id string) []*Portal {
return pq.getAll(portalSelect+" WHERE other_user_id=$1 AND type=$2", id, discordgo.ChannelTypeDM)
}
@@ -112,6 +116,7 @@ type Portal struct {
PlainName string
Name string
NameSet bool
FriendNick bool
Topic string
TopicSet bool
Avatar string
@@ -132,7 +137,7 @@ func (p *Portal) Scan(row dbutil.Scannable) *Portal {
var avatarURL string
err := row.Scan(&p.Key.ChannelID, &p.Key.Receiver, &chanType, &otherUserID, &guildID, &parentID,
&mxid, &p.PlainName, &p.Name, &p.NameSet, &p.Topic, &p.TopicSet, &p.Avatar, &avatarURL, &p.AvatarSet,
&mxid, &p.PlainName, &p.Name, &p.NameSet, &p.FriendNick, &p.Topic, &p.TopicSet, &p.Avatar, &avatarURL, &p.AvatarSet,
&p.Encrypted, &p.InSpace, &firstEventID, &relayWebhookID, &relayWebhookSecret)
if err != nil {
@@ -160,13 +165,13 @@ func (p *Portal) Scan(row dbutil.Scannable) *Portal {
func (p *Portal) Insert() {
query := `
INSERT INTO portal (dcid, receiver, type, other_user_id, dc_guild_id, dc_parent_id, mxid,
plain_name, name, name_set, topic, topic_set, avatar, avatar_url, avatar_set,
plain_name, name, name_set, friend_nick, topic, topic_set, avatar, avatar_url, avatar_set,
encrypted, in_space, first_event_id, relay_webhook_id, relay_webhook_secret)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)
`
_, err := p.db.Exec(query, p.Key.ChannelID, p.Key.Receiver, p.Type,
strPtr(p.OtherUserID), strPtr(p.GuildID), strPtr(p.ParentID), strPtr(string(p.MXID)),
p.PlainName, p.Name, p.NameSet, p.Topic, p.TopicSet, p.Avatar, p.AvatarURL.String(), p.AvatarSet,
p.PlainName, p.Name, p.NameSet, p.FriendNick, p.Topic, p.TopicSet, p.Avatar, p.AvatarURL.String(), p.AvatarSet,
p.Encrypted, p.InSpace, p.FirstEventID.String(), strPtr(p.RelayWebhookID), strPtr(p.RelayWebhookSecret))
if err != nil {
@@ -179,14 +184,16 @@ func (p *Portal) Update() {
query := `
UPDATE portal
SET type=$1, other_user_id=$2, dc_guild_id=$3, dc_parent_id=$4, mxid=$5,
plain_name=$6, name=$7, name_set=$8, topic=$9, topic_set=$10, avatar=$11, avatar_url=$12, avatar_set=$13,
encrypted=$14, in_space=$15, first_event_id=$16, relay_webhook_id=$17, relay_webhook_secret=$18
WHERE dcid=$19 AND receiver=$20
plain_name=$6, name=$7, name_set=$8, friend_nick=$9, topic=$10, topic_set=$11,
avatar=$12, avatar_url=$13, avatar_set=$14, encrypted=$15, in_space=$16, first_event_id=$17,
relay_webhook_id=$18, relay_webhook_secret=$19
WHERE dcid=$20 AND receiver=$21
`
_, err := p.db.Exec(query,
p.Type, strPtr(p.OtherUserID), strPtr(p.GuildID), strPtr(p.ParentID), strPtr(string(p.MXID)),
p.PlainName, p.Name, p.NameSet, p.Topic, p.TopicSet, p.Avatar, p.AvatarURL.String(), p.AvatarSet,
p.Encrypted, p.InSpace, p.FirstEventID.String(), strPtr(p.RelayWebhookID), strPtr(p.RelayWebhookSecret),
p.PlainName, p.Name, p.NameSet, p.FriendNick, p.Topic, p.TopicSet,
p.Avatar, p.AvatarURL.String(), p.AvatarSet, p.Encrypted, p.InSpace, p.FirstEventID.String(),
strPtr(p.RelayWebhookID), strPtr(p.RelayWebhookSecret),
p.Key.ChannelID, p.Key.Receiver)
if err != nil {

View File

@@ -1,4 +1,4 @@
-- v0 -> v16: Latest revision
-- v0 -> v17: Latest revision
CREATE TABLE guild (
dcid TEXT PRIMARY KEY,
@@ -29,6 +29,7 @@ CREATE TABLE portal (
plain_name TEXT NOT NULL,
name TEXT NOT NULL,
name_set BOOLEAN NOT NULL,
friend_nick BOOLEAN NOT NULL,
topic TEXT NOT NULL,
topic_set BOOLEAN NOT NULL,
avatar TEXT NOT NULL,

View File

@@ -0,0 +1,2 @@
-- v17: Store whether DM portal name is a friend nickname
ALTER TABLE portal ADD COLUMN friend_nick BOOLEAN NOT NULL DEFAULT false;

2
go.mod
View File

@@ -37,4 +37,4 @@ require (
maunium.net/go/mauflag v1.0.0 // indirect
)
replace github.com/bwmarrin/discordgo => github.com/beeper/discordgo v0.0.0-20230421223629-940c512c92de
replace github.com/bwmarrin/discordgo => github.com/beeper/discordgo v0.0.0-20230421234845-798d8c4943a5

4
go.sum
View File

@@ -1,6 +1,6 @@
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
github.com/beeper/discordgo v0.0.0-20230421223629-940c512c92de h1:jq5xgpkSvFJiiXH8w0SWGjK6jzwU8gZvrNahAq24nyI=
github.com/beeper/discordgo v0.0.0-20230421223629-940c512c92de/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
github.com/beeper/discordgo v0.0.0-20230421234845-798d8c4943a5 h1:goe2lr+0qYbVtdidyl3UMurIGdFnXXZqmNq0vWZo4f4=
github.com/beeper/discordgo v0.0.0-20230421234845-798d8c4943a5/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=

View File

@@ -165,6 +165,20 @@ func (user *User) GetPortalByID(id string, chanType discordgo.ChannelType) *Port
return user.bridge.GetPortalByID(database.NewPortalKey(id, user.DiscordID), chanType)
}
func (user *User) FindPrivateChatWith(userID string) *Portal {
user.bridge.portalsLock.Lock()
defer user.bridge.portalsLock.Unlock()
dbPortal := user.bridge.DB.Portal.FindPrivateChatBetween(userID, user.DiscordID)
if dbPortal == nil {
return nil
}
existing, ok := user.bridge.portalsByID[dbPortal.Key]
if ok {
return existing
}
return user.bridge.loadPortal(dbPortal, nil, discordgo.ChannelTypeDM)
}
func (br *DiscordBridge) GetExistingPortalByID(key database.PortalKey) *Portal {
br.portalsLock.Lock()
defer br.portalsLock.Unlock()
@@ -466,7 +480,7 @@ func (portal *Portal) CreateMatrixRoom(user *User, channel *discordgo.Channel) e
InitialState: initialState,
CreationContent: creationContent,
}
if !portal.shouldSetDMRoomMetadata() {
if !portal.shouldSetDMRoomMetadata() && !portal.FriendNick {
req.Name = ""
}
@@ -1854,11 +1868,13 @@ func (portal *Portal) UpdateName(meta *discordgo.Channel) bool {
GuildName: guildName,
NSFW: meta.NSFW,
Type: meta.Type,
})) || plainNameChanged
}), false) || plainNameChanged
}
func (portal *Portal) UpdateNameDirect(name string) bool {
if portal.Name == name && (portal.NameSet || portal.MXID == "" || !portal.shouldSetDMRoomMetadata()) {
func (portal *Portal) UpdateNameDirect(name string, isFriendNick bool) bool {
if portal.FriendNick && !isFriendNick {
return false
} else if portal.Name == name && (portal.NameSet || portal.MXID == "" || (!portal.shouldSetDMRoomMetadata() && !isFriendNick)) {
return false
}
portal.log.Debugfln("Updating name %q -> %q", portal.Name, name)
@@ -1869,7 +1885,7 @@ func (portal *Portal) UpdateNameDirect(name string) bool {
}
func (portal *Portal) updateRoomName() {
if portal.MXID != "" && portal.shouldSetDMRoomMetadata() {
if portal.MXID != "" && (portal.shouldSetDMRoomMetadata() || portal.FriendNick) {
_, err := portal.MainIntent().SetRoomName(portal.MXID, portal.Name)
if err != nil {
portal.log.Warnln("Failed to update room name:", err)
@@ -2073,7 +2089,13 @@ func (portal *Portal) UpdateInfo(source *User, meta *discordgo.Channel) *discord
if portal.OtherUserID != "" {
puppet := portal.bridge.GetPuppetByID(portal.OtherUserID)
changed = portal.UpdateAvatarFromPuppet(puppet) || changed
changed = portal.UpdateNameDirect(puppet.Name) || changed
if rel, ok := source.relationships[portal.OtherUserID]; ok && rel.Nickname != "" {
portal.FriendNick = true
changed = portal.UpdateNameDirect(rel.Nickname, true) || changed
} else {
portal.FriendNick = false
changed = portal.UpdateNameDirect(puppet.Name, false) || changed
}
}
case discordgo.ChannelTypeGroupDM:
changed = portal.UpdateGroupDMAvatar(meta.Icon) || changed

View File

@@ -205,7 +205,7 @@ func (puppet *Puppet) UpdateName(info *discordgo.User) bool {
puppet.log.Warn().Err(err).Msg("Failed to update displayname")
} else {
go puppet.updatePortalMeta(func(portal *Portal) {
if portal.UpdateNameDirect(puppet.Name) {
if portal.UpdateNameDirect(puppet.Name, false) {
portal.Update()
portal.UpdateBridgeInfo()
}

61
user.go
View File

@@ -62,6 +62,8 @@ type User struct {
pendingInteractionsLock sync.Mutex
nextDiscordUploadID atomic.Int32
relationships map[string]*discordgo.Relationship
}
func (user *User) GetRemoteID() string {
@@ -189,6 +191,8 @@ func (br *DiscordBridge) NewUser(dbUser *database.User) *User {
PermissionLevel: br.Config.Bridge.Permissions.Get(dbUser.MXID),
pendingInteractions: make(map[string]*WrappedCommandEvent),
relationships: make(map[string]*discordgo.Relationship),
}
user.nextDiscordUploadID.Store(rand.Int31n(100))
user.BridgeState = br.NewBridgeStateQueue(user)
@@ -581,6 +585,10 @@ func (user *User) Connect() error {
user.Session.AddHandler(user.channelPinsUpdateHandler)
user.Session.AddHandler(user.channelUpdateHandler)
user.Session.AddHandler(user.relationshipAddHandler)
user.Session.AddHandler(user.relationshipRemoveHandler)
user.Session.AddHandler(user.relationshipUpdateHandler)
user.Session.AddHandler(user.messageCreateHandler)
user.Session.AddHandler(user.messageDeleteHandler)
user.Session.AddHandler(user.messageUpdateHandler)
@@ -660,6 +668,10 @@ func (user *User) readyHandler(_ *discordgo.Session, r *discordgo.Ready) {
user.BridgeState.Send(status.BridgeState{StateEvent: status.StateBackfilling})
user.tryAutomaticDoublePuppeting()
for _, relationship := range r.Relationships {
user.relationships[relationship.ID] = relationship
}
updateTS := time.Now()
portalsInSpace := make(map[string]bool)
for _, guild := range user.GetPortals() {
@@ -738,6 +750,55 @@ func (user *User) addPrivateChannelToSpace(portal *Portal) bool {
}
}
func (user *User) relationshipAddHandler(_ *discordgo.Session, r *discordgo.RelationshipAdd) {
user.log.Debug().Interface("relationship", r.Relationship).Msg("Relationship added")
user.relationships[r.ID] = r.Relationship
user.handleRelationshipChange(r.ID, r.Nickname)
}
func (user *User) relationshipUpdateHandler(_ *discordgo.Session, r *discordgo.RelationshipUpdate) {
user.log.Debug().Interface("relationship", r.Relationship).Msg("Relationship update")
user.relationships[r.ID] = r.Relationship
user.handleRelationshipChange(r.ID, r.Nickname)
}
func (user *User) relationshipRemoveHandler(_ *discordgo.Session, r *discordgo.RelationshipRemove) {
user.log.Debug().Str("other_user_id", r.ID).Msg("Relationship removed")
delete(user.relationships, r.ID)
user.handleRelationshipChange(r.ID, "")
}
func (user *User) handleRelationshipChange(userID, nickname string) {
puppet := user.bridge.GetPuppetByID(userID)
portal := user.FindPrivateChatWith(userID)
if portal == nil || puppet == nil {
return
}
updated := portal.FriendNick == (nickname != "")
portal.FriendNick = nickname != ""
if nickname != "" {
updated = portal.UpdateNameDirect(nickname, true)
} else if portal.Name != puppet.Name {
if portal.shouldSetDMRoomMetadata() {
updated = portal.UpdateNameDirect(puppet.Name, false)
} else if portal.NameSet {
_, err := portal.MainIntent().SendStateEvent(portal.MXID, event.StateRoomName, "", map[string]any{})
if err != nil {
portal.zlog.Warn().Err(err).Msg("Failed to clear room name after friend nickname was removed")
} else {
portal.zlog.Debug().Msg("Cleared room name after friend nickname was removed")
portal.NameSet = false
portal.Update()
updated = true
}
}
}
if !updated {
portal.Update()
}
}
func (user *User) handlePrivateChannel(portal *Portal, meta *discordgo.Channel, timestamp time.Time, create, isInSpace bool) {
if create && portal.MXID == "" {
err := portal.CreateMatrixRoom(user, meta)