- commit
- 65b128e
- parent
- ca51aef
- author
- Eric Bower
- date
- 2024-05-07 13:13:13 +0000 UTC
feat: senpai integration (#129) Co-authored-by: Antonio Mika <antoniomika@gmail.com>
11 files changed,
+223,
-16
+2,
-0
1@@ -56,10 +56,12 @@ ENTRYPOINT ["/app/web"]
2 FROM scratch as release-ssh
3
4 WORKDIR /app
5+ENV TERM="xterm-256color"
6
7 ARG APP=prose
8
9 COPY --from=builder-ssh /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
10 COPY --from=builder-ssh /go/bin/${APP}-ssh ./ssh
11
12+
13 ENTRYPOINT ["/app/ssh"]
M
db/db.go
+2,
-1
1@@ -340,7 +340,8 @@ type DB interface {
2 FindUserForToken(token string) (*User, error)
3 FindTokensForUser(userID string) ([]*Token, error)
4 InsertToken(userID, name string) (string, error)
5- FindRssToken(userID string) (string, error)
6+ UpsertToken(userID, name string) (string, error)
7+ FindTokenByName(userID, name string) (string, error)
8 RemoveToken(tokenID string) error
9
10 FindPosts() ([]*Post, error)
+16,
-6
1@@ -147,10 +147,10 @@ const (
2 FROM app_users
3 LEFT JOIN tokens ON tokens.user_id = app_users.id
4 WHERE tokens.token = $1 AND tokens.expires_at > NOW()`
5- sqlInsertToken = `INSERT INTO tokens (user_id, name) VALUES($1, $2) RETURNING token;`
6- sqlRemoveToken = `DELETE FROM tokens WHERE id = $1`
7- sqlSelectTokensForUser = `SELECT id, user_id, name, created_at, expires_at FROM tokens WHERE user_id = $1`
8- sqlSelectRssTokenForUser = `SELECT token FROM tokens WHERE user_id = $1 AND name = 'pico-rss'`
9+ sqlInsertToken = `INSERT INTO tokens (user_id, name) VALUES($1, $2) RETURNING token;`
10+ sqlRemoveToken = `DELETE FROM tokens WHERE id = $1`
11+ sqlSelectTokensForUser = `SELECT id, user_id, name, created_at, expires_at FROM tokens WHERE user_id = $1`
12+ sqlSelectTokenByNameForUser = `SELECT token FROM tokens WHERE user_id = $1 AND name = $2`
13
14 sqlSelectTotalUsers = `SELECT count(id) FROM app_users`
15 sqlSelectUsersAfterDate = `SELECT count(id) FROM app_users WHERE created_at >= $1`
16@@ -1789,9 +1789,19 @@ func (me *PsqlDB) InsertToken(userID, name string) (string, error) {
17 return token, nil
18 }
19
20-func (me *PsqlDB) FindRssToken(userID string) (string, error) {
21+func (me *PsqlDB) UpsertToken(userID, name string) (string, error) {
22+ token, _ := me.FindTokenByName(userID, name)
23+ if token != "" {
24+ return token, nil
25+ }
26+
27+ token, err := me.InsertToken(userID, name)
28+ return token, err
29+}
30+
31+func (me *PsqlDB) FindTokenByName(userID, name string) (string, error) {
32 var token string
33- err := me.Db.QueryRow(sqlSelectRssTokenForUser, userID).Scan(&token)
34+ err := me.Db.QueryRow(sqlSelectTokenByNameForUser, userID, name).Scan(&token)
35 if err != nil {
36 return "", err
37 }
M
go.mod
+13,
-0
1@@ -9,7 +9,12 @@ replace github.com/charmbracelet/wish => github.com/charmbracelet/wish v1.2.0
2
3 replace github.com/charmbracelet/ssh => github.com/charmbracelet/ssh v0.0.0-20230822194956-1a051f898e09
4
5+replace git.sr.ht/~delthas/senpai => github.com/picosh/senpai v0.0.0-20240503200611-af89e73973b0
6+
7+replace github.com/gdamore/tcell/v2 => github.com/delthas/tcell/v2 v2.4.1-0.20230710100648-1489e78d90fb
8+
9 require (
10+ git.sr.ht/~delthas/senpai v0.3.1-0.20240425235039-206be659439e
11 github.com/alecthomas/chroma v0.10.0
12 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
13 github.com/charmbracelet/bubbles v0.16.1
14@@ -47,6 +52,7 @@ require (
15 )
16
17 require (
18+ git.sr.ht/~emersion/go-scfg v0.0.0-20231004133111-9dce55c8d63b // indirect
19 github.com/DavidGamba/go-getoptions v0.29.0 // indirect
20 github.com/PuerkitoBio/goquery v1.8.1 // indirect
21 github.com/andybalholm/cascadia v1.3.2 // indirect
22@@ -61,6 +67,8 @@ require (
23 github.com/charmbracelet/log v0.3.1 // indirect
24 github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
25 github.com/creack/pty v1.1.21 // indirect
26+ github.com/delthas/go-libnp v0.0.0-20221222161248-0e45ece1f878 // indirect
27+ github.com/delthas/go-localeinfo v0.0.0-20221116001557-686a1e185118 // indirect
28 github.com/dlclark/regexp2 v1.10.0 // indirect
29 github.com/dsoprea/go-exif v0.0.0-20230826092837-6579e82b732d // indirect
30 github.com/dsoprea/go-exif/v2 v2.0.0-20230826092837-6579e82b732d // indirect
31@@ -71,10 +79,13 @@ require (
32 github.com/dsoprea/go-utility v0.0.0-20221003172846-a3e1774ef349 // indirect
33 github.com/dustin/go-humanize v1.0.1 // indirect
34 github.com/forPelevin/gomoji v1.1.3 // indirect
35+ github.com/gdamore/encoding v1.0.0 // indirect
36+ github.com/gdamore/tcell/v2 v2.6.1-0.20230327043120-47ec3a77754f // indirect
37 github.com/go-errors/errors v1.5.1 // indirect
38 github.com/go-logfmt/logfmt v0.6.0 // indirect
39 github.com/go-ole/go-ole v1.3.0 // indirect
40 github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect
41+ github.com/godbus/dbus/v5 v5.1.0 // indirect
42 github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
43 github.com/golang/geo v0.0.0-20230421003525-6adc56603217 // indirect
44 github.com/golang/protobuf v1.5.3 // indirect
45@@ -129,6 +140,8 @@ require (
46 golang.org/x/sys v0.16.0 // indirect
47 golang.org/x/term v0.16.0 // indirect
48 golang.org/x/text v0.14.0 // indirect
49+ golang.org/x/time v0.4.0 // indirect
50 google.golang.org/protobuf v1.31.0 // indirect
51 gopkg.in/ini.v1 v1.67.0 // indirect
52+ mvdan.cc/xurls/v2 v2.5.0 // indirect
53 )
M
go.sum
+28,
-0
1@@ -1,3 +1,5 @@
2+git.sr.ht/~emersion/go-scfg v0.0.0-20231004133111-9dce55c8d63b h1:Lf4oYBOJVmbYzrfqWfXUvSpXQPNMgnbN0efn5A7bH3M=
3+git.sr.ht/~emersion/go-scfg v0.0.0-20231004133111-9dce55c8d63b/go.mod h1:ybgvEJTIx5XbaspSviB3KNa6OdPmAZqDoSud7z8fFlw=
4 github.com/DavidGamba/go-getoptions v0.29.0 h1:cU8MjOyfAyPZke4hrgEuiGBJHS9PFYPAHve2fhDhdDk=
5 github.com/DavidGamba/go-getoptions v0.29.0/go.mod h1:zE97E3PR9P3BI/HKyNYgdMlYxodcuiC6W68KIgeYT84=
6 github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
7@@ -47,6 +49,12 @@ github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr
8 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
9 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
10 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
11+github.com/delthas/go-libnp v0.0.0-20221222161248-0e45ece1f878 h1:v8W8eW7eb2bHFXBA80UKcoe0TvEu46NlTHSDRvgAbMU=
12+github.com/delthas/go-libnp v0.0.0-20221222161248-0e45ece1f878/go.mod h1:aGVXnhWpDlt5U4SphG97o1gszctZKvBTXy320E8Buw4=
13+github.com/delthas/go-localeinfo v0.0.0-20221116001557-686a1e185118 h1:Xzf9ra1QRJXD62gwudjI2iBq7x9CusvHd83Dg2OnUmE=
14+github.com/delthas/go-localeinfo v0.0.0-20221116001557-686a1e185118/go.mod h1:sG54BxlyQgIskYURLrg7mvhoGBe0Qq12DNtYRALwNa4=
15+github.com/delthas/tcell/v2 v2.4.1-0.20230710100648-1489e78d90fb h1:x0hrYPzXpmn3L/4QnW0UXJnHX9oz0OcZNcsSgregusw=
16+github.com/delthas/tcell/v2 v2.4.1-0.20230710100648-1489e78d90fb/go.mod h1:be9omFATkdr0D9qewWW3d+MEvl5dha+Etb5y65J2H8Y=
17 github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
18 github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
19 github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
20@@ -78,6 +86,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
21 github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
22 github.com/forPelevin/gomoji v1.1.3 h1:7c3dYzVmYhpOL3bS4riXqSWJBX3BhSvH68yoNNf3FH0=
23 github.com/forPelevin/gomoji v1.1.3/go.mod h1:ypB7Kz3Fsp+LVR7KoT7mEFOioYBuTuAtaAT4RGl+ASY=
24+github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
25+github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
26 github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
27 github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
28 github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
29@@ -91,6 +101,8 @@ github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW
30 github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
31 github.com/go-xmlfmt/xmlfmt v1.1.2 h1:Nea7b4icn8s57fTx1M5AI4qQT5HEM3rVUO8MuE6g80U=
32 github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
33+github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
34+github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
35 github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
36 github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
37 github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
38@@ -146,6 +158,7 @@ github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2J
39 github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
40 github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
41 github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
42+github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
43 github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
44 github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
45 github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
46@@ -193,6 +206,8 @@ github.com/picosh/ptun v0.0.0-20240417140706-811cc2b70d9a h1:sBqfT6KBIYllVaw4bT1
47 github.com/picosh/ptun v0.0.0-20240417140706-811cc2b70d9a/go.mod h1:WXCrhe0l9VL3ji0pdhvSJD6LLx99rJoAA/+PUQXf0Mo=
48 github.com/picosh/send v0.0.0-20240217194807-77b972121e63 h1:VSSbAejFzj2KBThfVnMcNXQwzHmwjPUridgi29LxihU=
49 github.com/picosh/send v0.0.0-20240217194807-77b972121e63/go.mod h1:1JCq0NVOdTDenQ0/Kd8e4rP80lu06UHJJ+6dQxhcpew=
50+github.com/picosh/senpai v0.0.0-20240503200611-af89e73973b0 h1:pBRIbiCj7K6rGELijb//dYhyCo8A3fvxW5dijrJVtjs=
51+github.com/picosh/senpai v0.0.0-20240503200611-af89e73973b0/go.mod h1:QaBDtybFC5gz7EG/9c3bgzuyW7W5W2rYLFZxWNuWk3Q=
52 github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo=
53 github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk=
54 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
55@@ -213,8 +228,10 @@ github.com/prometheus/prom2json v1.3.3 h1:IYfSMiZ7sSOfliBoo89PcufjWO4eAR0gznGcET
56 github.com/prometheus/prom2json v1.3.3/go.mod h1:Pv4yIPktEkK7btWsrUTWDDDrnpUrAELaOCj+oFwlgmc=
57 github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
58 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
59+github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
60 github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
61 github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
62+github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
63 github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
64 github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
65 github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
66@@ -281,12 +298,14 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh
67 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
68 golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
69 golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
70+golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
71 golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
72 golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
73 golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 h1:mchzmB1XO2pMaKFRqk/+MV3mgGG96aqaPXaMifQU47w=
74 golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
75 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
76 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
77+golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
78 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
79 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
80 golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
81@@ -303,6 +322,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
82 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
83 golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
84 golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
85+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
86+golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
87 golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
88 golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
89 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
90@@ -330,6 +351,7 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
91 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
92 golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
93 golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
94+golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
95 golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
96 golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
97 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
98@@ -337,6 +359,8 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX
99 golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
100 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
101 golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
102+golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
103+golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
104 golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
105 golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
106 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
107@@ -348,6 +372,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
108 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
109 golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
110 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
111+golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY=
112+golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
113 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
114 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
115 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
116@@ -370,5 +396,7 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
117 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
118 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
119 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
120+mvdan.cc/xurls/v2 v2.5.0 h1:lyBNOm8Wo71UknhUs4QTFUNNMyxy2JEIaKKo0RWOh+8=
121+mvdan.cc/xurls/v2 v2.5.0/go.mod h1:yQgaGQ1rFtJUzkmKiHYSSfuQxqfYmd//X6PxvholpeE=
122 pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw=
123 pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
+14,
-0
1@@ -488,6 +488,10 @@ figure {
2 margin-left: 0.5rem;
3 }
4
5+.pt-0 {
6+ padding-top: 0;
7+}
8+
9 .my {
10 margin-top: 0.5rem;
11 margin-bottom: 0.5rem;
12@@ -572,6 +576,12 @@ figure {
13 gap: 0.5rem;
14 }
15
16+.group-2 {
17+ display: flex;
18+ flex-direction: column;
19+ gap: 1rem;
20+}
21+
22 .group-h {
23 display: flex;
24 gap: 0.5rem;
25@@ -710,4 +720,8 @@ figure {
26 header {
27 margin: 0;
28 }
29+
30+ .flex-collapse {
31+ flex-direction: column;
32+ }
33 }
+26,
-8
1@@ -123,8 +123,8 @@ func WishMiddleware(handler *CliHandler) wish.Middleware {
2
3 return func(next ssh.Handler) ssh.Handler {
4 return func(sesh ssh.Session) {
5- _, _, activePty := sesh.Pty()
6- if activePty {
7+ args := sesh.Command()
8+ if len(args) == 0 {
9 next(sesh)
10 return
11 }
12@@ -135,7 +135,30 @@ func WishMiddleware(handler *CliHandler) wish.Middleware {
13 return
14 }
15
16- args := sesh.Command()
17+ if len(args) > 0 && args[0] == "chat" {
18+ _, _, hasPty := sesh.Pty()
19+ if !hasPty {
20+ wish.Fatalln(
21+ sesh,
22+ "In order to render chat you need to enable PTY with the `ssh -t` flag",
23+ )
24+ return
25+ }
26+
27+ pass, err := dbpool.UpsertToken(user.ID, "pico-chat")
28+ if err != nil {
29+ wish.Fatalln(sesh, err)
30+ return
31+ }
32+ app, err := shared.NewSenpaiApp(sesh, user.Name, pass)
33+ if err != nil {
34+ wish.Fatalln(sesh, err)
35+ return
36+ }
37+ app.Run()
38+ app.Close()
39+ return
40+ }
41
42 opts := Cmd{
43 Session: sesh,
44@@ -145,11 +168,6 @@ func WishMiddleware(handler *CliHandler) wish.Middleware {
45 Write: false,
46 }
47
48- if len(args) == 0 {
49- next(sesh)
50- return
51- }
52-
53 cmd := strings.TrimSpace(args[0])
54 if len(args) == 1 {
55 if cmd == "help" {
+14,
-0
1@@ -488,6 +488,10 @@ figure {
2 margin-left: 0.5rem;
3 }
4
5+.pt-0 {
6+ padding-top: 0;
7+}
8+
9 .my {
10 margin-top: 0.5rem;
11 margin-bottom: 0.5rem;
12@@ -572,6 +576,12 @@ figure {
13 gap: 0.5rem;
14 }
15
16+.group-2 {
17+ display: flex;
18+ flex-direction: column;
19+ gap: 1rem;
20+}
21+
22 .group-h {
23 display: flex;
24 gap: 0.5rem;
25@@ -710,4 +720,8 @@ figure {
26 header {
27 margin: 0;
28 }
29+
30+ .flex-collapse {
31+ flex-direction: column;
32+ }
33 }
1@@ -0,0 +1,62 @@
2+package shared
3+
4+import (
5+ "git.sr.ht/~delthas/senpai"
6+ "github.com/charmbracelet/ssh"
7+)
8+
9+type Vtty struct {
10+ ssh.Session
11+}
12+
13+func (v Vtty) Drain() error {
14+ _, err := v.Write([]byte("\033[?25h\033[0 q\033[34h\033[?25h\033[39;49m\033[m^O\033[H\033[J\033[?1049l\033[?1l\033>\033[?1000l\033[?1002l\033[?1003l\033[?1006l\033[?2004l"))
15+ if err != nil {
16+ return err
17+ }
18+
19+ err = v.Exit(0)
20+ if err != nil {
21+ return err
22+ }
23+
24+ err = v.Close()
25+ return err
26+}
27+
28+func (v Vtty) Start() error {
29+ return nil
30+}
31+
32+func (v Vtty) Stop() error {
33+ return nil
34+}
35+
36+func (v Vtty) WindowSize() (width int, height int, err error) {
37+ pty, _, _ := v.Pty()
38+ return pty.Window.Width, pty.Window.Height, nil
39+}
40+
41+func (v Vtty) NotifyResize(cb func()) {
42+ _, notify, _ := v.Pty()
43+ go func() {
44+ for range notify {
45+ cb()
46+ }
47+ }()
48+}
49+
50+func NewSenpaiApp(sesh ssh.Session, username, pass string) (*senpai.App, error) {
51+ vty := Vtty{
52+ sesh,
53+ }
54+ senpaiCfg := senpai.Defaults()
55+ senpaiCfg.TLS = true
56+ senpaiCfg.Addr = "irc.pico.sh:6697"
57+ senpaiCfg.Nick = username
58+ senpaiCfg.Password = &pass
59+ senpaiCfg.Tty = vty
60+
61+ app, err := senpai.NewApp(senpaiCfg)
62+ return app, err
63+}
+45,
-0
1@@ -3,6 +3,7 @@ package tui
2 import (
3 "errors"
4 "fmt"
5+ "io"
6
7 "github.com/charmbracelet/bubbles/spinner"
8 tea "github.com/charmbracelet/bubbletea"
9@@ -30,6 +31,7 @@ const (
10 statusNoAccount
11 statusBrowsingKeys
12 statusBrowsingTokens
13+ statusChat
14 statusQuitting
15 )
16
17@@ -50,6 +52,7 @@ type menuChoice int
18 const (
19 keysChoice menuChoice = iota
20 tokensChoice
21+ chatChoice
22 exitChoice
23 unsetChoice // set when no choice has been made
24 )
25@@ -58,6 +61,7 @@ const (
26 var menuChoices = map[menuChoice]string{
27 keysChoice: "Manage keys",
28 tokensChoice: "Manage tokens",
29+ chatChoice: "Chat",
30 exitChoice: "Exit",
31 }
32
33@@ -87,6 +91,7 @@ func CmsMiddleware(cfg *shared.ConfigSite) bm.Handler {
34 styles := common.DefaultStyles(renderer)
35
36 m := model{
37+ session: s,
38 cfg: cfg,
39 publicKey: s.PublicKey(),
40 dbpool: dbpool,
41@@ -134,6 +139,7 @@ type model struct {
42 tokens tokens.Model
43 createAccount account.CreateModel
44 terminalSize tea.WindowSizeMsg
45+ session ssh.Session
46 }
47
48 func (m model) Init() tea.Cmd {
49@@ -311,6 +317,10 @@ func updateChildren(msg tea.Msg, m model) (model, tea.Cmd) {
50 m.status = statusBrowsingTokens
51 m.menuChoice = unsetChoice
52 cmd = tokens.LoadKeys(m.tokens)
53+ case chatChoice:
54+ m.status = statusChat
55+ m.menuChoice = unsetChoice
56+ cmd = m.loadChat()
57 case exitChoice:
58 m.status = statusQuitting
59 m.dbpool.Close()
60@@ -320,6 +330,41 @@ func updateChildren(msg tea.Msg, m model) (model, tea.Cmd) {
61 return m, cmd
62 }
63
64+type SenpaiCmd struct {
65+ user *db.User
66+ session ssh.Session
67+ dbpool db.DB
68+}
69+
70+func (m *SenpaiCmd) Run() error {
71+ pass, err := m.dbpool.UpsertToken(m.user.ID, "pico-chat")
72+ if err != nil {
73+ return err
74+ }
75+ app, err := shared.NewSenpaiApp(m.session, m.user.Name, pass)
76+ if err != nil {
77+ return err
78+ }
79+ app.Run()
80+ app.Close()
81+ return nil
82+}
83+
84+func (m *SenpaiCmd) SetStdin(io.Reader) {}
85+func (m *SenpaiCmd) SetStdout(io.Writer) {}
86+func (m *SenpaiCmd) SetStderr(io.Writer) {}
87+
88+func (m model) loadChat() tea.Cmd {
89+ sp := &SenpaiCmd{
90+ session: m.session,
91+ dbpool: m.dbpool,
92+ user: m.user,
93+ }
94+ return tea.Exec(sp, func(err error) tea.Msg {
95+ return tea.Quit()
96+ })
97+}
98+
99 func (m model) menuView() string {
100 var s string
101 for i := 0; i < len(menuChoices); i++ {
+1,
-1
1@@ -105,7 +105,7 @@ func findOrCreateRssToken(apiConfig *shared.ApiConfig, ctx ssh.Context) http.Han
2
3 dbpool := shared.GetDB(r)
4 var err error
5- rssToken, _ := dbpool.FindRssToken(user.ID)
6+ rssToken, _ := dbpool.FindTokenByName(user.ID, "pico-rss")
7 if rssToken == "" {
8 rssToken, err = dbpool.InsertToken(user.ID, "pico-rss")
9 if err != nil {