From 7ef0f71b7b4326cbb9b20f21cb94c3fac0051df2 Mon Sep 17 00:00:00 2001 From: KodiCraft <45794386+KodiCraft@users.noreply.github.com> Date: Sun, 13 Mar 2022 19:22:10 +0100 Subject: [PATCH] feat: implement keybindings for opening and downloading attachments (#137) * Added support for opening or downloading attachments * Removed some debug statements * Added close statements I forgot * Added gitignore to ignore built executables * Removed executables I accidentally added * Fixed opening files not working on Windows * Updated .gitignore to ignore .vscode Co-authored-by: ayntgl <90600421+ayntgl@users.noreply.github.com> * Deleted launch.json that doesn't need to be included * Implemented suggestions on #137 Co-authored-by: ayntgl <90600421+ayntgl@users.noreply.github.com> --- .gitignore | 4 +++ config/general.go | 41 +++++++++++++++++----- config/keybindings.go | 8 +++++ ui/messages.go | 81 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 9 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ca87ce3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +discordo* + +# Visual Studio Code +.vscode/ \ No newline at end of file diff --git a/config/general.go b/config/general.go index 7074392..718adff 100644 --- a/config/general.go +++ b/config/general.go @@ -1,27 +1,50 @@ package config +import ( + "os" + "runtime" +) + type IdentifyConfig struct { Os string `toml:"os"` Browser string `toml:"browser"` } type GeneralConfig struct { - UserAgent string `toml:"user_agent"` - FetchMessagesLimit int `toml:"fetch_messages_limit"` - Mouse bool `toml:"mouse"` - Timestamps bool `toml:"timestamps"` - Identify IdentifyConfig `toml:"identify"` + UserAgent string `toml:"user_agent"` + FetchMessagesLimit int `toml:"fetch_messages_limit"` + Mouse bool `toml:"mouse"` + Timestamps bool `toml:"timestamps"` + Identify IdentifyConfig `toml:"identify"` + AttachmentDownloadsDir string `toml:"attachment_downloads_dir"` } func newGeneralConfig() GeneralConfig { + return GeneralConfig{ - UserAgent: "Mozilla/5.0 (X11; Linux x86_64; rv:97.0) Gecko/20100101 Firefox/97.0", - FetchMessagesLimit: 50, - Mouse: true, - Timestamps: false, + UserAgent: "Mozilla/5.0 (X11; Linux x86_64; rv:97.0) Gecko/20100101 Firefox/97.0", + FetchMessagesLimit: 50, + Mouse: true, + Timestamps: false, + AttachmentDownloadsDir: UserDownloadsDir(), Identify: IdentifyConfig{ Os: "Linux", Browser: "Firefox", }, } } + +func UserDownloadsDir() string { + // We try to set the download folder location to the default Downloads folder + var dlloc string + if runtime.GOOS == "windows" { + h, _ := os.UserHomeDir() + dlloc = h + "\\Downloads" + } else if runtime.GOOS == "linux" || runtime.GOOS == "darwin" { + h, _ := os.UserHomeDir() + dlloc = h + "/Downloads" + } else { + dlloc = os.TempDir() // Very lame fallback, I know + } + return dlloc +} diff --git a/config/keybindings.go b/config/keybindings.go index d9715ec..3633055 100644 --- a/config/keybindings.go +++ b/config/keybindings.go @@ -7,12 +7,17 @@ type KeybindingsConfig struct { ToggleMessageInputField string `toml:"toggle_message_input_field"` OpenMessageActionsList string `toml:"open_message_actions_list"` + OpenExternalEditor string `toml:"open_external_editor"` + OpenAttachment string `toml:"open_attachment"` + DownloadAttachment string `toml:"download_attachment"` + SelectPreviousMessage string `toml:"select_previous_message"` SelectNextMessage string `toml:"select_next_message"` SelectFirstMessage string `toml:"select_first_message"` SelectLastMessage string `toml:"select_last_message"` + } func newKeybindingsConfig() KeybindingsConfig { @@ -25,6 +30,9 @@ func newKeybindingsConfig() KeybindingsConfig { OpenMessageActionsList: "Rune[a]", OpenExternalEditor: "Ctrl+E", + OpenAttachment: "Rune[o]", + DownloadAttachment: "Rune[d]", + SelectPreviousMessage: "Up", SelectNextMessage: "Down", SelectFirstMessage: "Home", diff --git a/ui/messages.go b/ui/messages.go index c45815f..7cb2f0e 100644 --- a/ui/messages.go +++ b/ui/messages.go @@ -2,8 +2,12 @@ package ui import ( "io" + "io/ioutil" + "net/http" "os" "os/exec" + "path/filepath" + "runtime" "strings" "github.com/atotto/clipboard" @@ -91,6 +95,83 @@ func (mtv *MessagesTextView) onInputCapture(e *tcell.EventKey) *tcell.EventKey { ScrollToHighlight() return nil + + case mtv.app.Config.Keybindings.OpenAttachment: + if ms[mtv.app.SelectedMessage].Attachments != nil { + for _, a := range ms[mtv.app.SelectedMessage].Attachments { + // We are caching the files, but files with the same name can still exist, so it ultimately does not matter + t, _ := os.UserCacheDir() + f, err := os.Create(filepath.Join(t, a.Filename)) + if err != nil { + f.Close() + return nil + } + response, err := http.Get(a.URL) + if err != nil { + f.Close() + return nil + } + + d, err := ioutil.ReadAll(response.Body) + if err != nil { + f.Close() + return nil + } + f.Write(d) + f.Close() + if runtime.GOOS == "windows" { + // On windows, `start` can do all the heavy lifting through `cmd` + cmd := exec.Command("cmd", "/C start "+f.Name()) + err := cmd.Run() + if err != nil { + return nil + } + } else if runtime.GOOS == "linux" { + // On linux, we'll assume xdg-open exists and try using that + cmd := exec.Command("xdg-open", f.Name()) + err := cmd.Run() + if err != nil { + return nil + } + } else if runtime.GOOS == "darwin" { + // On MacOS open should do the trick + cmd := exec.Command("open", f.Name()) + err := cmd.Run() + if err != nil { + return nil + } + } else { + // If we're on none of those platforms, I think I made some sort of fatal mistake + return nil + } + } + } + return nil + case mtv.app.Config.Keybindings.DownloadAttachment: + if ms[mtv.app.SelectedMessage].Attachments != nil { + // Download the files to the configured location + for _, a := range ms[mtv.app.SelectedMessage].Attachments { + f, err := os.Create(filepath.Join(mtv.app.Config.General.AttachmentDownloadsDir + a.Filename)) + if err != nil { + f.Close() + return nil + } + + response, err := http.Get(a.URL) + if err != nil { + f.Close() + return nil + } + + d, err := ioutil.ReadAll(response.Body) + if err != nil { + f.Close() + return nil + } + f.Write(d) + f.Close() + } + } case mtv.app.Config.Keybindings.OpenMessageActionsList: messageActionsList := tview.NewList()