From 6d47ef01291da35ec0e79f6c1b790250e1e2a56b Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Wed, 19 Jul 2023 18:09:12 -0400
Subject: [PATCH 01/26] check again for the announce/create/page activity

---
 activity.go | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/activity.go b/activity.go
index 0b49557..37ef878 100644
--- a/activity.go
+++ b/activity.go
@@ -638,6 +638,10 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
 						ilog.Printf("lost object inside create %s", id)
 						return nil
 					}
+					what, _ = obj.GetString("type")
+				}
+				if what == "Page" {
+					waspage = true
 				}
 				xid, _ = obj.GetString("id")
 			} else {

From 77280808552bb9ecb10356823ffa115c0fdbfcee Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Thu, 20 Jul 2023 17:32:30 -0400
Subject: [PATCH 02/26] try simpler emu css from yonle

---
 views/style.css | 2 --
 1 file changed, 2 deletions(-)

diff --git a/views/style.css b/views/style.css
index 3c6f45d..4b91a28 100644
--- a/views/style.css
+++ b/views/style.css
@@ -330,10 +330,8 @@ img, video {
 	display: block;
 }
 img.emu {
-	width: 2em;
 	height: 2em;
 	vertical-align: middle;
-	margin: -2px;
 	object-fit: contain;
 }
 .nophone {

From 3c0bce83f6bb1967ecdcc2261fb96d1f7becccb2 Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Thu, 20 Jul 2023 17:38:24 -0400
Subject: [PATCH 03/26] try lazy loading imgs.

---
 views/honk.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/views/honk.html b/views/honk.html
index f015a3a..e44c31b 100644
--- a/views/honk.html
+++ b/views/honk.html
@@ -72,7 +72,7 @@ in reply to: <a href="{{ .RID }}" rel=noreferrer>{{ .RID }}</a>
 {{ if $omitimages }}
 <p><a href="/d/{{ .XID }}">Image: {{ .Name }}</a>{{ if not (eq .Desc .Name) }} {{ .Desc }}{{ end }}
 {{ else }}
-<p><img src="/d/{{ .XID }}" title="{{ .Desc }}" alt="{{ .Desc }}">
+<p><img src="/d/{{ .XID }}" loading=lazy title="{{ .Desc }}" alt="{{ .Desc }}">
 {{ end }}
 {{ end }}
 {{ else }}

From b688f24e6d0b5f8fdebf8b30a293f6e4658e8a93 Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Sat, 22 Jul 2023 16:37:07 -0400
Subject: [PATCH 04/26] webs 63 with the cookie fix

---
 go.mod | 2 +-
 go.sum | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/go.mod b/go.mod
index 44c9571..45abb76 100644
--- a/go.mod
+++ b/go.mod
@@ -9,5 +9,5 @@ require (
 	golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
 	golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
 	humungus.tedunangst.com/r/go-sqlite3 v1.1.3
-	humungus.tedunangst.com/r/webs v0.6.62
+	humungus.tedunangst.com/r/webs v0.6.63
 )
diff --git a/go.sum b/go.sum
index 69ad019..953c460 100644
--- a/go.sum
+++ b/go.sum
@@ -25,5 +25,5 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 humungus.tedunangst.com/r/go-sqlite3 v1.1.3 h1:G2N4wzDS0NbuvrZtQJhh4F+3X+s7BF8b9ga8k38geUI=
 humungus.tedunangst.com/r/go-sqlite3 v1.1.3/go.mod h1:FtEEmQM7U2Ey1TuEEOyY1BmphTZnmiEjPsNLEAkpf/M=
-humungus.tedunangst.com/r/webs v0.6.62 h1:T/T0a2xWw1cYKTMqKXwP4GStRPUfOWYytN9zCMMlqpA=
-humungus.tedunangst.com/r/webs v0.6.62/go.mod h1:03R0N9BcT49HB4TDd1YmarpbiPvPzVDm74Mk4h1hYPc=
+humungus.tedunangst.com/r/webs v0.6.63 h1:Nrf8zqFnlxKKa0VChCq4nD3exItrJvVTZC1jYNvTx2M=
+humungus.tedunangst.com/r/webs v0.6.63/go.mod h1:03R0N9BcT49HB4TDd1YmarpbiPvPzVDm74Mk4h1hYPc=

From c19490a80234b9b0310184e791152b8e4fb1088c Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Mon, 24 Jul 2023 14:28:24 -0400
Subject: [PATCH 05/26] donks for days. multiple images with api, adapted from
 benjojo

---
 web.go | 33 ++++++++++++++++++---------------
 1 file changed, 18 insertions(+), 15 deletions(-)

diff --git a/web.go b/web.go
index 234cc53..a61d3ae 100644
--- a/web.go
+++ b/web.go
@@ -1801,20 +1801,22 @@ func submithonk(w http.ResponseWriter, r *http.Request) *Honk {
 			donkxid = fmt.Sprintf("%s:%d", d.XID, d.FileID)
 		}
 	} else {
-		p := strings.Split(donkxid, ":")
-		xid := p[0]
-		url := fmt.Sprintf("https://%s/d/%s", serverName, xid)
-		var donk *Donk
-		if len(p) > 1 {
-			fileid, _ := strconv.ParseInt(p[1], 10, 0)
-			donk = finddonkid(fileid, url)
-		} else {
-			donk = finddonk(url)
-		}
-		if donk != nil {
-			honk.Donks = append(honk.Donks, donk)
-		} else {
-			ilog.Printf("can't find file: %s", xid)
+		for _, xid := range r.Form["donkxid"] {
+			p := strings.Split(xid, ":")
+			xid = p[0]
+			url := fmt.Sprintf("https://%s/d/%s", serverName, xid)
+			var donk *Donk
+			if len(p) > 1 {
+				fileid, _ := strconv.ParseInt(p[1], 10, 0)
+				donk = finddonkid(fileid, url)
+			} else {
+				donk = finddonk(url)
+			}
+			if donk != nil {
+				honk.Donks = append(honk.Donks, donk)
+			} else {
+				ilog.Printf("can't find file: %s", xid)
+			}
 		}
 	}
 	memetize(honk)
@@ -2490,7 +2492,8 @@ func apihandler(w http.ResponseWriter, r *http.Request) {
 			http.Error(w, "missing donk", http.StatusBadRequest)
 			return
 		}
-		w.Write([]byte(d.XID))
+		donkxid := fmt.Sprintf("%s:%d", d.XID, d.FileID)
+		w.Write([]byte(donkxid))
 	case "zonkit":
 		zonkit(w, r)
 	case "gethonks":

From fde918783215e6f26adfc24b8e7b684de987388b Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Thu, 27 Jul 2023 20:51:19 -0400
Subject: [PATCH 06/26] let's just say http 400 is okie dokie

---
 deliverator.go | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/deliverator.go b/deliverator.go
index 814c2f0..6f55964 100644
--- a/deliverator.go
+++ b/deliverator.go
@@ -69,6 +69,14 @@ func lethaldose(err error) int64 {
 	return 0
 }
 
+func letitslide(err error) bool {
+	str := err.Error()
+	if strings.Contains(str, "http post status: 400") {
+		return true
+	}
+	return false
+}
+
 var dqmtx sync.Mutex
 
 func delinquent(userid int64, rcpt string, msg []byte) bool {
@@ -144,6 +152,10 @@ func deliveration(doover Doover) {
 			if t := lethaldose(err); t > doover.Tries {
 				doover.Tries = t
 			}
+			if letitslide(err) {
+				dlog.Printf("whatever myever %s", inbox)
+				continue
+			}
 			doover.Msgs = doover.Msgs[i:]
 			sayitagain(doover)
 			return

From 2c925935534753a30de2ae30c6b68fdc3e7721fd Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Sat, 29 Jul 2023 14:36:30 -0400
Subject: [PATCH 07/26] be mindful style for summary applies only to our
 details field

---
 go.mod          | 2 ++
 views/honk.html | 2 +-
 views/style.css | 2 +-
 3 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/go.mod b/go.mod
index 45abb76..22fd146 100644
--- a/go.mod
+++ b/go.mod
@@ -11,3 +11,5 @@ require (
 	humungus.tedunangst.com/r/go-sqlite3 v1.1.3
 	humungus.tedunangst.com/r/webs v0.6.63
 )
+
+replace humungus.tedunangst.com/r/webs => ../webs
diff --git a/views/honk.html b/views/honk.html
index e44c31b..bdad39f 100644
--- a/views/honk.html
+++ b/views/honk.html
@@ -52,7 +52,7 @@ in reply to: <a href="{{ .RID }}" rel=noreferrer>{{ .RID }}</a>
 </header>
 <p>
 <details class="noise" {{ .Open }} >
-<summary>{{ .HTPrecis }}<p></summary>
+<summary class="noise">{{ .HTPrecis }}<p></summary>
 <p>{{ .HTPrecis }}
 <p class="content">{{ .HTML }}
 {{ with .Time }}
diff --git a/views/style.css b/views/style.css
index 4b91a28..326edb5 100644
--- a/views/style.css
+++ b/views/style.css
@@ -309,7 +309,7 @@ input[type=file] {
 .limited	details.actions summary {
 		color: var(--fg-limited);
 	}
-details.noise[open] summary {
+details.noise[open] summary.noise {
 	display: none;
 }
 h1, h2 {

From d5a2c8694c04fe4a7d971735886489eaa690d212 Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Sat, 29 Jul 2023 14:45:01 -0400
Subject: [PATCH 08/26] ::: spoiler markdown support

---
 docs/changelog.txt | 2 ++
 docs/honk.5        | 7 +++++++
 go.mod             | 4 +---
 go.sum             | 4 ++--
 4 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/docs/changelog.txt b/docs/changelog.txt
index 9ba4351..f120f7a 100644
--- a/docs/changelog.txt
+++ b/docs/changelog.txt
@@ -2,6 +2,8 @@ changelog
 
 === next
 
++ ::: spoiler markdown
+
 + New threaded display order.
 
 + Improved search.
diff --git a/docs/honk.5 b/docs/honk.5
index 0c4a404..c643889 100644
--- a/docs/honk.5
+++ b/docs/honk.5
@@ -70,6 +70,13 @@ will be autolinked.
 Exactly three dashes on a line,
 .Dq --- ,
 will become a horizontal rule.
+.It spoilers
+Hide blocks of text between triple colon delimted regions.
+.Bd -literal
+::: warning
+text that should be hidden behind a warning
+:::
+.Ed
 .El
 .Pp
 If the first line of a honk begins with
diff --git a/go.mod b/go.mod
index 22fd146..8afc19c 100644
--- a/go.mod
+++ b/go.mod
@@ -9,7 +9,5 @@ require (
 	golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
 	golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
 	humungus.tedunangst.com/r/go-sqlite3 v1.1.3
-	humungus.tedunangst.com/r/webs v0.6.63
+	humungus.tedunangst.com/r/webs v0.6.66
 )
-
-replace humungus.tedunangst.com/r/webs => ../webs
diff --git a/go.sum b/go.sum
index 953c460..93eefd6 100644
--- a/go.sum
+++ b/go.sum
@@ -25,5 +25,5 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 humungus.tedunangst.com/r/go-sqlite3 v1.1.3 h1:G2N4wzDS0NbuvrZtQJhh4F+3X+s7BF8b9ga8k38geUI=
 humungus.tedunangst.com/r/go-sqlite3 v1.1.3/go.mod h1:FtEEmQM7U2Ey1TuEEOyY1BmphTZnmiEjPsNLEAkpf/M=
-humungus.tedunangst.com/r/webs v0.6.63 h1:Nrf8zqFnlxKKa0VChCq4nD3exItrJvVTZC1jYNvTx2M=
-humungus.tedunangst.com/r/webs v0.6.63/go.mod h1:03R0N9BcT49HB4TDd1YmarpbiPvPzVDm74Mk4h1hYPc=
+humungus.tedunangst.com/r/webs v0.6.66 h1:+3cB6m1tBtO/Sc9+ggAZP7MNo76Dm66+mMWc1Kub34k=
+humungus.tedunangst.com/r/webs v0.6.66/go.mod h1:03R0N9BcT49HB4TDd1YmarpbiPvPzVDm74Mk4h1hYPc=

From 92b1c61dc1de616562ce4356b3c75fe6a06b22b8 Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Sat, 29 Jul 2023 15:04:11 -0400
Subject: [PATCH 09/26] handle the case where the tag is just an object

---
 activity.go | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/activity.go b/activity.go
index 37ef878..237602b 100644
--- a/activity.go
+++ b/activity.go
@@ -967,12 +967,7 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
 					procatt(att)
 				}
 			}
-			tags, _ := obj.GetArray("tag")
-			for _, tagi := range tags {
-				tag, ok := tagi.(junk.Junk)
-				if !ok {
-					continue
-				}
+			proctag := func(tag junk.Junk) {
 				tt, _ := tag.GetString("type")
 				name, _ := tag.GetString("name")
 				desc, _ := tag.GetString("summary")
@@ -1017,6 +1012,18 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
 					mentions = append(mentions, m)
 				}
 			}
+			tags, _ := obj.GetArray("tag")
+			for _, tagi := range tags {
+				tag, ok := tagi.(junk.Junk)
+				if !ok {
+					continue
+				}
+				proctag(tag)
+			}
+			tag, ok := obj.GetMap("tag")
+			if ok {
+				proctag(tag)
+			}
 			if starttime, ok := obj.GetString("startTime"); ok {
 				if start, err := time.Parse(time.RFC3339, starttime); err == nil {
 					t := new(Time)

From a7adc79f202d4b9c91ba9cc00d0c4ab233c8c4a1 Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Sat, 29 Jul 2023 16:13:19 -0400
Subject: [PATCH 10/26] handle announce of update like announce of create

---
 activity.go | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/activity.go b/activity.go
index 237602b..7ccacf1 100644
--- a/activity.go
+++ b/activity.go
@@ -631,13 +631,17 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
 		case "Announce":
 			obj, ok = item.GetMap("object")
 			if ok {
+				// at some point we should just recurse
 				what, ok := obj.GetString("type")
-				if ok && what == "Create" {
+				if ok && (what == "Create" || what == "Update") {
 					obj, ok = obj.GetMap("object")
 					if !ok {
-						ilog.Printf("lost object inside create %s", id)
+						ilog.Printf("lost object inside announce %s", id)
 						return nil
 					}
+					if what == "Update" {
+						isUpdate = true
+					}
 					what, _ = obj.GetString("type")
 				}
 				if what == "Page" {

From bd80e1b4a8306939d7384e8319164fac7e22ef89 Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Sat, 29 Jul 2023 21:56:45 -0400
Subject: [PATCH 11/26] search precis too

---
 database.go | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/database.go b/database.go
index 28e42f2..c806d3f 100644
--- a/database.go
+++ b/database.go
@@ -342,7 +342,8 @@ func gethonksbysearch(userid int64, q string, wanted int64) []*Honk {
 			continue
 		}
 		t = "%" + t + "%"
-		queries = append(queries, "noise"+negate+"like ?")
+		queries = append(queries, negate+"(noise like ? or precis like ?)")
+		params = append(params, t)
 		params = append(params, t)
 	}
 

From bed7d662bba221788cd625d036303ea4b35c2e65 Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Sun, 30 Jul 2023 13:08:53 -0400
Subject: [PATCH 12/26] clean up the one or many arrays a little

---
 activity.go | 43 ++++++++++++++++++-------------------------
 1 file changed, 18 insertions(+), 25 deletions(-)

diff --git a/activity.go b/activity.go
index 7ccacf1..6f5599b 100644
--- a/activity.go
+++ b/activity.go
@@ -468,19 +468,12 @@ func newphone(a []string, obj junk.Junk) []string {
 }
 
 func extractattrto(obj junk.Junk) string {
-	who, _ := obj.GetString("attributedTo")
-	if who != "" {
-		return who
-	}
-	o, ok := obj.GetMap("attributedTo")
-	if ok {
-		id, ok := o.GetString("id")
-		if ok {
-			return id
-		}
-	}
-	arr, _ := obj.GetArray("attributedTo")
+	arr := oneforall(obj, "attributedTo")
 	for _, a := range arr {
+		s, ok := a.(string)
+		if ok {
+			return s
+		}
 		o, ok := a.(junk.Junk)
 		if ok {
 			t, _ := o.GetString("type")
@@ -489,14 +482,21 @@ func extractattrto(obj junk.Junk) string {
 				return id
 			}
 		}
-		s, ok := a.(string)
-		if ok {
-			return s
-		}
 	}
 	return ""
 }
 
+func oneforall(obj junk.Junk, key string) []interface{} {
+	if val, ok := obj.GetMap(key); ok {
+		return []interface{}{val}
+	}
+	if str, ok := obj.GetString(key); ok {
+		return []interface{}{str}
+	}
+	arr, _ := obj.GetArray(key)
+	return arr
+}
+
 func firstofmany(obj junk.Junk, key string) string {
 	if val, _ := obj.GetString(key); val != "" {
 		return val
@@ -958,7 +958,7 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
 				}
 			}
 			if !preferorig {
-				atts, _ := obj.GetArray("attachment")
+				atts := oneforall(obj, "attachment")
 				for _, atti := range atts {
 					att, ok := atti.(junk.Junk)
 					if !ok {
@@ -967,9 +967,6 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
 					}
 					procatt(att)
 				}
-				if att, ok := obj.GetMap("attachment"); ok {
-					procatt(att)
-				}
 			}
 			proctag := func(tag junk.Junk) {
 				tt, _ := tag.GetString("type")
@@ -1016,7 +1013,7 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
 					mentions = append(mentions, m)
 				}
 			}
-			tags, _ := obj.GetArray("tag")
+			tags := oneforall(obj, "tag")
 			for _, tagi := range tags {
 				tag, ok := tagi.(junk.Junk)
 				if !ok {
@@ -1024,10 +1021,6 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
 				}
 				proctag(tag)
 			}
-			tag, ok := obj.GetMap("tag")
-			if ok {
-				proctag(tag)
-			}
 			if starttime, ok := obj.GetString("startTime"); ok {
 				if start, err := time.Parse(time.RFC3339, starttime); err == nil {
 					t := new(Time)

From a8aa306af49f91c24ec13e838efe4449a875c9ba Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Sun, 30 Jul 2023 16:49:34 -0400
Subject: [PATCH 13/26] simplify junk.get code

---
 activity.go | 45 ++++++---------------------------------------
 1 file changed, 6 insertions(+), 39 deletions(-)

diff --git a/activity.go b/activity.go
index 6f5599b..3c211a1 100644
--- a/activity.go
+++ b/activity.go
@@ -132,64 +132,31 @@ var flightdeck = gate.NewSerializer()
 
 var signGets = true
 
-func junkGet(userid int64, url string, args junk.GetArgs) (junk.Junk, error) {
+func GetJunkTimeout(userid int64, url string, timeout time.Duration) (junk.Junk, error) {
 	client := http.DefaultClient
-	if args.Client != nil {
-		client = args.Client
-	}
-	req, err := http.NewRequest("GET", url, nil)
-	if err != nil {
-		return nil, err
-	}
-	if args.Accept != "" {
-		req.Header.Set("Accept", args.Accept)
-	}
-	if args.Agent != "" {
-		req.Header.Set("User-Agent", args.Agent)
-	}
-	if signGets {
+	sign := func(req *http.Request) error {
 		var ki *KeyInfo
 		ok := ziggies.Get(userid, &ki)
 		if ok {
 			httpsig.SignRequest(ki.keyname, ki.seckey, req, nil)
 		}
+		return nil
 	}
-	if args.Timeout != 0 {
-		ctx, cancel := context.WithTimeout(context.Background(), args.Timeout)
-		defer cancel()
-		req = req.WithContext(ctx)
-	}
-	resp, err := client.Do(req)
-	if err != nil {
-		return nil, err
-	}
-	defer resp.Body.Close()
-
-	switch resp.StatusCode {
-	case 200:
-	case 201:
-	case 202:
-	default:
-		return nil, fmt.Errorf("http get status: %d", resp.StatusCode)
-	}
-	return junk.Read(resp.Body)
-}
-
-func GetJunkTimeout(userid int64, url string, timeout time.Duration) (junk.Junk, error) {
-	client := http.DefaultClient
 	if develMode {
 		client = develClient
+		sign = nil
 	}
 	fn := func() (interface{}, error) {
 		at := thefakename
 		if strings.Contains(url, ".well-known/webfinger?resource") {
 			at = "application/jrd+json"
 		}
-		j, err := junkGet(userid, url, junk.GetArgs{
+		j, err := junk.Get(url, junk.GetArgs{
 			Accept:  at,
 			Agent:   "honksnonk/5.0; " + serverName,
 			Timeout: timeout,
 			Client:  client,
+			Fixup:   sign,
 		})
 		return j, err
 	}

From d3bf2b7bc8ad4ce4d647ab8b16ea1355a124655a Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Sun, 30 Jul 2023 17:03:46 -0400
Subject: [PATCH 14/26] retarget mention links to local profile

---
 docs/changelog.txt |  2 ++
 fun.go             | 10 ++++++++++
 go.mod             |  2 +-
 go.sum             |  4 ++--
 4 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/docs/changelog.txt b/docs/changelog.txt
index f120f7a..70f01ab 100644
--- a/docs/changelog.txt
+++ b/docs/changelog.txt
@@ -2,6 +2,8 @@ changelog
 
 === next
 
++ Mention links locally.
+
 + ::: spoiler markdown
 
 + New threaded display order.
diff --git a/fun.go b/fun.go
index 3caa320..4524ebd 100644
--- a/fun.go
+++ b/fun.go
@@ -159,6 +159,16 @@ func reverbolate(userid int64, honks []*Honk) {
 				data = re_emus.ReplaceAllStringFunc(data, emuxifier)
 				io.WriteString(w, data)
 			}
+			if user != nil {
+				htf.RetargetLink = func(href string) string {
+					for _, m := range h.Mentions {
+						if href == m.Where {
+							return "/h?xid=" + url.QueryEscape(href)
+						}
+					}
+					return href
+				}
+			}
 			p, _ := htf.String(h.Precis)
 			n, _ := htf.String(h.Noise)
 			h.Precis = string(p)
diff --git a/go.mod b/go.mod
index 8afc19c..15379a8 100644
--- a/go.mod
+++ b/go.mod
@@ -9,5 +9,5 @@ require (
 	golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
 	golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
 	humungus.tedunangst.com/r/go-sqlite3 v1.1.3
-	humungus.tedunangst.com/r/webs v0.6.66
+	humungus.tedunangst.com/r/webs v0.6.67
 )
diff --git a/go.sum b/go.sum
index 93eefd6..0f8e915 100644
--- a/go.sum
+++ b/go.sum
@@ -25,5 +25,5 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 humungus.tedunangst.com/r/go-sqlite3 v1.1.3 h1:G2N4wzDS0NbuvrZtQJhh4F+3X+s7BF8b9ga8k38geUI=
 humungus.tedunangst.com/r/go-sqlite3 v1.1.3/go.mod h1:FtEEmQM7U2Ey1TuEEOyY1BmphTZnmiEjPsNLEAkpf/M=
-humungus.tedunangst.com/r/webs v0.6.66 h1:+3cB6m1tBtO/Sc9+ggAZP7MNo76Dm66+mMWc1Kub34k=
-humungus.tedunangst.com/r/webs v0.6.66/go.mod h1:03R0N9BcT49HB4TDd1YmarpbiPvPzVDm74Mk4h1hYPc=
+humungus.tedunangst.com/r/webs v0.6.67 h1:OO5UkQa+bHeiIrZ5IGR9JGtgGPsKsYlRJEVk1bSt+Qo=
+humungus.tedunangst.com/r/webs v0.6.67/go.mod h1:03R0N9BcT49HB4TDd1YmarpbiPvPzVDm74Mk4h1hYPc=

From c57a52a90c4e6bc0e56fa023b11533508ae9c6d1 Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Sun, 30 Jul 2023 20:31:00 -0400
Subject: [PATCH 15/26] allow tagging outside of content

---
 activity.go |  1 +
 fun.go      | 40 ++++++++++++++++++++++++++++++++++++++--
 honk.go     |  5 +++++
 web.go      |  1 +
 4 files changed, 45 insertions(+), 2 deletions(-)

diff --git a/activity.go b/activity.go
index 3c211a1..f0dd39b 100644
--- a/activity.go
+++ b/activity.go
@@ -1242,6 +1242,7 @@ func jonkjonk(user *WhatAbout, h *Honk) (junk.Junk, junk.Junk) {
 		if !h.Public {
 			jo["directMessage"] = true
 		}
+		h.Noise = re_retag.ReplaceAllString(h.Noise, "")
 		translate(h)
 		redoimages(h)
 		if h.Precis != "" {
diff --git a/fun.go b/fun.go
index 4524ebd..09327e3 100644
--- a/fun.go
+++ b/fun.go
@@ -82,6 +82,7 @@ func reverbolate(userid int64, honks []*Honk) {
 			local = true
 		}
 		if local && h.What != "bonked" {
+			h.Noise = re_retag.ReplaceAllString(h.Noise, "")
 			h.Noise = re_memes.ReplaceAllString(h.Noise, "")
 		}
 		h.Username, h.Handle = handles(h.Honker)
@@ -124,11 +125,27 @@ func reverbolate(userid int64, honks []*Honk) {
 		h.Precis = demoji(h.Precis)
 		h.Noise = demoji(h.Noise)
 		h.Open = "open"
+		var misto string
 		for _, m := range h.Mentions {
 			if m.Where != h.Honker && !m.IsPresent(h.Noise) {
-				h.Noise = "(" + m.Who + ")" + h.Noise
+				misto += "(" + m.Who + ")"
 			}
 		}
+		var mistag string
+		for _, o := range h.Onts {
+			if !OntIsPresent(o, h.Noise) {
+				mistag += "(" + o + ")"
+			}
+		}
+		if len(misto) > 0 || len(mistag) > 0 {
+			if len(misto) > 0 {
+				misto = misto + "<p>"
+			}
+			if len(mistag) > 0 {
+				mistag = "<p>" + mistag
+			}
+			h.Noise = misto + h.Noise + mistag
+		}
 
 		zap := make(map[string]bool)
 		{
@@ -329,7 +346,7 @@ func translate(honk *Honk) {
 	noise = strings.TrimSpace(noise)
 	noise = marker.Mark(noise)
 	honk.Noise = noise
-	honk.Onts = oneofakind(marker.HashTags)
+	honk.Onts = oneofakind(append(honk.Onts, marker.HashTags...))
 	honk.Mentions = bunchofgrapes(marker.Mentions)
 }
 
@@ -438,6 +455,7 @@ var re_memes = regexp.MustCompile("meme: ?([^\n]+)")
 var re_avatar = regexp.MustCompile("avatar: ?([^\n]+)")
 var re_banner = regexp.MustCompile("banner: ?([^\n]+)")
 var re_convoy = regexp.MustCompile("convoy: ?([^\n]+)")
+var re_retag = regexp.MustCompile("tags: ?([^\n]+)")
 var re_convalidate = regexp.MustCompile("^(https?|tag|data):")
 
 func memetize(honk *Honk) {
@@ -475,6 +493,24 @@ func memetize(honk *Honk) {
 	honk.Noise = re_memes.ReplaceAllStringFunc(honk.Noise, repl)
 }
 
+func recategorize(honk *Honk) {
+	repl := func(x string) string {
+		x = x[5:]
+		for _, t := range strings.Split(x, " ") {
+			if t == "" {
+				continue
+			}
+			if t[0] != '#' {
+				t = "#" + t
+			}
+			dlog.Printf("hashtag: %s", t)
+			honk.Onts = append(honk.Onts, t)
+		}
+		return ""
+	}
+	honk.Noise = re_retag.ReplaceAllStringFunc(honk.Noise, repl)
+}
+
 var re_quickmention = regexp.MustCompile("(^|[ \n])@[[:alnum:]_]+([ \n:;.,']|$)")
 
 func quickrename(s string, userid int64) string {
diff --git a/honk.go b/honk.go
index 1f6d09e..80b3584 100644
--- a/honk.go
+++ b/honk.go
@@ -144,6 +144,11 @@ func (mention *Mention) IsPresent(noise string) bool {
 	return strings.Contains(noise, ">@"+nick) || strings.Contains(noise, "@<span>"+nick)
 }
 
+func OntIsPresent(ont, noise string) bool {
+	ont = ont[1:]
+	return strings.Contains(noise, ">#"+ont) || strings.Contains(noise, "#<span>"+ont)
+}
+
 type OldRevision struct {
 	Precis string
 	Noise  string
diff --git a/web.go b/web.go
index a61d3ae..c7507cc 100644
--- a/web.go
+++ b/web.go
@@ -1743,6 +1743,7 @@ func submithonk(w http.ResponseWriter, r *http.Request) *Honk {
 	honk.Noise = noise
 	precipitate(honk)
 	noise = honk.Noise
+	recategorize(honk)
 	translate(honk)
 
 	if rid != "" {

From c44af763560742536de5027dc4664ea0d1ce1f3e Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Sun, 30 Jul 2023 20:39:31 -0400
Subject: [PATCH 16/26] better presentation for readded invisible content

---
 fun.go | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/fun.go b/fun.go
index 09327e3..23079ac 100644
--- a/fun.go
+++ b/fun.go
@@ -128,21 +128,21 @@ func reverbolate(userid int64, honks []*Honk) {
 		var misto string
 		for _, m := range h.Mentions {
 			if m.Where != h.Honker && !m.IsPresent(h.Noise) {
-				misto += "(" + m.Who + ")"
+				misto += " " + m.Who
 			}
 		}
 		var mistag string
 		for _, o := range h.Onts {
 			if !OntIsPresent(o, h.Noise) {
-				mistag += "(" + o + ")"
+				mistag += " " + o
 			}
 		}
 		if len(misto) > 0 || len(mistag) > 0 {
 			if len(misto) > 0 {
-				misto = misto + "<p>"
+				misto = "(" + misto[1:] + ")<p>"
 			}
 			if len(mistag) > 0 {
-				mistag = "<p>" + mistag
+				mistag = "<p>(" + mistag[1:] + ")"
 			}
 			h.Noise = misto + h.Noise + mistag
 		}

From 8387dd440052ef434c7147dd6c03c9b683857d84 Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Sun, 30 Jul 2023 21:01:45 -0400
Subject: [PATCH 17/26] mixed case hashtag complicates things a bit

---
 honk.go | 25 +++++++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/honk.go b/honk.go
index 80b3584..daf6526 100644
--- a/honk.go
+++ b/honk.go
@@ -145,8 +145,29 @@ func (mention *Mention) IsPresent(noise string) bool {
 }
 
 func OntIsPresent(ont, noise string) bool {
-	ont = ont[1:]
-	return strings.Contains(noise, ">#"+ont) || strings.Contains(noise, "#<span>"+ont)
+	ont = strings.ToLower(ont[1:] + "<")
+	idx := strings.IndexByte(noise, '#')
+	for idx >= 0 {
+		if strings.HasPrefix(noise[idx:], "#<span>") {
+			idx += 5
+		} else {
+			idx += 1
+		}
+		if idx + len(ont) + 1 > len(noise) {
+			return false
+		}
+		test := noise[idx:idx+len(ont)]
+		test = strings.ToLower(test)
+		if test == ont {
+			return true
+		}
+		newidx := strings.IndexByte(noise[idx:], '#')
+		if newidx == -1 {
+			return false
+		}
+		idx += newidx
+	}
+	return false
 }
 
 type OldRevision struct {

From 22d448fa5f9e28cbdc1ee7b669e7198d06e1be1d Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Sun, 30 Jul 2023 21:31:53 -0400
Subject: [PATCH 18/26] strings are hard

---
 honk.go | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/honk.go b/honk.go
index daf6526..8a9b86f 100644
--- a/honk.go
+++ b/honk.go
@@ -145,15 +145,14 @@ func (mention *Mention) IsPresent(noise string) bool {
 }
 
 func OntIsPresent(ont, noise string) bool {
-	ont = strings.ToLower(ont[1:] + "<")
+	ont = strings.ToLower(ont[1:])
 	idx := strings.IndexByte(noise, '#')
 	for idx >= 0 {
 		if strings.HasPrefix(noise[idx:], "#<span>") {
-			idx += 5
-		} else {
-			idx += 1
+			idx += 6
 		}
-		if idx + len(ont) + 1 > len(noise) {
+		idx += 1
+		if idx + len(ont) >= len(noise) {
 			return false
 		}
 		test := noise[idx:idx+len(ont)]

From a08e0d0cb8856cb9104f09a50f9cb2c8ceacdd01 Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Mon, 31 Jul 2023 19:46:55 -0400
Subject: [PATCH 19/26] create a plain column and use that for search

---
 database.go  | 35 +++++++++++++++++++++++++++++------
 honk.go      |  4 ++--
 schema.sql   |  3 +--
 upgradedb.go | 17 ++++++++++++++++-
 4 files changed, 48 insertions(+), 11 deletions(-)

diff --git a/database.go b/database.go
index c806d3f..cf58d80 100644
--- a/database.go
+++ b/database.go
@@ -30,6 +30,7 @@ import (
 	"time"
 
 	"humungus.tedunangst.com/r/webs/cache"
+	"humungus.tedunangst.com/r/webs/htfilter"
 	"humungus.tedunangst.com/r/webs/httpsig"
 	"humungus.tedunangst.com/r/webs/login"
 	"humungus.tedunangst.com/r/webs/mz"
@@ -342,8 +343,7 @@ func gethonksbysearch(userid int64, q string, wanted int64) []*Honk {
 			continue
 		}
 		t = "%" + t + "%"
-		queries = append(queries, negate+"(noise like ? or precis like ?)")
-		params = append(params, t)
+		queries = append(queries, negate+"(plain like ?)")
 		params = append(params, t)
 	}
 
@@ -789,6 +789,27 @@ func loadchatter(userid int64) []*Chatter {
 	return chatter
 }
 
+func (honk *Honk) Plain() string {
+	var plain []string
+	var filt htfilter.Filter
+	filt.WithLinks = true
+	if honk.Precis != "" {
+		t, _ := filt.TextOnly(honk.Precis)
+		plain = append(plain, t)
+	}
+	if honk.Format == "html" {
+		t, _ := filt.TextOnly(honk.Noise)
+		plain = append(plain, t)
+	} else {
+		plain = append(plain, honk.Noise)
+	}
+	for _, d := range honk.Donks {
+		plain = append(plain, d.Name)
+		plain = append(plain, d.Desc)
+	}
+	return strings.Join(plain, " ")
+}
+
 func savehonk(h *Honk) error {
 	dt := h.Date.UTC().Format(dbtimeformat)
 	aud := strings.Join(h.Audience, " ")
@@ -799,10 +820,11 @@ func savehonk(h *Honk) error {
 		elog.Printf("can't begin tx: %s", err)
 		return err
 	}
+	plain := h.Plain()
 
 	res, err := tx.Stmt(stmtSaveHonk).Exec(h.UserID, h.What, h.Honker, h.XID, h.RID, dt, h.URL,
 		aud, h.Noise, h.Convoy, h.Whofore, h.Format, h.Precis,
-		h.Oonker, h.Flags)
+		h.Oonker, h.Flags, plain)
 	if err == nil {
 		h.ID, _ = res.LastInsertId()
 		err = saveextras(tx, h)
@@ -833,10 +855,11 @@ func updatehonk(h *Honk) error {
 		elog.Printf("can't begin tx: %s", err)
 		return err
 	}
+	plain := h.Plain()
 
 	err = deleteextras(tx, h.ID, false)
 	if err == nil {
-		_, err = tx.Stmt(stmtUpdateHonk).Exec(h.Precis, h.Noise, h.Format, h.Whofore, dt, h.ID)
+		_, err = tx.Stmt(stmtUpdateHonk).Exec(h.Precis, h.Noise, h.Format, h.Whofore, dt, plain, h.ID)
 	}
 	if err == nil {
 		err = saveextras(tx, h)
@@ -1171,9 +1194,9 @@ func prepareStatements(db *sql.DB) {
 	stmtDeleteAllMeta = preparetodie(db, "delete from honkmeta where honkid = ?")
 	stmtDeleteSomeMeta = preparetodie(db, "delete from honkmeta where honkid = ? and genus not in ('oldrev')")
 	stmtDeleteOneMeta = preparetodie(db, "delete from honkmeta where honkid = ? and genus = ?")
-	stmtSaveHonk = preparetodie(db, "insert into honks (userid, what, honker, xid, rid, dt, url, audience, noise, convoy, whofore, format, precis, oonker, flags) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
+	stmtSaveHonk = preparetodie(db, "insert into honks (userid, what, honker, xid, rid, dt, url, audience, noise, convoy, whofore, format, precis, oonker, flags, plain) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
 	stmtDeleteHonk = preparetodie(db, "delete from honks where honkid = ?")
-	stmtUpdateHonk = preparetodie(db, "update honks set precis = ?, noise = ?, format = ?, whofore = ?, dt = ? where honkid = ?")
+	stmtUpdateHonk = preparetodie(db, "update honks set precis = ?, noise = ?, format = ?, whofore = ?, dt = ?, plain = ? where honkid = ?")
 	stmtSaveOnt = preparetodie(db, "insert into onts (ontology, honkid) values (?, ?)")
 	stmtDeleteOnts = preparetodie(db, "delete from onts where honkid = ?")
 	stmtSaveDonk = preparetodie(db, "insert into donks (honkid, chonkid, fileid) values (?, ?, ?)")
diff --git a/honk.go b/honk.go
index 8a9b86f..403c3de 100644
--- a/honk.go
+++ b/honk.go
@@ -152,10 +152,10 @@ func OntIsPresent(ont, noise string) bool {
 			idx += 6
 		}
 		idx += 1
-		if idx + len(ont) >= len(noise) {
+		if idx+len(ont) >= len(noise) {
 			return false
 		}
-		test := noise[idx:idx+len(ont)]
+		test := noise[idx : idx+len(ont)]
 		test = strings.ToLower(test)
 		if test == ont {
 			return true
diff --git a/schema.sql b/schema.sql
index fe97049..1ce0071 100644
--- a/schema.sql
+++ b/schema.sql
@@ -1,5 +1,4 @@
-
-create table honks (honkid integer primary key, userid integer, what text, honker text, xid text, rid text, dt text, url text, audience text, noise text, convoy text, whofore integer, format text, precis text, oonker text, flags integer);
+create table honks (honkid integer primary key, userid integer, what text, honker text, xid text, rid text, dt text, url text, audience text, noise text, convoy text, whofore integer, format text, precis text, oonker text, flags integer, plain text);
 create table chonks (chonkid integer primary key, userid integer, xid text, who txt, target text, dt text, noise text, format text);
 create table donks (honkid integer, chonkid integer, fileid integer);
 create table filemeta (fileid integer primary key, xid text, name text, description text, url text, media text, local integer);
diff --git a/upgradedb.go b/upgradedb.go
index b4bf806..de4e135 100644
--- a/upgradedb.go
+++ b/upgradedb.go
@@ -20,7 +20,7 @@ import (
 	"os"
 )
 
-var myVersion = 43
+var myVersion = 44
 
 type dbexecer interface {
 	Exec(query string, args ...interface{}) (sql.Result, error)
@@ -42,6 +42,16 @@ func upgradedb() {
 	if dbversion < 40 {
 		elog.Fatal("database is too old to upgrade")
 	}
+	try := func(s string, args ...interface{}) {
+		_, err := db.Exec(s, args...)
+		if err != nil {
+			elog.Fatalf("can't run %s: %s", s, err)
+		}
+	}
+	setV := func(ver int64) {
+		try("update config set value = ? where key = 'dbversion'", ver)
+	}
+
 	switch dbversion {
 	case 40:
 		doordie(db, "PRAGMA journal_mode=WAL")
@@ -87,6 +97,11 @@ func upgradedb() {
 		doordie(db, "update config set value = 43 where key = 'dbversion'")
 		fallthrough
 	case 43:
+		try("alter table honks add column plain text")
+		try("update honks set plain = ''")
+		setV(44)
+		fallthrough
+	case 44:
 
 	default:
 		elog.Fatalf("can't upgrade unknown version %d", dbversion)

From 14c9d31bd78b3eb3e29c2857dc3876b23fb551ff Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Mon, 31 Jul 2023 20:36:15 -0400
Subject: [PATCH 20/26] upgrade to backfill the plain text column

---
 upgradedb.go | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 72 insertions(+), 2 deletions(-)

diff --git a/upgradedb.go b/upgradedb.go
index de4e135..de6cb61 100644
--- a/upgradedb.go
+++ b/upgradedb.go
@@ -18,9 +18,12 @@ package main
 import (
 	"database/sql"
 	"os"
+	"strings"
+
+	"humungus.tedunangst.com/r/webs/htfilter"
 )
 
-var myVersion = 44
+var myVersion = 45
 
 type dbexecer interface {
 	Exec(query string, args ...interface{}) (sql.Result, error)
@@ -42,8 +45,14 @@ func upgradedb() {
 	if dbversion < 40 {
 		elog.Fatal("database is too old to upgrade")
 	}
+	var err error
+	var tx *sql.Tx
 	try := func(s string, args ...interface{}) {
-		_, err := db.Exec(s, args...)
+		if tx != nil {
+			_, err = tx.Exec(s, args...)
+		} else {
+			_, err = db.Exec(s, args...)
+		}
 		if err != nil {
 			elog.Fatalf("can't run %s: %s", s, err)
 		}
@@ -102,6 +111,67 @@ func upgradedb() {
 		setV(44)
 		fallthrough
 	case 44:
+		makeplain := func(noise, precis, format string) []string {
+			var plain []string
+			var filt htfilter.Filter
+			filt.WithLinks = true
+			if precis != "" {
+				t, _ := filt.TextOnly(precis)
+				plain = append(plain, t)
+			}
+			if format == "html" {
+				t, _ := filt.TextOnly(noise)
+				plain = append(plain, t)
+			} else {
+				plain = append(plain, noise)
+			}
+			return plain
+		}
+		tx, err = db.Begin()
+		if err != nil {
+			elog.Fatal(err)
+		}
+		plainmap := make(map[int64][]string)
+		rows, err := tx.Query("select honkid, noise, precis, format from honks")
+		if err != nil {
+			elog.Fatal(err)
+		}
+		for rows.Next() {
+			var honkid int64
+			var noise, precis, format string
+			err = rows.Scan(&honkid, &noise, &precis, &format)
+			if err != nil {
+				elog.Fatal(err)
+			}
+			plainmap[honkid] = makeplain(noise, precis, format)
+		}
+		rows.Close()
+		rows, err = tx.Query("select honkid, name, description from donks join filemeta on donks.fileid = filemeta.fileid")
+		if err != nil {
+			elog.Fatal(err)
+		}
+		for rows.Next() {
+			var honkid int64
+			var name, desc string
+			err = rows.Scan(&honkid, &name, &desc)
+			if err != nil {
+				elog.Fatal(err)
+			}
+			plainmap[honkid] = append(plainmap[honkid], name)
+			plainmap[honkid] = append(plainmap[honkid], desc)
+		}
+		rows.Close()
+		for honkid, plain := range plainmap {
+			try("update honks set plain = ? where honkid = ?", strings.Join(plain, " "), honkid)
+		}
+		setV(45)
+		err = tx.Commit()
+		if err != nil {
+			elog.Fatal(err)
+		}
+		tx = nil
+		fallthrough
+	case 45:
 
 	default:
 		elog.Fatalf("can't upgrade unknown version %d", dbversion)

From c24f06b9f3659f4f8afb8d07fb07c4a856a483c3 Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Mon, 31 Jul 2023 20:37:03 -0400
Subject: [PATCH 21/26] going forward, include tag in plain

---
 database.go | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/database.go b/database.go
index cf58d80..e398a22 100644
--- a/database.go
+++ b/database.go
@@ -807,6 +807,9 @@ func (honk *Honk) Plain() string {
 		plain = append(plain, d.Name)
 		plain = append(plain, d.Desc)
 	}
+	for _, o := range honk.Onts {
+		plain = append(plain, o)
+	}
 	return strings.Join(plain, " ")
 }
 

From 6cc464910dd59b3753d2327031c88f4ffde5107d Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Mon, 31 Jul 2023 20:45:56 -0400
Subject: [PATCH 22/26] change notes

---
 docs/changelog.txt | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/docs/changelog.txt b/docs/changelog.txt
index 70f01ab..5e9a09e 100644
--- a/docs/changelog.txt
+++ b/docs/changelog.txt
@@ -2,6 +2,10 @@ changelog
 
 === next
 
++ More reliable search.
+
++ Secret tags.
+
 + Mention links locally.
 
 + ::: spoiler markdown

From 592c189e8b9a739803faf5faad25e8df91b5556a Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Mon, 31 Jul 2023 20:46:43 -0400
Subject: [PATCH 23/26] need to demasto the mention

---
 fun.go | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/fun.go b/fun.go
index 23079ac..d013ac9 100644
--- a/fun.go
+++ b/fun.go
@@ -178,9 +178,10 @@ func reverbolate(userid int64, honks []*Honk) {
 			}
 			if user != nil {
 				htf.RetargetLink = func(href string) string {
+					h2 := strings.ReplaceAll(href, "/@", "/users/")
 					for _, m := range h.Mentions {
-						if href == m.Where {
-							return "/h?xid=" + url.QueryEscape(href)
+						if h2 == m.Where || href == m.Where {
+							return "/h?xid=" + url.QueryEscape(m.Where)
 						}
 					}
 					return href

From 2e82a931b98fdbef0637f3b51e6233e1ab861d21 Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Mon, 31 Jul 2023 22:54:53 -0400
Subject: [PATCH 24/26] some people are determined to fill my database with
 tombstones, so only save zonk if we deleted something

---
 activity.go | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/activity.go b/activity.go
index f0dd39b..855f087 100644
--- a/activity.go
+++ b/activity.go
@@ -315,10 +315,10 @@ func eradicatexonk(userid int64, xid string) {
 	xonk := getxonk(userid, xid)
 	if xonk != nil {
 		deletehonk(xonk.ID)
-	}
-	_, err := stmtSaveZonker.Exec(userid, xid, "zonk")
-	if err != nil {
-		elog.Printf("error eradicating: %s", err)
+		_, err := stmtSaveZonker.Exec(userid, xid, "zonk")
+		if err != nil {
+			elog.Printf("error eradicating: %s", err)
+		}
 	}
 }
 

From 04cc94facd80b9e3aa508b32ed82c478049773ac Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Tue, 1 Aug 2023 16:17:45 -0400
Subject: [PATCH 25/26] a little rework of the chonky bonk flow

---
 activity.go | 57 ++++++++++++++++++++++++++++++++---------------------
 1 file changed, 34 insertions(+), 23 deletions(-)

diff --git a/activity.go b/activity.go
index 855f087..4cf0285 100644
--- a/activity.go
+++ b/activity.go
@@ -490,7 +490,7 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
 	maxdepth := 10
 	currenttid := ""
 	goingup := 0
-	var xonkxonkfn func(item junk.Junk, origin string, isUpdate bool) *Honk
+	var xonkxonkfn func(junk.Junk, string, bool, bool) *Honk
 
 	qutify := func(user *WhatAbout, content string) string {
 		if depth >= maxdepth {
@@ -522,7 +522,7 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
 					}
 					prevdepth := depth
 					depth = maxdepth
-					xonkxonkfn(j, originate(m), false)
+					xonkxonkfn(j, originate(m), false, false)
 					depth = prevdepth
 				}
 			}
@@ -541,18 +541,22 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
 			ilog.Printf("error getting onemore: %s: %s", xid, err)
 			return
 		}
-		depth++
-		xonkxonkfn(obj, originate(xid), false)
-		depth--
+		xonkxonkfn(obj, originate(xid), false, false)
 	}
 
-	xonkxonkfn = func(item junk.Junk, origin string, isUpdate bool) *Honk {
+	xonkxonkfn = func(item junk.Junk, origin string, isUpdate bool, isAnnounce bool) *Honk {
 		id, _ := item.GetString("id")
 		what := firstofmany(item, "type")
 		dt, ok := item.GetString("published")
 		if !ok {
 			dt = time.Now().Format(time.RFC3339)
 		}
+		if depth >= maxdepth+5 {
+			ilog.Printf("went too deep in xonkxonk")
+			return nil
+		}
+		depth++
+		defer func() { depth-- }()
 
 		var err error
 		var xid, rid, url, convoy string
@@ -598,27 +602,26 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
 		case "Announce":
 			obj, ok = item.GetMap("object")
 			if ok {
-				// at some point we should just recurse
+				// peek ahead some
 				what, ok := obj.GetString("type")
 				if ok && (what == "Create" || what == "Update") {
-					obj, ok = obj.GetMap("object")
-					if !ok {
-						ilog.Printf("lost object inside announce %s", id)
-						return nil
-					}
 					if what == "Update" {
 						isUpdate = true
 					}
-					what, _ = obj.GetString("type")
+					inner, ok := obj.GetMap("object")
+					if ok {
+						obj = inner
+					} else {
+						xid, _ = obj.GetString("object")
+					}
 				}
-				if what == "Page" {
-					waspage = true
+				if xid == "" {
+					xid, _ = obj.GetString("id")
 				}
-				xid, _ = obj.GetString("id")
 			} else {
 				xid, _ = item.GetString("object")
 			}
-			if !needbonkid(user, xid) {
+			if !isUpdate && !needbonkid(user, xid) {
 				return nil
 			}
 			origin = originate(xid)
@@ -631,7 +634,7 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
 					ilog.Printf("error getting bonk: %s: %s", xid, err)
 				}
 			}
-			what = "bonk"
+			return xonkxonkfn(obj, origin, isUpdate, true)
 		case "Update":
 			isUpdate = true
 			fallthrough
@@ -653,7 +656,7 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
 				ilog.Printf("no object for creation %s", id)
 				return nil
 			}
-			return xonkxonkfn(obj, origin, isUpdate)
+			return xonkxonkfn(obj, origin, isUpdate, isAnnounce)
 		case "Read":
 			xid, ok = item.GetString("object")
 			if ok {
@@ -666,7 +669,7 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
 					ilog.Printf("error getting read: %s", err)
 					return nil
 				}
-				return xonkxonkfn(obj, originate(xid), false)
+				return xonkxonkfn(obj, originate(xid), false, false)
 			}
 			return nil
 		case "Add":
@@ -682,7 +685,7 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
 					ilog.Printf("error getting add: %s", err)
 					return nil
 				}
-				return xonkxonkfn(obj, originate(xid), false)
+				return xonkxonkfn(obj, originate(xid), false, false)
 			}
 			return nil
 		case "Move":
@@ -711,6 +714,7 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
 			obj = item
 			what = "event"
 		case "ChatMessage":
+			isAnnounce = false
 			obj = item
 			what = "chonk"
 		default:
@@ -718,6 +722,9 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
 			dumpactivity(item)
 			return nil
 		}
+		if isAnnounce {
+			what = "bonk"
+		}
 
 		if obj != nil {
 			xid, _ = obj.GetString("id")
@@ -1054,11 +1061,15 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
 		imaginate(&xonk)
 
 		if what == "chonk" {
+			target, _ := obj.GetString("to")
+			if target == user.URL {
+				target = xonk.Honker
+			}
 			ch := Chonk{
 				UserID: xonk.UserID,
 				XID:    xid,
 				Who:    xonk.Honker,
-				Target: xonk.Honker,
+				Target: target,
 				Date:   xonk.Date,
 				Noise:  xonk.Noise,
 				Format: xonk.Format,
@@ -1114,7 +1125,7 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
 		return &xonk
 	}
 
-	return xonkxonkfn(item, origin, false)
+	return xonkxonkfn(item, origin, false, false)
 }
 
 func dumpactivity(item junk.Junk) {

From ef8b188fa9fc5e4ea4650c983bd4676b5c4614ea Mon Sep 17 00:00:00 2001
From: Ted Unangst <tedu@tedunangst.com>
Date: Wed, 2 Aug 2023 18:49:28 -0400
Subject: [PATCH 26/26] raw sendmsg from command line

---
 honk.go | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/honk.go b/honk.go
index 403c3de..8466c67 100644
--- a/honk.go
+++ b/honk.go
@@ -433,6 +433,22 @@ func main() {
 			return
 		}
 		unfollowyou(user, honkerid, true)
+	case "sendmsg":
+		if len(args) < 4 {
+			fmt.Printf("usage: honk send username filename rcpt\n")
+			return
+		}
+		user, err := butwhatabout(args[1])
+		if err != nil {
+			fmt.Printf("user not found\n")
+			return
+		}
+		data, err := os.ReadFile(args[2])
+		if err != nil {
+			fmt.Printf("can't read file\n")
+			return
+		}
+		deliverate(user.ID, args[3], data)
 	case "cleanup":
 		arg := "30"
 		if len(args) > 1 {