mirror of
https://github.com/fiatjaf/nak.git
synced 2025-12-09 17:18:50 +00:00
Compare commits
6 Commits
v0.17.1
...
789c6a3884
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
789c6a3884 | ||
|
|
eb6fdfdd39 | ||
|
|
2b189756d1 | ||
|
|
530b484662 | ||
|
|
3ff4dbe196 | ||
|
|
2de3ff78ee |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
nak
|
nak
|
||||||
mnt
|
mnt
|
||||||
nak.exe
|
nak.exe
|
||||||
|
qtbox
|
||||||
|
|||||||
8
go.mod
8
go.mod
@@ -1,10 +1,11 @@
|
|||||||
module github.com/fiatjaf/nak
|
module github.com/fiatjaf/nak
|
||||||
|
|
||||||
go 1.24.1
|
go 1.25
|
||||||
|
|
||||||
require (
|
require (
|
||||||
fiatjaf.com/lib v0.3.1
|
fiatjaf.com/lib v0.3.1
|
||||||
fiatjaf.com/nostr v0.0.0-20251124002842-de54dd1fa4b8
|
fiatjaf.com/nostr v0.0.0-20251126101225-44130595c606
|
||||||
|
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||||
github.com/bep/debounce v1.2.1
|
github.com/bep/debounce v1.2.1
|
||||||
github.com/btcsuite/btcd/btcec/v2 v2.3.6
|
github.com/btcsuite/btcd/btcec/v2 v2.3.6
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
|
||||||
@@ -21,6 +22,7 @@ require (
|
|||||||
github.com/mdp/qrterminal/v3 v3.2.1
|
github.com/mdp/qrterminal/v3 v3.2.1
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1
|
github.com/puzpuzpuz/xsync/v3 v3.5.1
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.10.0
|
||||||
|
github.com/therecipe/qt v0.0.0-20200904063919-c0c124a5770d
|
||||||
github.com/urfave/cli/v3 v3.0.0-beta1
|
github.com/urfave/cli/v3 v3.0.0-beta1
|
||||||
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6
|
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6
|
||||||
golang.org/x/sync v0.18.0
|
golang.org/x/sync v0.18.0
|
||||||
@@ -28,7 +30,6 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/AlecAivazis/survey/v2 v2.3.7 // indirect
|
|
||||||
github.com/FastFilter/xorfilter v0.2.1 // indirect
|
github.com/FastFilter/xorfilter v0.2.1 // indirect
|
||||||
github.com/ImVexed/fasturl v0.0.0-20230304231329-4e41488060f3 // indirect
|
github.com/ImVexed/fasturl v0.0.0-20230304231329-4e41488060f3 // indirect
|
||||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||||
@@ -50,6 +51,7 @@ require (
|
|||||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||||
github.com/go-git/go-git/v5 v5.16.3 // indirect
|
github.com/go-git/go-git/v5 v5.16.3 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e // indirect
|
||||||
github.com/hablullah/go-hijri v1.0.2 // indirect
|
github.com/hablullah/go-hijri v1.0.2 // indirect
|
||||||
github.com/hablullah/go-juliandays v1.0.0 // indirect
|
github.com/hablullah/go-juliandays v1.0.0 // indirect
|
||||||
github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958 // indirect
|
github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958 // indirect
|
||||||
|
|||||||
24
go.sum
24
go.sum
@@ -1,13 +1,14 @@
|
|||||||
fiatjaf.com/lib v0.3.1 h1:/oFQwNtFRfV+ukmOCxfBEAuayoLwXp4wu2/fz5iHpwA=
|
fiatjaf.com/lib v0.3.1 h1:/oFQwNtFRfV+ukmOCxfBEAuayoLwXp4wu2/fz5iHpwA=
|
||||||
fiatjaf.com/lib v0.3.1/go.mod h1:Ycqq3+mJ9jAWu7XjbQI1cVr+OFgnHn79dQR5oTII47g=
|
fiatjaf.com/lib v0.3.1/go.mod h1:Ycqq3+mJ9jAWu7XjbQI1cVr+OFgnHn79dQR5oTII47g=
|
||||||
fiatjaf.com/nostr v0.0.0-20251124002842-de54dd1fa4b8 h1:R16mnlJ3qvVar7G4rzY+Z+mEAf2O6wpHTlRlHAt2Od8=
|
fiatjaf.com/nostr v0.0.0-20251126101225-44130595c606 h1:wQHJ0TFA0Fuq92p/6u6AbsBFq6ZVToSdxV6puXVIruI=
|
||||||
fiatjaf.com/nostr v0.0.0-20251124002842-de54dd1fa4b8/go.mod h1:QEGyTgAjjTFwDx2BJGZiCdmoAcWA/G+sQy7wDqKzSPU=
|
fiatjaf.com/nostr v0.0.0-20251126101225-44130595c606/go.mod h1:ue7yw0zHfZj23Ml2kVSdBx0ENEaZiuvGxs/8VEN93FU=
|
||||||
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
|
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
|
||||||
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
|
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
|
||||||
github.com/FastFilter/xorfilter v0.2.1 h1:lbdeLG9BdpquK64ZsleBS8B4xO/QW1IM0gMzF7KaBKc=
|
github.com/FastFilter/xorfilter v0.2.1 h1:lbdeLG9BdpquK64ZsleBS8B4xO/QW1IM0gMzF7KaBKc=
|
||||||
github.com/FastFilter/xorfilter v0.2.1/go.mod h1:aumvdkhscz6YBZF9ZA/6O4fIoNod4YR50kIVGGZ7l9I=
|
github.com/FastFilter/xorfilter v0.2.1/go.mod h1:aumvdkhscz6YBZF9ZA/6O4fIoNod4YR50kIVGGZ7l9I=
|
||||||
github.com/ImVexed/fasturl v0.0.0-20230304231329-4e41488060f3 h1:ClzzXMDDuUbWfNNZqGeYq4PnYOlwlOVIvSyNaIy0ykg=
|
github.com/ImVexed/fasturl v0.0.0-20230304231329-4e41488060f3 h1:ClzzXMDDuUbWfNNZqGeYq4PnYOlwlOVIvSyNaIy0ykg=
|
||||||
github.com/ImVexed/fasturl v0.0.0-20230304231329-4e41488060f3/go.mod h1:we0YA5CsBbH5+/NUzC/AlMmxaDtWlXeNsqrwXjTzmzA=
|
github.com/ImVexed/fasturl v0.0.0-20230304231329-4e41488060f3/go.mod h1:we0YA5CsBbH5+/NUzC/AlMmxaDtWlXeNsqrwXjTzmzA=
|
||||||
|
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
|
||||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
|
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
|
||||||
github.com/PowerDNS/lmdb-go v1.9.3 h1:AUMY2pZT8WRpkEv39I9Id3MuoHd+NZbTVpNhruVkPTg=
|
github.com/PowerDNS/lmdb-go v1.9.3 h1:AUMY2pZT8WRpkEv39I9Id3MuoHd+NZbTVpNhruVkPTg=
|
||||||
github.com/PowerDNS/lmdb-go v1.9.3/go.mod h1:TE0l+EZK8Z1B4dx070ZxkWTlp8RG1mjN0/+FkFRQMtU=
|
github.com/PowerDNS/lmdb-go v1.9.3/go.mod h1:TE0l+EZK8Z1B4dx070ZxkWTlp8RG1mjN0/+FkFRQMtU=
|
||||||
@@ -56,6 +57,7 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWs
|
|||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g=
|
github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g=
|
||||||
github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
|
github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
|
||||||
|
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
|
||||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -105,6 +107,8 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX
|
|||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e h1:XWcjeEtTFTOVA9Fs1w7n2XBftk5ib4oZrhzWk0B+3eA=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/hablullah/go-hijri v1.0.2 h1:drT/MZpSZJQXo7jftf5fthArShcaMtsal0Zf/dnmp6k=
|
github.com/hablullah/go-hijri v1.0.2 h1:drT/MZpSZJQXo7jftf5fthArShcaMtsal0Zf/dnmp6k=
|
||||||
github.com/hablullah/go-hijri v1.0.2/go.mod h1:OS5qyYLDjORXzK4O1adFw9Q5WfhOcMdAKglDkcTxgWQ=
|
github.com/hablullah/go-hijri v1.0.2/go.mod h1:OS5qyYLDjORXzK4O1adFw9Q5WfhOcMdAKglDkcTxgWQ=
|
||||||
@@ -112,6 +116,7 @@ github.com/hablullah/go-juliandays v1.0.0 h1:A8YM7wIj16SzlKT0SRJc9CD29iiaUzpBLzh
|
|||||||
github.com/hablullah/go-juliandays v1.0.0/go.mod h1:0JOYq4oFOuDja+oospuc61YoX+uNEn7Z6uHYTbBzdGc=
|
github.com/hablullah/go-juliandays v1.0.0/go.mod h1:0JOYq4oFOuDja+oospuc61YoX+uNEn7Z6uHYTbBzdGc=
|
||||||
github.com/hanwen/go-fuse/v2 v2.7.2 h1:SbJP1sUP+n1UF8NXBA14BuojmTez+mDgOk0bC057HQw=
|
github.com/hanwen/go-fuse/v2 v2.7.2 h1:SbJP1sUP+n1UF8NXBA14BuojmTez+mDgOk0bC057HQw=
|
||||||
github.com/hanwen/go-fuse/v2 v2.7.2/go.mod h1:ugNaD/iv5JYyS1Rcvi57Wz7/vrLQJo10mmketmoef48=
|
github.com/hanwen/go-fuse/v2 v2.7.2/go.mod h1:ugNaD/iv5JYyS1Rcvi57Wz7/vrLQJo10mmketmoef48=
|
||||||
|
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
||||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958 h1:qxLoi6CAcXVzjfvu+KXIXJOAsQB62LXjsfbOaErsVzE=
|
github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958 h1:qxLoi6CAcXVzjfvu+KXIXJOAsQB62LXjsfbOaErsVzE=
|
||||||
@@ -128,6 +133,8 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:C
|
|||||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
@@ -184,7 +191,11 @@ github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
|
|||||||
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||||
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 h1:D0vL7YNisV2yqE55+q0lFuGse6U8lxlg7fYTctlT5Gc=
|
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 h1:D0vL7YNisV2yqE55+q0lFuGse6U8lxlg7fYTctlT5Gc=
|
||||||
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
|
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
|
||||||
|
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
@@ -197,6 +208,8 @@ github.com/templexxx/xhex v0.0.0-20200614015412-aed53437177b h1:XeDLE6c9mzHpdv3W
|
|||||||
github.com/templexxx/xhex v0.0.0-20200614015412-aed53437177b/go.mod h1:7rwmCH0wC2fQvNEvPZ3sKXukhyCTyiaZ5VTZMQYpZKQ=
|
github.com/templexxx/xhex v0.0.0-20200614015412-aed53437177b/go.mod h1:7rwmCH0wC2fQvNEvPZ3sKXukhyCTyiaZ5VTZMQYpZKQ=
|
||||||
github.com/tetratelabs/wazero v1.8.0 h1:iEKu0d4c2Pd+QSRieYbnQC9yiFlMS9D+Jr0LsRmcF4g=
|
github.com/tetratelabs/wazero v1.8.0 h1:iEKu0d4c2Pd+QSRieYbnQC9yiFlMS9D+Jr0LsRmcF4g=
|
||||||
github.com/tetratelabs/wazero v1.8.0/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs=
|
github.com/tetratelabs/wazero v1.8.0/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs=
|
||||||
|
github.com/therecipe/qt v0.0.0-20200904063919-c0c124a5770d h1:T+d8FnaLSvM/1BdlDXhW4d5dr2F07bAbB+LpgzMxx+o=
|
||||||
|
github.com/therecipe/qt v0.0.0-20200904063919-c0c124a5770d/go.mod h1:SUUR2j3aE1z6/g76SdD6NwACEpvCxb3fvG82eKbD6us=
|
||||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
@@ -226,6 +239,7 @@ go.etcd.io/bbolt v1.4.2 h1:IrUHp260R8c+zYx/Tm8QZr04CX+qWS5PGfPdevhdm1I=
|
|||||||
go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM=
|
go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM=
|
||||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||||
@@ -235,7 +249,9 @@ golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6/go.mod h1:46edojNIoXTNOhySWI
|
|||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190420063019-afa5a82059c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
@@ -248,10 +264,13 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/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.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -277,6 +296,7 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|||||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||||
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-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|||||||
1
main.go
1
main.go
@@ -49,6 +49,7 @@ var app = &cli.Command{
|
|||||||
fsCmd,
|
fsCmd,
|
||||||
publish,
|
publish,
|
||||||
git,
|
git,
|
||||||
|
nip,
|
||||||
},
|
},
|
||||||
Version: version,
|
Version: version,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
|
|||||||
186
nip.go
Normal file
186
nip.go
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
var nip = &cli.Command{
|
||||||
|
Name: "nip",
|
||||||
|
Usage: "get the description of a NIP from its number",
|
||||||
|
Description: `fetches the NIPs README from GitHub and parses it to find the description of the given NIP number.
|
||||||
|
|
||||||
|
example:
|
||||||
|
nak nip 1
|
||||||
|
nak nip list
|
||||||
|
nak nip open 1`,
|
||||||
|
ArgsUsage: "<NIP number>",
|
||||||
|
Commands: []*cli.Command{
|
||||||
|
{
|
||||||
|
Name: "list",
|
||||||
|
Usage: "list all NIPs",
|
||||||
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
|
return iterateNips(func(nip, desc, link string) bool {
|
||||||
|
stdout(nip + ": " + desc)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "open",
|
||||||
|
Usage: "open the NIP page in the browser",
|
||||||
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
|
reqNum := c.Args().First()
|
||||||
|
if reqNum == "" {
|
||||||
|
return fmt.Errorf("missing NIP number")
|
||||||
|
}
|
||||||
|
|
||||||
|
normalize := func(s string) string {
|
||||||
|
s = strings.ToLower(s)
|
||||||
|
s = strings.TrimPrefix(s, "nip-")
|
||||||
|
s = strings.TrimLeft(s, "0")
|
||||||
|
if s == "" {
|
||||||
|
s = "0"
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
reqNum = normalize(reqNum)
|
||||||
|
|
||||||
|
foundLink := ""
|
||||||
|
err := iterateNips(func(nip, desc, link string) bool {
|
||||||
|
nipNum := normalize(nip)
|
||||||
|
if nipNum == reqNum {
|
||||||
|
foundLink = link
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if foundLink == "" {
|
||||||
|
return fmt.Errorf("NIP-%s not found", strings.ToUpper(reqNum))
|
||||||
|
}
|
||||||
|
|
||||||
|
url := "https://github.com/nostr-protocol/nips/blob/master/" + foundLink
|
||||||
|
fmt.Println("Opening " + url)
|
||||||
|
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "darwin":
|
||||||
|
cmd = exec.Command("open", url)
|
||||||
|
case "windows":
|
||||||
|
cmd = exec.Command("cmd", "/c", "start", url)
|
||||||
|
default:
|
||||||
|
cmd = exec.Command("xdg-open", url)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd.Start()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
|
reqNum := c.Args().First()
|
||||||
|
if reqNum == "" {
|
||||||
|
return fmt.Errorf("missing NIP number")
|
||||||
|
}
|
||||||
|
|
||||||
|
normalize := func(s string) string {
|
||||||
|
s = strings.ToLower(s)
|
||||||
|
s = strings.TrimPrefix(s, "nip-")
|
||||||
|
s = strings.TrimLeft(s, "0")
|
||||||
|
if s == "" {
|
||||||
|
s = "0"
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
reqNum = normalize(reqNum)
|
||||||
|
|
||||||
|
found := false
|
||||||
|
err := iterateNips(func(nip, desc, link string) bool {
|
||||||
|
nipNum := normalize(nip)
|
||||||
|
|
||||||
|
if nipNum == reqNum {
|
||||||
|
stdout(strings.TrimSpace(desc))
|
||||||
|
found = true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return fmt.Errorf("NIP-%s not found", strings.ToUpper(reqNum))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func iterateNips(yield func(nip, desc, link string) bool) error {
|
||||||
|
resp, err := http.Get("https://raw.githubusercontent.com/nostr-protocol/nips/master/README.md")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to fetch NIPs README: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read NIPs README: %w", err)
|
||||||
|
}
|
||||||
|
bodyStr := string(body)
|
||||||
|
epoch := strings.Index(bodyStr, "## List")
|
||||||
|
|
||||||
|
lines := strings.SplitSeq(bodyStr[epoch+8:], "\n")
|
||||||
|
for line := range lines {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if strings.HasPrefix(line, "##") {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(line, "- [NIP-") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
start := strings.Index(line, "[")
|
||||||
|
end := strings.Index(line, "]")
|
||||||
|
if start == -1 || end == -1 || end < start {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
content := line[start+1 : end]
|
||||||
|
|
||||||
|
parts := strings.SplitN(content, ":", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
nipPart := parts[0]
|
||||||
|
descPart := parts[1]
|
||||||
|
|
||||||
|
rest := line[end+1:]
|
||||||
|
linkStart := strings.Index(rest, "(")
|
||||||
|
linkEnd := strings.Index(rest, ")")
|
||||||
|
link := ""
|
||||||
|
if linkStart != -1 && linkEnd != -1 && linkEnd > linkStart {
|
||||||
|
link = rest[linkStart+1 : linkEnd]
|
||||||
|
}
|
||||||
|
|
||||||
|
if !yield(nipPart, strings.TrimSpace(descPart), link) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
5
view/.gitignore
vendored
Normal file
5
view/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
dist
|
||||||
|
view
|
||||||
|
*.json
|
||||||
|
deploy
|
||||||
|
qtbox
|
||||||
13
view/Makefile
Normal file
13
view/Makefile
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
dist: deploy/linux/nakv deploy/windows/nakv.exe
|
||||||
|
mkdir -p dist
|
||||||
|
cd deploy/linux && tar -czvf nakv_linux.tar.gz nakv
|
||||||
|
mv deploy/linux/nakv_linux.tar.gz dist/
|
||||||
|
rm -f deploy/windows/nakv_windows.zip
|
||||||
|
cd deploy/windows && zip nakv_windows *.exe
|
||||||
|
mv deploy/windows/nakv_windows.zip dist/
|
||||||
|
|
||||||
|
deploy/linux/nakv: $(shell find . -name "*.go")
|
||||||
|
qtdeploy -ldflags="-s -w" -fast build desktop github.com/fiatjaf/nakv
|
||||||
|
|
||||||
|
deploy/windows/nakv.exe: $(shell find . -name "*.go")
|
||||||
|
qtdeploy -ldflags="-s -w" -docker build windows_64_static
|
||||||
201
view/event.go
Normal file
201
view/event.go
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"fiatjaf.com/nostr"
|
||||||
|
"github.com/therecipe/qt/core"
|
||||||
|
"github.com/therecipe/qt/widgets"
|
||||||
|
)
|
||||||
|
|
||||||
|
var event struct {
|
||||||
|
kindSpin *widgets.QSpinBox
|
||||||
|
kindNameLabel *widgets.QLabel
|
||||||
|
tagRows [][]*widgets.QLineEdit
|
||||||
|
contentEdit *widgets.QTextEdit
|
||||||
|
createdAtEdit *widgets.QDateTimeEdit
|
||||||
|
outputEdit *widgets.QTextEdit
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateEvent() {
|
||||||
|
kind := nostr.Kind(event.kindSpin.Value())
|
||||||
|
kindName := kind.Name()
|
||||||
|
if kindName != "unknown" {
|
||||||
|
event.kindNameLabel.SetText(kindName)
|
||||||
|
} else {
|
||||||
|
event.kindNameLabel.SetText("")
|
||||||
|
}
|
||||||
|
tags := make(nostr.Tags, 0, len(event.tagRows))
|
||||||
|
for y, tagItems := range event.tagRows {
|
||||||
|
if y == len(event.tagRows)-1 && strings.TrimSpace(tagItems[0].Text()) == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tag := make(nostr.Tag, 0, len(tagItems))
|
||||||
|
for x, edit := range tagItems {
|
||||||
|
text := strings.TrimSpace(edit.Text())
|
||||||
|
if x == len(tagItems)-1 && text == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
text = decodeTagValue(text)
|
||||||
|
tag = append(tag, text)
|
||||||
|
}
|
||||||
|
if len(tag) > 0 {
|
||||||
|
tags = append(tags, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := nostr.Event{
|
||||||
|
Kind: kind,
|
||||||
|
Content: event.contentEdit.ToPlainText(),
|
||||||
|
CreatedAt: nostr.Timestamp(event.createdAtEdit.DateTime().ToMSecsSinceEpoch() / 1000),
|
||||||
|
Tags: tags,
|
||||||
|
}
|
||||||
|
|
||||||
|
finalize := func() {
|
||||||
|
jsonBytes, _ := json.MarshalIndent(result, "", " ")
|
||||||
|
event.outputEdit.SetPlainText(string(jsonBytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentKeyer != nil {
|
||||||
|
signAndFinalize := func() {
|
||||||
|
if currentKeyer != nil {
|
||||||
|
if err := currentKeyer.SignEvent(ctx, &result); err == nil {
|
||||||
|
finalize()
|
||||||
|
} else {
|
||||||
|
statusLabel.SetText("failed to sign: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentSec == [32]byte{} {
|
||||||
|
// empty key, we must have a bunker
|
||||||
|
debounced.Call(signAndFinalize)
|
||||||
|
} else {
|
||||||
|
// we have a key, can sign immediately
|
||||||
|
signAndFinalize()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finalize()
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupEventTab() *widgets.QWidget {
|
||||||
|
tab := widgets.NewQWidget(nil, 0)
|
||||||
|
|
||||||
|
// set up event tab
|
||||||
|
layout := widgets.NewQVBoxLayout()
|
||||||
|
tab.SetLayout(layout)
|
||||||
|
|
||||||
|
// kind input
|
||||||
|
kindHBox := widgets.NewQHBoxLayout()
|
||||||
|
layout.AddLayout(kindHBox, 0)
|
||||||
|
kindLabel := widgets.NewQLabel2("kind:", nil, 0)
|
||||||
|
kindHBox.AddWidget(kindLabel, 0, 0)
|
||||||
|
event.kindSpin = widgets.NewQSpinBox(nil)
|
||||||
|
event.kindSpin.SetValue(1)
|
||||||
|
event.kindSpin.SetMinimum(0)
|
||||||
|
event.kindSpin.SetMaximum(1<<16 - 1)
|
||||||
|
kindHBox.AddWidget(event.kindSpin, 0, 0)
|
||||||
|
event.kindSpin.ConnectValueChanged(func(int) {
|
||||||
|
updateEvent()
|
||||||
|
})
|
||||||
|
event.kindNameLabel = widgets.NewQLabel2("", nil, 0)
|
||||||
|
kindHBox.AddWidget(event.kindNameLabel, 0, 0)
|
||||||
|
|
||||||
|
// content input
|
||||||
|
contentLabel := widgets.NewQLabel2("content:", nil, 0)
|
||||||
|
layout.AddWidget(contentLabel, 0, 0)
|
||||||
|
event.contentEdit = widgets.NewQTextEdit(nil)
|
||||||
|
layout.AddWidget(event.contentEdit, 0, 0)
|
||||||
|
event.contentEdit.ConnectTextChanged(updateEvent)
|
||||||
|
|
||||||
|
// created_at input
|
||||||
|
createdAtLabel := widgets.NewQLabel2("created at:", nil, 0)
|
||||||
|
layout.AddWidget(createdAtLabel, 0, 0)
|
||||||
|
event.createdAtEdit = widgets.NewQDateTimeEdit(nil)
|
||||||
|
event.createdAtEdit.SetDateTime(core.QDateTime_CurrentDateTime())
|
||||||
|
layout.AddWidget(event.createdAtEdit, 0, 0)
|
||||||
|
event.createdAtEdit.ConnectDateTimeChanged(func(*core.QDateTime) {
|
||||||
|
updateEvent()
|
||||||
|
})
|
||||||
|
|
||||||
|
// tags input
|
||||||
|
tagsLabel := widgets.NewQLabel2("tags:", nil, 0)
|
||||||
|
layout.AddWidget(tagsLabel, 0, 0)
|
||||||
|
tagsLayout := widgets.NewQVBoxLayout()
|
||||||
|
tagRowHBoxes := make([]widgets.QLayout_ITF, 0, 2)
|
||||||
|
event.tagRows = make([][]*widgets.QLineEdit, 0, 2)
|
||||||
|
layout.AddLayout(tagsLayout, 0)
|
||||||
|
|
||||||
|
var addTagRow func()
|
||||||
|
addTagRow = func() {
|
||||||
|
hbox := widgets.NewQHBoxLayout()
|
||||||
|
tagRowHBoxes = append(tagRowHBoxes, hbox)
|
||||||
|
tagsLayout.AddLayout(hbox, 0)
|
||||||
|
tagItems := []*widgets.QLineEdit{}
|
||||||
|
y := len(event.tagRows)
|
||||||
|
event.tagRows = append(event.tagRows, tagItems)
|
||||||
|
|
||||||
|
var addItem func()
|
||||||
|
addItem = func() {
|
||||||
|
edit := widgets.NewQLineEdit(nil)
|
||||||
|
hbox.AddWidget(edit, 0, 0)
|
||||||
|
x := len(tagItems)
|
||||||
|
tagItems = append(tagItems, edit)
|
||||||
|
event.tagRows[y] = tagItems
|
||||||
|
edit.ConnectTextChanged(func(text string) {
|
||||||
|
if strings.TrimSpace(text) != "" {
|
||||||
|
// when an item input has been filled check if we have to show more
|
||||||
|
if y == len(event.tagRows)-1 {
|
||||||
|
addTagRow()
|
||||||
|
}
|
||||||
|
if x == len(tagItems)-1 {
|
||||||
|
addItem()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// do this when an item input has been emptied: check if we need to remove an item from this row
|
||||||
|
nItems := len(tagItems)
|
||||||
|
if nItems >= 2 && strings.TrimSpace(tagItems[nItems-1].Text()) == "" && strings.TrimSpace(tagItems[nItems-2].Text()) == "" {
|
||||||
|
// remove last item if the last 2 are empty
|
||||||
|
hbox.Layout().RemoveWidget(tagItems[nItems-1])
|
||||||
|
tagItems[nItems-1].DeleteLater()
|
||||||
|
tagItems = tagItems[0 : nItems-1]
|
||||||
|
event.tagRows[y] = tagItems
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we need to remove rows
|
||||||
|
nRows := len(event.tagRows)
|
||||||
|
itemIsFilled := func(edit *widgets.QLineEdit) bool { return strings.TrimSpace(edit.Text()) != "" }
|
||||||
|
if nRows >= 2 && !slices.ContainsFunc(event.tagRows[nRows-1], itemIsFilled) && !slices.ContainsFunc(event.tagRows[nRows-2], itemIsFilled) {
|
||||||
|
// remove the last row if the last 2 are empty
|
||||||
|
tagsLayout.RemoveItem(tagRowHBoxes[nRows-1])
|
||||||
|
for _, tagItem := range event.tagRows[nRows-1] {
|
||||||
|
tagItem.DeleteLater()
|
||||||
|
}
|
||||||
|
tagRowHBoxes[nRows-1].QLayout_PTR().DeleteLater()
|
||||||
|
event.tagRows = event.tagRows[0 : nRows-1]
|
||||||
|
tagRowHBoxes = tagRowHBoxes[0 : nRows-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateEvent()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
addItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
// first
|
||||||
|
addTagRow()
|
||||||
|
|
||||||
|
// output JSON
|
||||||
|
outputLabel := widgets.NewQLabel2("event:", nil, 0)
|
||||||
|
layout.AddWidget(outputLabel, 0, 0)
|
||||||
|
event.outputEdit = widgets.NewQTextEdit(nil)
|
||||||
|
event.outputEdit.SetReadOnly(true)
|
||||||
|
layout.AddWidget(event.outputEdit, 0, 0)
|
||||||
|
|
||||||
|
return tab
|
||||||
|
}
|
||||||
49
view/helpers.go
Normal file
49
view/helpers.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"fiatjaf.com/nostr"
|
||||||
|
"fiatjaf.com/nostr/keyer"
|
||||||
|
"fiatjaf.com/nostr/nip19"
|
||||||
|
"fiatjaf.com/nostr/nip46"
|
||||||
|
)
|
||||||
|
|
||||||
|
func handleSecretKeyOrBunker(sec string) (nostr.SecretKey, nostr.Keyer, error) {
|
||||||
|
if strings.HasPrefix(sec, "bunker://") {
|
||||||
|
// it's a bunker
|
||||||
|
bunkerURL := sec
|
||||||
|
clientKey := nostr.Generate()
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
bunker, err := nip46.ConnectBunker(ctx, clientKey, bunkerURL, nil, func(s string) {})
|
||||||
|
if err != nil {
|
||||||
|
return nostr.SecretKey{}, nil, fmt.Errorf("failed to connect to %s: %w", bunkerURL, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nostr.SecretKey{}, keyer.NewBunkerSignerFromBunkerClient(bunker), err
|
||||||
|
}
|
||||||
|
|
||||||
|
if prefix, ski, err := nip19.Decode(sec); err == nil && prefix == "nsec" {
|
||||||
|
sk := ski.(nostr.SecretKey)
|
||||||
|
return sk, keyer.NewPlainKeySigner(sk), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sk, err := nostr.SecretKeyFromHex(sec)
|
||||||
|
if err != nil {
|
||||||
|
return nostr.SecretKey{}, nil, fmt.Errorf("invalid secret key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sk, keyer.NewPlainKeySigner(sk), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeTagValue(value string) string {
|
||||||
|
if strings.HasPrefix(value, "npub1") || strings.HasPrefix(value, "nevent1") || strings.HasPrefix(value, "note1") || strings.HasPrefix(value, "nprofile1") || strings.HasPrefix(value, "naddr1") {
|
||||||
|
if ptr, err := nip19.ToPointer(value); err == nil {
|
||||||
|
return ptr.AsTagReference()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
139
view/main.go
Normal file
139
view/main.go
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"fiatjaf.com/lib/debouncer"
|
||||||
|
"fiatjaf.com/nostr"
|
||||||
|
"fiatjaf.com/nostr/nip19"
|
||||||
|
"fiatjaf.com/nostr/nip49"
|
||||||
|
"fiatjaf.com/nostr/sdk"
|
||||||
|
"github.com/therecipe/qt/widgets"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
currentSec nostr.SecretKey
|
||||||
|
currentKeyer nostr.Keyer
|
||||||
|
statusLabel *widgets.QLabel
|
||||||
|
debounced = debouncer.New(800 * time.Millisecond)
|
||||||
|
sys = sdk.NewSystem()
|
||||||
|
ctx = context.Background()
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := widgets.NewQApplication(len(os.Args), os.Args)
|
||||||
|
|
||||||
|
window := widgets.NewQMainWindow(nil, 0)
|
||||||
|
window.SetMinimumSize2(800, 600)
|
||||||
|
window.SetWindowTitle("nakv")
|
||||||
|
|
||||||
|
centralWidget := widgets.NewQWidget(nil, 0)
|
||||||
|
window.SetCentralWidget(centralWidget)
|
||||||
|
|
||||||
|
mainLayout := widgets.NewQVBoxLayout()
|
||||||
|
centralWidget.SetLayout(mainLayout)
|
||||||
|
|
||||||
|
// private key input
|
||||||
|
secLabel := widgets.NewQLabel2("private key (hex or nsec):", nil, 0)
|
||||||
|
mainLayout.AddWidget(secLabel, 0, 0)
|
||||||
|
|
||||||
|
secHBox := widgets.NewQHBoxLayout()
|
||||||
|
mainLayout.AddLayout(secHBox, 0)
|
||||||
|
secEdit := widgets.NewQLineEdit(nil)
|
||||||
|
secHBox.AddWidget(secEdit, 0, 0)
|
||||||
|
generateButton := widgets.NewQPushButton2("generate", nil)
|
||||||
|
secHBox.AddWidget(generateButton, 0, 0)
|
||||||
|
|
||||||
|
// password input
|
||||||
|
passwordHBox := widgets.NewQHBoxLayout()
|
||||||
|
passwordWidget := widgets.NewQWidget(nil, 0)
|
||||||
|
passwordWidget.SetLayout(passwordHBox)
|
||||||
|
passwordWidget.SetVisible(false)
|
||||||
|
mainLayout.AddWidget(passwordWidget, 0, 0)
|
||||||
|
passwordLabel := widgets.NewQLabel2("password:", nil, 0)
|
||||||
|
passwordHBox.AddWidget(passwordLabel, 0, 0)
|
||||||
|
secPasswordEdit := widgets.NewQLineEdit(nil)
|
||||||
|
secPasswordEdit.SetEchoMode(widgets.QLineEdit__Password)
|
||||||
|
passwordHBox.AddWidget(secPasswordEdit, 0, 0)
|
||||||
|
keyChanged := func(text string) {
|
||||||
|
text = strings.TrimSpace(text)
|
||||||
|
|
||||||
|
var sk nostr.SecretKey
|
||||||
|
var keyer nostr.Keyer
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if text == "" {
|
||||||
|
passwordWidget.SetVisible(false)
|
||||||
|
goto empty
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(text, "ncryptsec1") {
|
||||||
|
passwordWidget.SetVisible(true)
|
||||||
|
password := secPasswordEdit.Text()
|
||||||
|
if password != "" {
|
||||||
|
sk, err = nip49.Decrypt(text, password)
|
||||||
|
if err != nil {
|
||||||
|
statusLabel.SetText("decryption failed: " + err.Error())
|
||||||
|
goto empty
|
||||||
|
}
|
||||||
|
text = hex.EncodeToString(sk[:])
|
||||||
|
} else {
|
||||||
|
goto empty
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
passwordWidget.SetVisible(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
sk, keyer, err = handleSecretKeyOrBunker(text)
|
||||||
|
if err != nil {
|
||||||
|
statusLabel.SetText(err.Error())
|
||||||
|
currentSec = nostr.SecretKey{}
|
||||||
|
currentKeyer = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
currentSec = sk
|
||||||
|
currentKeyer = keyer
|
||||||
|
statusLabel.SetText("")
|
||||||
|
updateEvent()
|
||||||
|
return
|
||||||
|
|
||||||
|
empty:
|
||||||
|
currentSec = nostr.SecretKey{}
|
||||||
|
currentKeyer = nil
|
||||||
|
statusLabel.SetText("")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
secEdit.ConnectTextChanged(keyChanged)
|
||||||
|
secPasswordEdit.ConnectTextChanged(keyChanged)
|
||||||
|
generateButton.ConnectClicked(func(bool) {
|
||||||
|
sk := nostr.Generate()
|
||||||
|
nsec := nip19.EncodeNsec(sk)
|
||||||
|
secEdit.SetText(nsec)
|
||||||
|
keyChanged(nsec)
|
||||||
|
})
|
||||||
|
|
||||||
|
tabWidget := widgets.NewQTabWidget(nil)
|
||||||
|
|
||||||
|
eventTab := setupEventTab()
|
||||||
|
reqTab := setupReqTab()
|
||||||
|
|
||||||
|
tabWidget.AddTab(eventTab, "event")
|
||||||
|
tabWidget.AddTab(reqTab, "req")
|
||||||
|
|
||||||
|
mainLayout.AddWidget(tabWidget, 0, 0)
|
||||||
|
|
||||||
|
statusLabel = widgets.NewQLabel2("", nil, 0)
|
||||||
|
mainLayout.AddWidget(statusLabel, 0, 0)
|
||||||
|
|
||||||
|
// initial render
|
||||||
|
updateEvent()
|
||||||
|
updateReq()
|
||||||
|
|
||||||
|
window.Show()
|
||||||
|
app.Exec()
|
||||||
|
}
|
||||||
353
view/req.go
Normal file
353
view/req.go
Normal file
@@ -0,0 +1,353 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"fiatjaf.com/nostr"
|
||||||
|
"github.com/therecipe/qt/core"
|
||||||
|
"github.com/therecipe/qt/widgets"
|
||||||
|
)
|
||||||
|
|
||||||
|
type reqVars struct {
|
||||||
|
authorsEdits []*widgets.QLineEdit
|
||||||
|
idsEdits []*widgets.QLineEdit
|
||||||
|
kindsEdits []*widgets.QLineEdit
|
||||||
|
kindsLabels []*widgets.QLabel
|
||||||
|
relaysEdits []*widgets.QLineEdit
|
||||||
|
sinceEdit *widgets.QDateTimeEdit
|
||||||
|
untilEdit *widgets.QDateTimeEdit
|
||||||
|
limitSpin *widgets.QSpinBox
|
||||||
|
|
||||||
|
filter nostr.Filter
|
||||||
|
|
||||||
|
outputEdit *widgets.QTextEdit
|
||||||
|
resultsEdit *widgets.QTextEdit
|
||||||
|
}
|
||||||
|
|
||||||
|
var req = reqVars{}
|
||||||
|
|
||||||
|
func setupReqTab() *widgets.QWidget {
|
||||||
|
tab := widgets.NewQWidget(nil, 0)
|
||||||
|
layout := widgets.NewQVBoxLayout()
|
||||||
|
tab.SetLayout(layout)
|
||||||
|
|
||||||
|
// authors
|
||||||
|
authorsLabel := widgets.NewQLabel2("authors:", nil, 0)
|
||||||
|
layout.AddWidget(authorsLabel, 0, 0)
|
||||||
|
authorsVBox := widgets.NewQVBoxLayout()
|
||||||
|
layout.AddLayout(authorsVBox, 0)
|
||||||
|
req.authorsEdits = []*widgets.QLineEdit{}
|
||||||
|
var addAuthorEdit func()
|
||||||
|
addAuthorEdit = func() {
|
||||||
|
edit := widgets.NewQLineEdit(nil)
|
||||||
|
req.authorsEdits = append(req.authorsEdits, edit)
|
||||||
|
authorsVBox.AddWidget(edit, 0, 0)
|
||||||
|
edit.ConnectTextChanged(func(text string) {
|
||||||
|
if strings.TrimSpace(text) != "" {
|
||||||
|
if edit == req.authorsEdits[len(req.authorsEdits)-1] {
|
||||||
|
addAuthorEdit()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
n := len(req.authorsEdits)
|
||||||
|
if n >= 2 && strings.TrimSpace(req.authorsEdits[n-1].Text()) == "" && strings.TrimSpace(req.authorsEdits[n-2].Text()) == "" {
|
||||||
|
authorsVBox.Layout().RemoveWidget(req.authorsEdits[n-1])
|
||||||
|
req.authorsEdits[n-1].DeleteLater()
|
||||||
|
req.authorsEdits = req.authorsEdits[0 : n-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateReq()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
addAuthorEdit()
|
||||||
|
|
||||||
|
// ids
|
||||||
|
idsLabel := widgets.NewQLabel2("ids:", nil, 0)
|
||||||
|
layout.AddWidget(idsLabel, 0, 0)
|
||||||
|
idsVBox := widgets.NewQVBoxLayout()
|
||||||
|
layout.AddLayout(idsVBox, 0)
|
||||||
|
req.idsEdits = []*widgets.QLineEdit{}
|
||||||
|
var addIdEdit func()
|
||||||
|
addIdEdit = func() {
|
||||||
|
edit := widgets.NewQLineEdit(nil)
|
||||||
|
req.idsEdits = append(req.idsEdits, edit)
|
||||||
|
idsVBox.AddWidget(edit, 0, 0)
|
||||||
|
edit.ConnectTextChanged(func(text string) {
|
||||||
|
if strings.TrimSpace(text) != "" {
|
||||||
|
if edit == req.idsEdits[len(req.idsEdits)-1] {
|
||||||
|
addIdEdit()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
n := len(req.idsEdits)
|
||||||
|
if n >= 2 && strings.TrimSpace(req.idsEdits[n-1].Text()) == "" && strings.TrimSpace(req.idsEdits[n-2].Text()) == "" {
|
||||||
|
idsVBox.Layout().RemoveWidget(req.idsEdits[n-1])
|
||||||
|
req.idsEdits[n-1].DeleteLater()
|
||||||
|
req.idsEdits = req.idsEdits[0 : n-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateReq()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
addIdEdit()
|
||||||
|
|
||||||
|
// kinds
|
||||||
|
kindsLabel := widgets.NewQLabel2("kinds:", nil, 0)
|
||||||
|
layout.AddWidget(kindsLabel, 0, 0)
|
||||||
|
kindsVBox := widgets.NewQVBoxLayout()
|
||||||
|
layout.AddLayout(kindsVBox, 0)
|
||||||
|
req.kindsEdits = []*widgets.QLineEdit{}
|
||||||
|
req.kindsLabels = []*widgets.QLabel{}
|
||||||
|
var addKindEdit func()
|
||||||
|
addKindEdit = func() {
|
||||||
|
hbox := widgets.NewQHBoxLayout()
|
||||||
|
kindsVBox.AddLayout(hbox, 0)
|
||||||
|
edit := widgets.NewQLineEdit(nil)
|
||||||
|
req.kindsEdits = append(req.kindsEdits, edit)
|
||||||
|
hbox.AddWidget(edit, 0, 0)
|
||||||
|
label := widgets.NewQLabel2("", nil, 0)
|
||||||
|
req.kindsLabels = append(req.kindsLabels, label)
|
||||||
|
hbox.AddWidget(label, 0, 0)
|
||||||
|
edit.ConnectTextChanged(func(text string) {
|
||||||
|
if strings.TrimSpace(text) != "" {
|
||||||
|
if edit == req.kindsEdits[len(req.kindsEdits)-1] {
|
||||||
|
addKindEdit()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
n := len(req.kindsEdits)
|
||||||
|
if n >= 2 && strings.TrimSpace(req.kindsEdits[n-1].Text()) == "" && strings.TrimSpace(req.kindsEdits[n-2].Text()) == "" {
|
||||||
|
lastItem := kindsVBox.ItemAt(kindsVBox.Count() - 1)
|
||||||
|
kindsVBox.RemoveItem(lastItem)
|
||||||
|
lastHBox := lastItem.Layout()
|
||||||
|
lastHBox.RemoveWidget(req.kindsEdits[n-1])
|
||||||
|
lastHBox.RemoveWidget(req.kindsLabels[n-1])
|
||||||
|
req.kindsEdits[n-1].DeleteLater()
|
||||||
|
req.kindsLabels[n-1].DeleteLater()
|
||||||
|
lastHBox.DeleteLater()
|
||||||
|
req.kindsEdits = req.kindsEdits[0 : n-1]
|
||||||
|
req.kindsLabels = req.kindsLabels[0 : n-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateReq()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
addKindEdit()
|
||||||
|
|
||||||
|
// since
|
||||||
|
sinceHBox := widgets.NewQHBoxLayout()
|
||||||
|
layout.AddLayout(sinceHBox, 0)
|
||||||
|
sinceLabel := widgets.NewQLabel2("since:", nil, 0)
|
||||||
|
sinceHBox.AddWidget(sinceLabel, 0, 0)
|
||||||
|
req.sinceEdit = widgets.NewQDateTimeEdit(nil)
|
||||||
|
{
|
||||||
|
time := core.NewQDateTime3(core.NewQDate3(0, 0, 0), core.NewQTime3(0, 0, 0, 0), 0)
|
||||||
|
time.SetMSecsSinceEpoch(0)
|
||||||
|
req.sinceEdit.SetDateTime(time)
|
||||||
|
}
|
||||||
|
sinceHBox.AddWidget(req.sinceEdit, 0, 0)
|
||||||
|
req.sinceEdit.ConnectDateTimeChanged(func(*core.QDateTime) {
|
||||||
|
updateReq()
|
||||||
|
})
|
||||||
|
|
||||||
|
// until
|
||||||
|
untilHBox := widgets.NewQHBoxLayout()
|
||||||
|
layout.AddLayout(untilHBox, 0)
|
||||||
|
untilLabel := widgets.NewQLabel2("until:", nil, 0)
|
||||||
|
untilHBox.AddWidget(untilLabel, 0, 0)
|
||||||
|
req.untilEdit = widgets.NewQDateTimeEdit(nil)
|
||||||
|
{
|
||||||
|
time := core.NewQDateTime3(core.NewQDate3(0, 0, 0), core.NewQTime3(0, 0, 0, 0), 0)
|
||||||
|
time.SetMSecsSinceEpoch(0)
|
||||||
|
req.untilEdit.SetDateTime(time)
|
||||||
|
}
|
||||||
|
untilHBox.AddWidget(req.untilEdit, 0, 0)
|
||||||
|
req.untilEdit.ConnectDateTimeChanged(func(*core.QDateTime) {
|
||||||
|
updateReq()
|
||||||
|
})
|
||||||
|
|
||||||
|
// limit
|
||||||
|
limitHBox := widgets.NewQHBoxLayout()
|
||||||
|
layout.AddLayout(limitHBox, 0)
|
||||||
|
limitLabel := widgets.NewQLabel2("limit:", nil, 0)
|
||||||
|
limitHBox.AddWidget(limitLabel, 0, 0)
|
||||||
|
req.limitSpin = widgets.NewQSpinBox(nil)
|
||||||
|
req.limitSpin.SetMinimum(0)
|
||||||
|
req.limitSpin.SetMaximum(1000)
|
||||||
|
limitHBox.AddWidget(req.limitSpin, 0, 0)
|
||||||
|
req.limitSpin.ConnectValueChanged(func(int) {
|
||||||
|
updateReq()
|
||||||
|
})
|
||||||
|
|
||||||
|
// output
|
||||||
|
outputLabel := widgets.NewQLabel2("filter:", nil, 0)
|
||||||
|
layout.AddWidget(outputLabel, 0, 0)
|
||||||
|
req.outputEdit = widgets.NewQTextEdit(nil)
|
||||||
|
req.outputEdit.SetReadOnly(true)
|
||||||
|
layout.AddWidget(req.outputEdit, 0, 0)
|
||||||
|
|
||||||
|
// relays
|
||||||
|
relaysLabel := widgets.NewQLabel2("relays:", nil, 0)
|
||||||
|
layout.AddWidget(relaysLabel, 0, 0)
|
||||||
|
relaysVBox := widgets.NewQVBoxLayout()
|
||||||
|
layout.AddLayout(relaysVBox, 0)
|
||||||
|
req.relaysEdits = []*widgets.QLineEdit{}
|
||||||
|
var addRelayEdit func()
|
||||||
|
addRelayEdit = func() {
|
||||||
|
edit := widgets.NewQLineEdit(nil)
|
||||||
|
req.relaysEdits = append(req.relaysEdits, edit)
|
||||||
|
relaysVBox.AddWidget(edit, 0, 0)
|
||||||
|
edit.ConnectTextChanged(func(text string) {
|
||||||
|
if strings.TrimSpace(text) != "" {
|
||||||
|
if edit == req.relaysEdits[len(req.relaysEdits)-1] {
|
||||||
|
addRelayEdit()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
n := len(req.relaysEdits)
|
||||||
|
if n >= 2 && strings.TrimSpace(req.relaysEdits[n-1].Text()) == "" && strings.TrimSpace(req.relaysEdits[n-2].Text()) == "" {
|
||||||
|
relaysVBox.Layout().RemoveWidget(req.relaysEdits[n-1])
|
||||||
|
req.relaysEdits[n-1].DeleteLater()
|
||||||
|
req.relaysEdits = req.relaysEdits[0 : n-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
addRelayEdit()
|
||||||
|
|
||||||
|
// send button
|
||||||
|
buttonHBox := widgets.NewQHBoxLayout()
|
||||||
|
layout.AddLayout(buttonHBox, 0)
|
||||||
|
sendButton := widgets.NewQPushButton2("send request", nil)
|
||||||
|
buttonHBox.AddWidget(sendButton, 0, 0)
|
||||||
|
buttonHBox.AddStretch(1)
|
||||||
|
|
||||||
|
// results
|
||||||
|
resultsLabel := widgets.NewQLabel2("results:", nil, 0)
|
||||||
|
layout.AddWidget(resultsLabel, 0, 0)
|
||||||
|
req.resultsEdit = widgets.NewQTextEdit(nil)
|
||||||
|
req.resultsEdit.SetReadOnly(true)
|
||||||
|
layout.AddWidget(req.resultsEdit, 0, 0)
|
||||||
|
|
||||||
|
sendButton.ConnectClicked(func(checked bool) {
|
||||||
|
req.subscribe()
|
||||||
|
})
|
||||||
|
|
||||||
|
return tab
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateReq() {
|
||||||
|
req.filter = nostr.Filter{}
|
||||||
|
|
||||||
|
// collect authors
|
||||||
|
authors := []nostr.PubKey{}
|
||||||
|
for _, edit := range req.authorsEdits {
|
||||||
|
if pk, err := nostr.PubKeyFromHex(strings.TrimSpace(edit.Text())); err == nil {
|
||||||
|
authors = append(authors, pk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(authors) > 0 {
|
||||||
|
req.filter.Authors = authors
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect ids
|
||||||
|
ids := []nostr.ID{}
|
||||||
|
for _, edit := range req.idsEdits {
|
||||||
|
if id, err := nostr.IDFromHex(strings.TrimSpace(edit.Text())); err == nil {
|
||||||
|
ids = append(ids, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(ids) > 0 {
|
||||||
|
req.filter.IDs = ids
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect kinds
|
||||||
|
kinds := []nostr.Kind{}
|
||||||
|
for _, edit := range req.kindsEdits {
|
||||||
|
text := strings.TrimSpace(edit.Text())
|
||||||
|
if k, err := strconv.Atoi(text); err == nil {
|
||||||
|
kinds = append(kinds, nostr.Kind(k))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(kinds) > 0 {
|
||||||
|
req.filter.Kinds = kinds
|
||||||
|
}
|
||||||
|
|
||||||
|
// update kind labels
|
||||||
|
for i, kind := range kinds {
|
||||||
|
if i < len(req.kindsLabels) {
|
||||||
|
name := kind.Name()
|
||||||
|
if name != "unknown" {
|
||||||
|
req.kindsLabels[i].SetText(name)
|
||||||
|
} else {
|
||||||
|
req.kindsLabels[i].SetText("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := len(kinds); i < len(req.kindsLabels); i++ {
|
||||||
|
req.kindsLabels[i].SetText("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// since
|
||||||
|
if req.sinceEdit.DateTime().IsValid() {
|
||||||
|
ts := nostr.Timestamp(req.sinceEdit.DateTime().ToMSecsSinceEpoch() / 1000)
|
||||||
|
req.filter.Since = ts
|
||||||
|
}
|
||||||
|
|
||||||
|
// until
|
||||||
|
if req.untilEdit.DateTime().IsValid() {
|
||||||
|
ts := nostr.Timestamp(req.untilEdit.DateTime().ToMSecsSinceEpoch() / 1000)
|
||||||
|
req.filter.Until = ts
|
||||||
|
}
|
||||||
|
|
||||||
|
// limit
|
||||||
|
if req.limitSpin.Value() > 0 {
|
||||||
|
req.filter.Limit = req.limitSpin.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonBytes, _ := json.Marshal(req.filter)
|
||||||
|
req.outputEdit.SetPlainText(string(jsonBytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req reqVars) subscribe() {
|
||||||
|
// collect relays
|
||||||
|
relays := []string{}
|
||||||
|
for _, edit := range req.relaysEdits {
|
||||||
|
url := strings.TrimSpace(edit.Text())
|
||||||
|
if url != "" {
|
||||||
|
relays = append(relays, url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(relays) == 0 {
|
||||||
|
statusLabel.SetText("no relays specified")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// subscribe
|
||||||
|
statusLabel.SetText("subscribed to " + strings.Join(relays, " "))
|
||||||
|
eoseChan := make(chan struct{})
|
||||||
|
eventsChan := sys.Pool.SubscribeManyNotifyEOSE(ctx, relays, req.filter, eoseChan, nostr.SubscriptionOptions{
|
||||||
|
Label: "nakv-req",
|
||||||
|
})
|
||||||
|
|
||||||
|
// collect events
|
||||||
|
go func() {
|
||||||
|
eosed := false
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case ie, ok := <-eventsChan:
|
||||||
|
if !ok {
|
||||||
|
statusLabel.SetText("subscription ended")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonBytes, _ := json.Marshal(ie.Event)
|
||||||
|
if eosed {
|
||||||
|
req.resultsEdit.SetPlainText(string(jsonBytes) + "\n" + req.resultsEdit.ToPlainText())
|
||||||
|
} else {
|
||||||
|
req.resultsEdit.InsertPlainText("\n" + string(jsonBytes))
|
||||||
|
}
|
||||||
|
case <-eoseChan:
|
||||||
|
eosed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user