2017-02-20 21:16:44 +10:30
package main
2020-03-20 05:49:12 +10:30
//go:generate go-bindata -pkg assets -o assets/static.go -prefix data/ data
2017-02-20 21:16:44 +10:30
import (
2017-02-22 21:13:07 +10:30
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"mime/multipart"
"net/http"
"os"
"path/filepath"
"strings"
"time"
2017-02-26 21:06:48 +10:30
"image"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
"github.com/fogleman/gg"
2017-02-22 21:13:07 +10:30
"github.com/pborman/getopt"
2021-06-02 23:42:29 +09:30
2021-01-31 17:53:32 +10:30
// "github.com/skratchdot/open-golang/open"
2017-02-26 21:06:48 +10:30
"golang.org/x/image/font/inconsolata"
2017-07-26 14:24:02 +09:30
2021-01-31 17:53:32 +10:30
"github.com/tardisx/discord-auto-upload/config"
2021-06-03 19:36:48 +09:30
daulog "github.com/tardisx/discord-auto-upload/log"
2021-01-31 18:48:48 +10:30
"github.com/tardisx/discord-auto-upload/web"
2017-02-20 21:16:44 +10:30
)
2021-01-31 18:48:48 +10:30
var lastCheck = time . Now ( )
2017-02-22 21:13:07 +10:30
var newLastCheck = time . Now ( )
2017-02-21 12:24:14 +10:30
2017-02-20 21:16:44 +10:30
func main ( ) {
2017-02-22 21:13:07 +10:30
2021-01-31 17:53:32 +10:30
parseOptions ( )
2017-07-26 14:24:02 +09:30
2021-01-31 17:53:32 +10:30
// log.Print("Opening web browser")
// open.Start("http://localhost:9090")
2021-06-02 23:42:29 +09:30
web . StartWebServer ( )
2017-07-27 22:04:47 +09:30
2017-07-26 22:40:21 +09:30
checkUpdates ( )
2021-06-03 19:36:48 +09:30
daulog . SendLog ( fmt . Sprintf ( "Waiting for images to appear in %s" , config . Config . Path ) , daulog . LogTypeInfo )
2017-02-22 21:13:07 +10:30
// wander the path, forever
for {
2021-02-07 11:42:13 +10:30
if checkPath ( config . Config . Path ) {
err := filepath . Walk ( config . Config . Path ,
func ( path string , f os . FileInfo , err error ) error { return checkFile ( path , f , err ) } )
if err != nil {
log . Fatal ( "could not watch path" , err )
}
lastCheck = newLastCheck
2017-02-22 21:13:07 +10:30
}
2021-06-03 19:36:48 +09:30
daulog . SendLog ( fmt . Sprintf ( "sleeping for %ds before next check of %s" , config . Config . Watch , config . Config . Path ) , daulog . LogTypeDebug )
2021-01-31 17:53:32 +10:30
time . Sleep ( time . Duration ( config . Config . Watch ) * time . Second )
2017-07-27 12:18:02 +09:30
}
}
2021-02-07 11:42:13 +10:30
func checkPath ( path string ) bool {
2017-02-22 21:13:07 +10:30
src , err := os . Stat ( path )
if err != nil {
2021-02-07 22:06:19 +10:30
log . Printf ( "Problem with path '%s': %s" , path , err )
2021-02-07 11:42:13 +10:30
return false
2017-02-22 21:13:07 +10:30
}
if ! src . IsDir ( ) {
2021-02-07 22:06:19 +10:30
log . Printf ( "Problem with path '%s': is not a directory" , path )
2021-02-07 11:42:13 +10:30
return false
2017-02-22 21:13:07 +10:30
}
2021-02-07 11:42:13 +10:30
return true
2017-02-21 17:10:00 +10:30
}
2017-02-22 21:13:07 +10:30
func checkUpdates ( ) {
type GithubRelease struct {
HTMLURL string
TagName string
Name string
Body string
}
2021-06-03 19:36:48 +09:30
daulog . SendLog ( "checking for new version" , daulog . LogTypeInfo )
2021-06-02 23:42:29 +09:30
2017-02-22 21:13:07 +10:30
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 {
2021-06-03 19:36:48 +09:30
daulog . SendLog ( fmt . Sprintf ( "WARNING: Update check failed: %v" , err ) , daulog . LogTypeError )
2017-07-26 22:44:56 +09:30
return
2017-02-22 21:13:07 +10:30
}
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 )
}
2021-01-31 17:53:32 +10:30
if config . CurrentVersion < latest . TagName {
fmt . Printf ( "You are currently on version %s, but version %s is available\n" , config . CurrentVersion , latest . TagName )
2017-02-22 21:13:07 +10:30
fmt . Println ( "----------- Release Info -----------" )
fmt . Println ( latest . Body )
fmt . Println ( "------------------------------------" )
2017-02-28 22:50:03 +10:30
fmt . Println ( "Upgrade at https://github.com/tardisx/discord-auto-upload/releases/latest" )
2021-06-03 19:36:48 +09:30
daulog . SendLog ( fmt . Sprintf ( "New version available: %s - download at https://github.com/tardisx/discord-auto-upload/releases/latest" ) , daulog . LogTypeInfo )
2017-02-22 21:13:07 +10:30
}
2017-02-21 11:15:12 +10:30
}
2021-01-31 17:53:32 +10:30
func parseOptions ( ) {
2017-02-20 21:16:44 +10:30
2017-02-22 21:13:07 +10:30
// Declare the flags to be used
helpFlag := getopt . BoolLong ( "help" , 'h' , "help" )
versionFlag := getopt . BoolLong ( "version" , 'v' , "show version" )
getopt . SetParameters ( "" )
2017-02-20 21:16:44 +10:30
2017-02-22 21:13:07 +10:30
getopt . Parse ( )
2017-02-20 21:16:44 +10:30
2017-02-22 21:13:07 +10:30
if * helpFlag {
getopt . PrintUsage ( os . Stderr )
os . Exit ( 0 )
}
2017-02-21 12:28:26 +10:30
2017-02-22 21:13:07 +10:30
if * versionFlag {
fmt . Println ( "dau - https://github.com/tardisx/discord-auto-upload" )
2021-01-31 17:53:32 +10:30
fmt . Printf ( "Version: %s\n" , config . CurrentVersion )
2017-02-22 21:13:07 +10:30
os . Exit ( 0 )
}
2017-02-21 12:24:14 +10:30
2021-02-07 11:42:13 +10:30
// grab the config
config . LoadOrInit ( )
2017-02-20 21:16:44 +10:30
}
2021-01-31 17:53:32 +10:30
func checkFile ( path string , f os . FileInfo , err error ) error {
2017-02-22 21:13:07 +10:30
if f . ModTime ( ) . After ( lastCheck ) && f . Mode ( ) . IsRegular ( ) {
2017-02-20 21:16:44 +10:30
2021-01-31 17:53:32 +10:30
if fileEligible ( path ) {
2017-02-22 21:13:07 +10:30
// process file
2021-01-31 17:53:32 +10:30
processFile ( path )
2017-02-22 21:13:07 +10:30
}
2017-02-20 21:16:44 +10:30
2017-02-22 21:13:07 +10:30
if newLastCheck . Before ( f . ModTime ( ) ) {
newLastCheck = f . ModTime ( )
}
}
2017-02-20 21:16:44 +10:30
2017-02-22 21:13:07 +10:30
return nil
2017-02-20 21:16:44 +10:30
}
2021-01-31 17:53:32 +10:30
func fileEligible ( file string ) bool {
2017-02-28 22:50:03 +10:30
2021-01-31 17:53:32 +10:30
if config . Config . Exclude != "" && strings . Contains ( file , config . Config . Exclude ) {
2017-02-28 22:50:03 +10:30
return false
}
2017-02-22 21:13:07 +10:30
extension := strings . ToLower ( filepath . Ext ( file ) )
if extension == ".png" || extension == ".jpg" || extension == ".gif" {
return true
}
2017-02-28 22:50:03 +10:30
2017-02-22 21:13:07 +10:30
return false
2017-02-20 21:16:44 +10:30
}
2021-01-31 17:53:32 +10:30
func processFile ( file string ) {
2017-02-26 21:06:48 +10:30
2021-01-31 17:53:32 +10:30
if ! config . Config . NoWatermark {
2021-06-03 19:36:48 +09:30
daulog . SendLog ( "Copying to temp location and watermarking " , daulog . LogTypeInfo )
2017-02-28 21:32:18 +10:30
file = mungeFile ( file )
}
2017-02-26 21:06:48 +10:30
2021-02-07 11:42:13 +10:30
if config . Config . WebHookURL == "" {
2021-06-03 19:36:48 +09:30
daulog . SendLog ( "WebHookURL is not configured - cannot upload!" , daulog . LogTypeError )
2021-02-07 11:42:13 +10:30
return
}
2021-06-03 19:36:48 +09:30
daulog . SendLog ( "Uploading" , daulog . LogTypeInfo )
2017-02-22 21:13:07 +10:30
extraParams := map [ string ] string { }
2021-01-31 17:53:32 +10:30
if config . Config . Username != "" {
log . Print ( "Overriding username with " + config . Config . Username )
extraParams [ "username" ] = config . Config . Username
2017-02-22 21:13:07 +10:30
}
type DiscordAPIResponseAttachment struct {
URL string
ProxyURL string
Size int
Width int
Height int
Filename string
}
type DiscordAPIResponse struct {
Attachments [ ] DiscordAPIResponseAttachment
ID int64 ` json:",string" `
}
2017-02-28 22:07:57 +10:30
var retriesRemaining = 5
for retriesRemaining > 0 {
2020-03-26 11:40:35 +10:30
2021-01-31 17:53:32 +10:30
request , err := newfileUploadRequest ( config . Config . WebHookURL , extraParams , "file" , file )
2017-02-22 21:13:07 +10:30
if err != nil {
2017-02-28 22:07:57 +10:30
log . Fatal ( err )
2017-02-22 21:13:07 +10:30
}
2017-02-28 22:07:57 +10:30
start := time . Now ( )
client := & http . Client { Timeout : time . Second * 30 }
resp , err := client . Do ( request )
2017-02-22 21:13:07 +10:30
if err != nil {
2017-02-28 22:07:57 +10:30
log . Print ( "Error performing request:" , err )
retriesRemaining --
sleepForRetries ( retriesRemaining )
continue
} else {
if resp . StatusCode != 200 {
log . Print ( "Bad response from server:" , resp . StatusCode )
2020-03-26 11:40:35 +10:30
if b , err := ioutil . ReadAll ( resp . Body ) ; err == nil {
log . Print ( "Body:" , string ( b ) )
2021-01-31 18:48:48 +10:30
}
2017-02-28 22:07:57 +10:30
retriesRemaining --
sleepForRetries ( retriesRemaining )
continue
}
resBody , err := ioutil . ReadAll ( resp . Body )
if err != nil {
log . Print ( "could not deal with body: " , err )
retriesRemaining --
sleepForRetries ( retriesRemaining )
continue
}
resp . Body . Close ( )
var res DiscordAPIResponse
err = json . Unmarshal ( resBody , & res )
if err != nil {
log . Print ( "could not parse JSON: " , err )
fmt . Println ( "Response was:" , string ( resBody [ : ] ) )
retriesRemaining --
sleepForRetries ( retriesRemaining )
continue
}
if len ( res . Attachments ) < 1 {
log . Print ( "bad response - no attachments?" )
retriesRemaining --
sleepForRetries ( retriesRemaining )
continue
}
var a = res . Attachments [ 0 ]
elapsed := time . Since ( start )
rate := float64 ( a . Size ) / elapsed . Seconds ( ) / 1024.0
log . Printf ( "Uploaded to %s %dx%d" , a . URL , a . Width , a . Height )
log . Printf ( "id: %d, %d bytes transferred in %.2f seconds (%.2f KiB/s)" , res . ID , a . Size , elapsed . Seconds ( ) , rate )
break
2017-02-22 21:13:07 +10:30
}
2017-02-28 22:07:57 +10:30
}
2017-02-22 21:13:07 +10:30
2021-01-31 17:53:32 +10:30
if ! config . Config . NoWatermark {
2021-06-03 19:36:48 +09:30
daulog . SendLog ( fmt . Sprintf ( "Removing temporary file: %s" , file ) , daulog . LogTypeDebug )
2017-02-28 22:07:57 +10:30
os . Remove ( file )
}
2017-02-28 21:32:18 +10:30
2017-02-28 22:07:57 +10:30
if retriesRemaining == 0 {
log . Fatal ( "Failed to upload, even after retries" )
2017-02-22 21:13:07 +10:30
}
2017-02-28 22:07:57 +10:30
}
2017-02-21 11:15:12 +10:30
2017-02-28 22:07:57 +10:30
func sleepForRetries ( retry int ) {
if retry == 0 {
return
}
retryTime := ( 6 - retry ) * ( 6 - retry ) + 6
2021-06-03 19:36:48 +09:30
daulog . SendLog ( fmt . Sprintf ( "Will retry in %d seconds (%d remaining attempts)" , retryTime , retry ) , daulog . LogTypeError )
2021-02-07 22:06:19 +10:30
time . Sleep ( time . Duration ( retryTime ) * time . Second )
2017-02-20 21:54:16 +10:30
}
func newfileUploadRequest ( uri string , params map [ string ] string , paramName , path string ) ( * http . Request , error ) {
2017-02-22 21:13:07 +10:30
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 )
if err != nil {
log . Fatal ( "Could not copy: " , err )
}
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
2017-02-20 21:16:44 +10:30
}
2017-02-26 21:06:48 +10:30
2017-02-28 21:32:18 +10:30
func mungeFile ( path string ) string {
2017-02-26 21:06:48 +10:30
reader , err := os . Open ( path )
if err != nil {
log . Fatal ( err )
}
defer reader . Close ( )
im , _ , err := image . Decode ( reader )
if err != nil {
log . Fatal ( err )
}
bounds := im . Bounds ( )
// var S float64 = float64(bounds.Max.X)
dc := gg . NewContext ( bounds . Max . X , bounds . Max . Y )
dc . Clear ( )
dc . SetRGB ( 0 , 0 , 0 )
dc . SetFontFace ( inconsolata . Regular8x16 )
dc . DrawImage ( im , 0 , 0 )
dc . DrawRoundedRectangle ( 0 , float64 ( bounds . Max . Y - 18.0 ) , 320 , float64 ( bounds . Max . Y ) , 0 )
dc . SetRGB ( 0 , 0 , 0 )
dc . Fill ( )
dc . SetRGB ( 1 , 1 , 1 )
dc . DrawString ( "github.com/tardisx/discord-auto-upload" , 5.0 , float64 ( bounds . Max . Y ) - 5.0 )
2017-02-28 21:32:18 +10:30
tempfile , err := ioutil . TempFile ( "" , "dau" )
if err != nil {
log . Fatal ( err )
}
tempfile . Close ( )
os . Remove ( tempfile . Name ( ) )
actualName := tempfile . Name ( ) + ".png"
dc . SavePNG ( actualName )
return actualName
2017-02-26 21:06:48 +10:30
}