Compare commits

...

43 commits

Author SHA1 Message Date
Ted Unangst
10bca44dc5 we should support Update for more types than Note 2023-03-25 16:29:50 -04:00
Ted Unangst
fc567f7f69 m 2023-03-25 15:10:30 -04:00
Ted Unangst
353a3b27f6 for image types, prefer the original over attachments 2023-03-25 15:09:53 -04:00
Ted Unangst
b5470175c7 allow setting context by hand 2023-03-25 14:46:11 -04:00
Ted Unangst
9e43ccbb88 Added tag v0.9.91 for changeset d7c3a01e7aae 2023-03-17 14:29:34 -04:00
Ted Unangst
67c2d983bd that should do it 2023-03-17 14:29:22 -04:00
Ted Unangst
2a8f20feff quiet a log error 2023-03-17 14:28:29 -04:00
Ted Unangst
ae8d1909d8 use the right variable 2023-03-17 14:24:15 -04:00
Ted Unangst
deec523437 Added tag v0.9.9 for changeset 4b8cf31560b7 2023-03-17 10:46:01 -04:00
Ted Unangst
4f8a33eb2b 0.9.9 release 2023-03-17 10:45:51 -04:00
Ted Unangst
7058ecd1d0 clean up the dust 2023-03-17 10:45:25 -04:00
Ted Unangst
31a9ea6a08 need to allow media src for le memes 2023-03-15 17:01:30 -04:00
Ted Unangst
2eb5b9d3c0 these logs are annoying 2023-03-15 15:43:57 -04:00
Ted Unangst
858ce2f291 allow filtering of replies 2023-03-15 15:26:46 -04:00
Ted Unangst
af2deab137 revert to sending summary for images, it should work 2023-03-09 12:32:53 -05:00
Ted Unangst
a857fdd06c only dumb it down for images, stuff like PDF should retain name 2023-03-09 02:00:12 -05:00
Ted Unangst
dab8de6670 don't drop the donk! sometimes preview would find the wrong one on resubmit. 2023-03-09 01:57:20 -05:00
Ted Unangst
b261f73c49 some pleroma frontends apparently use notice urls 2023-03-09 00:54:59 -05:00
Ted Unangst
bf9e65d699 simplify image descriptions so that lesser implementations can find them 2023-03-09 00:51:33 -05:00
Ted Unangst
7f8d2068fd m 2023-03-06 17:46:50 -05:00
Ted Unangst
2559b0cf16 experiment with allowing user avatar to be visible 2023-03-06 17:42:30 -05:00
Ted Unangst
ff5b731644 newlines in error messages 2023-03-02 23:37:16 -05:00
Ted Unangst
ba9d745efa need to query for the sub here 2023-03-02 23:34:54 -05:00
Ted Unangst
e965f7090f command line follow and unfollow 2023-03-01 14:48:50 -05:00
Ted Unangst
e8d1d64f1c namecheck regexp should match whole string 2023-02-26 12:08:43 -05:00
Ted Unangst
5e481273df rename checkin to assassination coordinates as benjojo did 2023-02-24 13:42:30 -05:00
Ted Unangst
e9813defe2 genschema isn't used anymore 2023-02-24 13:17:30 -05:00
Ted Unangst
23117ad450 database upgrade to fixup the subject lines 2023-02-24 13:09:45 -05:00
Ted Unangst
108cd4fff3 remove excess logging 2023-02-24 12:59:22 -05:00
Ted Unangst
55ece74faa only markdown precis if we found a new one 2023-02-23 18:32:24 -05:00
Ted Unangst
5eb2821d10 note that some old posts may have two subjects now 2023-02-23 18:26:43 -05:00
Ted Unangst
65face4f35 consistently process precis as html.
only do the markdown conversion once and early.
2023-02-23 18:19:37 -05:00
Ted Unangst
ed13b68989 collapse the danger honks by default when logged out 2023-02-22 15:04:24 -05:00
Ted Unangst
b6efa7d4bd js lint from kuijsten 2023-02-22 14:56:26 -05:00
Ted Unangst
ca9f48106c remove very old dead code which is confusing 2023-02-19 19:45:09 -05:00
Ted Unangst
65a27bf359 cleanup up emoji in hoots 2023-02-19 19:39:13 -05:00
Ted Unangst
4ed4ae3186 seasonal rotation of the reactions 2023-02-19 17:58:27 -05:00
Ted Unangst
398d430e97 wonkles are gone 2023-02-19 17:47:45 -05:00
Ted Unangst
e08c21228c API access for honkers. 2023-02-19 17:46:59 -05:00
Ted Unangst
ad5a149f64 safer to use templates sprintf to build html 2023-02-11 19:56:27 -05:00
Ted Unangst
9b8018ff26 alt text for hoot images 2023-02-11 19:46:29 -05:00
Ted Unangst
6d52b4f492 remove wonk support 2023-02-07 20:35:38 -05:00
Ted Unangst
628fc34ae4 all inclusive danger zone 2023-02-05 20:42:14 -05:00
26 changed files with 336 additions and 467 deletions

View file

@ -36,3 +36,5 @@ dac64bc6a93cedeb6ae618cba8f8647af96d2ece v0.9.3
6a522536238fe25b6d048543f52ed406ccf720b2 v0.9.6 6a522536238fe25b6d048543f52ed406ccf720b2 v0.9.6
bc1bcfb9c0cc86b3c63325b07e13a36b9d4500f0 v0.9.7 bc1bcfb9c0cc86b3c63325b07e13a36b9d4500f0 v0.9.7
916cefdc24363b6e7e193dbde82632c17f58adfd v0.9.8 916cefdc24363b6e7e193dbde82632c17f58adfd v0.9.8
4b8cf31560b7d1e1696af109b158766c4ce823ab v0.9.9
d7c3a01e7aaef67c40920bbc4e8507350fc33e31 v0.9.91

View file

@ -511,7 +511,8 @@ var re_mast0link = regexp.MustCompile(`https://[[:alnum:].]+/users/[[:alnum:]]+/
var re_masto1ink = regexp.MustCompile(`https://[[:alnum:].]+/@[[:alnum:]]+/[[:digit:]]+`) var re_masto1ink = regexp.MustCompile(`https://[[:alnum:].]+/@[[:alnum:]]+/[[:digit:]]+`)
var re_misslink = regexp.MustCompile(`https://[[:alnum:].]+/notes/[[:alnum:]]+`) var re_misslink = regexp.MustCompile(`https://[[:alnum:].]+/notes/[[:alnum:]]+`)
var re_honklink = regexp.MustCompile(`https://[[:alnum:].]+/u/[[:alnum:]]+/h/[[:alnum:]]+`) var re_honklink = regexp.MustCompile(`https://[[:alnum:].]+/u/[[:alnum:]]+/h/[[:alnum:]]+`)
var re_romalink = regexp.MustCompile(`https://[[:alnum:].]+/objects/[[:alnum:]-]+`) var re_r0malink = regexp.MustCompile(`https://[[:alnum:].]+/objects/[[:alnum:]-]+`)
var re_roma1ink = regexp.MustCompile(`https://[[:alnum:].]+/notice/[[:alnum:]]+`)
var re_qtlinks = regexp.MustCompile(`>https://[^\s<]+<`) var re_qtlinks = regexp.MustCompile(`>https://[^\s<]+<`)
func qutify(user *WhatAbout, content string) string { func qutify(user *WhatAbout, content string) string {
@ -521,14 +522,11 @@ func qutify(user *WhatAbout, content string) string {
mlinks := re_qtlinks.FindAllString(malcontent, -1) mlinks := re_qtlinks.FindAllString(malcontent, -1)
for _, m := range mlinks { for _, m := range mlinks {
m = m[1 : len(m)-1] m = m[1 : len(m)-1]
dlog.Printf("consider qt: %s", m) if re_mast0link.MatchString(m) || re_masto1ink.MatchString(m) ||
if re_mast0link.MatchString(m) ||
re_masto1ink.MatchString(m) ||
re_misslink.MatchString(m) || re_misslink.MatchString(m) ||
re_honklink.MatchString(m) || re_honklink.MatchString(m) ||
re_romalink.MatchString(m) { re_r0malink.MatchString(m) || re_roma1ink.MatchString(m) {
j, err := GetJunk(user.ID, m) j, err := GetJunk(user.ID, m)
dlog.Printf("fetched %s: %s", m, err)
if err == nil { if err == nil {
q, ok := j.GetString("content") q, ok := j.GetString("content")
if ok { if ok {
@ -576,6 +574,7 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
var replies []string var replies []string
var obj junk.Junk var obj junk.Junk
waspage := false waspage := false
preferorig := false
switch what { switch what {
case "Delete": case "Delete":
obj, ok = item.GetMap("object") obj, ok = item.GetMap("object")
@ -687,11 +686,10 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
case "Page": case "Page":
waspage = true waspage = true
fallthrough fallthrough
case "GuessWord": // dealt with below
fallthrough
case "Audio": case "Audio":
fallthrough fallthrough
case "Image": case "Image":
preferorig = true
fallthrough fallthrough
case "Video": case "Video":
fallthrough fallthrough
@ -820,12 +818,6 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
targ, _ := obj.GetString("target") targ, _ := obj.GetString("target")
content += string(templates.Sprintf(`<p>Moved to <a href="%s">%s</a>`, targ, targ)) 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 != "" { if what == "honk" && rid != "" {
what = "tonk" what = "tonk"
} }
@ -846,6 +838,9 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
at, _ := att.GetString("type") at, _ := att.GetString("type")
mt, _ := att.GetString("mediaType") mt, _ := att.GetString("mediaType")
u, ok := att.GetString("url") u, ok := att.GetString("url")
if !ok {
u, ok = att.GetString("href")
}
if !ok { if !ok {
if ua, ok := att.GetArray("url"); ok && len(ua) > 0 { if ua, ok := att.GetArray("url"); ok && len(ua) > 0 {
u, ok = ua[0].(string) u, ok = ua[0].(string)
@ -873,7 +868,7 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
localize := false localize := false
if numatts > 4 { if numatts > 4 {
ilog.Printf("excessive attachment: %s", at) ilog.Printf("excessive attachment: %s", at)
} else if at == "Document" || at == "Image" { } else if at == "Document" || at == "Image" || (preferorig && at == "Link") {
mt = strings.ToLower(mt) mt = strings.ToLower(mt)
dlog.Printf("attachment: %s %s", mt, u) dlog.Printf("attachment: %s %s", mt, u)
if mt == "text/plain" || mt == "application/pdf" || if mt == "text/plain" || mt == "application/pdf" ||
@ -886,23 +881,42 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
if skipMedia(&xonk) { if skipMedia(&xonk) {
localize = false localize = false
} }
if preferorig && !localize {
return
}
donk := savedonk(u, name, desc, mt, localize) donk := savedonk(u, name, desc, mt, localize)
if donk != nil { if donk != nil {
xonk.Donks = append(xonk.Donks, donk) xonk.Donks = append(xonk.Donks, donk)
} }
numatts++ numatts++
} }
atts, _ := obj.GetArray("attachment") if preferorig {
for _, atti := range atts { atts, _ := obj.GetArray("url")
att, ok := atti.(junk.Junk) for _, atti := range atts {
if !ok { att, ok := atti.(junk.Junk)
ilog.Printf("attachment that wasn't map?") if !ok {
continue ilog.Printf("attachment that wasn't map?")
continue
}
procatt(att)
}
if numatts == 0 {
preferorig = false
} }
procatt(att)
} }
if att, ok := obj.GetMap("attachment"); ok { if !preferorig {
procatt(att) atts, _ := obj.GetArray("attachment")
for _, atti := range atts {
att, ok := atti.(junk.Junk)
if !ok {
ilog.Printf("attachment that wasn't map?")
continue
}
procatt(att)
}
if att, ok := obj.GetMap("attachment"); ok {
procatt(att)
}
} }
tags, _ := obj.GetArray("tag") tags, _ := obj.GetArray("tag")
for _, tagi := range tags { for _, tagi := range tags {
@ -1181,8 +1195,6 @@ func jonkjonk(user *WhatAbout, h *Honk) (junk.Junk, junk.Junk) {
fallthrough fallthrough
case "event": case "event":
fallthrough fallthrough
case "wonk":
fallthrough
case "honk": case "honk":
j["type"] = "Create" j["type"] = "Create"
jo = junk.New() jo = junk.New()
@ -1190,8 +1202,6 @@ func jonkjonk(user *WhatAbout, h *Honk) (junk.Junk, junk.Junk) {
jo["type"] = "Note" jo["type"] = "Note"
if h.What == "event" { if h.What == "event" {
jo["type"] = "Event" jo["type"] = "Event"
} else if h.What == "wonk" {
jo["type"] = "GuessWord"
} }
if h.What == "update" { if h.What == "update" {
j["type"] = "Update" j["type"] = "Update"
@ -1298,14 +1308,11 @@ func jonkjonk(user *WhatAbout, h *Honk) (junk.Junk, junk.Junk) {
jo["duration"] = "PT" + strings.ToUpper(t.Duration.String()) jo["duration"] = "PT" + strings.ToUpper(t.Duration.String())
} }
} }
if w := h.Wonkles; w != "" {
jo["wordlist"] = w
}
atts := activatedonks(h.Donks) atts := activatedonks(h.Donks)
if len(atts) > 0 { if len(atts) > 0 {
jo["attachment"] = atts jo["attachment"] = atts
} }
jo["summary"] = html.EscapeString(h.Precis) jo["summary"] = h.Precis
jo["content"] = h.Noise jo["content"] = h.Noise
j["object"] = jo j["object"] = jo
case "bonk": case "bonk":
@ -1899,7 +1906,7 @@ func unfollowme(user *WhatAbout, who string, name string, j junk.Junk) {
} }
} }
func followyou(user *WhatAbout, honkerid int64) { func followyou(user *WhatAbout, honkerid int64, sync bool) {
var url, owner string var url, owner string
db := opendatabase() db := opendatabase()
row := db.QueryRow("select xid, owner from honkers where honkerid = ? and userid = ? and flavor in ('unsub', 'peep', 'presub', 'sub')", row := db.QueryRow("select xid, owner from honkers where honkerid = ? and userid = ? and flavor in ('unsub', 'peep', 'presub', 'sub')",
@ -1916,26 +1923,37 @@ func followyou(user *WhatAbout, honkerid int64) {
elog.Printf("error updating honker: %s", err) elog.Printf("error updating honker: %s", err)
return return
} }
go subsub(user, url, owner, folxid) if sync {
subsub(user, url, owner, folxid)
} else {
go subsub(user, url, owner, folxid)
}
} }
func unfollowyou(user *WhatAbout, honkerid int64) { func unfollowyou(user *WhatAbout, honkerid int64, sync bool) {
db := opendatabase() db := opendatabase()
row := db.QueryRow("select xid, owner, folxid from honkers where honkerid = ? and userid = ? and flavor in ('sub')", row := db.QueryRow("select xid, owner, folxid, flavor from honkers where honkerid = ? and userid = ? and flavor in ('unsub', 'peep', 'presub', 'sub')",
honkerid, user.ID) honkerid, user.ID)
var url, owner, folxid string var url, owner, folxid, flavor string
err := row.Scan(&url, &owner, &folxid) err := row.Scan(&url, &owner, &folxid, &flavor)
if err != nil { if err != nil {
elog.Printf("can't get honker xid: %s", err) elog.Printf("can't get honker xid: %s", err)
return return
} }
if flavor == "peep" {
return
}
ilog.Printf("unsubscribing from %s", url) ilog.Printf("unsubscribing from %s", url)
_, err = db.Exec("update honkers set flavor = ? where honkerid = ?", "unsub", honkerid) _, err = db.Exec("update honkers set flavor = ? where honkerid = ?", "unsub", honkerid)
if err != nil { if err != nil {
elog.Printf("error updating honker: %s", err) elog.Printf("error updating honker: %s", err)
return return
} }
go itakeitallback(user, url, owner, folxid) if sync {
itakeitallback(user, url, owner, folxid)
} else {
go itakeitallback(user, url, owner, folxid)
}
} }
func followyou2(user *WhatAbout, j junk.Junk) { func followyou2(user *WhatAbout, j junk.Junk) {

View file

@ -14,54 +14,3 @@
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package main 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
}

View file

@ -485,9 +485,7 @@ func donksforhonks(honks []*Honk) {
continue continue
} }
case "wonkles": case "wonkles":
h.Wonkles = j
case "guesses": case "guesses":
h.Guesses = template.HTML(j)
case "oldrev": case "oldrev":
default: default:
elog.Printf("unknown meta genus: %s", genus) elog.Printf("unknown meta genus: %s", genus)
@ -576,6 +574,20 @@ func savefileandxid(name string, desc string, url string, media string, local bo
return fileid, xid, nil return fileid, xid, nil
} }
func finddonkid(fileid int64, url string) *Donk {
donk := new(Donk)
row := stmtFindFileId.QueryRow(fileid, url)
err := row.Scan(&donk.XID, &donk.Local, &donk.Desc)
if err == nil {
donk.FileID = fileid
return donk
}
if err != sql.ErrNoRows {
elog.Printf("error finding file: %s", err)
}
return nil
}
func finddonk(url string) *Donk { func finddonk(url string) *Donk {
donk := new(Donk) donk := new(Donk)
row := stmtFindFile.QueryRow(url) row := stmtFindFile.QueryRow(url)
@ -897,20 +909,6 @@ func saveextras(tx *sql.Tx, h *Honk) error {
return err 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 return nil
} }
@ -978,7 +976,7 @@ func savexonker(what, value, flav, when string) {
stmtSaveXonker.Exec(what, value, flav, when) stmtSaveXonker.Exec(what, value, flav, when)
} }
func savehonker(user *WhatAbout, url, name, flavor, combos, mj string) error { func savehonker(user *WhatAbout, url, name, flavor, combos, mj string) (int64, error) {
var owner string var owner string
if url[0] == '#' { if url[0] == '#' {
flavor = "peep" flavor = "peep"
@ -990,7 +988,7 @@ func savehonker(user *WhatAbout, url, name, flavor, combos, mj string) error {
info, err := investigate(url) info, err := investigate(url)
if err != nil { if err != nil {
ilog.Printf("failed to investigate honker: %s", err) ilog.Printf("failed to investigate honker: %s", err)
return err return 0, err
} }
url = info.XID url = info.XID
if name == "" { if name == "" {
@ -1009,19 +1007,16 @@ func savehonker(user *WhatAbout, url, name, flavor, combos, mj string) error {
} else { } else {
err = fmt.Errorf("it seems you are already subscribed to them") err = fmt.Errorf("it seems you are already subscribed to them")
} }
return err return 0, err
} }
res, err := stmtSaveHonker.Exec(user.ID, name, url, flavor, combos, owner, mj) res, err := stmtSaveHonker.Exec(user.ID, name, url, flavor, combos, owner, mj)
if err != nil { if err != nil {
elog.Print(err) elog.Print(err)
return err return 0, err
} }
honkerid, _ := res.LastInsertId() honkerid, _ := res.LastInsertId()
if flavor == "presub" { return honkerid, nil
followyou(user, honkerid)
}
return nil
} }
func cleanupdb(arg string) { func cleanupdb(arg string) {
@ -1098,7 +1093,7 @@ var stmtHonksByOntology, stmtHonksForUser, stmtHonksForMe, stmtSaveDub, stmtHonk
var stmtHonksFromLongAgo *sql.Stmt var stmtHonksFromLongAgo *sql.Stmt
var stmtHonksByHonker, stmtSaveHonk, stmtUserByName, stmtUserByNumber *sql.Stmt var stmtHonksByHonker, stmtSaveHonk, stmtUserByName, stmtUserByNumber *sql.Stmt
var stmtEventHonks, stmtOneBonk, stmtFindZonk, stmtFindXonk, stmtSaveDonk *sql.Stmt var stmtEventHonks, stmtOneBonk, stmtFindZonk, stmtFindXonk, stmtSaveDonk *sql.Stmt
var stmtFindFile, stmtGetFileData, stmtSaveFileData, stmtSaveFile *sql.Stmt var stmtFindFile, stmtFindFileId, stmtGetFileData, stmtSaveFileData, stmtSaveFile *sql.Stmt
var stmtCheckFileData *sql.Stmt var stmtCheckFileData *sql.Stmt
var stmtAddDoover, stmtGetDoovers, stmtLoadDoover, stmtZapDoover, stmtOneHonker *sql.Stmt var stmtAddDoover, stmtGetDoovers, stmtLoadDoover, stmtZapDoover, stmtOneHonker *sql.Stmt
var stmtUntagged, stmtDeleteHonk, stmtDeleteDonks, stmtDeleteOnts, stmtSaveZonker *sql.Stmt var stmtUntagged, stmtDeleteHonk, stmtDeleteDonks, stmtDeleteOnts, stmtSaveZonker *sql.Stmt
@ -1168,6 +1163,7 @@ func prepareStatements(db *sql.DB) {
stmtGetFileData = preparetodie(blobdb, "select media, content from filedata where xid = ?") stmtGetFileData = preparetodie(blobdb, "select media, content from filedata where xid = ?")
stmtFindXonk = preparetodie(db, "select honkid from honks where userid = ? and xid = ?") stmtFindXonk = preparetodie(db, "select honkid from honks where userid = ? and xid = ?")
stmtFindFile = preparetodie(db, "select fileid, xid from filemeta where url = ? and local = 1") stmtFindFile = preparetodie(db, "select fileid, xid from filemeta where url = ? and local = 1")
stmtFindFileId = preparetodie(db, "select xid, local, description from filemeta where fileid = ? and url = ? and local = 1")
stmtUserByName = preparetodie(db, "select userid, username, displayname, about, pubkey, seckey, options from users where username = ? and userid > 0") stmtUserByName = preparetodie(db, "select userid, username, displayname, about, pubkey, seckey, options from users where username = ? and userid > 0")
stmtUserByNumber = preparetodie(db, "select userid, username, displayname, about, pubkey, seckey, options from users where userid = ?") stmtUserByNumber = preparetodie(db, "select userid, username, displayname, about, pubkey, seckey, options from users where userid = ?")
stmtSaveDub = preparetodie(db, "insert into honkers (userid, name, xid, flavor, combos, owner, meta, folxid) values (?, ?, ?, ?, '', '', '', ?)") stmtSaveDub = preparetodie(db, "insert into honkers (userid, name, xid, flavor, combos, owner, meta, folxid) values (?, ?, ?, ?, '', '', '', ?)")

View file

@ -54,13 +54,6 @@ activities are ignored.
Limited support. Limited support.
.It Vt Audio .It Vt Audio
Limited Support. 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 .El
.Pp .Pp
Honk primarily supports HTML content, not markdown or other formats, Honk primarily supports HTML content, not markdown or other formats,

View file

@ -1,6 +1,18 @@
changelog changelog
=== next === 0.9.91 One More Time
+ Swallow a follow bug.
=== 0.9.9 Eat the Bugs
+ Some fixes for image descriptions.
+ Fix double htmlization of subject lines.
+ Remove the wonk support. Fun's over, back to work.
+ All inclusive danger zone spoiler alerts.
+ Emu peeker + Emu peeker

View file

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

View file

@ -158,6 +158,27 @@ Delete this honk.
Mute this thread. Mute this thread.
What should identify a convoy. What should identify a convoy.
.El .El
.Ss gethonkers
Returns a list of current honkers in json format.
.Ss savehonker
Save a new honker, or update an existing one.
The following fields are used.
.Bl -tag -width honkerid
.It Fa honkerid
The numeric ID of an existing honker to update.
.It Fa name
The preferred short name.
.It Fa url
The ActivityPub actor url.
.It Fa combos
Space separated list of combos.
.It Fa notes
Some notes.
.El
.Pp
The honker numeric ID will be returned for success.
To delete, unsub, or sub, include a form value with name and value equal.
As in, a form value named delete with the value delete, or unsub=unsub, etc.
.Ss sendactivity .Ss sendactivity
Send anything. Send anything.
No limits, no error checking. No limits, no error checking.

View file

@ -74,7 +74,8 @@ will become a horizontal rule.
.Pp .Pp
If the first line of a honk begins with If the first line of a honk begins with
.Dq DZ: .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 .Pp
Mentioning a specfic user such as Mentioning a specfic user such as
.Pq @user@example.social .Pq @user@example.social
@ -121,7 +122,7 @@ A description, or caption, is encouraged.
Text files and PDFs are also supported as attachments. Text files and PDFs are also supported as attachments.
Other formats are not supported. Other formats are not supported.
.Pp .Pp
One may also check in to a location. One may also live dangerously by posting assassination coordinates.
The available fields, all optional, are The available fields, all optional, are
.Ar name , .Ar name ,
.Ar url , .Ar url ,

View file

@ -142,6 +142,11 @@ command.
Users may be deleted with the Users may be deleted with the
.Ic deluser Ar username .Ic deluser Ar username
command. command.
.Pp
Follow and unfollow requests can be sent via command line with
.Ic follow Ar username Ar url
and
.Ic unfollow Ar username Ar url .
.Ss Maintenance .Ss Maintenance
The database may grow large over time. The database may grow large over time.
The The

24
fun.go
View file

@ -181,9 +181,6 @@ func reverbolate(userid int64, honks []*Honk) {
h.HTPrecis = template.HTML(h.Precis) h.HTPrecis = template.HTML(h.Precis)
h.HTML = template.HTML(h.Noise) h.HTML = template.HTML(h.Noise)
if h.What == "wonked" {
h.HTML = "? wonk ?"
}
if redo := relingo[h.What]; redo != "" { if redo := relingo[h.What]; redo != "" {
h.What = redo h.What = redo
} }
@ -290,12 +287,11 @@ func imaginate(honk *Honk) {
htf.String(honk.Noise) htf.String(honk.Noise)
} }
func translate(honk *Honk) { var re_dangerous = regexp.MustCompile("^[a-zA-Z]{2}:")
if honk.Format == "html" {
return func precipitate(honk *Honk) {
}
noise := honk.Noise noise := honk.Noise
if strings.HasPrefix(noise, "DZ:") { if re_dangerous.MatchString(noise) {
idx := strings.Index(noise, "\n") idx := strings.Index(noise, "\n")
if idx == -1 { if idx == -1 {
honk.Precis = noise honk.Precis = noise
@ -304,8 +300,16 @@ func translate(honk *Honk) {
honk.Precis = noise[:idx] honk.Precis = noise[:idx]
noise = noise[idx+1:] noise = noise[idx+1:]
} }
honk.Precis = markitzero(strings.TrimSpace(honk.Precis))
honk.Noise = noise
} }
honk.Precis = markitzero(strings.TrimSpace(honk.Precis)) }
func translate(honk *Honk) {
if honk.Format == "html" {
return
}
noise := honk.Noise
var marker mz.Marker var marker mz.Marker
marker.HashLinker = ontoreplacer marker.HashLinker = ontoreplacer
@ -421,6 +425,8 @@ func herdofemus(noise string) []Emu {
var re_memes = regexp.MustCompile("meme: ?([^\n]+)") var re_memes = regexp.MustCompile("meme: ?([^\n]+)")
var re_avatar = regexp.MustCompile("avatar: ?([^\n]+)") var re_avatar = regexp.MustCompile("avatar: ?([^\n]+)")
var re_banner = regexp.MustCompile("banner: ?([^\n]+)") var re_banner = regexp.MustCompile("banner: ?([^\n]+)")
var re_convoy = regexp.MustCompile("convoy: ?([^\n]+)")
var re_convalidate = regexp.MustCompile("^(https?|tag|data):")
func memetize(honk *Honk) { func memetize(honk *Honk) {
repl := func(x string) string { repl := func(x string) string {

View file

@ -1,6 +0,0 @@
echo "package main" > schema.go
echo "var sqlSchema = \`" >> schema.go
cat schema.sql >> schema.go
echo "\`" >> schema.go
go fmt schema.go

14
hfcs.go
View file

@ -33,6 +33,7 @@ type Filter struct {
IncludeAudience bool `json:",omitempty"` IncludeAudience bool `json:",omitempty"`
Text string `json:",omitempty"` Text string `json:",omitempty"`
re_text *regexp.Regexp re_text *regexp.Regexp
IsReply bool `json:",omitempty"`
IsAnnounce bool `json:",omitempty"` IsAnnounce bool `json:",omitempty"`
AnnounceOf string `json:",omitempty"` AnnounceOf string `json:",omitempty"`
Reject bool `json:",omitempty"` Reject bool `json:",omitempty"`
@ -306,6 +307,13 @@ func matchfilterX(h *Honk, f *Filter) string {
} }
} }
} }
if match && f.IsReply {
match = false
if h.RID != "" {
match = true
rv += " reply"
}
}
if match && f.IsAnnounce { if match && f.IsAnnounce {
match = false match = false
if (f.AnnounceOf == "" && h.Oonker != "") || f.AnnounceOf == h.Oonker || if (f.AnnounceOf == "" && h.Oonker != "") || f.AnnounceOf == h.Oonker ||
@ -407,6 +415,12 @@ func unsee(honks []*Honk, userid int64) {
h.Open = "" h.Open = ""
} }
} }
} else {
for _, h := range honks {
if h.Precis != "" {
h.Open = ""
}
}
} }
} }

42
honk.go
View file

@ -104,8 +104,6 @@ type Honk struct {
Time *Time Time *Time
Mentions []Mention Mentions []Mention
Badonks []Badonk Badonks []Badonk
Wonkles string
Guesses template.HTML
} }
type Badonk struct { type Badonk struct {
@ -180,10 +178,6 @@ func (honk *Honk) IsReacted() bool {
return honk.Flags&flagIsReacted != 0 return honk.Flags&flagIsReacted != 0
} }
func (honk *Honk) IsWonked() bool {
return honk.Flags&flagIsWonked != 0
}
type Donk struct { type Donk struct {
FileID int64 FileID int64
XID string XID string
@ -379,6 +373,42 @@ func main() {
return return
} }
chpass(args[1]) chpass(args[1])
case "follow":
if len(args) < 3 {
fmt.Printf("usage: honk follow username url\n")
return
}
user, err := butwhatabout(args[1])
if err != nil {
fmt.Printf("user not found\n")
return
}
var meta HonkerMeta
mj, _ := jsonify(&meta)
honkerid, err := savehonker(user, args[2], "", "presub", "", mj)
if err != nil {
fmt.Printf("had some trouble with that: %s\n", err)
return
}
followyou(user, honkerid, true)
case "unfollow":
if len(args) < 3 {
fmt.Printf("usage: honk unfollow username url\n")
return
}
user, err := butwhatabout(args[1])
if err != nil {
fmt.Printf("user not found\n")
return
}
row := db.QueryRow("select honkerid from honkers where xid = ? and userid = ? and flavor in ('sub')", args[2], user.ID)
var honkerid int64
err = row.Scan(&honkerid)
if err != nil {
fmt.Printf("sorry couldn't find them\n")
return
}
unfollowyou(user, honkerid, true)
case "cleanup": case "cleanup":
arg := "30" arg := "30"
if len(args) > 1 { if len(args) > 1 {

53
hoot.go
View file

@ -26,6 +26,7 @@ import (
"github.com/andybalholm/cascadia" "github.com/andybalholm/cascadia"
"golang.org/x/net/html" "golang.org/x/net/html"
"humungus.tedunangst.com/r/webs/htfilter" "humungus.tedunangst.com/r/webs/htfilter"
"humungus.tedunangst.com/r/webs/templates"
) )
var tweetsel = cascadia.MustCompile("div[data-testid=tweetText]") var tweetsel = cascadia.MustCompile("div[data-testid=tweetText]")
@ -54,10 +55,11 @@ func hootextractor(r io.Reader, url string, seen map[string]bool) string {
var htf htfilter.Filter var htf htfilter.Filter
htf.Imager = func(node *html.Node) string { htf.Imager = func(node *html.Node) string {
alt := htfilter.GetAttr(node, "alt") alt := htfilter.GetAttr(node, "alt")
if htfilter.HasClass(node, "Emoji") && alt != "" { src := htfilter.GetAttr(node, "src")
if htfilter.HasClass(node, "Emoji") || strings.HasSuffix(src, ".svg") {
return alt return alt
} }
return fmt.Sprintf(" <img src='%s'>", htfilter.GetAttr(node, "src")) return string(templates.Sprintf(" <img src='%s' alt='%s'>", src, alt))
} }
var buf strings.Builder var buf strings.Builder
@ -65,41 +67,6 @@ func hootextractor(r io.Reader, url string, seen map[string]bool) string {
divs := tweetsel.MatchAll(root) divs := tweetsel.MatchAll(root)
for i, div := range divs { for i, div := range divs {
{
twp := div.Parent.Parent.Parent.Parent.Parent
link := url
alink := linksel.MatchFirst(twp)
if alink == nil {
if i != 0 {
dlog.Printf("missing link")
continue
}
} else {
alink = alink.Parent
link = "https://twitter.com" + htfilter.GetAttr(alink, "href")
}
authormatch := authorregex.FindStringSubmatch(link)
if len(authormatch) < 2 {
dlog.Printf("no author?: %s", link)
continue
}
author := authormatch[1]
if wanted == "" {
wanted = author
}
if author != wanted {
continue
}
for _, img := range imgsel.MatchAll(twp) {
img.Parent.RemoveChild(img)
div.AppendChild(img)
}
text := htf.NodeText(div)
text = strings.Replace(text, "\n", " ", -1)
fmt.Fprintf(&buf, "> @%s: %s\n", author, text)
continue
}
twp := div.Parent.Parent.Parent.Parent.Parent twp := div.Parent.Parent.Parent.Parent.Parent
link := url link := url
alink := linksel.MatchFirst(twp) alink := linksel.MatchFirst(twp)
@ -109,12 +76,9 @@ func hootextractor(r io.Reader, url string, seen map[string]bool) string {
continue continue
} }
} else { } else {
alink = alink.Parent
link = "https://twitter.com" + htfilter.GetAttr(alink, "href") link = "https://twitter.com" + htfilter.GetAttr(alink, "href")
} }
replto := replyingto.MatchFirst(twp)
if replto != nil {
continue
}
authormatch := authorregex.FindStringSubmatch(link) authormatch := authorregex.FindStringSubmatch(link)
if len(authormatch) < 2 { if len(authormatch) < 2 {
dlog.Printf("no author?: %s", link) dlog.Printf("no author?: %s", link)
@ -133,14 +97,7 @@ func hootextractor(r io.Reader, url string, seen map[string]bool) string {
} }
text := htf.NodeText(div) text := htf.NodeText(div)
text = strings.Replace(text, "\n", " ", -1) text = strings.Replace(text, "\n", " ", -1)
text = re_removepics.ReplaceAllString(text, "")
if seen[text] {
continue
}
fmt.Fprintf(&buf, "> @%s: %s\n", author, text) fmt.Fprintf(&buf, "> @%s: %s\n", author, text)
seen[text] = true
} }
return buf.String() return buf.String()
} }

View file

@ -210,7 +210,7 @@ func importMastotooters(user *WhatAbout, source string) {
name := "" name := ""
flavor := "peep" flavor := "peep"
combos := "" combos := ""
err := savehonker(user, url, name, flavor, combos, mj) _, err := savehonker(user, url, name, flavor, combos, mj)
if err != nil { if err != nil {
elog.Printf("trouble with a honker: %s", err) elog.Printf("trouble with a honker: %s", err)
} }

View file

@ -1,5 +1,5 @@
PROGS=autobonker gettoken saytheday sprayandpray wonkawonk youvegothonks PROGS=autobonker gettoken saytheday sprayandpray youvegothonks
all: $(PROGS) all: $(PROGS)
@ -18,8 +18,5 @@ saytheday: saytheday.go
sprayandpray: sprayandpray.go sprayandpray: sprayandpray.go
go build sprayandpray.go go build sprayandpray.go
wonkawonk: wonkawonk.go fetch.go
go build wonkawonk.go fetch.go
youvegothonks: youvegothonks.go youvegothonks: youvegothonks.go
go build youvegothonks.go go build youvegothonks.go

View file

@ -1,79 +0,0 @@
package main
import (
"crypto/rand"
"flag"
"fmt"
"io/ioutil"
"log"
"math/big"
"net/http"
"net/url"
"os"
"strings"
)
var debugMode = false
func honkahonk(server, token, wonk, wonkles string) {
form := make(url.Values)
form.Add("token", token)
form.Add("action", "honk")
form.Add("noise", wonk)
form.Add("wonkles", wonkles)
apiurl := fmt.Sprintf("https://%s/api", server)
req, err := http.NewRequest("POST", apiurl, strings.NewReader(form.Encode()))
if err != nil {
log.Fatal(err)
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
client := http.DefaultClient
if debugMode {
client = debugClient
}
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
answer, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
if resp.StatusCode != 200 {
log.Fatalf("status: %d: %s", resp.StatusCode, answer)
}
}
func main() {
server := ""
token := ""
wonkles := ""
flag.StringVar(&server, "server", server, "server to connnect")
flag.StringVar(&token, "token", token, "auth token to use")
flag.StringVar(&wonkles, "wonkles", wonkles, "wordlist to use")
flag.BoolVar(&debugMode, "debug", debugMode, "debug mode")
flag.Parse()
if server == "" || token == "" || wonkles == "" {
flag.Usage()
os.Exit(1)
}
wordlist, err := fetchsome(wonkles)
if err != nil {
log.Printf("error fetching wonkles: %s", err)
}
var words []string
for _, w := range strings.Split(string(wordlist), "\n") {
words = append(words, w)
}
max := big.NewInt(int64(len(words)))
i, _ := rand.Int(rand.Reader, max)
wonk := words[i.Int64()]
log.Printf("picking: %s", wonk)
honkahonk(server, token, wonk, wonkles)
}

View file

@ -22,7 +22,7 @@ import (
"time" "time"
) )
var myVersion = 41 var myVersion = 42
type dbexecer interface { type dbexecer interface {
Exec(query string, args ...interface{}) (sql.Result, error) Exec(query string, args ...interface{}) (sql.Result, error)
@ -206,6 +206,38 @@ func upgradedb() {
doordie(db, "update config set value = 41 where key = 'dbversion'") doordie(db, "update config set value = 41 where key = 'dbversion'")
fallthrough fallthrough
case 41: case 41:
tx, err := db.Begin()
if err != nil {
elog.Fatal(err)
}
rows, err := tx.Query("select honkid, noise from honks where format = 'markdown' and precis <> ''")
if err != nil {
elog.Fatal(err)
}
m := make(map[int64]string)
var dummy Honk
for rows.Next() {
err = rows.Scan(&dummy.ID, &dummy.Noise)
if err != nil {
elog.Fatal(err)
}
precipitate(&dummy)
m[dummy.ID] = dummy.Noise
}
rows.Close()
for id, noise := range m {
_, err = tx.Exec("update honks set noise = ? where honkid = ?", noise, id)
if err != nil {
elog.Fatal(err)
}
}
err = tx.Commit()
if err != nil {
elog.Fatal(err)
}
doordie(db, "update config set value = 42 where key = 'dbversion'")
fallthrough
case 42:
default: default:
elog.Fatalf("can't upgrade unknown version %d", dbversion) elog.Fatalf("can't upgrade unknown version %d", dbversion)

View file

@ -21,20 +21,14 @@
<p><label class="button" for="reaction">reaction:</label> <p><label class="button" for="reaction">reaction:</label>
<select tabindex=1 name="reaction"> <select tabindex=1 name="reaction">
<option {{ and (eq .User.Options.Reaction "none") "selected" }}>none</option> <option {{ and (eq .User.Options.Reaction "none") "selected" }}>none</option>
<option {{ and (eq .User.Options.Reaction "\U0001F61E") "selected" }}>{{ "\U0001F61E" }}</option> <option {{ and (eq .User.Options.Reaction "\U0001F596") "selected" }}>{{ "\U0001F596" }}</option>
<option {{ and (eq .User.Options.Reaction "\U0001F937") "selected" }}>{{ "\U0001F937" }}</option>
<option {{ and (eq .User.Options.Reaction "\U0001F648") "selected" }}>{{ "\U0001F648" }}</option> <option {{ and (eq .User.Options.Reaction "\U0001F648") "selected" }}>{{ "\U0001F648" }}</option>
<option {{ and (eq .User.Options.Reaction "\U0001F9BE") "selected" }}>{{ "\U0001F9BE" }}</option> <option {{ and (eq .User.Options.Reaction "\U0001F9BE") "selected" }}>{{ "\U0001F9BE" }}</option>
<option {{ and (eq .User.Options.Reaction "\U0001F346") "selected" }}>{{ "\U0001F346" }}</option>
<option {{ and (eq .User.Options.Reaction "\U0001F351") "selected" }}>{{ "\U0001F351" }}</option>
<option {{ and (eq .User.Options.Reaction "\U0001F32E") "selected" }}>{{ "\U0001F32E" }}</option>
<option {{ and (eq .User.Options.Reaction "\U0001F951") "selected" }}>{{ "\U0001F951" }}</option>
<option {{ and (eq .User.Options.Reaction "\U0001F5FF") "selected" }}>{{ "\U0001F5FF" }}</option> <option {{ and (eq .User.Options.Reaction "\U0001F5FF") "selected" }}>{{ "\U0001F5FF" }}</option>
<option {{ and (eq .User.Options.Reaction "\U0001F99A") "selected" }}>{{ "\U0001F99A" }}</option> <option {{ and (eq .User.Options.Reaction "\U0001F99A") "selected" }}>{{ "\U0001F99A" }}</option>
<option {{ and (eq .User.Options.Reaction "\U0001F3BB") "selected" }}>{{ "\U0001F3BB" }}</option> <option {{ and (eq .User.Options.Reaction "\U0001F3BB") "selected" }}>{{ "\U0001F3BB" }}</option>
<option {{ and (eq .User.Options.Reaction "\U0001FA93") "selected" }}>{{ "\U0001FA93" }}</option> <option {{ and (eq .User.Options.Reaction "\U0001FA93") "selected" }}>{{ "\U0001FA93" }}</option>
<option {{ and (eq .User.Options.Reaction "\U0001F1EB") "selected" }}>{{ "\U0001F1EB" }}</option> <option {{ and (eq .User.Options.Reaction "\U0001F9EF") "selected" }}>{{ "\U0001F9EF" }}</option>
<option {{ and (eq .User.Options.Reaction "\U0001F1FD") "selected" }}>{{ "\U0001F1FD" }}</option>
</select> </select>
<p><button>update settings</button> <p><button>update settings</button>
</form> </form>

View file

@ -20,6 +20,8 @@ Honk Filtering and Censorship System
<input tabindex=1 type="checkbox" id="incaud" name="incaud" value="yes"><span></span></label></span> <input tabindex=1 type="checkbox" id="incaud" name="incaud" value="yes"><span></span></label></span>
<p><label for="filttext">text matches:</label><br> <p><label for="filttext">text matches:</label><br>
<input tabindex=1 type="text" name="filttext" value="" autocomplete=off> <input tabindex=1 type="text" name="filttext" value="" autocomplete=off>
<p><span><label class=button for="isreply">is reply:
<input tabindex=1 type="checkbox" id="isreply" name="isreply" value="yes"><span></span></label></span>
<p><span><label class=button for="isannounce">is announce: <p><span><label class=button for="isannounce">is announce:
<input tabindex=1 type="checkbox" id="isannounce" name="isannounce" value="yes"><span></span></label></span> <input tabindex=1 type="checkbox" id="isannounce" name="isannounce" value="yes"><span></span></label></span>
<p><label for="announceof">announce of:</label><br> <p><label for="announceof">announce of:</label><br>
@ -54,6 +56,7 @@ Honk Filtering and Censorship System
{{ with .Notes }}<p>Notes: {{ . }}{{ end }} {{ with .Notes }}<p>Notes: {{ . }}{{ end }}
<p>Date: {{ .Date.Format "2006-01-02" }} <p>Date: {{ .Date.Format "2006-01-02" }}
{{ with .Actor }}<p>Who: {{ . }}{{ end }} {{ with .IncludeAudience }} (inclusive) {{ end }} {{ with .Actor }}<p>Who: {{ . }}{{ end }} {{ with .IncludeAudience }} (inclusive) {{ end }}
{{ if .IsReply }}<p>Reply: y{{ end }}
{{ if .IsAnnounce }}<p>Announce: {{ .AnnounceOf }}{{ end }} {{ if .IsAnnounce }}<p>Announce: {{ .AnnounceOf }}{{ end }}
{{ with .Text }}<p>Text: {{ . }}{{ end }} {{ with .Text }}<p>Text: {{ . }}{{ end }}
<p>Actions: {{ range .Actions }} {{ . }} {{ end }} <p>Actions: {{ range .Actions }} {{ . }} {{ end }}

View file

@ -52,7 +52,7 @@ in reply to: <a href="{{ .RID }}" rel=noreferrer>{{ .RID }}</a>
</header> </header>
<p> <p>
<details class="noise" {{ .Open }} > <details class="noise" {{ .Open }} >
<summary>{{ .HTPrecis }}</summary> <summary>{{ .HTPrecis }}<p></summary>
<p>{{ .HTPrecis }} <p>{{ .HTPrecis }}
<p class="content">{{ .HTML }} <p class="content">{{ .HTML }}
{{ with .Time }} {{ with .Time }}
@ -89,15 +89,6 @@ in reply to: <a href="{{ .RID }}" rel=noreferrer>{{ .RID }}</a>
{{ end }} {{ end }}
</details> </details>
{{ end }} {{ end }}
{{ if eq .Honk.What "wonked" }}
<p>
{{ if and $bonkcsrf .Honk.IsWonked }}
{{ .Honk.Guesses }}
<p>{{ .Honk.Noise }}
{{ else }}
<button class="playit" data-noise="{{ .Honk.Noise }}" data-wonk="{{ .Honk.Wonkles }}">it's play time!</button>
{{ end }}
{{ end }}
{{ if and $bonkcsrf (not $IsPreview) }} {{ if and $bonkcsrf (not $IsPreview) }}
<p> <p>
<details class="actions"> <details class="actions">

View file

@ -14,7 +14,7 @@
<p id="donkdescriptor"><label for=donkdesc>description:</label><br> <p id="donkdescriptor"><label for=donkdesc>description:</label><br>
<input type="text" name="donkdesc" value="{{ .DonkDesc }}" autocomplete=off> <input type="text" name="donkdesc" value="{{ .DonkDesc }}" autocomplete=off>
{{ with .SavedPlace }} {{ with .SavedPlace }}
<p><button id=checkinbutton type=button>checkin</button> <p><button id=checkinbutton type=button>assassination coordinates</button>
<div id=placedescriptor> <div id=placedescriptor>
<p><label>name:</label><br><input type="text" name="placename" id=placenameinput value="{{ .Name }}"> <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 }}"> <p><label>url:</label><br><input type="text" name="placeurl" id=placeurlinput value="{{ .Url }}">
@ -22,7 +22,7 @@
<label>lon: </label><input type="text" size=9 name="placelong" id=placelonginput value="{{ .Longitude }}"> <label>lon: </label><input type="text" size=9 name="placelong" id=placelonginput value="{{ .Longitude }}">
</div> </div>
{{ else }} {{ else }}
<p><button id=checkinbutton type=button>checkin</button> <p><button id=checkinbutton type=button>assassination coordinates</button>
<div id=placedescriptor class="hide"> <div id=placedescriptor class="hide">
<p><label>name:</label><br><input type="text" name="placename" id=placenameinput value=""> <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>url:</label><br><input type="text" name="placeurl" id=placeurlinput value="">

View file

@ -139,7 +139,6 @@ function fillinhonks(xhr, glowit) {
} else { } else {
holder.append(h) holder.append(h)
} }
} }
relinklinks() relinklinks()
return lenhonks return lenhonks
@ -212,7 +211,7 @@ function switchtopage(name, arg) {
curpagestate.name = name curpagestate.name = name
curpagestate.arg = arg curpagestate.arg = arg
// get the holder for the target page // get the holder for the target page
var stash = name + ":" + arg stash = name + ":" + arg
holder = honksforpage[stash] holder = honksforpage[stash]
if (holder) { if (holder) {
honksonpage.prepend(holder) honksonpage.prepend(holder)
@ -226,7 +225,7 @@ function switchtopage(name, arg) {
var args = hydrargs() var args = hydrargs()
get("/hydra?" + encode(args), function(xhr) { get("/hydra?" + encode(args), function(xhr) {
if (xhr.status == 200) { if (xhr.status == 200) {
var lenhonks = fillinhonks(xhr, false) fillinhonks(xhr, false)
} else { } else {
refreshupdate(" status: " + xhr.status) refreshupdate(" status: " + xhr.status)
} }
@ -337,12 +336,6 @@ function relinklinks() {
el.onclick = function() { el.onclick = function() {
flogit(el, "react", xid); flogit(el, "react", xid);
} }
} else if (el.classList.contains("playit")) {
var noise = el.dataset.noise
var wonk = el.dataset.wonk
el.onclick = function() {
playit(el, noise, wonk, xid)
}
} }
}) })
} }
@ -358,6 +351,7 @@ function showhonkform(elem, rid, hname) {
elem.insertAdjacentElement('afterend', form) elem.insertAdjacentElement('afterend', form)
} }
var ridinput = document.getElementById("ridinput") var ridinput = document.getElementById("ridinput")
var honknoise = document.getElementById("honknoise")
if (rid) { if (rid) {
ridinput.value = rid ridinput.value = rid
if (hname) { if (hname) {
@ -371,7 +365,7 @@ function showhonkform(elem, rid, hname) {
} }
var updateinput = document.getElementById("updatexidinput") var updateinput = document.getElementById("updatexidinput")
updateinput.value = "" updateinput.value = ""
document.getElementById("honknoise").focus() honknoise.focus()
return false return false
} }
function cancelhonking() { function cancelhonking() {
@ -393,9 +387,9 @@ function hideelement(el) {
function updatedonker() { function updatedonker() {
var el = document.getElementById("donker") var el = document.getElementById("donker")
el.children[1].textContent = el.children[0].value.slice(-20) el.children[1].textContent = el.children[0].value.slice(-20)
var el = document.getElementById("donkdescriptor") el = document.getElementById("donkdescriptor")
el.style.display = "" el.style.display = ""
var el = document.getElementById("saveddonkxid") el = document.getElementById("saveddonkxid")
el.value = "" el.value = ""
} }
var checkinprec = 100.0 var checkinprec = 100.0
@ -417,25 +411,19 @@ function fillcheckin() {
gpsoptions.timeout = 2000 gpsoptions.timeout = 2000
}, function(err) { }, function(err) {
showelement("placedescriptor") showelement("placedescriptor")
el = document.getElementById("placenameinput") var el = document.getElementById("placenameinput")
el.value = err.message el.value = err.message
}, gpsoptions) }, gpsoptions)
} }
} }
function playit(elem, word, wordlist, xid) {
import('/wonk.js').then(module => {
makeaguess = module.makeaguess
module.addguesscontrols(elem, word, wordlist, xid)
})
}
function addemu(elem) { function addemu(elem) {
const data = elem.alt const data = elem.alt
const box = document.getElementById("honknoise"); const box = document.getElementById("honknoise");
box.value += data; box.value += data;
} }
function loademus() { function loademus() {
div = document.getElementById("emupicker") var div = document.getElementById("emupicker")
request = new XMLHttpRequest() var request = new XMLHttpRequest()
request.open('GET', '/emus') request.open('GET', '/emus')
request.onload = function() { request.onload = function() {
div.innerHTML = request.responseText div.innerHTML = request.responseText

View file

@ -1,89 +0,0 @@
export function addguesscontrols(elem, word, wordlist, xid) {
var host = elem.parentElement
elem.innerHTML = "loading..."
host.correctAnswer = word
host.guesses = []
host.xid = xid
var xhr = new XMLHttpRequest()
xhr.open("GET", "/bloat/wonkles?w=" + encodeURIComponent(wordlist))
xhr.responseType = "json"
xhr.onload = function() {
var wordlist = xhr.response.wordlist
var validguesses = {}
console.log("valid " + wordlist.length)
for (var i = 0; i < wordlist.length; i++) {
validguesses[wordlist[i]] = true
}
host.validGuesses = validguesses
var div = document.createElement( 'div' );
div.innerHTML = "<p><input> <button>guess</button>"
div.querySelector('button').onclick = function() {
makeaguess(this)
}
host.append(div)
elem.remove()
}
xhr.send()
}
export function makeaguess(btn) {
var host = btn.parentElement.parentElement.parentElement
var correct = host.correctAnswer
var valid = host.validGuesses
var inp = btn.previousElementSibling
var g = inp.value.toLowerCase()
var res = ""
if (valid[g]) {
var letters = {}
var obfu = ""
for (var i = 0; i < correct.length; i++) {
var l = correct[i]
letters[l] = (letters[l] | 0) + 1
}
for (var i = 0; i < g.length && i < correct.length; i++) {
if (g[i] == correct[i]) {
letters[g[i]] = letters[g[i]] - 1
}
}
for (var i = 0; i < g.length; i++) {
if (i < correct.length && g[i] == correct[i]) {
res += g[i].toUpperCase()
obfu += "&#129001;"
} else if (letters[g[i]] > 0) {
res += g[i]
obfu += "&#129000;"
letters[g[i]] = letters[g[i]] - 1
} else {
obfu += "&#11035;"
res += "."
}
}
var div = document.createElement( 'div' );
div.innerHTML = "<p class='fontmonospace'>" + res
host.append(div)
host.guesses.push(obfu)
} else {
var div = document.createElement( 'div' );
div.innerHTML = "<p> invalid guess"
host.append(div)
}
var div = document.createElement( 'div' );
if (res == correct.toUpperCase()) {
var mess = "<p>you are very smart!"
mess += "<p>" + host.xid
for (var i = 0; i < host.guesses.length; i++) {
mess += "<p>" + host.guesses[i]
}
div.innerHTML = mess
if (typeof(csrftoken) != "undefined")
post("/zonkit", encode({"CSRF": csrftoken, "wherefore": "wonk", "guesses": host.guesses.join("<p>"), "what": host.xid}))
} else {
div.innerHTML = "<p><input> <button>guess</button>"
div.querySelector('button').onclick = function() {
makeaguess(this)
}
}
host.append(div)
btn.parentElement.remove()
}

148
web.go
View file

@ -381,8 +381,6 @@ func inbox(w http.ResponseWriter, r *http.Request) {
ilog.Printf("inbox message failed signature for %s from %s: %s", keyname, r.Header.Get("X-Forwarded-For"), err) ilog.Printf("inbox message failed signature for %s from %s: %s", keyname, r.Header.Get("X-Forwarded-For"), err)
if keyname != "" { if keyname != "" {
ilog.Printf("bad signature from %s", keyname) ilog.Printf("bad signature from %s", keyname)
ilog.Writer().Write(payload)
ilog.Writer().Write([]byte{'\n'})
} }
http.Error(w, "what did you call me?", http.StatusTeapot) http.Error(w, "what did you call me?", http.StatusTeapot)
return return
@ -421,13 +419,9 @@ func inbox(w http.ResponseWriter, r *http.Request) {
return return
case "Question": case "Question":
return return
case "Note":
go xonksaver(user, j, origin)
return
} }
} }
ilog.Printf("unknown Update activity") go xonksaver(user, j, origin)
dumpactivity(j)
case "Undo": case "Undo":
obj, ok := j.GetMap("object") obj, ok := j.GetMap("object")
if !ok { if !ok {
@ -487,8 +481,6 @@ func serverinbox(w http.ResponseWriter, r *http.Request) {
ilog.Printf("inbox message failed signature for %s from %s: %s", keyname, r.Header.Get("X-Forwarded-For"), err) ilog.Printf("inbox message failed signature for %s from %s: %s", keyname, r.Header.Get("X-Forwarded-For"), err)
if keyname != "" { if keyname != "" {
ilog.Printf("bad signature from %s", keyname) ilog.Printf("bad signature from %s", keyname)
ilog.Writer().Write(payload)
ilog.Writer().Write([]byte{'\n'})
} }
http.Error(w, "what did you call me?", http.StatusTeapot) http.Error(w, "what did you call me?", http.StatusTeapot)
return return
@ -1330,21 +1322,6 @@ func zonkit(w http.ResponseWriter, r *http.Request) {
return return
} }
if wherefore == "wonk" {
xonk := getxonk(userinfo.UserID, what)
if xonk != nil {
_, err := stmtUpdateFlags.Exec(flagIsWonked, xonk.ID)
if err == nil {
guesses := r.FormValue("guesses")
_, err = stmtSaveMeta.Exec(xonk.ID, "guesses", guesses)
}
if err != nil {
elog.Printf("error saving: %s", err)
}
}
return
}
// my hammer is too big, oh well // my hammer is too big, oh well
defer oldjonks.Flush() defer oldjonks.Flush()
@ -1578,7 +1555,7 @@ func submitdonk(w http.ResponseWriter, r *http.Request) (*Donk, error) {
return d, nil return d, nil
} }
func submitwebhonk(w http.ResponseWriter, r *http.Request) { func websubmithonk(w http.ResponseWriter, r *http.Request) {
h := submithonk(w, r) h := submithonk(w, r)
if h == nil { if h == nil {
return return
@ -1620,10 +1597,6 @@ func submithonk(w http.ResponseWriter, r *http.Request) *Honk {
if rid != "" { if rid != "" {
what = "tonk" what = "tonk"
} }
wonkles := r.FormValue("wonkles")
if wonkles != "" {
what = "wonk"
}
honk = &Honk{ honk = &Honk{
UserID: userinfo.UserID, UserID: userinfo.UserID,
Username: userinfo.Username, Username: userinfo.Username,
@ -1632,17 +1605,28 @@ func submithonk(w http.ResponseWriter, r *http.Request) *Honk {
XID: xid, XID: xid,
Date: dt, Date: dt,
Format: format, Format: format,
Wonkles: wonkles,
} }
} }
var convoy string
noise = strings.Replace(noise, "\r", "", -1) noise = strings.Replace(noise, "\r", "", -1)
if updatexid == "" && rid == "" {
noise = re_convoy.ReplaceAllStringFunc(noise, func(m string) string {
convoy = m[7:]
convoy = strings.TrimSpace(convoy)
if !re_convalidate.MatchString(convoy) {
convoy = ""
}
return ""
})
}
noise = quickrename(noise, userinfo.UserID) noise = quickrename(noise, userinfo.UserID)
noise = hooterize(noise) noise = hooterize(noise)
honk.Noise = noise honk.Noise = noise
precipitate(honk)
noise = honk.Noise
translate(honk) translate(honk)
var convoy string
if rid != "" { if rid != "" {
xonk := getxonk(userinfo.UserID, rid) xonk := getxonk(userinfo.UserID, rid)
if xonk == nil { if xonk == nil {
@ -1662,7 +1646,7 @@ func submithonk(w http.ResponseWriter, r *http.Request) *Honk {
honk.RID = rid honk.RID = rid
if xonk.Precis != "" && honk.Precis == "" { if xonk.Precis != "" && honk.Precis == "" {
honk.Precis = xonk.Precis honk.Precis = xonk.Precis
if !(strings.HasPrefix(honk.Precis, "DZ:") || strings.HasPrefix(honk.Precis, "re: re: re: ")) { if !re_dangerous.MatchString(honk.Precis) {
honk.Precis = "re: " + honk.Precis honk.Precis = "re: " + honk.Precis
} }
} }
@ -1696,12 +1680,19 @@ func submithonk(w http.ResponseWriter, r *http.Request) *Honk {
} }
if d != nil { if d != nil {
honk.Donks = append(honk.Donks, d) honk.Donks = append(honk.Donks, d)
donkxid = d.XID donkxid = fmt.Sprintf("%s:%d", d.XID, d.FileID)
} }
} else { } else {
xid := donkxid p := strings.Split(donkxid, ":")
xid := p[0]
url := fmt.Sprintf("https://%s/d/%s", serverName, xid) url := fmt.Sprintf("https://%s/d/%s", serverName, xid)
donk := finddonk(url) 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 { if donk != nil {
honk.Donks = append(honk.Donks, donk) honk.Donks = append(honk.Donks, donk)
} else { } else {
@ -1904,7 +1895,15 @@ func showcombos(w http.ResponseWriter, r *http.Request) {
} }
} }
func submithonker(w http.ResponseWriter, r *http.Request) { func websubmithonker(w http.ResponseWriter, r *http.Request) {
h := submithonker(w, r)
if h == nil {
return
}
http.Redirect(w, r, "/honkers", http.StatusSeeOther)
}
func submithonker(w http.ResponseWriter, r *http.Request) *Honker {
u := login.GetUserInfo(r) u := login.GetUserInfo(r)
user, _ := butwhatabout(u.Username) user, _ := butwhatabout(u.Username)
name := strings.TrimSpace(r.FormValue("name")) name := strings.TrimSpace(r.FormValue("name"))
@ -1914,10 +1913,10 @@ func submithonker(w http.ResponseWriter, r *http.Request) {
combos = " " + combos + " " combos = " " + combos + " "
honkerid, _ := strconv.ParseInt(r.FormValue("honkerid"), 10, 0) honkerid, _ := strconv.ParseInt(r.FormValue("honkerid"), 10, 0)
re_namecheck := regexp.MustCompile("[\\pL[:digit:]_.-]+") re_namecheck := regexp.MustCompile("^[\\pL[:digit:]_.-]+$")
if name != "" && !re_namecheck.MatchString(name) { if name != "" && !re_namecheck.MatchString(name) {
http.Error(w, "please use a plainer name", http.StatusInternalServerError) http.Error(w, "please use a plainer name", http.StatusInternalServerError)
return return nil
} }
var meta HonkerMeta var meta HonkerMeta
@ -1926,31 +1925,34 @@ func submithonker(w http.ResponseWriter, r *http.Request) {
defer honkerinvalidator.Clear(u.UserID) defer honkerinvalidator.Clear(u.UserID)
// mostly dummy, fill in later...
h := &Honker{
ID: honkerid,
}
if honkerid > 0 { if honkerid > 0 {
if r.FormValue("delete") == "delete" { if r.FormValue("delete") == "delete" {
unfollowyou(user, honkerid) unfollowyou(user, honkerid, false)
stmtDeleteHonker.Exec(honkerid) stmtDeleteHonker.Exec(honkerid)
http.Redirect(w, r, "/honkers", http.StatusSeeOther) return h
return
} }
if r.FormValue("unsub") == "unsub" { if r.FormValue("unsub") == "unsub" {
unfollowyou(user, honkerid) unfollowyou(user, honkerid, false)
} }
if r.FormValue("sub") == "sub" { if r.FormValue("sub") == "sub" {
followyou(user, honkerid) followyou(user, honkerid, false)
} }
_, err := stmtUpdateHonker.Exec(name, combos, mj, honkerid, u.UserID) _, err := stmtUpdateHonker.Exec(name, combos, mj, honkerid, u.UserID)
if err != nil { if err != nil {
elog.Printf("update honker err: %s", err) elog.Printf("update honker err: %s", err)
return return nil
} }
http.Redirect(w, r, "/honkers", http.StatusSeeOther) return h
return
} }
if url == "" { if url == "" {
http.Error(w, "subscribing to nothing?", http.StatusInternalServerError) http.Error(w, "subscribing to nothing?", http.StatusInternalServerError)
return return nil
} }
flavor := "presub" flavor := "presub"
@ -1958,13 +1960,17 @@ func submithonker(w http.ResponseWriter, r *http.Request) {
flavor = "peep" flavor = "peep"
} }
err := savehonker(user, url, name, flavor, combos, mj) var err error
honkerid, err = savehonker(user, url, name, flavor, combos, mj)
if err != nil { if err != nil {
http.Error(w, "had some trouble with that: "+err.Error(), http.StatusInternalServerError) http.Error(w, "had some trouble with that: "+err.Error(), http.StatusInternalServerError)
return return nil
} }
if flavor == "presub" {
http.Redirect(w, r, "/honkers", http.StatusSeeOther) followyou(user, honkerid, false)
}
h.ID = honkerid
return h
} }
func hfcspage(w http.ResponseWriter, r *http.Request) { func hfcspage(w http.ResponseWriter, r *http.Request) {
@ -2001,6 +2007,7 @@ func savehfcs(w http.ResponseWriter, r *http.Request) {
filt.Actor = strings.TrimSpace(r.FormValue("actor")) filt.Actor = strings.TrimSpace(r.FormValue("actor"))
filt.IncludeAudience = r.FormValue("incaud") == "yes" filt.IncludeAudience = r.FormValue("incaud") == "yes"
filt.Text = strings.TrimSpace(r.FormValue("filttext")) filt.Text = strings.TrimSpace(r.FormValue("filttext"))
filt.IsReply = r.FormValue("isreply") == "yes"
filt.IsAnnounce = r.FormValue("isannounce") == "yes" filt.IsAnnounce = r.FormValue("isannounce") == "yes"
filt.AnnounceOf = strings.TrimSpace(r.FormValue("announceof")) filt.AnnounceOf = strings.TrimSpace(r.FormValue("announceof"))
filt.Reject = r.FormValue("doreject") == "yes" filt.Reject = r.FormValue("doreject") == "yes"
@ -2116,11 +2123,29 @@ func somedays() string {
return fmt.Sprintf("%d", secs) return fmt.Sprintf("%d", secs)
} }
func lookatme(ava string) string {
if strings.Contains(ava, serverName+"/"+userSep) {
idx := strings.LastIndexByte(ava, '/')
if idx < len(ava) {
name := ava[idx+1:]
user, _ := butwhatabout(name)
if user != nil && user.URL == ava {
return user.Options.Avatar
}
}
}
return ""
}
func avatate(w http.ResponseWriter, r *http.Request) { func avatate(w http.ResponseWriter, r *http.Request) {
if develMode { if develMode {
loadAvatarColors() loadAvatarColors()
} }
n := r.FormValue("a") n := r.FormValue("a")
if redir := lookatme(n); redir != "" {
http.Redirect(w, r, redir, http.StatusSeeOther)
return
}
a := genAvatar(n) a := genAvatar(n)
if !develMode { if !develMode {
w.Header().Set("Cache-Control", "max-age="+somedays()) w.Header().Set("Cache-Control", "max-age="+somedays())
@ -2331,7 +2356,7 @@ func apihandler(w http.ResponseWriter, r *http.Request) {
if h == nil { if h == nil {
return return
} }
w.Write([]byte(h.XID)) fmt.Fprintf(w, "%s", h.XID)
case "donk": case "donk":
d, err := submitdonk(w, r) d, err := submitdonk(w, r)
if err != nil { if err != nil {
@ -2391,6 +2416,16 @@ func apihandler(w http.ResponseWriter, r *http.Request) {
for rcpt := range rcpts { for rcpt := range rcpts {
go deliverate(0, userid, rcpt, msg, true) go deliverate(0, userid, rcpt, msg, true)
} }
case "gethonkers":
j := junk.New()
j["honkers"] = gethonkers(u.UserID)
j.Write(w)
case "savehonker":
h := submithonker(w, r)
if h == nil {
return
}
fmt.Fprintf(w, "%d", h.ID)
default: default:
http.Error(w, "unknown action", http.StatusNotFound) http.Error(w, "unknown action", http.StatusNotFound)
return return
@ -2433,7 +2468,7 @@ func bgmonitor() {
func addcspheaders(next http.Handler) http.Handler { func addcspheaders(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Security-Policy", "default-src 'none'; script-src 'self'; connect-src 'self'; style-src 'self'; img-src 'self'; report-uri /csp-violation") w.Header().Set("Content-Security-Policy", "default-src 'none'; script-src 'self'; connect-src 'self'; style-src 'self'; img-src 'self'; media-src 'self'; report-uri /csp-violation")
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
}) })
} }
@ -2553,7 +2588,6 @@ func serve() {
getters.HandleFunc("/style.css", serveviewasset) getters.HandleFunc("/style.css", serveviewasset)
getters.HandleFunc("/honkpage.js", serveviewasset) getters.HandleFunc("/honkpage.js", serveviewasset)
getters.HandleFunc("/wonk.js", serveviewasset)
getters.HandleFunc("/misc.js", serveviewasset) getters.HandleFunc("/misc.js", serveviewasset)
getters.HandleFunc("/local.css", servedataasset) getters.HandleFunc("/local.css", servedataasset)
getters.HandleFunc("/local.js", servedataasset) getters.HandleFunc("/local.js", servedataasset)
@ -2566,8 +2600,6 @@ func serve() {
getters.HandleFunc("/logout", login.LogoutFunc) getters.HandleFunc("/logout", login.LogoutFunc)
getters.HandleFunc("/help/{name:[\\pL[:digit:]_.-]+}", servehelp) getters.HandleFunc("/help/{name:[\\pL[:digit:]_.-]+}", servehelp)
getters.HandleFunc("/bloat/wonkles", servewonkles)
loggedin := mux.NewRoute().Subrouter() loggedin := mux.NewRoute().Subrouter()
loggedin.Use(login.Required) loggedin.Use(login.Required)
loggedin.HandleFunc("/first", homepage) loggedin.HandleFunc("/first", homepage)
@ -2583,7 +2615,7 @@ func serve() {
loggedin.HandleFunc("/xzone", xzone) loggedin.HandleFunc("/xzone", xzone)
loggedin.HandleFunc("/newhonk", newhonkpage) loggedin.HandleFunc("/newhonk", newhonkpage)
loggedin.HandleFunc("/edit", edithonkpage) loggedin.HandleFunc("/edit", edithonkpage)
loggedin.Handle("/honk", login.CSRFWrap("honkhonk", http.HandlerFunc(submitwebhonk))) loggedin.Handle("/honk", login.CSRFWrap("honkhonk", http.HandlerFunc(websubmithonk)))
loggedin.Handle("/bonk", login.CSRFWrap("honkhonk", http.HandlerFunc(submitbonk))) loggedin.Handle("/bonk", login.CSRFWrap("honkhonk", http.HandlerFunc(submitbonk)))
loggedin.Handle("/zonkit", login.CSRFWrap("honkhonk", http.HandlerFunc(zonkit))) loggedin.Handle("/zonkit", login.CSRFWrap("honkhonk", http.HandlerFunc(zonkit)))
loggedin.Handle("/savehfcs", login.CSRFWrap("filter", http.HandlerFunc(savehfcs))) loggedin.Handle("/savehfcs", login.CSRFWrap("filter", http.HandlerFunc(savehfcs)))
@ -2598,7 +2630,7 @@ func serve() {
loggedin.HandleFunc("/q", showsearch) loggedin.HandleFunc("/q", showsearch)
loggedin.HandleFunc("/hydra", webhydra) loggedin.HandleFunc("/hydra", webhydra)
loggedin.HandleFunc("/emus", showemus) loggedin.HandleFunc("/emus", showemus)
loggedin.Handle("/submithonker", login.CSRFWrap("submithonker", http.HandlerFunc(submithonker))) loggedin.Handle("/submithonker", login.CSRFWrap("submithonker", http.HandlerFunc(websubmithonker)))
err = http.Serve(listener, mux) err = http.Serve(listener, mux)
if err != nil { if err != nil {