Compare commits

..

No commits in common. "10bca44dc51f729d5c4579d03b886974fb43ee69" and "50524cade2d1236e4be684993f0bfd470b240452" have entirely different histories.

26 changed files with 467 additions and 336 deletions

View file

@ -36,5 +36,3 @@ 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,8 +511,7 @@ 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_r0malink = regexp.MustCompile(`https://[[:alnum:].]+/objects/[[:alnum:]-]+`) var re_romalink = 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 {
@ -522,11 +521,14 @@ 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]
if re_mast0link.MatchString(m) || re_masto1ink.MatchString(m) || dlog.Printf("consider qt: %s", 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_r0malink.MatchString(m) || re_roma1ink.MatchString(m) { re_romalink.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 {
@ -574,7 +576,6 @@ 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")
@ -686,10 +687,11 @@ 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
@ -818,6 +820,12 @@ 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"
} }
@ -838,9 +846,6 @@ 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)
@ -868,7 +873,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" || (preferorig && at == "Link") { } else if at == "Document" || at == "Image" {
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" ||
@ -881,30 +886,12 @@ 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++
} }
if preferorig {
atts, _ := obj.GetArray("url")
for _, atti := range atts {
att, ok := atti.(junk.Junk)
if !ok {
ilog.Printf("attachment that wasn't map?")
continue
}
procatt(att)
}
if numatts == 0 {
preferorig = false
}
}
if !preferorig {
atts, _ := obj.GetArray("attachment") atts, _ := obj.GetArray("attachment")
for _, atti := range atts { for _, atti := range atts {
att, ok := atti.(junk.Junk) att, ok := atti.(junk.Junk)
@ -917,7 +904,6 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
if att, ok := obj.GetMap("attachment"); ok { if att, ok := obj.GetMap("attachment"); ok {
procatt(att) procatt(att)
} }
}
tags, _ := obj.GetArray("tag") tags, _ := obj.GetArray("tag")
for _, tagi := range tags { for _, tagi := range tags {
tag, ok := tagi.(junk.Junk) tag, ok := tagi.(junk.Junk)
@ -1195,6 +1181,8 @@ 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()
@ -1202,6 +1190,8 @@ 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"
@ -1308,11 +1298,14 @@ 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"] = h.Precis jo["summary"] = html.EscapeString(h.Precis)
jo["content"] = h.Noise jo["content"] = h.Noise
j["object"] = jo j["object"] = jo
case "bonk": case "bonk":
@ -1906,7 +1899,7 @@ func unfollowme(user *WhatAbout, who string, name string, j junk.Junk) {
} }
} }
func followyou(user *WhatAbout, honkerid int64, sync bool) { func followyou(user *WhatAbout, honkerid int64) {
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')",
@ -1923,38 +1916,27 @@ func followyou(user *WhatAbout, honkerid int64, sync bool) {
elog.Printf("error updating honker: %s", err) elog.Printf("error updating honker: %s", err)
return return
} }
if sync {
subsub(user, url, owner, folxid)
} else {
go subsub(user, url, owner, folxid) go subsub(user, url, owner, folxid)
}
} }
func unfollowyou(user *WhatAbout, honkerid int64, sync bool) { func unfollowyou(user *WhatAbout, honkerid int64) {
db := opendatabase() db := opendatabase()
row := db.QueryRow("select xid, owner, folxid, flavor from honkers where honkerid = ? and userid = ? and flavor in ('unsub', 'peep', 'presub', 'sub')", row := db.QueryRow("select xid, owner, folxid from honkers where honkerid = ? and userid = ? and flavor in ('sub')",
honkerid, user.ID) honkerid, user.ID)
var url, owner, folxid, flavor string var url, owner, folxid string
err := row.Scan(&url, &owner, &folxid, &flavor) err := row.Scan(&url, &owner, &folxid)
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
} }
if sync {
itakeitallback(user, url, owner, folxid)
} else {
go itakeitallback(user, url, owner, folxid) go itakeitallback(user, url, owner, folxid)
} }
}
func followyou2(user *WhatAbout, j junk.Junk) { func followyou2(user *WhatAbout, j junk.Junk) {
who, _ := j.GetString("actor") who, _ := j.GetString("actor")

View file

@ -14,3 +14,54 @@
// 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,7 +485,9 @@ 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)
@ -574,20 +576,6 @@ 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)
@ -909,6 +897,20 @@ 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
} }
@ -976,7 +978,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) (int64, error) { func savehonker(user *WhatAbout, url, name, flavor, combos, mj string) error {
var owner string var owner string
if url[0] == '#' { if url[0] == '#' {
flavor = "peep" flavor = "peep"
@ -988,7 +990,7 @@ func savehonker(user *WhatAbout, url, name, flavor, combos, mj string) (int64, e
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 0, err return err
} }
url = info.XID url = info.XID
if name == "" { if name == "" {
@ -1007,16 +1009,19 @@ func savehonker(user *WhatAbout, url, name, flavor, combos, mj string) (int64, e
} 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 0, err return 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 0, err return err
} }
honkerid, _ := res.LastInsertId() honkerid, _ := res.LastInsertId()
return honkerid, nil if flavor == "presub" {
followyou(user, honkerid)
}
return nil
} }
func cleanupdb(arg string) { func cleanupdb(arg string) {
@ -1093,7 +1098,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, stmtFindFileId, stmtGetFileData, stmtSaveFileData, stmtSaveFile *sql.Stmt var stmtFindFile, 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
@ -1163,7 +1168,6 @@ 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,6 +54,13 @@ 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,18 +1,6 @@
changelog changelog
=== 0.9.91 One More Time === next
+ 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,8 +52,6 @@ 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,27 +158,6 @@ 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,8 +74,7 @@ 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) or any other combination of two letters and a colon, (danger zone) it will be used a summary and the post marked sensitive.
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
@ -122,7 +121,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 live dangerously by posting assassination coordinates. One may also check in to a location.
The available fields, all optional, are The available fields, all optional, are
.Ar name , .Ar name ,
.Ar url , .Ar url ,

View file

@ -142,11 +142,6 @@ 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,6 +181,9 @@ 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
} }
@ -287,11 +290,12 @@ func imaginate(honk *Honk) {
htf.String(honk.Noise) htf.String(honk.Noise)
} }
var re_dangerous = regexp.MustCompile("^[a-zA-Z]{2}:") func translate(honk *Honk) {
if honk.Format == "html" {
func precipitate(honk *Honk) { return
}
noise := honk.Noise noise := honk.Noise
if re_dangerous.MatchString(noise) { if strings.HasPrefix(noise, "DZ:") {
idx := strings.Index(noise, "\n") idx := strings.Index(noise, "\n")
if idx == -1 { if idx == -1 {
honk.Precis = noise honk.Precis = noise
@ -300,16 +304,8 @@ func precipitate(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.Precis = markitzero(strings.TrimSpace(honk.Precis))
honk.Noise = noise
}
}
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
@ -425,8 +421,6 @@ 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 {

6
genschemago.sh Normal file
View file

@ -0,0 +1,6 @@
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,7 +33,6 @@ 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"`
@ -307,13 +306,6 @@ 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 ||
@ -415,12 +407,6 @@ 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,6 +104,8 @@ 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 {
@ -178,6 +180,10 @@ 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
@ -373,42 +379,6 @@ 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 {

51
hoot.go
View file

@ -26,7 +26,6 @@ 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]")
@ -55,11 +54,10 @@ 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")
src := htfilter.GetAttr(node, "src") if htfilter.HasClass(node, "Emoji") && alt != "" {
if htfilter.HasClass(node, "Emoji") || strings.HasSuffix(src, ".svg") {
return alt return alt
} }
return string(templates.Sprintf(" <img src='%s' alt='%s'>", src, alt)) return fmt.Sprintf(" <img src='%s'>", htfilter.GetAttr(node, "src"))
} }
var buf strings.Builder var buf strings.Builder
@ -67,6 +65,7 @@ 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 twp := div.Parent.Parent.Parent.Parent.Parent
link := url link := url
alink := linksel.MatchFirst(twp) alink := linksel.MatchFirst(twp)
@ -98,6 +97,50 @@ 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)
fmt.Fprintf(&buf, "> @%s: %s\n", author, text) fmt.Fprintf(&buf, "> @%s: %s\n", author, text)
continue
}
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 {
link = "https://twitter.com" + htfilter.GetAttr(alink, "href")
}
replto := replyingto.MatchFirst(twp)
if replto != nil {
continue
}
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)
text = re_removepics.ReplaceAllString(text, "")
if seen[text] {
continue
}
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 youvegothonks PROGS=autobonker gettoken saytheday sprayandpray wonkawonk youvegothonks
all: $(PROGS) all: $(PROGS)
@ -18,5 +18,8 @@ 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

79
toys/wonkawonk.go Normal file
View file

@ -0,0 +1,79 @@
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 = 42 var myVersion = 41
type dbexecer interface { type dbexecer interface {
Exec(query string, args ...interface{}) (sql.Result, error) Exec(query string, args ...interface{}) (sql.Result, error)
@ -206,38 +206,6 @@ 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,14 +21,20 @@
<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 "\U0001F596") "selected" }}>{{ "\U0001F596" }}</option> <option {{ and (eq .User.Options.Reaction "\U0001F61E") "selected" }}>{{ "\U0001F61E" }}</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 "\U0001F9EF") "selected" }}>{{ "\U0001F9EF" }}</option> <option {{ and (eq .User.Options.Reaction "\U0001F1EB") "selected" }}>{{ "\U0001F1EB" }}</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,8 +20,6 @@ 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>
@ -56,7 +54,6 @@ 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 }}<p></summary> <summary>{{ .HTPrecis }}</summary>
<p>{{ .HTPrecis }} <p>{{ .HTPrecis }}
<p class="content">{{ .HTML }} <p class="content">{{ .HTML }}
{{ with .Time }} {{ with .Time }}
@ -89,6 +89,15 @@ 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>assassination coordinates</button> <p><button id=checkinbutton type=button>checkin</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>assassination coordinates</button> <p><button id=checkinbutton type=button>checkin</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,6 +139,7 @@ function fillinhonks(xhr, glowit) {
} else { } else {
holder.append(h) holder.append(h)
} }
} }
relinklinks() relinklinks()
return lenhonks return lenhonks
@ -211,7 +212,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
stash = name + ":" + arg var stash = name + ":" + arg
holder = honksforpage[stash] holder = honksforpage[stash]
if (holder) { if (holder) {
honksonpage.prepend(holder) honksonpage.prepend(holder)
@ -225,7 +226,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) {
fillinhonks(xhr, false) var lenhonks = fillinhonks(xhr, false)
} else { } else {
refreshupdate(" status: " + xhr.status) refreshupdate(" status: " + xhr.status)
} }
@ -336,6 +337,12 @@ 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)
}
} }
}) })
} }
@ -351,7 +358,6 @@ 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) {
@ -365,7 +371,7 @@ function showhonkform(elem, rid, hname) {
} }
var updateinput = document.getElementById("updatexidinput") var updateinput = document.getElementById("updatexidinput")
updateinput.value = "" updateinput.value = ""
honknoise.focus() document.getElementById("honknoise").focus()
return false return false
} }
function cancelhonking() { function cancelhonking() {
@ -387,9 +393,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)
el = document.getElementById("donkdescriptor") var el = document.getElementById("donkdescriptor")
el.style.display = "" el.style.display = ""
el = document.getElementById("saveddonkxid") var el = document.getElementById("saveddonkxid")
el.value = "" el.value = ""
} }
var checkinprec = 100.0 var checkinprec = 100.0
@ -411,19 +417,25 @@ function fillcheckin() {
gpsoptions.timeout = 2000 gpsoptions.timeout = 2000
}, function(err) { }, function(err) {
showelement("placedescriptor") showelement("placedescriptor")
var el = document.getElementById("placenameinput") 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() {
var div = document.getElementById("emupicker") div = document.getElementById("emupicker")
var request = new XMLHttpRequest() 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

89
views/wonk.js Normal file
View file

@ -0,0 +1,89 @@
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()
}

150
web.go
View file

@ -381,6 +381,8 @@ 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
@ -419,9 +421,13 @@ func inbox(w http.ResponseWriter, r *http.Request) {
return return
case "Question": case "Question":
return return
} case "Note":
}
go xonksaver(user, j, origin) go xonksaver(user, j, origin)
return
}
}
ilog.Printf("unknown Update activity")
dumpactivity(j)
case "Undo": case "Undo":
obj, ok := j.GetMap("object") obj, ok := j.GetMap("object")
if !ok { if !ok {
@ -481,6 +487,8 @@ 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
@ -1322,6 +1330,21 @@ 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()
@ -1555,7 +1578,7 @@ func submitdonk(w http.ResponseWriter, r *http.Request) (*Donk, error) {
return d, nil return d, nil
} }
func websubmithonk(w http.ResponseWriter, r *http.Request) { func submitwebhonk(w http.ResponseWriter, r *http.Request) {
h := submithonk(w, r) h := submithonk(w, r)
if h == nil { if h == nil {
return return
@ -1597,6 +1620,10 @@ 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,
@ -1605,28 +1632,17 @@ 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 {
@ -1646,7 +1662,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 !re_dangerous.MatchString(honk.Precis) { if !(strings.HasPrefix(honk.Precis, "DZ:") || strings.HasPrefix(honk.Precis, "re: re: re: ")) {
honk.Precis = "re: " + honk.Precis honk.Precis = "re: " + honk.Precis
} }
} }
@ -1680,19 +1696,12 @@ 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 = fmt.Sprintf("%s:%d", d.XID, d.FileID) donkxid = d.XID
} }
} else { } else {
p := strings.Split(donkxid, ":") xid := donkxid
xid := p[0]
url := fmt.Sprintf("https://%s/d/%s", serverName, xid) url := fmt.Sprintf("https://%s/d/%s", serverName, xid)
var donk *Donk donk := finddonk(url)
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 {
@ -1895,15 +1904,7 @@ func showcombos(w http.ResponseWriter, r *http.Request) {
} }
} }
func websubmithonker(w http.ResponseWriter, r *http.Request) { func submithonker(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"))
@ -1913,10 +1914,10 @@ func submithonker(w http.ResponseWriter, r *http.Request) *Honker {
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 nil return
} }
var meta HonkerMeta var meta HonkerMeta
@ -1925,34 +1926,31 @@ func submithonker(w http.ResponseWriter, r *http.Request) *Honker {
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, false) unfollowyou(user, honkerid)
stmtDeleteHonker.Exec(honkerid) stmtDeleteHonker.Exec(honkerid)
return h http.Redirect(w, r, "/honkers", http.StatusSeeOther)
return
} }
if r.FormValue("unsub") == "unsub" { if r.FormValue("unsub") == "unsub" {
unfollowyou(user, honkerid, false) unfollowyou(user, honkerid)
} }
if r.FormValue("sub") == "sub" { if r.FormValue("sub") == "sub" {
followyou(user, honkerid, false) followyou(user, honkerid)
} }
_, 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 nil return
} }
return h http.Redirect(w, r, "/honkers", http.StatusSeeOther)
return
} }
if url == "" { if url == "" {
http.Error(w, "subscribing to nothing?", http.StatusInternalServerError) http.Error(w, "subscribing to nothing?", http.StatusInternalServerError)
return nil return
} }
flavor := "presub" flavor := "presub"
@ -1960,17 +1958,13 @@ func submithonker(w http.ResponseWriter, r *http.Request) *Honker {
flavor = "peep" flavor = "peep"
} }
var err error err := savehonker(user, url, name, flavor, combos, mj)
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 nil return
} }
if flavor == "presub" {
followyou(user, honkerid, false) http.Redirect(w, r, "/honkers", http.StatusSeeOther)
}
h.ID = honkerid
return h
} }
func hfcspage(w http.ResponseWriter, r *http.Request) { func hfcspage(w http.ResponseWriter, r *http.Request) {
@ -2007,7 +2001,6 @@ 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"
@ -2123,29 +2116,11 @@ 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())
@ -2356,7 +2331,7 @@ func apihandler(w http.ResponseWriter, r *http.Request) {
if h == nil { if h == nil {
return return
} }
fmt.Fprintf(w, "%s", h.XID) w.Write([]byte(h.XID))
case "donk": case "donk":
d, err := submitdonk(w, r) d, err := submitdonk(w, r)
if err != nil { if err != nil {
@ -2416,16 +2391,6 @@ 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
@ -2468,7 +2433,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'; media-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'; report-uri /csp-violation")
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
}) })
} }
@ -2588,6 +2553,7 @@ 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)
@ -2600,6 +2566,8 @@ 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)
@ -2615,7 +2583,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(websubmithonk))) loggedin.Handle("/honk", login.CSRFWrap("honkhonk", http.HandlerFunc(submitwebhonk)))
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)))
@ -2630,7 +2598,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(websubmithonker))) loggedin.Handle("/submithonker", login.CSRFWrap("submithonker", http.HandlerFunc(submithonker)))
err = http.Serve(listener, mux) err = http.Serve(listener, mux)
if err != nil { if err != nil {