Compare commits
51 Commits
887bf8e8a6
...
58ecc76b90
Author | SHA1 | Date |
---|---|---|
Ted Unangst | 58ecc76b90 | |
Ted Unangst | 386dc6f798 | |
Ted Unangst | dbbd4468fa | |
Ted Unangst | c750122b7f | |
Ted Unangst | 82c7e22193 | |
Ted Unangst | 850ec873a8 | |
Ted Unangst | 90bef0b0b9 | |
Ted Unangst | 81bb8099ae | |
Ted Unangst | 1a00ca5b43 | |
Ted Unangst | 281570fe6b | |
Ted Unangst | ae3843690f | |
Ted Unangst | 0a66a0e13a | |
Ted Unangst | b60e2e6156 | |
Ted Unangst | 99aed03a36 | |
Ted Unangst | 8380a32139 | |
Ted Unangst | 7dc99cec9b | |
Ted Unangst | 1a1c56200c | |
Ted Unangst | 76da4515bc | |
Ted Unangst | a6bc763507 | |
Ted Unangst | 15572f2e21 | |
Ted Unangst | a4817e149b | |
Ted Unangst | aad5243bb1 | |
Ted Unangst | b539f292a6 | |
Ted Unangst | cc3976daa6 | |
Ted Unangst | d69156f96e | |
Ted Unangst | b360a01719 | |
Ted Unangst | 3bbd1ad2b3 | |
Ted Unangst | a4be8b9e02 | |
Ted Unangst | 0dd17d0038 | |
Ted Unangst | 1b36e72cc1 | |
Ted Unangst | 314949d045 | |
Ted Unangst | db63bd3fc3 | |
Ted Unangst | 86738e30b8 | |
Ted Unangst | 0ba146c00c | |
Ted Unangst | 2c5a051376 | |
Ted Unangst | 17f2f729ee | |
Ted Unangst | 6f5bf49fca | |
Ted Unangst | bddd8554a2 | |
Ted Unangst | dbff9a6151 | |
Ted Unangst | 4f0d15ebb5 | |
Ted Unangst | 634d74d139 | |
Ted Unangst | 93a79455d2 | |
Ted Unangst | f7cd268fe2 | |
Ted Unangst | 57e34c344d | |
Ted Unangst | f4c4c8a917 | |
Ted Unangst | 773eb649a1 | |
Ted Unangst | 9658fb8605 | |
Ted Unangst | 20699112a0 | |
Ted Unangst | 842816f1bf | |
Ted Unangst | 1910279c6b | |
Ted Unangst | 0e4a87c523 |
|
@ -7,3 +7,11 @@ memes
|
||||||
emus
|
emus
|
||||||
honk
|
honk
|
||||||
violations.json
|
violations.json
|
||||||
|
docs/activitypub.7.html
|
||||||
|
docs/hfcs.1.html
|
||||||
|
docs/honk.1.html
|
||||||
|
docs/honk.3.html
|
||||||
|
docs/honk.5.html
|
||||||
|
docs/honk.8.html
|
||||||
|
docs/intro.1.html
|
||||||
|
docs/vim.3.html
|
||||||
|
|
2
.hgtags
2
.hgtags
|
@ -39,3 +39,5 @@ bc1bcfb9c0cc86b3c63325b07e13a36b9d4500f0 v0.9.7
|
||||||
4b8cf31560b7d1e1696af109b158766c4ce823ab v0.9.9
|
4b8cf31560b7d1e1696af109b158766c4ce823ab v0.9.9
|
||||||
d7c3a01e7aaef67c40920bbc4e8507350fc33e31 v0.9.91
|
d7c3a01e7aaef67c40920bbc4e8507350fc33e31 v0.9.91
|
||||||
b1e7ac92a58a7183310b1a5cca8222d65f242d81 v1.0.0
|
b1e7ac92a58a7183310b1a5cca8222d65f242d81 v1.0.0
|
||||||
|
36c2a2746133f4b5b31103c0b4232554d2b15a5d v1.1.0
|
||||||
|
135cdbfa6d7d1a9b1b436cb57d9837a943d83227 v1.1.1
|
||||||
|
|
5
Makefile
5
Makefile
|
@ -7,6 +7,11 @@ honk: .preflightcheck schema.sql *.go go.mod
|
||||||
.preflightcheck: preflight.sh
|
.preflightcheck: preflight.sh
|
||||||
@sh ./preflight.sh
|
@sh ./preflight.sh
|
||||||
|
|
||||||
|
help:
|
||||||
|
for m in docs/*.[13578] ; do \
|
||||||
|
mandoc -T html -O style=mandoc.css,man=%N.%S.html $$m | sed -E 's/<a class="Lk" href="([[:alnum:]._-]*)">/<img src="\1"><br>/g' > $$m.html ; \
|
||||||
|
done
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f honk
|
rm -f honk
|
||||||
|
|
||||||
|
|
2
README
2
README
|
@ -20,7 +20,7 @@ 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.
|
It should be sufficient to type make after unpacking a release.
|
||||||
You'll need a go compiler version 1.16 or later. And libsqlite3.
|
You'll need a go compiler version 1.18 or later. And libsqlite3.
|
||||||
|
|
||||||
Even on a fast machine, building from source can take several seconds.
|
Even on a fast machine, building from source can take several seconds.
|
||||||
|
|
||||||
|
|
36
activity.go
36
activity.go
|
@ -59,12 +59,14 @@ func friendorfoe(ct string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var develClient = &http.Client{
|
var honkClient = http.Client{}
|
||||||
Transport: &http.Transport{
|
|
||||||
|
func gogglesDoNothing() {
|
||||||
|
honkClient.Transport = &http.Transport{
|
||||||
TLSClientConfig: &tls.Config{
|
TLSClientConfig: &tls.Config{
|
||||||
InsecureSkipVerify: true,
|
InsecureSkipVerify: true,
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func PostJunk(keyname string, key httpsig.PrivateKey, url string, j junk.Junk) error {
|
func PostJunk(keyname string, key httpsig.PrivateKey, url string, j junk.Junk) error {
|
||||||
|
@ -72,10 +74,6 @@ func PostJunk(keyname string, key httpsig.PrivateKey, url string, j junk.Junk) e
|
||||||
}
|
}
|
||||||
|
|
||||||
func PostMsg(keyname string, key httpsig.PrivateKey, url string, msg []byte) error {
|
func PostMsg(keyname string, key httpsig.PrivateKey, url string, msg []byte) error {
|
||||||
client := http.DefaultClient
|
|
||||||
if develMode {
|
|
||||||
client = develClient
|
|
||||||
}
|
|
||||||
req, err := http.NewRequest("POST", url, bytes.NewReader(msg))
|
req, err := http.NewRequest("POST", url, bytes.NewReader(msg))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -86,7 +84,7 @@ func PostMsg(keyname string, key httpsig.PrivateKey, url string, msg []byte) err
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 2*slowTimeout*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 2*slowTimeout*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
req = req.WithContext(ctx)
|
req = req.WithContext(ctx)
|
||||||
resp, err := client.Do(req)
|
resp, err := honkClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -130,13 +128,10 @@ func GetJunkHardMode(userid int64, url string) (junk.Junk, error) {
|
||||||
|
|
||||||
var flightdeck = gate.NewSerializer()
|
var flightdeck = gate.NewSerializer()
|
||||||
|
|
||||||
var signGets = true
|
|
||||||
|
|
||||||
func GetJunkTimeout(userid int64, url string, timeout time.Duration) (junk.Junk, error) {
|
func GetJunkTimeout(userid int64, url string, timeout time.Duration) (junk.Junk, error) {
|
||||||
if rejectorigin(userid, url, false) {
|
if rejectorigin(userid, url, false) {
|
||||||
return nil, fmt.Errorf("rejected origin: %s", url)
|
return nil, fmt.Errorf("rejected origin: %s", url)
|
||||||
}
|
}
|
||||||
client := http.DefaultClient
|
|
||||||
sign := func(req *http.Request) error {
|
sign := func(req *http.Request) error {
|
||||||
var ki *KeyInfo
|
var ki *KeyInfo
|
||||||
ok := ziggies.Get(userid, &ki)
|
ok := ziggies.Get(userid, &ki)
|
||||||
|
@ -146,9 +141,18 @@ func GetJunkTimeout(userid int64, url string, timeout time.Duration) (junk.Junk,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if develMode {
|
if develMode {
|
||||||
client = develClient
|
|
||||||
sign = nil
|
sign = nil
|
||||||
}
|
}
|
||||||
|
client := honkClient
|
||||||
|
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||||
|
if len(via) >= 5 {
|
||||||
|
return fmt.Errorf("stopped after 5 redirects")
|
||||||
|
}
|
||||||
|
if sign != nil {
|
||||||
|
sign(req)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
fn := func() (interface{}, error) {
|
fn := func() (interface{}, error) {
|
||||||
at := theonetruename
|
at := theonetruename
|
||||||
if strings.Contains(url, ".well-known/webfinger?resource") {
|
if strings.Contains(url, ".well-known/webfinger?resource") {
|
||||||
|
@ -158,7 +162,7 @@ func GetJunkTimeout(userid int64, url string, timeout time.Duration) (junk.Junk,
|
||||||
Accept: at,
|
Accept: at,
|
||||||
Agent: "honksnonk/5.0; " + serverName,
|
Agent: "honksnonk/5.0; " + serverName,
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
Client: client,
|
Client: &client,
|
||||||
Fixup: sign,
|
Fixup: sign,
|
||||||
})
|
})
|
||||||
return j, err
|
return j, err
|
||||||
|
@ -173,10 +177,6 @@ func GetJunkTimeout(userid int64, url string, timeout time.Duration) (junk.Junk,
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchsome(url string) ([]byte, error) {
|
func fetchsome(url string) ([]byte, error) {
|
||||||
client := http.DefaultClient
|
|
||||||
if develMode {
|
|
||||||
client = develClient
|
|
||||||
}
|
|
||||||
req, err := http.NewRequest("GET", url, nil)
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ilog.Printf("error fetching %s: %s", url, err)
|
ilog.Printf("error fetching %s: %s", url, err)
|
||||||
|
@ -186,7 +186,7 @@ func fetchsome(url string) ([]byte, error) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
req = req.WithContext(ctx)
|
req = req.WithContext(ctx)
|
||||||
resp, err := client.Do(req)
|
resp, err := honkClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ilog.Printf("error fetching %s: %s", url, err)
|
ilog.Printf("error fetching %s: %s", url, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
25
backend.go
25
backend.go
|
@ -83,6 +83,31 @@ func imageFromSVG(data []byte) (*image.Image, error) {
|
||||||
return svg, nil
|
return svg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func bigshrink(data []byte) (*image.Image, error) {
|
||||||
|
if isSVG(data) {
|
||||||
|
return imageFromSVG(data)
|
||||||
|
}
|
||||||
|
cl, err := rpc.Dial("unix", backendSockname())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer cl.Close()
|
||||||
|
var res ShrinkerResult
|
||||||
|
err = cl.Call("Shrinker.Shrink", &ShrinkerArgs{
|
||||||
|
Buf: data,
|
||||||
|
Params: image.Params{
|
||||||
|
LimitSize: 14200 * 4200,
|
||||||
|
MaxWidth: 2600,
|
||||||
|
MaxHeight: 2048,
|
||||||
|
MaxSize: 768 * 1024,
|
||||||
|
},
|
||||||
|
}, &res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res.Image, nil
|
||||||
|
}
|
||||||
|
|
||||||
func shrinkit(data []byte) (*image.Image, error) {
|
func shrinkit(data []byte) (*image.Image, error) {
|
||||||
if isSVG(data) {
|
if isSVG(data) {
|
||||||
return imageFromSVG(data)
|
return imageFromSVG(data)
|
||||||
|
|
10
backupdb.go
10
backupdb.go
|
@ -35,6 +35,7 @@ func svalbard(dirname string) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
elog.Fatalf("can't open backup database")
|
elog.Fatalf("can't open backup database")
|
||||||
}
|
}
|
||||||
|
_, err = backup.Exec("PRAGMA journal_mode=WAL")
|
||||||
for _, line := range strings.Split(sqlSchema, ";") {
|
for _, line := range strings.Split(sqlSchema, ";") {
|
||||||
_, err = backup.Exec(line)
|
_, err = backup.Exec(line)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -76,16 +77,16 @@ func svalbard(dirname string) {
|
||||||
|
|
||||||
honkids := make(map[int64]bool)
|
honkids := make(map[int64]bool)
|
||||||
for c := range convoys {
|
for c := range convoys {
|
||||||
rows = qordie(orig, "select honkid, userid, what, honker, xid, rid, dt, url, audience, noise, convoy, whofore, format, precis, oonker, flags from honks where convoy = ?", c)
|
rows = qordie(orig, "select honkid, userid, what, honker, xid, rid, dt, url, audience, noise, convoy, whofore, format, precis, oonker, flags, plain from honks where convoy = ?", c)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var honkid, userid int64
|
var honkid, userid int64
|
||||||
var what, honker, xid, rid, dt, url, audience, noise, convoy string
|
var what, honker, xid, rid, dt, url, audience, noise, convoy, plain string
|
||||||
var whofore int64
|
var whofore int64
|
||||||
var format, precis, oonker string
|
var format, precis, oonker string
|
||||||
var flags int64
|
var flags int64
|
||||||
scanordie(rows, &honkid, &userid, &what, &honker, &xid, &rid, &dt, &url, &audience, &noise, &convoy, &whofore, &format, &precis, &oonker, &flags)
|
scanordie(rows, &honkid, &userid, &what, &honker, &xid, &rid, &dt, &url, &audience, &noise, &convoy, &whofore, &format, &precis, &oonker, &flags, &plain)
|
||||||
honkids[honkid] = true
|
honkids[honkid] = true
|
||||||
doordie(tx, "insert into honks (honkid, userid, what, honker, xid, rid, dt, url, audience, noise, convoy, whofore, format, precis, oonker, flags) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", honkid, userid, what, honker, xid, rid, dt, url, audience, noise, convoy, whofore, format, precis, oonker, flags)
|
doordie(tx, "insert into honks (honkid, userid, what, honker, xid, rid, dt, url, audience, noise, convoy, whofore, format, precis, oonker, flags, plain) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", honkid, userid, what, honker, xid, rid, dt, url, audience, noise, convoy, whofore, format, precis, oonker, flags, plain)
|
||||||
}
|
}
|
||||||
rows.Close()
|
rows.Close()
|
||||||
}
|
}
|
||||||
|
@ -169,6 +170,7 @@ func svalbard(dirname string) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
elog.Fatalf("can't open backup blob database")
|
elog.Fatalf("can't open backup blob database")
|
||||||
}
|
}
|
||||||
|
_, err = blob.Exec("PRAGMA journal_mode=WAL")
|
||||||
doordie(blob, "create table filedata (xid text, media text, hash text, content blob)")
|
doordie(blob, "create table filedata (xid text, media text, hash text, content blob)")
|
||||||
doordie(blob, "create index idx_filexid on filedata(xid)")
|
doordie(blob, "create index idx_filexid on filedata(xid)")
|
||||||
doordie(blob, "create index idx_filehash on filedata(hash)")
|
doordie(blob, "create index idx_filehash on filedata(hash)")
|
||||||
|
|
32
database.go
32
database.go
|
@ -36,6 +36,8 @@ import (
|
||||||
"humungus.tedunangst.com/r/webs/mz"
|
"humungus.tedunangst.com/r/webs/mz"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var honkwindow time.Duration = 7
|
||||||
|
|
||||||
//go:embed schema.sql
|
//go:embed schema.sql
|
||||||
var sqlSchema string
|
var sqlSchema string
|
||||||
|
|
||||||
|
@ -187,7 +189,7 @@ func getbonk(userid int64, xid string) *Honk {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getpublichonks() []*Honk {
|
func getpublichonks() []*Honk {
|
||||||
dt := time.Now().Add(-7 * 24 * time.Hour).UTC().Format(dbtimeformat)
|
dt := time.Now().Add(-honkwindow).UTC().Format(dbtimeformat)
|
||||||
rows, err := stmtPublicHonks.Query(dt, 100)
|
rows, err := stmtPublicHonks.Query(dt, 100)
|
||||||
return getsomehonks(rows, err)
|
return getsomehonks(rows, err)
|
||||||
}
|
}
|
||||||
|
@ -223,7 +225,7 @@ func geteventhonks(userid int64) []*Honk {
|
||||||
return honks
|
return honks
|
||||||
}
|
}
|
||||||
func gethonksbyuser(name string, includeprivate bool, wanted int64) []*Honk {
|
func gethonksbyuser(name string, includeprivate bool, wanted int64) []*Honk {
|
||||||
dt := time.Now().Add(-7 * 24 * time.Hour).UTC().Format(dbtimeformat)
|
dt := time.Now().Add(-honkwindow).UTC().Format(dbtimeformat)
|
||||||
limit := 50
|
limit := 50
|
||||||
whofore := 2
|
whofore := 2
|
||||||
if includeprivate {
|
if includeprivate {
|
||||||
|
@ -233,19 +235,19 @@ func gethonksbyuser(name string, includeprivate bool, wanted int64) []*Honk {
|
||||||
return getsomehonks(rows, err)
|
return getsomehonks(rows, err)
|
||||||
}
|
}
|
||||||
func gethonksforuser(userid int64, wanted int64) []*Honk {
|
func gethonksforuser(userid int64, wanted int64) []*Honk {
|
||||||
dt := time.Now().Add(-7 * 24 * time.Hour).UTC().Format(dbtimeformat)
|
dt := time.Now().Add(-honkwindow).UTC().Format(dbtimeformat)
|
||||||
rows, err := stmtHonksForUser.Query(wanted, userid, dt, userid, userid)
|
rows, err := stmtHonksForUser.Query(wanted, userid, dt, userid, userid)
|
||||||
return getsomehonks(rows, err)
|
return getsomehonks(rows, err)
|
||||||
}
|
}
|
||||||
func gethonksforuserfirstclass(userid int64, wanted int64) []*Honk {
|
func gethonksforuserfirstclass(userid int64, wanted int64) []*Honk {
|
||||||
dt := time.Now().Add(-7 * 24 * time.Hour).UTC().Format(dbtimeformat)
|
dt := time.Now().Add(-honkwindow).UTC().Format(dbtimeformat)
|
||||||
rows, err := stmtHonksForUserFirstClass.Query(wanted, userid, dt, userid, userid)
|
rows, err := stmtHonksForUserFirstClass.Query(wanted, userid, dt, userid, userid)
|
||||||
return getsomehonks(rows, err)
|
return getsomehonks(rows, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func gethonksforme(userid int64, wanted int64) []*Honk {
|
func gethonksforme(userid int64, wanted int64) []*Honk {
|
||||||
dt := time.Now().Add(-7 * 24 * time.Hour).UTC().Format(dbtimeformat)
|
dt := time.Now().Add(-honkwindow).UTC().Format(dbtimeformat)
|
||||||
rows, err := stmtHonksForMe.Query(wanted, userid, dt, userid)
|
rows, err := stmtHonksForMe.Query(wanted, userid, dt, userid, 250)
|
||||||
return getsomehonks(rows, err)
|
return getsomehonks(rows, err)
|
||||||
}
|
}
|
||||||
func gethonksfromlongago(userid int64, wanted int64) []*Honk {
|
func gethonksfromlongago(userid int64, wanted int64) []*Honk {
|
||||||
|
@ -305,11 +307,11 @@ func gethonksbysearch(userid int64, q string, wanted int64) []*Honk {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if t == "@me" {
|
if t == "@me" {
|
||||||
queries = append(queries, "whofore = 1")
|
queries = append(queries, negate+"whofore = 1")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if t == "@self" {
|
if t == "@self" {
|
||||||
queries = append(queries, "(whofore = 2 or whofore = 3)")
|
queries = append(queries, negate+"(whofore = 2 or whofore = 3)")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(t, "before:") {
|
if strings.HasPrefix(t, "before:") {
|
||||||
|
@ -790,9 +792,19 @@ func loadchatter(userid int64) []*Chatter {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (honk *Honk) Plain() string {
|
func (honk *Honk) Plain() string {
|
||||||
|
return honktoplain(honk, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (honk *Honk) VeryPlain() string {
|
||||||
|
return honktoplain(honk, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func honktoplain(honk *Honk, very bool) string {
|
||||||
var plain []string
|
var plain []string
|
||||||
var filt htfilter.Filter
|
var filt htfilter.Filter
|
||||||
filt.WithLinks = true
|
if !very {
|
||||||
|
filt.WithLinks = true
|
||||||
|
}
|
||||||
if honk.Precis != "" {
|
if honk.Precis != "" {
|
||||||
t, _ := filt.TextOnly(honk.Precis)
|
t, _ := filt.TextOnly(honk.Precis)
|
||||||
plain = append(plain, t)
|
plain = append(plain, t)
|
||||||
|
@ -1184,7 +1196,7 @@ func prepareStatements(db *sql.DB) {
|
||||||
myhonkers := " and honker in (select xid from honkers where userid = ? and (flavor = 'sub' or flavor = 'peep' or flavor = 'presub') and combos not like '% - %')"
|
myhonkers := " and honker in (select xid from honkers where userid = ? and (flavor = 'sub' or flavor = 'peep' or flavor = 'presub') and combos not like '% - %')"
|
||||||
stmtHonksForUser = preparetodie(db, selecthonks+"where honks.honkid > ? and honks.userid = ? and dt > ?"+myhonkers+butnotthose+limit)
|
stmtHonksForUser = preparetodie(db, selecthonks+"where honks.honkid > ? and honks.userid = ? and dt > ?"+myhonkers+butnotthose+limit)
|
||||||
stmtHonksForUserFirstClass = preparetodie(db, selecthonks+"where honks.honkid > ? and honks.userid = ? and dt > ? and (rid = '' or what = 'bonk')"+myhonkers+butnotthose+limit)
|
stmtHonksForUserFirstClass = preparetodie(db, selecthonks+"where honks.honkid > ? and honks.userid = ? and dt > ? and (rid = '' or what = 'bonk')"+myhonkers+butnotthose+limit)
|
||||||
stmtHonksForMe = preparetodie(db, selecthonks+"where honks.honkid > ? and honks.userid = ? and dt > ? and whofore = 1"+butnotthose+limit)
|
stmtHonksForMe = preparetodie(db, selecthonks+"where honks.honkid > ? and honks.userid = ? and dt > ? and whofore = 1"+butnotthose+smalllimit)
|
||||||
stmtHonksFromLongAgo = preparetodie(db, selecthonks+"where honks.honkid > ? and honks.userid = ? and dt > ? and dt < ? and whofore = 2"+butnotthose+limit)
|
stmtHonksFromLongAgo = preparetodie(db, selecthonks+"where honks.honkid > ? and honks.userid = ? and dt > ? and dt < ? and whofore = 2"+butnotthose+limit)
|
||||||
stmtHonksISaved = preparetodie(db, selecthonks+"where honks.honkid > ? and honks.userid = ? and flags & 4 order by honks.honkid desc")
|
stmtHonksISaved = preparetodie(db, selecthonks+"where honks.honkid > ? and honks.userid = ? and flags & 4 order by honks.honkid desc")
|
||||||
stmtHonksByHonker = preparetodie(db, selecthonks+"join honkers on (honkers.xid = honks.honker or honkers.xid = honks.oonker) where honks.honkid > ? and honks.userid = ? and honkers.name = ?"+butnotthose+limit)
|
stmtHonksByHonker = preparetodie(db, selecthonks+"join honkers on (honkers.xid = honks.honker or honkers.xid = honks.oonker) where honks.honkid > ? and honks.userid = ? and honkers.name = ?"+butnotthose+limit)
|
||||||
|
|
|
@ -118,8 +118,9 @@ func deliverate(userid int64, rcpt string, msg []byte) {
|
||||||
var garage = gate.NewLimiter(40)
|
var garage = gate.NewLimiter(40)
|
||||||
|
|
||||||
func deliveration(doover Doover) {
|
func deliveration(doover Doover) {
|
||||||
garage.Start()
|
rcpt := doover.Rcpt
|
||||||
defer garage.Finish()
|
garage.StartKey(rcpt)
|
||||||
|
defer garage.FinishKey(rcpt)
|
||||||
|
|
||||||
var ki *KeyInfo
|
var ki *KeyInfo
|
||||||
ok := ziggies.Get(doover.Userid, &ki)
|
ok := ziggies.Get(doover.Userid, &ki)
|
||||||
|
@ -128,7 +129,6 @@ func deliveration(doover Doover) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var inbox string
|
var inbox string
|
||||||
rcpt := doover.Rcpt
|
|
||||||
// already did the box indirection
|
// already did the box indirection
|
||||||
if rcpt[0] == '%' {
|
if rcpt[0] == '%' {
|
||||||
inbox = rcpt[1:]
|
inbox = rcpt[1:]
|
||||||
|
|
|
@ -1,5 +1,39 @@
|
||||||
changelog
|
changelog
|
||||||
|
|
||||||
|
### next
|
||||||
|
|
||||||
|
+ Finally fix slow public queries.
|
||||||
|
|
||||||
|
### 1.1.1 Required Refinement
|
||||||
|
|
||||||
|
+ Fix help file stylesheet link.
|
||||||
|
|
||||||
|
### 1.1.0 Eventual Enshittification
|
||||||
|
|
||||||
|
+ Fix backup command.
|
||||||
|
|
||||||
|
+ Fixes for markdown.
|
||||||
|
|
||||||
|
+ Allow bigger image uploads.
|
||||||
|
|
||||||
|
+ Some hotkeys for the web UI.
|
||||||
|
|
||||||
|
+ Upload multiple files (but beware).
|
||||||
|
|
||||||
|
+ Better page titles.
|
||||||
|
|
||||||
|
+ Refine thread sort.
|
||||||
|
|
||||||
|
+ Send updates to correct audience.
|
||||||
|
|
||||||
|
+ Run analyze to improve database performance.
|
||||||
|
|
||||||
|
+ Delivery performance improvements.
|
||||||
|
|
||||||
|
+ Export command to ActivityPub data. (And import.)
|
||||||
|
|
||||||
|
+ Note that we require go 1.18 now.
|
||||||
|
|
||||||
### 1.0.0 Happy Honker
|
### 1.0.0 Happy Honker
|
||||||
|
|
||||||
+ A great big honk composition text box.
|
+ A great big honk composition text box.
|
||||||
|
|
19
docs/honk.1
19
docs/honk.1
|
@ -130,11 +130,28 @@ Replies higher in the tree are still received.
|
||||||
Please no.
|
Please no.
|
||||||
.It Ic edit
|
.It Ic edit
|
||||||
Change it up.
|
Change it up.
|
||||||
Alas, Update activities do not federate reliably.
|
|
||||||
.Ss Refresh
|
.Ss Refresh
|
||||||
Clicking the refresh button will load new honks, if any.
|
Clicking the refresh button will load new honks, if any.
|
||||||
New honks will be subtly highlighted.
|
New honks will be subtly highlighted.
|
||||||
.El
|
.El
|
||||||
|
.Ss Hotkeys
|
||||||
|
The following keyboard shortcuts may also be used to navigate.
|
||||||
|
.Bl -tag -width short
|
||||||
|
.It j
|
||||||
|
Scroll to next honk.
|
||||||
|
.It k
|
||||||
|
Scroll to previous honk.
|
||||||
|
.It r
|
||||||
|
Refresh.
|
||||||
|
.It s
|
||||||
|
Scroll down to oldest newest.
|
||||||
|
.It m
|
||||||
|
Open menu.
|
||||||
|
.It esc
|
||||||
|
Close menu.
|
||||||
|
.It /
|
||||||
|
Search.
|
||||||
|
.El
|
||||||
.Ss Honking
|
.Ss Honking
|
||||||
Refer to the
|
Refer to the
|
||||||
.Xr honk 5
|
.Xr honk 5
|
||||||
|
|
14
docs/honk.8
14
docs/honk.8
|
@ -41,7 +41,7 @@ proxy_set_header Host $http_host;
|
||||||
.Ss Build
|
.Ss Build
|
||||||
Building
|
Building
|
||||||
.Nm
|
.Nm
|
||||||
requires a go compiler 1.16 and libsqlite.
|
requires a go compiler 1.18 and libsqlite.
|
||||||
On
|
On
|
||||||
.Ox
|
.Ox
|
||||||
this is the go and sqlite3 packages.
|
this is the go and sqlite3 packages.
|
||||||
|
@ -194,10 +194,13 @@ and templates are reloaded every request.
|
||||||
Data may be imported and converted from other services using the
|
Data may be imported and converted from other services using the
|
||||||
.Ic import
|
.Ic import
|
||||||
command.
|
command.
|
||||||
Currently supports Mastodon, Twitter, and Instagram exported data.
|
Currently supports Honk, Mastodon, Twitter, and Instagram exported data.
|
||||||
Posts are imported and backdated to appear as old honks.
|
Posts are imported and backdated to appear as old honks.
|
||||||
The Mastodon following list is imported, but must be refollowed.
|
The Mastodon following list is imported, but must be refollowed.
|
||||||
.Pp
|
.Pp
|
||||||
|
To prepare a Honk data archive, extract the export.zip file.
|
||||||
|
.Dl ./honk import username honk source-directory
|
||||||
|
.Pp
|
||||||
To prepare a Mastodon data archive, extract the archive-longhash.tar.gz file.
|
To prepare a Mastodon data archive, extract the archive-longhash.tar.gz file.
|
||||||
.Dl ./honk import username mastodon source-directory
|
.Dl ./honk import username mastodon source-directory
|
||||||
.Pp
|
.Pp
|
||||||
|
@ -208,6 +211,13 @@ and unzip any zip files contained within.
|
||||||
.Pp
|
.Pp
|
||||||
To prepare an Instagram data archive, extract the igusername.zip file.
|
To prepare an Instagram data archive, extract the igusername.zip file.
|
||||||
.Dl ./honk import username instagram source-directory
|
.Dl ./honk import username instagram source-directory
|
||||||
|
.Ss Export
|
||||||
|
User data may be exported to a zip archive using the
|
||||||
|
.Ic export
|
||||||
|
command.
|
||||||
|
This will export the user's outbox and inbox in ActvityPub json format,
|
||||||
|
along with associated media.
|
||||||
|
.Dl ./honk export username zipname
|
||||||
.Ss Advanced Options
|
.Ss Advanced Options
|
||||||
Advanced configuration values may be set by running the
|
Advanced configuration values may be set by running the
|
||||||
.Ic setconfig Ar key value
|
.Ic setconfig Ar key value
|
||||||
|
|
5
fun.go
5
fun.go
|
@ -330,6 +330,7 @@ func precipitate(honk *Honk) {
|
||||||
noise = noise[idx+1:]
|
noise = noise[idx+1:]
|
||||||
}
|
}
|
||||||
var marker mz.Marker
|
var marker mz.Marker
|
||||||
|
marker.Short = true
|
||||||
honk.Precis = marker.Mark(strings.TrimSpace(honk.Precis))
|
honk.Precis = marker.Mark(strings.TrimSpace(honk.Precis))
|
||||||
honk.Noise = noise
|
honk.Noise = noise
|
||||||
}
|
}
|
||||||
|
@ -344,6 +345,7 @@ func translate(honk *Honk) {
|
||||||
var marker mz.Marker
|
var marker mz.Marker
|
||||||
marker.HashLinker = ontoreplacer
|
marker.HashLinker = ontoreplacer
|
||||||
marker.AtLinker = attoreplacer
|
marker.AtLinker = attoreplacer
|
||||||
|
marker.AllowImages = true
|
||||||
noise = strings.TrimSpace(noise)
|
noise = strings.TrimSpace(noise)
|
||||||
noise = marker.Mark(noise)
|
noise = marker.Mark(noise)
|
||||||
honk.Noise = noise
|
honk.Noise = noise
|
||||||
|
@ -432,6 +434,9 @@ var emucache = cache.New(cache.Options{Filler: func(ename string) (Emu, bool) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
url := fmt.Sprintf("https://%s/emu/%s%s", serverName, fname, ext)
|
url := fmt.Sprintf("https://%s/emu/%s%s", serverName, fname, ext)
|
||||||
|
if develMode {
|
||||||
|
url = fmt.Sprintf("/emu/%s%s", fname, ext)
|
||||||
|
}
|
||||||
return Emu{ID: url, Name: ename, Type: "image/" + ext[1:]}, true
|
return Emu{ID: url, Name: ename, Type: "image/" + ext[1:]}, true
|
||||||
}
|
}
|
||||||
return Emu{Name: ename, ID: "", Type: "image/png"}, true
|
return Emu{Name: ename, ID: "", Type: "image/png"}, true
|
||||||
|
|
13
go.mod
13
go.mod
|
@ -1,13 +1,18 @@
|
||||||
module humungus.tedunangst.com/r/honk
|
module humungus.tedunangst.com/r/honk
|
||||||
|
|
||||||
go 1.16
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/andybalholm/cascadia v1.3.1
|
github.com/andybalholm/cascadia v1.3.1
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/mattn/go-runewidth v0.0.13
|
github.com/mattn/go-runewidth v0.0.13
|
||||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
|
golang.org/x/crypto v0.12.0
|
||||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
|
golang.org/x/net v0.14.0
|
||||||
humungus.tedunangst.com/r/go-sqlite3 v1.1.3
|
humungus.tedunangst.com/r/go-sqlite3 v1.1.3
|
||||||
humungus.tedunangst.com/r/webs v0.6.68
|
humungus.tedunangst.com/r/webs v0.7.9
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
|
golang.org/x/image v0.11.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
43
go.sum
43
go.sum
|
@ -6,24 +6,47 @@ github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4
|
||||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9 h1:LRtI4W37N+KFebI/qV0OFiLUv4GLOWeEW5hn/KEJvxE=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||||
|
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||||
|
golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo=
|
||||||
|
golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||||
|
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
humungus.tedunangst.com/r/go-sqlite3 v1.1.3 h1:G2N4wzDS0NbuvrZtQJhh4F+3X+s7BF8b9ga8k38geUI=
|
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/go-sqlite3 v1.1.3/go.mod h1:FtEEmQM7U2Ey1TuEEOyY1BmphTZnmiEjPsNLEAkpf/M=
|
||||||
humungus.tedunangst.com/r/webs v0.6.68 h1:veKjASf1krPf4o3O7hMRsNvE4+Z6LzXVso/qMccZntk=
|
humungus.tedunangst.com/r/webs v0.7.9 h1:LC9o2F9joAcf4SxWaRFs5ZqXHSbzdfre9/9BY0gcM0w=
|
||||||
humungus.tedunangst.com/r/webs v0.6.68/go.mod h1:03R0N9BcT49HB4TDd1YmarpbiPvPzVDm74Mk4h1hYPc=
|
humungus.tedunangst.com/r/webs v0.7.9/go.mod h1:ylhqHSPI0Oi7b4nsnx5mSO7AjLXN7wFpEHayLfN/ugk=
|
||||||
|
|
179
import.go
179
import.go
|
@ -16,6 +16,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/zip"
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -27,12 +28,16 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"humungus.tedunangst.com/r/webs/junk"
|
||||||
)
|
)
|
||||||
|
|
||||||
func importMain(username, flavor, source string) {
|
func importMain(username, flavor, source string) {
|
||||||
switch flavor {
|
switch flavor {
|
||||||
case "mastodon":
|
case "mastodon":
|
||||||
importMastodon(username, source)
|
importMastodon(username, source)
|
||||||
|
case "honk":
|
||||||
|
importHonk(username, source)
|
||||||
case "twitter":
|
case "twitter":
|
||||||
importTwitter(username, source)
|
importTwitter(username, source)
|
||||||
case "instagram":
|
case "instagram":
|
||||||
|
@ -42,11 +47,17 @@ func importMain(username, flavor, source string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type TootObject struct {
|
type ActivityObject struct {
|
||||||
|
AttributedTo string
|
||||||
Summary string
|
Summary string
|
||||||
Content string
|
Content string
|
||||||
|
Source struct {
|
||||||
|
MediaType string
|
||||||
|
Content string
|
||||||
|
}
|
||||||
InReplyTo string
|
InReplyTo string
|
||||||
Conversation string
|
Conversation string
|
||||||
|
Context string
|
||||||
Published time.Time
|
Published time.Time
|
||||||
Tag []struct {
|
Tag []struct {
|
||||||
Type string
|
Type string
|
||||||
|
@ -60,10 +71,10 @@ type TootObject struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type PlainTootObject TootObject
|
type PlainActivityObject ActivityObject
|
||||||
|
|
||||||
func (obj *TootObject) UnmarshalJSON(b []byte) error {
|
func (obj *ActivityObject) UnmarshalJSON(b []byte) error {
|
||||||
p := (*PlainTootObject)(obj)
|
p := (*PlainActivityObject)(obj)
|
||||||
json.Unmarshal(b, p)
|
json.Unmarshal(b, p)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -74,8 +85,9 @@ func importMastodon(username, source string) {
|
||||||
elog.Fatal(err)
|
elog.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := os.Stat(source + "/outbox.json"); err == nil {
|
outbox := source + "/outbox.json"
|
||||||
importMastotoots(user, source)
|
if _, err := os.Stat(outbox); err == nil {
|
||||||
|
importActivities(user, outbox, source)
|
||||||
} else {
|
} else {
|
||||||
ilog.Printf("skipping outbox.json!")
|
ilog.Printf("skipping outbox.json!")
|
||||||
}
|
}
|
||||||
|
@ -86,19 +98,33 @@ func importMastodon(username, source string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func importMastotoots(user *WhatAbout, source string) {
|
func importHonk(username, source string) {
|
||||||
type Toot struct {
|
user, err := butwhatabout(username)
|
||||||
|
if err != nil {
|
||||||
|
elog.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
outbox := source + "/outbox.json"
|
||||||
|
if _, err := os.Stat(outbox); err == nil {
|
||||||
|
importActivities(user, outbox, source)
|
||||||
|
} else {
|
||||||
|
ilog.Printf("skipping outbox.json!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func importActivities(user *WhatAbout, filename, source string) {
|
||||||
|
type Activity struct {
|
||||||
Id string
|
Id string
|
||||||
Type string
|
Type string
|
||||||
To []string
|
To interface{}
|
||||||
Cc []string
|
Cc []string
|
||||||
Object TootObject
|
Object ActivityObject
|
||||||
}
|
}
|
||||||
var outbox struct {
|
var outbox struct {
|
||||||
OrderedItems []Toot
|
OrderedItems []Activity
|
||||||
}
|
}
|
||||||
ilog.Println("Importing honks...")
|
ilog.Println("Importing honks...")
|
||||||
fd, err := os.Open(source + "/outbox.json")
|
fd, err := os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
elog.Fatal(err)
|
elog.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -120,7 +146,11 @@ func importMastotoots(user *WhatAbout, source string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
re_tootid := regexp.MustCompile("[^/]+$")
|
re_tootid := regexp.MustCompile("[^/]+$")
|
||||||
for _, item := range outbox.OrderedItems {
|
items := outbox.OrderedItems
|
||||||
|
for i, j := 0, len(items)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
items[i], items[j] = items[j], items[i]
|
||||||
|
}
|
||||||
|
for _, item := range items {
|
||||||
toot := item
|
toot := item
|
||||||
if toot.Type != "Create" {
|
if toot.Type != "Create" {
|
||||||
continue
|
continue
|
||||||
|
@ -133,6 +163,27 @@ func importMastotoots(user *WhatAbout, source string) {
|
||||||
if havetoot(xid) {
|
if havetoot(xid) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
convoy := toot.Object.Context
|
||||||
|
if convoy == "" {
|
||||||
|
convoy = toot.Object.Conversation
|
||||||
|
}
|
||||||
|
var audience []string
|
||||||
|
to, ok := toot.To.(string)
|
||||||
|
if ok {
|
||||||
|
audience = append(audience, to)
|
||||||
|
} else {
|
||||||
|
for _, t := range toot.To.([]interface{}) {
|
||||||
|
audience = append(audience, t.(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
content := toot.Object.Content
|
||||||
|
format := "html"
|
||||||
|
if toot.Object.Source.MediaType == "text/markdown" {
|
||||||
|
content = toot.Object.Source.Content
|
||||||
|
format = "markdown"
|
||||||
|
}
|
||||||
|
audience = append(audience, toot.Cc...)
|
||||||
honk := Honk{
|
honk := Honk{
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
What: "honk",
|
What: "honk",
|
||||||
|
@ -141,11 +192,11 @@ func importMastotoots(user *WhatAbout, source string) {
|
||||||
RID: toot.Object.InReplyTo,
|
RID: toot.Object.InReplyTo,
|
||||||
Date: toot.Object.Published,
|
Date: toot.Object.Published,
|
||||||
URL: xid,
|
URL: xid,
|
||||||
Audience: append(toot.To, toot.Cc...),
|
Audience: audience,
|
||||||
Noise: toot.Object.Content,
|
Noise: content,
|
||||||
Convoy: toot.Object.Conversation,
|
Convoy: convoy,
|
||||||
Whofore: 2,
|
Whofore: 2,
|
||||||
Format: "html",
|
Format: format,
|
||||||
Precis: toot.Object.Summary,
|
Precis: toot.Object.Summary,
|
||||||
}
|
}
|
||||||
if !loudandproud(honk.Audience) {
|
if !loudandproud(honk.Audience) {
|
||||||
|
@ -157,7 +208,7 @@ func importMastotoots(user *WhatAbout, source string) {
|
||||||
fname := fmt.Sprintf("%s/%s", source, att.Url)
|
fname := fmt.Sprintf("%s/%s", source, att.Url)
|
||||||
data, err := ioutil.ReadFile(fname)
|
data, err := ioutil.ReadFile(fname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
elog.Printf("error reading media: %s", fname)
|
elog.Printf("error reading media for %s: %s", honk.XID, fname)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
u := xfiltrate()
|
u := xfiltrate()
|
||||||
|
@ -513,3 +564,95 @@ func importInstagram(username, source string) {
|
||||||
log.Printf("honk saved %v -> %v", xid, err)
|
log.Printf("honk saved %v -> %v", xid, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func export(username, file string) {
|
||||||
|
user, err := butwhatabout(username)
|
||||||
|
if err != nil {
|
||||||
|
elog.Fatal(err)
|
||||||
|
}
|
||||||
|
fd, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
|
||||||
|
if err != nil {
|
||||||
|
elog.Fatal(err)
|
||||||
|
}
|
||||||
|
zd := zip.NewWriter(fd)
|
||||||
|
donks := make(map[string]bool)
|
||||||
|
{
|
||||||
|
w, err := zd.Create("outbox.json")
|
||||||
|
if err != nil {
|
||||||
|
elog.Fatal("error creating outbox.json", err)
|
||||||
|
}
|
||||||
|
var jonks []junk.Junk
|
||||||
|
rows, err := stmtUserHonks.Query(0, 3, user.Name, "0", 1234567)
|
||||||
|
honks := getsomehonks(rows, err)
|
||||||
|
for _, honk := range honks {
|
||||||
|
for _, donk := range honk.Donks {
|
||||||
|
donk.URL = "media/" + donk.XID
|
||||||
|
donks[donk.XID] = true
|
||||||
|
}
|
||||||
|
noise := honk.Noise
|
||||||
|
j, jo := jonkjonk(user, honk)
|
||||||
|
if honk.Format == "markdown" {
|
||||||
|
source := junk.New()
|
||||||
|
source["mediaType"] = "text/markdown"
|
||||||
|
source["content"] = noise
|
||||||
|
jo["source"] = source
|
||||||
|
}
|
||||||
|
jonks = append(jonks, j)
|
||||||
|
}
|
||||||
|
j := junk.New()
|
||||||
|
j["@context"] = itiswhatitis
|
||||||
|
j["id"] = user.URL + "/outbox"
|
||||||
|
j["attributedTo"] = user.URL
|
||||||
|
j["type"] = "OrderedCollection"
|
||||||
|
j["totalItems"] = len(jonks)
|
||||||
|
j["orderedItems"] = jonks
|
||||||
|
j.Write(w)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
w, err := zd.Create("inbox.json")
|
||||||
|
if err != nil {
|
||||||
|
elog.Fatal("error creating inbox.json", err)
|
||||||
|
}
|
||||||
|
var jonks []junk.Junk
|
||||||
|
rows, err := stmtHonksForMe.Query(0, user.ID, "0", user.ID, 1234567)
|
||||||
|
honks := getsomehonks(rows, err)
|
||||||
|
for _, honk := range honks {
|
||||||
|
for _, donk := range honk.Donks {
|
||||||
|
donk.URL = "media/" + donk.XID
|
||||||
|
donks[donk.XID] = true
|
||||||
|
}
|
||||||
|
j, _ := jonkjonk(user, honk)
|
||||||
|
jonks = append(jonks, j)
|
||||||
|
}
|
||||||
|
j := junk.New()
|
||||||
|
j["@context"] = itiswhatitis
|
||||||
|
j["id"] = user.URL + "/inbox"
|
||||||
|
j["attributedTo"] = user.URL
|
||||||
|
j["type"] = "OrderedCollection"
|
||||||
|
j["totalItems"] = len(jonks)
|
||||||
|
j["orderedItems"] = jonks
|
||||||
|
j.Write(w)
|
||||||
|
}
|
||||||
|
zd.Create("media/")
|
||||||
|
for donk := range donks {
|
||||||
|
if donk == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var media string
|
||||||
|
var data []byte
|
||||||
|
w, err := zd.Create("media/" + donk)
|
||||||
|
if err != nil {
|
||||||
|
elog.Printf("error creating %s: %s", donk, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
row := stmtGetFileData.QueryRow(donk)
|
||||||
|
err = row.Scan(&media, &data)
|
||||||
|
if err != nil {
|
||||||
|
elog.Printf("error scanning file %s: %s", donk, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
w.Write(data)
|
||||||
|
}
|
||||||
|
zd.Close()
|
||||||
|
fd.Close()
|
||||||
|
}
|
||||||
|
|
73
main.go
73
main.go
|
@ -64,6 +64,11 @@ func reexecArgs(cmd string) []string {
|
||||||
|
|
||||||
var elog, ilog, dlog *golog.Logger
|
var elog, ilog, dlog *golog.Logger
|
||||||
|
|
||||||
|
func errx(msg string, args ...interface{}) {
|
||||||
|
fmt.Fprintf(os.Stderr, msg+"\n", args...)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.StringVar(&dataDir, "datadir", dataDir, "data directory")
|
flag.StringVar(&dataDir, "datadir", dataDir, "data directory")
|
||||||
flag.StringVar(&viewDir, "viewdir", viewDir, "view directory")
|
flag.StringVar(&viewDir, "viewdir", viewDir, "view directory")
|
||||||
|
@ -110,21 +115,32 @@ func main() {
|
||||||
getconfig("usersep", &userSep)
|
getconfig("usersep", &userSep)
|
||||||
getconfig("honksep", &honkSep)
|
getconfig("honksep", &honkSep)
|
||||||
getconfig("devel", &develMode)
|
getconfig("devel", &develMode)
|
||||||
|
if develMode {
|
||||||
|
gogglesDoNothing()
|
||||||
|
}
|
||||||
getconfig("fasttimeout", &fastTimeout)
|
getconfig("fasttimeout", &fastTimeout)
|
||||||
getconfig("slowtimeout", &slowTimeout)
|
getconfig("slowtimeout", &slowTimeout)
|
||||||
getconfig("signgets", &signGets)
|
getconfig("honkwindow", &honkwindow)
|
||||||
|
honkwindow *= 24 * time.Hour
|
||||||
|
|
||||||
prepareStatements(db)
|
prepareStatements(db)
|
||||||
|
|
||||||
switch cmd {
|
switch cmd {
|
||||||
case "admin":
|
case "admin":
|
||||||
adminscreen()
|
adminscreen()
|
||||||
case "import":
|
case "import":
|
||||||
if len(args) != 4 {
|
if len(args) != 4 {
|
||||||
elog.Fatal("import username mastodon|twitter srcdir")
|
errx("import username honk|mastodon|twitter srcdir")
|
||||||
}
|
}
|
||||||
importMain(args[1], args[2], args[3])
|
importMain(args[1], args[2], args[3])
|
||||||
|
case "export":
|
||||||
|
if len(args) != 3 {
|
||||||
|
errx("export username destdir")
|
||||||
|
}
|
||||||
|
export(args[1], args[2])
|
||||||
case "devel":
|
case "devel":
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
elog.Fatal("need an argument: devel (on|off)")
|
errx("need an argument: devel (on|off)")
|
||||||
}
|
}
|
||||||
switch args[1] {
|
switch args[1] {
|
||||||
case "on":
|
case "on":
|
||||||
|
@ -132,11 +148,11 @@ func main() {
|
||||||
case "off":
|
case "off":
|
||||||
setconfig("devel", 0)
|
setconfig("devel", 0)
|
||||||
default:
|
default:
|
||||||
elog.Fatal("argument must be on or off")
|
errx("argument must be on or off")
|
||||||
}
|
}
|
||||||
case "setconfig":
|
case "setconfig":
|
||||||
if len(args) != 3 {
|
if len(args) != 3 {
|
||||||
elog.Fatal("need an argument: setconfig key val")
|
errx("need an argument: setconfig key val")
|
||||||
}
|
}
|
||||||
var val interface{}
|
var val interface{}
|
||||||
var err error
|
var err error
|
||||||
|
@ -148,66 +164,55 @@ func main() {
|
||||||
adduser()
|
adduser()
|
||||||
case "deluser":
|
case "deluser":
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
fmt.Printf("usage: honk deluser username\n")
|
errx("usage: honk deluser username")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
deluser(args[1])
|
deluser(args[1])
|
||||||
case "chpass":
|
case "chpass":
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
fmt.Printf("usage: honk chpass username\n")
|
errx("usage: honk chpass username")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
chpass(args[1])
|
chpass(args[1])
|
||||||
case "follow":
|
case "follow":
|
||||||
if len(args) < 3 {
|
if len(args) < 3 {
|
||||||
fmt.Printf("usage: honk follow username url\n")
|
errx("usage: honk follow username url")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
user, err := butwhatabout(args[1])
|
user, err := butwhatabout(args[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("user not found\n")
|
errx("user %s not found", args[1])
|
||||||
return
|
|
||||||
}
|
}
|
||||||
var meta HonkerMeta
|
var meta HonkerMeta
|
||||||
mj, _ := jsonify(&meta)
|
mj, _ := jsonify(&meta)
|
||||||
honkerid, err := savehonker(user, args[2], "", "presub", "", mj)
|
honkerid, err := savehonker(user, args[2], "", "presub", "", mj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("had some trouble with that: %s\n", err)
|
errx("had some trouble with that: %s", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
followyou(user, honkerid, true)
|
followyou(user, honkerid, true)
|
||||||
case "unfollow":
|
case "unfollow":
|
||||||
if len(args) < 3 {
|
if len(args) < 3 {
|
||||||
fmt.Printf("usage: honk unfollow username url\n")
|
errx("usage: honk unfollow username url")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
user, err := butwhatabout(args[1])
|
user, err := butwhatabout(args[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("user not found\n")
|
errx("user not found")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
row := db.QueryRow("select honkerid from honkers where xid = ? and userid = ? and flavor in ('sub')", args[2], user.ID)
|
row := db.QueryRow("select honkerid from honkers where xid = ? and userid = ? and flavor in ('sub')", args[2], user.ID)
|
||||||
var honkerid int64
|
var honkerid int64
|
||||||
err = row.Scan(&honkerid)
|
err = row.Scan(&honkerid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("sorry couldn't find them\n")
|
errx("sorry couldn't find them")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
unfollowyou(user, honkerid, true)
|
unfollowyou(user, honkerid, true)
|
||||||
case "sendmsg":
|
case "sendmsg":
|
||||||
if len(args) < 4 {
|
if len(args) < 4 {
|
||||||
fmt.Printf("usage: honk send username filename rcpt\n")
|
errx("usage: honk send username filename rcpt")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
user, err := butwhatabout(args[1])
|
user, err := butwhatabout(args[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("user not found\n")
|
errx("user %s not found", args[1])
|
||||||
return
|
|
||||||
}
|
}
|
||||||
data, err := os.ReadFile(args[2])
|
data, err := os.ReadFile(args[2])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("can't read file\n")
|
errx("can't read file: %s", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
deliverate(user.ID, args[3], data)
|
deliverate(user.ID, args[3], data)
|
||||||
case "cleanup":
|
case "cleanup":
|
||||||
|
@ -218,29 +223,25 @@ func main() {
|
||||||
cleanupdb(arg)
|
cleanupdb(arg)
|
||||||
case "unplug":
|
case "unplug":
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
fmt.Printf("usage: honk unplug servername\n")
|
errx("usage: honk unplug servername")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
name := args[1]
|
name := args[1]
|
||||||
unplugserver(name)
|
unplugserver(name)
|
||||||
case "backup":
|
case "backup":
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
fmt.Printf("usage: honk backup dirname\n")
|
errx("usage: honk backup dirname")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
name := args[1]
|
name := args[1]
|
||||||
svalbard(name)
|
svalbard(name)
|
||||||
case "ping":
|
case "ping":
|
||||||
if len(args) < 3 {
|
if len(args) < 3 {
|
||||||
fmt.Printf("usage: honk ping (from username) (to username or url)\n")
|
errx("usage: honk ping (from username) (to username or url)")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
name := args[1]
|
name := args[1]
|
||||||
targ := args[2]
|
targ := args[2]
|
||||||
user, err := butwhatabout(name)
|
user, err := butwhatabout(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
elog.Printf("unknown user")
|
errx("unknown user %s", name)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
ping(user, targ)
|
ping(user, targ)
|
||||||
case "run":
|
case "run":
|
||||||
|
@ -250,6 +251,6 @@ func main() {
|
||||||
case "test":
|
case "test":
|
||||||
ElaborateUnitTests()
|
ElaborateUnitTests()
|
||||||
default:
|
default:
|
||||||
elog.Fatal("unknown command")
|
errx("unknown command")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
go version > /dev/null 2>&1 || (echo go 1.16+ is required && false)
|
go version > /dev/null 2>&1 || (echo go 1.18+ is required && false)
|
||||||
|
|
||||||
v=`go version | egrep -o "go1\.[^.]+"` || echo failed to identify go version
|
v=`go version | egrep -o "go1\.[^.]+"` || echo failed to identify go version
|
||||||
if [ "$v" \< "go1.16" ] ; then
|
if [ "$v" \< "go1.18" ] ; then
|
||||||
echo go version is too old: $v
|
echo go version is too old: $v
|
||||||
echo go 1.16+ is required
|
echo go 1.18+ is required
|
||||||
false
|
false
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ create index idx_honksxid on honks(xid);
|
||||||
create index idx_honksconvoy on honks(convoy);
|
create index idx_honksconvoy on honks(convoy);
|
||||||
create index idx_honkshonker on honks(honker);
|
create index idx_honkshonker on honks(honker);
|
||||||
create index idx_honksoonker on honks(oonker);
|
create index idx_honksoonker on honks(oonker);
|
||||||
|
create index idx_honkswhotwo on honks(whofore) where whofore = 2;
|
||||||
create index idx_donkshonk on donks(honkid);
|
create index idx_donkshonk on donks(honkid);
|
||||||
create index idx_donkschonk on donks(chonkid);
|
create index idx_donkschonk on donks(chonkid);
|
||||||
create index idx_honkerxid on honkers(xid);
|
create index idx_honkerxid on honkers(xid);
|
||||||
|
|
|
@ -29,6 +29,9 @@ func demoji(s string) string {
|
||||||
|
|
||||||
zw := false
|
zw := false
|
||||||
for _, c := range s {
|
for _, c := range s {
|
||||||
|
if c == '\n' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if runewidth.RuneWidth(c) == 0 {
|
if runewidth.RuneWidth(c) == 0 {
|
||||||
zw = true
|
zw = true
|
||||||
break
|
break
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
//go:build openbsd
|
//go:build openbsd
|
||||||
// +build openbsd
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Copyright (c) 2019 Ted Unangst <tedu@tedunangst.com>
|
// Copyright (c) 2019 Ted Unangst <tedu@tedunangst.com>
|
||||||
|
|
|
@ -23,7 +23,7 @@ import (
|
||||||
"humungus.tedunangst.com/r/webs/htfilter"
|
"humungus.tedunangst.com/r/webs/htfilter"
|
||||||
)
|
)
|
||||||
|
|
||||||
var myVersion = 45
|
var myVersion = 46 // idx whotwo
|
||||||
|
|
||||||
type dbexecer interface {
|
type dbexecer interface {
|
||||||
Exec(query string, args ...interface{}) (sql.Result, error)
|
Exec(query string, args ...interface{}) (sql.Result, error)
|
||||||
|
@ -172,6 +172,11 @@ func upgradedb() {
|
||||||
tx = nil
|
tx = nil
|
||||||
fallthrough
|
fallthrough
|
||||||
case 45:
|
case 45:
|
||||||
|
try("create index idx_honkswhotwo on honks(whofore) where whofore = 2")
|
||||||
|
setV(46)
|
||||||
|
fallthrough
|
||||||
|
case 46:
|
||||||
|
try("analyze")
|
||||||
|
|
||||||
default:
|
default:
|
||||||
elog.Fatalf("can't upgrade unknown version %d", dbversion)
|
elog.Fatalf("can't upgrade unknown version %d", dbversion)
|
||||||
|
|
18
util.go
18
util.go
|
@ -36,10 +36,8 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/sha512"
|
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
@ -52,24 +50,8 @@ import (
|
||||||
"humungus.tedunangst.com/r/webs/login"
|
"humungus.tedunangst.com/r/webs/login"
|
||||||
)
|
)
|
||||||
|
|
||||||
var savedassetparams = make(map[string]string)
|
|
||||||
|
|
||||||
var re_plainname = regexp.MustCompile("^[[:alnum:]_-]+$")
|
var re_plainname = regexp.MustCompile("^[[:alnum:]_-]+$")
|
||||||
|
|
||||||
func getassetparam(file string) string {
|
|
||||||
if p, ok := savedassetparams[file]; ok {
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
data, err := ioutil.ReadFile(file)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
hasher := sha512.New()
|
|
||||||
hasher.Write(data)
|
|
||||||
|
|
||||||
return fmt.Sprintf("?v=%.8x", hasher.Sum(nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
var dbtimeformat = "2006-01-02 15:04:05"
|
var dbtimeformat = "2006-01-02 15:04:05"
|
||||||
|
|
||||||
var alreadyopendb *sql.DB
|
var alreadyopendb *sql.DB
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
<p>
|
<p>
|
||||||
<table class="font08em">
|
<table class="font08em">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr><td>version:<td class="textright">{{ .HonkVersion }}
|
<tr><td>version:<td class="text-right">{{ .HonkVersion }}
|
||||||
<tr><td>memory:<td class="textright">{{ printf "%.02f" .Sensors.Memory }}MB
|
<tr><td>memory:<td class="text-right">{{ printf "%.02f" .Sensors.Memory }}MB
|
||||||
<tr><td>uptime:<td class="textright">{{ printf "%.02f" .Sensors.Uptime }}s
|
<tr><td>uptime:<td class="text-right">{{ printf "%.02f" .Sensors.Uptime }}s
|
||||||
<tr><td>cputime:<td class="textright">{{ printf "%.02f" .Sensors.CPU }}s
|
<tr><td>cputime:<td class="text-right">{{ printf "%.02f" .Sensors.CPU }}s
|
||||||
</table>
|
</table>
|
||||||
<p>
|
<p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>honk</title>
|
<title>{{ or .Title .ServerName }}</title>
|
||||||
<link href="/style.css{{ .StyleParam }}" rel="stylesheet">
|
<link href="/style.css{{ .StyleParam }}" rel="stylesheet">
|
||||||
{{ if .LocalStyleParam }}
|
{{ if .LocalStyleParam }}
|
||||||
<link href="/local.css{{ .LocalStyleParam }}" rel="stylesheet">
|
<link href="/local.css{{ .LocalStyleParam }}" rel="stylesheet">
|
||||||
|
@ -52,7 +52,7 @@
|
||||||
<li><a href="/help/honk.1.html">help</a>
|
<li><a href="/help/honk.1.html">help</a>
|
||||||
<li>
|
<li>
|
||||||
<form action="/q" method="GET">
|
<form action="/q" method="GET">
|
||||||
<input type="text" name="q" autocomplete=off size=10 placeholder="search">
|
<input type="text" name="q" autocomplete=off size=10 id="searchbox" placeholder="search">
|
||||||
</form>
|
</form>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
|
|
|
@ -9,8 +9,7 @@
|
||||||
<details>
|
<details>
|
||||||
<summary>more options</summary>
|
<summary>more options</summary>
|
||||||
<p>
|
<p>
|
||||||
<label class=button id="donker">attach: <input type="file" name="donk"><span>{{ .SavedFile }}</span></label>
|
<label class=button id="donker">attach: <input type="file" multiple name="donk"><span>{{ .SavedFile }}</span></label><input type="hidden" id="saveddonkxid" name="donkxid" value="{{ .SavedFile }}">
|
||||||
<input type="hidden" id="saveddonkxid" name="donkxid" value="{{ .SavedFile }}">
|
|
||||||
<p id="donkdescriptor"><label for=donkdesc>description:</label><br>
|
<p id="donkdescriptor"><label for=donkdesc>description:</label><br>
|
||||||
<input type="text" name="donkdesc" value="{{ .DonkDesc }}" autocomplete=off>
|
<input type="text" name="donkdesc" value="{{ .DonkDesc }}" autocomplete=off>
|
||||||
{{ with .SavedPlace }}
|
{{ with .SavedPlace }}
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
</div>
|
</div>
|
||||||
{{ if and .HonkCSRF (not .IsPreview) }}
|
{{ if and .HonkCSRF (not .IsPreview) }}
|
||||||
<div class="info" id="refreshbox">
|
<div class="info" id="refreshbox">
|
||||||
<p><button class="refresh">refresh</button><span></span>
|
<p><button id="honkrefresher" class="refresh">refresh</button><span></span>
|
||||||
<button class="scrolldown">scroll down</button>
|
<button id="newerscroller" class="scrolldown">scroll down</button>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<div id="honksonpage">
|
<div id="honksonpage">
|
||||||
|
|
|
@ -82,7 +82,7 @@ var lehonkbutton = document.getElementById("honkingtime")
|
||||||
function oldestnewest(btn) {
|
function oldestnewest(btn) {
|
||||||
var els = document.getElementsByClassName("glow")
|
var els = document.getElementsByClassName("glow")
|
||||||
if (els.length) {
|
if (els.length) {
|
||||||
els[els.length-1].scrollIntoView()
|
els[els.length-1].scrollIntoView({ behavior: "smooth" })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function removeglow() {
|
function removeglow() {
|
||||||
|
@ -384,13 +384,13 @@ function hideelement(el) {
|
||||||
if (!el) return
|
if (!el) return
|
||||||
el.style.display = "none"
|
el.style.display = "none"
|
||||||
}
|
}
|
||||||
function updatedonker() {
|
function updatedonker(ev) {
|
||||||
var el = document.getElementById("donker")
|
var el = ev.target.parentElement
|
||||||
el.children[1].textContent = el.children[0].value.slice(-20)
|
el.children[1].textContent = el.children[0].value.slice(-20)
|
||||||
el = document.getElementById("donkdescriptor")
|
el = el.nextSibling
|
||||||
el.style.display = ""
|
|
||||||
el = document.getElementById("saveddonkxid")
|
|
||||||
el.value = ""
|
el.value = ""
|
||||||
|
el = el.parentElement.nextSibling
|
||||||
|
el.style.display = ""
|
||||||
}
|
}
|
||||||
var checkinprec = 100.0
|
var checkinprec = 100.0
|
||||||
var gpsoptions = {
|
var gpsoptions = {
|
||||||
|
@ -416,6 +416,76 @@ function fillcheckin() {
|
||||||
}, gpsoptions)
|
}, gpsoptions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function scrollnexthonk() {
|
||||||
|
var honks = document.getElementsByClassName("honk");
|
||||||
|
for (var i = 0; i < honks.length; i++) {
|
||||||
|
var h = honks[i];
|
||||||
|
var b = h.getBoundingClientRect();
|
||||||
|
if (b.top > 1.0) {
|
||||||
|
h.scrollIntoView()
|
||||||
|
var a = h.querySelector(".actions summary")
|
||||||
|
if (a) a.focus({ preventScroll: true })
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollprevioushonk() {
|
||||||
|
var honks = document.getElementsByClassName("honk");
|
||||||
|
for (var i = 1; i < honks.length; i++) {
|
||||||
|
var b = honks[i].getBoundingClientRect();
|
||||||
|
if (b.top > -1.0) {
|
||||||
|
honks[i-1].scrollIntoView()
|
||||||
|
var a = honks[i-1].querySelector(".actions summary")
|
||||||
|
if (a) a.focus({ preventScroll: true })
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hotkey(e) {
|
||||||
|
if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement)
|
||||||
|
return
|
||||||
|
if (e.ctrlKey || e.altKey)
|
||||||
|
return
|
||||||
|
|
||||||
|
switch (e.code) {
|
||||||
|
case "KeyR":
|
||||||
|
refreshhonks(document.getElementById("honkrefresher"));
|
||||||
|
break;
|
||||||
|
case "KeyS":
|
||||||
|
oldestnewest(document.getElementById("newerscroller"));
|
||||||
|
break;
|
||||||
|
case "KeyJ":
|
||||||
|
scrollnexthonk();
|
||||||
|
break;
|
||||||
|
case "KeyK":
|
||||||
|
scrollprevioushonk();
|
||||||
|
break;
|
||||||
|
case "KeyM":
|
||||||
|
var menu = document.getElementById("topmenu")
|
||||||
|
if (!menu.open) {
|
||||||
|
menu.open = true
|
||||||
|
menu.querySelector("a").focus()
|
||||||
|
} else {
|
||||||
|
menu.open = false
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case "Escape":
|
||||||
|
var menu = document.getElementById("topmenu")
|
||||||
|
menu.open = false
|
||||||
|
break
|
||||||
|
case "Slash":
|
||||||
|
document.getElementById("topmenu").open = true
|
||||||
|
document.getElementById("searchbox").focus()
|
||||||
|
e.preventDefault()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("keydown", hotkey)
|
||||||
|
|
||||||
function addemu(elem) {
|
function addemu(elem) {
|
||||||
const data = elem.alt
|
const data = elem.alt
|
||||||
const box = document.getElementById("honknoise");
|
const box = document.getElementById("honknoise");
|
||||||
|
|
|
@ -389,9 +389,15 @@ li.details {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.textright {
|
.text-left {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.text-right {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
.text-center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.font08em {
|
.font08em {
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
|
|
162
web.go
162
web.go
|
@ -17,11 +17,13 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/sha512"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
notrand "math/rand"
|
notrand "math/rand"
|
||||||
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
@ -36,6 +38,7 @@ import (
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"humungus.tedunangst.com/r/webs/cache"
|
"humungus.tedunangst.com/r/webs/cache"
|
||||||
|
"humungus.tedunangst.com/r/webs/gencache"
|
||||||
"humungus.tedunangst.com/r/webs/httpsig"
|
"humungus.tedunangst.com/r/webs/httpsig"
|
||||||
"humungus.tedunangst.com/r/webs/junk"
|
"humungus.tedunangst.com/r/webs/junk"
|
||||||
"humungus.tedunangst.com/r/webs/login"
|
"humungus.tedunangst.com/r/webs/login"
|
||||||
|
@ -81,11 +84,14 @@ func getInfo(r *http.Request) map[string]interface{} {
|
||||||
templinfo["StyleParam"] = getassetparam(viewDir + "/views/style.css")
|
templinfo["StyleParam"] = getassetparam(viewDir + "/views/style.css")
|
||||||
templinfo["LocalStyleParam"] = getassetparam(dataDir + "/views/local.css")
|
templinfo["LocalStyleParam"] = getassetparam(dataDir + "/views/local.css")
|
||||||
templinfo["JSParam"] = getassetparam(viewDir + "/views/honkpage.js")
|
templinfo["JSParam"] = getassetparam(viewDir + "/views/honkpage.js")
|
||||||
|
templinfo["MiscJSParam"] = getassetparam(viewDir + "/views/misc.js")
|
||||||
templinfo["LocalJSParam"] = getassetparam(dataDir + "/views/local.js")
|
templinfo["LocalJSParam"] = getassetparam(dataDir + "/views/local.js")
|
||||||
templinfo["MiscJSParam"] = getassetparam(dataDir + "/views/misc.js")
|
|
||||||
templinfo["ServerName"] = serverName
|
templinfo["ServerName"] = serverName
|
||||||
templinfo["IconName"] = iconName
|
templinfo["IconName"] = iconName
|
||||||
templinfo["UserSep"] = userSep
|
templinfo["UserSep"] = userSep
|
||||||
|
if r == nil {
|
||||||
|
return templinfo
|
||||||
|
}
|
||||||
if u := login.GetUserInfo(r); u != nil {
|
if u := login.GetUserInfo(r); u != nil {
|
||||||
templinfo["UserInfo"], _ = butwhatabout(u.Username)
|
templinfo["UserInfo"], _ = butwhatabout(u.Username)
|
||||||
templinfo["UserStyle"] = getuserstyle(u)
|
templinfo["UserStyle"] = getuserstyle(u)
|
||||||
|
@ -96,9 +102,50 @@ func getInfo(r *http.Request) map[string]interface{} {
|
||||||
return templinfo
|
return templinfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var oldnews = gencache.New(gencache.Options[string, []byte]{
|
||||||
|
Fill: func(url string) ([]byte, bool) {
|
||||||
|
templinfo := getInfo(nil)
|
||||||
|
var honks []*Honk
|
||||||
|
var userid int64 = -1
|
||||||
|
|
||||||
|
templinfo["ServerMessage"] = serverMsg
|
||||||
|
switch url {
|
||||||
|
case "/events":
|
||||||
|
honks = geteventhonks(userid)
|
||||||
|
templinfo["ServerMessage"] = "some recent and upcoming events"
|
||||||
|
default:
|
||||||
|
templinfo["ShowRSS"] = true
|
||||||
|
honks = getpublichonks()
|
||||||
|
}
|
||||||
|
reverbolate(userid, honks)
|
||||||
|
templinfo["Honks"] = honks
|
||||||
|
templinfo["MapLink"] = getmaplink(nil)
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := readviews.Execute(&buf, "honkpage.html", templinfo)
|
||||||
|
if err != nil {
|
||||||
|
elog.Print(err)
|
||||||
|
}
|
||||||
|
return buf.Bytes(), true
|
||||||
|
|
||||||
|
},
|
||||||
|
Duration: 1 * time.Minute,
|
||||||
|
})
|
||||||
|
|
||||||
|
func lonelypage(w http.ResponseWriter, r *http.Request) {
|
||||||
|
page, _ := oldnews.Get(r.URL.Path)
|
||||||
|
if !develMode {
|
||||||
|
w.Header().Set("Cache-Control", "max-age=60")
|
||||||
|
}
|
||||||
|
w.Write(page)
|
||||||
|
}
|
||||||
|
|
||||||
func homepage(w http.ResponseWriter, r *http.Request) {
|
func homepage(w http.ResponseWriter, r *http.Request) {
|
||||||
templinfo := getInfo(r)
|
|
||||||
u := login.GetUserInfo(r)
|
u := login.GetUserInfo(r)
|
||||||
|
if u == nil {
|
||||||
|
lonelypage(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
templinfo := getInfo(r)
|
||||||
var honks []*Honk
|
var honks []*Honk
|
||||||
var userid int64 = -1
|
var userid int64 = -1
|
||||||
|
|
||||||
|
@ -449,7 +496,20 @@ func inbox(w http.ResponseWriter, r *http.Request) {
|
||||||
addreaction(user, obj, who, content)
|
addreaction(user, obj, who, content)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
go xonksaver(user, j, origin)
|
go saveandcheck(user, j, origin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveandcheck(user *WhatAbout, j junk.Junk, origin string) {
|
||||||
|
xonk := xonksaver(user, j, origin)
|
||||||
|
if xonk == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if sname := shortname(user.ID, xonk.Honker); sname == "" {
|
||||||
|
dlog.Printf("received unexpected activity from %s", xonk.Honker)
|
||||||
|
if xonk.Whofore == 0 {
|
||||||
|
dlog.Printf("it's not even for me!")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1065,9 +1125,16 @@ func threadsort(honks []*Honk) []*Honk {
|
||||||
}
|
}
|
||||||
p.Style += fmt.Sprintf(" level%d", level)
|
p.Style += fmt.Sprintf(" level%d", level)
|
||||||
childs := kids[p.XID]
|
childs := kids[p.XID]
|
||||||
sort.SliceStable(childs, func(i, j int) bool {
|
if false {
|
||||||
return sameperson(childs[i], p) && !sameperson(childs[j], p)
|
sort.SliceStable(childs, func(i, j int) bool {
|
||||||
})
|
return sameperson(childs[i], p) && !sameperson(childs[j], p)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if true {
|
||||||
|
sort.SliceStable(childs, func(i, j int) bool {
|
||||||
|
return !sameperson(childs[i], p) && sameperson(childs[j], p)
|
||||||
|
})
|
||||||
|
}
|
||||||
for _, h := range childs {
|
for _, h := range childs {
|
||||||
if !done[h] {
|
if !done[h] {
|
||||||
done[h] = true
|
done[h] = true
|
||||||
|
@ -1177,10 +1244,10 @@ func showonehonk(w http.ResponseWriter, r *http.Request) {
|
||||||
//reversehonks(rawhonks)
|
//reversehonks(rawhonks)
|
||||||
rawhonks = threadsort(rawhonks)
|
rawhonks = threadsort(rawhonks)
|
||||||
var honks []*Honk
|
var honks []*Honk
|
||||||
for _, h := range rawhonks {
|
for i, h := range rawhonks {
|
||||||
if h.XID == xid {
|
if h.XID == xid {
|
||||||
templinfo["Honkology"] = honkology(h)
|
templinfo["Honkology"] = honkology(h)
|
||||||
if len(honks) != 0 {
|
if i > 0 {
|
||||||
h.Style += " glow"
|
h.Style += " glow"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1548,11 +1615,15 @@ func edithonkpage(w http.ResponseWriter, r *http.Request) {
|
||||||
templinfo["Duration"] = tm.Duration
|
templinfo["Duration"] = tm.Duration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
templinfo["ServerMessage"] = "honk edit 2"
|
templinfo["ServerMessage"] = "honk edit"
|
||||||
templinfo["IsPreview"] = true
|
templinfo["IsPreview"] = true
|
||||||
templinfo["UpdateXID"] = honk.XID
|
templinfo["UpdateXID"] = honk.XID
|
||||||
if len(honk.Donks) > 0 {
|
if len(honk.Donks) > 0 {
|
||||||
templinfo["SavedFile"] = honk.Donks[0].XID
|
var savedfiles []string
|
||||||
|
for _, d := range honk.Donks {
|
||||||
|
savedfiles = append(savedfiles, fmt.Sprintf("%s:%d", d.XID, d.FileID))
|
||||||
|
}
|
||||||
|
templinfo["SavedFile"] = strings.Join(savedfiles, ",")
|
||||||
}
|
}
|
||||||
err := readviews.Execute(w, "honkpage.html", templinfo)
|
err := readviews.Execute(w, "honkpage.html", templinfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1592,11 +1663,26 @@ func canedithonk(user *WhatAbout, honk *Honk) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func submitdonk(w http.ResponseWriter, r *http.Request) (*Donk, error) {
|
func submitdonk(w http.ResponseWriter, r *http.Request) ([]*Donk, error) {
|
||||||
if !strings.HasPrefix(strings.ToLower(r.Header.Get("Content-Type")), "multipart/form-data") {
|
if !strings.HasPrefix(strings.ToLower(r.Header.Get("Content-Type")), "multipart/form-data") {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
file, filehdr, err := r.FormFile("donk")
|
var donks []*Donk
|
||||||
|
for i, hdr := range r.MultipartForm.File["donk"] {
|
||||||
|
if i > 16 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
donk, err := formtodonk(w, r, hdr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
donks = append(donks, donk)
|
||||||
|
}
|
||||||
|
return donks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func formtodonk(w http.ResponseWriter, r *http.Request, filehdr *multipart.FileHeader) (*Donk, error) {
|
||||||
|
file, err := filehdr.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == http.ErrMissingFile {
|
if err == http.ErrMissingFile {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -1610,7 +1696,7 @@ func submitdonk(w http.ResponseWriter, r *http.Request) (*Donk, error) {
|
||||||
file.Close()
|
file.Close()
|
||||||
data := buf.Bytes()
|
data := buf.Bytes()
|
||||||
var media, name string
|
var media, name string
|
||||||
img, err := shrinkit(data)
|
img, err := bigshrink(data)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
data = img.Data
|
data = img.Data
|
||||||
format := img.Format
|
format := img.Format
|
||||||
|
@ -1770,7 +1856,7 @@ func submithonk(w http.ResponseWriter, r *http.Request) *Honk {
|
||||||
honk.Precis = "re: " + honk.Precis
|
honk.Precis = "re: " + honk.Precis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if updatexid == "" {
|
||||||
honk.Audience = []string{thewholeworld}
|
honk.Audience = []string{thewholeworld}
|
||||||
}
|
}
|
||||||
if honk.Noise != "" && honk.Noise[0] == '@' {
|
if honk.Noise != "" && honk.Noise[0] == '@' {
|
||||||
|
@ -1792,18 +1878,26 @@ func submithonk(w http.ResponseWriter, r *http.Request) *Honk {
|
||||||
honk.Public = loudandproud(honk.Audience)
|
honk.Public = loudandproud(honk.Audience)
|
||||||
honk.Convoy = convoy
|
honk.Convoy = convoy
|
||||||
|
|
||||||
donkxid := r.FormValue("donkxid")
|
donkxid := strings.Join(r.Form["donkxid"], ",")
|
||||||
if donkxid == "" {
|
if donkxid == "" {
|
||||||
d, err := submitdonk(w, r)
|
donks, err := submitdonk(w, r)
|
||||||
if err != nil && err != http.ErrMissingFile {
|
if err != nil && err != http.ErrMissingFile {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if d != nil {
|
if len(donks) > 0 {
|
||||||
honk.Donks = append(honk.Donks, d)
|
honk.Donks = append(honk.Donks, donks...)
|
||||||
donkxid = fmt.Sprintf("%s:%d", d.XID, d.FileID)
|
var xids []string
|
||||||
|
for _, d := range honk.Donks {
|
||||||
|
xids = append(xids, fmt.Sprintf("%s:%d", d.XID, d.FileID))
|
||||||
|
}
|
||||||
|
donkxid = strings.Join(xids, ",")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for _, xid := range r.Form["donkxid"] {
|
xids := strings.Split(donkxid, ",")
|
||||||
|
for i, xid := range xids {
|
||||||
|
if i > 16 {
|
||||||
|
break
|
||||||
|
}
|
||||||
p := strings.Split(xid, ":")
|
p := strings.Split(xid, ":")
|
||||||
xid = p[0]
|
xid = p[0]
|
||||||
url := fmt.Sprintf("https://%s/d/%s", serverName, xid)
|
url := fmt.Sprintf("https://%s/d/%s", serverName, xid)
|
||||||
|
@ -1972,12 +2066,12 @@ func submitchonk(w http.ResponseWriter, r *http.Request) {
|
||||||
Noise: noise,
|
Noise: noise,
|
||||||
Format: format,
|
Format: format,
|
||||||
}
|
}
|
||||||
d, err := submitdonk(w, r)
|
donks, err := submitdonk(w, r)
|
||||||
if err != nil && err != http.ErrMissingFile {
|
if err != nil && err != http.ErrMissingFile {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if d != nil {
|
if len(donks) > 0 {
|
||||||
ch.Donks = append(ch.Donks, d)
|
ch.Donks = append(ch.Donks, donks...)
|
||||||
}
|
}
|
||||||
|
|
||||||
translatechonk(&ch)
|
translatechonk(&ch)
|
||||||
|
@ -2485,15 +2579,16 @@ func apihandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, "%s", h.XID)
|
fmt.Fprintf(w, "%s", h.XID)
|
||||||
case "donk":
|
case "donk":
|
||||||
d, err := submitdonk(w, r)
|
donks, err := submitdonk(w, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if d == nil {
|
if len(donks) == 0 {
|
||||||
http.Error(w, "missing donk", http.StatusBadRequest)
|
http.Error(w, "missing donk", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
d := donks[0]
|
||||||
donkxid := fmt.Sprintf("%s:%d", d.XID, d.FileID)
|
donkxid := fmt.Sprintf("%s:%d", d.XID, d.FileID)
|
||||||
w.Write([]byte(donkxid))
|
w.Write([]byte(donkxid))
|
||||||
case "zonkit":
|
case "zonkit":
|
||||||
|
@ -2643,6 +2738,22 @@ func emuinit() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var savedassetparams = make(map[string]string)
|
||||||
|
|
||||||
|
func getassetparam(file string) string {
|
||||||
|
if p, ok := savedassetparams[file]; ok {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
data, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
hasher := sha512.New()
|
||||||
|
hasher.Write(data)
|
||||||
|
|
||||||
|
return fmt.Sprintf("?v=%.8x", hasher.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
func serve() {
|
func serve() {
|
||||||
db := opendatabase()
|
db := opendatabase()
|
||||||
login.Init(login.InitArgs{Db: db, Logger: ilog, Insecure: develMode, SameSiteStrict: !develMode})
|
login.Init(login.InitArgs{Db: db, Logger: ilog, Insecure: develMode, SameSiteStrict: !develMode})
|
||||||
|
@ -2685,6 +2796,7 @@ func serve() {
|
||||||
viewDir + "/views/style.css",
|
viewDir + "/views/style.css",
|
||||||
dataDir + "/views/local.css",
|
dataDir + "/views/local.css",
|
||||||
viewDir + "/views/honkpage.js",
|
viewDir + "/views/honkpage.js",
|
||||||
|
viewDir + "/views/misc.js",
|
||||||
dataDir + "/views/local.js",
|
dataDir + "/views/local.js",
|
||||||
}
|
}
|
||||||
for _, s := range assets {
|
for _, s := range assets {
|
||||||
|
|
Loading…
Reference in New Issue