bridge.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. package config
  2. import (
  3. "fmt"
  4. "strings"
  5. "text/template"
  6. "maunium.net/go/mautrix/id"
  7. "github.com/bwmarrin/discordgo"
  8. )
  9. type bridge struct {
  10. UsernameTemplate string `yaml:"username_template"`
  11. DisplaynameTemplate string `yaml:"displayname_template"`
  12. ChannelnameTemplate string `yaml:"channelname_template"`
  13. CommandPrefix string `yaml:"command_prefix"`
  14. ManagementRoomText managementRoomText `yaml:"management_root_text"`
  15. PortalMessageBuffer int `yaml:"portal_message_buffer"`
  16. SyncWithCustomPuppets bool `yaml:"sync_with_custom_puppets"`
  17. SyncDirectChatList bool `yaml:"sync_direct_chat_list"`
  18. DefaultBridgeReceipts bool `yaml:"default_bridge_receipts"`
  19. DefaultBridgePresence bool `yaml:"default_bridge_presence"`
  20. DoublePuppetServerMap map[string]string `yaml:"double_puppet_server_map"`
  21. DoublePuppetAllowDiscovery bool `yaml:"double_puppet_allow_discovery"`
  22. LoginSharedSecretMap map[string]string `yaml:"login_shared_secret_map"`
  23. usernameTemplate *template.Template `yaml:"-"`
  24. displaynameTemplate *template.Template `yaml:"-"`
  25. channelnameTemplate *template.Template `yaml:"-"`
  26. }
  27. func (config *Config) CanAutoDoublePuppet(userID id.UserID) bool {
  28. _, homeserver, _ := userID.Parse()
  29. _, hasSecret := config.Bridge.LoginSharedSecretMap[homeserver]
  30. return hasSecret
  31. }
  32. func (b *bridge) validate() error {
  33. var err error
  34. if b.UsernameTemplate == "" {
  35. b.UsernameTemplate = "discord_{{.}}"
  36. }
  37. b.usernameTemplate, err = template.New("username").Parse(b.UsernameTemplate)
  38. if err != nil {
  39. return err
  40. }
  41. if b.DisplaynameTemplate == "" {
  42. b.DisplaynameTemplate = "{{.Username}}#{{.Discriminator}} (D){{if .Bot}} (bot){{end}}"
  43. }
  44. b.displaynameTemplate, err = template.New("displayname").Parse(b.DisplaynameTemplate)
  45. if err != nil {
  46. return err
  47. }
  48. if b.ChannelnameTemplate == "" {
  49. b.ChannelnameTemplate = "{{if .Guild}}{{.Guild}} - {{end}}{{if .Folder}}{{.Folder}} - {{end}}{{.Name}} (D)"
  50. }
  51. b.channelnameTemplate, err = template.New("channelname").Parse(b.ChannelnameTemplate)
  52. if err != nil {
  53. return err
  54. }
  55. if b.PortalMessageBuffer <= 0 {
  56. b.PortalMessageBuffer = 128
  57. }
  58. if b.CommandPrefix == "" {
  59. b.CommandPrefix = "!dis"
  60. }
  61. if err := b.ManagementRoomText.validate(); err != nil {
  62. return err
  63. }
  64. return nil
  65. }
  66. func (b *bridge) UnmarshalYAML(unmarshal func(interface{}) error) error {
  67. type rawBridge bridge
  68. // Set our defaults that aren't zero values.
  69. raw := rawBridge{
  70. SyncWithCustomPuppets: true,
  71. DefaultBridgeReceipts: true,
  72. DefaultBridgePresence: true,
  73. }
  74. err := unmarshal(&raw)
  75. if err != nil {
  76. return err
  77. }
  78. *b = bridge(raw)
  79. return b.validate()
  80. }
  81. func (b bridge) FormatUsername(userid string) string {
  82. var buffer strings.Builder
  83. b.usernameTemplate.Execute(&buffer, userid)
  84. return buffer.String()
  85. }
  86. type simplfiedUser struct {
  87. Username string
  88. Discriminator string
  89. Locale string
  90. Verified bool
  91. MFAEnabled bool
  92. Bot bool
  93. System bool
  94. }
  95. func (b bridge) FormatDisplayname(user *discordgo.User) string {
  96. var buffer strings.Builder
  97. b.displaynameTemplate.Execute(&buffer, simplfiedUser{
  98. Username: user.Username,
  99. Discriminator: user.Discriminator,
  100. Locale: user.Locale,
  101. Verified: user.Verified,
  102. MFAEnabled: user.MFAEnabled,
  103. Bot: user.Bot,
  104. System: user.System,
  105. })
  106. return buffer.String()
  107. }
  108. type simplfiedChannel struct {
  109. Guild string
  110. Folder string
  111. Name string
  112. NSFW bool
  113. }
  114. func (b bridge) FormatChannelname(channel *discordgo.Channel, session *discordgo.Session) (string, error) {
  115. var buffer strings.Builder
  116. var guildName, folderName string
  117. if channel.Type != discordgo.ChannelTypeDM && channel.Type != discordgo.ChannelTypeGroupDM {
  118. guild, err := session.Guild(channel.GuildID)
  119. if err != nil {
  120. return "", fmt.Errorf("find guild: %w", err)
  121. }
  122. guildName = guild.Name
  123. folder, err := session.Channel(channel.ParentID)
  124. if err == nil {
  125. folderName = folder.Name
  126. }
  127. } else {
  128. // Group DM's can have a name, but DM's can't, so if we didn't get a
  129. // name return a comma separated list of the formatted user names.
  130. if channel.Name == "" {
  131. recipients := make([]string, len(channel.Recipients))
  132. for idx, user := range channel.Recipients {
  133. recipients[idx] = b.FormatDisplayname(user)
  134. }
  135. return strings.Join(recipients, ", "), nil
  136. }
  137. }
  138. b.channelnameTemplate.Execute(&buffer, simplfiedChannel{
  139. Guild: guildName,
  140. Folder: folderName,
  141. Name: channel.Name,
  142. NSFW: channel.NSFW,
  143. })
  144. return buffer.String(), nil
  145. }