guild.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. package database
  2. import (
  3. "database/sql"
  4. "errors"
  5. "fmt"
  6. "strings"
  7. log "maunium.net/go/maulogger/v2"
  8. "maunium.net/go/mautrix/id"
  9. "maunium.net/go/mautrix/util/dbutil"
  10. )
  11. type GuildBridgingMode int
  12. const (
  13. // GuildBridgeNothing tells the bridge to never bridge messages, not even checking if a portal exists.
  14. GuildBridgeNothing GuildBridgingMode = iota
  15. // GuildBridgeIfPortalExists tells the bridge to bridge messages in channels that already have portals.
  16. GuildBridgeIfPortalExists
  17. // GuildBridgeCreateOnMessage tells the bridge to create portals as soon as a message is received.
  18. GuildBridgeCreateOnMessage
  19. // GuildBridgeEverything tells the bridge to proactively create portals on startup and when receiving channel create notifications.
  20. GuildBridgeEverything
  21. GuildBridgeInvalid GuildBridgingMode = -1
  22. )
  23. func ParseGuildBridgingMode(str string) GuildBridgingMode {
  24. str = strings.ToLower(str)
  25. str = strings.ReplaceAll(str, "-", "")
  26. str = strings.ReplaceAll(str, "_", "")
  27. switch str {
  28. case "nothing", "0":
  29. return GuildBridgeNothing
  30. case "ifportalexists", "1":
  31. return GuildBridgeIfPortalExists
  32. case "createonmessage", "2":
  33. return GuildBridgeCreateOnMessage
  34. case "everything", "3":
  35. return GuildBridgeEverything
  36. default:
  37. return GuildBridgeInvalid
  38. }
  39. }
  40. func (gbm GuildBridgingMode) String() string {
  41. switch gbm {
  42. case GuildBridgeNothing:
  43. return "nothing"
  44. case GuildBridgeIfPortalExists:
  45. return "if-portal-exists"
  46. case GuildBridgeCreateOnMessage:
  47. return "create-on-message"
  48. case GuildBridgeEverything:
  49. return "everything"
  50. default:
  51. return ""
  52. }
  53. }
  54. func (gbm GuildBridgingMode) Description() string {
  55. switch gbm {
  56. case GuildBridgeNothing:
  57. return "never bridge messages"
  58. case GuildBridgeIfPortalExists:
  59. return "bridge messages in existing portals"
  60. case GuildBridgeCreateOnMessage:
  61. return "bridge all messages and create portals on first message"
  62. case GuildBridgeEverything:
  63. return "bridge all messages and create portals proactively"
  64. default:
  65. return ""
  66. }
  67. }
  68. type GuildQuery struct {
  69. db *Database
  70. log log.Logger
  71. }
  72. const (
  73. guildSelect = "SELECT dcid, mxid, plain_name, name, name_set, avatar, avatar_url, avatar_set, bridging_mode FROM guild"
  74. )
  75. func (gq *GuildQuery) New() *Guild {
  76. return &Guild{
  77. db: gq.db,
  78. log: gq.log,
  79. }
  80. }
  81. func (gq *GuildQuery) GetByID(dcid string) *Guild {
  82. query := guildSelect + " WHERE dcid=$1"
  83. return gq.New().Scan(gq.db.QueryRow(query, dcid))
  84. }
  85. func (gq *GuildQuery) GetByMXID(mxid id.RoomID) *Guild {
  86. query := guildSelect + " WHERE mxid=$1"
  87. return gq.New().Scan(gq.db.QueryRow(query, mxid))
  88. }
  89. func (gq *GuildQuery) GetAll() []*Guild {
  90. rows, err := gq.db.Query(guildSelect)
  91. if err != nil {
  92. gq.log.Errorln("Failed to query guilds:", err)
  93. return nil
  94. }
  95. var guilds []*Guild
  96. for rows.Next() {
  97. guild := gq.New().Scan(rows)
  98. if guild != nil {
  99. guilds = append(guilds, guild)
  100. }
  101. }
  102. return guilds
  103. }
  104. type Guild struct {
  105. db *Database
  106. log log.Logger
  107. ID string
  108. MXID id.RoomID
  109. PlainName string
  110. Name string
  111. NameSet bool
  112. Avatar string
  113. AvatarURL id.ContentURI
  114. AvatarSet bool
  115. BridgingMode GuildBridgingMode
  116. }
  117. func (g *Guild) Scan(row dbutil.Scannable) *Guild {
  118. var mxid sql.NullString
  119. var avatarURL string
  120. err := row.Scan(&g.ID, &mxid, &g.PlainName, &g.Name, &g.NameSet, &g.Avatar, &avatarURL, &g.AvatarSet, &g.BridgingMode)
  121. if err != nil {
  122. if !errors.Is(err, sql.ErrNoRows) {
  123. g.log.Errorln("Database scan failed:", err)
  124. panic(err)
  125. }
  126. return nil
  127. }
  128. if g.BridgingMode < GuildBridgeNothing || g.BridgingMode > GuildBridgeEverything {
  129. panic(fmt.Errorf("invalid guild bridging mode %d in guild %s", g.BridgingMode, g.ID))
  130. }
  131. g.MXID = id.RoomID(mxid.String)
  132. g.AvatarURL, _ = id.ParseContentURI(avatarURL)
  133. return g
  134. }
  135. func (g *Guild) mxidPtr() *id.RoomID {
  136. if g.MXID != "" {
  137. return &g.MXID
  138. }
  139. return nil
  140. }
  141. func (g *Guild) Insert() {
  142. query := `
  143. INSERT INTO guild (dcid, mxid, plain_name, name, name_set, avatar, avatar_url, avatar_set, bridging_mode)
  144. VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
  145. `
  146. _, err := g.db.Exec(query, g.ID, g.mxidPtr(), g.PlainName, g.Name, g.NameSet, g.Avatar, g.AvatarURL.String(), g.AvatarSet, g.BridgingMode)
  147. if err != nil {
  148. g.log.Warnfln("Failed to insert %s: %v", g.ID, err)
  149. panic(err)
  150. }
  151. }
  152. func (g *Guild) Update() {
  153. query := `
  154. UPDATE guild SET mxid=$1, plain_name=$2, name=$3, name_set=$4, avatar=$5, avatar_url=$6, avatar_set=$7, bridging_mode=$8
  155. WHERE dcid=$9
  156. `
  157. _, err := g.db.Exec(query, g.mxidPtr(), g.PlainName, g.Name, g.NameSet, g.Avatar, g.AvatarURL.String(), g.AvatarSet, g.BridgingMode, g.ID)
  158. if err != nil {
  159. g.log.Warnfln("Failed to update %s: %v", g.ID, err)
  160. panic(err)
  161. }
  162. }
  163. func (g *Guild) Delete() {
  164. _, err := g.db.Exec("DELETE FROM guild WHERE dcid=$1", g.ID)
  165. if err != nil {
  166. g.log.Warnfln("Failed to delete %s: %v", g.ID, err)
  167. panic(err)
  168. }
  169. }