diff --git a/cmd/wayback/wayback.go b/cmd/wayback/wayback.go index 5baa7d8a..6acfec39 100644 --- a/cmd/wayback/wayback.go +++ b/cmd/wayback/wayback.go @@ -31,11 +31,11 @@ func archive(cmd *cobra.Command, args []string) { cmd.Println(col.Src, "=>", col.Dst) } for src, bundle := range bundles { - for _, path := range bundle.Paths() { - if path == "" { + for _, asset := range bundle.Asset() { + if asset.Local == "" { continue } - cmd.Println(src, "=>", path) + cmd.Println(src, "=>", asset.Local) } } diff --git a/errors/errors.go b/errors/errors.go index 5522e973..2ede34c9 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -4,7 +4,11 @@ package errors // import "github.com/wabarc/wayback/errors" -import "fmt" +import ( + "fmt" + + "github.com/pkg/errors" +) // Error represents an error type Error struct { @@ -21,3 +25,7 @@ func (e Error) Error() string { func New(message string, args ...interface{}) *Error { return &Error{message: message, args: args} } + +func Wrap(err error, message string) error { + return errors.Wrap(err, message) +} diff --git a/go.mod b/go.mod index 039e1a80..6819488f 100644 --- a/go.mod +++ b/go.mod @@ -31,6 +31,7 @@ require ( github.com/mattn/go-mastodon v0.0.5-0.20210515144304-86627ec7d635 github.com/multiformats/go-multiaddr v0.3.3 // indirect github.com/multiformats/go-multihash v0.0.15 // indirect + github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.11.0 github.com/prometheus/common v0.30.0 github.com/robertkrimen/otto v0.0.0-20210614181706-373ff5438452 // indirect @@ -41,12 +42,14 @@ require ( github.com/tidwall/pretty v1.2.0 // indirect github.com/wabarc/archive.is v1.3.0 github.com/wabarc/archive.org v1.2.1-0.20210708220121-cb9b83ff9896 + github.com/wabarc/go-anonfile v0.1.0 + github.com/wabarc/go-catbox v0.1.0 github.com/wabarc/helper v0.0.0-20210718171053-59c70d0b20c2 github.com/wabarc/logger v0.0.0-20210730133522-86bd3f31e792 github.com/wabarc/playback v0.0.0-20210718054702-cab6c6004933 github.com/wabarc/screenshot v1.2.1-0.20210713083422-eee9add4752d github.com/wabarc/telegra.ph v0.0.0-20210718173528-f72fc9cdc365 - github.com/wabarc/warcraft v0.1.1-0.20210707001544-e897dbede7c3 + github.com/wabarc/warcraft v0.1.1-0.20210711171056-a5eec617b86c github.com/wabarc/wbipfs v0.2.0 github.com/whyrusleeping/tar-utils v0.0.0-20201201191210-20a61371de5b // indirect go.etcd.io/bbolt v1.3.6 @@ -55,6 +58,7 @@ require ( golang.org/x/sync v0.0.0-20210220032951-036812b2e83c gopkg.in/tucnak/telebot.v2 v2.3.5 maunium.net/go/mautrix v0.9.17 + mvdan.cc/xurls/v2 v2.3.0 // indirect ) replace github.com/go-shiori/obelisk => github.com/wabarc/obelisk v0.0.0-20210420023708-aac2bcc00a78 diff --git a/go.sum b/go.sum index 4c1cd444..4935f056 100644 --- a/go.sum +++ b/go.sum @@ -465,6 +465,7 @@ github.com/orisano/pixelmatch v0.0.0-20210112091706-4fa4c7ba91d5/go.mod h1:nZgzb github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -508,6 +509,7 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -587,9 +589,14 @@ github.com/wabarc/archive.is v1.3.0/go.mod h1:01mgwPSUip7WkigtaZypTTHSpv3NVbtWpK github.com/wabarc/archive.org v1.2.0/go.mod h1:yEmUMlNO2PPAxIvo/Hf/VxOrCS5SBwL2/vCW8pyTWjA= github.com/wabarc/archive.org v1.2.1-0.20210708220121-cb9b83ff9896 h1:c3uD+IKXpNhtAHC2/fTt7R9dXnNhw1Q6sQrHUX3xo64= github.com/wabarc/archive.org v1.2.1-0.20210708220121-cb9b83ff9896/go.mod h1:yEmUMlNO2PPAxIvo/Hf/VxOrCS5SBwL2/vCW8pyTWjA= +github.com/wabarc/go-anonfile v0.1.0 h1:M4jKUAMROxxVaqLQt30ONmaHA/0YnvyJtX8qg/zI9+I= +github.com/wabarc/go-anonfile v0.1.0/go.mod h1:CH1LzSKQ0x/RlKpEG2gi+hXaOOZrfeRx6Rp717o+65A= +github.com/wabarc/go-catbox v0.1.0 h1:/UhV9md3MJrjZtm+EToSyFjawXgPiHSExLNRqsWNisg= +github.com/wabarc/go-catbox v0.1.0/go.mod h1:Zjs9Y55f2WOwGWwmKSCrUuMfwh+nDktkjub9jgHq4CQ= github.com/wabarc/helper v0.0.0-20210127120855-10af37cc2616/go.mod h1:N9P4r7Rn46p4nkWtXV6ztN3p5ACVnp++bgfwjTqSxQ8= github.com/wabarc/helper v0.0.0-20210407153720-1bfe98b427fe/go.mod h1:TuTZtoiOu984UWOf7FfX58JllKMjq7FCz701kB5W88E= github.com/wabarc/helper v0.0.0-20210613000316-cc3df6340ab5/go.mod h1:TuTZtoiOu984UWOf7FfX58JllKMjq7FCz701kB5W88E= +github.com/wabarc/helper v0.0.0-20210614160629-1a5ba5e551eb/go.mod h1:TuTZtoiOu984UWOf7FfX58JllKMjq7FCz701kB5W88E= github.com/wabarc/helper v0.0.0-20210701193643-e0fe0a807cb9/go.mod h1:TuTZtoiOu984UWOf7FfX58JllKMjq7FCz701kB5W88E= github.com/wabarc/helper v0.0.0-20210718171053-59c70d0b20c2 h1:6rMZse2rdD7N6GxHRZqHlkSptBWh/Vf9aHiFVQjlQNo= github.com/wabarc/helper v0.0.0-20210718171053-59c70d0b20c2/go.mod h1:uS6mimKlWkGvEZXkJ6JoW7LYnnB2JP6dLU9q7pgDaWQ= @@ -614,8 +621,8 @@ github.com/wabarc/telebot v0.0.0-20210614085950-9479567e0e0a h1:RCrMXolMzoNxmN9X github.com/wabarc/telebot v0.0.0-20210614085950-9479567e0e0a/go.mod h1:BgaIIx50PSRS9pG59JH+geT82cfvoJU/IaI5TJdN3v8= github.com/wabarc/telegra.ph v0.0.0-20210718173528-f72fc9cdc365 h1:AZeSe84nDyD5bYNpkCVVaOxdvaHIOj5tjVJPqcjjFLI= github.com/wabarc/telegra.ph v0.0.0-20210718173528-f72fc9cdc365/go.mod h1:e2nxaKoyr34W/jZefnrTn4HGXKdg0HBygLvIDJKFAiw= -github.com/wabarc/warcraft v0.1.1-0.20210707001544-e897dbede7c3 h1:P9KKKeXbGdvzJoEEMQ2M9DlusYdS/9hzimS0+rVYrJY= -github.com/wabarc/warcraft v0.1.1-0.20210707001544-e897dbede7c3/go.mod h1:/BbCwReBjlqHRaw8Yh+7sfAicOesiMYNhiFpuL1x8Rc= +github.com/wabarc/warcraft v0.1.1-0.20210711171056-a5eec617b86c h1:HPTqoafOgXbqEMD54v3cTTJKKK3fd79UqG1ILR9S640= +github.com/wabarc/warcraft v0.1.1-0.20210711171056-a5eec617b86c/go.mod h1:/BbCwReBjlqHRaw8Yh+7sfAicOesiMYNhiFpuL1x8Rc= github.com/wabarc/wbipfs v0.2.0 h1:TksxONxL/LrBGZ6TTcWD4p+FAzuQPfLQyCL7K64isW8= github.com/wabarc/wbipfs v0.2.0/go.mod h1:wh76ZvNbhSrDNxStkKORKcnfwjozfoTtq8XyzU7GKDw= github.com/whyrusleeping/tar-utils v0.0.0-20180509141711-8c6c8ba81d5c/go.mod h1:xxcJeBb7SIUl/Wzkz1eVKJE/CB34YNrqX2TQI6jY9zs= @@ -1066,8 +1073,9 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 maunium.net/go/maulogger/v2 v2.2.4/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A= maunium.net/go/mautrix v0.9.17 h1:4rRAN11LPQGS0TOGEb4uppUzGet7nSwT9C0jHzSWmtg= maunium.net/go/mautrix v0.9.17/go.mod h1:7IzKfWvpQtN+W2Lzxc0rLvIxFM3ryKX6Ys3S/ZoWbg8= -mvdan.cc/xurls/v2 v2.2.0 h1:NSZPykBXJFCetGZykLAxaL6SIpvbVy/UFEniIfHAa8A= mvdan.cc/xurls/v2 v2.2.0/go.mod h1:EV1RMtya9D6G5DMYPGD8zTQzaHet6Jh8gFlRgGRJeO8= +mvdan.cc/xurls/v2 v2.3.0 h1:59Olnbt67UKpxF1EwVBopJvkSUBmgtb468E4GVWIZ1I= +mvdan.cc/xurls/v2 v2.3.0/go.mod h1:AjuTy7gEiUArFMjgBBDU4SMxlfUYsRokpJQgNWOt3e4= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/publish/discord.go b/publish/discord.go index 33606df2..1e7f5734 100644 --- a/publish/discord.go +++ b/publish/discord.go @@ -8,7 +8,6 @@ import ( "context" "os" "path" - "strings" "github.com/dustin/go-humanize" "github.com/wabarc/helper" @@ -53,7 +52,7 @@ func (d *discordBot) Publish(ctx context.Context, cols []wayback.Collect, args . } var bnd = bundle(ctx, cols) - var txt = render.ForPublish(&render.Discord{Cols: cols}).String() + var txt = render.ForPublish(&render.Discord{Cols: cols, Data: bnd}).String() if d.toChannel(ctx, bnd, txt) { metrics.IncrementPublish(metrics.PublishDiscord, metrics.StatusSuccess) return @@ -64,7 +63,7 @@ func (d *discordBot) Publish(ctx context.Context, cols []wayback.Collect, args . // toChannel for publish to message to Discord channel, // returns boolean as result. -func (d *discordBot) toChannel(ctx context.Context, bundle *reduxer.Bundle, text string) (ok bool) { +func (d *discordBot) toChannel(_ context.Context, bundle *reduxer.Bundle, text string) (ok bool) { if text == "" { logger.Warn("post to message to channel failed, text empty") return ok @@ -78,58 +77,51 @@ func (d *discordBot) toChannel(ctx context.Context, bundle *reduxer.Bundle, text } } - // TODO: move to render - var b strings.Builder - if head := title(ctx, bundle); head != "" { - b.WriteString(`**`) - b.WriteString(head) - b.WriteString(`**`) - b.WriteString("\n\n") + msg, err := d.bot.ChannelMessageSendComplex(config.Opts.DiscordChannel(), &discord.MessageSend{Content: text}) + if err != nil { + logger.Error("post message to channel failed, %v", err) + return ok + } + + // Send files as reference + files := UploadToDiscord(bundle) + if len(files) == 0 { + logger.Debug("without files, complete.") + return true } - if dgst := digest(ctx, bundle); dgst != "" { - b.WriteString(dgst) - b.WriteString("\n\n") + ms := &discord.MessageSend{Files: files, Reference: msg.Reference()} + if _, err := d.bot.ChannelMessageSendComplex(config.Opts.DiscordChannel(), ms); err != nil { + logger.Error("upload files failed, %v", err) } - b.WriteString(text) - msg := &discord.MessageSend{Content: b.String()} + return true +} + +func UploadToDiscord(bundle *reduxer.Bundle) (files []*discord.File) { if bundle != nil { var fsize int64 - var files []*discord.File upper := config.Opts.MaxAttachSize("discord") - for _, p := range bundle.Paths() { - if p == "" { + for _, asset := range bundle.Asset() { + if asset.Local == "" { continue } - if !helper.Exists(p) { - logger.Warn("invalid file %s", p) + if !helper.Exists(asset.Local) { + logger.Warn("invalid file %s", asset.Local) continue } - fsize += helper.FileSize(p) + fsize += helper.FileSize(asset.Local) if fsize > upper { logger.Warn("total file size large than %s, skipped", humanize.Bytes(uint64(upper))) continue } - logger.Debug("open file: %s", p) - rd, err := os.Open(p) + logger.Debug("open file: %s", asset.Local) + rd, err := os.Open(asset.Local) if err != nil { logger.Error("open file failed: %v", err) continue } - files = append(files, &discord.File{Name: path.Base(p), Reader: rd}) - } - if len(files) == 0 { - logger.Warn("files empty") - return ok + files = append(files, &discord.File{Name: path.Base(asset.Local), Reader: rd}) } - msg.Files = files } - - _, err := d.bot.ChannelMessageSendComplex(config.Opts.DiscordChannel(), msg) - if err != nil { - logger.Error("post message to channel failed, %v", err) - return ok - } - - return true + return } diff --git a/publish/discord_test.go b/publish/discord_test.go index ce3202e2..a3a3a08a 100644 --- a/publish/discord_test.go +++ b/publish/discord_test.go @@ -170,8 +170,8 @@ func TestToDiscordChannel(t *testing.T) { } bot.Client = httpClient d := NewDiscord(bot) - txt := render.ForPublish(&render.Discord{Cols: collects}).String() - got := d.toChannel(context.Background(), nil, txt) + txt := render.ForPublish(&render.Discord{Cols: collects, Data: bundleExample}).String() + got := d.toChannel(context.Background(), bundleExample, txt) if !got { t.Errorf("Unexpected publish to discord channel got %t instead of %t", got, true) } diff --git a/publish/github.go b/publish/github.go index 63aad762..96b1bc70 100644 --- a/publish/github.go +++ b/publish/github.go @@ -52,7 +52,7 @@ func (gh *gitHub) Publish(ctx context.Context, cols []wayback.Collect, args ...s } var bnd = bundle(ctx, cols) - var txt = render.ForPublish(&render.GitHub{Cols: cols}).String() + var txt = render.ForPublish(&render.GitHub{Cols: cols, Data: bnd}).String() if gh.toIssues(ctx, bnd, txt) { metrics.IncrementPublish(metrics.PublishGithub, metrics.StatusSuccess) return @@ -76,19 +76,11 @@ func (gh *gitHub) toIssues(ctx context.Context, bundle *reduxer.Bundle, text str logger.Debug("authorized GitHub user: %v", user) } - t := strings.TrimSpace(title(ctx, bundle)) + t := strings.TrimSpace(render.Title(bundle)) if t == "" { t = "Published at " + time.Now().Format("2006-01-02T15:04:05") } - var b strings.Builder - if dgst := digest(ctx, bundle); dgst != "" { - b.WriteString(dgst) - b.WriteString("\n\n") - } - b.WriteString(text) - text = b.String() - // Create an issue to GitHub ir := &github.IssueRequest{Title: github.String(t), Body: github.String(text)} issue, _, err := gh.client.Issues.Create(ctx, config.Opts.GitHubOwner(), config.Opts.GitHubRepo(), ir) diff --git a/publish/mastodon.go b/publish/mastodon.go index 333cb441..3237594e 100644 --- a/publish/mastodon.go +++ b/publish/mastodon.go @@ -6,7 +6,6 @@ package publish // import "github.com/wabarc/wayback/publish" import ( "context" - "strings" mstdn "github.com/mattn/go-mastodon" "github.com/wabarc/logger" @@ -52,7 +51,7 @@ func (m *mastodon) Publish(ctx context.Context, cols []wayback.Collect, args ... } var bnd = bundle(ctx, cols) - var txt = render.ForPublish(&render.Mastodon{Cols: cols}).String() + var txt = render.ForPublish(&render.Mastodon{Cols: cols, Data: bnd}).String() if m.ToMastodon(ctx, bnd, txt, id) { metrics.IncrementPublish(metrics.PublishMstdn, metrics.StatusSuccess) return @@ -71,15 +70,8 @@ func (m *mastodon) ToMastodon(ctx context.Context, bundle *reduxer.Bundle, text, return false } - var b strings.Builder - if head := title(ctx, bundle); head != "" { - b.WriteString(`‹ `) - b.WriteString(head) - b.WriteString(" ›\n\n") - } - b.WriteString(text) toot := &mstdn.Toot{ - Status: b.String(), + Status: text, Visibility: mstdn.VisibilityPublic, } if id != "" { diff --git a/publish/matrix.go b/publish/matrix.go index 4e51ad40..b6f4e5cc 100644 --- a/publish/matrix.go +++ b/publish/matrix.go @@ -6,13 +6,11 @@ package publish // import "github.com/wabarc/wayback/publish" import ( "context" - "strings" "github.com/wabarc/logger" "github.com/wabarc/wayback" "github.com/wabarc/wayback/config" "github.com/wabarc/wayback/metrics" - "github.com/wabarc/wayback/reduxer" "github.com/wabarc/wayback/template/render" matrix "maunium.net/go/mautrix" "maunium.net/go/mautrix/event" @@ -59,8 +57,8 @@ func (m *matrixBot) Publish(ctx context.Context, cols []wayback.Collect, args .. } var bnd = bundle(ctx, cols) - var txt = render.ForPublish(&render.Matrix{Cols: cols}).String() - if m.toRoom(ctx, bnd, txt) { + var txt = render.ForPublish(&render.Matrix{Cols: cols, Data: bnd}).String() + if m.toRoom(txt) { metrics.IncrementPublish(metrics.PublishMatrix, metrics.StatusSuccess) return } @@ -68,7 +66,7 @@ func (m *matrixBot) Publish(ctx context.Context, cols []wayback.Collect, args .. return } -func (m *matrixBot) toRoom(ctx context.Context, bundle *reduxer.Bundle, text string) bool { +func (m *matrixBot) toRoom(text string) bool { if !config.Opts.PublishToMatrixRoom() || m.client == nil { logger.Warn("publish to Matrix room abort.") return false @@ -78,19 +76,6 @@ func (m *matrixBot) toRoom(ctx context.Context, bundle *reduxer.Bundle, text str return false } - var b strings.Builder - if head := title(ctx, bundle); head != "" { - b.WriteString(`‹ `) - b.WriteString(head) - b.WriteString(`

`) - } - if dgst := digest(ctx, bundle); dgst != "" { - b.WriteString(dgst) - b.WriteString(`

`) - } - b.WriteString(text) - - text = b.String() content := &event.MessageEventContent{ FormattedBody: text, Format: event.FormatHTML, diff --git a/publish/matrix_test.go b/publish/matrix_test.go index 7b7be0a4..c7086c09 100644 --- a/publish/matrix_test.go +++ b/publish/matrix_test.go @@ -5,7 +5,6 @@ package publish // import "github.com/wabarc/wayback/publish" import ( - "context" "fmt" "io/ioutil" "net/http" @@ -53,7 +52,7 @@ func TestToMatrixRoom(t *testing.T) { mat := NewMatrix(nil) txt := render.ForPublish(&render.Mastodon{Cols: collects}).String() - got := mat.toRoom(context.Background(), nil, txt) + got := mat.toRoom(txt) if !got { t.Errorf("Unexpected publish room message got %t instead of %t", got, true) } diff --git a/publish/publish.go b/publish/publish.go index 1c349487..814a766f 100644 --- a/publish/publish.go +++ b/publish/publish.go @@ -40,9 +40,6 @@ const ( FlagIRC = "irc" PubBundle = "reduxer-bundle" - - maxTitleLen = 256 - maxDigestLen = 500 ) var maxDelayTime = 10 @@ -244,37 +241,3 @@ func bundle(ctx context.Context, cols []wayback.Collect) (b *reduxer.Bundle) { return b } - -func title(_ context.Context, bundle *reduxer.Bundle) string { - if bundle == nil { - return "" - } - logger.Debug("extract title from reduxer bundle title: %s", bundle.Title) - - t := []rune(bundle.Title) - l := len(t) - if l > maxTitleLen { - t = t[:maxTitleLen] - } - - return strings.TrimSpace(string(t)) -} - -func digest(_ context.Context, bundle *reduxer.Bundle) string { - if bundle == nil { - return "" - } - logger.Debug("generate digest from article content: %s", bundle.Article.TextContent) - - txt := []rune(bundle.Article.TextContent) - l := len(txt) - switch { - case l == 0: - return "" - case l > maxDigestLen: - txt = txt[:maxDigestLen] - return string(txt) + ` ...` - default: - return string(txt) - } -} diff --git a/publish/publish_test.go b/publish/publish_test.go index 58116488..4cf60bdd 100644 --- a/publish/publish_test.go +++ b/publish/publish_test.go @@ -18,6 +18,7 @@ import ( "github.com/wabarc/helper" "github.com/wabarc/wayback" "github.com/wabarc/wayback/config" + "github.com/wabarc/wayback/reduxer" telegram "gopkg.in/tucnak/telebot.v2" ) @@ -48,6 +49,53 @@ var collects = []wayback.Collect{ }, } +var bundleExample = &reduxer.Bundle{ + Assets: reduxer.Assets{ + Img: reduxer.Asset{ + Local: "/path/to/image", + Remote: reduxer.Remote{ + Anonfile: "https://anonfiles.com/FbZfSa9eu4", + Catbox: "https://files.catbox.moe/9u6yvu.png", + }, + }, + PDF: reduxer.Asset{ + Local: "/path/to/pdf", + Remote: reduxer.Remote{ + Anonfile: "https://anonfiles.com/r4G8Sb90ud", + Catbox: "https://files.catbox.moe/q73uqh.pdf", + }, + }, + Raw: reduxer.Asset{ + Local: "/path/to/htm", + Remote: reduxer.Remote{ + Anonfile: "https://anonfiles.com/pbG4Se94ua", + Catbox: "https://files.catbox.moe/bph1g6.htm", + }, + }, + Txt: reduxer.Asset{ + Local: "/path/to/txt", + Remote: reduxer.Remote{ + Anonfile: "https://anonfiles.com/naG6S09bu1", + Catbox: "https://files.catbox.moe/wwrby6.txt", + }, + }, + WARC: reduxer.Asset{ + Local: "/path/to/warc", + Remote: reduxer.Remote{ + Anonfile: "https://anonfiles.com/v4G4S09auc", + Catbox: "https://files.catbox.moe/kkai0w.warc", + }, + }, + Media: reduxer.Asset{ + Local: "", + Remote: reduxer.Remote{ + Anonfile: "", + Catbox: "", + }, + }, + }, +} + func unsetAllEnv() { lines := os.Environ() for _, line := range lines { @@ -99,6 +147,7 @@ func TestPublishToChannelFromTelegram(t *testing.T) { } ctx := context.WithValue(context.Background(), FlagTelegram, bot) + ctx = context.WithValue(ctx, PubBundle, bundleExample) To(ctx, collects, FlagTelegram) } diff --git a/publish/slack.go b/publish/slack.go index 7a7a87af..70373534 100644 --- a/publish/slack.go +++ b/publish/slack.go @@ -7,7 +7,6 @@ package publish // import "github.com/wabarc/wayback/publish" import ( "context" "os" - "strings" "github.com/wabarc/helper" "github.com/wabarc/logger" @@ -54,8 +53,8 @@ func (s *slackBot) Publish(ctx context.Context, cols []wayback.Collect, args ... } var bnd = bundle(ctx, cols) - var txt = render.ForPublish(&render.Slack{Cols: cols}).String() - if s.toChannel(ctx, bnd, txt) { + var txt = render.ForPublish(&render.Slack{Cols: cols, Data: bnd}).String() + if s.toChannel(bnd, txt) { metrics.IncrementPublish(metrics.PublishSlack, metrics.StatusSuccess) return } @@ -65,7 +64,7 @@ func (s *slackBot) Publish(ctx context.Context, cols []wayback.Collect, args ... // toChannel for publish to message to Slack channel, // returns boolean as result. -func (s *slackBot) toChannel(ctx context.Context, bundle *reduxer.Bundle, text string) (ok bool) { +func (s *slackBot) toChannel(bundle *reduxer.Bundle, text string) (ok bool) { if text == "" { logger.Warn("post to message to channel failed, text empty") return ok @@ -74,21 +73,8 @@ func (s *slackBot) toChannel(ctx context.Context, bundle *reduxer.Bundle, text s s.bot = slack.New(config.Opts.SlackBotToken()) } - // TODO: move to render - var b strings.Builder - if head := title(ctx, bundle); head != "" { - b.WriteString(`‹ `) - b.WriteString(head) - b.WriteString(" ›\n\n") - } - if dgst := digest(ctx, bundle); dgst != "" { - b.WriteString(dgst) - b.WriteString("\n\n") - } - b.WriteString(text) - msgOpts := []slack.MsgOption{ - slack.MsgOptionText(b.String(), false), + slack.MsgOptionText(text, false), slack.MsgOptionDisableMarkdown(), } _, tstamp, err := s.bot.PostMessage(config.Opts.SlackChannel(), msgOpts...) @@ -104,48 +90,45 @@ func (s *slackBot) toChannel(ctx context.Context, bundle *reduxer.Bundle, text s } // UploadToSlack upload files to channel and attach as a reply by the given bundle -func UploadToSlack(client *slack.Client, bundle *reduxer.Bundle, channel, timestamp string) error { +func UploadToSlack(client *slack.Client, bundle *reduxer.Bundle, channel, timestamp string) (err error) { if client == nil { return errors.New("client invalid") } var fsize int64 - // TODO: clean code and wrap errors - for _, path := range bundle.Paths() { - if path == "" { + for _, asset := range bundle.Asset() { + if asset.Local == "" { continue } - if !helper.Exists(path) { - logger.Warn("[publish] invalid file %s", path) + if !helper.Exists(asset.Local) { + err = errors.Wrap(err, "invalid file "+asset.Local) continue } - fsize += helper.FileSize(path) + fsize += helper.FileSize(asset.Local) if fsize > config.Opts.MaxAttachSize("slack") { - logger.Warn("total file size large than 5GB, skipped") + err = errors.Wrap(err, "total file size large than 5GB, skipped") continue } - logger.Debug("append document: %s", path) - reader, err := os.Open(path) - if err != nil { - logger.Error("open file failed: %v", err) + reader, e := os.Open(asset.Local) + if e != nil { + err = errors.Wrap(err, e.Error()) continue } params := slack.FileUploadParameters{ - Filename: path, + Filename: asset.Local, Reader: reader, Title: bundle.Title, Channels: []string{channel}, ThreadTimestamp: timestamp, } - file, err := client.UploadFile(params) - if err != nil { - logger.Error("unexpected error: %s", err) + file, e := client.UploadFile(params) + if e != nil { + err = errors.Wrap(err, e.Error()) continue } - logger.Debug("uploaded file: %#v", file) - file, _, _, err = client.ShareFilePublicURL(file.ID) - if err != nil { - logger.Warn("create external link failed: %v", err) + file, _, _, e = client.ShareFilePublicURL(file.ID) + if e != nil { + err = errors.Wrap(err, e.Error()) continue } logger.Info("slack external file permalink: %s", file.PermalinkPublic) diff --git a/publish/telegram.go b/publish/telegram.go index 3519e5ab..f511b6ab 100644 --- a/publish/telegram.go +++ b/publish/telegram.go @@ -6,7 +6,6 @@ package publish // import "github.com/wabarc/wayback/publish" import ( "context" - "strings" "github.com/wabarc/helper" "github.com/wabarc/logger" @@ -52,8 +51,8 @@ func (t *telegramBot) Publish(ctx context.Context, cols []wayback.Collect, args } var bnd = bundle(ctx, cols) - var txt = render.ForPublish(&render.Telegram{Cols: cols}).String() - if t.toChannel(ctx, bnd, txt) { + var txt = render.ForPublish(&render.Telegram{Cols: cols, Data: bnd}).String() + if t.toChannel(bnd, txt) { metrics.IncrementPublish(metrics.PublishChannel, metrics.StatusSuccess) return } @@ -63,7 +62,7 @@ func (t *telegramBot) Publish(ctx context.Context, cols []wayback.Collect, args // toChannel for publish to message to Telegram channel, // returns boolean as result. -func (t *telegramBot) toChannel(ctx context.Context, bundle *reduxer.Bundle, text string) (ok bool) { +func (t *telegramBot) toChannel(bundle *reduxer.Bundle, text string) (ok bool) { if text == "" { logger.Warn("post to message to channel failed, text empty") return ok @@ -86,19 +85,7 @@ func (t *telegramBot) toChannel(ctx context.Context, bundle *reduxer.Bundle, tex return ok } - var b strings.Builder - if head := title(ctx, bundle); head != "" { - b.WriteString("") - b.WriteString(head) - b.WriteString("\n\n") - } - if dgst := digest(ctx, bundle); dgst != "" { - b.WriteString(dgst) - b.WriteString("\n\n") - } - b.WriteString(text) - - stage, err := t.bot.Send(chat, b.String()) + stage, err := t.bot.Send(chat, text) if err != nil { logger.Error("post message to channel failed, %v", err) return ok @@ -109,37 +96,42 @@ func (t *telegramBot) toChannel(ctx context.Context, bundle *reduxer.Bundle, tex return true } + album := UploadToTelegram(bundle) + if len(album) == 0 { + return true + } + // Send album attach files, and reply to wayback result message + opts := &telegram.SendOptions{ReplyTo: stage, DisableNotification: true} + if _, err := t.bot.SendAlbum(stage.Chat, album, opts); err != nil { + logger.Error("reply failed: %v", err) + } + + return true +} + +func UploadToTelegram(bundle *reduxer.Bundle) telegram.Album { // Attach image and pdf files var album telegram.Album var fsize int64 - for _, path := range bundle.Paths() { - if path == "" { + for _, asset := range bundle.Asset() { + if asset.Local == "" { continue } - if !helper.Exists(path) { - logger.Warn("invalid file %s", path) + if !helper.Exists(asset.Local) { + logger.Warn("invalid file %s", asset.Local) continue } - fsize += helper.FileSize(path) + fsize += helper.FileSize(asset.Local) if fsize > config.Opts.MaxAttachSize("telegram") { logger.Warn("total file size large than 50MB, skipped") continue } - logger.Debug("append document: %s", path) + logger.Debug("append document: %s", asset.Local) album = append(album, &telegram.Document{ - File: telegram.FromDisk(path), + File: telegram.FromDisk(asset.Local), Caption: bundle.Title, - FileName: path, + FileName: asset.Local, }) } - if len(album) == 0 { - return true - } - // Send album attach files, and reply to wayback result message - opts := &telegram.SendOptions{ReplyTo: stage, DisableNotification: true} - if _, err := t.bot.SendAlbum(stage.Chat, album, opts); err != nil { - logger.Error("reply failed: %v", err) - } - - return true + return album } diff --git a/publish/telegram_test.go b/publish/telegram_test.go index 15350986..8e074455 100644 --- a/publish/telegram_test.go +++ b/publish/telegram_test.go @@ -5,7 +5,6 @@ package publish // import "github.com/wabarc/wayback/publish" import ( - "context" "encoding/json" "fmt" "io" @@ -94,7 +93,7 @@ func TestToChannel(t *testing.T) { tel := &telegramBot{bot: bot} txt := render.ForPublish(&render.Telegram{Cols: collects}).String() - got := tel.toChannel(context.Background(), nil, txt) + got := tel.toChannel(nil, txt) if !got { t.Errorf("Unexpected publish Telegram Channel message got %t instead of %t", got, true) } diff --git a/publish/twitter.go b/publish/twitter.go index cdab822d..36edd35e 100644 --- a/publish/twitter.go +++ b/publish/twitter.go @@ -6,7 +6,6 @@ package publish // import "github.com/wabarc/wayback/publish" import ( "context" - "strings" "github.com/dghubble/go-twitter/twitter" "github.com/dghubble/oauth1" @@ -47,7 +46,7 @@ func (t *twitterBot) Publish(ctx context.Context, cols []wayback.Collect, args . } var bnd = bundle(ctx, cols) - var txt = render.ForPublish(&render.Twitter{Cols: cols}).String() + var txt = render.ForPublish(&render.Twitter{Cols: cols, Data: bnd}).String() if t.ToTwitter(ctx, bnd, txt) { metrics.IncrementPublish(metrics.PublishTwitter, metrics.StatusSuccess) return @@ -66,15 +65,7 @@ func (t *twitterBot) ToTwitter(ctx context.Context, bundle *reduxer.Bundle, text return false } - // TODO: character limit - var b strings.Builder - if head := title(ctx, bundle); head != "" { - b.WriteString(`‹ `) - b.WriteString(head) - b.WriteString(" ›\n\n") - } - b.WriteString(text) - tweet, resp, err := t.client.Statuses.Update(b.String(), nil) + tweet, resp, err := t.client.Statuses.Update(text, nil) if err != nil { logger.Error("create tweet failed: %v", err) return false diff --git a/reduxer/reduxer.go b/reduxer/reduxer.go index d1d97288..e26b7c64 100644 --- a/reduxer/reduxer.go +++ b/reduxer/reduxer.go @@ -23,28 +23,36 @@ import ( "github.com/iawia002/annie/downloader" "github.com/iawia002/annie/extractors" "github.com/iawia002/annie/extractors/types" + "github.com/wabarc/go-anonfile" + "github.com/wabarc/go-catbox" "github.com/wabarc/helper" "github.com/wabarc/logger" "github.com/wabarc/screenshot" "github.com/wabarc/warcraft" "github.com/wabarc/wayback/config" "github.com/wabarc/wayback/errors" + "golang.org/x/sync/errgroup" ) -type file struct { - Local string - Remote map[string]string +type Bundle struct { + screenshot.Screenshots + + Assets Assets + Article readability.Article } type Assets struct { - Img, PDF, Raw, Txt, WARC, Media file + Img, PDF, Raw, Txt, WARC, Media Asset } -type Bundle struct { - screenshot.Screenshots +type Asset struct { + Local string + Remote Remote +} - Assets Assets - Article readability.Article +type Remote struct { + Anonfile string + Catbox string } type Bundles map[string]*Bundle @@ -81,7 +89,7 @@ func Do(ctx context.Context, urls ...string) (bundles Bundles, err error) { logger.Debug("create warc for %s failed", u.String()) return "" } - path, err := warc.Download(u) + path, err := warc.Download(ctx, u) if err != nil { logger.Debug("create warc for %s failed: %v", u.String(), err) return "" @@ -90,7 +98,7 @@ func Do(ctx context.Context, urls ...string) (bundles Bundles, err error) { } type m struct { - key *file + key *Asset buf []byte } @@ -141,6 +149,10 @@ func Do(ctx context.Context, urls ...string) (bundles Bundles, err error) { logger.Error("assign field Txt to assets struct failed: %v", err) } } + // Upload files to third-party server + if err := remotely(ctx, &assets); err != nil { + logger.Error("upload files to third-party failed: %v", err) + } bundle := &Bundle{shot, assets, article} mu.Lock() bundles[shot.URL] = bundle @@ -203,15 +215,15 @@ func Capture(ctx context.Context, urls ...string) (shots []screenshot.Screenshot return shots, nil } -func (b *Bundle) Paths() (paths []string) { +func (b *Bundle) Asset() (paths []Asset) { logger.Debug("assets: %#v", b.Assets) - paths = []string{ - b.Assets.Img.Local, - b.Assets.PDF.Local, - b.Assets.Raw.Local, - b.Assets.Txt.Local, - b.Assets.WARC.Local, - b.Assets.Media.Local, + paths = []Asset{ + b.Assets.Img, + b.Assets.PDF, + b.Assets.Raw, + b.Assets.Txt, + b.Assets.WARC, + b.Assets.Media, } return } @@ -295,8 +307,7 @@ func media(ctx context.Context, dir, in string) string { } cmd := exec.CommandContext(ctx, "youtube-dl", args...) if err := cmd.Run(); err != nil { - logger.Error("start youtube-dl failed: %v", err) - return "" + logger.Warn("start youtube-dl failed: %v", err) } return match(fp + "*") } @@ -387,3 +398,45 @@ func sortStreams(streams map[string]*types.Stream) []*types.Stream { } return sortedStreams } + +func remotely(ctx context.Context, assets *Assets) (err error) { + v := []*Asset{ + &assets.Img, + &assets.PDF, + &assets.Raw, + &assets.Txt, + &assets.WARC, + &assets.Media, + } + + c := &http.Client{} + cat := catbox.New(c) + anon := anonfile.NewAnonfile(c) + g, ctx := errgroup.WithContext(ctx) + for _, asset := range v { + if !helper.Exists(asset.Local) { + continue + } + asset := asset + g.Go(func() error { + r, e := anon.Upload(asset.Local) + if e != nil { + err = errors.Wrap(err, e.Error()) + return e + } + asset.Remote.Anonfile = r.Short() + c, e := cat.Upload(asset.Local) + if e != nil { + err = errors.Wrap(err, e.Error()) + return e + } + asset.Remote.Catbox = c + return nil + }) + } + if err := g.Wait(); err != nil { + return err + } + + return err +} diff --git a/service/discord/discord.go b/service/discord/discord.go index 0fd6cbcb..c7960bcc 100644 --- a/service/discord/discord.go +++ b/service/discord/discord.go @@ -7,12 +7,9 @@ package discord // import "github.com/wabarc/wayback/service/discord" import ( "context" "encoding/base64" - "os" - "path" "strconv" "strings" - "github.com/dustin/go-humanize" "github.com/fatih/color" "github.com/wabarc/helper" "github.com/wabarc/logger" @@ -284,37 +281,15 @@ func (d *Discord) wayback(ctx context.Context, m *discord.MessageCreate, urls [] } msg := &discord.MessageSend{Content: replyText, Reference: stage.Message.Reference()} - var fsize int64 var files []*discord.File - upper := config.Opts.MaxAttachSize("discord") for _, bundle := range bundles { - for _, p := range bundle.Paths() { - if p == "" { - continue - } - if !helper.Exists(p) { - logger.Warn("invalid file %s", p) - continue - } - fsize += helper.FileSize(p) - if fsize > upper { - logger.Warn("total file size large than %s, skipped", humanize.Bytes(uint64(upper))) - continue - } - logger.Debug("open file: %s", p) - rd, err := os.Open(p) - if err != nil { - logger.Error("open file failed: %v", err) - continue - } - files = append(files, &discord.File{Name: path.Base(p), Reader: rd}) - } - msg.Files = files + files = append(files, publish.UploadToDiscord(bundle)...) } if len(files) == 0 { logger.Warn("files empty") return nil } + msg.Files = files if _, err := d.bot.ChannelMessageSendComplex(m.ChannelID, msg); err != nil { logger.Error("post message to channel failed, %v", err) diff --git a/service/slack/slack.go b/service/slack/slack.go index 53250b90..9c2107dd 100644 --- a/service/slack/slack.go +++ b/service/slack/slack.go @@ -279,7 +279,7 @@ func (s *Slack) wayback(ctx context.Context, ev *event, urls []string) error { } logger.Debug("bundles: %#v", bundles) - replyText := render.ForReply(&render.Slack{Cols: cols}).String() + replyText := render.ForReply(&render.Slack{Cols: cols, Data: bundles}).String() logger.Debug("reply text, %s", replyText) if _, err := s.edit(ev.Channel, tstamp, replyText); err != nil { diff --git a/service/telegram/telegram.go b/service/telegram/telegram.go index 29d92f4b..5a544d59 100644 --- a/service/telegram/telegram.go +++ b/service/telegram/telegram.go @@ -233,7 +233,7 @@ func (t *Telegram) wayback(ctx context.Context, message *telegram.Message, urls } logger.Debug("bundles: %#v", bundles) - replyText := render.ForReply(&render.Telegram{Cols: cols}).String() + replyText := render.ForReply(&render.Telegram{Cols: cols, Data: bundles}).String() logger.Debug("reply text, %s", replyText) opts := &telegram.SendOptions{DisableWebPagePreview: true} @@ -247,28 +247,8 @@ func (t *Telegram) wayback(ctx context.Context, message *telegram.Message, urls go publish.To(ctx, cols, publish.FlagTelegram) var album telegram.Album - var fsize int64 for _, bundle := range bundles { - for _, path := range bundle.Paths() { - if path == "" { - continue - } - if !helper.Exists(path) { - logger.Warn("[publish] invalid file %s", path) - continue - } - fsize += helper.FileSize(path) - if fsize > config.Opts.MaxAttachSize("telegram") { - logger.Warn("total file size large than 50MB, skipped") - continue - } - logger.Debug("append document: %s", path) - album = append(album, &telegram.Document{ - File: telegram.FromDisk(path), - Caption: bundle.Title, - FileName: path, - }) - } + album = append(album, publish.UploadToTelegram(bundle)...) } // Send album attach files, and reply to wayback result message opts = &telegram.SendOptions{ReplyTo: stage, DisableNotification: true} diff --git a/template/render/discord.go b/template/render/discord.go index ab873645..05afce47 100644 --- a/template/render/discord.go +++ b/template/render/discord.go @@ -16,15 +16,46 @@ var _ Renderer = (*Discord)(nil) type Discord struct { Cols []wayback.Collect + Data interface{} } func (d *Discord) ForReply() (r *Render) { - return d.ForPublish() + var tmplBytes bytes.Buffer + + const tmpl = `{{range $ := .}}{{ $.Arc | name }}: +• {{ $.Dst }} + +{{end}}` + + tpl, err := template.New("message").Funcs(funcMap()).Parse(tmpl) + if err != nil { + logger.Error("parse Discord template failed, %v", err) + return r + } + + if err = tpl.Execute(&tmplBytes, d.Cols); err != nil { + logger.Error("execute Discord template failed, %v", err) + return r + } + tmplBytes = *bytes.NewBuffer(bytes.TrimSpace(tmplBytes.Bytes())) + + return &Render{buf: tmplBytes} } func (d *Discord) ForPublish() (r *Render) { var tmplBytes bytes.Buffer + if head := Title(bundle(d.Data)); head != "" { + tmplBytes.WriteString(`**`) + tmplBytes.WriteString(head) + tmplBytes.WriteString(`**`) + tmplBytes.WriteString("\n\n") + } + if dgst := Digest(bundle(d.Data)); dgst != "" { + tmplBytes.WriteString(dgst) + tmplBytes.WriteString("\n\n") + } + const tmpl = `{{range $ := .}}{{ $.Arc | name }}: • {{ $.Dst }} diff --git a/template/render/github.go b/template/render/github.go index a4202144..0a7ad475 100644 --- a/template/render/github.go +++ b/template/render/github.go @@ -10,12 +10,14 @@ import ( "github.com/wabarc/logger" "github.com/wabarc/wayback" + "github.com/wabarc/wayback/reduxer" ) var _ Renderer = (*GitHub)(nil) type GitHub struct { Cols []wayback.Collect + Data interface{} } func (gh *GitHub) ForReply() *Render { @@ -25,6 +27,12 @@ func (gh *GitHub) ForReply() *Render { func (gh *GitHub) ForPublish() *Render { var tmplBytes bytes.Buffer + bundle := bundle(gh.Data) + if dgst := Digest(bundle); dgst != "" { + tmplBytes.WriteString(dgst) + tmplBytes.WriteString("\n\n") + } + const tmpl = `{{range $ := .}}**[{{ $.Arc | name }}]({{ $.Ext | extra }})**: > source: [{{ $.Src | unescape | revert }}]({{ $.Src | revert }}) > archived: {{ if $.Dst | isURL }}[{{ $.Dst | unescape }}]({{ $.Dst }}) @@ -43,6 +51,30 @@ func (gh *GitHub) ForPublish() *Render { return new(Render) } tmplBytes = *bytes.NewBuffer(bytes.TrimSpace(tmplBytes.Bytes())) + if bundle != nil { + tmplBytes.WriteString("\n") + gh.renderAssets(bundle.Assets, &tmplBytes) + } return &Render{buf: tmplBytes} } + +func (gh *GitHub) renderAssets(assets reduxer.Assets, tmplBytes *bytes.Buffer) { + tmpl := `**[AnonFiles](https://anonfiles.com/)** - [ [IMG]({{ .Img.Remote.Anonfile -}} +) ¦ [PDF]({{ .PDF.Remote.Anonfile }}) ¦ [RAW]({{ .Raw.Remote.Anonfile -}} +) ¦ [TXT]({{ .Txt.Remote.Anonfile }}) ¦ [WARC]({{ .WARC.Remote.Anonfile -}} +) ¦ [MEDIA]({{ .Media.Remote.Anonfile }}) ] +**[Catbox](https://catbox.moe/)** - [ [IMG]({{ .Img.Remote.Catbox -}} +) ¦ [PDF]({{ .PDF.Remote.Catbox }}) ¦ [RAW]({{ .Raw.Remote.Catbox -}} +) ¦ [TXT]({{ .Txt.Remote.Catbox }}) ¦ [WARC]({{ .WARC.Remote.Catbox -}} +) ¦ [MEDIA]({{ .Media.Remote.Catbox }}) ]` + + tpl, err := template.New("assets").Funcs(funcMap()).Parse(tmpl) + if err != nil { + logger.Error("parse Telegram template failed, %v", err) + } + tmplBytes.WriteString("\n") + if err = tpl.Execute(tmplBytes, assets); err != nil { + logger.Error("execute Telegram template failed, %v", err) + } +} diff --git a/template/render/github_test.go b/template/render/github_test.go index 328e5be5..d00d09cd 100644 --- a/template/render/github_test.go +++ b/template/render/github_test.go @@ -53,7 +53,10 @@ func TestRenderGitHub(t *testing.T) { **[Telegraph](https://telegra.ph/)**: > source: [https://example.com/?q=中文](https://example.com/?q=%E4%B8%AD%E6%96%87) -> archived: [http://telegra.ph/title-01-01](http://telegra.ph/title-01-01)` +> archived: [http://telegra.ph/title-01-01](http://telegra.ph/title-01-01) + +**[AnonFiles](https://anonfiles.com/)** - [ [IMG]() ¦ [PDF]() ¦ [RAW]() ¦ [TXT]() ¦ [WARC]() ¦ [MEDIA]() ] +**[Catbox](https://catbox.moe/)** - [ [IMG]() ¦ [PDF]() ¦ [RAW]() ¦ [TXT]() ¦ [WARC]() ¦ [MEDIA]() ]` got := ForPublish(&GitHub{Cols: collects}).String() if got != expected { @@ -76,7 +79,10 @@ func TestRenderGitHubFlawed(t *testing.T) { **[Telegraph](https://telegra.ph/)**: > source: [https://example.com/404](https://example.com/404) -> archived: [https://web.archive.org/*/https://webcache.googleusercontent.com/search?q=cache:https://example.com/404](https://web.archive.org/*/https://webcache.googleusercontent.com/search?q=cache:https://example.com/404)` +> archived: [https://web.archive.org/*/https://webcache.googleusercontent.com/search?q=cache:https://example.com/404](https://web.archive.org/*/https://webcache.googleusercontent.com/search?q=cache:https://example.com/404) + +**[AnonFiles](https://anonfiles.com/)** - [ [IMG]() ¦ [PDF]() ¦ [RAW]() ¦ [TXT]() ¦ [WARC]() ¦ [MEDIA]() ] +**[Catbox](https://catbox.moe/)** - [ [IMG]() ¦ [PDF]() ¦ [RAW]() ¦ [TXT]() ¦ [WARC]() ¦ [MEDIA]() ]` got := ForPublish(&GitHub{Cols: flawed}).String() if got != expected { diff --git a/template/render/mastodon.go b/template/render/mastodon.go index a6a0f26d..68f7b0ba 100644 --- a/template/render/mastodon.go +++ b/template/render/mastodon.go @@ -16,6 +16,7 @@ var _ Renderer = (*Mastodon)(nil) type Mastodon struct { Cols []wayback.Collect + Data interface{} } func (m *Mastodon) ForReply() *Render { @@ -25,6 +26,12 @@ func (m *Mastodon) ForReply() *Render { func (m *Mastodon) ForPublish() *Render { var tmplBytes bytes.Buffer + if head := Title(bundle(m.Data)); head != "" { + tmplBytes.WriteString(`‹ `) + tmplBytes.WriteString(head) + tmplBytes.WriteString(" ›\n\n") + } + const tmpl = `{{range $ := .}}{{ $.Arc | name }}: • {{ $.Dst }} diff --git a/template/render/matrix.go b/template/render/matrix.go index f15904c4..d79ca954 100644 --- a/template/render/matrix.go +++ b/template/render/matrix.go @@ -10,12 +10,14 @@ import ( "github.com/wabarc/logger" "github.com/wabarc/wayback" + "github.com/wabarc/wayback/reduxer" ) var _ Renderer = (*Matrix)(nil) type Matrix struct { Cols []wayback.Collect + Data interface{} } func (m *Matrix) ForReply() *Render { @@ -40,10 +42,68 @@ func (m *Matrix) ForReply() *Render { b = bytes.TrimRight(b, `
`) b = bytes.TrimRight(b, "\n") tmplBytes = *bytes.NewBuffer(b) + for _, bundle := range bundles(m.Data) { + m.renderAssets(bundle.Assets, &tmplBytes) + } return &Render{buf: tmplBytes} } func (m *Matrix) ForPublish() *Render { - return m.ForReply() + var tmplBytes bytes.Buffer + + bundle := bundle(m.Data) + if head := Title(bundle); head != "" { + tmplBytes.WriteString(`‹ `) + tmplBytes.WriteString(head) + tmplBytes.WriteString(`

`) + } + if dgst := Digest(bundle); dgst != "" { + tmplBytes.WriteString(dgst) + tmplBytes.WriteString(`

`) + } + + const tmpl = `{{range $ := .}}{{ $.Arc | name }}:
+• source - {{ $.Dst }}
+
+{{ end }}` + + tpl, err := template.New("matrix").Funcs(funcMap()).Parse(tmpl) + if err != nil { + logger.Error("parse Mastodon template failed, %v", err) + return new(Render) + } + + if err := tpl.Execute(&tmplBytes, m.Cols); err != nil { + logger.Error("execute Mastodon template failed, %v", err) + return new(Render) + } + if bundle != nil { + m.renderAssets(bundle.Assets, &tmplBytes) + } + b := bytes.TrimSpace(tmplBytes.Bytes()) + b = bytes.TrimRight(b, `
`) + b = bytes.TrimRight(b, "\n") + tmplBytes = *bytes.NewBuffer(b) + + return &Render{buf: tmplBytes} +} + +func (m *Matrix) renderAssets(assets reduxer.Assets, tmplBytes *bytes.Buffer) { + tmpl := `AnonFiles - [ IMG ¦ PDF ¦ RAW ¦ TXT ¦ WARC ¦ MEDIA ]
+Catbox - [ IMG ¦ PDF ¦ RAW ¦ TXT ¦ WARC ¦ MEDIA ]` + + tpl, err := template.New("assets").Funcs(funcMap()).Parse(tmpl) + if err != nil { + logger.Error("parse Telegram template failed, %v", err) + } + if err = tpl.Execute(tmplBytes, assets); err != nil { + logger.Error("execute Telegram template failed, %v", err) + } } diff --git a/template/render/matrix_test.go b/template/render/matrix_test.go index 0859ebad..b182ee09 100644 --- a/template/render/matrix_test.go +++ b/template/render/matrix_test.go @@ -19,10 +19,35 @@ func TestRenderMatrix(t *testing.T) { • source - https://ipfs.io/ipfs/QmTbDmpvQ3cPZG6TA5tnar4ZG6q9JMBYVmX2n3wypMQMtr

Telegraph:
-• source - http://telegra.ph/title-01-01
` +• source - http://telegra.ph/title-01-01
+
+AnonFiles - [ IMG ¦ PDF ¦ RAW ¦ TXT ¦ WARC ¦ MEDIA ]
+Catbox - [ IMG ¦ PDF ¦ RAW ¦ TXT ¦ WARC ¦ MEDIA ]` got := ForPublish(&Matrix{Cols: collects}).String() if got != matExp { t.Errorf("Unexpected render template for Matrix, got \n%s\ninstead of \n%s", got, matExp) } } + +func TestRenderMatrixWithRemotely(t *testing.T) { + const matExp = `Internet Archive:
+• source - https://web.archive.org/web/20211000000001/https://example.com/
+
+archive.today:
+• source - http://archive.today/abcdE
+
+IPFS:
+• source - https://ipfs.io/ipfs/QmTbDmpvQ3cPZG6TA5tnar4ZG6q9JMBYVmX2n3wypMQMtr
+
+Telegraph:
+• source - http://telegra.ph/title-01-01
+
+AnonFiles - [ IMG ¦ PDF ¦ RAW ¦ TXT ¦ WARC ¦ MEDIA ]
+Catbox - [ IMG ¦ PDF ¦ RAW ¦ TXT ¦ WARC ¦ MEDIA ]` + + got := ForPublish(&Matrix{Cols: collects, Data: bundleExample}).String() + if got != matExp { + t.Errorf("Unexpected render template for Matrix, got \n%s\ninstead of \n%s", got, matExp) + } +} diff --git a/template/render/relaychat.go b/template/render/relaychat.go index d82ab0bc..c5ca760f 100644 --- a/template/render/relaychat.go +++ b/template/render/relaychat.go @@ -16,6 +16,7 @@ var _ Renderer = (*Relaychat)(nil) type Relaychat struct { Cols []wayback.Collect + Data interface{} } func (i *Relaychat) ForReply() *Render { diff --git a/template/render/render.go b/template/render/render.go index 1b2b8dcc..097b5a9a 100644 --- a/template/render/render.go +++ b/template/render/render.go @@ -11,8 +11,15 @@ import ( "text/template" "github.com/wabarc/helper" + "github.com/wabarc/logger" "github.com/wabarc/wayback" "github.com/wabarc/wayback/config" + "github.com/wabarc/wayback/reduxer" +) + +const ( + maxTitleLen = 256 + maxDigestLen = 500 ) type Render struct { @@ -32,7 +39,7 @@ func ForPublish(r Renderer) *Render { return r.ForPublish() } -func (r *Render) String() (text string) { +func (r *Render) String() string { if r != nil { return r.buf.String() } @@ -85,3 +92,51 @@ func groupBySlot(cols []wayback.Collect) *Collects { } return &c } + +func bundle(data interface{}) *reduxer.Bundle { + if bundle, ok := data.(*reduxer.Bundle); ok { + return bundle + } + return new(reduxer.Bundle) +} + +func bundles(data interface{}) reduxer.Bundles { + if bundles, ok := data.(reduxer.Bundles); ok { + return bundles + } + return make(reduxer.Bundles) +} + +func Title(bundle *reduxer.Bundle) string { + if bundle == nil { + return "" + } + logger.Debug("extract title from reduxer bundle title: %s", bundle.Title) + + t := []rune(bundle.Title) + l := len(t) + if l > maxTitleLen { + t = t[:maxTitleLen] + } + + return strings.TrimSpace(string(t)) +} + +func Digest(bundle *reduxer.Bundle) string { + if bundle == nil { + return "" + } + logger.Debug("generate digest from article content: %s", bundle.Article.TextContent) + + txt := []rune(bundle.Article.TextContent) + l := len(txt) + switch { + case l == 0: + return "" + case l > maxDigestLen: + txt = txt[:maxDigestLen] + return string(txt) + ` ...` + default: + return string(txt) + } +} diff --git a/template/render/render_test.go b/template/render/render_test.go index dfe1e7e2..0802b3a3 100644 --- a/template/render/render_test.go +++ b/template/render/render_test.go @@ -7,6 +7,7 @@ package render // import "github.com/wabarc/wayback/template/render" import ( "github.com/wabarc/wayback" "github.com/wabarc/wayback/config" + "github.com/wabarc/wayback/reduxer" ) var collects = []wayback.Collect{ @@ -89,3 +90,50 @@ var multi = []wayback.Collect{ Ext: config.SLOT_IS, }, } + +var bundleExample = &reduxer.Bundle{ + Assets: reduxer.Assets{ + Img: reduxer.Asset{ + Local: "/path/to/image", + Remote: reduxer.Remote{ + Anonfile: "https://anonfiles.com/FbZfSa9eu4", + Catbox: "https://files.catbox.moe/9u6yvu.png", + }, + }, + PDF: reduxer.Asset{ + Local: "/path/to/pdf", + Remote: reduxer.Remote{ + Anonfile: "https://anonfiles.com/r4G8Sb90ud", + Catbox: "https://files.catbox.moe/q73uqh.pdf", + }, + }, + Raw: reduxer.Asset{ + Local: "/path/to/htm", + Remote: reduxer.Remote{ + Anonfile: "https://anonfiles.com/pbG4Se94ua", + Catbox: "https://files.catbox.moe/bph1g6.htm", + }, + }, + Txt: reduxer.Asset{ + Local: "/path/to/txt", + Remote: reduxer.Remote{ + Anonfile: "https://anonfiles.com/naG6S09bu1", + Catbox: "https://files.catbox.moe/wwrby6.txt", + }, + }, + WARC: reduxer.Asset{ + Local: "/path/to/warc", + Remote: reduxer.Remote{ + Anonfile: "https://anonfiles.com/v4G4S09auc", + Catbox: "https://files.catbox.moe/kkai0w.warc", + }, + }, + Media: reduxer.Asset{ + Local: "", + Remote: reduxer.Remote{ + Anonfile: "", + Catbox: "", + }, + }, + }, +} diff --git a/template/render/slack.go b/template/render/slack.go index 5eae3257..45bea190 100644 --- a/template/render/slack.go +++ b/template/render/slack.go @@ -10,21 +10,56 @@ import ( "github.com/wabarc/logger" "github.com/wabarc/wayback" + "github.com/wabarc/wayback/reduxer" ) var _ Renderer = (*Slack)(nil) type Slack struct { Cols []wayback.Collect + Data interface{} } func (s *Slack) ForReply() (r *Render) { - return s.ForPublish() + var tmplBytes bytes.Buffer + + const tmpl = `{{range $ := .}}{{ $.Arc | name }}: +• {{ $.Dst }} + +{{end}}` + + tpl, err := template.New("message").Funcs(funcMap()).Parse(tmpl) + if err != nil { + logger.Error("parse Slack template failed, %v", err) + return r + } + + if err = tpl.Execute(&tmplBytes, s.Cols); err != nil { + logger.Error("execute Slack template failed, %v", err) + return r + } + for _, bundle := range bundles(s.Data) { + s.renderAssets(bundle.Assets, &tmplBytes) + } + tmplBytes = *bytes.NewBuffer(bytes.TrimSpace(tmplBytes.Bytes())) + + return &Render{buf: tmplBytes} } func (s *Slack) ForPublish() (r *Render) { var tmplBytes bytes.Buffer + bundle := bundle(s.Data) + if head := Title(bundle); head != "" { + tmplBytes.WriteString(`‹ `) + tmplBytes.WriteString(head) + tmplBytes.WriteString(" ›\n\n") + } + if dgst := Digest(bundle); dgst != "" { + tmplBytes.WriteString(dgst) + tmplBytes.WriteString("\n\n") + } + const tmpl = `{{range $ := .}}{{ $.Arc | name }}: • {{ $.Dst }} @@ -40,7 +75,31 @@ func (s *Slack) ForPublish() (r *Render) { logger.Error("execute Slack template failed, %v", err) return r } + if bundle != nil { + s.renderAssets(bundle.Assets, &tmplBytes) + } tmplBytes = *bytes.NewBuffer(bytes.TrimSpace(tmplBytes.Bytes())) return &Render{buf: tmplBytes} } + +func (s *Slack) renderAssets(assets reduxer.Assets, tmplBytes *bytes.Buffer) { + tmpl := ` - [ <{{ .Img.Remote.Anonfile -}} +|IMG> ¦ <{{ .PDF.Remote.Anonfile }}|PDF> ¦ <{{ .Raw.Remote.Anonfile -}} +|RAW> ¦ <{{ .Txt.Remote.Anonfile }}|TXT> ¦ <{{ .WARC.Remote.Anonfile -}} +|WARC> ¦ <{{ .Media.Remote.Anonfile }}|MEDIA> ] + - [ <{{ .Img.Remote.Catbox -}} +|IMG> ¦ <{{ .PDF.Remote.Catbox }}|PDF> ¦ <{{ .Raw.Remote.Catbox -}} +|RAW> ¦ <{{ .Txt.Remote.Catbox }}|TXT> ¦ <{{ .WARC.Remote.Catbox -}} +|WARC> ¦ <{{ .Media.Remote.Catbox }}|MEDIA> ]` + + tpl, err := template.New("assets").Funcs(funcMap()).Parse(tmpl) + if err != nil { + logger.Error("parse Telegram template failed, %v", err) + } + tmplBytes.WriteString("\n") + if err = tpl.Execute(tmplBytes, assets); err != nil { + logger.Error("execute Telegram template failed, %v", err) + } + tmplBytes.WriteString("\n") +} diff --git a/template/render/slack_test.go b/template/render/slack_test.go index 3c2d4efa..e19a5267 100644 --- a/template/render/slack_test.go +++ b/template/render/slack_test.go @@ -19,7 +19,11 @@ IPFS: • https://ipfs.io/ipfs/QmTbDmpvQ3cPZG6TA5tnar4ZG6q9JMBYVmX2n3wypMQMtr Telegraph: -• http://telegra.ph/title-01-01` +• http://telegra.ph/title-01-01 + + + - [ <|IMG> ¦ <|PDF> ¦ <|RAW> ¦ <|TXT> ¦ <|WARC> ¦ <|MEDIA> ] + - [ <|IMG> ¦ <|PDF> ¦ <|RAW> ¦ <|TXT> ¦ <|WARC> ¦ <|MEDIA> ]` got := ForPublish(&Slack{Cols: collects}).String() if got != message { t.Errorf("Unexpected render template for Slack got \n%s\ninstead of \n%s", got, message) @@ -37,7 +41,11 @@ IPFS: • Archive failed. Telegraph: -• https://web.archive.org/*/https://webcache.googleusercontent.com/search?q=cache:https://example.com/404` +• https://web.archive.org/*/https://webcache.googleusercontent.com/search?q=cache:https://example.com/404 + + + - [ <|IMG> ¦ <|PDF> ¦ <|RAW> ¦ <|TXT> ¦ <|WARC> ¦ <|MEDIA> ] + - [ <|IMG> ¦ <|PDF> ¦ <|RAW> ¦ <|TXT> ¦ <|WARC> ¦ <|MEDIA> ]` got := ForPublish(&Slack{Cols: flawed}).String() if got != message { t.Errorf("Unexpected render template for Slack, got \n%s\ninstead of \n%s", got, message) diff --git a/template/render/telegram.go b/template/render/telegram.go index f9a6b2d4..ec4efc80 100644 --- a/template/render/telegram.go +++ b/template/render/telegram.go @@ -10,12 +10,14 @@ import ( "github.com/wabarc/logger" "github.com/wabarc/wayback" + "github.com/wabarc/wayback/reduxer" ) var _ Renderer = (*Telegram)(nil) type Telegram struct { Cols []wayback.Collect + Data interface{} } func (t *Telegram) ForReply() (r *Render) { @@ -41,7 +43,11 @@ func (t *Telegram) ForReply() (r *Render) { return r } tmplBytes = *bytes.NewBuffer(bytes.TrimSpace(tmplBytes.Bytes())) - tmplBytes.WriteString("\n\n#wayback #存档") + tmplBytes.WriteString("\n") + for _, bundle := range bundles(t.Data) { + t.renderAssets(bundle.Assets, &tmplBytes) + } + tmplBytes.WriteString("\n#wayback #存档") return &Render{buf: tmplBytes} } @@ -49,7 +55,18 @@ func (t *Telegram) ForReply() (r *Render) { func (t *Telegram) ForPublish() (r *Render) { var tmplBytes bytes.Buffer - const tmpl = `{{range $ := .}} + bundle := bundle(t.Data) + if head := Title(bundle); head != "" { + tmplBytes.WriteString("") + tmplBytes.WriteString(head) + tmplBytes.WriteString("\n\n") + } + if dgst := Digest(bundle); dgst != "" { + tmplBytes.WriteString(dgst) + tmplBytes.WriteString("\n\n") + } + + tmpl := `{{range $ := .}} {{ $.Arc | name }}: • source - {{ if $.Dst | isURL }}{{ $.Dst }}{{ else }}{{ $.Dst }}{{ end }} {{ end }}` @@ -59,13 +76,37 @@ func (t *Telegram) ForPublish() (r *Render) { logger.Error("parse Telegram template failed, %v", err) return r } - if err = tpl.Execute(&tmplBytes, t.Cols); err != nil { logger.Error("execute Telegram template failed, %v", err) return r } - tmplBytes.WriteString("\n#wayback #存档") + if bundle != nil { + t.renderAssets(bundle.Assets, &tmplBytes) + } + tmplBytes = *bytes.NewBuffer(bytes.TrimSpace(tmplBytes.Bytes())) + tmplBytes.WriteString("\n\n#wayback #存档") return &Render{buf: tmplBytes} } + +func (t *Telegram) renderAssets(assets reduxer.Assets, tmplBytes *bytes.Buffer) { + tmpl := `AnonFiles - [ IMG ¦ PDF ¦ RAW ¦ TXT ¦ WARC ¦ MEDIA ] +Catbox - [ IMG ¦ PDF ¦ RAW ¦ TXT ¦ WARC ¦ MEDIA ]` + + tpl, err := template.New("assets").Funcs(funcMap()).Parse(tmpl) + if err != nil { + logger.Error("parse Telegram template failed, %v", err) + } + tmplBytes.WriteString("\n") + if err = tpl.Execute(tmplBytes, assets); err != nil { + logger.Error("execute Telegram template failed, %v", err) + } + tmplBytes.WriteString("\n") +} diff --git a/template/render/telegram_test.go b/template/render/telegram_test.go index d369077a..e5e0291e 100644 --- a/template/render/telegram_test.go +++ b/template/render/telegram_test.go @@ -19,16 +19,32 @@ var message = `Internet Archive: Telegraph: • source - http://telegra.ph/title-01-01 - -#wayback #存档` +` func TestRenderTelegram(t *testing.T) { + message := message + ` +AnonFiles - [ IMG ¦ PDF ¦ RAW ¦ TXT ¦ WARC ¦ MEDIA ] +Catbox - [ IMG ¦ PDF ¦ RAW ¦ TXT ¦ WARC ¦ MEDIA ] + +#wayback #存档` got := ForPublish(&Telegram{Cols: collects}).String() if got != message { t.Errorf("Unexpected render template for Telegram got \n%s\ninstead of \n%s", got, message) } } +func TestRenderTelegramForPublishWithRemotely(t *testing.T) { + message := message + ` +AnonFiles - [ IMG ¦ PDF ¦ RAW ¦ TXT ¦ WARC ¦ MEDIA ] +Catbox - [ IMG ¦ PDF ¦ RAW ¦ TXT ¦ WARC ¦ MEDIA ] + +#wayback #存档` + got := ForPublish(&Telegram{Cols: collects, Data: bundleExample}).String() + if got != message { + t.Errorf("Unexpected render template for Telegram got \n%s\ninstead of \n%s", got, message) + } +} + func TestRenderTelegramFlawed(t *testing.T) { message := `Internet Archive: • source - Get "https://web.archive.org/save/https://example.com": context deadline exceeded (Client.Timeout exceeded while awaiting headers) @@ -42,6 +58,9 @@ func TestRenderTelegramFlawed(t *testing.T) { Telegraph: • source - https://web.archive.org/*/https://webcache.googleusercontent.com/search?q=cache:https://example.com/404 +AnonFiles - [ IMG ¦ PDF ¦ RAW ¦ TXT ¦ WARC ¦ MEDIA ] +Catbox - [ IMG ¦ PDF ¦ RAW ¦ TXT ¦ WARC ¦ MEDIA ] + #wayback #存档` got := ForPublish(&Telegram{Cols: flawed}).String() if got != message { diff --git a/template/render/twitter.go b/template/render/twitter.go index 14767785..f4b6c728 100644 --- a/template/render/twitter.go +++ b/template/render/twitter.go @@ -18,6 +18,7 @@ var _ Renderer = (*Twitter)(nil) type Twitter struct { Cols []wayback.Collect + Data interface{} } func (t *Twitter) ForReply() *Render { @@ -55,6 +56,12 @@ func (t *Twitter) ForReply() *Render { func (t *Twitter) ForPublish() *Render { var tmplBytes bytes.Buffer + if head := Title(bundle(t.Data)); head != "" { + tmplBytes.WriteString(`‹ `) + tmplBytes.WriteString(head) + tmplBytes.WriteString(" ›\n\n") + } + const tmpl = `{{range $ := .}}{{ if not $.Arc "ph" }}{{ $.Arc | name }}: • {{ $.Dst }} {{end}}