-- Leo's gemini proxy

-- Connecting to git.thebackupbox.net:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/gemini

repo: actpub
action: commit
revision:
path_from:
revision_from: 530ca5a061b6219605380fee4abcf6c8675cb357:
path_to:
revision_to:

git.thebackupbox.net

actpub

git://git.thebackupbox.net/actpub

commit 530ca5a061b6219605380fee4abcf6c8675cb357
Author: epoch <epoch@thebackupbox.net>
Date:   Sat Feb 18 03:59:21 2023 +0000

    changed a lot of things. mostly changed "object" to "actor" in all places.

diff --git a/README b/README
new file mode 100644
index 0000000000000000000000000000000000000000..6930f5791f143d8a069576d57734931061b7cd30
--- /dev/null
+++ b/README
@@ -0,0 +1 @@
+https://www.w3.org/TR/activitypub/
diff --git a/ap-activity2html b/ap-activity2html

index 8071673029a62ed53056f00ef7c44bbb45adc570..

index ..8038c456d6e039c8be2c67051d0e1db7fccd866b 100755

--- a/ap-activity2html
+++ b/ap-activity2html
@@ -13,56 +13,54 @@ if [ "$type" = "Undo" ];then
 fi

 if [ "$(jq -r '.to | type' < "${activity_file}" )" = "array" ];then
-  if jq -r '.to[]' < "${activity_file}" | grep -vF 'https://www.w3.org/ns/activitystreams#Public' >/dev/null 2>&1;then
-    printf 'non-public post not shown. %s<br/>' "${activity_file}"
-    exit 0
-  else
+  #if jq -r '.to[]' < "${activity_file}" | grep -vF 'https://www.w3.org/ns/activitystreams#Public' >/dev/null 2>&1;then
+  #  printf 'non-public post not shown. %s<br/>' "${activity_file}"
+  #  exit 0
+  #else
     printf 'public post: <a href="%s">%s</a><br/>' "${activity_file}" "${activity_file}"
-  fi
+  #fi
 else
   printf "no to: found, guess public. *shrugs*\n"
 fi

 printf '<div class="actdiv">\n'
-printf "Id: %s<br/>\n" "$(jq -r '.id' 2>/dev/null < "${activity_file}" | html_entities_encode)"
+id="$(jq -r '.id' 2>/dev/null < "${activity_file}")"
+printf "Id: <a href="%s">%s</a><br/>\n" "$(html_entities_encode <<< "$id")" "$(html_entities_encode <<< "$id")"

 actor="$(jq -r '.actor' 2>/dev/null < "${activity_file}" | html_entities_encode)"

-acct="$(ap-object2acct "${actor}" 2>/dev/null | uricut -p 2>/dev/null)"
+acct="$(ap-actor2acct "${actor}" 2>/dev/null | uricut -p 2>/dev/null)"
 printf 'Actor: <a href="%s">%s</a><br/>\n' "${actor}" "${actor}"
 printf 'Acct: <a href="acct:%s">%s</a><br/>\n' "${acct}" "${acct}"
-icon="$(ap-getobject "${acct}" 2>/dev/null | jq -r .icon.url | html_entities_encode)"
+icon="$(ap-getactor "${acct}" 2>/dev/null | jq -r .icon.url | html_entities_encode)"
 printf 'Icon: <img src="%s" width="128" height="128">' "${icon}"

 printf 'Type: %s<br/>\n' "$(html_entities_encode <<< "$type")"

 objType="$(jq -r '.object | type' < "${activity_file}")"
+printf "Created: %s<br/>\n" "$(jq -r '.signature.created' 2>/dev/null < "${activity_file}" | html_entities_encode)"
 printf "ObjType: %s<br/>" "${objType}"
-if [ "$objType" = "string" ];then
-  object="$(jq -r '.object' < "${activity_file}")"
-  printf "Object: <a href="%s">%s</a><br/>" "${object}" "${object}"
-else
-  inReplyTo="$(jq -r '.object.inReplyTo//""' 2>/dev/null < "${activity_file}" | html_entities_encode)"
-  if [ "$inReplyTo" ];then
-    printf "inReplyTo: <a href="%s">%s</a><br/>\n" "${inReplyTo}" "${inReplyTo}"
-  fi
-  #printf "Content: %s<br/>\n" "$(jq -r '.object.content' 2>/dev/null < "${activity_file}" | html_entities_encode)"
-  printf "Content: %s<br/>\n" "$(jq -r '.object.content' 2>/dev/null < "${activity_file}")"
-  printf "Created: %s<br/>\n" "$(jq -r '.signature.created' 2>/dev/null < "${activity_file}" | html_entities_encode)"
-  if [ "$(jq -r '.object.attachement | type' < "${activity_file}")" == "array" ];then
-    jq -r '.object.attachment | .[] | .mediaType,.url' 2>/dev/null < "${activity_file}" | paste - - | html_entities_encode | while read type url;do
-      case "$type" in
-        image/*)
-          printf 'attachment: <img src="%s" /><br/>' "$url"
-          ;;
-        video/*)
-          printf 'attachment: <video loop autoplay><source src="%s" type="%s"></video><br/>' "$url" "$type"
-          ;;
-        *)
-          printf '<a href="%s">attachment</a><br/>' "$url"
-          ;;
-      esac
-    done
-  fi
-fi
+
+case "$objType" in
+string)
+  uri="$(jq -r '.object' < "${activity_file}")"
+  printf 'Object: <a href="%s">%s</a><br/>' "${uri}" "${uri}"
+  cache_file="$(ap-cacheobject "${uri}")"
+  printf 'Cache_File: %s\n' "${cache_file}"
+  ap-object2html "${cache_file}" 2>&1
+  ;;
+object)
+  uri="$(jq -r ".object.id" < "${activity_file}")"
+  cache_file=~/.cache/ap/object/"$(sha256sum <<< "$uri" | cut '-d ' -f1)"
+  jq -r .object < "${activity_file}" > "${cache_file}"
+  printf "Cache_File: %s\n" "${cache_file}"
+  ap-object2html "${cache_file}" 2>&1
+  ;;
+*)
+  printf "not sure how to handle this object."
+  printf "Object: "
+  jq -r '.object' < "${activity_file}"
+  ;;
+esac
+
 printf '</div>'
diff --git a/ap-actor.cgi b/ap-actor.cgi
new file mode 100755
index 0000000000000000000000000000000000000000..bf5a7c9b5cdd8283cfa2ca70f7461bc4cdf9eaf8
--- /dev/null
+++ b/ap-actor.cgi
@@ -0,0 +1,144 @@
+#!/usr/bin/env bash
+
+### we're going to use $USER for all this shit.
+### and $SERVER_NAME
+
+### I dunno. I need to grab these values from somewhere.
+
+# HTTP_ACCEPT: application/activity+json, application/ld+json
+
+### TODO: make use of If-Modified-Since header. your/my fmrl code has useful examples.
+	printf "Status: 200 OK\r\n"
+	printf "Date: %s\r\n" "$(rfc7231date)"
+	printf "Content-Security-Policy: \r\n"
+	printf "Cache-Control: max-age=180\r\n"
+	printf "Server: epochttpd\r\n"
+	printf "Content-Type: application/activity+json; charset=utf-8\r\n\r\n"
+	#printf "Content-Type: application/ld+json\r\n\r\n"
+	PUBLIC_KEY="$(cat public.pem | jq --slurp -R)"
+cat <<EOF
+{
+	"@context": [
+		"https://www.w3.org/ns/activitystreams",
+		"https://w3id.org/security/v1"
+EOF
+### all of this stuff seems optional based on pleoroma response
+cat <<EOF >/dev/null #long comment. lol
+		{
+			"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
+			"toot": "http://joinmastodon.org/ns#",
+			"featured": {
+				"@id": "toot:featured",
+				"@type": "@id"
+			},
+			"featuredTags": {
+				"@id": "toot:featuredTags",
+				"@type": "@id"
+			},
+			"alsoKnownAs": {
+				"@id": "as:alsoKnownAs",
+				"@type": "@id"
+			},
+			"movedTo": {
+				"@id": "as:movedTo",
+				"@type": "@id"
+			},
+			"schema": "http://schema.org#",
+			"PropertyValue": "schema:PropertyValue",
+			"value": "schema:value",
+			"discoverable": "toot:discoverable",
+			"Device": "toot:Device",
+			"Ed25519Signature": "toot:Ed25519Signature",
+			"Ed25519Key": "toot:Ed25519Key",
+			"Curve25519Key": "toot:Curve25519Key",
+			"EncryptedMessage": "toot:EncryptedMessage",
+			"publicKeyBase64": "toot:publicKeyBase64",
+			"deviceId": "toot:deviceId",
+			"claim": {
+				"@type": "@id",
+				"@id": "toot:claim"
+			},
+			"fingerprintKey": {
+				"@type": "@id",
+				"@id": "toot:fingerprintKey"
+			},
+			"identityKey": {
+				"@type": "@id",
+				"@id": "toot:identityKey"
+			},
+			"devices": {
+				"@type": "@id",
+				"@id": "toot:devices"
+			},
+			"messageFranking": "toot:messageFranking",
+			"messageType": "toot:messageType",
+			"cipherText": "toot:cipherText",
+			"suspended": "toot:suspended",
+			"focalPoint": {
+				"@container": "@list",
+				"@id": "toot:focalPoint"
+			}
+		}
+EOF
+cat <<EOF
+	],
+
+	"id": "https://${SERVER_NAME}/~${USER}/",
+	"type": "Person",
+	"following": "https://${SERVER_NAME}/~${USER}/following/",
+	"followers": "https://${SERVER_NAME}/~${USER}/followers/",
+	"inbox": "https://${SERVER_NAME}/~${USER}/inbox/",
+	"outbox": "https://${SERVER_NAME}/~${USER}/outbox/",
+	"featured": "",
+	"featuredTags": "",
+	"name": "${USER}",
+	"preferredUsername": "${USER}",
+	"summary": "<p>hacks</p>",
+	"url": "https://${SERVER_NAME}/~${USER}/",
+	"manuallyApprovesFollowers": false,
+	"discoverable": true,
+	"published": "1970-01-01T03:13:37Z",
+	"devices": "https://${SERVER_NAME}/~${USER}/collections/devices/",
+	"publicKey": {
+		"id": "https://${SERVER_NAME}/~${USER}/#main-key",
+		"owner": "https://${SERVER_NAME}/~${USER}/",
+EOF
+printf '		"publicKeyPem": %s' "$PUBLIC_KEY"
+cat <<EOF
+	},
+	"tag": [],
+	"attachment": [
+		{
+			"type": "PropertyValue",
+			"name": "h-card",
+			"value": "<a href=\"https://${SERVER_NAME}/~${USER}/\">https://${SERVER_NAME}/~${USER}/</a>"
+		},
+		{
+			"type": "PropertyValue",
+			"name": "hobbies",
+			"value": "void staring"
+		}
+	],
+	"endpoints": {
+		"sharedInbox": "https://${SERVER_NAME}/inbox/"
+	},
+EOF
+### blocks go here for icon (pfp/avatar) and image (banner, whatever)
+cat << EOF
+	"image": {
+		"type": "Image",
+		"mediaType": "image/jpeg",
+		"url": "https://${SERVER_NAME}/~${USER}/image.jpg"
+	},
+EOF
+cat <<EOF
+	"icon": {
+		"type": "Image",
+		"mediaType": "image/png",
+		"url": "https://${SERVER_NAME}/~${USER}/icon.png"
+	}
+EOF
+#		"url": "https://${SERVER_NAME}/cgi-bin/xface.cgi?IlrD8fxN=HlbdLwwiJkkn=qs4%3EtlW=8)AJX~vDBf8=,%7B4m5iD0Rt@uJMy%25c%5C%5Ez%3E0%25.N\$,QXXY)A@GPVl%3CtFo3A%605XDxcUh%7BtZj=]%3Co[%5EGH-Thr%7Cl+F)x~j%25Gb4U!XyXX;?a(hC6@yPNl5V%7B7[\$~P=M"
+cat <<EOF
+}
+EOF
diff --git a/ap-object2acct b/ap-actor2acct
similarity index 81%
rename from ap-object2acct
rename to ap-actor2acct

index 5ab5a26b7b8d5f7359f283a7acdf50b8515e83b0..

index ..8e12a37b8edd991de0ecb1cb301ef49f35a4a52d 100755

--- a/ap-object2acct
+++ b/ap-actor2acct
@@ -1,7 +1,7 @@
 #!/usr/bin/env bash

 if [ ! "$1" ];then
-  printf "arg1 needs to be the object URI of someone (aka actor uri)\n" >&2
+  printf "usage: <actor URL>\n" >&2
   exit 1
 fi

diff --git a/ap-cacheobject b/ap-cacheobject
new file mode 100755
index 0000000000000000000000000000000000000000..0722c28c34c0daeb8de4d2da61e8cf762593d037
--- /dev/null
+++ b/ap-cacheobject
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+if [ ! "$1" ];then
+  printf 'Usage: ap-cacheobject <uri>\n' >&2
+  exit 1
+fi
+
+uri="$1"
+
+cache_file=~/.cache/ap/object/"$(sha256sum <<< "$uri" | cut '-d ' -f1)"
+
+if [ ! -s "$cache_file" ];then
+  curl -so "${cache_file}" -H "Accept: application/activity+json, application/ld+json" -g "$uri"
+fi
+
+printf "%s\n" "${cache_file}"
diff --git a/ap-getactor b/ap-getactor
new file mode 100755
index 0000000000000000000000000000000000000000..a5db0d7129610687f383894890574f8da09b82e2
--- /dev/null
+++ b/ap-getactor
@@ -0,0 +1,26 @@
+#!/bin/bash
+no_cache=0
+if [ "$1" == "--no-cache" ];then
+  no_cache=1
+  shift
+fi
+user="$(printf "%s\n" "$1" | sed 's/^@//' | cut -d@ -f1)"
+domain="$(printf "%s\n" "$1" | sed 's/^@//' | cut -d@ -f2)"
+target="${user}@${domain}"
+mkdir -p ~/.cache/ap/actor
+cache_file=~/.cache/ap/actor/${target}
+if [ ! -s ${cache_file} -o "${no_cache}" = 1 ];then
+  printf "downloading actor file for %s@%s\n" "${user}" "${domain}" >&2
+  find ~/.cache/ap/actor -mtime 30 -rm # clear cache after 30 minutes.
+  ### I want to get the Cache-Control: max-age=180, derp header,
+  ### then get the max-age's value,
+  ### then set the mtime of the file that far into the future.
+  ### so I can find -mtime 0 . -rm on all of the object files. anything in the past gets deleted.
+  curl -s -o "${cache_file}" -H "Accept: application/activity+json, application/ld+json" -g "$(ap-getactoruri "$target")"
+  #id="$(date +%s)"
+  #touch -mt "/tmp/${id}"
+  #mv "/tmp/${id}" "${cache_file}"
+else
+  printf "using cached actor file for %s@%s\n" "${user}" "${domain}" >&2
+fi
+cat "${cache_file}"
diff --git a/ap-getobjecturi b/ap-getactoruri
similarity index 100%
rename from ap-getobjecturi
rename to ap-getactoruri
diff --git a/ap-getattachmentlist b/ap-getattachmentlist

index 3cfb01ad8d099c5d85affd326681756b61cd1f71..

index ..6ec24685d7f8efb63554335f6d8ba778971464c5 100755

--- a/ap-getattachmentlist
+++ b/ap-getattachmentlist
@@ -1,5 +1,5 @@
 #!/bin/sh
-ap-getobject "$1" \
+ap-getactor "$1" \
   | jq -r '.attachment[] | .name,.value' \
   | sed 's/<\([^>]*\)>//g' \
   | html_entities_decode \
diff --git a/ap-geticon b/ap-geticon

index 743520955d319d3fa7e8b5e5a0e19111355ea866..

index ..8e80e3464717c5834b25b0c130c5618c18b6103d 100755

--- a/ap-geticon
+++ b/ap-geticon
@@ -1,5 +1,5 @@
 #!/bin/sh
-ap-getobject "$1" \
+ap-getactor "$1" \
   | jq -r .icon.url \
   | tr '\n' '\0' \
   | xargs -0 curl -g \
diff --git a/ap-getimage b/ap-getimage

index b63ea0f022468f2853e013d7316ff1cdf695cf94..

index ..a7e2603642a97f1b1614b7f25d86bfda819c02b0 100755

--- a/ap-getimage
+++ b/ap-getimage
@@ -1,5 +1,5 @@
 #!/bin/sh
-ap-getobject "$1" \
+ap-getactor "$1" \
   | jq -r .image.url \
   | xargs curl -g \
   | img2sixel -w 100% -
diff --git a/ap-getobject b/ap-getobject
deleted file mode 100755
index 5022566ac0e42d59e24ac1f2737ba166999e33d2..0000000000000000000000000000000000000000
--- a/ap-getobject
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-no_cache=0
-if [ "$1" == "--no-cache" ];then
-  no_cache=1
-  shift
-fi
-user="$(printf "%s\n" "$1" | sed 's/^@//' | cut -d@ -f1)"
-domain="$(printf "%s\n" "$1" | sed 's/^@//' | cut -d@ -f2)"
-target="${user}@${domain}"
-mkdir -p ~/.cache/ap/object
-cache_file=~/.cache/ap/object/${target}
-if [ ! -s ${cache_file} -o "${no_cache}" = 1 ];then
-  printf "downloading object file for %s@%s\n" "${user}" "${domain}" >&2
-  curl -s -o "${cache_file}" -H "Accept: application/activity+json, application/ld+json" -g "$(ap-getobjecturi "$target")"
-else
-  printf "using cached object file for %s@%s\n" "${user}" "${domain}" >&2
-fi
-cat "${cache_file}"
diff --git a/ap-inbox.cgi b/ap-inbox.cgi

index 89e0d0ab38a5b3270b8c88a4317f73ce3aeab10c..

index ..5a445f963310115171b72c489eb85bdd39194153 100755

--- a/ap-inbox.cgi
+++ b/ap-inbox.cgi
@@ -1,6 +1,6 @@
 #!/usr/bin/env bash

-logger "ap-inbox at ${SCRIPT_NAME} requested! HTTP_ACCEPT: ${HTTP_ACCEPT}"
+#logger "ap-inbox at ${SCRIPT_NAME} requested! HTTP_ACCEPT: ${HTTP_ACCEPT}"

 #if grep -E '' <<< "${HTTP_ACCEPT}";then

@@ -17,42 +17,62 @@ if [ "$PATH_INFO" = "" ];then
 	if [ "$REQUEST_METHOD" = "GET" ];then
 		first="$(ls | sort -rn | grep '^[0-9]' | head -n1)"
 		at="$(query_param "at")"
+		before="$(query_param "before" | tr -cd '0-9')"
+		reload="$(query_param "reload" | tr -cd '0-9')"
+		perpage="$(query_param "perpage" | tr -cd '0-9')"
+		if [ "${before}" ];then
+			printf 'Content-Type: text/html\r\n\r\n'
+			if [ "$reload" ];then
+				printf '<div hx-get="?before=%s&amp;reload=%s" hx-trigger="every %ss" hx-swap="outerHTML">' "${first}" "${reload}" "${reload}"
+				printf 'last check %s<br/>' "$(date)"
+				printf 'next check in %s seconds<br/>' "${reload}"
+			fi
+			printf '<a href="?before=%s">click here for only the newer posts than currently displayed.</a><br/>' "${first}"
+			newer_posts="$(ls | sort -rn | grep '^[0-9]' | grep -B 1000 -F "${before}" | tac | tail -n+2 | tac)"
+			printf 'newer posts than %s: %s' "${before}" "$newer_posts"
+			printf '</div>'
+			ls | sort -rn | grep '^[0-9]' | grep -B 1000 -F "${before}" | tac | tail -n+2 | tac | while read next_file;do
+				ap-activity2html "${next_file}"
+			done
+			exit 0
+		fi
 		if [ "${at}" ] ;then
+			per_page=8
+			total="$(ls | sort -rn | grep -c '^[0-9]')"
+			at="$(query_param at | tr -cd '0-9')"
+			if [ ! "$at" ];then
+				at="$(ls | sort -rn | head -n1)"
+			else
+				if [ ! -e "$at" ];then
+					printf "Status: 404 Not Found\r\n"
+					printf "Content-Type: text/plain\r\n\r\n"
+					printf "the note you are looking for can not be found."
+				fi
+			fi
+			next_at="$(ls | grep '^[0-9]' | sort -rn | grep -A "${per_page}" -F "${at}" | tail -n+$[${per_page}+1])"

-per_page=8
-total="$(ls | sort -rn | grep -c '^[0-9]')"
-at="$(query_param at | tr -cd '0-9')"
-if [ ! "$at" ];then
-  at="$(ls | sort -rn | head -n1)"
-else
-  if [ ! -e "$at" ];then
-    printf "Status: 404 Not Found\r\n"
-    printf "Content-Type: text/plain\r\n\r\n"
-    printf "the note you are looking for can not be found."
-  fi
-fi
-next_at="$(ls | grep '^[0-9]' | sort -rn | grep -A "${per_page}" -F "${at}" | tail -n+$[${per_page}+1])"
-
-printf "Content-Type: text/html\r\n\r\n"
-
-ls | grep '^[0-9]' | sort -rn | grep -A "$[${per_page}-1]" -F "${at}" | while read next_file;do
-  if [ "$next_file" ];then
-    ap-activity2html "$next_file"
-  else
-    printf "<div>not really infinite. you got to the end.</div>"
-  fi
-done
+			printf "Content-Type: text/html\r\n\r\n"

-if [ "${next_at}" ];then
-  printf '<div hx-get="?at=%s" hx-trigger="revealed" hx-swap="replace">loading loading loading... keep them notes a loading... RAW HIDE!</div>\n' "${next_at}"
-else
-  printf "this is the end. total of %s things\n" "$total"
-fi
-exit 0
+			ls | grep '^[0-9]' | sort -rn | grep -A "$[${per_page}-1]" -F "${at}" | while read next_file;do
+				if [ "$next_file" ];then
+					ap-activity2html "$next_file"
+				else
+					printf "<div>not really infinite. you got to the end.</div>"
+				fi
+			done
+			if [ "${next_at}" ];then
+				printf '<div hx-get="?at=%s" hx-trigger="revealed" hx-swap="replace">' "${next_at}"
+				printf 'loading loading loading... keep them notes a loading... RAW HIDE!'
+				printf '<a href="?at=%s">no javascript? load the next %s posts</a>' "${next_at}" "${per_page}"
+				printf '</div>\n'
+			else
+				printf "this is the end. total of %s things\n" "$total"
+			fi
+			exit 0
 		fi
 		printf "Content-Type: text/html\r\n\r\n"
 cat <<EOF
-<html>
+<html id="top">
   <head>
   <title>htmx fediverse inbox infinite scroll</title>
   <script src="/htmx.org.1.7.0.js"></script>
@@ -63,11 +83,43 @@ cat <<EOF
         border-color: pink;
         max-width: 700px;
         margin: auto;
+}
+.objdiv {
+	border-style: solid;
+	border-width: 3px;
+	border-color blue;
+	max-width: 700px;
+	margin: auto;
+}
+.stick {
+	width: 100%;
+	text-align: right;
+	position: sticky;
+	top: 0px;
+}
+.stick span {
+	border-style: solid;
+	border-width: 3px;
+	border-color: black;
 }
   </style>
   </head>
   <body>
-    <div hx-get="?at=${first}" hx-trigger="revealed" hx-swap="afterend">INBOX</div>
+    <div class="stick">
+      <span>
+        <a href="#top">top</a>
+        <a href="#center">center</a>
+        <a href="#bottom">bottom</a>
+      </span>
+    </div>
+    <div hx-get="?before=${first}&amp;reload=10" hx-trigger="every 10s" hx-swap="outerHTML">
+<a href="?before=${first}">no javascript? load the newer posts than when the page loaded.</a>
+    </div>
+    <div id="center" hx-get="?at=${first}" hx-trigger="revealed" hx-swap="afterend">
+INBOX (page loaded here. newer posts above. older posts below)
+<a href="?at=${first}">no javascript? load the next ${per_page} posts</a>
+    </div>
+    <a href="#top">top</a> <a id="bottom" href="#bottom">bottom</a>
   </body>
 </html>
 EOF
diff --git a/ap-object2html b/ap-object2html
new file mode 100755
index 0000000000000000000000000000000000000000..2bfba93c74718767abb767cc12723f2a36ec8cc4
--- /dev/null
+++ b/ap-object2html
@@ -0,0 +1,58 @@
+#!/usr/bin/env bash
+
+object_file="$1"
+
+recursion_count="${2:-0}"
+
+if [ "$recursion_count" -gt 10 ];then
+  printf "WHOOOOOOA THERE BUDDY. WE'RE RECURSING TOO DEEP.\n"
+  exit 1
+fi
+
+if [ ! "$object_file" ];then
+  printf "usage: ap-object2html <object file>\n" >&2
+  exit 1
+fi
+
+printf '<div class="objdiv">\n'
+
+id="$(jq -r '.id' < "${object_file}")"
+qs_id="$(uriescape "$id" | sed 's|\&|%26|g;s|;|%3b|g;s|#|%23|g')"
+entitied_qs_id="$(html_entities_encode <<< "${qs_id}")"
+entitied_id="$(html_entities_encode <<< "${id}")"
+printf 'Id: <a href="%s">%s</a>\n' "$id" "$entitied_id"
+printf '<a href="actpub:like?uri=%s">[like]</a>' "$entitied_qs_id"
+printf '<br/>\n'
+
+attributed_to="$(jq -r .attributedTo < "${object_file}")"
+printf 'Attributed To: <a href="%s">%s</a><br/>\n' "$attributed_to" "${attributed_to}"
+
+inReplyTo="$(jq -r '.inReplyTo//""' 2>/dev/null < "${object_file}" | html_entities_encode)"
+if [ "$inReplyTo" ];then
+  printf "inReplyTo: <a href="%s">%s</a><br/>\n" "${inReplyTo}" "${inReplyTo}"
+  ## let's cache this fucker too and show it first :D
+  cache_file="$(ap-cacheobject "${inReplyTo}")"
+  ap-object2html "${cache_file}" $[$recursion_count + 1]
+fi
+#printf "Content: %s<br/>\n" "$(jq -r '.object.content' 2>/dev/null < "${object_file}" | html_entities_encode)"
+printf "Content: %s<br/>\n" "$(jq -r '.content' 2>/dev/null < "${object_file}")"
+if [ "$(jq -r '.attachment | type' < "${object_file}")" == "array" ];then
+  jq -r '.attachment | .[] | .mediaType,.url' 2>/dev/null < "${object_file}" | paste - - | html_entities_encode | while read type url;do
+    case "$type" in
+      image/*)
+        printf 'attachment: <a href="%s"><img src="%s" /></a><br/>' "$uri" "$url"
+        ;;
+      audio/*)
+        printf 'attachment: <audio controls><source src="%s" type="%s"></video><br/>' "$url" "$type"
+        ;;
+      video/*)
+        printf 'attachment: <video loop autoplay><source src="%s" type="%s"></video><br/>' "$url" "$type"
+        ;;
+      *)
+        printf '<a href="%s">attachment</a><br/>' "$url"
+        ;;
+    esac
+  done
+fi
+
+printf '</div>\n'

-----END OF PAGE-----

-- Response ended

-- Page fetched on Sun Jun 2 14:56:16 2024