bridge.go 4.5 KB

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