Merge remote-tracking branch 'upstream/master'

Bring on the newness
master
Jason Staten 1 year ago
commit aee97f0395

@ -27,6 +27,7 @@ import (
"net/http"
"net/url"
"os"
"regexp"
"strings"
"time"
@ -371,7 +372,7 @@ var boxofboxes = cache.New(cache.Options{Filler: func(ident string) (*Box, bool)
if err != nil {
dlog.Printf("need to get boxes for %s", ident)
var j junk.Junk
j, err = GetJunk(serverUID, ident)
j, err = GetJunk(readyLuserOne, ident)
if err != nil {
dlog.Printf("error getting boxes: %s", err)
return nil, false
@ -506,6 +507,39 @@ func firstofmany(obj junk.Junk, key string) string {
return ""
}
var re_mast0link = regexp.MustCompile(`https://[[:alnum:].]+/users/[[:alnum:]]+/statuses/[[:digit:]]+`)
var re_masto1ink = regexp.MustCompile(`https://[[:alnum:].]+/@[[:alnum:]]+/[[:digit:]]+`)
var re_misslink = regexp.MustCompile(`https://[[:alnum:].]+/notes/[[:alnum:]]+`)
var re_honklink = regexp.MustCompile(`https://[[:alnum:].]+/u/[[:alnum:]]+/h/[[:alnum:]]+`)
var re_romalink = regexp.MustCompile(`https://[[:alnum:].]+/objects/[[:alnum:]-]+`)
var re_qtlinks = regexp.MustCompile(`>https://[^\s<]+<`)
func qutify(user *WhatAbout, content string) string {
// well this is gross
malcontent := strings.ReplaceAll(content, `</span><span class="ellipsis">`, "")
malcontent = strings.ReplaceAll(malcontent, `</span><span class="invisible">`, "")
mlinks := re_qtlinks.FindAllString(malcontent, -1)
for _, m := range mlinks {
m = m[1 : len(m)-1]
dlog.Printf("consider qt: %s", m)
if re_mast0link.MatchString(m) ||
re_masto1ink.MatchString(m) ||
re_misslink.MatchString(m) ||
re_honklink.MatchString(m) ||
re_romalink.MatchString(m) {
j, err := GetJunk(user.ID, m)
dlog.Printf("fetched %s: %s", m, err)
if err == nil {
q, ok := j.GetString("content")
if ok {
content = fmt.Sprintf("%s<blockquote>%s</blockquote>", content, q)
}
}
}
}
return content
}
func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
depth := 0
maxdepth := 10
@ -541,6 +575,7 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
var xid, rid, url, convoy string
var replies []string
var obj junk.Junk
waspage := false
switch what {
case "Delete":
obj, ok = item.GetMap("object")
@ -649,7 +684,8 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
case "Move":
obj = item
what = "move"
case "GuessWord": // dealt with below
case "Page":
waspage = true
fallthrough
case "Audio":
fallthrough
@ -662,8 +698,6 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
case "Note":
fallthrough
case "Article":
fallthrough
case "Page":
obj = item
what = "honk"
case "Event":
@ -688,9 +722,11 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
return nil
}
if originate(xid) != origin {
ilog.Printf("original sin: %s not from %s", xid, origin)
item.Write(ilog.Writer())
return nil
if !develMode && origin != "" {
ilog.Printf("original sin: %s not from %s", xid, origin)
item.Write(ilog.Writer())
return nil
}
}
var xonk Honk
@ -736,6 +772,13 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
if sens, _ := obj["sensitive"].(bool); sens && precis == "" {
precis = "unspecified horror"
}
if waspage {
content += fmt.Sprintf(`<p><a href="%s">%s</a>`, url, url)
url = xid
}
if user.Options.InlineQuotes {
content = qutify(user, content)
}
rid, ok = obj.GetString("inReplyTo")
if !ok {
if robj, ok := obj.GetMap("inReplyTo"); ok {
@ -775,12 +818,6 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
targ, _ := obj.GetString("target")
content += string(templates.Sprintf(`<p>Moved to <a href="%s">%s</a>`, targ, targ))
}
if ot == "GuessWord" {
what = "wonk"
content, _ = obj.GetString("content")
xonk.Wonkles, _ = obj.GetString("wordlist")
go savewonkles(xonk.Wonkles)
}
if what == "honk" && rid != "" {
what = "tonk"
}
@ -1136,8 +1173,6 @@ func jonkjonk(user *WhatAbout, h *Honk) (junk.Junk, junk.Junk) {
fallthrough
case "event":
fallthrough
case "wonk":
fallthrough
case "honk":
j["type"] = "Create"
jo = junk.New()
@ -1145,8 +1180,6 @@ func jonkjonk(user *WhatAbout, h *Honk) (junk.Junk, junk.Junk) {
jo["type"] = "Note"
if h.What == "event" {
jo["type"] = "Event"
} else if h.What == "wonk" {
jo["type"] = "GuessWord"
}
if h.What == "update" {
j["type"] = "Update"
@ -1253,9 +1286,6 @@ func jonkjonk(user *WhatAbout, h *Honk) (junk.Junk, junk.Junk) {
jo["duration"] = "PT" + strings.ToUpper(t.Duration.String())
}
}
if w := h.Wonkles; w != "" {
jo["wordlist"] = w
}
atts := activatedonks(h.Donks)
if len(atts) > 0 {
jo["attachment"] = atts
@ -1474,7 +1504,7 @@ func doesitmatter(what string) bool {
func collectiveaction(honk *Honk) {
user := getserveruser()
for _, ont := range honk.Onts {
dubs := getnameddubs(serverUID, ont)
dubs := getnameddubs(readyLuserOne, ont)
if len(dubs) == 0 {
continue
}
@ -1536,9 +1566,6 @@ func junkuser(user *WhatAbout) junk.Junk {
a["url"] = ava
} else {
u := fmt.Sprintf("https://%s/a?a=%s", serverName, url.QueryEscape(user.URL))
if user.Options.Avahex {
u += "&hex=1"
}
a["url"] = u
}
j["icon"] = a
@ -1591,7 +1618,7 @@ var handfull = cache.New(cache.Options{Filler: func(name string) (string, bool)
return href, true
}
dlog.Printf("fishing for %s", name)
j, err := GetJunkFast(serverUID, fmt.Sprintf("https://%s/.well-known/webfinger?resource=acct:%s", m[1], name))
j, err := GetJunkFast(readyLuserOne, fmt.Sprintf("https://%s/.well-known/webfinger?resource=acct:%s", m[1], name))
if err != nil {
ilog.Printf("failed to go fish %s: %s", name, err)
return "", true
@ -1636,7 +1663,7 @@ func investigate(name string) (*SomeThing, error) {
if name == "" {
return nil, fmt.Errorf("no name")
}
obj, err := GetJunkFast(serverUID, name)
obj, err := GetJunkFast(readyLuserOne, name)
if err != nil {
return nil, err
}
@ -1901,7 +1928,7 @@ func followyou2(user *WhatAbout, j junk.Junk) {
ilog.Printf("updating honker accept: %s", who)
db := opendatabase()
row := db.QueryRow("select name, folxid from honkers where userid = ? and xid = ? and flavor in ('presub')",
row := db.QueryRow("select name, folxid from honkers where userid = ? and xid = ? and flavor in ('presub', 'sub')",
user.ID, who)
var name, folxid string
err := row.Scan(&name, &folxid)

@ -65,7 +65,7 @@ func loadAvatarColors() {
}
}
func genAvatar(name string, hex bool) []byte {
func genAvatar(name string) []byte {
h := sha512.New()
h.Write([]byte(name))
s := h.Sum(nil)
@ -73,27 +73,6 @@ func genAvatar(name string, hex bool) []byte {
for i := 0; i < 64; i++ {
for j := 0; j < 64; j++ {
p := i*img.Stride + j*4
if hex {
tan := 0.577
if i < 32 {
if j < 17-int(float64(i)*tan) || j > 46+int(float64(i)*tan) {
img.Pix[p+0] = 0
img.Pix[p+1] = 0
img.Pix[p+2] = 0
img.Pix[p+3] = 255
continue
}
} else {
if j < 17-int(float64(64-i)*tan) || j > 46+int(float64(64-i)*tan) {
img.Pix[p+0] = 0
img.Pix[p+1] = 0
img.Pix[p+2] = 0
img.Pix[p+3] = 255
continue
}
}
}
xx := i/16*16 + j/16
x := s[xx]
if x < 64 {

@ -14,54 +14,3 @@
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package main
import (
"net/http"
"strings"
"humungus.tedunangst.com/r/webs/junk"
)
func servewonkles(w http.ResponseWriter, r *http.Request) {
url := r.FormValue("w")
dlog.Printf("getting wordlist: %s", url)
wonkles := getxonker(url, "wonkles")
if wonkles == "" {
wonkles = savewonkles(url)
if wonkles == "" {
http.NotFound(w, r)
return
}
}
var words []string
for _, l := range strings.Split(wonkles, "\n") {
words = append(words, l)
}
if !develMode {
w.Header().Set("Cache-Control", "max-age=7776000")
}
j := junk.New()
j["wordlist"] = words
j.Write(w)
}
func savewonkles(url string) string {
w := getxonker(url, "wonkles")
if w != "" {
return w
}
ilog.Printf("fetching wonkles: %s", url)
res, err := fetchsome(url)
if err != nil {
ilog.Printf("error fetching wonkles: %s", err)
return ""
}
w = getxonker(url, "wonkles")
if w != "" {
return w
}
w = string(res)
savexonker(url, w, "wonkles", "")
return w
}

@ -437,9 +437,7 @@ func donksforhonks(honks []*Honk) {
continue
}
case "wonkles":
h.Wonkles = j
case "guesses":
h.Guesses = template.HTML(j)
case "oldrev":
default:
elog.Printf("unknown meta genus: %s", genus)
@ -849,20 +847,6 @@ func saveextras(tx *sql.Tx, h *Honk) error {
return err
}
}
if w := h.Wonkles; w != "" {
_, err := tx.Stmt(stmtSaveMeta).Exec(h.ID, "wonkles", w)
if err != nil {
elog.Printf("error saving wonkles: %s", err)
return err
}
}
if g := h.Guesses; g != "" {
_, err := tx.Stmt(stmtSaveMeta).Exec(h.ID, "guesses", g)
if err != nil {
elog.Printf("error saving guesses: %s", err)
return err
}
}
return nil
}

@ -54,13 +54,6 @@ activities are ignored.
Limited support.
.It Vt Audio
Limited Support.
.It Vt GuessWord
Guess the word game.
(Unofficial extension.)
The solution is stored in
.Fa content
with the possible words, one per line, in a file located at
.Fa wordlist .
.El
.Pp
Honk primarily supports HTML content, not markdown or other formats,

@ -2,6 +2,22 @@ changelog
=== next
+ Remove the wonk support. Fun's over, back to work.
+ All inclusive danger zone spoiler alerts.
+ Emu peeker
+ CSP compliance
+ Filter to match anything with summary/warning.
+ Start collecting quties.
+ Fix http signatures for GET requests.
+ Fix adjacent mentions.
+ Fix argv for chpass.
+ Avoid self mention in reply all.

@ -51,6 +51,7 @@ fields as well.
.It Ar text
Regular expression match against the post
.Fa content .
The special value of "." will match any post with a summary only.
.It Ar is announce
Is announced (shared).
.It Ar announce of

@ -74,7 +74,8 @@ will become a horizontal rule.
.Pp
If the first line of a honk begins with
.Dq DZ:
(danger zone) it will be used a summary and the post marked sensitive.
(danger zone) or any other combination of two letters and a colon,
it will be used a summary and the post marked sensitive.
.Pp
Mentioning a specfic user such as
.Pq @user@example.social
@ -139,6 +140,9 @@ A 24 hour clock is assumed, unless am or pm are specified.
The duration is optional and may be specified as XdYhZm for X days, Y hours,
and Z minutes (1d12h would be a 36 hour event).
.Pp
Clicking the pretty circle face will open the emu peeker to add in the
selection of emus.
.Pp
When everything is at last ready to go, press the
.Dq it's gonna be honked
button.

@ -41,7 +41,7 @@ proxy_set_header Host $http_host;
.Ss Build
Building
.Nm
requires a go compiler 1.13 and libsqlite.
requires a go compiler 1.16 and libsqlite.
On
.Ox
this is the go and sqlite3 packages.

@ -181,9 +181,6 @@ func reverbolate(userid int64, honks []*Honk) {
h.HTPrecis = template.HTML(h.Precis)
h.HTML = template.HTML(h.Noise)
if h.What == "wonked" {
h.HTML = "? wonk ?"
}
if redo := relingo[h.What]; redo != "" {
h.What = redo
}
@ -290,12 +287,14 @@ func imaginate(honk *Honk) {
htf.String(honk.Noise)
}
var re_dangerous = regexp.MustCompile("^[a-zA-Z]{2}:")
func translate(honk *Honk) {
if honk.Format == "html" {
return
}
noise := honk.Noise
if strings.HasPrefix(noise, "DZ:") {
if re_dangerous.MatchString(noise) {
idx := strings.Index(noise, "\n")
if idx == -1 {
honk.Precis = noise
@ -457,7 +456,7 @@ func memetize(honk *Honk) {
honk.Noise = re_memes.ReplaceAllStringFunc(honk.Noise, repl)
}
var re_quickmention = regexp.MustCompile("(^|[ \n])@[[:alnum:]]+([ \n.]|$)")
var re_quickmention = regexp.MustCompile("(^|[ \n])@[[:alnum:]]+([ \n.,']|$)")
func quickrename(s string, userid int64) string {
nonstop := true
@ -472,7 +471,8 @@ func quickrename(s string, userid int64) string {
prefix += "@"
m = m[1:]
tail := ""
if last := m[len(m)-1]; last == ' ' || last == '\n' || last == '.' {
if last := m[len(m)-1]; last == ' ' || last == '\n' ||
last == '.' || last == ',' || last == '\'' {
tail = m[len(m)-1:]
m = m[:len(m)-1]
}
@ -643,7 +643,7 @@ var zaggies = cache.New(cache.Options{Filler: func(keyname string) (httpsig.Publ
data := getxonker(keyname, "pubkey")
if data == "" {
dlog.Printf("hitting the webs for missing pubkey: %s", keyname)
j, err := GetJunk(serverUID, keyname)
j, err := GetJunk(readyLuserOne, keyname)
if err != nil {
ilog.Printf("error getting %s pubkey: %s", keyname, err)
when := time.Now().UTC().Format(dbtimeformat)

@ -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.59
humungus.tedunangst.com/r/webs v0.6.60
)

@ -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.59 h1:zOlBGZqrRo22QND1CN8HbhEv3JwoiZkspi7TgVuJS0s=
humungus.tedunangst.com/r/webs v0.6.59/go.mod h1:03R0N9BcT49HB4TDd1YmarpbiPvPzVDm74Mk4h1hYPc=
humungus.tedunangst.com/r/webs v0.6.60 h1:2PjVTVH3js4PXv8lrEw7nxtRmwwt1COl7t7tZMPxBPs=
humungus.tedunangst.com/r/webs v0.6.60/go.mod h1:03R0N9BcT49HB4TDd1YmarpbiPvPzVDm74Mk4h1hYPc=

@ -107,7 +107,7 @@ func filtcachefiller(userid int64) (afiltermap, bool) {
expflush = filt.Expiration
}
}
if t := filt.Text; t != "" {
if t := filt.Text; t != "" && t != "." {
wordfront := t[0] != '#'
wordtail := true
t = "(?i:" + t + ")"
@ -314,7 +314,7 @@ func matchfilterX(h *Honk, f *Filter) string {
rv += " announce"
}
}
if match && f.Text != "" {
if match && f.Text != "" && f.Text != "." {
match = false
re := f.re_text
m := re.FindString(h.Precis)
@ -334,6 +334,13 @@ func matchfilterX(h *Honk, f *Filter) string {
rv = m
}
}
if match && f.Text == "." {
match = false
if h.Precis != "" {
match = true
rv = h.Precis
}
}
if match {
return rv
}

@ -51,16 +51,16 @@ type WhatAbout struct {
}
type UserOptions struct {
SkinnyCSS bool `json:",omitempty"`
OmitImages bool `json:",omitempty"`
Avahex bool `json:",omitempty"`
MentionAll bool `json:",omitempty"`
Avatar string `json:",omitempty"`
Banner string `json:",omitempty"`
MapLink string `json:",omitempty"`
Reaction string `json:",omitempty"`
MeCount int64
ChatCount int64
SkinnyCSS bool `json:",omitempty"`
OmitImages bool `json:",omitempty"`
MentionAll bool `json:",omitempty"`
InlineQuotes bool `json:",omitempty"`
Avatar string `json:",omitempty"`
Banner string `json:",omitempty"`
MapLink string `json:",omitempty"`
Reaction string `json:",omitempty"`
MeCount int64
ChatCount int64
}
type KeyInfo struct {
@ -69,6 +69,7 @@ type KeyInfo struct {
}
const serverUID int64 = -2
const readyLuserOne int64 = 1
type Honk struct {
ID int64
@ -103,8 +104,6 @@ type Honk struct {
Time *Time
Mentions []Mention
Badonks []Badonk
Wonkles string
Guesses template.HTML
}
type Badonk struct {
@ -179,10 +178,6 @@ func (honk *Honk) IsReacted() bool {
return honk.Flags&flagIsReacted != 0
}
func (honk *Honk) IsWonked() bool {
return honk.Flags&flagIsWonked != 0
}
type Donk struct {
FileID int64
XID string

@ -3,12 +3,12 @@
<div class="info">
{{ .AboutMsg }}
<p>
<table style="font-size:0.8em">
<table class="font08em">
<tbody>
<tr><td>version:<td style="text-align:right">{{ .HonkVersion }}
<tr><td>memory:<td style="text-align:right">{{ printf "%.02f" .Sensors.Memory }}MB
<tr><td>uptime:<td style="text-align:right">{{ printf "%.02f" .Sensors.Uptime }}s
<tr><td>cputime:<td style="text-align:right">{{ printf "%.02f" .Sensors.CPU }}s
<tr><td>version:<td class="textright">{{ .HonkVersion }}
<tr><td>memory:<td class="textright">{{ printf "%.02f" .Sensors.Memory }}MB
<tr><td>uptime:<td class="textright">{{ printf "%.02f" .Sensors.Uptime }}s
<tr><td>cputime:<td class="textright">{{ printf "%.02f" .Sensors.CPU }}s
</table>
<p>
</div>

@ -10,13 +10,12 @@
<p><textarea name="whatabout">{{ .WhatAbout }}</textarea>
<p><label class="button" for="skinny">skinny layout:</label>
<input tabindex=1 type="checkbox" id="skinny" name="skinny" value="skinny" {{ if .User.Options.SkinnyCSS }}checked{{ end }}><span></span>
<p><label class="button" for="avahex">hex avatar:</label>
<input tabindex=1 type="checkbox" id="avahex" name="avahex" value="avahex" {{ if .User.Options.Avahex }}checked{{ end }}><span></span>
<p><label class="button" for="omitimages">omit images:</label>
<input tabindex=1 type="checkbox" id="omitimages" name="omitimages" value="omitimages" {{ if .User.Options.OmitImages }}checked{{ end }}><span></span>
<p><label class="button" for="mentionall">mention all:</label>
<input tabindex=1 type="checkbox" id="mentionall" name="mentionall" value="mentionall" {{ if .User.Options.MentionAll }}checked{{ end }}><span></span>
<p><label class="button" for="inlineqts">inline quotes:</label>
<input tabindex=1 type="checkbox" id="inlineqts" name="inlineqts" value="inlineqts" {{ if .User.Options.InlineQuotes }}checked{{ end }}><span></span>
<p><label class="button" for="maps">apple map links:</label>
<input tabindex=1 type="checkbox" id="maps" name="maps" value="apple" {{ if eq "apple" .User.Options.MapLink }}checked{{ end }}><span></span>
<p><label class="button" for="reaction">reaction:</label>

@ -1,4 +1,5 @@
{{ template "header.html" . }}
<script src="/misc.js{{ .MiscJSParam }}" defer></script>
<main>
<div class="info">
<p>
@ -10,14 +11,8 @@
<p><label for=noise>noise:</label><br>
<textarea name="noise" id="noise"></textarea>
<p><button name="chonk" value="chonk">chonk</button>
<label class=button id="donker">attach: <input onchange="updatedonker(this);" type="file" name="donk"><span></span></label>
<label class=button id="donker">attach: <input type="file" name="donk"><span></span></label>
</form>
<script>
function updatedonker(el) {
el = el.parentElement
el.children[1].textContent = el.children[0].value.slice(-20)
}
</script>
</div>
{{ $chonkcsrf := .ChonkCSRF }}
{{ range .Chatter }}
@ -59,7 +54,7 @@ chatter: {{ .Target }}
<p><label for=noise>noise:</label><br>
<textarea name="noise" id="noise"></textarea>
<p><button name="chonk" value="chonk">chonk</button>
<label class=button id="donker">attach: <input onchange="updatedonker(this);" type="file" name="donk"><span></span></label>
<label class=button id="donker">attach: <input type="file" name="donk"><span></span></label>
</form>
</section>
{{ end }}

@ -6,7 +6,7 @@
{{ range .Combos }}
<section class="honk">
<header>
<p style="font-size: 1.8em"><a href="/c/{{ . }}">{{ . }}</a>
<p class="font18em"><a href="/c/{{ . }}">{{ . }}</a>
</header>
</section>
{{ end }}

@ -0,0 +1 @@
{{ range .Emus }}<img class="emu" alt=":{{.Name}}:" src="{{ .ID }}">{{ end }}

@ -6,14 +6,11 @@
{{ if .LocalStyleParam }}
<link href="/local.css{{ .LocalStyleParam }}" rel="stylesheet">
{{ end }}
<style>
{{ .UserStyle }}
</style>
<link href="/icon.png" rel="icon">
<meta name="theme-color" content="#305">
<meta name="viewport" content="width=device-width">
</head>
<body>
<body{{ if .UserStyle}} {{ .UserStyle }}{{ end }}>
<header>
{{ if .UserInfo }}
<details id="topmenu">
@ -22,7 +19,7 @@
<li><a id="homelink" href="/">home</a>
<li><a id="atmelink" href="/atme">@me<span id=mecount>{{ if .UserInfo.Options.MeCount }}({{ .UserInfo.Options.MeCount }}){{ end }}</span></a>
<li><a id="firstlink" href="/first">first</a>
<li style="list-style-type:none; margin-left:-1em">
<li class="details">
<details>
<summary>combos</summary>
<ul>
@ -39,7 +36,7 @@
<li><a href="/honkers">honkers</a>
<li><a href="/hfcs">filters</a>
<li><a href="/account">account</a>
<li style="list-style-type:none; margin-left:-1em">
<li class="details">
<details>
<summary>more stuff</summary>
<ul>
@ -59,7 +56,7 @@
</details>
<div id="topspacer">
<p>
<p class="nophone" onclick="window.scrollTo(0,0)">top
<p class="nophone">top
</div>
{{ else }}
<div id="topmenu">

@ -1,4 +1,4 @@
<article class="honk {{ .Honk.Style }}" data-convoy="{{ .Honk.Convoy }}">
<article class="honk {{ .Honk.Style }}" data-convoy="{{ .Honk.Convoy }}" data-hname="{{ .Honk.Handles }}" data-xid="{{ .Honk.XID }}" data-id="{{ .Honk.ID }}">
{{ $bonkcsrf := .BonkCSRF }}
{{ $IsPreview := .IsPreview }}
{{ $maplink := .MapLink }}
@ -30,7 +30,7 @@
<span class="clip"><a href="{{ .URL }}" rel=noreferrer>{{ .What }}</a> {{ .Date.Local.Format "02 Jan 2006 15:04 -0700" }}</span>
{{ if .Oonker }}
<br>
<span style="margin-left: 1em;" class="clip">
<span class="left1em clip">
{{ if $bonkcsrf }}
original: <a class="honkerlink" href="/h?xid={{ .Oonker }}" data-xid="{{ .Oonker }}">{{ .Oondle }}</a>
{{ else }}
@ -40,14 +40,14 @@ original: <a href="{{ .Oonker }}" rel=noreferrer>{{ .Oondle }}</a>
{{ else }}
{{ if .RID }}
<br>
<span style="margin-left: 1em;" class="clip">
<span class="left1em clip">
in reply to: <a href="{{ .RID }}" rel=noreferrer>{{ .RID }}</a>
</span>
{{ end }}
{{ end }}
<br>
{{ if $bonkcsrf }}
<span style="margin-left: 1em;" class="clip">convoy: <a class="convoylink" href="/t?c={{ .Convoy }}">{{ .Convoy }}</a></span>
<span class="left1em clip">convoy: <a class="convoylink" href="/t?c={{ .Convoy }}">{{ .Convoy }}</a></span>
{{ end }}
</header>
<p>
@ -89,15 +89,6 @@ in reply to: <a href="{{ .RID }}" rel=noreferrer>{{ .RID }}</a>
{{ end }}
</details>
{{ end }}
{{ if eq .Honk.What "wonked" }}
<p>
{{ if and $bonkcsrf .Honk.IsWonked }}
{{ .Honk.Guesses }}
<p>{{ .Honk.Noise }}
{{ else }}
<button onclick="return playit(this, '{{ .Honk.Noise }}', '{{ .Honk.Wonkles }}', '{{ .Honk.XID }}')">it's play time!</button>
{{ end }}
{{ end }}
{{ if and $bonkcsrf (not $IsPreview) }}
<p>
<details class="actions">
@ -106,41 +97,41 @@ in reply to: <a href="{{ .RID }}" rel=noreferrer>{{ .RID }}</a>
<p>
{{ if .Honk.Public }}
{{ if .Honk.IsBonked }}
<button onclick="return unbonk(this, '{{ .Honk.XID }}');">unbonk</button>
<button class="unbonk">unbonk</button>
{{ else }}
<button onclick="return bonk(this, '{{ .Honk.XID }}');">bonk</button>
<button class="bonk">bonk</button>
{{ end }}
{{ else }}
<button disabled>nope</button>
{{ end }}
<button onclick="return showhonkform(this, '{{ .Honk.XID }}', '{{ .Honk.Handles }}');"><a href="/newhonk?rid={{ .Honk.XID }}">honk back</a></button>
<button onclick="return muteit(this, '{{ .Honk.Convoy }}');">mute</button>
<button onclick="return showelement('evenmore{{ .Honk.ID }}')">even more</button>
<button class="honkback"><a href="/newhonk?rid={{ .Honk.XID }}">honk back</a></button>
<button class="mute">mute</button>
<button class="evenmore">even more</button>
</div>
<div id="evenmore{{ .Honk.ID }}" style="display:none">
<div id="evenmore{{ .Honk.ID }}" class="hide">
<p>
<button onclick="return zonkit(this, '{{ .Honk.XID }}');">zonk</button>
<button class="zonk">zonk</button>
{{ if .Honk.IsAcked }}
<button onclick="return flogit(this, 'deack', '{{ .Honk.XID }}');">deack</button>
<button class="flogit-deack">deack</button>
{{ else }}
<button onclick="return flogit(this, 'ack', '{{ .Honk.XID }}');">ack</button>
<button class="flogit-ack" >ack</button>
{{ end }}
{{ if .Honk.IsSaved }}
<button onclick="return flogit(this, 'unsave', '{{ .Honk.XID }}');">unsave</button>
<button class="flogit-unsave">unsave</button>
{{ else }}
<button onclick="return flogit(this, 'save', '{{ .Honk.XID }}');">save</button>
<button class="flogit-save">save</button>
{{ end }}
{{ if .Honk.IsUntagged }}
<button disabled>untagged</button>
{{ else }}
<button onclick="return flogit(this, 'untag', '{{ .Honk.XID }}');">untag me</button>
<button class="flogit-untag">untag me</button>
{{ end }}
<button><a href="/edit?xid={{ .Honk.XID }}">edit</a></button>
{{ if not (eq .Badonk "none") }}
{{ if .Honk.IsReacted }}
<button disabled>badonked</button>
{{ else }}
<button onclick="return flogit(this, 'react', '{{ .Honk.XID }}');">{{ .Badonk }}</button>
<button class="flogit-react" >{{ .Badonk }}</button>
{{ end }}
{{ end }}
</div>

@ -1,4 +1,5 @@
{{ template "header.html" . }}
<script src="/misc.js{{ .MiscJSParam }}" defer></script>
<main>
<div class="info">
<p>
@ -21,21 +22,13 @@
</div>
{{ $honkercsrf := .HonkerCSRF }}
<div class="info">
<script>
function expandstuff() {
var els = document.querySelectorAll(".honk details")
for (var i = 0; i < els.length; i++) {
els[i].open = true
}
}
</script>
<p><button onclick="expandstuff()">expand</button>
<p><button class="expand">expand</button>
</div>
{{ range .Honkers }}
<section class="honk">
<header>
<img alt="avatar" src="/a?a={{ .XID }}" loading="lazy">
<p style="font-size: 1.8em"><a href="/h/{{ .Name }}">{{ .Name }}<a>
<p class="font18em"><a href="/h/{{ .Name }}">{{ .Name }}</a>
</header>
<p>
<details>

@ -1,6 +1,6 @@
<p id="honkformhost">
<button id="honkingtime" onclick="return showhonkform();" {{ if .IsPreview }}style="display:none"{{ end }}><a href="/newhonk">it's honking time</a></button>
<form id="honkform" action="/honk" method="POST" enctype="multipart/form-data" {{ if not .IsPreview }}style="display: none"{{ end }}>
<button id="honkingtime" {{ if .IsPreview }}class="hide"{{ end }}><a href="/newhonk">it's honking time</a></button>
<form id="honkform" action="/honk" method="POST" enctype="multipart/form-data" {{ if not .IsPreview }}class="hide"{{ end }}>
<input type="hidden" name="CSRF" value="{{ .HonkCSRF }}">
<input type="hidden" name="updatexid" id="updatexidinput" value = "{{ .UpdateXID }}">
<input type="hidden" name="rid" id="ridinput" value="{{ .InReplyTo }}">
@ -9,12 +9,12 @@
<details>
<summary>more options</summary>
<p>
<label class=button id="donker">attach: <input onchange="updatedonker();" type="file" name="donk"><span>{{ .SavedFile }}</span></label>
<label class=button id="donker">attach: <input type="file" name="donk"><span>{{ .SavedFile }}</span></label>
<input type="hidden" id="saveddonkxid" name="donkxid" value="{{ .SavedFile }}">
<p id="donkdescriptor"><label for=donkdesc>description:</label><br>
<input type="text" name="donkdesc" value="{{ .DonkDesc }}" autocomplete=off>
{{ with .SavedPlace }}
<p><button id=checkinbutton type=button onclick="fillcheckin()">checkin</button>
<p><button id=checkinbutton type=button>checkin</button>
<div id=placedescriptor>
<p><label>name:</label><br><input type="text" name="placename" id=placenameinput value="{{ .Name }}">
<p><label>url:</label><br><input type="text" name="placeurl" id=placeurlinput value="{{ .Url }}">
@ -22,26 +22,34 @@
<label>lon: </label><input type="text" size=9 name="placelong" id=placelonginput value="{{ .Longitude }}">
</div>
{{ else }}
<p><button id=checkinbutton type=button onclick="fillcheckin()">checkin</button>
<div id=placedescriptor style="display: none">
<p><button id=checkinbutton type=button>checkin</button>
<div id=placedescriptor class="hide">
<p><label>name:</label><br><input type="text" name="placename" id=placenameinput value="">
<p><label>url:</label><br><input type="text" name="placeurl" id=placeurlinput value="">
<p><label>lat: </label><input type="text" size=9 name="placelat" id=placelatinput value="">
<label>lon: </label><input type="text" size=9 name="placelong" id=placelonginput value="">
</div>
{{ end }}
<p><button id=addtimebutton type=button onclick="showelement('timedescriptor')">add time</button>
<div id=timedescriptor style="{{ or .ShowTime "display: none" }}">
<p><button id=addtimebutton type=button>add time</button>
<div id=timedescriptor class="{{ or .ShowTime "hide" }}">
<p><label for=timestart>start:</label><br>
<input type="text" name="timestart" value="{{ .StartTime }}">
<p><label for=timeend>duration:</label><br>
<input type="text" name="timeend" value="{{ .Duration }}">
</div>
<svg class="emuload" id="emuload" xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-mood-neutral" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<circle cx="12" cy="12" r="9"></circle>
<line x1="9" y1="10" x2="9.01" y2="10"></line>
<line x1="15" y1="10" x2="15.01" y2="10"></line>
</svg>
<div id="emupicker">
</div>
</details>
<p>
<textarea name="noise" id="honknoise">{{ .Noise }}</textarea>
<p class="buttonarray">
<button>it's gonna be honked</button>
<button name="preview" value="preview">preview</button>
<button type=button name="cancel" value="cancel" onclick="cancelhonking()">cancel</button>
<button type=button name="cancel" value="cancel">cancel</button>
</form>

@ -3,42 +3,24 @@
<div class="info" id="infobox">
<div id="srvmsg">
{{ if .Name }}
<p>{{ .Name }} <span style="margin-left:1em;"><a href="/u/{{ .Name }}/rss">rss</a></span>
<p>{{ .Name }} <span class="left1em"><a href="/u/{{ .Name }}/rss">rss</a></span>
<p>{{ .WhatAbout }}
{{ end }}
<p>{{ .ServerMessage }}
</div>
{{ if .HonkCSRF }}
{{ template "honkform.html" . }}
<script>
var csrftoken = {{ .HonkCSRF }}
var honksforpage = { }
var curpagestate = { name: "{{ .PageName }}", arg : "{{ .PageArg }}" }
var tophid = { }
tophid[curpagestate.name + ":" + curpagestate.arg] = "{{ .TopHID }}"
var servermsgs = { }
servermsgs[curpagestate.name + ":" + curpagestate.arg] = "{{ .ServerMessage }}"
</script>
<script src="/honkpage.js{{ .JSParam }}"></script>
<script src="/honkpage.js{{ .JSParam }}" defer data-csrf="{{ .HonkCSRF }}" data-pagename="{{ .PageName }}" data-pagearg="{{ .PageArg }}" data-tophid="{{ .TopHID }}" data-srvmsg="{{ .ServerMessage }}"></script>
{{ end }}
<script>
function playit(elem, word, wordlist, xid) {
import('/wonk.js').then(module => {
makeaguess = module.makeaguess
module.addguesscontrols(elem, word, wordlist, xid)
})
}
</script>
{{ if .LocalJSParam }}
<script src="/local.js{{ .LocalJSParam }}"></script>
<script src="/local.js{{ .LocalJSParam }}" defer></script>
{{ end }}
</div>
{{ if and .HonkCSRF (not .IsPreview) }}
<div class="info" id="refreshbox">
<p><button onclick="refreshhonks(this)">refresh</button><span></span>
<button onclick="oldestnewest(this)">scroll down</button>
<p><button class="refresh">refresh</button><span></span>
<button class="scrolldown">scroll down</button>
</div>
{{ if eq .ServerMessage "one honk maybe more" }} <script> hideelement("refreshbox")</script> {{ end }}
{{ end }}
<div id="honksonpage">
<div>

@ -1,10 +1,16 @@
var csrftoken = ""
var honksforpage = { }
var curpagestate = { name: "", arg : "" }
var tophid = { }
var servermsgs = { }
function encode(hash) {
var s = []
for (var key in hash) {
var val = hash[key]
s.push(escape(key) + "=" + escape(val))
}
return s.join("&")
var s = []
for (var key in hash) {
var val = hash[key]
s.push(encodeURIComponent(key) + "=" + encodeURIComponent(val))
}
return s.join("&")
}
function post(url, data) {
var x = new XMLHttpRequest()
@ -268,25 +274,72 @@ function relinklinks() {
el.onclick = pageswitcher("honker", xid)
el.classList.remove("honkerlink")
}
els = document.querySelectorAll("#honksonpage article button")
els.forEach(function(el) {
var honk = el.closest("article")
var convoy = honk.dataset.convoy
var hname = honk.dataset.hname
var xid = honk.dataset.xid
var id = Number(honk.dataset.id)
if (!(id > 0)) {
console.error("could not determine honk id")
return
}
if (el.classList.contains("unbonk")) {
el.onclick = function() {
unbonk(el, xid);
}
} else if (el.classList.contains("bonk")) {
el.onclick = function() {
bonk(el, xid)
}
} else if (el.classList.contains("honkback")) {
el.onclick = function() {
return showhonkform(el, xid, hname)
}
} else if (el.classList.contains("mute")) {
el.onclick = function() {
muteit(el, convoy);
}
} else if (el.classList.contains("evenmore")) {
var more = document.querySelector("#evenmore"+id);
el.onclick = function() {
more.classList.toggle("hide");
}
} else if (el.classList.contains("zonk")) {
el.onclick = function() {
zonkit(el, xid);
}
} else if (el.classList.contains("flogit-deack")) {
el.onclick = function() {
flogit(el, "deack", xid);
}
} else if (el.classList.contains("flogit-ack")) {
el.onclick = function() {
flogit(el, "ack", xid);
}
} else if (el.classList.contains("flogit-unsave")) {
el.onclick = function() {
flogit(el, "unsave", xid);
}
} else if (el.classList.contains("flogit-save")) {
el.onclick = function() {
flogit(el, "save", xid);
}
} else if (el.classList.contains("flogit-untag")) {
el.onclick = function() {
flogit(el, "untag", xid);
}
} else if (el.classList.contains("flogit-react")) {
el.onclick = function() {
flogit(el, "react", xid);
}
}
})
}
(function() {
var el = document.getElementById("homelink")
el.onclick = pageswitcher("home", "")
el = document.getElementById("atmelink")
el.onclick = pageswitcher("atme", "")
el = document.getElementById("firstlink")
el.onclick = pageswitcher("first", "")
el = document.getElementById("savedlink")
el.onclick = pageswitcher("saved", "")
el = document.getElementById("longagolink")
el.onclick = pageswitcher("longago", "")
relinklinks()
window.onpopstate = statechanger
history.replaceState(curpagestate, "some title", "")
})();
(function() {
hideelement("donkdescriptor")
})();
function showhonkform(elem, rid, hname) {
var form = lehonkform
form.style = "display: block"
@ -363,3 +416,92 @@ function fillcheckin() {
}, gpsoptions)
}
}
function addemu(elem) {
const data = elem.alt
const box = document.getElementById("honknoise");
box.value += data;
}
function loademus() {
div = document.getElementById("emupicker")
request = new XMLHttpRequest()
request.open('GET', '/emus')
request.onload = function() {
div.innerHTML = request.responseText
div.querySelectorAll(".emu").forEach(function(el) {
el.onclick = function() {
addemu(el)
}
})
}
if (div.style.display === "none") {
div.style.display = "block";
} else {
div.style.display = "none";
}
request.send()
}
// init
(function() {
var me = document.currentScript;
csrftoken = me.dataset.csrf
curpagestate.name = me.dataset.pagename
curpagestate.arg = me.dataset.pagearg
tophid[curpagestate.name + ":" + curpagestate.arg] = me.dataset.tophid
servermsgs[curpagestate.name + ":" + curpagestate.arg] = me.dataset.srvmsg
var el = document.getElementById("homelink")
el.onclick = pageswitcher("home", "")
el = document.getElementById("atmelink")
el.onclick = pageswitcher("atme", "")
el = document.getElementById("firstlink")
el.onclick = pageswitcher("first", "")
el = document.getElementById("savedlink")
el.onclick = pageswitcher("saved", "")
el = document.getElementById("longagolink")
el.onclick = pageswitcher("longago", "")
var totop = document.querySelector(".nophone")
if (totop) {
totop.onclick = function() {
window.scrollTo(0,0)
}
}
var refreshbox = document.getElementById("refreshbox")
if (refreshbox) {
refreshbox.querySelectorAll("button").forEach(function(el) {
if (el.classList.contains("refresh")) {
el.onclick = function() {
refreshhonks(el)
}
} else if (el.classList.contains("scrolldown")) {
el.onclick = function() {
oldestnewest(el)
}
}
})
if (me.dataset.srvmsg == "one honk maybe more") {
hideelement(refreshbox)
}
}
var td = document.getElementById("timedescriptor")
document.getElementById("addtimebutton").onclick = function() {
td.classList.toggle("hide")
}
document.getElementById("honkingtime").onclick = function() {
return showhonkform()
}
document.getElementById("checkinbutton").onclick = fillcheckin
document.getElementById("emuload").onclick = loademus
document.querySelector("#donker input").onchange = updatedonker
document.querySelector("button[name=cancel]").onclick = cancelhonking
relinklinks()
window.onpopstate = statechanger
history.replaceState(curpagestate, "some title", "")
hideelement("donkdescriptor")
})();

@ -0,0 +1,25 @@
function expandstuff() {
var els = document.querySelectorAll(".honk details")
for (var i = 0; i < els.length; i++) {
els[i].open = true
}
}
function updatedonker(el) {
el = el.parentElement
el.children[1].textContent = el.children[0].value.slice(-20)
}
(function() {
var expand = document.querySelector("button.expand")
if (expand) {
expand.onclick = expandstuff
}
var donk = document.querySelector("#donker input")
if (donk) {
donk.onchange = function() {
updatedonker(this);
}
}
})()

@ -10,7 +10,7 @@
{{ $letter = (call $firstrune .Name) }}
<li><p>
{{ end }}
<span style="white-space: nowrap;"><a href="/o/{{ .Name }}">#{{ .Name }}</a> ({{ .Count }})</span>
<span class="wsnowrap"><a href="/o/{{ .Name }}">#{{ .Name }}</a> ({{ .Count }})</span>
{{ end }}
</ul>
</div>

@ -253,6 +253,11 @@ input[type=file] {
.honk details.actions summary {
color: var(--fg-subtle);
}
#emupicker{height:300px;overflow-y:scroll;padding:3px;background:var(--bg-dark);border:solid 5px var(--fg-subtle);text-align:center;display:none;}
#emupicker img{margin:0;}
.emuload{background:var(--bg-page);padding:0.5em;}
.subtle .noise {
color: var(--fg-subtle);
font-size: 0.8em;
@ -340,3 +345,44 @@ img.emu {
--fg-limited:</