Fix #335
This commit is contained in:
parent
710475bc0b
commit
b704c9183b
|
@ -1,11 +1,15 @@
|
|||
package commandimpls
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Bios-Marcel/discordgo"
|
||||
|
||||
|
@ -19,15 +23,26 @@ const fileSendDocumentation = `[::b]NAME
|
|||
file-send - send files from your local machine
|
||||
|
||||
[::b]SYNOPSIS
|
||||
[::b]file-send <FILE_PATH>...
|
||||
|
||||
[::b]file-send [OPTION[]... <FILE_PATH>...
|
||||
|
||||
[::b]DESCRIPTION
|
||||
The file-send command allows you to send multiple files to your current channel.
|
||||
|
||||
[::b]OPTIONS
|
||||
[::b]-b, --bulk
|
||||
Zips all files and sends them as a single file.
|
||||
Without this option, the folder structure won't be preserved.
|
||||
[::b]-r, --recursive
|
||||
Allow sending folders as well
|
||||
|
||||
[::b]EXAMPLES
|
||||
[gray]$ file-send ~/file.txt
|
||||
[gray]$ file-send -r ~/folder
|
||||
[gray]$ file-send -r -b ~/folder
|
||||
[gray]$ file-send ~/file1.txt ~/file2.txt
|
||||
[gray]$ file-send "~/file one.txt" ~/file2.txt`
|
||||
[gray]$ file-send "~/file one.txt" ~/file2.txt
|
||||
[gray]$ file-send -b "~/file one.txt" ~/file2.txt
|
||||
[gray]$ file-send -b -r "~/file one.txt" ~/folder ~/file2.txt`
|
||||
|
||||
// FileSend represents the command used to send multiple files to a channel.
|
||||
type FileSend struct {
|
||||
|
@ -56,33 +71,103 @@ func (cmd *FileSend) Execute(writer io.Writer, parameters []string) {
|
|||
return
|
||||
}
|
||||
|
||||
var filteredParameters []string
|
||||
//Will cause all files in folders to be upload. This counts for subfolders as well.
|
||||
var recursive bool
|
||||
//Puts all files into one zip.
|
||||
var bulk bool
|
||||
|
||||
//Parse flags and sort them out for further processing.
|
||||
for _, parameter := range parameters {
|
||||
if parameter == "-r" || parameter == "--recursive" {
|
||||
recursive = true
|
||||
} else if parameter == "-b" || parameter == "--bulk" {
|
||||
bulk = true
|
||||
} else {
|
||||
filteredParameters = append(filteredParameters, parameter)
|
||||
}
|
||||
}
|
||||
|
||||
//Assume that all leftofer parameters are paths and convert them to absolute paths.
|
||||
var consumablePaths []string
|
||||
for _, parameter := range filteredParameters {
|
||||
resolvedPath, resolveError := files.ToAbsolutePath(parameter)
|
||||
if resolveError != nil {
|
||||
fmt.Fprintf(writer, "["+tviewutil.ColorToHex(config.GetTheme().ErrorColor)+"]Error reading file:\n\t["+tviewutil.ColorToHex(config.GetTheme().ErrorColor)+"]%s\n", resolveError.Error())
|
||||
continue
|
||||
return
|
||||
}
|
||||
|
||||
data, readError := ioutil.ReadFile(resolvedPath)
|
||||
if readError != nil {
|
||||
fmt.Fprintf(writer, "["+tviewutil.ColorToHex(config.GetTheme().ErrorColor)+"]Error reading file:\n\t["+tviewutil.ColorToHex(config.GetTheme().ErrorColor)+"]%s\n", readError.Error())
|
||||
continue
|
||||
}
|
||||
consumablePaths = append(consumablePaths, resolvedPath)
|
||||
}
|
||||
|
||||
dataChannel := bytes.NewReader(data)
|
||||
_, sendError := cmd.discord.ChannelFileSend(channel.ID, path.Base(resolvedPath), dataChannel)
|
||||
if sendError != nil {
|
||||
fmt.Fprintf(writer, "["+tviewutil.ColorToHex(config.GetTheme().ErrorColor)+"]Error sending file:\n\t["+tviewutil.ColorToHex(config.GetTheme().ErrorColor)+"]%s\n", sendError.Error())
|
||||
//If folders are not to be included, we error if any folder is found.
|
||||
if !recursive {
|
||||
for _, path := range consumablePaths {
|
||||
//FIXME Handle error.
|
||||
stats, _ := os.Stat(path)
|
||||
if stats.IsDir() {
|
||||
fmt.Fprintf(writer, "["+tviewutil.ColorToHex(config.GetTheme().ErrorColor)+"]Directories can only be uploaded if the '-r' flag is set.\n")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if bulk {
|
||||
//We read and write at the same time to save performance and memory.
|
||||
zipOutput, zipInput := io.Pipe()
|
||||
|
||||
//While we write, we read in a background thread. We stay in
|
||||
//memory, instead of going over the filesystem.
|
||||
go func() {
|
||||
defer zipOutput.Close()
|
||||
_, sendError := cmd.discord.ChannelFileSend(channel.ID, "files.zip", zipOutput)
|
||||
if sendError != nil {
|
||||
fmt.Fprintf(writer, "["+tviewutil.ColorToHex(config.GetTheme().ErrorColor)+"]Error sending file:\n\t["+tviewutil.ColorToHex(config.GetTheme().ErrorColor)+"]%s\n", sendError.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
zipWriter := zip.NewWriter(zipInput)
|
||||
defer zipInput.Close()
|
||||
defer zipWriter.Close()
|
||||
for _, parameter := range consumablePaths {
|
||||
zipError := files.AddToZip(zipWriter, parameter)
|
||||
if zipError != nil {
|
||||
log.Println(zipError.Error())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//We skip directories and flatten the folder structure.
|
||||
for _, filePath := range consumablePaths {
|
||||
filepath.Walk(filePath, func(file string, info os.FileInfo, err error) error {
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
data, readError := ioutil.ReadFile(file)
|
||||
if readError != nil {
|
||||
fmt.Fprintf(writer, "["+tviewutil.ColorToHex(config.GetTheme().ErrorColor)+"]Error reading file:\n\t["+tviewutil.ColorToHex(config.GetTheme().ErrorColor)+"]%s\n", readError.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
dataChannel := bytes.NewReader(data)
|
||||
_, sendError := cmd.discord.ChannelFileSend(channel.ID, path.Base(file), dataChannel)
|
||||
if sendError != nil {
|
||||
fmt.Fprintf(writer, "["+tviewutil.ColorToHex(config.GetTheme().ErrorColor)+"]Error sending file:\n\t["+tviewutil.ColorToHex(config.GetTheme().ErrorColor)+"]%s\n", sendError.Error())
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Name represents the main-name of the command.
|
||||
func (cmd *FileSend) Name() string {
|
||||
return "file-send"
|
||||
}
|
||||
|
||||
// Aliases represents all available aliases this command can be called with.
|
||||
func (cmd *FileSend) Aliases() []string {
|
||||
return []string{"filesend", "sendfile", "send-file"}
|
||||
return []string{"filesend", "sendfile", "send-file", "file-upload", "upload-file"}
|
||||
}
|
||||
|
||||
// PrintHelp prints the help for the FileSend command.
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package files
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// AddToZip writes the given data to a zip archive using the passed writer.
|
||||
// Folders are added to the zip recursively. The folder structure is fully
|
||||
// preserved.
|
||||
func AddToZip(zipWriter *zip.Writer, filename string) error {
|
||||
info, statError := os.Stat(filename)
|
||||
if os.IsNotExist(statError) || statError != nil {
|
||||
return statError
|
||||
}
|
||||
|
||||
var baseDir string
|
||||
if info.IsDir() {
|
||||
baseDir = filepath.Base(filename)
|
||||
}
|
||||
|
||||
filepath.Walk(filename, func(path string, info os.FileInfo, readError error) error {
|
||||
if readError != nil {
|
||||
return readError
|
||||
}
|
||||
|
||||
header, readError := zip.FileInfoHeader(info)
|
||||
if readError != nil {
|
||||
return readError
|
||||
}
|
||||
|
||||
if baseDir != "" {
|
||||
header.Name = filepath.Join(baseDir, strings.TrimPrefix(path, filename))
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
header.Name += "/"
|
||||
} else {
|
||||
header.Method = zip.Deflate
|
||||
}
|
||||
|
||||
writer, headerError := zipWriter.CreateHeader(header)
|
||||
if headerError != nil {
|
||||
return headerError
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
file, readError := os.Open(path)
|
||||
if readError != nil {
|
||||
return readError
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
_, readError = io.Copy(writer, file)
|
||||
return readError
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue