import
@@ -250,7 +250,11 @@ }
func gethonksfromlongago(userid int64, wanted int64) []*Honk { now := time.Now() var honks []*Honk +<<<<<<< HEAD for i := 1; i <= 4; i++ { +======= + for i := 1; i <= 3; i++ { +>>>>>>> 4506960 (import) dt := time.Date(now.Year()-i, now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second(), 0, now.Location()) dt1 := dt.Add(-36 * time.Hour).UTC().Format(dbtimeformat)@@ -485,7 +489,13 @@ elog.Printf("error parsing badonks: %s", err)
continue } case "wonkles": +<<<<<<< HEAD case "guesses": +======= + h.Wonkles = j + case "guesses": + h.Guesses = template.HTML(j) +>>>>>>> 4506960 (import) case "oldrev": default: elog.Printf("unknown meta genus: %s", genus)@@ -548,8 +558,11 @@ case "image/png":
xid += ".png" case "image/jpeg": xid += ".jpg" +<<<<<<< HEAD case "image/svg+xml": xid += ".svg" +======= +>>>>>>> 4506960 (import) case "application/pdf": xid += ".pdf" case "text/plain":@@ -576,6 +589,7 @@ fileid, _ := res.LastInsertId()
return fileid, xid, nil } +<<<<<<< HEAD func finddonkid(fileid int64, url string) *Donk { donk := new(Donk) row := stmtFindFileId.QueryRow(fileid, url)@@ -590,6 +604,8 @@ }
return nil } +======= +>>>>>>> 4506960 (import) func finddonk(url string) *Donk { donk := new(Donk) row := stmtFindFile.QueryRow(url)@@ -911,6 +927,23 @@ elog.Printf("error saving mentions: %s", err)
return err } } +<<<<<<< HEAD +======= + 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 + } + } +>>>>>>> 4506960 (import) return nil }@@ -978,7 +1011,11 @@ func savexonker(what, value, flav, when string) {
stmtSaveXonker.Exec(what, value, flav, when) } +<<<<<<< HEAD func savehonker(user *WhatAbout, url, name, flavor, combos, mj string) (int64, error) { +======= +func savehonker(user *WhatAbout, url, name, flavor, combos, mj string) error { +>>>>>>> 4506960 (import) var owner string if url[0] == '#' { flavor = "peep"@@ -990,7 +1027,11 @@ } else {
info, err := investigate(url) if err != nil { ilog.Printf("failed to investigate honker: %s", err) +<<<<<<< HEAD return 0, err +======= + return err +>>>>>>> 4506960 (import) } url = info.XID if name == "" {@@ -1009,16 +1050,30 @@ elog.Printf("honker scan err: %s", err)
} else { err = fmt.Errorf("it seems you are already subscribed to them") } +<<<<<<< HEAD return 0, err +======= + return err +>>>>>>> 4506960 (import) } res, err := stmtSaveHonker.Exec(user.ID, name, url, flavor, combos, owner, mj) if err != nil { elog.Print(err) +<<<<<<< HEAD return 0, err } honkerid, _ := res.LastInsertId() return honkerid, nil +======= + return err + } + honkerid, _ := res.LastInsertId() + if flavor == "presub" { + followyou(user, honkerid) + } + return nil +>>>>>>> 4506960 (import) } func cleanupdb(arg string) {@@ -1095,7 +1150,11 @@ var stmtHonksByOntology, stmtHonksForUser, stmtHonksForMe, stmtSaveDub, stmtHonksByXonker *sql.Stmt
var stmtHonksFromLongAgo *sql.Stmt var stmtHonksByHonker, stmtSaveHonk, stmtUserByName, stmtUserByNumber *sql.Stmt var stmtEventHonks, stmtOneBonk, stmtFindZonk, stmtFindXonk, stmtSaveDonk *sql.Stmt +<<<<<<< HEAD var stmtFindFile, stmtFindFileId, stmtGetFileData, stmtSaveFileData, stmtSaveFile *sql.Stmt +======= +var stmtFindFile, stmtGetFileData, stmtSaveFileData, stmtSaveFile *sql.Stmt +>>>>>>> 4506960 (import) var stmtCheckFileData *sql.Stmt var stmtAddDoover, stmtGetDoovers, stmtLoadDoover, stmtZapDoover, stmtOneHonker *sql.Stmt var stmtUntagged, stmtDeleteHonk, stmtDeleteDonks, stmtDeleteOnts, stmtSaveZonker *sql.Stmt@@ -1106,7 +1165,10 @@ var stmtSaveMeta, stmtDeleteAllMeta, stmtDeleteOneMeta, stmtDeleteSomeMeta, stmtUpdateHonk *sql.Stmt
var stmtHonksISaved, stmtGetFilters, stmtSaveFilter, stmtDeleteFilter *sql.Stmt var stmtGetTracks *sql.Stmt var stmtSaveChonk, stmtLoadChonks, stmtGetChatters *sql.Stmt +<<<<<<< HEAD var stmtDeliquentCheck, stmtDeliquentUpdate *sql.Stmt +======= +>>>>>>> 4506960 (import) func preparetodie(db *sql.DB, s string) *sql.Stmt { stmt, err := db.Prepare(s)
@@ -154,10 +154,13 @@ .Ic cleanup Op Ar days
command exists to purge old external data, by default 30 days. This removes unreferenced, unsaved posts and attachments. It does not remove any original content. +<<<<<< HEAD This will not immediately reduce the size of the database, but frees space for future use. A vacuum may be performed manually if necessary, but will require more time and additional disk space. +======= +>>>>>>> 4506960 (import) .Pp Backups may be performed by running .Ic backup dirname .
@@ -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 +
@@ -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) +}
@@ -3,24 +3,59 @@ <main>
<div class="info" id="infobox"> <div id="srvmsg"> {{ if .Name }} +<<<<<<< HEAD <p>{{ .Name }} <span class="left1em"><a href="/u/{{ .Name }}/rss">rss</a></span> +======= +<p>{{ .Name }} <span style="margin-left:1em;"><a href="/u/{{ .Name }}/rss">rss</a></span> +>>>>>>> 4506960 (import) <p>{{ .WhatAbout }} {{ end }} <p>{{ .ServerMessage }} </div> {{ if .HonkCSRF }} {{ template "honkform.html" . }} +<<<<<<< HEAD <script src="/honkpage.js{{ .JSParam }}" defer data-csrf="{{ .HonkCSRF }}" data-pagename="{{ .PageName }}" data-pagearg="{{ .PageArg }}" data-tophid="{{ .TopHID }}" data-srvmsg="{{ .ServerMessage }}"></script> {{ end }} {{ if .LocalJSParam }} <script src="/local.js{{ .LocalJSParam }}" defer></script> +======= +<script> +var csrftoken = {{ .HonkCSRF }} +var honksforpage = { } +var curpagestate = { name: "{{ .PageName }}", arg : "{{ .PageArg }}" } +var tophid = { } +tophid[curpagestate.name + ":" + curpagestate.arg] = "{{ .TopHID }}" +var servermsgs = { } +servermsgs[curpagestate.name + ":" + curpagestate.arg] = "{{ .ServerMessage }}" +</script> +<script src="/honkpage.js{{ .JSParam }}"></script> +{{ end }} +<script> +function playit(elem, word, wordlist, xid) { + import('/wonk.js').then(module => { + makeaguess = module.makeaguess + module.addguesscontrols(elem, word, wordlist, xid) + }) +} +</script> +{{ if .LocalJSParam }} +<script src="/local.js{{ .LocalJSParam }}"></script> +>>>>>>> 4506960 (import) {{ end }} </div> {{ if and .HonkCSRF (not .IsPreview) }} <div class="info" id="refreshbox"> +<<<<<<< HEAD <p><button class="refresh">refresh</button><span></span> <button class="scrolldown">scroll down</button> </div> +======= +<p><button onclick="refreshhonks(this)">refresh</button><span></span> +<button onclick="oldestnewest(this)">scroll down</button> +</div> +{{ if eq .ServerMessage "one honk maybe more" }} <script> hideelement("refreshbox")</script> {{ end }} +>>>>>>> 4506960 (import) {{ end }} <div id="honksonpage"> <div>
@@ -0,0 +1,381 @@
+:root { + --bg-page: #f4f4f4; + --bg-dark: #eee; + --bg-limited: #ddd; + --fg: #000; + --fg-subtle: #666; + --fg-limited: #509c93; + --hl: #c2c2c2; +} + +@media (prefers-color-scheme: dark) { + :root { + --bg-page: #111; + --bg-dark: #222; + --fg: #ccc; + --hl: #333; + --fg-subtle: #ccc; + --fg-limited: #509c93; + --bg-limited: #333; + } +} + +* { + font-size: 14px !important; +} + +body { + background: var(--bg-page); + color: var(--fg); + font-size: 14px !important; + word-wrap: break-word; + font-family: -apple-system, sans-serif, "Noto Color Emoji"; + line-height: 1.2; + overscroll-behavior-y: contain; +} +pre, code { + white-space: pre-wrap; +} +blockquote { + margin-left: 0em; + margin-bottom: 0em; + padding-left: 0.5em; + border-left: 1px solid var(--fg-subtle); +} +cite { + margin-left: 2em; +} +table { + display: block; + max-width: 100%; + overflow-x: auto; +} +a { + color: var(--fg); +} +form, input, textarea { + font-family: -apple-system, sans-serif, "Noto Color Emoji"; +} +p { + margin-top: 1em; + margin-bottom: 1em; +} +input { + background: var(--bg-page); + color: var(--fg); + font-size: 1.0em; + line-height: 1.2em; + padding: 0.4em; +} +#honkform input { + font-size: 0.8em; +} +body > header { + margin: 1em auto; + font-size: 1.5em; +} +body > header span { + margin-left: 2em; +} +body > header p { + padding: 1em; +} +header > details { + background: var(--bg-page); + padding: 1em 1em 1em 1em; + position: fixed; + top: 0; + left: 0; + display: inline; + max-height: calc(100% - 1em); + overflow: auto; + opacity: 0.7; + overscroll-behavior: contain; + z-index: 2; +} +header > details[open] { + padding: 1em 1em 0em 1em; + background: var(--bg-dark); + border: 1px solid var(--hl); + margin-bottom: 1em; + opacity: 1.0; +} +header > details summary span { + display: none; +} +header > details[open] summary span { + display: inline; +} +header > details li { + margin: 1em 0em 1em 0em; +} +details summary { + cursor: pointer; +} +main { + max-width: 1200px; + margin: auto; + font-size: 1.5em; +} +hr { + border-color: var(--hl); +} +.info { + background: var(--bg-dark); + border: 1px solid var(--hl); + margin-bottom: 1em; + padding: 0em 1em 0em 1em; +} +.info div { + margin-top: 1em; + margin-bottom: 1em; +} +label { + font-size: 0.8em; +} +label.button, button, select { + font-size: 16px; + font-family: -apple-system, sans-serif; + color: var(--fg); + background: var(--bg-page); + border: 1px solid var(--hl); + padding: 0.5em; + white-space: nowrap; +} +.buttonarray { + margin-top: -2.0em; +} +.buttonarray button, .buttonarray > span { + margin-top: 2.0em; + display: inline-block; +} +button a { + text-decoration: none; +} +button { + cursor: pointer; +} +form { + margin-top: 1em; +} +textarea { + padding: 0.5em; + font-size: 1em; + background: var(--bg-page); + color: var(--fg); + width: 600px; + height: 4em; + margin-bottom: 0.5em; + box-sizing: border-box; + max-width: 100%; +} +textarea#honknoise { + height: 10em; +} +input[type="checkbox"] { + position: fixed; + top: -9999px; +} +input[type="checkbox"] + span:after { + content: "no"; +} +input[type="checkbox"]:checked + span:after { + content: "yes"; +} +input[type="checkbox"]:focus + span:after { + outline: 1px solid var(--fg); +} +input[type=file] { + display: none; +} + +.glow { + box-shadow: 0px 0px 16px var(--hl); +} + +.honk { + margin: auto; + background: var(--bg-dark); + border: 0px solid var(--hl); + margin-bottom: 1em; + border-radius: 0em !important; + padding-left: 1em; + padding-right: 1em; + padding-top: 0; + overflow: hidden; +} + +.chat { + border-bottom: 0.5px solid var(--fg-subtle); + padding-left: 1em; +} +.chat p { + margin-top: 0.2em; + margin-bottom: 0.2em; +} +.chattarget { + border-bottom: 1px solid var(--fg-subtle); +} +.chatstamp { + margin-left: -1em; +} + +.honk #honkform { + padding: 1em; + border: 1px solid var(--fg); +} +.honk a { + color: var(--fg); +} +.honk header { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: 0.8em; + line-height: 1.1; + margin-top: 1em; + height: 64px; +} + +.honk header .clip a { + color: var(--fg-subtle); +} + +.clip { + text-transform: lowercase; +} + +.honk header img { + float: left; + margin-right: 1em; + width: 64px; + height: 64px; +} +.honk header p { + margin-top: 0px; +} +.honk .actions button { + margin-left: 4em; + margin-top: 2em; +} +.honk .noise { + line-height: 1.4; +} + +.honk .noise code .kw { font-weight: bold; } +.honk .noise code .bi { font-weight: bold; } +.honk .noise code .st { color: var(--fg-subtle); } +.honk .noise code .nm { color: #ba88ff; } +.honk .noise code .op { color: #ba88ff; } +.honk .noise code .tp { font-weight: bold; } +.honk .noise code .cm { color: var(--fg-subtle); font-style: italic; } +.honk .noise code .al { color: #aaffbb; } +.honk .noise code .dl { color: #ffaabb; } + +.honk details.actions summary { + color: var(--fg-subtle); +} +.subtle .noise { + color: var(--fg-subtle); + font-size: 0.8em; +} +.subtle .noise a { + color: var(--fg-subtle); +} +.limited { + background: var(--bg-limited); + border: 0px solid var(--fg-limited); + color: var(--fg-subtle); +} +.limited .glow { + box-shadow: 0px 0px 16px var(--fg-limited); +} +.limited .noise { + color: var(--fg-subtle); +} +.limited .noise a { + color: var(--fg-limited); +} +.limited details.actions summary { + color: var(--fg-limited); +} +details.noise[open] summary { + display: none; +} +h1, h2 { + font-size: 1.2em; +} +h3, h4 { + font-size: 1.1em; +} + +nav { + float: right; +} + +nav ul { + padding: 0; + margin: 0; + list-style: none; + padding-bottom: 20px; +} + +nav ul li { + padding-right: 10px; + display: inline-block; +} + +img:not(.emu) { + background: var(--bg-page); +} +img, video { + max-width: 100%; + max-height: 600px; +} +.noise img:not(.emu) { + display: block; +} +img.emu { + width: 2em; + height: 2em; + vertical-align: middle; + margin: -2px; + object-fit: contain; +} +.nophone { + position: fixed; + opacity: 0.7; + cursor: pointer; +} +@media screen and (max-width: 1360px) { + .nophone { + display: none; + } +} +@media screen and (max-width: 740px) { + body { + font-size: 12px; + } + .honk header { + height: 52px; + } + .honk header img { + width: 48px; + height: 48px; + } + details summary { + outline: none; + } +} +@media print { + #topmenu, #topspacer, #infobox, #refreshbox, .actions { + display: none; + } + html { + --bg-page: white; + --bg-dark: white; + --fg: black; + --fg-subtle: black; + --fg-limited: #a79; + } +}
@@ -0,0 +1,6 @@
+ const donk = document.getElementById("donkinput"); + + window.addEventListener('paste', e => { + donk.files = e.clipboardData.files; + }); + donk.onchange();
@@ -0,0 +1,83 @@
+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=" + escape(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 onclick='return makeaguess(this)'>guess</button>" + 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 += "🟩" + } else if (letters[g[i]] > 0) { + res += g[i] + obfu += "🟨" + letters[g[i]] = letters[g[i]] - 1 + } else { + obfu += "⬛" + res += "." + } + } + + var div = document.createElement( 'div' ); + div.innerHTML = "<p style='font-family: monospace'>" + 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 onclick='return makeaguess(this)'>guess</button>" + } + host.append(div) + btn.parentElement.remove() +}
@@ -35,6 +35,7 @@ "time"
"unicode/utf8" "github.com/gorilla/mux" + "github.com/sassoftware/relic/lib/dlog" "humungus.tedunangst.com/r/webs/cache" "humungus.tedunangst.com/r/webs/httpsig" "humungus.tedunangst.com/r/webs/junk"@@ -2464,6 +2465,7 @@ h := submithonk(w, r)
if h == nil { return } + fmt.Fprintf(w, "%s", h.XID) case "donk": d, err := submitdonk(w, r)