Compare commits
18 Commits
v0.12.0
...
refactor-a
| Author | SHA1 | Date | |
|---|---|---|---|
| 4dd166e65e | |||
| 9f7090a2f8 | |||
| 1bf557c3eb | |||
| df0c6d090d | |||
| b851a4f773 | |||
| e612f8ae59 | |||
| 05ddf07fc4 | |||
| 9423bc32e9 | |||
| 243c349366 | |||
| d8b16674dd | |||
| 9294ca9e33 | |||
| 2a99541f58 | |||
| b04ed6aa62 | |||
| 06ac259e0a | |||
| 0c2fafdc7a | |||
| f9433ae0cd | |||
| ba7ae21248 | |||
| fbd267e687 |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
github: tardisx
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@ discord-auto-upload.exe
|
||||
*.png
|
||||
*.jpg
|
||||
.DS_Store
|
||||
versioninfo.json
|
||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"daulog",
|
||||
"inconsolata"
|
||||
"Debugf",
|
||||
"inconsolata",
|
||||
"skratchdot"
|
||||
]
|
||||
}
|
||||
19
CHANGELOG.md
19
CHANGELOG.md
@@ -3,7 +3,24 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v0.12.0] - 2020-04-03
|
||||
## [v0.12.3] - 2022-05-09
|
||||
|
||||
- Fix a race condition occasionally causing multiple duplicate uploads
|
||||
|
||||
## [v0.12.2] - 2022-05-01
|
||||
|
||||
- Automatically open your web browser to the `dau` web interface
|
||||
(can be disabled in configuration)
|
||||
- Add system tray/menubar icon with menus to open web interface, quit and
|
||||
other links
|
||||
- Superfluous text console removed on windows
|
||||
|
||||
## [v0.12.1] - 2022-05-01
|
||||
|
||||
- Show if a new version is available in the web interface
|
||||
- Rework logging and fix the log display in the web interface
|
||||
|
||||
## [v0.12.0] - 2022-04-03
|
||||
|
||||
- Break upload page into pending/current/complete sections
|
||||
- Add preview thumbnails for each upload
|
||||
|
||||
@@ -29,8 +29,13 @@ You'll need to [download Go](https://golang.org/dl/), check the code out somewhe
|
||||
## Using it
|
||||
|
||||
`dau` configuration is managed via its internal web interface. When the executable is run, you can visit
|
||||
`http://localhost:9090` in your web browser to configure the it. Configuration persists across runs, it is
|
||||
saved in a file called '.dau.json' in your home directory.
|
||||
`http://localhost:9090` in your web browser to configure it. On Windows, a tray icon is created to provide
|
||||
access to the web interface.
|
||||
|
||||
The web browser will be loaded automatically when you start the program, if possible. This option can be
|
||||
disabled in the settings.
|
||||
|
||||
Configuration persists across runs, it is saved in a file called '.dau.json' in your home directory.
|
||||
|
||||
The first time you run it, you will need to configure at least the discord web hook and the watch path for
|
||||
`dau` to be useful.
|
||||
|
||||
@@ -2,17 +2,27 @@
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Mojo::JSON qw/encode_json decode_json/;
|
||||
use Mojo::File;
|
||||
|
||||
open my $fh, "<", "version/version.go" || die $!;
|
||||
|
||||
my $version;
|
||||
while (<$fh>) {
|
||||
$version = $1 if /^const\s+CurrentVersion.*?"(v[\d\.]+)"/;
|
||||
$version = $1 if /^const\s+CurrentVersion.*?"v([\d\.]+)"/;
|
||||
}
|
||||
close $fh;
|
||||
|
||||
die "no version?" unless defined $version;
|
||||
|
||||
my @version_parts = split /\./, $version;
|
||||
die "bad version?" unless defined $version_parts[2];
|
||||
|
||||
foreach (@version_parts) {
|
||||
$_ = 0 + $_;
|
||||
}
|
||||
|
||||
$version = "v$version";
|
||||
|
||||
# quit if tests fail
|
||||
system("go test ./...") && die "not building release with failing tests";
|
||||
|
||||
@@ -34,9 +44,33 @@ foreach my $type (keys %build) {
|
||||
add_extras();
|
||||
|
||||
foreach my $type (keys %build) {
|
||||
print "building for $type\n";
|
||||
local $ENV{GOOS} = $build{$type}->{env}->{GOOS};
|
||||
local $ENV{GOARCH} = $build{$type}->{env}->{GOARCH};
|
||||
system "go", "build", "-o", "release/$type/" . $build{$type}->{filename};
|
||||
|
||||
unlink "resource.syso";
|
||||
|
||||
my @ldflags = ();
|
||||
if ($type eq "win") {
|
||||
# create the versioninfo.json based on the current version
|
||||
my $tmp = Mojo::File->new("versioninfo.json-template")->slurp();
|
||||
my $vdata = decode_json($tmp);
|
||||
$vdata->{FixedFileInfo}->{FileVersion}->{Major} = $version_parts[0] ;
|
||||
$vdata->{FixedFileInfo}->{FileVersion}->{Minor} = $version_parts[1] ;
|
||||
$vdata->{FixedFileInfo}->{FileVersion}->{Patch} = $version_parts[2] ;
|
||||
$vdata->{FixedFileInfo}->{ProductVersion}->{Major} = $version_parts[0] ;
|
||||
$vdata->{FixedFileInfo}->{ProductVersion}->{Minor} = $version_parts[1] ;
|
||||
$vdata->{FixedFileInfo}->{ProductVersion}->{Patch} = $version_parts[2] ;
|
||||
|
||||
$vdata->{StringFileInfo}->{ProductVersion} = $version;
|
||||
|
||||
Mojo::File->new("versioninfo.json")->spurt(encode_json($vdata));
|
||||
|
||||
@ldflags = (qw/ -ldflags -H=windowsgui/);
|
||||
system "go", "generate";
|
||||
}
|
||||
warn join(' ', "go", "build", @ldflags, "-o", "release/$type/" . $build{$type}->{filename});
|
||||
system "go", "build", @ldflags, "-o", "release/$type/" . $build{$type}->{filename};
|
||||
system "zip", "-j", "dist/dau-$type-$version.zip", ( glob "release/$type/*" );
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,16 @@ type ConfigV2 struct {
|
||||
Watchers []Watcher
|
||||
}
|
||||
|
||||
type ConfigV3 struct {
|
||||
WatchInterval int
|
||||
Version int
|
||||
Port int
|
||||
OpenBrowserOnStart bool
|
||||
Watchers []Watcher
|
||||
}
|
||||
|
||||
type ConfigService struct {
|
||||
Config *ConfigV2
|
||||
Config *ConfigV3
|
||||
Changed chan bool
|
||||
ConfigFilename string
|
||||
}
|
||||
@@ -54,11 +62,11 @@ func DefaultConfigService() *ConfigService {
|
||||
// LoadOrInit loads the current configuration from the config file, or creates
|
||||
// a new config file if none exists.
|
||||
func (c *ConfigService) LoadOrInit() error {
|
||||
daulog.SendLog(fmt.Sprintf("Trying to load config from %s\n", c.ConfigFilename), daulog.LogTypeDebug)
|
||||
daulog.Debugf("Trying to load config from %s\n", c.ConfigFilename)
|
||||
_, err := os.Stat(c.ConfigFilename)
|
||||
if os.IsNotExist(err) {
|
||||
daulog.SendLog("NOTE: No config file, writing out sample configuration", daulog.LogTypeInfo)
|
||||
daulog.SendLog("You need to set the configuration via the web interface", daulog.LogTypeInfo)
|
||||
daulog.Info("NOTE: No config file, writing out sample configuration")
|
||||
daulog.Info("You need to set the configuration via the web interface")
|
||||
c.Config = DefaultConfig()
|
||||
return c.Save()
|
||||
} else {
|
||||
@@ -66,11 +74,12 @@ func (c *ConfigService) LoadOrInit() error {
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultConfig() *ConfigV2 {
|
||||
c := ConfigV2{}
|
||||
c.Version = 2
|
||||
func DefaultConfig() *ConfigV3 {
|
||||
c := ConfigV3{}
|
||||
c.Version = 3
|
||||
c.WatchInterval = 10
|
||||
c.Port = 9090
|
||||
c.OpenBrowserOnStart = true
|
||||
w := Watcher{
|
||||
WebHookURL: "https://webhook.url.here",
|
||||
Path: "/your/screenshot/dir/here",
|
||||
@@ -84,7 +93,7 @@ func DefaultConfig() *ConfigV2 {
|
||||
|
||||
// Load will load the configuration from a known-to-exist config file.
|
||||
func (c *ConfigService) Load() error {
|
||||
fmt.Printf("Loading from %s\n\n", c.ConfigFilename)
|
||||
daulog.Debugf("Loading from %s", c.ConfigFilename)
|
||||
|
||||
data, err := ioutil.ReadFile(c.ConfigFilename)
|
||||
if err != nil {
|
||||
@@ -98,7 +107,7 @@ func (c *ConfigService) Load() error {
|
||||
// Version 0 predates config migrations
|
||||
if c.Config.Version == 0 {
|
||||
// need to migrate this
|
||||
daulog.SendLog("Migrating config to V2", daulog.LogTypeInfo)
|
||||
daulog.Info("Migrating config to V2")
|
||||
|
||||
configV1 := ConfigV1{}
|
||||
err = json.Unmarshal([]byte(data), &configV1)
|
||||
@@ -122,11 +131,18 @@ func (c *ConfigService) Load() error {
|
||||
c.Config.Watchers = []Watcher{onlyWatcher}
|
||||
}
|
||||
|
||||
if c.Config.Version == 2 {
|
||||
// need to migrate this
|
||||
daulog.Info("Migrating config to V3")
|
||||
c.Config.Version = 3
|
||||
c.Config.OpenBrowserOnStart = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ConfigService) Save() error {
|
||||
daulog.SendLog("saving configuration", daulog.LogTypeInfo)
|
||||
daulog.Info("saving configuration")
|
||||
// sanity checks
|
||||
for _, watcher := range c.Config.Watchers {
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ func TestNoConfig(t *testing.T) {
|
||||
t.Errorf("unexpected failure from load: %s", err)
|
||||
}
|
||||
|
||||
if c.Config.Version != 2 {
|
||||
t.Error("not version 2 starting config")
|
||||
if c.Config.Version != 3 {
|
||||
t.Error("not version 3 starting config")
|
||||
}
|
||||
|
||||
if fileSize(c.ConfigFilename) < 40 {
|
||||
@@ -45,7 +45,7 @@ func TestEmptyFileConfig(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestMigrateFromV1toV2(t *testing.T) {
|
||||
func TestMigrateFromV1toV3(t *testing.T) {
|
||||
c := ConfigService{}
|
||||
|
||||
c.ConfigFilename = v1Config()
|
||||
@@ -53,8 +53,12 @@ func TestMigrateFromV1toV2(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error("unexpected error from LoadOrInit()")
|
||||
}
|
||||
if c.Config.Version != 2 {
|
||||
t.Errorf("Version %d not 2", c.Config.Version)
|
||||
if c.Config.Version != 3 {
|
||||
t.Errorf("Version %d not 3", c.Config.Version)
|
||||
}
|
||||
|
||||
if c.Config.OpenBrowserOnStart != true {
|
||||
t.Errorf("Open browser on start not true")
|
||||
}
|
||||
|
||||
if len(c.Config.Watchers) != 1 {
|
||||
|
||||
103
dau.go
103
dau.go
@@ -2,13 +2,10 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -18,12 +15,12 @@ import (
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
|
||||
// "github.com/skratchdot/open-golang/open"
|
||||
|
||||
"github.com/tardisx/discord-auto-upload/config"
|
||||
daulog "github.com/tardisx/discord-auto-upload/log"
|
||||
"github.com/tardisx/discord-auto-upload/upload"
|
||||
|
||||
"github.com/skratchdot/open-golang/open"
|
||||
|
||||
// "github.com/tardisx/discord-auto-upload/upload"
|
||||
"github.com/tardisx/discord-auto-upload/version"
|
||||
"github.com/tardisx/discord-auto-upload/web"
|
||||
@@ -40,41 +37,58 @@ func main() {
|
||||
|
||||
parseOptions()
|
||||
|
||||
// grab the config, register to notice changes
|
||||
config := config.DefaultConfigService()
|
||||
// grab the conf, register to notice changes
|
||||
conf := config.DefaultConfigService()
|
||||
configChanged := make(chan bool)
|
||||
config.Changed = configChanged
|
||||
config.LoadOrInit()
|
||||
conf.Changed = configChanged
|
||||
conf.LoadOrInit()
|
||||
|
||||
// create the uploader
|
||||
up := upload.NewUploader()
|
||||
|
||||
// log.Print("Opening web browser")
|
||||
// open.Start("http://localhost:9090")
|
||||
web := web.WebService{Config: config, Uploader: up}
|
||||
web := web.WebService{Config: conf, Uploader: up}
|
||||
web.StartWebServer()
|
||||
|
||||
go func() { checkUpdates() }()
|
||||
if conf.Config.OpenBrowserOnStart {
|
||||
openWebBrowser(conf.Config.Port)
|
||||
}
|
||||
|
||||
go func() {
|
||||
version.GetOnlineVersion()
|
||||
if version.UpdateAvailable() {
|
||||
daulog.Info("*** NEW VERSION AVAILABLE ***")
|
||||
daulog.Infof("You are currently on version %s, but version %s is available\n", version.CurrentVersion, version.LatestVersionInfo.TagName)
|
||||
daulog.Info("----------- Release Info -----------")
|
||||
daulog.Info(version.LatestVersionInfo.Body)
|
||||
daulog.Info("------------------------------------")
|
||||
daulog.Info("Upgrade at https://github.com/tardisx/discord-auto-upload/releases/latest")
|
||||
}
|
||||
}()
|
||||
|
||||
// create the watchers, restart them if config changes
|
||||
// blocks forever
|
||||
startWatchers(config, up, configChanged)
|
||||
go func() {
|
||||
startWatchers(conf, up, configChanged)
|
||||
}()
|
||||
mainloop(conf)
|
||||
|
||||
}
|
||||
|
||||
func startWatchers(config *config.ConfigService, up *upload.Uploader, configChange chan bool) {
|
||||
for {
|
||||
daulog.SendLog("Creating watchers", daulog.LogTypeInfo)
|
||||
daulog.Debug("Creating watchers")
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
for _, c := range config.Config.Watchers {
|
||||
log.Printf("Creating watcher for %s interval %d", c.Path, config.Config.WatchInterval)
|
||||
daulog.Infof("Creating watcher for %s with interval %d", c.Path, config.Config.WatchInterval)
|
||||
watcher := watch{uploader: up, lastCheck: time.Now(), newLastCheck: time.Now(), config: c}
|
||||
go watcher.Watch(config.Config.WatchInterval, ctx)
|
||||
}
|
||||
// wait for single that the config changed
|
||||
<-configChange
|
||||
cancel()
|
||||
daulog.SendLog("starting new watchers due to config change", daulog.LogTypeInfo)
|
||||
daulog.Info("starting new watchers due to config change")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -83,7 +97,7 @@ func (w *watch) Watch(interval int, ctx context.Context) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
daulog.SendLog("Killing old watcher", daulog.LogTypeInfo)
|
||||
daulog.Info("Killing old watcher")
|
||||
return
|
||||
default:
|
||||
newFiles := w.ProcessNewFiles()
|
||||
@@ -92,7 +106,7 @@ func (w *watch) Watch(interval int, ctx context.Context) {
|
||||
}
|
||||
// upload them
|
||||
w.uploader.Upload()
|
||||
daulog.SendLog(fmt.Sprintf("sleeping for %ds before next check of %s", interval, w.config.Path), daulog.LogTypeDebug)
|
||||
daulog.Debugf("sleeping for %ds before next check of %s", interval, w.config.Path)
|
||||
time.Sleep(time.Duration(interval) * time.Second)
|
||||
}
|
||||
}
|
||||
@@ -115,6 +129,7 @@ func (w *watch) ProcessNewFiles() []string {
|
||||
}
|
||||
w.lastCheck = w.newLastCheck
|
||||
}
|
||||
|
||||
return newFiles
|
||||
}
|
||||
|
||||
@@ -123,11 +138,11 @@ func (w *watch) ProcessNewFiles() []string {
|
||||
func (w *watch) checkPath() bool {
|
||||
src, err := os.Stat(w.config.Path)
|
||||
if err != nil {
|
||||
daulog.SendLog(fmt.Sprintf("Problem with path '%s': %s", w.config.Path, err), daulog.LogTypeError)
|
||||
daulog.Errorf("Problem with path '%s': %s", w.config.Path, err)
|
||||
return false
|
||||
}
|
||||
if !src.IsDir() {
|
||||
daulog.SendLog(fmt.Sprintf("Problem with path '%s': is not a directory", w.config.Path), daulog.LogTypeError)
|
||||
daulog.Errorf("Problem with path '%s': is not a directory", w.config.Path)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@@ -169,51 +184,6 @@ func (w *watch) checkFile(path string, found *[]string, exclusions []string) err
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkUpdates() {
|
||||
|
||||
type GithubRelease struct {
|
||||
HTMLURL string `json:"html_url"`
|
||||
TagName string `json:"tag_name"`
|
||||
Name string `json:"name"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
daulog.SendLog("checking for new version", daulog.LogTypeInfo)
|
||||
|
||||
client := &http.Client{Timeout: time.Second * 5}
|
||||
resp, err := client.Get("https://api.github.com/repos/tardisx/discord-auto-upload/releases/latest")
|
||||
if err != nil {
|
||||
daulog.SendLog(fmt.Sprintf("WARNING: Update check failed: %v", err), daulog.LogTypeError)
|
||||
return
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
// pre v0.11.0 version (ie before semver) did a simple string comparison,
|
||||
// but since "0.10.0" < "v0.11.0" they should still get prompted to upgrade
|
||||
// ok
|
||||
if version.NewVersionAvailable(latest.TagName) {
|
||||
fmt.Printf("You are currently on version %s, but version %s is available\n", version.CurrentVersion, latest.TagName)
|
||||
fmt.Println("----------- Release Info -----------")
|
||||
fmt.Println(latest.Body)
|
||||
fmt.Println("------------------------------------")
|
||||
fmt.Println("Upgrade at https://github.com/tardisx/discord-auto-upload/releases/latest")
|
||||
daulog.SendLog(fmt.Sprintf("New version available: %s - download at https://github.com/tardisx/discord-auto-upload/releases/latest", latest.TagName), daulog.LogTypeInfo)
|
||||
}
|
||||
|
||||
daulog.SendLog("already running latest version", daulog.LogTypeInfo)
|
||||
}
|
||||
|
||||
func parseOptions() {
|
||||
var versionFlag bool
|
||||
flag.BoolVar(&versionFlag, "version", false, "show version")
|
||||
@@ -226,3 +196,8 @@ func parseOptions() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func openWebBrowser(port int) {
|
||||
address := fmt.Sprintf("http://localhost:%d", port)
|
||||
open.Start(address)
|
||||
}
|
||||
|
||||
11
dau_nonwindows.go
Normal file
11
dau_nonwindows.go
Normal file
@@ -0,0 +1,11 @@
|
||||
//go:build !windows
|
||||
|
||||
package main
|
||||
|
||||
import "github.com/tardisx/discord-auto-upload/config"
|
||||
|
||||
func mainloop(c *config.ConfigService) {
|
||||
|
||||
ch := make(chan bool)
|
||||
<-ch
|
||||
}
|
||||
62
dau_windows.go
Normal file
62
dau_windows.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"github.com/getlantern/systray"
|
||||
"github.com/skratchdot/open-golang/open"
|
||||
"github.com/tardisx/discord-auto-upload/config"
|
||||
daulog "github.com/tardisx/discord-auto-upload/log"
|
||||
"github.com/tardisx/discord-auto-upload/version"
|
||||
)
|
||||
|
||||
//go:embed dau.ico
|
||||
var appIcon []byte
|
||||
|
||||
//go:generate goversioninfo
|
||||
|
||||
// -manifest=testdata/resource/goversioninfo.exe.manifest
|
||||
|
||||
func mainloop(c *config.ConfigService) {
|
||||
systray.Run(func() { onReady(c) }, onExit)
|
||||
}
|
||||
|
||||
func onReady(c *config.ConfigService) {
|
||||
|
||||
systray.SetIcon(appIcon)
|
||||
//systray.SetTitle("DAU")
|
||||
systray.SetTooltip(fmt.Sprintf("discord-auto-upload %s", version.CurrentVersion))
|
||||
openApp := systray.AddMenuItem("Open", "Open in web browser")
|
||||
gh := systray.AddMenuItem("Github", "Open project page")
|
||||
discord := systray.AddMenuItem("Discord", "Join us on discord")
|
||||
ghr := systray.AddMenuItem("Release Notes", "Open project release notes")
|
||||
quit := systray.AddMenuItem("Quit", "Quit")
|
||||
|
||||
go func() {
|
||||
<-quit.ClickedCh
|
||||
systray.Quit()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-openApp.ClickedCh:
|
||||
openWebBrowser(c.Config.Port)
|
||||
case <-gh.ClickedCh:
|
||||
open.Start("https://github.com/tardisx/discord-auto-upload")
|
||||
case <-ghr.ClickedCh:
|
||||
open.Start(fmt.Sprintf("https://github.com/tardisx/discord-auto-upload/releases/tag/%s", version.CurrentVersion))
|
||||
case <-discord.ClickedCh:
|
||||
open.Start("https://discord.gg/eErG9sntbZ")
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Sets the icon of a menu item. Only available on Mac and Windows.
|
||||
// mQuit.SetIcon(icon.Data)
|
||||
}
|
||||
|
||||
func onExit() {
|
||||
// clean up here
|
||||
daulog.Info("quitting on user request")
|
||||
}
|
||||
5
go.mod
5
go.mod
@@ -4,9 +4,12 @@ go 1.16
|
||||
|
||||
require (
|
||||
github.com/fogleman/gg v1.3.0
|
||||
github.com/getlantern/systray v1.2.1
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/josephspurrier/goversioninfo v1.4.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
|
||||
golang.org/x/image v0.0.0-20210504121937-7319ad40d33e
|
||||
golang.org/x/mod v0.4.2
|
||||
)
|
||||
|
||||
37
go.sum
37
go.sum
@@ -1,11 +1,44 @@
|
||||
github.com/akavel/rsrc v0.10.2 h1:Zxm8V5eI1hW4gGaYsJQUhxpjkENuG91ki8B4zCrvEsw=
|
||||
github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
|
||||
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4=
|
||||
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY=
|
||||
github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 h1:6uJ+sZ/e03gkbqZ0kUG6mfKoqDb4XMAzMIwlajq19So=
|
||||
github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A=
|
||||
github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7 h1:guBYzEaLz0Vfc/jv0czrr2z7qyzTOGC9hiQ0VC+hKjk=
|
||||
github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7/go.mod h1:zx/1xUUeYPy3Pcmet8OSXLbF47l+3y6hIPpyLWoR9oc=
|
||||
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 h1:micT5vkcr9tOVk1FiH8SWKID8ultN44Z+yzd2y/Vyb0=
|
||||
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o=
|
||||
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 h1:XYzSdCbkzOC0FDNrgJqGRo8PCMFOBFL9py72DRs7bmc=
|
||||
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA=
|
||||
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSlsrcuKazukx/xqO/PpLZzZXsF+EA=
|
||||
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
|
||||
github.com/getlantern/systray v1.2.1 h1:udsC2k98v2hN359VTFShuQW6GGprRprw6kD6539JikI=
|
||||
github.com/getlantern/systray v1.2.1/go.mod h1:AecygODWIsBquJCJFop8MEQcJbWFfw/1yWbVabNgpCM=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/josephspurrier/goversioninfo v1.4.0 h1:Puhl12NSHUSALHSuzYwPYQkqa2E1+7SrtAPJorKK0C8=
|
||||
github.com/josephspurrier/goversioninfo v1.4.0/go.mod h1:JWzv5rKQr+MmW+LvM412ToT/IkYDZjaclF2pKDss8IY=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/image v0.0.0-20210504121937-7319ad40d33e h1:PzJMNfFQx+QO9hrC1GwZ4BoPGeNGhfeQEgcQFArEjPk=
|
||||
@@ -17,7 +50,11 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9 h1:YTzHMGlqJu67/uEo1lBv0n3wBXhXNeUbB1XfN2vmTm0=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
65
log/log.go
65
log/log.go
@@ -1,10 +1,14 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"log"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Logger interface {
|
||||
WriteEntry(l LogEntry)
|
||||
}
|
||||
|
||||
type LogEntryType string
|
||||
|
||||
type LogEntry struct {
|
||||
@@ -19,28 +23,73 @@ const (
|
||||
LogTypeDebug = "debug"
|
||||
)
|
||||
|
||||
var LogEntries []LogEntry
|
||||
var loggers []Logger
|
||||
var logInput chan LogEntry
|
||||
var Memory *MemoryLogger
|
||||
|
||||
func init() {
|
||||
|
||||
// create some loggers
|
||||
Memory = &MemoryLogger{maxsize: 100}
|
||||
stdout := &StdoutLogger{}
|
||||
|
||||
loggers = []Logger{Memory, stdout}
|
||||
|
||||
// wait for log entries
|
||||
logInput = make(chan LogEntry)
|
||||
go func() {
|
||||
for {
|
||||
aLog := <-logInput
|
||||
LogEntries = append(LogEntries, aLog)
|
||||
for len(LogEntries) > 100 {
|
||||
LogEntries = LogEntries[1:]
|
||||
for _, l := range loggers {
|
||||
l.WriteEntry(aLog)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func SendLog(entry string, entryType LogEntryType) {
|
||||
func Debug(entry string) {
|
||||
logInput <- LogEntry{
|
||||
Timestamp: time.Now(),
|
||||
Entry: entry,
|
||||
Type: entryType,
|
||||
Type: LogTypeDebug,
|
||||
}
|
||||
}
|
||||
func Debugf(entry string, args ...interface{}) {
|
||||
logInput <- LogEntry{
|
||||
Timestamp: time.Now(),
|
||||
Entry: fmt.Sprintf(entry, args...),
|
||||
Type: LogTypeDebug,
|
||||
}
|
||||
}
|
||||
|
||||
func Info(entry string) {
|
||||
logInput <- LogEntry{
|
||||
Timestamp: time.Now(),
|
||||
Entry: entry,
|
||||
Type: LogTypeInfo,
|
||||
}
|
||||
}
|
||||
|
||||
func Infof(entry string, args ...interface{}) {
|
||||
logInput <- LogEntry{
|
||||
Timestamp: time.Now(),
|
||||
Entry: fmt.Sprintf(entry, args...),
|
||||
Type: LogTypeInfo,
|
||||
}
|
||||
}
|
||||
|
||||
func Error(entry string) {
|
||||
logInput <- LogEntry{
|
||||
Timestamp: time.Now(),
|
||||
Entry: entry,
|
||||
Type: LogTypeError,
|
||||
}
|
||||
}
|
||||
|
||||
func Errorf(entry string, args ...interface{}) {
|
||||
logInput <- LogEntry{
|
||||
Timestamp: time.Now(),
|
||||
Entry: fmt.Sprintf(entry, args...),
|
||||
Type: LogTypeError,
|
||||
}
|
||||
log.Printf("%6s: %s", entryType, entry)
|
||||
}
|
||||
|
||||
29
log/memory.go
Normal file
29
log/memory.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type MemoryLogger struct {
|
||||
size int
|
||||
entries []LogEntry
|
||||
maxsize int
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func (m *MemoryLogger) WriteEntry(l LogEntry) {
|
||||
// xxx needs mutex
|
||||
// if m.entries == nil {
|
||||
// m.entries = make([]LogEntry, 0)
|
||||
// }
|
||||
m.lock.Lock()
|
||||
m.entries = append(m.entries, l)
|
||||
if len(m.entries) > m.maxsize {
|
||||
m.entries = m.entries[1:]
|
||||
}
|
||||
m.lock.Unlock()
|
||||
}
|
||||
|
||||
func (m *MemoryLogger) Entries() []LogEntry {
|
||||
return m.entries
|
||||
}
|
||||
12
log/stdout.go
Normal file
12
log/stdout.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"log"
|
||||
)
|
||||
|
||||
type StdoutLogger struct {
|
||||
}
|
||||
|
||||
func (m StdoutLogger) WriteEntry(l LogEntry) {
|
||||
log.Printf("%-6s %s", l.Type, l.Entry)
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@@ -43,6 +44,7 @@ type HTTPClient interface {
|
||||
|
||||
type Uploader struct {
|
||||
Uploads []*Upload `json:"uploads"`
|
||||
Lock sync.Mutex
|
||||
}
|
||||
|
||||
type Upload struct {
|
||||
@@ -76,6 +78,7 @@ func NewUploader() *Uploader {
|
||||
}
|
||||
|
||||
func (u *Uploader) AddFile(file string, conf config.Watcher) {
|
||||
u.Lock.Lock()
|
||||
atomic.AddInt32(¤tId, 1)
|
||||
thisUpload := Upload{
|
||||
Id: currentId,
|
||||
@@ -91,19 +94,27 @@ func (u *Uploader) AddFile(file string, conf config.Watcher) {
|
||||
thisUpload.State = StatePending
|
||||
}
|
||||
u.Uploads = append(u.Uploads, &thisUpload)
|
||||
u.Lock.Unlock()
|
||||
|
||||
}
|
||||
|
||||
// Upload uploads any files that have not yet been uploaded
|
||||
func (u *Uploader) Upload() {
|
||||
u.Lock.Lock()
|
||||
|
||||
for _, upload := range u.Uploads {
|
||||
if upload.State == StateQueued {
|
||||
upload.processUpload()
|
||||
}
|
||||
}
|
||||
u.Lock.Unlock()
|
||||
|
||||
}
|
||||
|
||||
func (u *Uploader) UploadById(id int32) *Upload {
|
||||
u.Lock.Lock()
|
||||
defer u.Lock.Unlock()
|
||||
|
||||
for _, anUpload := range u.Uploads {
|
||||
if anUpload.Id == int32(id) {
|
||||
return anUpload
|
||||
@@ -119,19 +130,19 @@ func (u *Upload) RemoveMarkupTempFile() {
|
||||
}
|
||||
|
||||
func (u *Upload) processUpload() error {
|
||||
daulog.SendLog(fmt.Sprintf("Uploading: %s", u.OriginalFilename), daulog.LogTypeInfo)
|
||||
daulog.Infof("Uploading: %s", u.OriginalFilename)
|
||||
|
||||
baseFilename := filepath.Base(u.OriginalFilename)
|
||||
|
||||
if u.webhookURL == "" {
|
||||
daulog.SendLog("WebHookURL is not configured - cannot upload!", daulog.LogTypeError)
|
||||
daulog.Error("WebHookURL is not configured - cannot upload!")
|
||||
return errors.New("webhook url not configured")
|
||||
}
|
||||
|
||||
extraParams := map[string]string{}
|
||||
|
||||
if u.usernameOverride != "" {
|
||||
daulog.SendLog("Overriding username with "+u.usernameOverride, daulog.LogTypeInfo)
|
||||
daulog.Infof("Overriding username with '%s'", u.usernameOverride)
|
||||
extraParams["username"] = u.usernameOverride
|
||||
}
|
||||
|
||||
@@ -158,7 +169,7 @@ func (u *Upload) processUpload() error {
|
||||
if len(u.MarkedUpFilename) > 0 {
|
||||
filedata, err = os.Open(u.MarkedUpFilename)
|
||||
if err != nil {
|
||||
log.Print("Error opening marked up file:", err)
|
||||
daulog.Errorf("Error opening marked up file: %s", err)
|
||||
retriesRemaining--
|
||||
sleepForRetries(retriesRemaining)
|
||||
continue
|
||||
@@ -166,7 +177,7 @@ func (u *Upload) processUpload() error {
|
||||
} else {
|
||||
filedata, err = os.Open(u.OriginalFilename)
|
||||
if err != nil {
|
||||
log.Print("Error opening original file:", err)
|
||||
daulog.Errorf("Error opening original file: %s", err)
|
||||
retriesRemaining--
|
||||
sleepForRetries(retriesRemaining)
|
||||
continue
|
||||
@@ -175,10 +186,10 @@ func (u *Upload) processUpload() error {
|
||||
|
||||
var imageData io.Reader
|
||||
if u.watermark {
|
||||
daulog.SendLog("Watermarking image", daulog.LogTypeInfo)
|
||||
daulog.Info("Watermarking image")
|
||||
imageData, err = u.applyWatermark(filedata)
|
||||
if err != nil {
|
||||
log.Print("Error watermarking:", err)
|
||||
daulog.Errorf("Error watermarking: %s", err)
|
||||
retriesRemaining--
|
||||
sleepForRetries(retriesRemaining)
|
||||
continue
|
||||
@@ -189,7 +200,7 @@ func (u *Upload) processUpload() error {
|
||||
|
||||
request, err := newfileUploadRequest(u.webhookURL, extraParams, "file", baseFilename, imageData)
|
||||
if err != nil {
|
||||
log.Printf("error creating upload request: %s", err)
|
||||
daulog.Errorf("error creating upload request: %s", err)
|
||||
return fmt.Errorf("could not create upload request: %s", err)
|
||||
}
|
||||
start := time.Now()
|
||||
@@ -202,24 +213,23 @@ func (u *Upload) processUpload() error {
|
||||
|
||||
resp, err := u.Client.Do(request)
|
||||
if err != nil {
|
||||
log.Print("Error performing request:", err)
|
||||
daulog.Errorf("Error performing request: %s", err)
|
||||
retriesRemaining--
|
||||
sleepForRetries(retriesRemaining)
|
||||
continue
|
||||
} else {
|
||||
if resp.StatusCode == 413 {
|
||||
// just fail immediately, we know this means the file was too big
|
||||
daulog.SendLog("413 received - file too large", daulog.LogTypeError)
|
||||
daulog.Error("413 received - file too large")
|
||||
u.State = StateFailed
|
||||
return errors.New("received 413 - file too large")
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
// {"message": "Request entity too large", "code": 40005}
|
||||
log.Print("Bad response from server:", resp.StatusCode)
|
||||
daulog.Errorf("Bad response code from server: %d", resp.StatusCode)
|
||||
if b, err := ioutil.ReadAll(resp.Body); err == nil {
|
||||
log.Print("Body:", string(b))
|
||||
daulog.SendLog(fmt.Sprintf("Bad response: %s", string(b)), daulog.LogTypeError)
|
||||
daulog.Errorf("Body:\n%s", string(b))
|
||||
}
|
||||
retriesRemaining--
|
||||
sleepForRetries(retriesRemaining)
|
||||
@@ -228,7 +238,7 @@ func (u *Upload) processUpload() error {
|
||||
|
||||
resBody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Print("could not deal with body: ", err)
|
||||
daulog.Errorf("could not deal with body: %s", err)
|
||||
retriesRemaining--
|
||||
sleepForRetries(retriesRemaining)
|
||||
continue
|
||||
@@ -240,17 +250,17 @@ func (u *Upload) processUpload() error {
|
||||
|
||||
// {"id": "851092588608880670", "type": 0, "content": "", "channel_id": "849615269706203171", "author": {"bot": true, "id": "849615314274484224", "username": "abcdedf", "avatar": null, "discriminator": "0000"}, "attachments": [{"id": "851092588332449812", "filename": "dau480457962.png", "size": 859505, "url": "https://cdn.discordapp.com/attachments/849615269706203171/851092588332449812/dau480457962.png", "proxy_url": "https://media.discordapp.net/attachments/849615269706203171/851092588332449812/dau480457962.png", "width": 640, "height": 640, "content_type": "image/png"}], "embeds": [], "mentions": [], "mention_roles": [], "pinned": false, "mention_everyone": false, "tts": false, "timestamp": "2021-06-06T13:38:05.660000+00:00", "edited_timestamp": null, "flags": 0, "components": [], "webhook_id": "849615314274484224"}
|
||||
|
||||
daulog.SendLog(fmt.Sprintf("Response: %s", string(resBody[:])), daulog.LogTypeDebug)
|
||||
daulog.Debugf("Response: %s", string(resBody[:]))
|
||||
|
||||
if err != nil {
|
||||
log.Print("could not parse JSON: ", err)
|
||||
fmt.Println("Response was:", string(resBody[:]))
|
||||
daulog.Errorf("could not parse JSON: %s", err)
|
||||
daulog.Errorf("Response was: %s", string(resBody[:]))
|
||||
retriesRemaining--
|
||||
sleepForRetries(retriesRemaining)
|
||||
continue
|
||||
}
|
||||
if len(res.Attachments) < 1 {
|
||||
log.Print("bad response - no attachments?")
|
||||
daulog.Error("bad response - no attachments?")
|
||||
retriesRemaining--
|
||||
sleepForRetries(retriesRemaining)
|
||||
continue
|
||||
@@ -259,8 +269,8 @@ func (u *Upload) processUpload() error {
|
||||
elapsed := time.Since(start)
|
||||
rate := float64(a.Size) / elapsed.Seconds() / 1024.0
|
||||
|
||||
daulog.SendLog(fmt.Sprintf("Uploaded to %s %dx%d", a.URL, a.Width, a.Height), daulog.LogTypeInfo)
|
||||
daulog.SendLog(fmt.Sprintf("id: %d, %d bytes transferred in %.2f seconds (%.2f KiB/s)", res.ID, a.Size, elapsed.Seconds(), rate), daulog.LogTypeInfo)
|
||||
daulog.Infof("Uploaded to %s %dx%d", a.URL, a.Width, a.Height)
|
||||
daulog.Infof("id: %d, %d bytes transferred in %.2f seconds (%.2f KiB/s)", res.ID, a.Size, elapsed.Seconds(), rate)
|
||||
|
||||
u.Url = a.URL
|
||||
u.State = StateComplete
|
||||
@@ -276,7 +286,7 @@ func (u *Upload) processUpload() error {
|
||||
u.RemoveMarkupTempFile()
|
||||
|
||||
if retriesRemaining == 0 {
|
||||
daulog.SendLog("Failed to upload, even after all retries", daulog.LogTypeError)
|
||||
daulog.Error("Failed to upload, even after all retries")
|
||||
u.State = StateFailed
|
||||
return errors.New("could not upload after all retries")
|
||||
}
|
||||
@@ -317,7 +327,7 @@ func (u *Upload) applyWatermark(in *os.File) (io.Reader, error) {
|
||||
|
||||
im, _, err := image.Decode(in)
|
||||
if err != nil {
|
||||
daulog.SendLog(fmt.Sprintf("Cannot decode image: %v - skipping watermarking", err), daulog.LogTypeError)
|
||||
daulog.Errorf("Cannot decode image: %v - skipping watermarking", err)
|
||||
return nil, errors.New("cannot decode image")
|
||||
}
|
||||
bounds := im.Bounds()
|
||||
@@ -349,6 +359,6 @@ func sleepForRetries(retry int) {
|
||||
return
|
||||
}
|
||||
retryTime := (6-retry)*(6-retry) + 6
|
||||
daulog.SendLog(fmt.Sprintf("Will retry in %d seconds (%d remaining attempts)", retryTime, retry), daulog.LogTypeError)
|
||||
daulog.Errorf("Will retry in %d seconds (%d remaining attempts)", retryTime, retry)
|
||||
time.Sleep(time.Duration(retryTime) * time.Second)
|
||||
}
|
||||
|
||||
@@ -1,24 +1,47 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
daulog "github.com/tardisx/discord-auto-upload/log"
|
||||
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
const CurrentVersion string = "v0.12.0"
|
||||
const CurrentVersion string = "v0.12.3"
|
||||
|
||||
func NewVersionAvailable(v string) bool {
|
||||
type GithubRelease struct {
|
||||
HTMLURL string `json:"html_url"`
|
||||
TagName string `json:"tag_name"`
|
||||
Name string `json:"name"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
var LatestVersion string
|
||||
var LatestVersionInfo GithubRelease
|
||||
|
||||
// UpdateAvailable returns true or false, depending on whether not a new version is available.
|
||||
// It always returns false if the OnlineVersion has not yet been fetched.
|
||||
func UpdateAvailable() bool {
|
||||
if !semver.IsValid(CurrentVersion) {
|
||||
panic(fmt.Sprintf("my current version '%s' is not valid", CurrentVersion))
|
||||
}
|
||||
if !semver.IsValid(v) {
|
||||
// maybe this should just be a warning
|
||||
log.Printf("passed in version '%s' is not valid - assuming no new version", v)
|
||||
|
||||
if LatestVersion == "" {
|
||||
return false
|
||||
}
|
||||
comp := semver.Compare(v, CurrentVersion)
|
||||
|
||||
if !semver.IsValid(LatestVersion) {
|
||||
// maybe this should just be a warning
|
||||
daulog.Errorf("online version '%s' is not valid - assuming no new version", LatestVersion)
|
||||
return false
|
||||
}
|
||||
comp := semver.Compare(LatestVersion, CurrentVersion)
|
||||
if comp == 0 {
|
||||
return false
|
||||
}
|
||||
@@ -27,3 +50,32 @@ func NewVersionAvailable(v string) bool {
|
||||
}
|
||||
return false // they are using a newer one than exists?
|
||||
}
|
||||
|
||||
func GetOnlineVersion() {
|
||||
|
||||
daulog.Info("checking for new version")
|
||||
|
||||
client := &http.Client{Timeout: time.Second * 5}
|
||||
resp, err := client.Get("https://api.github.com/repos/tardisx/discord-auto-upload/releases/latest")
|
||||
if err != nil {
|
||||
daulog.Errorf("WARNING: Update check failed: %s", err)
|
||||
return
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
LatestVersion = latest.TagName
|
||||
LatestVersionInfo = latest
|
||||
daulog.Debugf("Latest version: %s", LatestVersion)
|
||||
|
||||
}
|
||||
|
||||
@@ -4,8 +4,18 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestVersioning(t *testing.T) {
|
||||
if !NewVersionAvailable("v1.0.0") {
|
||||
t.Error("should be a version newer than v1.0.0")
|
||||
func TestVersioningUpdate(t *testing.T) {
|
||||
// pretend there is a new version
|
||||
LatestVersion = "v0.13.0"
|
||||
if !UpdateAvailable() {
|
||||
t.Error("should be a version newer than " + CurrentVersion)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersioningNoUpdate(t *testing.T) {
|
||||
// pretend there is a new version
|
||||
LatestVersion = "v0.12.1"
|
||||
if UpdateAvailable() {
|
||||
t.Error("should NOT be a version newer than " + CurrentVersion)
|
||||
}
|
||||
}
|
||||
|
||||
43
versioninfo.json-template
Normal file
43
versioninfo.json-template
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"FixedFileInfo": {
|
||||
"FileVersion": {
|
||||
"Major": 0,
|
||||
"Minor": 0,
|
||||
"Patch": 0,
|
||||
"Build": 0
|
||||
},
|
||||
"ProductVersion": {
|
||||
"Major": 0,
|
||||
"Minor": 0,
|
||||
"Patch": 0,
|
||||
"Build": 0
|
||||
},
|
||||
"FileFlagsMask": "3f",
|
||||
"FileFlags ": "00",
|
||||
"FileOS": "040004",
|
||||
"FileType": "01",
|
||||
"FileSubType": "00"
|
||||
},
|
||||
"StringFileInfo": {
|
||||
"Comments": "",
|
||||
"CompanyName": "tardisx@github",
|
||||
"FileDescription": "https://github.com/tardisx/discord-auto-upload",
|
||||
"FileVersion": "",
|
||||
"InternalName": "",
|
||||
"LegalCopyright": "https://github.com/tardisx/discord-auto-upload/blob/master/LICENSE",
|
||||
"LegalTrademarks": "",
|
||||
"OriginalFilename": "",
|
||||
"PrivateBuild": "",
|
||||
"ProductName": "discord-auto-upload",
|
||||
"ProductVersion": "v0.0.0",
|
||||
"SpecialBuild": ""
|
||||
},
|
||||
"VarFileInfo": {
|
||||
"Translation": {
|
||||
"LangID": "0409",
|
||||
"CharsetID": "04B0"
|
||||
}
|
||||
},
|
||||
"IconPath": "dau.ico",
|
||||
"ManifestPath": ""
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
</p>
|
||||
|
||||
<p>The Watch Interval is how often new files will be discovered by your
|
||||
watchers (configured below).</p>
|
||||
watchers (watchers are configured below).</p>
|
||||
|
||||
<div class="form-row align-items-center">
|
||||
<div class="col-sm-6 my-1">
|
||||
@@ -33,6 +33,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row align-items-center">
|
||||
<div class="col-sm-6 my-1">
|
||||
<span>Open browser on startup</span>
|
||||
</div>
|
||||
<div class="col-sm-6 my-1">
|
||||
<label class="sr-only">Open browser</label>
|
||||
<button type="button" @click="config.OpenBrowserOnStart = ! config.OpenBrowserOnStart" class="btn btn-success" x-text="config.OpenBrowserOnStart ? 'Enabled' : 'Disabled'"></button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row align-items-center">
|
||||
<div class="col-sm-6 my-1">
|
||||
<span>Watch interval</span>
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
{{ define "content" }}
|
||||
|
||||
<main role="main" class="inner DAU">
|
||||
<main role="main" class="inner DAU" x-data="logs()" x-init="get_logs()">
|
||||
<h1 class="DAU-heading">Logs</h1>
|
||||
<p class="lead">Discord-auto-upload logs</p>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<button type="button" onClick="debug=1; get_logs();" class="btn btn-primary">all logs</button>
|
||||
<button type="button" @click="debug = !debug" class="btn btn-primary" x-text="debug ? 'debug' : 'no debug'"></button>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<button type="button" onClick="debug=0; get_logs();" class="btn btn-primary">no debug</button>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<button type="button" id="scroll-button" onClick="toggle_scroll();" class="btn btn-primary">disable auto-scroll</button>
|
||||
<button type="button" @click="scroll = !scroll" class="btn btn-primary" x-text="scroll ? 'auto-scroll' : 'no scroll'"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<pre id="logs" class="text-left pre-scrollable">
|
||||
<pre id="logs" x-text="text" class="text-left pre-scrollable">
|
||||
</pre>
|
||||
</main>
|
||||
|
||||
@@ -27,35 +24,24 @@
|
||||
{{ define "js" }}
|
||||
|
||||
<script>
|
||||
var debug = 0;
|
||||
var scrl = true;
|
||||
$(document).ready(function() {
|
||||
get_logs();
|
||||
setInterval(function() { get_logs(); }, 1000);
|
||||
});
|
||||
|
||||
function toggle_scroll() {
|
||||
scrl = !scrl;
|
||||
if (scrl) {
|
||||
$('#scroll-button').text('disable auto-scroll');
|
||||
function logs() {
|
||||
return {
|
||||
text: '', scroll: true, debug: false,
|
||||
get_logs() {
|
||||
fetch('/rest/logs?' + new URLSearchParams({ debug: this.debug ? "1" : "0" }))
|
||||
.then(response => response.text())
|
||||
.then(text => {
|
||||
console.log(text);
|
||||
this.text = text;
|
||||
if (this.scroll) {
|
||||
document.getElementById('logs').scrollTop =10000;
|
||||
}
|
||||
let self = this;
|
||||
setTimeout(function() { self.get_logs(); }, 1000)
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
else {
|
||||
$('#scroll-button').text('auto-scroll');
|
||||
}
|
||||
}
|
||||
|
||||
function get_logs() {
|
||||
$.ajax({ method: 'get', url: '/rest/logs', data: { debug : debug }})
|
||||
.done(function(data) {
|
||||
$('#logs').text(data);
|
||||
console.log('scrl is ', scrl);
|
||||
if (scrl) {
|
||||
$('#logs').scrollTop(10000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
{{ end }}
|
||||
@@ -42,6 +42,10 @@
|
||||
<a class="nav-link {{ if eq .Path "config.html"}} active {{ end }}" href="/config.html">Config</a>
|
||||
<a class="nav-link {{ if eq .Path "uploads.html"}} active {{ end }}" href="/uploads.html">Uploads</a>
|
||||
<a class="nav-link {{ if eq .Path "logs.html"}} active {{ end }}" href="/logs.html">Logs</a>
|
||||
{{ if eq .NewVersionAvailable true }}
|
||||
<a class="nav-link" href="{{ .NewVersionInfo.HTMLURL }}">Ver {{ .NewVersionInfo.TagName }} available!</a>
|
||||
{{ end }}
|
||||
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -66,7 +66,7 @@ func (ws *WebService) getStatic(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
t, err := template.ParseFS(webFS, "data/wrapper.tmpl", "data/"+path)
|
||||
if err != nil {
|
||||
daulog.SendLog(fmt.Sprintf("when fetching: %s got: %s", path, err), daulog.LogTypeError)
|
||||
daulog.Errorf("when fetching: %s got: %s", path, err)
|
||||
w.Header().Add("Content-Type", "text/plain")
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte("not found"))
|
||||
@@ -74,12 +74,16 @@ func (ws *WebService) getStatic(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
var b struct {
|
||||
Body string
|
||||
Path string
|
||||
Version string
|
||||
Body string
|
||||
Path string
|
||||
Version string
|
||||
NewVersionAvailable bool
|
||||
NewVersionInfo version.GithubRelease
|
||||
}
|
||||
b.Path = path
|
||||
b.Version = version.CurrentVersion
|
||||
b.NewVersionAvailable = version.UpdateAvailable()
|
||||
b.NewVersionInfo = version.LatestVersionInfo
|
||||
|
||||
err = t.ExecuteTemplate(w, "layout", b)
|
||||
if err != nil {
|
||||
@@ -90,7 +94,7 @@ func (ws *WebService) getStatic(w http.ResponseWriter, r *http.Request) {
|
||||
otherStatic, err := webFS.ReadFile("data/" + path)
|
||||
|
||||
if err != nil {
|
||||
daulog.SendLog(fmt.Sprintf("when fetching: %s got: %s", path, err), daulog.LogTypeError)
|
||||
daulog.Errorf("when fetching: %s got: %s", path, err)
|
||||
w.Header().Add("Content-Type", "text/plain")
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte("not found"))
|
||||
@@ -114,7 +118,7 @@ func (ws *WebService) getLogs(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
text := ""
|
||||
for _, log := range daulog.LogEntries {
|
||||
for _, log := range daulog.Memory.Entries() {
|
||||
if !showDebug && log.Type == daulog.LogTypeDebug {
|
||||
continue
|
||||
}
|
||||
@@ -123,14 +127,13 @@ func (ws *WebService) getLogs(w http.ResponseWriter, r *http.Request) {
|
||||
)
|
||||
}
|
||||
|
||||
// js, _ := json.Marshal(daulog.LogEntries)
|
||||
w.Write([]byte(text))
|
||||
}
|
||||
|
||||
func (ws *WebService) handleConfig(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "POST" {
|
||||
|
||||
newConfig := config.ConfigV2{}
|
||||
newConfig := config.ConfigV3{}
|
||||
|
||||
defer r.Body.Close()
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
@@ -343,12 +346,11 @@ func (ws *WebService) StartWebServer() {
|
||||
|
||||
go func() {
|
||||
listen := fmt.Sprintf(":%d", ws.Config.Config.Port)
|
||||
log.Printf("Starting web server on http://localhost%s", listen)
|
||||
daulog.Infof("Starting web server on http://localhost%s", listen)
|
||||
|
||||
srv := &http.Server{
|
||||
Handler: r,
|
||||
Addr: listen,
|
||||
// Good practice: enforce timeouts for servers you create!
|
||||
Handler: r,
|
||||
Addr: listen,
|
||||
WriteTimeout: 15 * time.Second,
|
||||
ReadTimeout: 15 * time.Second,
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ func TestGetConfig(t *testing.T) {
|
||||
t.Errorf("expected error to be nil got %v", err)
|
||||
}
|
||||
|
||||
exp := `{"WatchInterval":10,"Version":2,"Port":9090,"Watchers":[{"WebHookURL":"https://webhook.url.here","Path":"/your/screenshot/dir/here","Username":"","NoWatermark":false,"HoldUploads":false,"Exclude":[]}]}`
|
||||
exp := `{"WatchInterval":10,"Version":3,"Port":9090,"OpenBrowserOnStart":true,"Watchers":[{"WebHookURL":"https://webhook.url.here","Path":"/your/screenshot/dir/here","Username":"","NoWatermark":false,"HoldUploads":false,"Exclude":[]}]}`
|
||||
if string(b) != exp {
|
||||
t.Errorf("Got unexpected response\n%v\n%v", string(b), exp)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user