the gods have spoken -- honk 1.0.0 is upon us
@@ -1,6 +1,6 @@
honk --- features +## features Take control of your honks and join the federation. An ActivityPub server with minimal setup and support costs.@@ -17,7 +17,7 @@
The honk mission is to work well if it's what you want. This does not imply the goal is to be what you want. --- build +## build It should be sufficient to type make after unpacking a release. You'll need a go compiler version 1.16 or later. And libsqlite3.@@ -26,7 +26,7 @@ Even on a fast machine, building from source can take several seconds.
Development sources: hg clone https://humungus.tedunangst.com/r/honk --- setup +## setup honk expects to be fronted by a TLS terminating reverse proxy.@@ -40,17 +40,17 @@
Then run honk. ./honk --- upgrade +## upgrade old-honk backup `date +backup-%F` ./honk upgrade ./honk --- documentation +## documentation There is a more complete incomplete manual. This is just the README. --- guidelines +## guidelines One honk per day, or call it an "eighth-tenth" honk. If your honk frequency changes, so will the number of honks.@@ -67,6 +67,6 @@
The honk may be made on public property only when the person doing the honk has the permission of the owner of that property. --- disclaimer +## disclaimer Do not use honk to contact emergency services.
@@ -30,6 +30,7 @@ "regexp"
"strings" "time" + "github.com/sassoftware/relic/lib/dlog" "humungus.tedunangst.com/r/webs/cache" "humungus.tedunangst.com/r/webs/gate" "humungus.tedunangst.com/r/webs/httpsig"@@ -132,60 +133,34 @@ var flightdeck = gate.NewSerializer()
var signGets = true -func junkGet(userid int64, url string, args junk.GetArgs) (junk.Junk, error) { +func GetJunkTimeout(userid int64, url string, timeout time.Duration) (junk.Junk, error) { + if rejectorigin(userid, url, false) { + return nil, fmt.Errorf("rejected origin: %s", url) + } client := http.DefaultClient - if args.Client != nil { - client = args.Client - } - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return nil, err - } - if args.Accept != "" { - req.Header.Set("Accept", args.Accept) - } - if args.Agent != "" { - req.Header.Set("User-Agent", args.Agent) - } - if signGets { + sign := func(req *http.Request) error { var ki *KeyInfo ok := ziggies.Get(userid, &ki) if ok { httpsig.SignRequest(ki.keyname, ki.seckey, req, nil) } - } - if args.Timeout != 0 { - ctx, cancel := context.WithTimeout(context.Background(), args.Timeout) - defer cancel() - req = req.WithContext(ctx) + return nil } - resp, err := client.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != 200 { - return nil, fmt.Errorf("http get status: %d", resp.StatusCode) - } - return junk.Read(resp.Body) -} - -func GetJunkTimeout(userid int64, url string, timeout time.Duration) (junk.Junk, error) { - client := http.DefaultClient if develMode { client = develClient + sign = nil } fn := func() (interface{}, error) { - at := thefakename + at := theonetruename if strings.Contains(url, ".well-known/webfinger?resource") { at = "application/jrd+json" } - j, err := junkGet(userid, url, junk.GetArgs{ + j, err := junk.Get(url, junk.GetArgs{ Accept: at, Agent: "honksnonk/5.0; " + serverName, Timeout: timeout, Client: client, + Fixup: sign, }) return j, err }@@ -344,10 +319,10 @@ func eradicatexonk(userid int64, xid string) {
xonk := getxonk(userid, xid) if xonk != nil { deletehonk(xonk.ID) - } - _, err := stmtSaveZonker.Exec(userid, xid, "zonk") - if err != nil { - elog.Printf("error eradicating: %s", err) + _, err := stmtSaveZonker.Exec(userid, xid, "zonk") + if err != nil { + elog.Printf("error eradicating: %s", err) + } } }@@ -464,19 +439,12 @@ return a
} func extractattrto(obj junk.Junk) string { - who, _ := obj.GetString("attributedTo") - if who != "" { - return who - } - o, ok := obj.GetMap("attributedTo") - if ok { - id, ok := o.GetString("id") + arr := oneforall(obj, "attributedTo") + for _, a := range arr { + s, ok := a.(string) if ok { - return id + return s } - } - arr, _ := obj.GetArray("attributedTo") - for _, a := range arr { o, ok := a.(junk.Junk) if ok { t, _ := o.GetString("type")@@ -485,12 +453,19 @@ if t == "Person" || t == "" {
return id } } - s, ok := a.(string) - if ok { - return s - } } return "" +} + +func oneforall(obj junk.Junk, key string) []interface{} { + if val, ok := obj.GetMap(key); ok { + return []interface{}{val} + } + if str, ok := obj.GetString(key); ok { + return []interface{}{str} + } + arr, _ := obj.GetArray(key) + return arr } func firstofmany(obj junk.Junk, key string) string {@@ -519,7 +494,7 @@ depth := 0
maxdepth := 10 currenttid := "" goingup := 0 - var xonkxonkfn func(item junk.Junk, origin string, isUpdate bool) *Honk + var xonkxonkfn func(junk.Junk, string, bool, string) *Honk qutify := func(user *WhatAbout, content string) string { if depth >= maxdepth {@@ -551,7 +526,7 @@ content = fmt.Sprintf("%s<blockquote>%s</blockquote>", content, q)
} prevdepth := depth depth = maxdepth - xonkxonkfn(j, originate(m), false) + xonkxonkfn(j, originate(m), false, "") depth = prevdepth } }@@ -570,18 +545,22 @@ if err != nil {
ilog.Printf("error getting onemore: %s: %s", xid, err) return } - depth++ - xonkxonkfn(obj, originate(xid), false) - depth-- + xonkxonkfn(obj, originate(xid), false, "") } - xonkxonkfn = func(item junk.Junk, origin string, isUpdate bool) *Honk { + xonkxonkfn = func(item junk.Junk, origin string, isUpdate bool, bonker string) *Honk { id, _ := item.GetString("id") what := firstofmany(item, "type") dt, ok := item.GetString("published") if !ok { dt = time.Now().Format(time.RFC3339) } + if depth >= maxdepth+5 { + ilog.Printf("went too deep in xonkxonk") + return nil + } + depth++ + defer func() { depth-- }() var err error var xid, rid, url, convoy string@@ -627,21 +606,29 @@ return nil
case "Announce": obj, ok = item.GetMap("object") if ok { + // peek ahead some what, ok := obj.GetString("type") - if ok && what == "Create" { - obj, ok = obj.GetMap("object") - if !ok { - ilog.Printf("lost object inside create %s", id) - return nil + if ok && (what == "Create" || what == "Update") { + if what == "Update" { + isUpdate = true + } + inner, ok := obj.GetMap("object") + if ok { + obj = inner + } else { + xid, _ = obj.GetString("object") } } - xid, _ = obj.GetString("id") + if xid == "" { + xid, _ = obj.GetString("id") + } } else { xid, _ = item.GetString("object") } - if !needbonkid(user, xid) { + if !isUpdate && !needbonkid(user, xid) { return nil } + bonker, _ = item.GetString("actor") origin = originate(xid) if ok && originate(id) == origin { dlog.Printf("using object in announce for %s", xid)@@ -650,9 +637,10 @@ dlog.Printf("getting bonk: %s", xid)
obj, err = GetJunkHardMode(user.ID, xid) if err != nil { ilog.Printf("error getting bonk: %s: %s", xid, err) + return nil } } - what = "bonk" + return xonkxonkfn(obj, origin, isUpdate, bonker) case "Update": isUpdate = true fallthrough@@ -674,7 +662,7 @@ if obj == nil {
ilog.Printf("no object for creation %s", id) return nil } - return xonkxonkfn(obj, origin, isUpdate) + return xonkxonkfn(obj, origin, isUpdate, bonker) case "Read": xid, ok = item.GetString("object") if ok {@@ -687,7 +675,7 @@ if err != nil {
ilog.Printf("error getting read: %s", err) return nil } - return xonkxonkfn(obj, originate(xid), false) + return xonkxonkfn(obj, originate(xid), false, "") } return nil case "Add":@@ -703,7 +691,7 @@ if err != nil {
ilog.Printf("error getting add: %s", err) return nil } - return xonkxonkfn(obj, originate(xid), false) + return xonkxonkfn(obj, originate(xid), false, "") } return nil case "Move":@@ -732,6 +720,7 @@ case "Event":
obj = item what = "event" case "ChatMessage": + bonker = "" obj = item what = "chonk" default:@@ -739,6 +728,9 @@ ilog.Printf("unknown activity: %s", what)
dumpactivity(item) return nil } + if bonker != "" { + what = "bonk" + } if obj != nil { xid, _ = obj.GetString("id")@@ -769,7 +761,9 @@ if obj != nil {
if xonk.Honker == "" { xonk.Honker = extractattrto(obj) } - xonk.Oonker = extractattrto(obj) + if bonker != "" { + xonk.Honker, xonk.Oonker = bonker, xonk.Honker + } if xonk.Oonker == xonk.Honker { xonk.Oonker = "" }@@ -946,25 +940,17 @@ preferorig = false
} } if !preferorig { - atts, _ := obj.GetArray("attachment") + atts := oneforall(obj, "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") - for _, tagi := range tags { - tag, ok := tagi.(junk.Junk) - if !ok { - continue - } + proctag := func(tag junk.Junk) { tt, _ := tag.GetString("type") name, _ := tag.GetString("name") desc, _ := tag.GetString("summary")@@ -1009,6 +995,14 @@ m.Where, _ = tag.GetString("href")
mentions = append(mentions, m) } } + tags := oneforall(obj, "tag") + for _, tagi := range tags { + tag, ok := tagi.(junk.Junk) + if !ok { + continue + } + proctag(tag) + } if starttime, ok := obj.GetString("startTime"); ok { if start, err := time.Parse(time.RFC3339, starttime); err == nil { t := new(Time)@@ -1075,11 +1069,15 @@ }
imaginate(&xonk) if what == "chonk" { + target, _ := obj.GetString("to") + if target == user.URL { + target = xonk.Honker + } ch := Chonk{ UserID: xonk.UserID, XID: xid, Who: xonk.Honker, - Target: xonk.Honker, + Target: target, Date: xonk.Date, Noise: xonk.Noise, Format: xonk.Format,@@ -1135,7 +1133,7 @@ }
return &xonk } - return xonkxonkfn(item, origin, false) + return xonkxonkfn(item, origin, false, "") } func dumpactivity(item junk.Junk) {@@ -1263,6 +1261,7 @@ }
if !h.Public { jo["directMessage"] = true } + h.Noise = re_retag.ReplaceAllString(h.Noise, "") translate(h) redoimages(h) if h.Precis != "" {@@ -1590,6 +1589,7 @@ j["outbox"] = user.URL + "/outbox"
if user.Options.CustomDisplay != "" { user.Display = user.Options.CustomDisplay } + j["name"] = user.Display j["preferredUsername"] = user.Name j["summary"] = user.HTAbout
@@ -14,3 +14,38 @@ // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. package main + +import ( + "io" + "net" + "time" +) + +func qotd() { + var qotdaddr string + getconfig("qotdaddr", &qotdaddr) + if qotdaddr == "" { + return + } + s, err := net.Listen("tcp", ":8017") + if err != nil { + return + } + for { + c, err := s.Accept() + if err != nil { + time.Sleep(time.Second) + continue + } + honks := getpublichonks() + for _, honk := range honks { + if !firstclass(honk) { + continue + } + io.WriteString(c, honk.Noise) + io.WriteString(c, "\n") + break + } + c.Close() + } +}
@@ -30,6 +30,7 @@ "sync"
"time" "humungus.tedunangst.com/r/webs/cache" + "humungus.tedunangst.com/r/webs/htfilter" "humungus.tedunangst.com/r/webs/httpsig" "humungus.tedunangst.com/r/webs/login" "humungus.tedunangst.com/r/webs/mz"@@ -344,7 +345,7 @@ params = append(params, honker)
continue } t = "%" + t + "%" - queries = append(queries, "noise"+negate+"like ?") + queries = append(queries, negate+"(plain like ?)") params = append(params, t) }@@ -790,6 +791,30 @@
return chatter } +func (honk *Honk) Plain() string { + var plain []string + var filt htfilter.Filter + filt.WithLinks = true + if honk.Precis != "" { + t, _ := filt.TextOnly(honk.Precis) + plain = append(plain, t) + } + if honk.Format == "html" { + t, _ := filt.TextOnly(honk.Noise) + plain = append(plain, t) + } else { + plain = append(plain, honk.Noise) + } + for _, d := range honk.Donks { + plain = append(plain, d.Name) + plain = append(plain, d.Desc) + } + for _, o := range honk.Onts { + plain = append(plain, o) + } + return strings.Join(plain, " ") +} + func savehonk(h *Honk) error { dt := h.Date.UTC().Format(dbtimeformat) aud := strings.Join(h.Audience, " ")@@ -800,10 +825,11 @@ if err != nil {
elog.Printf("can't begin tx: %s", err) return err } + plain := h.Plain() res, err := tx.Stmt(stmtSaveHonk).Exec(h.UserID, h.What, h.Honker, h.XID, h.RID, dt, h.URL, aud, h.Noise, h.Convoy, h.Whofore, h.Format, h.Precis, - h.Oonker, h.Flags) + h.Oonker, h.Flags, plain) if err == nil { h.ID, _ = res.LastInsertId() err = saveextras(tx, h)@@ -834,10 +860,11 @@ if err != nil {
elog.Printf("can't begin tx: %s", err) return err } + plain := h.Plain() err = deleteextras(tx, h.ID, false) if err == nil { - _, err = tx.Stmt(stmtUpdateHonk).Exec(h.Precis, h.Noise, h.Format, h.Whofore, dt, h.ID) + _, err = tx.Stmt(stmtUpdateHonk).Exec(h.Precis, h.Noise, h.Format, h.Whofore, dt, plain, h.ID) } if err == nil { err = saveextras(tx, h)@@ -1096,7 +1123,7 @@ tx, err := blobdb.Begin()
if err != nil { elog.Fatal(err) } - for xid, _ := range filexids { + for xid := range filexids { _, err = tx.Exec("delete from filedata where xid = ?", xid) if err != nil { elog.Fatal(err)@@ -1200,9 +1227,9 @@ stmtSaveMeta = preparetodie(db, "insert into honkmeta (honkid, genus, json) values (?, ?, ?)")
stmtDeleteAllMeta = preparetodie(db, "delete from honkmeta where honkid = ?") stmtDeleteSomeMeta = preparetodie(db, "delete from honkmeta where honkid = ? and genus not in ('oldrev')") stmtDeleteOneMeta = preparetodie(db, "delete from honkmeta where honkid = ? and genus = ?") - stmtSaveHonk = preparetodie(db, "insert into honks (userid, what, honker, xid, rid, dt, url, audience, noise, convoy, whofore, format, precis, oonker, flags) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") + stmtSaveHonk = preparetodie(db, "insert into honks (userid, what, honker, xid, rid, dt, url, audience, noise, convoy, whofore, format, precis, oonker, flags, plain) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") stmtDeleteHonk = preparetodie(db, "delete from honks where honkid = ?") - stmtUpdateHonk = preparetodie(db, "update honks set precis = ?, noise = ?, format = ?, whofore = ?, dt = ? where honkid = ?") + stmtUpdateHonk = preparetodie(db, "update honks set precis = ?, noise = ?, format = ?, whofore = ?, dt = ?, plain = ? where honkid = ?") stmtSaveOnt = preparetodie(db, "insert into onts (ontology, honkid) values (?, ?)") stmtDeleteOnts = preparetodie(db, "delete from onts where honkid = ?") stmtSaveDonk = preparetodie(db, "insert into donks (honkid, chonkid, fileid) values (?, ?, ?)")
@@ -69,6 +69,14 @@ }
return 0 } +func letitslide(err error) bool { + str := err.Error() + if strings.Contains(str, "http post status: 400") { + return true + } + return false +} + var dqmtx sync.Mutex func delinquent(userid int64, rcpt string, msg []byte) bool {@@ -143,6 +151,10 @@ if err != nil {
ilog.Printf("failed to post json to %s: %s", inbox, err) if t := lethaldose(err); t > doover.Tries { doover.Tries = t + } + if letitslide(err) { + dlog.Printf("whatever myever %s", inbox) + continue } doover.Msgs = doover.Msgs[i:] sayitagain(doover)
@@ -1,6 +1,18 @@
changelog -=== next +### 1.0.0 Happy Honker + ++ A great big honk composition text box. + ++ More reliable search. + ++ Secret tags. + ++ Mentions link locally. + ++ ::: spoiler markdown + ++ Fix the bonk and zonk crash. + New threaded display order.@@ -10,15 +22,19 @@ + Tuned up superdeliverator.
+ Import from instagram. -+ improve handling of some Page and Link objects ++ Improve handling of some Page and Link objects. + ++ Other federation fixes. + ++ Search can now load external posts. -+ search can now load external posts ++ Some hypercard opengraph markup for previews. -=== 0.9.91 One More Time +### 0.9.91 One More Time + Swallow a follow bug. -=== 0.9.9 Eat the Bugs +### 0.9.9 Eat the Bugs + Some fixes for image descriptions.@@ -50,7 +66,7 @@ + Add a default icon.png.
+ Try to fix hoot again because Twitter did a Twitter. -=== 0.9.8 Tentative Tentacle +### 0.9.8 Tentative Tentacle + Switch database to WAL mode.@@ -80,7 +96,7 @@ + Better error messages for timeouts.
+ Some improved html and markdown. -=== 0.9.7 Witless Weekender +### 0.9.7 Witless Weekender +++ Word guessing game. Wonk wonk!@@ -96,7 +112,7 @@ + Fix the memetizer to work in more environments.
+ Printing is prettier than ever before. -=== 0.9.6 Virile Vigorous and Potent +### 0.9.6 Virile Vigorous and Potent + A bug, a fix, a bug fix, a fix bug.@@ -112,17 +128,17 @@ + Filters work better with hashtags.
+ Fix hoot to work with Twitter's latest crap. -=== 0.9.5 Emergency Ejection +### 0.9.5 Emergency Ejection + Fix honk init user creation. -=== 0.9.4 Collegiate Colloquialism +### 0.9.4 Collegiate Colloquialism + Add validation to some more user inputs to prevent mistakes. + Easier to use ping command. -=== 0.9.3 Notacanthous Nutshell +### 0.9.3 Notacanthous Nutshell ++ backup command.@@ -144,11 +160,11 @@ + Fix pubkey issue with domain only keys.
- Custom lingo for those who don't like honking. -=== 0.9.2 Malleable Maltote +### 0.9.2 Malleable Maltote + Fix compilation on mac. -=== 0.9.1 Late Stage Lusciousness +### 0.9.1 Late Stage Lusciousness ++ Boing boom tschak chonky chatter. Chat messages with Pleroma.@@ -168,7 +184,7 @@ + Fix update activity.
+ A few API refinements and additions. -=== 0.9.0 Monitor vs Merrimack +### 0.9.0 Monitor vs Merrimack --- Add Reactions.@@ -186,7 +202,7 @@ + Reduce retries talking to dumb servers.
+ Maybe possible to use @user@example.com wihtout subdomain. -=== 0.8.6 Sartorial Headpiece +### 0.8.6 Sartorial Headpiece ++ Import command now supports the elephant in the room.@@ -212,7 +228,7 @@ + A raw sendactivity API action for the bold.
+ More flexible meme names. -=== 0.8.5 Turnkey Blaster +### 0.8.5 Turnkey Blaster + Codenames in changelog.@@ -232,17 +248,17 @@ + Disable overscroll (pull down) refresh.
+ Can never seem to version the changelog correctly. -=== 0.8.4 +### 0.8.4 + Fix bug preventing import of keys + Option to switch map links to Apple. -=== 0.8.3 +### 0.8.3 - mistag. -=== 0.8.2 Game Warden +### 0.8.2 Game Warden ++ Import command to preserve those embarssassing old posts from Twitter.@@ -258,7 +274,7 @@ + Rewrite admin console to avoid large dependencies.
+ "Bug" fixes. -=== 0.8.1 +### 0.8.1 ++ Make it easier to upgrade by decoupling data dir from ".".@@ -270,7 +286,7 @@ + Amend changelog for 0.8.0 to include omitted elements:
Syntax highlighting for code blocks. Something resembling an actual manual. -=== 0.8.0 Ordinary Octology +### 0.8.0 Ordinary Octology +++ Add Honk Filtering and Censorship System (HFCS).@@ -325,7 +341,7 @@
- Sometimes the cached state of the @me feed becomes unsynced. Acked status may display incorrectly. -=== 0.7.7 More 7 Than Ever +### 0.7.7 More 7 Than Ever + Add another retry to workaround pixelfed's general unreliability.@@ -337,11 +353,11 @@ + Keep reply to setting during preview.
+ Increase max thread retrieval depth to 10. -=== 0.7.6 +### 0.7.6 + Fix a bug where upgrades would not complete in one step. -=== 0.7.5 +### 0.7.5 + Fix a bug (introdcued 0.7.4) preventing new user creation from working.@@ -353,7 +369,7 @@ + Add server name to user agent.
+ What may be considered UI improvements. -=== 0.7.4 +### 0.7.4 + Ever more bug fixes.@@ -369,19 +385,19 @@ + Support for some user selectable styling. Currently, skinny column mode.
+ webp image transcoding. -=== 0.7.3 +### 0.7.3 + Better fedicompat so bonks are visible to pleroma followers. -=== 0.7.2 +### 0.7.2 + Add the funzone. Minor other UI tweaks. -=== 0.7.1 +### 0.7.1 + Fix bug preventing unfollow from working. -=== 0.7.0 Father Mother Maiden Crone Honker Bonker Zonker +### 0.7.0 Father Mother Maiden Crone Honker Bonker Zonker +++ Auto fetching and inlining of hoots.@@ -411,12 +427,12 @@ + Not all summaries need labels.
+ Add max-width for video tag. -=== 0.6.0 Sixy Delights +### 0.6.0 Sixy Delights Most records from this time of primitive development have been lost. -=== 0.5.0 Halfway to Heaven +### 0.5.0 Halfway to Heaven -=== 0.4.0 Fore Score +### 0.4.0 Fore Score -=== 0.3.0 Valorous Varaha +### 0.3.0 Valorous Varaha
@@ -70,6 +70,13 @@ .It rules
Exactly three dashes on a line, .Dq --- , will become a horizontal rule. +.It spoilers +Hide blocks of text between triple colon delimted regions. +.Bd -literal +::: warning +text that should be hidden behind a warning +::: +.Ed .El .Pp If the first line of a honk begins with
@@ -263,10 +263,10 @@ honk-v98> ./honk -datadir ../honkdata admin
honk-v98> date; ./honk -log honk.log -datadir ../honkdata .Ed .Pp -The views directory includes a sample pleroma.css to change color scheme. +The views directory includes a sample mastodon.css to change color scheme. .Bd -literal -offset indent honk-v98> mkdir ../honkdata/views -honk-v98> cp views/pleroma.css ../honkdata/views/local.css +honk-v98> cp views/mastodon.css ../honkdata/views/local.css .Ed .Pp Upgrade to the next version.
@@ -109,6 +109,7 @@ if h.Whofore == 2 || h.Whofore == 3 {
local = true } if local && h.What != "bonked" { + h.Noise = re_retag.ReplaceAllString(h.Noise, "") h.Noise = re_memes.ReplaceAllString(h.Noise, "") } h.Username, h.Handle = handles(h.Honker)@@ -151,11 +152,27 @@ }
h.Precis = demoji(h.Precis) h.Noise = demoji(h.Noise) h.Open = "open" + var misto string for _, m := range h.Mentions { if m.Where != h.Honker && !m.IsPresent(h.Noise) { - h.Noise = "(" + m.Who + ")" + h.Noise + misto += " " + m.Who } } + var mistag string + for _, o := range h.Onts { + if !OntIsPresent(o, h.Noise) { + mistag += " " + o + } + } + if len(misto) > 0 || len(mistag) > 0 { + if len(misto) > 0 { + misto = "(" + misto[1:] + ")<p>" + } + if len(mistag) > 0 { + mistag = "<p>(" + mistag[1:] + ")" + } + h.Noise = misto + h.Noise + mistag + } zap := make(map[string]bool) {@@ -186,6 +203,17 @@ data = htfilter.EscapeText(data)
data = re_emus.ReplaceAllStringFunc(data, emuxifier) io.WriteString(w, data) } + if user != nil { + htf.RetargetLink = func(href string) string { + h2 := strings.ReplaceAll(href, "/@", "/users/") + for _, m := range h.Mentions { + if h2 == m.Where || href == m.Where { + return "/h?xid=" + url.QueryEscape(m.Where) + } + } + return href + } + } p, _ := htf.String(h.Precis) n, _ := htf.String(h.Noise) h.Precis = string(p)@@ -346,7 +374,7 @@ marker.AtLinker = attoreplacer
noise = strings.TrimSpace(noise) noise = marker.Mark(noise) honk.Noise = noise - honk.Onts = oneofakind(marker.HashTags) + honk.Onts = oneofakind(append(honk.Onts, marker.HashTags...)) honk.Mentions = bunchofgrapes(marker.Mentions) }@@ -455,6 +483,7 @@ var re_memes = regexp.MustCompile("meme: ?([^\n]+)")
var re_avatar = regexp.MustCompile("avatar: ?([^\n]+)") var re_banner = regexp.MustCompile("banner: ?([^\n]+)") var re_convoy = regexp.MustCompile("convoy: ?([^\n]+)") +var re_retag = regexp.MustCompile("tags: ?([^\n]+)") var re_convalidate = regexp.MustCompile("^(https?|tag|data):") func memetize(honk *Honk) {@@ -490,6 +519,24 @@ honk.Donks = append(honk.Donks, d)
return "" } honk.Noise = re_memes.ReplaceAllStringFunc(honk.Noise, repl) +} + +func recategorize(honk *Honk) { + repl := func(x string) string { + x = x[5:] + for _, t := range strings.Split(x, " ") { + if t == "" { + continue + } + if t[0] != '#' { + t = "#" + t + } + dlog.Printf("hashtag: %s", t) + honk.Onts = append(honk.Onts, t) + } + return "" + } + honk.Noise = re_retag.ReplaceAllStringFunc(honk.Noise, repl) } var re_quickmention = regexp.MustCompile("(^|[ \n])@[[:alnum:]_]+([ \n:;.,']|$)")
@@ -10,5 +10,5 @@ github.com/mattn/go-runewidth v0.0.13
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 humungus.tedunangst.com/r/go-sqlite3 v1.1.3 - humungus.tedunangst.com/r/webs v0.6.62 + humungus.tedunangst.com/r/webs v0.6.68 )
@@ -27,5 +27,5 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= humungus.tedunangst.com/r/go-sqlite3 v1.1.3 h1:G2N4wzDS0NbuvrZtQJhh4F+3X+s7BF8b9ga8k38geUI= humungus.tedunangst.com/r/go-sqlite3 v1.1.3/go.mod h1:FtEEmQM7U2Ey1TuEEOyY1BmphTZnmiEjPsNLEAkpf/M= -humungus.tedunangst.com/r/webs v0.6.62 h1:T/T0a2xWw1cYKTMqKXwP4GStRPUfOWYytN9zCMMlqpA= -humungus.tedunangst.com/r/webs v0.6.62/go.mod h1:03R0N9BcT49HB4TDd1YmarpbiPvPzVDm74Mk4h1hYPc= +humungus.tedunangst.com/r/webs v0.6.68 h1:veKjASf1krPf4o3O7hMRsNvE4+Z6LzXVso/qMccZntk= +humungus.tedunangst.com/r/webs v0.6.68/go.mod h1:03R0N9BcT49HB4TDd1YmarpbiPvPzVDm74Mk4h1hYPc=
@@ -16,26 +16,13 @@
package main import ( - "flag" - "fmt" "html/template" - golog "log" - "log/syslog" - notrand "math/rand" - "os" "strconv" "strings" "time" "humungus.tedunangst.com/r/webs/httpsig" - "humungus.tedunangst.com/r/webs/log" ) - -var softwareVersion = "develop" - -func init() { - notrand.Seed(time.Now().Unix()) -} type WhatAbout struct { ID int64@@ -148,6 +135,31 @@ }
return strings.Contains(noise, ">@"+nick) || strings.Contains(noise, "@<span>"+nick) } +func OntIsPresent(ont, noise string) bool { + ont = strings.ToLower(ont[1:]) + idx := strings.IndexByte(noise, '#') + for idx >= 0 { + if strings.HasPrefix(noise[idx:], "#<span>") { + idx += 6 + } + idx += 1 + if idx+len(ont) >= len(noise) { + return false + } + test := noise[idx : idx+len(ont)] + test = strings.ToLower(test) + if test == ont { + return true + } + newidx := strings.IndexByte(noise[idx:], '#') + if newidx == -1 { + return false + } + idx += newidx + } + return false +} + type OldRevision struct { Precis string Noise string@@ -256,202 +268,3 @@ SomeNothing int = iota
SomeActor SomeCollection ) - -var serverName string -var serverPrefix string -var masqName string -var dataDir = "." -var viewDir = "." -var iconName = "icon.png" -var serverMsg template.HTML -var aboutMsg template.HTML -var loginMsg template.HTML - -func ElaborateUnitTests() { -} - -func unplugserver(hostname string) { - db := opendatabase() - xid := fmt.Sprintf("%%https://%s/%%", hostname) - db.Exec("delete from honkers where xid like ? and flavor = 'dub'", xid) - db.Exec("delete from doovers where rcpt like ?", xid) -} - -func reexecArgs(cmd string) []string { - args := []string{"-datadir", dataDir} - args = append(args, log.Args()...) - args = append(args, cmd) - return args -} - -var elog, ilog, dlog *golog.Logger - -func main() { - flag.StringVar(&dataDir, "datadir", dataDir, "data directory") - flag.StringVar(&viewDir, "viewdir", viewDir, "view directory") - flag.Parse() - - log.Init(log.Options{Progname: "honk", Facility: syslog.LOG_UUCP}) - elog = log.E - ilog = log.I - dlog = log.D - - args := flag.Args() - cmd := "run" - if len(args) > 0 { - cmd = args[0] - } - switch cmd { - case "init": - initdb() - case "upgrade": - upgradedb() - case "version": - fmt.Println(softwareVersion) - os.Exit(0) - } - db := opendatabase() - dbversion := 0 - getconfig("dbversion", &dbversion) - if dbversion != myVersion { - elog.Fatal("incorrect database version. run upgrade.") - } - getconfig("servermsg", &serverMsg) - getconfig("aboutmsg", &aboutMsg) - getconfig("loginmsg", &loginMsg) - getconfig("servername", &serverName) - getconfig("masqname", &masqName) - if masqName == "" { - masqName = serverName - } - serverPrefix = fmt.Sprintf("https://%s/", serverName) - getconfig("usersep", &userSep) - getconfig("honksep", &honkSep) - getconfig("devel", &develMode) - getconfig("fasttimeout", &fastTimeout) - getconfig("slowtimeout", &slowTimeout) - getconfig("signgets", &signGets) - prepareStatements(db) - switch cmd { - case "admin": - adminscreen() - case "import": - if len(args) != 4 { - elog.Fatal("import username mastodon|twitter srcdir") - } - importMain(args[1], args[2], args[3]) - case "devel": - if len(args) != 2 { - elog.Fatal("need an argument: devel (on|off)") - } - switch args[1] { - case "on": - setconfig("devel", 1) - case "off": - setconfig("devel", 0) - default: - elog.Fatal("argument must be on or off") - } - case "setconfig": - if len(args) != 3 { - elog.Fatal("need an argument: setconfig key val") - } - var val interface{} - var err error - if val, err = strconv.Atoi(args[2]); err != nil { - val = args[2] - } - setconfig(args[1], val) - case "adduser": - adduser() - case "deluser": - if len(args) < 2 { - fmt.Printf("usage: honk deluser username\n") - return - } - deluser(args[1]) - case "chpass": - if len(args) < 2 { - fmt.Printf("usage: honk chpass username\n") - return - } - 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": - arg := "30" - if len(args) > 1 { - arg = args[1] - } - cleanupdb(arg) - case "unplug": - if len(args) < 2 { - fmt.Printf("usage: honk unplug servername\n") - return - } - name := args[1] - unplugserver(name) - case "backup": - if len(args) < 2 { - fmt.Printf("usage: honk backup dirname\n") - return - } - name := args[1] - svalbard(name) - case "ping": - if len(args) < 3 { - fmt.Printf("usage: honk ping (from username) (to username or url)\n") - return - } - name := args[1] - targ := args[2] - user, err := butwhatabout(name) - if err != nil { - elog.Printf("unknown user") - return - } - ping(user, targ) - case "run": - serve() - case "backend": - backendServer() - case "test": - ElaborateUnitTests() - default: - elog.Fatal("unknown command") - } -}
@@ -338,7 +338,7 @@ } `json:"tweet"`
} var tweets []*Tweet - fd, err := os.Open(source + "/tweet.js") + fd, err := os.Open(source + "/tweets.js") if err != nil { elog.Fatal(err) }@@ -372,11 +372,6 @@ log.Printf("importing %v tweets", len(tweets))
for _, t := range tweets { xid := fmt.Sprintf("%s/%s/%s", user.URL, honkSep, t.Tweet.IdStr) if havetwid(xid) { - continue - } - - if t.Tweet.FavoriteCount == "0" || t.Tweet.FavoriteCount == "" { - log.Printf("skipping, unworthy tweet") continue }
@@ -0,0 +1,255 @@
+// +// Copyright (c) 2019 Ted Unangst <tedu@tedunangst.com> +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +package main + +import ( + "flag" + "fmt" + "html/template" + golog "log" + "log/syslog" + notrand "math/rand" + "os" + "strconv" + "time" + + "humungus.tedunangst.com/r/webs/log" +) + +var softwareVersion = "develop" + +func init() { + notrand.Seed(time.Now().Unix()) +} + +var serverName string +var serverPrefix string +var masqName string +var dataDir = "." +var viewDir = "." +var iconName = "icon.png" +var serverMsg template.HTML +var aboutMsg template.HTML +var loginMsg template.HTML + +func ElaborateUnitTests() { +} + +func unplugserver(hostname string) { + db := opendatabase() + xid := fmt.Sprintf("%%https://%s/%%", hostname) + db.Exec("delete from honkers where xid like ? and flavor = 'dub'", xid) + db.Exec("delete from doovers where rcpt like ?", xid) +} + +func reexecArgs(cmd string) []string { + args := []string{"-datadir", dataDir} + args = append(args, log.Args()...) + args = append(args, cmd) + return args +} + +var elog, ilog, dlog *golog.Logger + +func main() { + flag.StringVar(&dataDir, "datadir", dataDir, "data directory") + flag.StringVar(&viewDir, "viewdir", viewDir, "view directory") + flag.Parse() + + log.Init(log.Options{Progname: "honk", Facility: syslog.LOG_UUCP}) + elog = log.E + ilog = log.I + dlog = log.D + + if os.Geteuid() == 0 { + elog.Fatalf("do not run honk as root") + } + + args := flag.Args() + cmd := "run" + if len(args) > 0 { + cmd = args[0] + } + switch cmd { + case "init": + initdb() + case "upgrade": + upgradedb() + case "version": + fmt.Println(softwareVersion) + os.Exit(0) + } + db := opendatabase() + dbversion := 0 + getconfig("dbversion", &dbversion) + if dbversion != myVersion { + elog.Fatal("incorrect database version. run upgrade.") + } + getconfig("servermsg", &serverMsg) + getconfig("aboutmsg", &aboutMsg) + getconfig("loginmsg", &loginMsg) + getconfig("servername", &serverName) + getconfig("masqname", &masqName) + if masqName == "" { + masqName = serverName + } + serverPrefix = fmt.Sprintf("https://%s/", serverName) + getconfig("usersep", &userSep) + getconfig("honksep", &honkSep) + getconfig("devel", &develMode) + getconfig("fasttimeout", &fastTimeout) + getconfig("slowtimeout", &slowTimeout) + getconfig("signgets", &signGets) + prepareStatements(db) + switch cmd { + case "admin": + adminscreen() + case "import": + if len(args) != 4 { + elog.Fatal("import username mastodon|twitter srcdir") + } + importMain(args[1], args[2], args[3]) + case "devel": + if len(args) != 2 { + elog.Fatal("need an argument: devel (on|off)") + } + switch args[1] { + case "on": + setconfig("devel", 1) + case "off": + setconfig("devel", 0) + default: + elog.Fatal("argument must be on or off") + } + case "setconfig": + if len(args) != 3 { + elog.Fatal("need an argument: setconfig key val") + } + var val interface{} + var err error + if val, err = strconv.Atoi(args[2]); err != nil { + val = args[2] + } + setconfig(args[1], val) + case "adduser": + adduser() + case "deluser": + if len(args) < 2 { + fmt.Printf("usage: honk deluser username\n") + return + } + deluser(args[1]) + case "chpass": + if len(args) < 2 { + fmt.Printf("usage: honk chpass username\n") + return + } + 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 "sendmsg": + if len(args) < 4 { + fmt.Printf("usage: honk send username filename rcpt\n") + return + } + user, err := butwhatabout(args[1]) + if err != nil { + fmt.Printf("user not found\n") + return + } + data, err := os.ReadFile(args[2]) + if err != nil { + fmt.Printf("can't read file\n") + return + } + deliverate(user.ID, args[3], data) + case "cleanup": + arg := "30" + if len(args) > 1 { + arg = args[1] + } + cleanupdb(arg) + case "unplug": + if len(args) < 2 { + fmt.Printf("usage: honk unplug servername\n") + return + } + name := args[1] + unplugserver(name) + case "backup": + if len(args) < 2 { + fmt.Printf("usage: honk backup dirname\n") + return + } + name := args[1] + svalbard(name) + case "ping": + if len(args) < 3 { + fmt.Printf("usage: honk ping (from username) (to username or url)\n") + return + } + name := args[1] + targ := args[2] + user, err := butwhatabout(name) + if err != nil { + elog.Printf("unknown user") + return + } + ping(user, targ) + case "run": + serve() + case "backend": + backendServer() + case "test": + ElaborateUnitTests() + default: + elog.Fatal("unknown command") + } +}
@@ -1,5 +1,4 @@
- -create table honks (honkid integer primary key, userid integer, what text, honker text, xid text, rid text, dt text, url text, audience text, noise text, convoy text, whofore integer, format text, precis text, oonker text, flags integer); +create table honks (honkid integer primary key, userid integer, what text, honker text, xid text, rid text, dt text, url text, audience text, noise text, convoy text, whofore integer, format text, precis text, oonker text, flags integer, plain text); create table chonks (chonkid integer primary key, userid integer, xid text, who txt, target text, dt text, noise text, format text); create table donks (honkid integer, chonkid integer, fileid integer); create table filemeta (fileid integer primary key, xid text, name text, description text, url text, media text, local integer);
@@ -16,11 +16,39 @@
package main import ( + "runtime/debug" "syscall" "time" "github.com/dustin/go-humanize" ) + +func init() { + if softwareVersion != "develop" { + return + } + bi, ok := debug.ReadBuildInfo() + if !ok { + return + } + var vcs, rev, mod string + for _, bs := range bi.Settings { + if bs.Key == "vcs" { + vcs = "/" + bs.Value + } + if bs.Key == "vcs.revision" { + rev = bs.Value + if len(rev) > 12 { + rev = rev[:12] + } + rev = "-" + rev + } + if bs.Key == "vcs.modified" && bs.Value == "true" { + mod = "+" + } + } + softwareVersion += vcs + rev + mod +} type Sensors struct { Memory float64
@@ -18,9 +18,12 @@
import ( "database/sql" "os" + "strings" + + "humungus.tedunangst.com/r/webs/htfilter" ) -var myVersion = 43 +var myVersion = 45 type dbexecer interface { Exec(query string, args ...interface{}) (sql.Result, error)@@ -42,6 +45,22 @@
if dbversion < 40 { elog.Fatal("database is too old to upgrade") } + var err error + var tx *sql.Tx + try := func(s string, args ...interface{}) { + if tx != nil { + _, err = tx.Exec(s, args...) + } else { + _, err = db.Exec(s, args...) + } + if err != nil { + elog.Fatalf("can't run %s: %s", s, err) + } + } + setV := func(ver int64) { + try("update config set value = ? where key = 'dbversion'", ver) + } + switch dbversion { case 40: doordie(db, "PRAGMA journal_mode=WAL")@@ -87,6 +106,72 @@ doordie(db, "delete from honkmeta where genus = 'wonkles' or genus = 'guesses'")
doordie(db, "update config set value = 43 where key = 'dbversion'") fallthrough case 43: + try("alter table honks add column plain text") + try("update honks set plain = ''") + setV(44) + fallthrough + case 44: + makeplain := func(noise, precis, format string) []string { + var plain []string + var filt htfilter.Filter + filt.WithLinks = true + if precis != "" { + t, _ := filt.TextOnly(precis) + plain = append(plain, t) + } + if format == "html" { + t, _ := filt.TextOnly(noise) + plain = append(plain, t) + } else { + plain = append(plain, noise) + } + return plain + } + tx, err = db.Begin() + if err != nil { + elog.Fatal(err) + } + plainmap := make(map[int64][]string) + rows, err := tx.Query("select honkid, noise, precis, format from honks") + if err != nil { + elog.Fatal(err) + } + for rows.Next() { + var honkid int64 + var noise, precis, format string + err = rows.Scan(&honkid, &noise, &precis, &format) + if err != nil { + elog.Fatal(err) + } + plainmap[honkid] = makeplain(noise, precis, format) + } + rows.Close() + rows, err = tx.Query("select honkid, name, description from donks join filemeta on donks.fileid = filemeta.fileid") + if err != nil { + elog.Fatal(err) + } + for rows.Next() { + var honkid int64 + var name, desc string + err = rows.Scan(&honkid, &name, &desc) + if err != nil { + elog.Fatal(err) + } + plainmap[honkid] = append(plainmap[honkid], name) + plainmap[honkid] = append(plainmap[honkid], desc) + } + rows.Close() + for honkid, plain := range plainmap { + try("update honks set plain = ? where honkid = ?", strings.Join(plain, " "), honkid) + } + setV(45) + err = tx.Commit() + if err != nil { + elog.Fatal(err) + } + tx = nil + fallthrough + case 45: default: elog.Fatalf("can't upgrade unknown version %d", dbversion)
@@ -51,7 +51,7 @@ </header>
<p> {{ if .HTPrecis }} <details class="noise"> -<summary>{{ .HTPrecis }}<p></summary> +<summary class="noise">{{ .HTPrecis }}<p></summary> {{ end }} <p class="content">{{ .HTML }} {{ with .Time }}@@ -71,7 +71,7 @@ {{ else }}
{{ if $omitimages }} <p><a href="/d/{{ .XID }}">Image: {{ .Name }}</a>{{ if not (eq .Desc .Name) }} {{ .Desc }}{{ end }} {{ else }} -<p><img src="/d/{{ .XID }}" title="{{ .Desc }}" alt="{{ .Desc }}"> +<p><img src="/d/{{ .XID }}" loading=lazy title="{{ .Desc }}" alt="{{ .Desc }}"> {{ end }} {{ end }} {{ else }}
@@ -0,0 +1,7 @@
+html { + --bg-page: #353a49; + --bg-dark: #393f4f; + --fg: #fff; + --hl: #6364ff; + --fg-subtle: #606984 +}
@@ -1,7 +0,0 @@
-html { - --bg-page: #1b2735; - --bg-dark: #121a24; - --fg: #b9b9ba; - --hl: #d8a070; - --fg-subtle: rgba(185, 185, 186, 0.5); -}
@@ -190,7 +190,7 @@ padding: 0.5em;
font-size: 1em; background: var(--bg-page); color: var(--fg); - width: 600px; + width: 100%; height: 4em; margin-bottom: 0.5em; box-sizing: border-box;@@ -359,7 +359,7 @@ }
.limited details.actions summary { color: var(--fg-limited); } -details.noise[open] summary { +details.noise[open] summary.noise { display: none; } h1, h2 {@@ -447,10 +447,8 @@ .noise img:not(.emu) {
display: block; } img.emu { - width: 2em; height: 2em; vertical-align: middle; - margin: -2px; object-fit: contain; } .nophone {
@@ -603,7 +603,7 @@ rows.Scan(&xid)
honkers = append(honkers, Honker{XID: xid}) } rows.Close() - for i, _ := range honkers { + for i := range honkers { _, honkers[i].Handle = handles(honkers[i].XID) } templinfo := getInfo(r)@@ -734,6 +734,7 @@ templinfo["PageArg"] = name
templinfo["Name"] = user.Name templinfo["WhatAbout"] = user.HTAbout templinfo["ServerMessage"] = "" + templinfo["APAltLink"] = templates.Sprintf("<link href='%s' rel='alternate' type='application/activity+json'>", user.URL) if u != nil { templinfo["HonkCSRF"] = login.GetCSRF("honkhonk", r) }@@ -1768,6 +1769,7 @@ noise = hooterize(noise)
honk.Noise = noise precipitate(honk) noise = honk.Noise + recategorize(honk) translate(honk) if rid != "" {@@ -2592,7 +2594,8 @@ if d == nil {
http.Error(w, "missing donk", http.StatusBadRequest) return } - w.Write([]byte(d.XID)) + donkxid := fmt.Sprintf("%s:%d", d.XID, d.FileID) + w.Write([]byte(donkxid)) case "zonkit": zonkit(w, r) case "gethonks":@@ -2658,6 +2661,9 @@ }
} func fiveoh(w http.ResponseWriter, r *http.Request) { + if !develMode { + return + } fd, err := os.OpenFile("violations.json", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { elog.Printf("error opening violations! %s", err)@@ -2750,6 +2756,7 @@ go enditall()
go redeliverator() go tracker() go bgmonitor() + go qotd() loadLingo() emuinit()