Skip to content

Commit

Permalink
capabilities: update to new format
Browse files Browse the repository at this point in the history
  • Loading branch information
tulir committed Jan 10, 2025
1 parent 523e0e3 commit 53c933c
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 48 deletions.
14 changes: 7 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ require (
github.com/slack-go/slack v0.15.0
github.com/stretchr/testify v1.10.0
github.com/yuin/goldmark v1.7.8
go.mau.fi/util v0.8.3
golang.org/x/net v0.32.0
go.mau.fi/util v0.8.4-0.20250110124612-64d4dbbec957
golang.org/x/net v0.33.0
gopkg.in/yaml.v3 v3.0.1
maunium.net/go/mautrix v0.22.2-0.20241223114659-ba210a16b992
maunium.net/go/mautrix v0.22.2-0.20250110154103-bbcb1904e268
)

require (
Expand All @@ -34,13 +34,13 @@ require (
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
go.mau.fi/zeroconfig v0.1.3 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
maunium.net/go/mauflag v1.0.0 // indirect
)

replace github.com/slack-go/slack => github.com/beeper/slackgo v0.0.0-20241223114722-b8b7a4c49a18
replace github.com/slack-go/slack => github.com/beeper/slackgo v0.0.0-20250110160232-abcc246721a4
28 changes: 14 additions & 14 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/beeper/slackgo v0.0.0-20241223114722-b8b7a4c49a18 h1:s0gVLeb7pgYBIlwuUmFzMvDbxVWEWUvjDJaqurE5LI4=
github.com/beeper/slackgo v0.0.0-20241223114722-b8b7a4c49a18/go.mod h1:axoegr/0xf8uWt4I+coY6x+CVKPbWGs4YqpoYbCBRr8=
github.com/beeper/slackgo v0.0.0-20250110160232-abcc246721a4 h1:2xOB+RTGKAbnbILUmuqh+bTg6a40mQjoa+P2s5I5CPc=
github.com/beeper/slackgo v0.0.0-20250110160232-abcc246721a4/go.mod h1:axoegr/0xf8uWt4I+coY6x+CVKPbWGs4YqpoYbCBRr8=
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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
Expand Down Expand Up @@ -51,23 +51,23 @@ github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
go.mau.fi/util v0.8.3 h1:sulhXtfquMrQjsOP67x9CzWVBYUwhYeoo8hNQIpCWZ4=
go.mau.fi/util v0.8.3/go.mod h1:c00Db8xog70JeIsEvhdHooylTkTkakgnAOsZ04hplQY=
go.mau.fi/util v0.8.4-0.20250110124612-64d4dbbec957 h1:tsLt3t6ARc55niz+JMgJy6U4sL210Z0K/nyxF09xT0E=
go.mau.fi/util v0.8.4-0.20250110124612-64d4dbbec957/go.mod h1:MOfGTs1CBuK6ERTcSL4lb5YU7/ujz09eOPVEDckuazY=
go.mau.fi/zeroconfig v0.1.3 h1:As9wYDKmktjmNZW5i1vn8zvJlmGKHeVxHVIBMXsm4kM=
go.mau.fi/zeroconfig v0.1.3/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e h1:4qufH0hlUYs6AO6XmZC3GqfDPGSXHVXUFR6OND+iJX4=
golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 h1:9kj3STMvgqy3YA4VQXBrN7925ICMxD5wzMRcgA30588=
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
Expand All @@ -78,5 +78,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
maunium.net/go/mautrix v0.22.2-0.20241223114659-ba210a16b992 h1:7KLNq84HGVzpb7iBCtKTq5NhjDWh/r3Bj8Qmtv5gXy4=
maunium.net/go/mautrix v0.22.2-0.20241223114659-ba210a16b992/go.mod h1:1rhqwH34Rz54ZqzdQYkmNW6rQUymNeTdaLA4l9LK6AI=
maunium.net/go/mautrix v0.22.2-0.20250110154103-bbcb1904e268 h1:p+3TofdhqiVYIkLjgzidayg2XriGUEbj+nbWs3/UQbk=
maunium.net/go/mautrix v0.22.2-0.20250110154103-bbcb1904e268/go.mod h1:07i96D7BALyuAqxFhRzvaId8FC9NABgRQBPY5HWndf4=
160 changes: 141 additions & 19 deletions pkg/connector/capabilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,16 @@ package connector

import (
"context"
"strconv"
"time"

"go.mau.fi/util/ffmpeg"
"go.mau.fi/util/jsontime"
"go.mau.fi/util/ptr"
"maunium.net/go/mautrix/bridgev2"
"maunium.net/go/mautrix/event"

"go.mau.fi/mautrix-slack/pkg/slackid"
)

func (s *SlackConnector) GetCapabilities() *bridgev2.NetworkGeneralCapabilities {
Expand All @@ -30,25 +38,139 @@ func (s *SlackConnector) GetCapabilities() *bridgev2.NetworkGeneralCapabilities
}
}

var roomCaps = &bridgev2.NetworkRoomCapabilities{
FormattedText: true,
UserMentions: true,
RoomMentions: true,
LocationMessages: false,
Captions: true,
MaxTextLength: 40000,
MaxCaptionLength: 40000,
Threads: true,
Replies: false,
Edits: true,
EditMaxAge: 0, // TODO workspaces can have edit max age limits
Deletes: true,
DefaultFileRestriction: &bridgev2.FileRestriction{MaxSize: 1 * 1000 * 1000 * 1000},
ReadReceipts: false,
Reactions: true,
ReactionCount: 0, // unlimited
func (s *SlackConnector) GetBridgeInfoVersion() (info, caps int) {
return 1, 1
}

func supportedIfFFmpeg() event.CapabilitySupportLevel {
if ffmpeg.Supported() {
return event.CapLevelPartialSupport
}
return event.CapLevelRejected
}

func capID() string {
base := "fi.mau.slack.capabilities.2025_01_10"
if ffmpeg.Supported() {
return base + "+ffmpeg"
}
return base
}

func (s *SlackClient) GetCapabilities(ctx context.Context, portal *bridgev2.Portal) *bridgev2.NetworkRoomCapabilities {
return roomCaps
const MaxFileSize = 1 * 1000 * 1000 * 1000
const MaxTextLength = 40000

var roomCaps = &event.RoomFeatures{
ID: capID(),
Formatting: event.FormattingFeatureMap{
event.FmtBold: event.CapLevelFullySupported,
event.FmtItalic: event.CapLevelFullySupported,
event.FmtStrikethrough: event.CapLevelFullySupported,
event.FmtInlineCode: event.CapLevelFullySupported,
event.FmtCodeBlock: event.CapLevelFullySupported,
event.FmtSyntaxHighlighting: event.CapLevelDropped,
event.FmtBlockquote: event.CapLevelFullySupported,
event.FmtInlineLink: event.CapLevelFullySupported,
event.FmtUserLink: event.CapLevelFullySupported,
event.FmtRoomLink: event.CapLevelFullySupported,
event.FmtEventLink: event.CapLevelUnsupported,
event.FmtAtRoomMention: event.CapLevelFullySupported,
event.FmtUnorderedList: event.CapLevelFullySupported,
event.FmtOrderedList: event.CapLevelFullySupported,
event.FmtListStart: event.CapLevelFullySupported,
event.FmtListJumpValue: event.CapLevelDropped,
event.FmtCustomEmoji: event.CapLevelFullySupported,
},
File: event.FileFeatureMap{
event.MsgImage: {
MimeTypes: map[string]event.CapabilitySupportLevel{
"image/jpeg": event.CapLevelFullySupported,
"image/png": event.CapLevelFullySupported,
"image/gif": event.CapLevelFullySupported,
"image/webp": event.CapLevelFullySupported,
},
Caption: event.CapLevelFullySupported,
MaxCaptionLength: MaxTextLength,
MaxSize: MaxFileSize,
},
event.MsgVideo: {
MimeTypes: map[string]event.CapabilitySupportLevel{
"video/mp4": event.CapLevelFullySupported,
"video/webm": event.CapLevelFullySupported,
},
Caption: event.CapLevelFullySupported,
MaxCaptionLength: MaxTextLength,
MaxSize: MaxFileSize,
},
event.MsgAudio: {
MimeTypes: map[string]event.CapabilitySupportLevel{
"audio/mp3": event.CapLevelFullySupported,
"audio/webm": event.CapLevelFullySupported,
"audio/wav": event.CapLevelFullySupported,
},
Caption: event.CapLevelFullySupported,
MaxCaptionLength: MaxTextLength,
MaxSize: MaxFileSize,
},
event.MsgFile: {
MimeTypes: map[string]event.CapabilitySupportLevel{
// TODO Slack Connect rejects some types
// https://slack.com/intl/en-gb/help/articles/1500002249342-Restricted-file-types-in-Slack-Connect
"*/*": event.CapLevelFullySupported,
},
Caption: event.CapLevelFullySupported,
MaxCaptionLength: MaxTextLength,
MaxSize: MaxFileSize,
},
event.CapMsgGIF: {
MimeTypes: map[string]event.CapabilitySupportLevel{
"image/gif": event.CapLevelFullySupported,
},
Caption: event.CapLevelFullySupported,
MaxCaptionLength: MaxTextLength,
MaxSize: MaxFileSize,
},
event.CapMsgVoice: {
MimeTypes: map[string]event.CapabilitySupportLevel{
"audio/ogg": supportedIfFFmpeg(),
"audio/webm; codecs=opus": event.CapLevelFullySupported,
},
Caption: event.CapLevelFullySupported,
MaxCaptionLength: MaxTextLength,
MaxSize: MaxFileSize,
MaxDuration: ptr.Ptr(jsontime.S(5 * time.Minute)),
},
},
LocationMessage: event.CapLevelRejected,
MaxTextLength: MaxTextLength,
Thread: event.CapLevelFullySupported,
Edit: event.CapLevelFullySupported,
EditMaxAge: nil,
Delete: event.CapLevelFullySupported,
Reaction: event.CapLevelFullySupported,
}

func (s *SlackClient) GetCapabilities(ctx context.Context, portal *bridgev2.Portal) *event.RoomFeatures {
meta := &slackid.PortalMetadata{}
topLevel := portal.GetTopLevelParent()
if topLevel != nil {
meta = topLevel.Metadata.(*slackid.PortalMetadata)
}
caps := roomCaps
if meta.EditMaxAge != nil && *meta.EditMaxAge >= 0 {
caps = ptr.Clone(roomCaps)
caps.ID += "+edit_max_age=" + strconv.Itoa(*meta.EditMaxAge)
caps.EditMaxAge = ptr.Ptr(jsontime.S(time.Duration(*meta.EditMaxAge) * time.Minute))
if *meta.EditMaxAge == 0 {
caps.Edit = event.CapLevelRejected
}
}
if meta.AllowDelete != nil && !*meta.AllowDelete {
if caps == roomCaps {
caps = ptr.Clone(roomCaps)
}
caps.ID += "+disallow_delete"
caps.Delete = event.CapLevelRejected
}
return caps
}
17 changes: 13 additions & 4 deletions pkg/connector/chatinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ func (s *SlackClient) wrapChatInfo(ctx context.Context, info *slack.Channel, isN
if roomType != database.RoomTypeDM || len(members.MemberMap) == 1 {
name = ptr.Ptr(s.Main.Config.FormatChannelName(&ChannelNameParams{
Channel: info,
Team: &s.BootResp.Team,
Team: &s.BootResp.Team.TeamInfo,
IsNoteToSelf: info.IsIM && info.User == s.UserID,
}))
}
Expand Down Expand Up @@ -242,7 +242,7 @@ func (s *SlackClient) fetchChatInfo(ctx context.Context, channelID string, isNew
}

func (s *SlackClient) getTeamInfo() *bridgev2.ChatInfo {
name := s.Main.Config.FormatTeamName(&s.BootResp.Team)
name := s.Main.Config.FormatTeamName(&s.BootResp.Team.TeamInfo)
avatarURL, _ := s.BootResp.Team.Icon["image_230"].(string)
if s.BootResp.Team.Icon["image_default"] == true {
avatarURL = ""
Expand All @@ -265,6 +265,15 @@ func (s *SlackClient) getTeamInfo() *bridgev2.ChatInfo {
meta.TeamDomain = s.BootResp.Team.Domain
changed = true
}
prefs := s.BootResp.Team.Prefs
if prefs.MsgEditWindowMins != nil && (meta.EditMaxAge == nil || *meta.EditMaxAge != *prefs.MsgEditWindowMins) {
meta.EditMaxAge = prefs.MsgEditWindowMins
changed = true
}
if prefs.AllowMessageDeletion != nil && (meta.AllowDelete == nil || *meta.AllowDelete != *prefs.AllowMessageDeletion) {
meta.AllowDelete = prefs.AllowMessageDeletion
changed = true
}
return
},
}
Expand Down Expand Up @@ -303,7 +312,7 @@ func (s *SlackClient) wrapUserInfo(userID string, info *slack.User, botInfo *sla
if info != nil {
name = ptr.Ptr(s.Main.Config.FormatDisplayname(&DisplaynameParams{
User: info,
Team: &s.BootResp.Team,
Team: &s.BootResp.Team.TeamInfo,
}))
avatarURL := info.Profile.ImageOriginal
if avatarURL == "" && info.Profile.Image512 != "" {
Expand All @@ -325,7 +334,7 @@ func (s *SlackClient) wrapUserInfo(userID string, info *slack.User, botInfo *sla
}
isBot = isBot || info.IsBot || info.IsAppUser
} else if botInfo != nil {
name = ptr.Ptr(s.Main.Config.FormatBotDisplayname(botInfo, &s.BootResp.Team))
name = ptr.Ptr(s.Main.Config.FormatBotDisplayname(botInfo, &s.BootResp.Team.TeamInfo))
avatar = makeAvatar(botInfo.Icons.Image72, botInfo.Icons.Image72)
isBot = true
}
Expand Down
4 changes: 3 additions & 1 deletion pkg/connector/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,9 @@ func (s *SlackClient) Connect(ctx context.Context) {
}
bootResp = &slack.ClientUserBootResponse{
Self: *userResp,
Team: *teamResp,
Team: slack.BootTeam{
TeamInfo: *teamResp,
},
}
}
err := s.connect(ctx, bootResp)
Expand Down
6 changes: 4 additions & 2 deletions pkg/msgconv/from-matrix.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,14 +143,16 @@ func (mc *MessageConverter) ToSlack(
caption = content.Body
captionHTML = content.FormattedBody
}
if content.MSC3245Voice != nil && ffmpeg.Supported() {
if content.MSC3245Voice != nil && content.Info.MimeType != "audio/webm; codecs=opus" && ffmpeg.Supported() {
data, err = ffmpeg.ConvertBytes(ctx, data, ".webm", []string{}, []string{"-c:a", "copy"}, content.Info.MimeType)
if err != nil {
log.Err(err).Msg("Failed to convert voice message")
return nil, ErrMediaConvertFailed
}
filename += ".webm"
content.Info.MimeType = "audio/webm;codecs=opus"
content.Info.MimeType = "audio/webm; codecs=opus"
subtype = "slack_audio"
} else if content.MSC3245Voice != nil && content.Info.MimeType == "audio/webm; codecs=opus" {
subtype = "slack_audio"
}
_, channelID := slackid.ParsePortalID(portal.ID)
Expand Down
4 changes: 3 additions & 1 deletion pkg/slackid/dbmeta.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ import (

type PortalMetadata struct {
// Only present for team portals, not channels
TeamDomain string `json:"team_domain"`
TeamDomain string `json:"team_domain,omitempty"`
EditMaxAge *int `json:"edit_max_age,omitempty"`
AllowDelete *bool `json:"allow_delete,omitempty"`
}

type GhostMetadata struct {
Expand Down

0 comments on commit 53c933c

Please sign in to comment.