attachments.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. package main
  2. import (
  3. "bytes"
  4. "fmt"
  5. "image"
  6. "io"
  7. "net/http"
  8. "strings"
  9. "time"
  10. "github.com/bwmarrin/discordgo"
  11. "maunium.net/go/mautrix"
  12. "maunium.net/go/mautrix/appservice"
  13. "maunium.net/go/mautrix/crypto/attachment"
  14. "maunium.net/go/mautrix/event"
  15. "go.mau.fi/mautrix-discord/database"
  16. )
  17. func downloadDiscordAttachment(url string) ([]byte, error) {
  18. req, err := http.NewRequest(http.MethodGet, url, nil)
  19. if err != nil {
  20. return nil, err
  21. }
  22. for key, value := range discordgo.DroidDownloadHeaders {
  23. req.Header.Set(key, value)
  24. }
  25. resp, err := http.DefaultClient.Do(req)
  26. if err != nil {
  27. return nil, err
  28. }
  29. defer resp.Body.Close()
  30. if resp.StatusCode > 300 {
  31. data, _ := io.ReadAll(resp.Body)
  32. return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, data)
  33. }
  34. return io.ReadAll(resp.Body)
  35. }
  36. func (portal *Portal) downloadMatrixAttachment(content *event.MessageEventContent) ([]byte, error) {
  37. var file *event.EncryptedFileInfo
  38. rawMXC := content.URL
  39. if content.File != nil {
  40. file = content.File
  41. rawMXC = file.URL
  42. }
  43. mxc, err := rawMXC.Parse()
  44. if err != nil {
  45. return nil, err
  46. }
  47. data, err := portal.MainIntent().DownloadBytes(mxc)
  48. if err != nil {
  49. return nil, err
  50. }
  51. if file != nil {
  52. err = file.DecryptInPlace(data)
  53. if err != nil {
  54. return nil, err
  55. }
  56. }
  57. return data, nil
  58. }
  59. func (br *DiscordBridge) uploadMatrixAttachment(intent *appservice.IntentAPI, data []byte, url string, encrypt bool, attachmentID, mime string) (*database.File, error) {
  60. dbFile := br.DB.File.New()
  61. dbFile.Timestamp = time.Now()
  62. dbFile.URL = url
  63. dbFile.ID = attachmentID
  64. dbFile.Size = len(data)
  65. if strings.HasPrefix(mime, "image/") {
  66. cfg, _, _ := image.DecodeConfig(bytes.NewReader(data))
  67. dbFile.Width = cfg.Width
  68. dbFile.Height = cfg.Height
  69. }
  70. uploadMime := mime
  71. if encrypt {
  72. dbFile.Encrypted = true
  73. dbFile.DecryptionInfo = attachment.NewEncryptedFile()
  74. dbFile.DecryptionInfo.EncryptInPlace(data)
  75. uploadMime = "application/octet-stream"
  76. }
  77. req := mautrix.ReqUploadMedia{
  78. ContentBytes: data,
  79. ContentType: uploadMime,
  80. }
  81. if br.Config.Homeserver.AsyncMedia {
  82. resp, err := intent.UnstableCreateMXC()
  83. if err != nil {
  84. return nil, err
  85. }
  86. dbFile.MXC = resp.ContentURI
  87. req.UnstableMXC = resp.ContentURI
  88. req.UploadURL = resp.UploadURL
  89. go func() {
  90. _, err = intent.UploadMedia(req)
  91. if err != nil {
  92. br.Log.Errorfln("Failed to upload %s: %v", req.UnstableMXC, err)
  93. dbFile.Delete()
  94. }
  95. }()
  96. } else {
  97. uploaded, err := intent.UploadMedia(req)
  98. if err != nil {
  99. return nil, err
  100. }
  101. dbFile.MXC = uploaded.ContentURI
  102. }
  103. dbFile.Insert(nil)
  104. return dbFile, nil
  105. }
  106. func (br *DiscordBridge) copyAttachmentToMatrix(intent *appservice.IntentAPI, url string, encrypt bool, attachmentID, mime string) (*database.File, error) {
  107. dbFile := br.DB.File.Get(url, encrypt)
  108. if dbFile == nil {
  109. data, err := downloadDiscordAttachment(url)
  110. if err != nil {
  111. return nil, err
  112. }
  113. dbFile, err = br.uploadMatrixAttachment(intent, data, url, encrypt, attachmentID, mime)
  114. if err != nil {
  115. return nil, err
  116. }
  117. }
  118. return dbFile, nil
  119. }