Compare commits

...

16 Commits

Author SHA1 Message Date
Ted Unangst 50524cade2 just be explicit about match checking 2023-02-03 22:48:19 -05:00
Ted Unangst 91db81b44d more flexible regex for quick mentions, reported by dirk 2023-02-03 22:37:19 -05:00
Ted Unangst 59e07ff522 csp fix from tim 2023-01-27 15:42:50 -05:00
Ted Unangst 84312b409c need to add this 2023-01-27 12:39:17 -05:00
Ted Unangst 6957a4d950 i failed to add 2023-01-27 00:21:16 -05:00
Ted Unangst a1574411aa fixes for peekses 2023-01-27 00:20:27 -05:00
Ted Unangst 6883c5b6b9 mention new features 2023-01-26 16:40:03 -05:00
Ted Unangst a45e27038f make quote inlining an option 2023-01-26 16:38:52 -05:00
Ted Unangst 455989f85b add an emu peeker to the honkform.
from petersanchez, adapted to new csp rules
2023-01-26 16:27:11 -05:00
Ted Unangst 2983156e4b apply CSP patches. no more inline script or css.
from timkuijsten
2023-01-26 15:56:48 -05:00
Ted Unangst 78a58539ea allow subs to resub, from xwatt 2023-01-24 19:10:17 -05:00
Ted Unangst f4dfdcea25 allow . to match all summaries 2023-01-24 19:08:26 -05:00
Ted Unangst 9539575678 more masto stompage 2023-01-24 17:19:25 -05:00
Ted Unangst ebf1000f1c the mastodon made me do it; i am blameless 2023-01-24 17:13:49 -05:00
Ted Unangst 32ed638147 some basic qt support 2023-01-24 14:46:55 -05:00
Ted Unangst be858a970a fix http sigs for gets and adjacent mentions 2023-01-23 12:52:27 -05:00
25 changed files with 461 additions and 136 deletions

View File

@ -27,6 +27,7 @@ import (
"net/http"
"net/url"
"os"
"regexp"
"strings"
"time"
@ -506,6 +507,39 @@ func firstofmany(obj junk.Junk, key string) string {
return ""
}
var re_mast0link = regexp.MustCompile(`https://[[:alnum:].]+/users/[[:alnum:]]+/statuses/[[:digit:]]+`)
var re_masto1ink = regexp.MustCompile(`https://[[:alnum:].]+/@[[:alnum:]]+/[[:digit:]]+`)
var re_misslink = regexp.MustCompile(`https://[[:alnum:].]+/notes/[[:alnum:]]+`)
var re_honklink = regexp.MustCompile(`https://[[:alnum:].]+/u/[[:alnum:]]+/h/[[:alnum:]]+`)
var re_romalink = regexp.MustCompile(`https://[[:alnum:].]+/objects/[[:alnum:]-]+`)
var re_qtlinks = regexp.MustCompile(`>https://[^\s<]+<`)
func qutify(user *WhatAbout, content string) string {
// well this is gross
malcontent := strings.ReplaceAll(content, `</span><span class="ellipsis">`, "")
malcontent = strings.ReplaceAll(malcontent, `</span><span class="invisible">`, "")
mlinks := re_qtlinks.FindAllString(malcontent, -1)
for _, m := range mlinks {
m = m[1 : len(m)-1]
dlog.Printf("consider qt: %s", m)
if re_mast0link.MatchString(m) ||
re_masto1ink.MatchString(m) ||
re_misslink.MatchString(m) ||
re_honklink.MatchString(m) ||
re_romalink.MatchString(m) {
j, err := GetJunk(user.ID, m)
dlog.Printf("fetched %s: %s", m, err)
if err == nil {
q, ok := j.GetString("content")
if ok {
content = fmt.Sprintf("%s<blockquote>%s</blockquote>", content, q)
}
}
}
}
return content
}
func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
depth := 0
maxdepth := 10
@ -690,9 +724,11 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
return nil
}
if originate(xid) != origin {
ilog.Printf("original sin: %s not from %s", xid, origin)
item.Write(ilog.Writer())
return nil
if !develMode && origin != "" {
ilog.Printf("original sin: %s not from %s", xid, origin)
item.Write(ilog.Writer())
return nil
}
}
var xonk Honk
@ -742,6 +778,9 @@ func xonksaver(user *WhatAbout, item junk.Junk, origin string) *Honk {
content += fmt.Sprintf(`<p><a href="%s">%s</a>`, url, url)
url = xid
}
if user.Options.InlineQuotes {
content = qutify(user, content)
}
rid, ok = obj.GetString("inReplyTo")
if !ok {
if robj, ok := obj.GetMap("inReplyTo"); ok {
@ -1904,7 +1943,7 @@ func followyou2(user *WhatAbout, j junk.Junk) {
ilog.Printf("updating honker accept: %s", who)
db := opendatabase()
row := db.QueryRow("select name, folxid from honkers where userid = ? and xid = ? and flavor in ('presub')",
row := db.QueryRow("select name, folxid from honkers where userid = ? and xid = ? and flavor in ('presub', 'sub')",
user.ID, who)
var name, folxid string
err := row.Scan(&name, &folxid)

View File

@ -2,6 +2,18 @@ changelog
=== next
+ Emu peeker
+ CSP compliance
+ Filter to match anything with summary/warning.
+ Start collecting quties.
+ Fix http signatures for GET requests.
+ Fix adjacent mentions.
+ Fix argv for chpass.
+ Avoid self mention in reply all.

View File

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

View File

@ -139,6 +139,9 @@ A 24 hour clock is assumed, unless am or pm are specified.
The duration is optional and may be specified as XdYhZm for X days, Y hours,
and Z minutes (1d12h would be a 36 hour event).
.Pp
Clicking the pretty circle face will open the emu peeker to add in the
selection of emus.
.Pp
When everything is at last ready to go, press the
.Dq it's gonna be honked
button.

5
fun.go
View File

@ -457,7 +457,7 @@ func memetize(honk *Honk) {
honk.Noise = re_memes.ReplaceAllStringFunc(honk.Noise, repl)
}
var re_quickmention = regexp.MustCompile("(^|[ \n])@[[:alnum:]]+([ \n.]|$)")
var re_quickmention = regexp.MustCompile("(^|[ \n])@[[:alnum:]]+([ \n.,']|$)")
func quickrename(s string, userid int64) string {
nonstop := true
@ -472,7 +472,8 @@ func quickrename(s string, userid int64) string {
prefix += "@"
m = m[1:]
tail := ""
if last := m[len(m)-1]; last == ' ' || last == '\n' || last == '.' {
if last := m[len(m)-1]; last == ' ' || last == '\n' ||
last == '.' || last == ',' || last == '\'' {
tail = m[len(m)-1:]
m = m[:len(m)-1]
}

2
go.mod
View File

@ -9,5 +9,5 @@ require (
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
humungus.tedunangst.com/r/go-sqlite3 v1.1.3
humungus.tedunangst.com/r/webs v0.6.59
humungus.tedunangst.com/r/webs v0.6.60
)

4
go.sum
View File

@ -25,5 +25,5 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
humungus.tedunangst.com/r/go-sqlite3 v1.1.3 h1:G2N4wzDS0NbuvrZtQJhh4F+3X+s7BF8b9ga8k38geUI=
humungus.tedunangst.com/r/go-sqlite3 v1.1.3/go.mod h1:FtEEmQM7U2Ey1TuEEOyY1BmphTZnmiEjPsNLEAkpf/M=
humungus.tedunangst.com/r/webs v0.6.59 h1:zOlBGZqrRo22QND1CN8HbhEv3JwoiZkspi7TgVuJS0s=
humungus.tedunangst.com/r/webs v0.6.59/go.mod h1:03R0N9BcT49HB4TDd1YmarpbiPvPzVDm74Mk4h1hYPc=
humungus.tedunangst.com/r/webs v0.6.60 h1:2PjVTVH3js4PXv8lrEw7nxtRmwwt1COl7t7tZMPxBPs=
humungus.tedunangst.com/r/webs v0.6.60/go.mod h1:03R0N9BcT49HB4TDd1YmarpbiPvPzVDm74Mk4h1hYPc=

11
hfcs.go
View File

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

19
honk.go
View File

@ -51,15 +51,16 @@ type WhatAbout struct {
}
type UserOptions struct {
SkinnyCSS bool `json:",omitempty"`
OmitImages bool `json:",omitempty"`
MentionAll bool `json:",omitempty"`
Avatar string `json:",omitempty"`
Banner string `json:",omitempty"`
MapLink string `json:",omitempty"`
Reaction string `json:",omitempty"`
MeCount int64
ChatCount int64
SkinnyCSS bool `json:",omitempty"`
OmitImages bool `json:",omitempty"`
MentionAll bool `json:",omitempty"`
InlineQuotes bool `json:",omitempty"`
Avatar string `json:",omitempty"`
Banner string `json:",omitempty"`
MapLink string `json:",omitempty"`
Reaction string `json:",omitempty"`
MeCount int64
ChatCount int64
}
type KeyInfo struct {

View File

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

View File

@ -14,7 +14,8 @@
<input tabindex=1 type="checkbox" id="omitimages" name="omitimages" value="omitimages" {{ if .User.Options.OmitImages }}checked{{ end }}><span></span>
<p><label class="button" for="mentionall">mention all:</label>
<input tabindex=1 type="checkbox" id="mentionall" name="mentionall" value="mentionall" {{ if .User.Options.MentionAll }}checked{{ end }}><span></span>
<p><label class="button" for="inlineqts">inline quotes:</label>
<input tabindex=1 type="checkbox" id="inlineqts" name="inlineqts" value="inlineqts" {{ if .User.Options.InlineQuotes }}checked{{ end }}><span></span>
<p><label class="button" for="maps">apple map links:</label>
<input tabindex=1 type="checkbox" id="maps" name="maps" value="apple" {{ if eq "apple" .User.Options.MapLink }}checked{{ end }}><span></span>
<p><label class="button" for="reaction">reaction:</label>

View File

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

View File

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

1
views/emus.html Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

25
views/misc.js Normal file
View File

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

View File

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

View File

@ -253,6 +253,11 @@ input[type=file] {
.honk details.actions summary {
color: var(--fg-subtle);
}
#emupicker{height:300px;overflow-y:scroll;padding:3px;background:var(--bg-dark);border:solid 5px var(--fg-subtle);text-align:center;display:none;}
#emupicker img{margin:0;}
.emuload{background:var(--bg-page);padding:0.5em;}
.subtle .noise {
color: var(--fg-subtle);
font-size: 0.8em;
@ -340,3 +345,44 @@ img.emu {
--fg-limited: #a79;
}
}
/*
* CSP: style-src: self
*/
li.details {
list-style-type: none;
margin-left: -1em;
}
.left1em {
margin-left: 1em;
}
.hide {
display: none;
}
.textright {
text-align: right;
}
.font08em {
font-size: 0.8em;
}
.font18em {
font-size: 1.8em;
}
.wsnowrap {
white-space: nowrap;
}
.skinny main {
max-width: 700px;
}
.fontmonospace {
font-family: monospace;
}

View File

@ -17,7 +17,10 @@ export function addguesscontrols(elem, word, wordlist, xid) {
}
host.validGuesses = validguesses
var div = document.createElement( 'div' );
div.innerHTML = "<p><input> <button onclick='return makeaguess(this)'>guess</button>"
div.innerHTML = "<p><input> <button>guess</button>"
div.querySelector('button').onclick = function() {
makeaguess(this)
}
host.append(div)
elem.remove()
}
@ -57,7 +60,7 @@ export function makeaguess(btn) {
}
var div = document.createElement( 'div' );
div.innerHTML = "<p style='font-family: monospace'>" + res
div.innerHTML = "<p class='fontmonospace'>" + res
host.append(div)
host.guesses.push(obfu)
} else {
@ -76,7 +79,10 @@ export function makeaguess(btn) {
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>"
div.innerHTML = "<p><input> <button>guess</button>"
div.querySelector('button').onclick = function() {
makeaguess(this)
}
}
host.append(div)
btn.parentElement.remove()

65
web.go
View File

@ -50,16 +50,18 @@ var honkSep = "h"
var develMode = false
func getuserstyle(u *login.UserInfo) template.CSS {
var allemus []Emu
func getuserstyle(u *login.UserInfo) template.HTMLAttr {
if u == nil {
return ""
}
user, _ := butwhatabout(u.Username)
css := template.CSS("")
class := template.HTMLAttr("")
if user.Options.SkinnyCSS {
css += "main { max-width: 700px; }\n"
class += `class="skinny"`
}
return css
return class
}
func getmaplink(u *login.UserInfo) string {
@ -80,6 +82,7 @@ func getInfo(r *http.Request) map[string]interface{} {
templinfo["LocalStyleParam"] = getassetparam(dataDir + "/views/local.css")
templinfo["JSParam"] = getassetparam(viewDir + "/views/honkpage.js")
templinfo["LocalJSParam"] = getassetparam(dataDir + "/views/local.js")
templinfo["MiscJSParam"] = getassetparam(dataDir + "/views/misc.js")
templinfo["ServerName"] = serverName
templinfo["IconName"] = iconName
templinfo["UserSep"] = userSep
@ -148,6 +151,15 @@ func homepage(w http.ResponseWriter, r *http.Request) {
honkpage(w, u, honks, templinfo)
}
func showemus(w http.ResponseWriter, r *http.Request) {
templinfo := getInfo(r)
templinfo["Emus"] = allemus
err := readviews.Execute(w, "emus.html", templinfo)
if err != nil {
elog.Print(err)
}
}
func showfunzone(w http.ResponseWriter, r *http.Request) {
var emunames, memenames []string
emuext := make(map[string]string)
@ -1122,6 +1134,11 @@ func saveuser(w http.ResponseWriter, r *http.Request) {
} else {
options.MentionAll = false
}
if r.FormValue("inlineqts") == "inlineqts" {
options.InlineQuotes = true
} else {
options.InlineQuotes = false
}
if r.FormValue("maps") == "apple" {
options.MapLink = "apple"
} else {
@ -1429,7 +1446,7 @@ func edithonkpage(w http.ResponseWriter, r *http.Request) {
templinfo["Noise"] = noise
templinfo["SavedPlace"] = honk.Place
if tm := honk.Time; tm != nil {
templinfo["ShowTime"] = ";"
templinfo["ShowTime"] = " "
templinfo["StartTime"] = tm.StartTime.Format("2006-01-02 15:04")
if tm.Duration != 0 {
templinfo["Duration"] = tm.Duration
@ -1751,7 +1768,7 @@ func submithonk(w http.ResponseWriter, r *http.Request) *Honk {
templinfo["Noise"] = r.FormValue("noise")
templinfo["SavedFile"] = donkxid
if tm := honk.Time; tm != nil {
templinfo["ShowTime"] = ";"
templinfo["ShowTime"] = " "
templinfo["StartTime"] = tm.StartTime.Format("2006-01-02 15:04")
if tm.Duration != 0 {
templinfo["Duration"] = tm.Duration
@ -2414,6 +2431,37 @@ func bgmonitor() {
}
}
func addcspheaders(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Security-Policy", "default-src 'none'; script-src 'self'; connect-src 'self'; style-src 'self'; img-src 'self'; report-uri /csp-violation")
next.ServeHTTP(w, r)
})
}
func emuinit() {
var emunames []string
dir, err := os.Open(dataDir + "/emus")
if err == nil {
emunames, _ = dir.Readdirnames(0)
dir.Close()
}
for _, e := range emunames {
if len(e) <= 4 {
continue
}
ext := e[len(e)-4:]
emu := Emu{
ID: fmt.Sprintf("/emu/%s", e),
Name: e[:len(e)-4],
Type: "image/" + ext[1:],
}
allemus = append(allemus, emu)
}
sort.Slice(allemus, func(i, j int) bool {
return allemus[i].Name < allemus[j].Name
})
}
func serve() {
db := opendatabase()
login.Init(login.InitArgs{Db: db, Logger: ilog, Insecure: develMode, SameSiteStrict: !develMode})
@ -2428,6 +2476,7 @@ func serve() {
go tracker()
go bgmonitor()
loadLingo()
emuinit()
readviews = templates.Load(develMode,
viewDir+"/views/honkpage.html",
@ -2446,6 +2495,7 @@ func serve() {
viewDir+"/views/msg.html",
viewDir+"/views/header.html",
viewDir+"/views/onts.html",
viewDir+"/views/emus.html",
viewDir+"/views/honkpage.js",
)
if !develMode {
@ -2466,6 +2516,7 @@ func serve() {
}
mux := mux.NewRouter()
mux.Use(addcspheaders)
mux.Use(login.Checker)
mux.Handle("/api", login.TokenRequired(http.HandlerFunc(apihandler)))
@ -2503,6 +2554,7 @@ func serve() {
getters.HandleFunc("/style.css", serveviewasset)
getters.HandleFunc("/honkpage.js", serveviewasset)
getters.HandleFunc("/wonk.js", serveviewasset)
getters.HandleFunc("/misc.js", serveviewasset)
getters.HandleFunc("/local.css", servedataasset)
getters.HandleFunc("/local.js", servedataasset)
getters.HandleFunc("/icon.png", servedataasset)
@ -2545,6 +2597,7 @@ func serve() {
loggedin.HandleFunc("/t", showconvoy)
loggedin.HandleFunc("/q", showsearch)
loggedin.HandleFunc("/hydra", webhydra)
loggedin.HandleFunc("/emus", showemus)
loggedin.Handle("/submithonker", login.CSRFWrap("submithonker", http.HandlerFunc(submithonker)))
err = http.Serve(listener, mux)