Thumbnail

rani/matterbridge.git

Clone URL: https://git.buni.party/rani/matterbridge.git

commit eb32d68a28087483a9530a82d508e68d31649d7d Author: selfhoster1312 <selfhoster1312@kl.netlib.re> Date: Sat Dec 06 20:50:08 2025 +0000 feat: Add new unified HTTP helpers diff --git a/bridge/bridge.go b/bridge/bridge.go index 3511693..0451ecb 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -16 +110 @@  package bridge    import ( + "bytes" + "errors" + "fmt" + "io"   "log"   "net/http"   "net/url" @@ -176 +218 @@ type Bridger interface {   Connect() error   JoinChannel(channel config.ChannelInfo) error   Disconnect() error + NewHttpRequest(method, uri string, body io.Reader) (*http.Request, error) + NewHttpClient(proxy string) (*http.Client, error)  }    type Bridge struct { @@ -1703 +176138 @@ func (b *Bridge) NewHttpClient(http_proxy string) (*http.Client, error) {   Timeout: time.Second * 5,   }, nil  } + +var errHttpGetNotOk = errors.New("HTTP server responded non-OK code") + +func HttpGetNotOkError(uri string, code int) error { + return fmt.Errorf("%w: %s returned code %d", errHttpGetNotOk, uri, code) +} + +func (b *Bridge) HttpGetBytes(uri string) (*[]byte, error) { + req, err := b.Bridger.NewHttpRequest("GET", uri, nil) + if err != nil { + return nil, err + } + + b.Log.Debugf("Getting HTTP bytes with request: %#v", req) + + resp, err := b.HttpClient.Do(req) + if err != nil { + return nil, err + } + + if resp.StatusCode != http.StatusOK { + return nil, HttpGetNotOkError(uri, resp.StatusCode) + } + + var buf bytes.Buffer + + _, err = io.Copy(&buf, resp.Body) + if err != nil { + return nil, err + } + + err = resp.Body.Close() + if err != nil { + return nil, err + } + + data := buf.Bytes() + + return &data, nil +} + +func (b *Bridge) AddAttachmentFromURL(msg *config.Message, filename string, id string, comment string, uri string) error { + return b.addAttachment(msg, filename, id, comment, uri, nil, false) +} + +func (b *Bridge) AddAttachmentFromBytes(msg *config.Message, filename string, id string, comment string, data *[]byte) error { + return b.addAttachment(msg, filename, id, comment, "", data, false) +} + +func (b *Bridge) AddAvatarFromURL(msg *config.Message, filename string, id string, comment string, uri string) error { + return b.addAttachment(msg, filename, id, comment, uri, nil, true) +} + +func (b *Bridge) AddAvatarFromBytes(msg *config.Message, filename string, id string, comment string, data *[]byte) error { + return b.addAttachment(msg, filename, id, comment, "", data, true) +} + +// NewHttpRequest produces a new http.Request instance with bridge-specific settings. +// +// This is used by bridges where HTTP downloads require a cookie/token, by overriding +// this method in the bridge struct. +func (b *Bridge) NewHttpRequest(method, uri string, body io.Reader) (*http.Request, error) { + return http.NewRequest(method, uri, body) +} + +// Internal method including common parts to attachment/avatar handling methods. +// +// This method will process received bytes. If bytes are not set, they will be downloaded from the given URL. +// If neither data bytes nor uri is provided, this will be a hard error because there's a logic error somewhere. +func (b *Bridge) addAttachment(msg *config.Message, filename string, id string, comment string, uri string, data *[]byte, avatar bool) error { + if data != nil { + return b.addAttachmentProcess(msg, filename, id, comment, uri, data, avatar) + } + + if uri == "" { + // This should never happen + b.Log.Fatalf("Logic error in bridge %s: attachment should have either URL or data set, neither was provided", b.Protocol) + } + + data, err := b.HttpGetBytes(uri) + if err != nil { + return err + } + + return b.addAttachmentProcess(msg, filename, id, comment, uri, data, avatar) +} + +type errFileTooLarge struct { + FileName string + Size int + MaxSize int +} + +func (e *errFileTooLarge) Error() string { + return fmt.Sprintf("File %#v to large to download (%#v). MediaDownloadSize is %#v", e.FileName, e.Size, e.MaxSize) +} + +type errFileBlacklisted struct { + FileName string +} + +func (e *errFileBlacklisted) Error() string { + return fmt.Sprintf("File %#v matches the backlist, not downloading it", e.FileName) +} + +func (b *Bridge) addAttachmentProcess(msg *config.Message, filename string, id string, comment string, uri string, data *[]byte, avatar bool) error { + size := len(*data) + if size > b.General.MediaDownloadSize { + return &errFileTooLarge{ + FileName: filename, + Size: size, + MaxSize: b.General.MediaDownloadSize, + } + } + + // Apply `MediaDownloadBlackList` regexes + if b.Config.IsFilenameBlacklisted(filename) { + return &errFileBlacklisted{ + FileName: filename, + } + } + + b.Log.Debugf("Download OK %#v %#v", filename, size) + msg.Extra["file"] = append(msg.Extra["file"], config.FileInfo{ + Name: filename, + Data: data, + URL: uri, + Comment: comment, + Avatar: avatar, + // TODO: if id is not set, maybe use hash of bytes? + NativeID: id, + }) + + return nil +}