From 231202b2d3bfdda154b68983f6f712bfc1f50b3a Mon Sep 17 00:00:00 2001 From: Xe Iaso Date: Sat, 11 Jan 2025 15:14:44 -0500 Subject: cmd: add stickers Signed-off-by: Xe Iaso --- cmd/asbestos/html.templ | 114 +++++++++++ cmd/asbestos/html_templ.go | 403 +++++++++++++++++++++++++++++++++++++++ cmd/asbestos/main.go | 100 ++++++++++ cmd/mastodon2atproto/main.go | 245 ++++++++++++++++++++++++ cmd/stickers/data/error.avif | Bin 0 -> 6509 bytes cmd/stickers/data/error.jxl | Bin 0 -> 15104 bytes cmd/stickers/data/error.png | Bin 0 -> 30864 bytes cmd/stickers/data/error.webp | Bin 0 -> 10548 bytes cmd/stickers/data/not_found.avif | Bin 0 -> 6225 bytes cmd/stickers/data/not_found.jxl | Bin 0 -> 14346 bytes cmd/stickers/data/not_found.png | Bin 0 -> 29457 bytes cmd/stickers/data/not_found.webp | Bin 0 -> 9824 bytes cmd/stickers/main.go | 160 ++++++++++++++++ cmd/stickers/main.templ | 52 +++++ cmd/stickers/main_templ.go | 168 ++++++++++++++++ 15 files changed, 1242 insertions(+) create mode 100644 cmd/asbestos/html.templ create mode 100644 cmd/asbestos/html_templ.go create mode 100644 cmd/asbestos/main.go create mode 100644 cmd/mastodon2atproto/main.go create mode 100644 cmd/stickers/data/error.avif create mode 100644 cmd/stickers/data/error.jxl create mode 100644 cmd/stickers/data/error.png create mode 100644 cmd/stickers/data/error.webp create mode 100644 cmd/stickers/data/not_found.avif create mode 100644 cmd/stickers/data/not_found.jxl create mode 100644 cmd/stickers/data/not_found.png create mode 100644 cmd/stickers/data/not_found.webp create mode 100644 cmd/stickers/main.go create mode 100644 cmd/stickers/main.templ create mode 100644 cmd/stickers/main_templ.go (limited to 'cmd') diff --git a/cmd/asbestos/html.templ b/cmd/asbestos/html.templ new file mode 100644 index 0000000..ae57782 --- /dev/null +++ b/cmd/asbestos/html.templ @@ -0,0 +1,114 @@ +package main + +import ( + "within.website/x/htmx" + "within.website/x/xess" +) + +templ NotFound() { +

The URL you requested could not be found. Please check your URL and hang up to try your call again.

+} + +templ allClear() { +

Your data was not found in the dataset. No action is required on your part.

+} + +templ Error(why string) { +

Oopsie whoopsie uwu we made a fucky-wucky! A widdle fucko boingo! The code monkeys at our headquarters are working VEWY HARD to fix this!

+

Reason: { why }

+} + +templ Index() { +

TODO placeholder

+ +
+
+} + +templ headArea() { + @htmx.Use() + +} + +templ footer() { +

+ A product of Techaro + + { "@techaro.lol" } + , the only ethical AI company +

+} + +templ Layout(title string, body templ.Component) { + @xess.Base( + title, + headArea(), + nil, + body, + footer(), + ) +} + +templ searchPage(authorDID string, posts []Post) { + + + + + + + + + + for _, post := range posts { + + + + + + } + +
Created atTextLink
{ post.CreatedAt }{ post.Text }🔗
+
+

Since your data is in this dataset, here's what you can do about it:

+

Compose an email to dmca@huggingface.co with the subject line DMCA Takedown Request and something like the following body (rephrase this in your own words):

+
+ Hello, +
+
+ I am writing to you to inform you that my data is present in the dataset bluesky-community/one-million-bluesky-posts and I did not consent to it being included. I would like to request that you remove my data from the dataset. +
+
+ You can identify my data by searching for the following DID in the author_did column: { authorDID } +
+
+ Thank you for your attention and patience in this matter. +
+
+ Sincerely, +
+
+ { "(Your Name here)" }, { "(Your Email here)" } +
+ { "(identified as" } { authorDID }{ ")" } +
+

For more information, please refer to the dataset page.

+
+} diff --git a/cmd/asbestos/html_templ.go b/cmd/asbestos/html_templ.go new file mode 100644 index 0000000..7ac5c03 --- /dev/null +++ b/cmd/asbestos/html_templ.go @@ -0,0 +1,403 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.2.793 +package main + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +import ( + "within.website/x/htmx" + "within.website/x/xess" +) + +func NotFound() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

The URL you requested could not be found. Please check your URL and hang up to try your call again.

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +func allClear() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var2 := templ.GetChildren(ctx) + if templ_7745c5c3_Var2 == nil { + templ_7745c5c3_Var2 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

Your data was not found in the dataset. No action is required on your part.

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +func Error(why string) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var3 := templ.GetChildren(ctx) + if templ_7745c5c3_Var3 == nil { + templ_7745c5c3_Var3 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

Oopsie whoopsie uwu we made a fucky-wucky! A widdle fucko boingo! The code monkeys at our headquarters are working VEWY HARD to fix this!

Reason: ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var4 string + templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(why) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `html.templ`, Line: 18, Col: 17} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +func Index() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var5 := templ.GetChildren(ctx) + if templ_7745c5c3_Var5 == nil { + templ_7745c5c3_Var5 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

TODO placeholder


") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +func headArea() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var6 := templ.GetChildren(ctx) + if templ_7745c5c3_Var6 == nil { + templ_7745c5c3_Var6 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = htmx.Use().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +func footer() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var7 := templ.GetChildren(ctx) + if templ_7745c5c3_Var7 == nil { + templ_7745c5c3_Var7 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

A product of Techaro ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var8 string + templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs("@techaro.lol") + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `html.templ`, Line: 55, Col: 19} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(", the only ethical AI company

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +func Layout(title string, body templ.Component) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var9 := templ.GetChildren(ctx) + if templ_7745c5c3_Var9 == nil { + templ_7745c5c3_Var9 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = xess.Base( + title, + headArea(), + nil, + body, + footer(), + ).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +func searchPage(authorDID string, posts []Post) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var10 := templ.GetChildren(ctx) + if templ_7745c5c3_Var10 == nil { + templ_7745c5c3_Var10 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + for _, post := range posts { + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Created atTextLink
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var11 string + templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(post.CreatedAt) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `html.templ`, Line: 82, Col: 25} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var12 string + templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(post.Text) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `html.templ`, Line: 83, Col: 20} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("🔗

Since your data is in this dataset, here's what you can do about it:

Compose an email to dmca@huggingface.co with the subject line DMCA Takedown Request and something like the following body (rephrase this in your own words):

Hello,

I am writing to you to inform you that my data is present in the dataset bluesky-community/one-million-bluesky-posts and I did not consent to it being included. I would like to request that you remove my data from the dataset.

You can identify my data by searching for the following DID in the author_did column: ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var14 string + templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(authorDID) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `html.templ`, Line: 99, Col: 119} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

Thank you for your attention and patience in this matter.

Sincerely,

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var15 string + templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs("(Your Name here)") + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `html.templ`, Line: 108, Col: 23} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(", ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var16 string + templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs("(Your Email here)") + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `html.templ`, Line: 108, Col: 48} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var17 string + templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs("(identified as") + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `html.templ`, Line: 110, Col: 21} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var18 string + templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(authorDID) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `html.templ`, Line: 110, Col: 35} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var19 string + templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(")") + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `html.templ`, Line: 110, Col: 42} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

For more information, please refer to the dataset page.

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/cmd/asbestos/main.go b/cmd/asbestos/main.go new file mode 100644 index 0000000..9ab1f8e --- /dev/null +++ b/cmd/asbestos/main.go @@ -0,0 +1,100 @@ +package main + +import ( + "database/sql" + "flag" + "fmt" + "log" + "log/slog" + "net/http" + + "github.com/a-h/templ" + _ "github.com/mattn/go-sqlite3" + "within.website/x/htmx" + "within.website/x/internal" + "within.website/x/xess" +) + +//go:generate go run github.com/a-h/templ/cmd/templ@latest generate + +var ( + bind = flag.String("bind", ":8069", "http port to bind on") + dbURL = flag.String("database-url", "", "path to sqlite database") +) + +func main() { + internal.HandleStartup() + + slog.Info("opening database", "database_url", *dbURL) + db, err := sql.Open("sqlite3", *dbURL) + if err != nil { + log.Fatal(err) + } + defer db.Close() + + s := &Server{db: db} + + mux := http.NewServeMux() + xess.Mount(mux) + htmx.Mount(mux) + + mux.Handle("/{$}", templ.Handler( + Layout("Asbestos", Index()), + )) + + mux.HandleFunc("POST /search", s.searchHTMXPage) + + fmt.Printf("http://localhost%s\n", *bind) + log.Fatal(http.ListenAndServe(*bind, mux)) +} + +type Server struct { + db *sql.DB +} + +type Post struct { + ID int + Text string + CreatedAt string + Author string + URI string + HasImages bool + ReplyTo sql.NullString + BlueskyURL string +} + +func (s *Server) getPostsByAuthor(author string) ([]Post, error) { + rows, err := s.db.Query("SELECT id, text, created_at, author, uri, has_images, reply_to, bluesky_url FROM posts WHERE author = ?", author) + if err != nil { + return nil, err + } + defer rows.Close() + + var posts []Post + for rows.Next() { + var post Post + if err := rows.Scan(&post.ID, &post.Text, &post.CreatedAt, &post.Author, &post.URI, &post.HasImages, &post.ReplyTo, &post.BlueskyURL); err != nil { + return nil, err + } + posts = append(posts, post) + } + return posts, nil +} + +func (s *Server) searchHTMXPage(w http.ResponseWriter, r *http.Request) { + query := r.FormValue("search") + + posts, err := s.getPostsByAuthor(query) + if err != nil { + slog.Error("can't find posts", "author", query, "err", err) + templ.Handler(Error(err.Error())).ServeHTTP(w, r) + return + } + + if len(posts) == 0 { + templ.Handler(allClear()).ServeHTTP(w, r) + return + } + + templ.Handler(searchPage(query, posts)).ServeHTTPStreamed(w, r) +} diff --git a/cmd/mastodon2atproto/main.go b/cmd/mastodon2atproto/main.go new file mode 100644 index 0000000..a8957a8 --- /dev/null +++ b/cmd/mastodon2atproto/main.go @@ -0,0 +1,245 @@ +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "image" + "image/jpeg" + _ "image/png" + "log" + "log/slog" + "os" + "path/filepath" + "strings" + "time" + + "github.com/bluesky-social/indigo/api/atproto" + lexutil "github.com/bluesky-social/indigo/lex/util" + "github.com/rivo/uniseg" + "within.website/x/internal" + "within.website/x/web/bskybot" + "within.website/x/web/mastosan" +) + +var ( + bskyAuthkey = flag.String("bsky-authkey", "", "Bluesky authkey") + bskyHandle = flag.String("bsky-handle", "", "Bluesky handle/email") + bskyPDS = flag.String("bsky-pds", "https://bsky.social", "Bluesky PDS") + + afterStr = flag.String("after", "2017-04-22T04:59:14Z", "only consider posts after this point") +) + +func main() { + internal.HandleStartup() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + agent := bskybot.NewAgent(ctx, *bskyPDS, *bskyHandle, *bskyAuthkey) + if err := agent.Connect(ctx); err != nil { + log.Fatal(err) + } + + if flag.NArg() != 1 { + fmt.Println("Usage: mastodon2atproto [flags] ") + } + + dir := flag.Arg(0) + + fin, err := os.Open(filepath.Join(dir, "outbox.json")) + if err != nil { + log.Fatal(err) + } + defer fin.Close() + + after, err := time.Parse(time.RFC3339, *afterStr) + if err != nil { + log.Fatalf("can't parse after time: %v", err) + } + + var oc OrderedCollection + if err := json.NewDecoder(fin).Decode(&oc); err != nil { + log.Fatal(err) + } + + slog.Info("found items", "count", oc.TotalItems) + + var out []Object + + for _, item := range oc.OrderedItems { + slog.Debug("found item", "type", item.Type) + + if item.Type == "Announce" { + continue + } + + var obj Object + if err := json.Unmarshal(item.Object, &obj); err != nil { + log.Fatal(err) + } + + if obj.InReplyTo != nil { + continue + } + + text, err := mastosan.Text(ctx, obj.Content) + if err != nil { + log.Fatal(err) + } + + if strings.HasPrefix(text, "@") { + continue + } + + if uniseg.GraphemeClusterCount(text) > 300 { + continue + } + + if obj.Published.Before(after) { + continue + } + + skip := false + for _, att := range obj.Attachment { + if !strings.HasPrefix(att.URL, "image/") { + skip = true + } + } + + if skip { + continue + } + + obj.Content = text + + slog.Debug("found post", "text", text, "published", obj.Published) + + out = append(out, obj) + } + + slog.Info("found candidate posts for migration", "len", len(out)) + + t := time.NewTicker(5 * time.Second) + defer t.Stop() + + for _, obj := range out { + <-t.C + + slog.Info("migrating post", "post", obj) + + pb := bskybot.NewPostBuilder(obj.Content). + AtTime(obj.Published) + + if len(obj.Attachment) != 0 { + var blobs []lexutil.LexBlob + var images []bskybot.Image + for _, att := range obj.Attachment { + att.URL = strings.TrimPrefix(att.URL, "/interlinked-mst3k") + fname := filepath.Join(dir, att.URL) + + st, err := os.Stat(fname) + if err != nil { + log.Fatalf("can't stat file: %v", err) + } + if st.Size() >= 976560 { + fname, err = convertToJPG(fname) + if err != nil { + log.Fatalf("can't convert to jpeg: %v", err) + } + } + + fin, err := os.Open(fname) + if err != nil { + log.Fatalf("can't open file: %v", err) + } + + resp, err := atproto.RepoUploadBlob(ctx, agent.Client(), fin) + if err != nil { + log.Fatalf("can't upload blob: %v", err) + } + + blobs = append(blobs, lexutil.LexBlob{ + Ref: resp.Blob.Ref, + MimeType: resp.Blob.MimeType, + Size: resp.Blob.Size, + }) + images = append(images, bskybot.Image{}) + } + + pb = pb.WithImages(blobs, images) + } + + post, err := pb.Build() + if err != nil { + log.Fatalf("can't build post: %v", err) + } + + cid, uri, err := agent.PostToFeed(ctx, post) + if err != nil { + log.Fatalf("can't post to feed: %v", err) + } + + pathSp := strings.Split(uri, "/") + id := pathSp[len(pathSp)-1] + + postURL := fmt.Sprintf("https://bsky.app/profile/%s/post/%s", *bskyHandle, id) + + slog.Info("reposted old post", "at", obj.Published, "cid", cid, "uri", uri, "url", postURL) + } +} + +type OrderedCollection struct { + TotalItems int `json:"totalItems"` + OrderedItems []Item `json:"orderedItems"` +} + +type Item struct { + Type string `json:"type"` + Object json.RawMessage `json:"object"` +} + +type Object struct { + ID string `json:"id"` + To []string `json:"to"` + CC []string `json:"cc"` + InReplyTo *string `json:"inReplyTo"` + Published time.Time `json:"published"` + Content string `json:"content"` + Attachment []Attachment `json:"attachment"` + DirectMessage bool `json:"directMessage"` +} + +type Attachment struct { + MediaType string `json:"mediaType"` + URL string `json:"url"` + Name string `json:"name"` +} + +func convertToJPG(fname string) (string, error) { + fin, err := os.Open(fname) + if err != nil { + return "", fmt.Errorf("failed to open %s: %w", fname, err) + } + defer fin.Close() + + img, _, err := image.Decode(fin) + if err != nil { + return "", fmt.Errorf("failed to decode PNG file: %w", err) + } + + outFileName := fname[:len(fname)-len(filepath.Ext(fname))] + ".jpeg" + outFile, err := os.Create(outFileName) + if err != nil { + return "", fmt.Errorf("failed to create JPEG file: %w", err) + } + defer outFile.Close() + + options := jpeg.Options{Quality: 85} + if err := jpeg.Encode(outFile, img, &options); err != nil { + return "", fmt.Errorf("failed to encode JPEG file: %w", err) + } + + return outFileName, nil +} diff --git a/cmd/stickers/data/error.avif b/cmd/stickers/data/error.avif new file mode 100644 index 0000000..505da03 Binary files /dev/null and b/cmd/stickers/data/error.avif differ diff --git a/cmd/stickers/data/error.jxl b/cmd/stickers/data/error.jxl new file mode 100644 index 0000000..cb6cf89 Binary files /dev/null and b/cmd/stickers/data/error.jxl differ diff --git a/cmd/stickers/data/error.png b/cmd/stickers/data/error.png new file mode 100644 index 0000000..3143cdf Binary files /dev/null and b/cmd/stickers/data/error.png differ diff --git a/cmd/stickers/data/error.webp b/cmd/stickers/data/error.webp new file mode 100644 index 0000000..ebd4604 Binary files /dev/null and b/cmd/stickers/data/error.webp differ diff --git a/cmd/stickers/data/not_found.avif b/cmd/stickers/data/not_found.avif new file mode 100644 index 0000000..fa2e6de Binary files /dev/null and b/cmd/stickers/data/not_found.avif differ diff --git a/cmd/stickers/data/not_found.jxl b/cmd/stickers/data/not_found.jxl new file mode 100644 index 0000000..f7cc7df Binary files /dev/null and b/cmd/stickers/data/not_found.jxl differ diff --git a/cmd/stickers/data/not_found.png b/cmd/stickers/data/not_found.png new file mode 100644 index 0000000..043e0d0 Binary files /dev/null and b/cmd/stickers/data/not_found.png differ diff --git a/cmd/stickers/data/not_found.webp b/cmd/stickers/data/not_found.webp new file mode 100644 index 0000000..42188fb Binary files /dev/null and b/cmd/stickers/data/not_found.webp differ diff --git a/cmd/stickers/main.go b/cmd/stickers/main.go new file mode 100644 index 0000000..67f1e83 --- /dev/null +++ b/cmd/stickers/main.go @@ -0,0 +1,160 @@ +package main + +import ( + "context" + "embed" + "flag" + "fmt" + "log" + "log/slog" + "net/http" + "strings" + "sync" + "time" + + "github.com/a-h/templ" + "github.com/aws/aws-sdk-go-v2/aws" + v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4" + awsConfig "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/s3" + "within.website/x/htmx" + "within.website/x/internal" + "within.website/x/xess" +) + +//go:generate go run github.com/a-h/templ/cmd/templ@latest generate + +var ( + bind = flag.String("bind", ":3923", "TCP address to bind to") + bucketName = flag.String("bucket-name", "xedn", "Bucket to pull from") + folderName = flag.String("folder-name", "stickers", "Folder name for stickers") + + //go:embed data/* + static embed.FS + + cache = map[string]struct{}{} + cacheLock = sync.RWMutex{} +) + +func main() { + internal.HandleStartup() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + cfg, err := awsConfig.LoadDefaultConfig(ctx) + if err != nil { + log.Fatal(err) + } + + s3c := s3.NewFromConfig(cfg, func(o *s3.Options) { + o.UsePathStyle = true + }) + + presigner := Presigner{s3.NewPresignClient(s3c)} + _ = presigner + + mux := http.NewServeMux() + htmx.Mount(mux) + xess.Mount(mux) + + mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) { + character := r.FormValue("character") + mood := r.FormValue("mood") + + templ.Handler( + xess.Simple("Sticker Tester", index(character, mood)), + ).ServeHTTP(w, r) + }) + + mux.HandleFunc("GET /sticker/{name}/{mood}", func(w http.ResponseWriter, r *http.Request) { + acc := strings.Split(r.Header.Get("Accept"), ",") + + name := r.PathValue("name") + mood := r.PathValue("mood") + + format := "png" + for _, acceptFormat := range acc { + _, theirFormat, ok := strings.Cut(acceptFormat, "image/") + if !ok { + continue + } + + switch theirFormat { + case "avif", "webp": + format = theirFormat + } + } + + key := fmt.Sprintf("%s/%s/%s.%s", *folderName, name, mood, format) + + if !inCache(name, mood) { + _, err := s3c.HeadObject(r.Context(), &s3.HeadObjectInput{ + Bucket: aws.String(*bucketName), + Key: aws.String(key), + }) + if err != nil { + slog.Error("can't head key", "format", format, "bucket", *bucketName, "key", key, "err", err) + http.ServeFileFS(w, r, static, "data/not_found."+format) + return + } + + setCache(name, mood) + } + + req, err := presigner.GetObject(r.Context(), key, 3600) + if err != nil { + slog.Error("can't presign get for key", "format", format, "bucket", *bucketName, "key", key, "err", err) + http.ServeFileFS(w, r, static, "data/error."+format) + return + } + + http.Redirect(w, r, req.URL, http.StatusTemporaryRedirect) + }) + + slog.Info("listening", "bind", *bind) + log.Fatal(http.ListenAndServe(*bind, mux)) +} + +// Presigner encapsulates the Amazon Simple Storage Service (Amazon S3) presign actions +// used in the examples. +// It contains PresignClient, a client that is used to presign requests to Amazon S3. +// Presigned requests contain temporary credentials and can be made from any HTTP client. +type Presigner struct { + PresignClient *s3.PresignClient +} + +// GetObject makes a presigned request that can be used to get an object from a bucket. +// The presigned request is valid for the specified number of seconds. +func (presigner Presigner) GetObject( + ctx context.Context, + objectKey string, + lifetimeSecs int64, +) (*v4.PresignedHTTPRequest, error) { + request, err := presigner.PresignClient.PresignGetObject(ctx, &s3.GetObjectInput{ + Bucket: aws.String(*bucketName), + Key: aws.String(objectKey), + }, func(opts *s3.PresignOptions) { + opts.Expires = time.Duration(lifetimeSecs * int64(time.Second)) + }) + if err != nil { + log.Printf("Couldn't get a presigned request to get %v:%v. Here's why: %v\n", + bucketName, objectKey, err) + } + return request, err +} + +func inCache(character, mood string) bool { + cacheLock.RLock() + defer cacheLock.RUnlock() + + _, ok := cache[character+" "+mood] + return ok +} + +func setCache(character, mood string) { + cacheLock.Lock() + defer cacheLock.Unlock() + + cache[character+" "+mood] = struct{}{} +} diff --git a/cmd/stickers/main.templ b/cmd/stickers/main.templ new file mode 100644 index 0000000..dab6770 --- /dev/null +++ b/cmd/stickers/main.templ @@ -0,0 +1,52 @@ +package main + +import ( + "fmt" + "strings" +) + +templ index(character, mood string) { +
+ + + +
+ if character != "" && mood != "" { +
+ +

Inline

+
+
+ { +
+
+ <{ character }> + Have you ever been far even as decided to use even go want to do look more like? +
+
+

Standalone

+
+
+ { +
+
+ <{ character }> + Have you ever been far even as decided to use even go want to do look more like? +
+
+
+ } +} diff --git a/cmd/stickers/main_templ.go b/cmd/stickers/main_templ.go new file mode 100644 index 0000000..b692051 --- /dev/null +++ b/cmd/stickers/main_templ.go @@ -0,0 +1,168 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.3.819 +package main + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +import ( + "fmt" + "strings" +) + +func index(character, mood string) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if character != "" && mood != "" { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + return nil + }) +} + +var _ = templruntime.GeneratedTemplate -- cgit v1.2.3