diff --git a/go.mod b/go.mod index 66a0fab..5d76c24 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.14 require ( github.com/alecthomas/chroma v0.7.3 github.com/disintegration/imaging v1.6.2 + github.com/gabriel-vasile/mimetype v1.1.1 github.com/kyokomi/emoji v2.2.2+incompatible github.com/lithammer/fuzzysearch v1.1.0 github.com/lucasb-eyer/go-colorful v1.0.3 diff --git a/go.sum b/go.sum index 78ec8c3..8d66968 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,8 @@ github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1 github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/gabriel-vasile/mimetype v1.1.1 h1:qbN9MPuRf3bstHu9zkI9jDWNfH//9+9kHxr9oRBBBOA= +github.com/gabriel-vasile/mimetype v1.1.1/go.mod h1:6CDPel/o/3/s4+bp6kIbsWATq8pmgOisOPG40CJa6To= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= diff --git a/interface/matrix.go b/interface/matrix.go index 8a94d0f..792a7c6 100644 --- a/interface/matrix.go +++ b/interface/matrix.go @@ -60,6 +60,8 @@ type MatrixContainer interface { GetRoom(roomID id.RoomID) *rooms.Room GetOrCreateRoom(roomID id.RoomID) *rooms.Room + SendImage(roomID id.RoomID, body string, url id.ContentURI) + UploadMedia(data mautrix.ReqUploadMedia) (*id.ContentURI, error) Download(uri id.ContentURI, file *attachment.EncryptedFile) ([]byte, error) DownloadToDisk(uri id.ContentURI, file *attachment.EncryptedFile, target string) (string, error) GetDownloadURL(uri id.ContentURI) string diff --git a/matrix/matrix.go b/matrix/matrix.go index f7f308b..cea0769 100644 --- a/matrix/matrix.go +++ b/matrix/matrix.go @@ -44,7 +44,7 @@ import ( "maunium.net/go/gomuks/config" "maunium.net/go/gomuks/debug" - "maunium.net/go/gomuks/interface" + ifc "maunium.net/go/gomuks/interface" "maunium.net/go/gomuks/lib/open" "maunium.net/go/gomuks/matrix/muksevt" "maunium.net/go/gomuks/matrix/rooms" @@ -905,6 +905,20 @@ func (c *Container) SendEvent(evt *muksevt.Event) (id.EventID, error) { return resp.EventID, nil } +func (c *Container) SendImage(roomID id.RoomID, body string, url id.ContentURI) { + +} + +func (c *Container) UploadMedia(data mautrix.ReqUploadMedia) (*id.ContentURI, error) { + resp, err := c.client.UploadMedia(data) + + if err != nil { + return nil, err + } + + return &resp.ContentURI, nil +} + func (c *Container) sendTypingAsync(roomID id.RoomID, typing bool, timeout int64) { defer debug.Recover() _, _ = c.client.UserTyping(roomID, typing, timeout) diff --git a/ui/command-processor.go b/ui/command-processor.go index b33e88f..5121c74 100644 --- a/ui/command-processor.go +++ b/ui/command-processor.go @@ -110,6 +110,7 @@ func NewCommandProcessor(parent *MainView) *CommandProcessor { "device": autocompleteDevice, "verify": autocompleteDevice, "unverify": autocompleteDevice, + "upload": autocompleteFile, "import": autocompleteFile, "export": autocompleteFile, "export-room": autocompleteFile, @@ -138,6 +139,7 @@ func NewCommandProcessor(parent *MainView) *CommandProcessor { "react": cmdReact, "edit": cmdEdit, "download": cmdDownload, + "upload": cmdUpload, "open": cmdOpen, "copy": cmdCopy, "sendevent": cmdSendEvent, diff --git a/ui/commands.go b/ui/commands.go index 1fe27f3..9cdec4c 100644 --- a/ui/commands.go +++ b/ui/commands.go @@ -20,8 +20,10 @@ import ( "encoding/json" "fmt" "io" + "io/ioutil" "math" "os" + "path/filepath" "regexp" "runtime" dbg "runtime/debug" @@ -32,6 +34,7 @@ import ( "time" "unicode" + "github.com/gabriel-vasile/mimetype" "github.com/lucasb-eyer/go-colorful" "github.com/pkg/errors" "github.com/russross/blackfriday/v2" @@ -176,10 +179,82 @@ func cmdRedact(cmd *Command) { cmd.Room.StartSelecting(SelectRedact, strings.Join(cmd.Args, " ")) } +func autocompleteFile(cmd *CommandAutocomplete) (completions []string, newText string) { + inputPath, err := filepath.Abs(cmd.RawArgs) + if err != nil { + return + } + + var searchNamePrefix, searchDir string + if strings.HasSuffix(cmd.RawArgs, "/") { + searchDir = inputPath + } else { + searchNamePrefix = filepath.Base(inputPath) + searchDir = filepath.Dir(inputPath) + } + files, err := ioutil.ReadDir(searchDir) + if err != nil { + return + } + for _, file := range files { + name := file.Name() + if !strings.HasPrefix(name, searchNamePrefix) || (name[0] == '.' && searchNamePrefix == "") { + continue + } + fullPath := filepath.Join(searchDir, name) + if file.IsDir() { + fullPath += "/" + } + completions = append(completions, fullPath) + } + if len(completions) == 1 { + newText = fmt.Sprintf("/%s %s", cmd.OrigCommand, completions[0]) + } + return +} + func cmdDownload(cmd *Command) { cmd.Room.StartSelecting(SelectDownload, strings.Join(cmd.Args, " ")) } +func cmdUpload(cmd *Command) { + if len(cmd.Args) == 0 { + cmd.Reply("Usage: /upload ") + return + } + + path, err := filepath.Abs(cmd.RawArgs) + if err != nil { + cmd.Reply("Failed to get absolute path: %v", err) + return + } + + ftype, err := mimetype.DetectFile(path) + if err != nil { + cmd.Reply("Failed to get conetnt type: %v", err) + return + } + + fi, err := os.Stat(path) + if err != nil { + cmd.Reply("Failed to stat file: %v", err) + return + } + + reader, err := os.Open(path) + if err != nil { + cmd.Reply("Failed to open file: %v", err) + return + } + + cmd.Room.UploadMedia(mautrix.ReqUploadMedia{ + reader, + fi.Size(), + ftype.String(), + filepath.Base(cmd.RawArgs), + }) +} + func cmdOpen(cmd *Command) { cmd.Room.StartSelecting(SelectOpen, strings.Join(cmd.Args, " ")) } diff --git a/ui/crypto-commands.go b/ui/crypto-commands.go index 8a9fbb1..6c8896f 100644 --- a/ui/crypto-commands.go +++ b/ui/crypto-commands.go @@ -232,40 +232,6 @@ func cmdResetSession(cmd *Command) { } } -func autocompleteFile(cmd *CommandAutocomplete) (completions []string, newText string) { - inputPath, err := filepath.Abs(cmd.RawArgs) - if err != nil { - return - } - - var searchNamePrefix, searchDir string - if strings.HasSuffix(cmd.RawArgs, "/") { - searchDir = inputPath - } else { - searchNamePrefix = filepath.Base(inputPath) - searchDir = filepath.Dir(inputPath) - } - files, err := ioutil.ReadDir(searchDir) - if err != nil { - return - } - for _, file := range files { - name := file.Name() - if !strings.HasPrefix(name, searchNamePrefix) || (name[0] == '.' && searchNamePrefix == "") { - continue - } - fullPath := filepath.Join(searchDir, name) - if file.IsDir() { - fullPath += "/" - } - completions = append(completions, fullPath) - } - if len(completions) == 1 { - newText = fmt.Sprintf("/%s %s", cmd.OrigCommand, completions[0]) - } - return -} - func cmdImportKeys(cmd *Command) { path, err := filepath.Abs(cmd.RawArgs) if err != nil { diff --git a/ui/no-crypto-commands.go b/ui/no-crypto-commands.go index 781eb9f..2d1529d 100644 --- a/ui/no-crypto-commands.go +++ b/ui/no-crypto-commands.go @@ -22,10 +22,6 @@ func autocompleteDevice(cmd *CommandAutocomplete) ([]string, string) { return []string{}, "" } -func autocompleteFile(cmd *CommandAutocomplete) ([]string, string) { - return []string{}, "" -} - func cmdNoCrypto(cmd *Command) { cmd.Reply("This gomuks was built without encryption support") } diff --git a/ui/room-view.go b/ui/room-view.go index 0c06166..6bd2b6c 100644 --- a/ui/room-view.go +++ b/ui/room-view.go @@ -40,7 +40,7 @@ import ( "maunium.net/go/gomuks/config" "maunium.net/go/gomuks/debug" - "maunium.net/go/gomuks/interface" + ifc "maunium.net/go/gomuks/interface" "maunium.net/go/gomuks/lib/open" "maunium.net/go/gomuks/matrix/muksevt" "maunium.net/go/gomuks/matrix/rooms" @@ -606,8 +606,8 @@ func findWordToTabComplete(text string) string { } var ( - mentionMarkdown = "[%[1]s](https://matrix.to/#/%[2]s)" - mentionHTML = `%[1]s` + mentionMarkdown = "[%[1]s](https://matrix.to/#/%[2]s)" + mentionHTML = `%[1]s` mentionPlaintext = "%[1]s" ) @@ -806,6 +806,20 @@ func (view *RoomView) SendMessageHTML(msgtype event.MessageType, text, html stri } } +func (view *RoomView) SendImage(body string, url id.ContentURI) { + // evt := view.parent.matrix.SendImage(view.Room.ID, body, url) +} + +func (view *RoomView) UploadMedia(data mautrix.ReqUploadMedia) { + contentUri, err := view.parent.matrix.UploadMedia(data) + if err != nil { + view.AddServiceMessage(fmt.Sprintf("Failed to upload file: %v", err)) + return + } + + view.AddServiceMessage(fmt.Sprintf("ContentURI: %v", contentUri)) +} + func (view *RoomView) MessageView() *MessageView { return view.content }