Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e925136ba | ||
|
|
b37589985b | ||
| f60928fefb | |||
| 29fc0c67c9 | |||
| 6c3cb6066d | |||
| 1870313424 | |||
| 00218b6cc5 | |||
| fcf206b999 | |||
| 49c8ecd31a | |||
| 4c595d75d4 | |||
| 49d5ed58d0 | |||
| 77b7167d9c |
17
BINARIES.md
17
BINARIES.md
@@ -1,17 +0,0 @@
|
|||||||
# Building "binaries"
|
|
||||||
|
|
||||||
For perl toolchain-free distribution.
|
|
||||||
|
|
||||||
Install PAR::Packer first, then:
|
|
||||||
|
|
||||||
## Mac
|
|
||||||
|
|
||||||
pp -M IO::Socket::SSL -o dau-mac dau
|
|
||||||
|
|
||||||
## Linux
|
|
||||||
|
|
||||||
pp -M IO::Socket::SSL -o dau-linux dau
|
|
||||||
|
|
||||||
## Windows
|
|
||||||
|
|
||||||
pp -M IO::Socket::SSL -o dau.exe dau
|
|
||||||
64
README.md
64
README.md
@@ -1,6 +1,6 @@
|
|||||||
# Automatically upload screenshots from your computer into a discord channel
|
# Automatically upload screenshots from your computer into a discord channel
|
||||||
|
|
||||||
This script automaticall uploads new screenshots that appear in a folder on your computer to Discord and posts them in a channel:
|
This program automatically uploads new screenshots that appear in a folder on your computer to Discord and posts them in a channel:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -10,50 +10,17 @@ Point it at your Steam screenshot folder, or similar, and shortly after you hit
|
|||||||
|
|
||||||
* A folder where screenshots are stored
|
* A folder where screenshots are stored
|
||||||
* A [discord webhook](https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks)
|
* A [discord webhook](https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks)
|
||||||
* This script
|
* This program
|
||||||
* perl installed (or the windows binary)
|
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
### Linux
|
### Binaries
|
||||||
|
|
||||||
* Download this script:
|
TBD
|
||||||
|
|
||||||
`curl -O https://raw.githubusercontent.com/tardisx/discord-auto-upload/master/dau`
|
#### From source
|
||||||
|
|
||||||
* Put it somewhere on your path (if you want to be able to run it from anywhere)
|
TBD
|
||||||
* chmod +x it
|
|
||||||
* Install the dependencies:
|
|
||||||
|
|
||||||
CPAN: `cpan install Mojolicious IO::Socket::SSL`
|
|
||||||
|
|
||||||
CPANM: `cpanm Mojolicious IO::Socket::SSL`
|
|
||||||
|
|
||||||
Ubuntu/Debian: `sudo apt-get install libmojolicious-perl libio-socket-ssl-perl`
|
|
||||||
|
|
||||||
* test it:
|
|
||||||
|
|
||||||
`dau --help`
|
|
||||||
|
|
||||||
### Mac
|
|
||||||
|
|
||||||
Basically the same as Linux above. [Perlbrew](https://perlbrew.pl) is highly recommended so as not to disturb the system perl. No need for superuser access then either.
|
|
||||||
|
|
||||||
### Windows
|
|
||||||
|
|
||||||
* Grab the windows exe file:
|
|
||||||
|
|
||||||
`http://tba`
|
|
||||||
|
|
||||||
* Optional, put it somewhere on your path
|
|
||||||
* Open a command prompt
|
|
||||||
* Test it
|
|
||||||
|
|
||||||
`\some\path\dau --help`
|
|
||||||
|
|
||||||
If you want to hack it, audit it, or don't trust my exe, you can install
|
|
||||||
[Strawberry Perl](http://strawberryperl.com) and run it using that directly.
|
|
||||||
You'll need the same dependencies mentioned above in the Linux setup.
|
|
||||||
|
|
||||||
## Using it
|
## Using it
|
||||||
|
|
||||||
@@ -67,28 +34,23 @@ If `dau` is on your path, you can run it from your screenshot folder and there i
|
|||||||
|
|
||||||
Note that currently `dau` does not look in subdirectories. Please submit an issue if this is a use case for you.
|
Note that currently `dau` does not look in subdirectories. Please submit an issue if this is a use case for you.
|
||||||
|
|
||||||
The only mandatory command line parameter is the discord webhook URL:
|
The only two mandatory command line parameters are the discord webhook URL:
|
||||||
|
|
||||||
`--webhook URL` - the webhook URL (see [here](https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks) for details).
|
`--webhook URL` - the webhook URL (see [here](https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks) for details).
|
||||||
|
|
||||||
|
and the directory to watch:
|
||||||
|
|
||||||
|
`--directory /some/path/here` - the directory that screenshots will appear in.
|
||||||
|
|
||||||
|
You will have to quote the path on windows, or anywhere where the directory path contains spaces.
|
||||||
|
|
||||||
Other parameters are:
|
Other parameters are:
|
||||||
|
|
||||||
`--watch xx` - specify how many seconds to wait between scanning the directory. The default is 10 seconds.
|
`--watch xx` - specify how many seconds to wait between scanning the directory. The default is 10 seconds.
|
||||||
|
|
||||||
`--directory <somedir>` - the directory to watch for images to appear in. If this option is not supplied, will look in the current directory.
|
|
||||||
|
|
||||||
You will have to quote the path on windows, or anywhere where the directory path contains spaces.
|
|
||||||
|
|
||||||
`--username` - supply a 'username' with the webhook submission. Slightly misleading, it basically provides some extra text next to the "Bot" display on the upload to the channel.
|
|
||||||
|
|
||||||
In the example screenshot, this was set to "tardisx uploaded from EDD".
|
|
||||||
|
|
||||||
`--debug` - provide extra debugging.
|
|
||||||
|
|
||||||
## Limitations/bugs
|
## Limitations/bugs
|
||||||
|
|
||||||
* Only files ending jpg, gif or png are uploaded.
|
* Only files ending jpg, gif or png are uploaded.
|
||||||
* Subdirectories are not scanned.
|
|
||||||
* If multiple screenshots occur quickly (<1 second apart) not all may be uploaded.
|
* If multiple screenshots occur quickly (<1 second apart) not all may be uploaded.
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|||||||
128
dau
128
dau
@@ -1,128 +0,0 @@
|
|||||||
#!/usr/bin/env perl
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
|
|
||||||
use feature 'say';
|
|
||||||
|
|
||||||
use Mojo::UserAgent;
|
|
||||||
use Getopt::Long qw/GetOptions/;
|
|
||||||
use Data::Dumper qw/Dumper/;
|
|
||||||
|
|
||||||
my $webhook_url;
|
|
||||||
my $directory = "./";
|
|
||||||
my $username;
|
|
||||||
my $watch = 10;
|
|
||||||
my $help;
|
|
||||||
my $debug;
|
|
||||||
my $now = time();
|
|
||||||
my $error_count = 0;
|
|
||||||
my $error_max = 10;
|
|
||||||
my $version = '0.1';
|
|
||||||
|
|
||||||
GetOptions(
|
|
||||||
"webhook=s" => \$webhook_url,
|
|
||||||
"directory=s" => \$directory,
|
|
||||||
"username=s" => \$username,
|
|
||||||
"watch=i" => \$watch,
|
|
||||||
"help" => \$help,
|
|
||||||
"debug" => \$debug,
|
|
||||||
) || die usage();
|
|
||||||
|
|
||||||
usage() if $help;
|
|
||||||
|
|
||||||
if (! $webhook_url) {
|
|
||||||
usage("--webhook must be supplied");
|
|
||||||
}
|
|
||||||
|
|
||||||
sub usage {
|
|
||||||
my $error = shift || "";
|
|
||||||
my $indent = " " x length($0);
|
|
||||||
say "dau $version - https://github.com/tardisx/discord-auto-upload\n";
|
|
||||||
say "$0 --webhook <url> [--directory </some/path>]";
|
|
||||||
say "$indent [--username <\"custom username\">] [--watch <n>]\n";
|
|
||||||
say "The current directory will be used if no directory is specified.\n";
|
|
||||||
say "error: $error" if $error;
|
|
||||||
exit defined $error ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
chdir $directory || die "cannot chdir to $directory: $!\n";
|
|
||||||
|
|
||||||
watch_dir();
|
|
||||||
|
|
||||||
sub watch_dir {
|
|
||||||
while (1) {
|
|
||||||
my @files = glob("*");
|
|
||||||
@files = grep { qualifies($_) } @files;
|
|
||||||
foreach my $file (sort { mtime($a) <=> mtime($b) } @files) {
|
|
||||||
debug("examining $file");
|
|
||||||
if (mtime($file) > $now) {
|
|
||||||
$now = mtime($file);
|
|
||||||
upload($file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sleep $watch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub mtime {
|
|
||||||
my $f = shift;
|
|
||||||
return (stat($f))[9];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub qualifies {
|
|
||||||
my $filename = shift;
|
|
||||||
return 1 if ($filename =~ /\.jpg$|\.gif$|\.png$/i);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub info {
|
|
||||||
say "- " . shift;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub debug {
|
|
||||||
return unless $debug;
|
|
||||||
say "! " . shift;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub error {
|
|
||||||
say "* " . shift;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub upload {
|
|
||||||
my $file = shift;
|
|
||||||
info("uploading $file");
|
|
||||||
|
|
||||||
my $ua = Mojo::UserAgent->new;
|
|
||||||
my $data = {
|
|
||||||
upload => { file => $file },
|
|
||||||
$username ? ( username => $username ) : ()
|
|
||||||
};
|
|
||||||
|
|
||||||
my $tx = $ua->post($webhook_url, form => $data);
|
|
||||||
|
|
||||||
if (my $res = $tx->success) {
|
|
||||||
debug(Dumper($res->json));
|
|
||||||
my $url = $res->json->{attachments}->[0]->{url};
|
|
||||||
my $size = $res->json->{attachments}->[0]->{size};
|
|
||||||
my $width = $res->json->{attachments}->[0]->{width};
|
|
||||||
my $height = $res->json->{attachments}->[0]->{height};
|
|
||||||
info("uploaded ${width}x${height} $size bytes images to $url, submitted to webhook successfully");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
debug(Dumper($tx));
|
|
||||||
|
|
||||||
my $err = $tx->error;
|
|
||||||
if ($err->{code}) {
|
|
||||||
error("$err->{code} response: $err->{message}");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
error("Connection error: $err->{message}");
|
|
||||||
}
|
|
||||||
$error_count++;
|
|
||||||
if ($error_count >= $error_max) {
|
|
||||||
error("Sorry - too many errors - quitting");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
213
dau.go
Normal file
213
dau.go
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"github.com/pborman/getopt"
|
||||||
|
"path/filepath"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
"net/http"
|
||||||
|
"log"
|
||||||
|
"io"
|
||||||
|
"bytes"
|
||||||
|
"mime/multipart"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var current_version = "0.2"
|
||||||
|
var last_check = time.Now()
|
||||||
|
var new_last_check = time.Now()
|
||||||
|
var webhook_url string
|
||||||
|
|
||||||
|
type webhook_response struct {
|
||||||
|
Test string
|
||||||
|
}
|
||||||
|
|
||||||
|
func keepLines(s string, n int) string {
|
||||||
|
result := strings.Join(strings.Split(s, "\n")[:n], "\n")
|
||||||
|
return strings.Replace(result, "\r", "", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
webhook, path, watch := parse_options()
|
||||||
|
webhook_url = webhook
|
||||||
|
|
||||||
|
check_updates()
|
||||||
|
|
||||||
|
// wander the path, forever
|
||||||
|
for {
|
||||||
|
err := filepath.Walk(path, check_file)
|
||||||
|
if err != nil { log.Fatal("oh dear") }
|
||||||
|
//fmt.Printf("filepath.Walk() returned %v\n", err)
|
||||||
|
last_check = new_last_check
|
||||||
|
time.Sleep(time.Duration(watch)*time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func check_updates() {
|
||||||
|
|
||||||
|
type GithubRelease struct {
|
||||||
|
Html_url string
|
||||||
|
Tag_name string
|
||||||
|
Name string
|
||||||
|
Body string
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.Get("https://api.github.com/repos/tardisx/discord-auto-upload/releases/latest")
|
||||||
|
if (err != nil) {
|
||||||
|
log.Fatal("could not check for updates")
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if (err != nil) {
|
||||||
|
log.Fatal("could not check read update response")
|
||||||
|
}
|
||||||
|
|
||||||
|
var latest GithubRelease
|
||||||
|
err = json.Unmarshal(body, &latest)
|
||||||
|
|
||||||
|
if (err != nil) {
|
||||||
|
log.Fatal("could not parse JSON", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_version != latest.Tag_name) {
|
||||||
|
fmt.Println("A new version is available:", latest.Tag_name)
|
||||||
|
fmt.Println("----------- Release Info -----------")
|
||||||
|
fmt.Println(latest.Body)
|
||||||
|
fmt.Println("------------------------------------")
|
||||||
|
fmt.Println("( You are currently on version:", current_version, ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func parse_options() (webhook_url string, path string, watch int) {
|
||||||
|
|
||||||
|
// Declare the flags to be used
|
||||||
|
// helpFlag := getopt.Bool('h', "display help")
|
||||||
|
webhookFlag := getopt.StringLong("webhook", 'w', "", "webhook URL")
|
||||||
|
pathFlag := getopt.StringLong("directory", 'd', "", "directory")
|
||||||
|
watchFlag := getopt.Int16Long("watch", 's', 10, "time between scans")
|
||||||
|
|
||||||
|
getopt.Parse()
|
||||||
|
|
||||||
|
return *webhookFlag, *pathFlag, int(*watchFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func check_file(path string, f os.FileInfo, err error) error {
|
||||||
|
// fmt.Println("Comparing", f.ModTime(), "to", last_check, "for", path)
|
||||||
|
|
||||||
|
if f.ModTime().After(last_check) && f.Mode().IsRegular() {
|
||||||
|
|
||||||
|
if file_eligible(path) {
|
||||||
|
// process file
|
||||||
|
process_file(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if new_last_check.Before(f.ModTime()) {
|
||||||
|
new_last_check = f.ModTime()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func file_eligible(file string) (bool) {
|
||||||
|
extension := strings.ToLower(filepath.Ext(file))
|
||||||
|
if extension == ".png" || extension == ".jpg" || extension == ".gif" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func process_file(file string) {
|
||||||
|
log.Print("Uploading ", file)
|
||||||
|
|
||||||
|
extraParams := map[string]string{
|
||||||
|
// "username": "Some username",
|
||||||
|
}
|
||||||
|
|
||||||
|
type DiscordAPIResponseAttachment struct {
|
||||||
|
Url string
|
||||||
|
Proxy_url string
|
||||||
|
Size int
|
||||||
|
Width int
|
||||||
|
Height int
|
||||||
|
Filename string
|
||||||
|
}
|
||||||
|
|
||||||
|
type DiscordAPIResponse struct {
|
||||||
|
Attachments []DiscordAPIResponseAttachment
|
||||||
|
id int64
|
||||||
|
}
|
||||||
|
|
||||||
|
request, err := newfileUploadRequest(webhook_url, extraParams, "file", file)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(request)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
log.Fatal("Error performing request:", err)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (resp.StatusCode != 200) {
|
||||||
|
log.Print("Bad response from server:", resp.StatusCode)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res_body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if (err != nil) {
|
||||||
|
log.Fatal("could not deal with body", err)
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
var res DiscordAPIResponse
|
||||||
|
err = json.Unmarshal(res_body, &res)
|
||||||
|
|
||||||
|
if (err != nil) {
|
||||||
|
log.Fatal("could not parse JSON", err)
|
||||||
|
fmt.Println("Response was:", res_body)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (len(res.Attachments) < 1) {
|
||||||
|
log.Print("bad response - no attachments?")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var a = res.Attachments[0]
|
||||||
|
log.Printf("Uploaded to %s %dx%d, %d bytes\n", a.Url, a.Width, a.Height, a.Size)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func newfileUploadRequest(uri string, params map[string]string, paramName, path string) (*http.Request, error) {
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
body := &bytes.Buffer{}
|
||||||
|
writer := multipart.NewWriter(body)
|
||||||
|
part, err := writer.CreateFormFile(paramName, filepath.Base(path))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = io.Copy(part, file)
|
||||||
|
|
||||||
|
for key, val := range params {
|
||||||
|
_ = writer.WriteField(key, val)
|
||||||
|
}
|
||||||
|
err = writer.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", uri, body)
|
||||||
|
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||||
|
return req, err
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user