refactor: switch to Arikawa (#178)
* Basic * nav * remove discord pkg * fix guild delete handler * misc * remove ioutil * remove astatine, press f to pay respect * panic bad, log.Fatal good * add preview
This commit is contained in:
parent
7813d7914d
commit
c5be3b3c86
14 changed files with 383 additions and 369 deletions
BIN
.github/preview.png
vendored
BIN
.github/preview.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 202 KiB After Width: | Height: | Size: 236 KiB |
|
|
@ -16,10 +16,10 @@ type IdentifyConfig struct {
|
|||
}
|
||||
|
||||
type KeysConfig struct {
|
||||
ToggleGuildsList string `toml:"toggle_guilds_list"`
|
||||
ToggleChannelsTreeView string `toml:"toggle_channels_tree_view"`
|
||||
ToggleMessagesTextView string `toml:"toggle_messages_text_view"`
|
||||
ToggleMessageInputField string `toml:"toggle_message_input_field"`
|
||||
ToggleGuildsTree string `toml:"toggle_guilds_tree"`
|
||||
ToggleChannelsTree string `toml:"toggle_channels_tree"`
|
||||
ToggleMessagesTextView string `toml:"toggle_messages_text_view"`
|
||||
ToggleMessageInput string `toml:"toggle_message_input"`
|
||||
|
||||
OpenMessageActionsList string `toml:"open_message_actions_list"`
|
||||
OpenExternalEditor string `toml:"open_external_editor"`
|
||||
|
|
@ -39,7 +39,7 @@ type ThemeConfig struct {
|
|||
type Config struct {
|
||||
Mouse bool `toml:"mouse"`
|
||||
Timestamps bool `toml:"timestamps"`
|
||||
MessagesLimit int `toml:"messages_limit"`
|
||||
MessagesLimit uint `toml:"messages_limit"`
|
||||
Timezone string `toml:"timezone"`
|
||||
AttachmentDownloadsDir string `toml:"attachment_downloads_dir"`
|
||||
Identify IdentifyConfig `toml:"identify"`
|
||||
|
|
@ -65,10 +65,10 @@ func New() *Config {
|
|||
Title: "white",
|
||||
},
|
||||
Keys: KeysConfig{
|
||||
ToggleGuildsList: "Rune[g]",
|
||||
ToggleChannelsTreeView: "Rune[c]",
|
||||
ToggleMessagesTextView: "Rune[m]",
|
||||
ToggleMessageInputField: "Rune[i]",
|
||||
ToggleGuildsTree: "Rune[g]",
|
||||
ToggleChannelsTree: "Rune[c]",
|
||||
ToggleMessagesTextView: "Rune[m]",
|
||||
ToggleMessageInput: "Rune[i]",
|
||||
|
||||
OpenMessageActionsList: "Rune[a]",
|
||||
OpenExternalEditor: "Ctrl+E",
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
package discord
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestParseMarkdown(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{"bold", "**test**", "[::b]test[::-]"},
|
||||
{"italic", "*test*", "[::i]test[::-]"},
|
||||
{"underline", "__test__", "[::u]test[::-]"},
|
||||
{"strikethrough", "~~test~~", "[::s]test[::-]"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if got := ParseMarkdown(test.in); got != test.want {
|
||||
t.Errorf("got: %s\nwant: %s", got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParseMarkdown(b *testing.B) {
|
||||
in := `**Porro mollitia aut odio dolor rerum.** Saepe qui aut reiciendis illo nisi. Id illo et quo consequatur sint labore placeat maiores. __Commodi odio quae reprehenderit.__ Beatae illum est fugiat ut architecto itaque eveniet aut. ~~Consequuntur quas explicabo et impedit eum porro facere et.~~
|
||||
|
||||
Sit commodi sed iure et sed quae eveniet. *Sit non distinctio nihil sunt. Nesciunt cumque aspernatur *nulla* porro et earum quidem.* Sed omnis at commodi vel quasi. Fuga et **consequatur** molestias dicta vel provident et aspernatur. Dolorem molestias ipsa aut ~~facilis quae dolorem~~ eveniet dicta.`
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
ParseMarkdown(in)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
package discord
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/ayntgl/astatine"
|
||||
)
|
||||
|
||||
func ChannelToString(c *astatine.Channel) string {
|
||||
var repr string
|
||||
if c.Name != "" {
|
||||
repr = "#" + c.Name
|
||||
} else if len(c.Recipients) == 1 {
|
||||
rp := c.Recipients[0]
|
||||
repr = rp.Username + "#" + rp.Discriminator
|
||||
} else {
|
||||
rps := make([]string, len(c.Recipients))
|
||||
for i, r := range c.Recipients {
|
||||
rps[i] = r.Username + "#" + r.Discriminator
|
||||
}
|
||||
|
||||
repr = strings.Join(rps, ", ")
|
||||
}
|
||||
|
||||
return repr
|
||||
}
|
||||
|
||||
func FindMessageByID(ms []*astatine.Message, mID string) (int, *astatine.Message) {
|
||||
for i, m := range ms {
|
||||
if m.ID == mID {
|
||||
return i, m
|
||||
}
|
||||
}
|
||||
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func HasPermission(s *astatine.State, cID string, p int64) bool {
|
||||
perm, err := s.UserChannelPermissions(s.User.ID, cID)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return perm&p == p
|
||||
}
|
||||
6
go.mod
6
go.mod
|
|
@ -5,7 +5,7 @@ go 1.18
|
|||
require (
|
||||
github.com/BurntSushi/toml v1.1.0
|
||||
github.com/atotto/clipboard v0.1.4
|
||||
github.com/ayntgl/astatine v0.24.1-0.20220324085605-e85d32085ce8
|
||||
github.com/diamondburned/arikawa/v3 v3.0.0
|
||||
github.com/gdamore/tcell/v2 v2.5.1
|
||||
github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
|
||||
|
|
@ -20,13 +20,15 @@ require (
|
|||
github.com/danieljoos/wincred v1.1.2 // indirect
|
||||
github.com/gdamore/encoding v1.0.0 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||
github.com/gorilla/schema v1.2.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 // indirect
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
|
||||
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
|
||||
)
|
||||
|
|
|
|||
44
go.sum
44
go.sum
|
|
@ -1,6 +1,3 @@
|
|||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU=
|
||||
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
|
||||
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0=
|
||||
|
|
@ -9,35 +6,34 @@ github.com/antzucaro/matchr v0.0.0-20210222213004-b04723ef80f0 h1:R/qAiUxFT3mNgQ
|
|||
github.com/antzucaro/matchr v0.0.0-20210222213004-b04723ef80f0/go.mod h1:v3ZDlfVAL1OrkKHbGSFFK60k0/7hruHPDq2XMs9Gu6U=
|
||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||
github.com/ayntgl/astatine v0.24.1-0.20220324085605-e85d32085ce8 h1:tn/I/8Zv22Q8ERV/awDfLTSKQR5eH5AAYpzCIuRUaqw=
|
||||
github.com/ayntgl/astatine v0.24.1-0.20220324085605-e85d32085ce8/go.mod h1:xRC0h8PGhVmnvNh2eKSkJJ4xvtcQEZpvKmepqRMbNEU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/danieljoos/wincred v1.1.0 h1:3RNcEpBg4IhIChZdFRSdlQt1QjCp1sMAPIrOnm7Yf8g=
|
||||
github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg=
|
||||
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
|
||||
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/diamondburned/arikawa/v3 v3.0.0 h1:VbdX1DtrBLE752IJftZHInVy6v8I3T8vhN9rKGvO6AY=
|
||||
github.com/diamondburned/arikawa/v3 v3.0.0/go.mod h1:5jBSNnp82Z/EhsKa6Wk9FsOqSxfVkNZDTDBPOj47LpY=
|
||||
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||
github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1/go.mod h1:Az6Jt+M5idSED2YPGtwnfJV0kXohgdCBPmHGSYc1r04=
|
||||
github.com/gdamore/tcell/v2 v2.4.1-0.20220313203054-2a1a1b586447 h1:4idf9699cuWAc7ZIB+2RzuDWU30oRkB0X/FZTUlWOVY=
|
||||
github.com/gdamore/tcell/v2 v2.4.1-0.20220313203054-2a1a1b586447/go.mod h1:I8YJFI9gzgl4dHi9UlRDZosCW+jYkDA37AXmXvL51w4=
|
||||
github.com/gdamore/tcell/v2 v2.5.1 h1:zc3LPdpK184lBW7syF2a5C6MV827KmErk9jGVnmsl/I=
|
||||
github.com/gdamore/tcell/v2 v2.5.1/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo=
|
||||
github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro=
|
||||
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc=
|
||||
github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8 h1:xe+mmCnDN82KhC010l3NfYlA8ZbOuzbXAzSYBa6wbMc=
|
||||
|
|
@ -50,44 +46,38 @@ github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EE
|
|||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/urfave/cli/v2 v2.4.0 h1:m2pxjjDFgDxSPtO8WSdbndj17Wu2y8vOT86wE/tjr+I=
|
||||
github.com/urfave/cli/v2 v2.4.0/go.mod h1:NX9W0zmTvedE5oDoOMs2RTC8RvdK98NTYZE5LbaEYPg=
|
||||
github.com/urfave/cli/v2 v2.7.1 h1:DsAOFeI9T0vmUW4LiGR5mhuCIn5kqGIE4WMU2ytmH00=
|
||||
github.com/urfave/cli/v2 v2.7.1/go.mod h1:TYFbtzt/azQoJOrGH5mDfZtS0jIkl/OeFwlRWPR9KRM=
|
||||
github.com/zalando/go-keyring v0.2.1 h1:MBRN/Z8H4U5wEKXiD67YbDAr5cj/DOStmSga70/2qKc=
|
||||
github.com/zalando/go-keyring v0.2.1/go.mod h1:g63M2PPn0w5vjmEbwAX3ib5I+41zdm4esSETOn9Y6Dw=
|
||||
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 h1:S25/rfnfsMVgORT4/J61MJ7rdyseOZOyvLIrZEZ7s6s=
|
||||
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 h1:SLP7Q4Di66FONjDJbCYrCRrh97focO6sLogHO7/g8F0=
|
||||
golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/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-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211113001501-0c823b97ae02 h1:7NCfEGl0sfUojmX78nK9pBJuUlSZWEJA/TwASvfiPLo=
|
||||
golang.org/x/sys v0.0.0-20211113001501-0c823b97ae02/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211001092434-39dca1131b70/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs=
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8=
|
||||
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
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.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
|||
30
main.go
30
main.go
|
|
@ -45,7 +45,7 @@ func main() {
|
|||
c := config.New()
|
||||
err := c.Load(ctx.String("config"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
|
||||
token := ctx.String("token")
|
||||
|
|
@ -53,11 +53,11 @@ func main() {
|
|||
if token != "" {
|
||||
err := app.Connect()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return err
|
||||
}
|
||||
|
||||
app.DrawMainFlex()
|
||||
app.SetFocus(app.GuildsList)
|
||||
app.SetFocus(app.GuildsTree)
|
||||
} else {
|
||||
loginForm := ui.NewLoginForm(false)
|
||||
loginForm.AddButton("Login", func() {
|
||||
|
|
@ -67,21 +67,21 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
// Login using the email and password
|
||||
lr, err := app.Session.Login(email, password)
|
||||
// Login using the email and password only
|
||||
lr, err := app.State.Login(email, password)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if lr.Token != "" && !lr.Mfa {
|
||||
app.Session.Identify.Token = lr.Token
|
||||
if lr.Token != "" && !lr.MFA {
|
||||
app.State.Token = lr.Token
|
||||
err = app.Connect()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
app.DrawMainFlex()
|
||||
app.SetFocus(app.GuildsList)
|
||||
app.SetFocus(app.GuildsTree)
|
||||
|
||||
go keyring.Set(name, "token", lr.Token)
|
||||
} else {
|
||||
|
|
@ -93,19 +93,19 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
lr, err = app.Session.Totp(code, lr.Ticket)
|
||||
lr, err = app.State.TOTP(code, lr.Ticket)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
app.Session.Identify.Token = lr.Token
|
||||
app.State.Token = lr.Token
|
||||
err = app.Connect()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
app.DrawMainFlex()
|
||||
app.SetFocus(app.GuildsList)
|
||||
app.SetFocus(app.GuildsTree)
|
||||
|
||||
go keyring.Set(name, "token", lr.Token)
|
||||
})
|
||||
|
|
|
|||
120
ui/app.go
120
ui/app.go
|
|
@ -1,11 +1,15 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/ayntgl/astatine"
|
||||
"github.com/ayntgl/discordo/config"
|
||||
"github.com/diamondburned/arikawa/v3/api"
|
||||
"github.com/diamondburned/arikawa/v3/discord"
|
||||
"github.com/diamondburned/arikawa/v3/gateway"
|
||||
"github.com/diamondburned/arikawa/v3/state"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
|
@ -13,28 +17,39 @@ import (
|
|||
type App struct {
|
||||
*tview.Application
|
||||
MainFlex *tview.Flex
|
||||
GuildsList *GuildsList
|
||||
ChannelsTreeView *ChannelsTreeView
|
||||
GuildsTree *GuildsTree
|
||||
ChannelsTree *ChannelsTree
|
||||
MessagesTextView *MessagesTextView
|
||||
MessageInputField *MessageInputField
|
||||
Session *astatine.Session
|
||||
SelectedChannel *astatine.Channel
|
||||
Config *config.Config
|
||||
SelectedMessage int
|
||||
MessageInputField *MessageInput
|
||||
|
||||
Config *config.Config
|
||||
State *state.State
|
||||
SelectedChannel *discord.Channel
|
||||
SelectedMessage int
|
||||
}
|
||||
|
||||
func NewApp(token string, c *config.Config) *App {
|
||||
app := &App{
|
||||
MainFlex: tview.NewFlex(),
|
||||
Session: astatine.New(token),
|
||||
Config: c,
|
||||
MainFlex: tview.NewFlex(),
|
||||
Config: c,
|
||||
|
||||
State: state.NewWithIdentifier(gateway.NewIdentifier(gateway.IdentifyCommand{
|
||||
Token: token,
|
||||
Intents: nil,
|
||||
Properties: gateway.IdentifyProperties{
|
||||
OS: c.Identify.Os,
|
||||
Browser: c.Identify.Browser,
|
||||
},
|
||||
// The official client sets the compress field as false.
|
||||
Compress: false,
|
||||
})),
|
||||
SelectedMessage: -1,
|
||||
}
|
||||
|
||||
app.GuildsList = NewGuildsList(app)
|
||||
app.ChannelsTreeView = NewChannelsTreeView(app)
|
||||
app.GuildsTree = NewGuildsTree(app)
|
||||
app.ChannelsTree = NewChannelsTree(app)
|
||||
app.MessagesTextView = NewMessagesTextView(app)
|
||||
app.MessageInputField = NewMessageInputField(app)
|
||||
app.MessageInputField = NewMessageInput(app)
|
||||
|
||||
app.Application = tview.NewApplication()
|
||||
app.EnableMouse(app.Config.Mouse)
|
||||
|
|
@ -46,22 +61,15 @@ func NewApp(token string, c *config.Config) *App {
|
|||
func (app *App) Connect() error {
|
||||
// For user accounts, all of the guilds, the user is in, are dispatched in the READY gateway event.
|
||||
// Whereas, for bot accounts, the guilds are dispatched discretely in the GUILD_CREATE gateway events.
|
||||
if !strings.HasPrefix(app.Session.Identify.Token, "Bot") {
|
||||
app.Session.UserAgent = app.Config.Identify.UserAgent
|
||||
app.Session.Identify.Compress = false
|
||||
app.Session.Identify.LargeThreshold = 0
|
||||
app.Session.Identify.Intents = 0
|
||||
app.Session.Identify.Properties = astatine.IdentifyProperties{
|
||||
OS: app.Config.Identify.Os,
|
||||
Browser: app.Config.Identify.Browser,
|
||||
}
|
||||
app.Session.AddHandlerOnce(app.onSessionReady)
|
||||
if !strings.HasPrefix(app.State.Token, "Bot") {
|
||||
api.UserAgent = app.Config.Identify.UserAgent
|
||||
app.State.AddHandler(app.onStateReady)
|
||||
}
|
||||
|
||||
app.Session.AddHandler(app.onSessionGuildCreate)
|
||||
app.Session.AddHandler(app.onSessionGuildDelete)
|
||||
app.Session.AddHandler(app.onSessionMessageCreate)
|
||||
return app.Session.Open()
|
||||
app.State.AddHandler(app.onStateGuildCreate)
|
||||
app.State.AddHandler(app.onStateGuildDelete)
|
||||
app.State.AddHandler(app.onStateMessageCreate)
|
||||
return app.State.Open(context.Background())
|
||||
}
|
||||
|
||||
func (app *App) onInputCapture(e *tcell.EventKey) *tcell.EventKey {
|
||||
|
|
@ -71,16 +79,16 @@ func (app *App) onInputCapture(e *tcell.EventKey) *tcell.EventKey {
|
|||
|
||||
if app.MainFlex.GetItemCount() != 0 {
|
||||
switch e.Name() {
|
||||
case app.Config.Keys.ToggleGuildsList:
|
||||
app.SetFocus(app.GuildsList)
|
||||
case app.Config.Keys.ToggleGuildsTree:
|
||||
app.SetFocus(app.GuildsTree)
|
||||
return nil
|
||||
case app.Config.Keys.ToggleChannelsTreeView:
|
||||
app.SetFocus(app.ChannelsTreeView)
|
||||
case app.Config.Keys.ToggleChannelsTree:
|
||||
app.SetFocus(app.ChannelsTree)
|
||||
return nil
|
||||
case app.Config.Keys.ToggleMessagesTextView:
|
||||
app.SetFocus(app.MessagesTextView)
|
||||
return nil
|
||||
case app.Config.Keys.ToggleMessageInputField:
|
||||
case app.Config.Keys.ToggleMessageInput:
|
||||
app.SetFocus(app.MessageInputField)
|
||||
return nil
|
||||
}
|
||||
|
|
@ -92,8 +100,8 @@ func (app *App) onInputCapture(e *tcell.EventKey) *tcell.EventKey {
|
|||
func (app *App) DrawMainFlex() {
|
||||
leftFlex := tview.NewFlex().
|
||||
SetDirection(tview.FlexRow).
|
||||
AddItem(app.GuildsList, 10, 1, false).
|
||||
AddItem(app.ChannelsTreeView, 0, 1, false)
|
||||
AddItem(app.GuildsTree, 10, 1, false).
|
||||
AddItem(app.ChannelsTree, 0, 1, false)
|
||||
rightFlex := tview.NewFlex().
|
||||
SetDirection(tview.FlexRow).
|
||||
AddItem(app.MessagesTextView, 0, 1, false).
|
||||
|
|
@ -105,10 +113,10 @@ func (app *App) DrawMainFlex() {
|
|||
app.SetRoot(app.MainFlex, true)
|
||||
}
|
||||
|
||||
func (app *App) onSessionReady(_ *astatine.Session, r *astatine.Ready) {
|
||||
func (app *App) onStateReady(r *gateway.ReadyEvent) {
|
||||
sort.Slice(r.Guilds, func(a, b int) bool {
|
||||
found := false
|
||||
for _, guildID := range r.Settings.GuildPositions {
|
||||
for _, guildID := range r.UserSettings.GuildPositions {
|
||||
if found && guildID == r.Guilds[b].ID {
|
||||
return true
|
||||
}
|
||||
|
|
@ -120,28 +128,48 @@ func (app *App) onSessionReady(_ *astatine.Session, r *astatine.Ready) {
|
|||
return false
|
||||
})
|
||||
|
||||
rootNode := app.GuildsTree.GetRoot()
|
||||
for _, g := range r.Guilds {
|
||||
app.GuildsList.AddItem(g.Name, "", 0, nil)
|
||||
guildNode := tview.NewTreeNode(g.Name)
|
||||
guildNode.SetReference(g.ID)
|
||||
|
||||
rootNode.AddChild(guildNode)
|
||||
}
|
||||
|
||||
app.GuildsTree.SetCurrentNode(rootNode)
|
||||
app.SetFocus(app.GuildsTree)
|
||||
}
|
||||
|
||||
func (app *App) onSessionGuildCreate(_ *astatine.Session, g *astatine.GuildCreate) {
|
||||
app.GuildsList.AddItem(g.Name, "", 0, nil)
|
||||
func (app *App) onStateGuildCreate(g *gateway.GuildCreateEvent) {
|
||||
rootNode := app.GuildsTree.GetRoot()
|
||||
guildNode := tview.NewTreeNode(g.Name)
|
||||
guildNode.SetReference(g.ID)
|
||||
|
||||
rootNode.AddChild(guildNode)
|
||||
app.Draw()
|
||||
}
|
||||
|
||||
func (app *App) onSessionGuildDelete(_ *astatine.Session, g *astatine.GuildDelete) {
|
||||
items := app.GuildsList.FindItems(g.BeforeDelete.Name, "", false, false)
|
||||
if len(items) != 0 {
|
||||
app.GuildsList.RemoveItem(items[0])
|
||||
func (app *App) onStateGuildDelete(g *gateway.GuildDeleteEvent) {
|
||||
rootNode := app.GuildsTree.GetRoot()
|
||||
var parentNode *tview.TreeNode
|
||||
rootNode.Walk(func(node, _ *tview.TreeNode) bool {
|
||||
if node.GetReference() == g.ID {
|
||||
parentNode = node
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
if parentNode != nil {
|
||||
rootNode.RemoveChild(parentNode)
|
||||
}
|
||||
|
||||
app.Draw()
|
||||
}
|
||||
|
||||
func (app *App) onSessionMessageCreate(_ *astatine.Session, m *astatine.MessageCreate) {
|
||||
func (app *App) onStateMessageCreate(m *gateway.MessageCreateEvent) {
|
||||
if app.SelectedChannel != nil && app.SelectedChannel.ID == m.ChannelID {
|
||||
app.SelectedChannel.Messages = append(app.SelectedChannel.Messages, m.Message)
|
||||
_, err := app.MessagesTextView.Write(buildMessage(app, m.Message))
|
||||
if err != nil {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -5,23 +5,22 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ayntgl/astatine"
|
||||
"github.com/ayntgl/discordo/discord"
|
||||
"github.com/diamondburned/arikawa/v3/discord"
|
||||
)
|
||||
|
||||
func buildMessage(app *App, m *astatine.Message) []byte {
|
||||
func buildMessage(app *App, m discord.Message) []byte {
|
||||
var b strings.Builder
|
||||
|
||||
switch m.Type {
|
||||
case astatine.MessageTypeDefault, astatine.MessageTypeReply:
|
||||
case discord.DefaultMessage, discord.InlinedReplyMessage:
|
||||
// Define a new region and assign message ID as the region ID.
|
||||
// Learn more:
|
||||
// https://pkg.go.dev/github.com/rivo/tview#hdr-Regions_and_Highlights
|
||||
b.WriteString("[\"")
|
||||
b.WriteString(m.ID)
|
||||
b.WriteString(m.ID.String())
|
||||
b.WriteString("\"]")
|
||||
// Build the message associated with crosspost, channel follow add, pin, or a reply.
|
||||
buildReferencedMessage(&b, m.ReferencedMessage, app.Session.State.User.ID)
|
||||
buildReferencedMessage(&b, m.ReferencedMessage, app.State.Ready().User.ID)
|
||||
|
||||
if app.Config.Timestamps {
|
||||
loc, err := time.LoadLocation(app.Config.Timezone)
|
||||
|
|
@ -30,18 +29,18 @@ func buildMessage(app *App, m *astatine.Message) []byte {
|
|||
}
|
||||
|
||||
b.WriteString("[::d]")
|
||||
b.WriteString(m.Timestamp.In(loc).Format(time.Stamp))
|
||||
b.WriteString(m.Timestamp.Time().In(loc).Format(time.Stamp))
|
||||
b.WriteString("[::-]")
|
||||
b.WriteByte(' ')
|
||||
}
|
||||
|
||||
// Build the author of this message.
|
||||
buildAuthor(&b, m.Author, app.Session.State.User.ID)
|
||||
buildAuthor(&b, m.Author, app.State.Ready().User.ID)
|
||||
|
||||
// Build the contents of the message.
|
||||
buildContent(&b, m, app.Session.State.User.ID)
|
||||
buildContent(&b, m, app.State.Ready().User.ID)
|
||||
|
||||
if m.EditedTimestamp != nil {
|
||||
if m.EditedTimestamp.IsValid() {
|
||||
b.WriteString(" [::d](edited)[::-]")
|
||||
}
|
||||
|
||||
|
|
@ -56,19 +55,19 @@ func buildMessage(app *App, m *astatine.Message) []byte {
|
|||
b.WriteString("[\"\"]")
|
||||
|
||||
b.WriteByte('\n')
|
||||
case astatine.MessageTypeGuildMemberJoin:
|
||||
case discord.GuildMemberJoinMessage:
|
||||
b.WriteString("[#5865F2]")
|
||||
b.WriteString(m.Author.Username)
|
||||
b.WriteString("[-] joined the server.")
|
||||
|
||||
b.WriteByte('\n')
|
||||
case astatine.MessageTypeCall:
|
||||
case discord.CallMessage:
|
||||
b.WriteString("[#5865F2]")
|
||||
b.WriteString(m.Author.Username)
|
||||
b.WriteString("[-] started a call.")
|
||||
|
||||
b.WriteByte('\n')
|
||||
case astatine.MessageTypeChannelPinnedMessage:
|
||||
case discord.ChannelPinnedMessage:
|
||||
b.WriteString("[#5865F2]")
|
||||
b.WriteString(m.Author.Username)
|
||||
b.WriteString("[-] pinned a message.")
|
||||
|
|
@ -86,7 +85,7 @@ func buildMessage(app *App, m *astatine.Message) []byte {
|
|||
return nil
|
||||
}
|
||||
|
||||
func buildReferencedMessage(b *strings.Builder, rm *astatine.Message, clientID string) {
|
||||
func buildReferencedMessage(b *strings.Builder, rm *discord.Message, clientID discord.UserID) {
|
||||
if rm != nil {
|
||||
b.WriteString(" ╭ ")
|
||||
b.WriteString("[::d]")
|
||||
|
|
@ -94,7 +93,7 @@ func buildReferencedMessage(b *strings.Builder, rm *astatine.Message, clientID s
|
|||
|
||||
if rm.Content != "" {
|
||||
rm.Content = buildMentions(rm.Content, rm.Mentions, clientID)
|
||||
b.WriteString(discord.ParseMarkdown(rm.Content))
|
||||
b.WriteString(parseMarkdown(rm.Content))
|
||||
}
|
||||
|
||||
b.WriteString("[::-]")
|
||||
|
|
@ -102,16 +101,16 @@ func buildReferencedMessage(b *strings.Builder, rm *astatine.Message, clientID s
|
|||
}
|
||||
}
|
||||
|
||||
func buildContent(b *strings.Builder, m *astatine.Message, clientID string) {
|
||||
func buildContent(b *strings.Builder, m discord.Message, clientID discord.UserID) {
|
||||
if m.Content != "" {
|
||||
m.Content = buildMentions(m.Content, m.Mentions, clientID)
|
||||
b.WriteString(discord.ParseMarkdown(m.Content))
|
||||
b.WriteString(parseMarkdown(m.Content))
|
||||
}
|
||||
}
|
||||
|
||||
func buildEmbeds(b *strings.Builder, es []*astatine.MessageEmbed) {
|
||||
func buildEmbeds(b *strings.Builder, es []discord.Embed) {
|
||||
for _, e := range es {
|
||||
if e.Type != astatine.EmbedTypeRich {
|
||||
if e.Type != discord.NormalEmbed {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -148,7 +147,7 @@ func buildEmbeds(b *strings.Builder, es []*astatine.MessageEmbed) {
|
|||
embedBuilder.WriteByte('\n')
|
||||
}
|
||||
|
||||
embedBuilder.WriteString(discord.ParseMarkdown(e.Description))
|
||||
embedBuilder.WriteString(parseMarkdown(e.Description))
|
||||
}
|
||||
|
||||
if len(e.Fields) != 0 {
|
||||
|
|
@ -162,7 +161,7 @@ func buildEmbeds(b *strings.Builder, es []*astatine.MessageEmbed) {
|
|||
embedBuilder.WriteString(ef.Name)
|
||||
embedBuilder.WriteString("[::-]")
|
||||
embedBuilder.WriteByte('\n')
|
||||
embedBuilder.WriteString(discord.ParseMarkdown(ef.Value))
|
||||
embedBuilder.WriteString(parseMarkdown(ef.Value))
|
||||
|
||||
if i != len(e.Fields)-1 {
|
||||
embedBuilder.WriteString("\n\n")
|
||||
|
|
@ -182,7 +181,7 @@ func buildEmbeds(b *strings.Builder, es []*astatine.MessageEmbed) {
|
|||
}
|
||||
}
|
||||
|
||||
func buildAttachments(b *strings.Builder, as []*astatine.MessageAttachment) {
|
||||
func buildAttachments(b *strings.Builder, as []discord.Attachment) {
|
||||
for _, a := range as {
|
||||
b.WriteByte('\n')
|
||||
b.WriteByte('[')
|
||||
|
|
@ -192,7 +191,7 @@ func buildAttachments(b *strings.Builder, as []*astatine.MessageAttachment) {
|
|||
}
|
||||
}
|
||||
|
||||
func buildMentions(content string, mentions []*astatine.User, clientID string) string {
|
||||
func buildMentions(content string, mentions []discord.GuildUser, clientID discord.UserID) string {
|
||||
for _, mUser := range mentions {
|
||||
var color string
|
||||
if mUser.ID == clientID {
|
||||
|
|
@ -203,10 +202,10 @@ func buildMentions(content string, mentions []*astatine.User, clientID string) s
|
|||
|
||||
content = strings.NewReplacer(
|
||||
// <@USER_ID>
|
||||
"<@"+mUser.ID+">",
|
||||
"<@"+mUser.ID.String()+">",
|
||||
color+"@"+mUser.Username+"[-:-]",
|
||||
// <@!USER_ID>
|
||||
"<@!"+mUser.ID+">",
|
||||
"<@!"+mUser.ID.String()+">",
|
||||
color+"@"+mUser.Username+"[-:-]",
|
||||
).Replace(content)
|
||||
}
|
||||
|
|
@ -214,7 +213,7 @@ func buildMentions(content string, mentions []*astatine.User, clientID string) s
|
|||
return content
|
||||
}
|
||||
|
||||
func buildAuthor(b *strings.Builder, u *astatine.User, clientID string) {
|
||||
func buildAuthor(b *strings.Builder, u discord.User, clientID discord.UserID) {
|
||||
if u.ID == clientID {
|
||||
b.WriteString("[#57F287]")
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,77 +1,77 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"github.com/ayntgl/astatine"
|
||||
"github.com/ayntgl/discordo/discord"
|
||||
"github.com/diamondburned/arikawa/v3/discord"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
type ChannelsTreeView struct {
|
||||
type ChannelsTree struct {
|
||||
*tview.TreeView
|
||||
app *App
|
||||
}
|
||||
|
||||
func NewChannelsTreeView(app *App) *ChannelsTreeView {
|
||||
ctv := &ChannelsTreeView{
|
||||
func NewChannelsTree(app *App) *ChannelsTree {
|
||||
ct := &ChannelsTree{
|
||||
TreeView: tview.NewTreeView(),
|
||||
app: app,
|
||||
}
|
||||
|
||||
ctv.SetTopLevel(1)
|
||||
ctv.SetRoot(tview.NewTreeNode(""))
|
||||
ctv.SetTitle("Channels")
|
||||
ctv.SetTitleAlign(tview.AlignLeft)
|
||||
ctv.SetBorder(true)
|
||||
ctv.SetBorderPadding(0, 0, 1, 1)
|
||||
ctv.SetSelectedFunc(ctv.onSelected)
|
||||
return ctv
|
||||
ct.SetRoot(tview.NewTreeNode(""))
|
||||
ct.SetTopLevel(1)
|
||||
ct.SetSelectedFunc(ct.onSelected)
|
||||
|
||||
ct.SetTitle("Channels")
|
||||
ct.SetTitleAlign(tview.AlignLeft)
|
||||
ct.SetBorder(true)
|
||||
ct.SetBorderPadding(0, 0, 1, 1)
|
||||
|
||||
return ct
|
||||
}
|
||||
|
||||
func (ctv *ChannelsTreeView) onSelected(n *tview.TreeNode) {
|
||||
ctv.app.SelectedMessage = -1
|
||||
ctv.app.MessagesTextView.
|
||||
func (ct *ChannelsTree) onSelected(node *tview.TreeNode) {
|
||||
ct.app.SelectedChannel = nil
|
||||
ct.app.SelectedMessage = -1
|
||||
ct.app.MessagesTextView.
|
||||
Highlight().
|
||||
Clear().
|
||||
SetTitle("")
|
||||
ctv.app.MessageInputField.SetText("")
|
||||
ct.app.MessageInputField.SetText("")
|
||||
|
||||
c, err := ctv.app.Session.State.Channel(n.GetReference().(string))
|
||||
ref := node.GetReference()
|
||||
c, err := ct.app.State.Cabinet.Channel(ref.(discord.ChannelID))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if c.Type == astatine.ChannelTypeGuildCategory {
|
||||
n.SetExpanded(!n.IsExpanded())
|
||||
// If the channel is a category channel, expend the selected node if it is not expanded already.
|
||||
if c.Type == discord.GuildCategory {
|
||||
node.SetExpanded(!node.IsExpanded())
|
||||
return
|
||||
}
|
||||
|
||||
ctv.app.SelectedChannel = c
|
||||
ctv.app.SetFocus(ctv.app.MessageInputField)
|
||||
ct.app.SelectedChannel = c
|
||||
ct.app.SetFocus(ct.app.MessageInputField)
|
||||
|
||||
title := discord.ChannelToString(c)
|
||||
title := channelToString(*c)
|
||||
if c.Topic != "" {
|
||||
title += " - " + discord.ParseMarkdown(c.Topic)
|
||||
title += " - " + parseMarkdown(c.Topic)
|
||||
}
|
||||
ctv.app.MessagesTextView.SetTitle(title)
|
||||
ct.app.MessagesTextView.SetTitle(title)
|
||||
|
||||
go func() {
|
||||
ms, err := ctv.app.Session.ChannelMessages(c.ID, ctv.app.Config.MessagesLimit, "", "", "")
|
||||
// The returned slice will be sorted from latest to oldest.
|
||||
ms, err := ct.app.State.Messages(c.ID, ct.app.Config.MessagesLimit)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for i := len(ms) - 1; i >= 0; i-- {
|
||||
ctv.app.SelectedChannel.Messages = append(ctv.app.SelectedChannel.Messages, ms[i])
|
||||
ctv.drawMessage(ms[i])
|
||||
_, err = ct.app.MessagesTextView.Write(buildMessage(ct.app, ms[i]))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctv.app.MessagesTextView.ScrollToEnd()
|
||||
ct.app.MessagesTextView.ScrollToEnd()
|
||||
}()
|
||||
}
|
||||
|
||||
func (ctv *ChannelsTreeView) drawMessage(m *astatine.Message) {
|
||||
_, err := ctv.app.MessagesTextView.Write(buildMessage(ctv.app, m))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
|||
140
ui/guilds.go
140
ui/guilds.go
|
|
@ -3,129 +3,121 @@ package ui
|
|||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/ayntgl/astatine"
|
||||
"github.com/ayntgl/discordo/discord"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
dsc "github.com/diamondburned/arikawa/v3/discord"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
type GuildsList struct {
|
||||
*tview.List
|
||||
type GuildsTree struct {
|
||||
*tview.TreeView
|
||||
app *App
|
||||
}
|
||||
|
||||
func NewGuildsList(app *App) *GuildsList {
|
||||
gl := &GuildsList{
|
||||
List: tview.NewList(),
|
||||
app: app,
|
||||
func NewGuildsTree(app *App) *GuildsTree {
|
||||
gt := &GuildsTree{
|
||||
TreeView: tview.NewTreeView(),
|
||||
app: app,
|
||||
}
|
||||
|
||||
gl.AddItem("Direct Messages", "", 0, nil)
|
||||
gl.ShowSecondaryText(false)
|
||||
gl.SetTitle("Guilds")
|
||||
gl.SetTitleAlign(tview.AlignLeft)
|
||||
gl.SetBorder(true)
|
||||
gl.SetBorderPadding(0, 0, 1, 1)
|
||||
gl.SetSelectedFunc(gl.onSelected)
|
||||
gl.SetInputCapture(gl.onInputCapture)
|
||||
return gl
|
||||
rootNode := tview.NewTreeNode("")
|
||||
rootNode.AddChild(tview.NewTreeNode("Direct Messages"))
|
||||
|
||||
gt.SetRoot(rootNode)
|
||||
gt.SetTopLevel(1)
|
||||
gt.SetSelectedFunc(gt.onSelected)
|
||||
|
||||
gt.SetTitle("Guilds")
|
||||
gt.SetTitleAlign(tview.AlignLeft)
|
||||
gt.SetBorder(true)
|
||||
gt.SetBorderPadding(0, 0, 1, 1)
|
||||
|
||||
return gt
|
||||
}
|
||||
|
||||
func (gl *GuildsList) onInputCapture(e *tcell.EventKey) *tcell.EventKey {
|
||||
i := gl.List.GetCurrentItem()
|
||||
switch e.Rune() {
|
||||
case 'g': // Home.
|
||||
i = 0
|
||||
case 'G': // End.
|
||||
i = gl.List.GetItemCount() - 1
|
||||
case 'j': // Down.
|
||||
i++
|
||||
case 'k': // Up.
|
||||
i--
|
||||
if i < 0 {
|
||||
i = 0
|
||||
}
|
||||
default:
|
||||
return e
|
||||
}
|
||||
gl.List.SetCurrentItem(i)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gl *GuildsList) onSelected(idx int, mainText string, secondaryText string, shortcut rune) {
|
||||
rootTreeNode := gl.app.ChannelsTreeView.GetRoot()
|
||||
rootTreeNode.ClearChildren()
|
||||
gl.app.SelectedMessage = -1
|
||||
gl.app.MessagesTextView.
|
||||
func (gt *GuildsTree) onSelected(node *tview.TreeNode) {
|
||||
gt.app.SelectedChannel = nil
|
||||
gt.app.SelectedMessage = -1
|
||||
rootNode := gt.app.ChannelsTree.GetRoot()
|
||||
rootNode.ClearChildren()
|
||||
gt.app.MessagesTextView.
|
||||
Highlight().
|
||||
Clear()
|
||||
gl.app.MessageInputField.SetText("")
|
||||
Clear().
|
||||
SetTitle("")
|
||||
gt.app.MessageInputField.SetText("")
|
||||
|
||||
ref := node.GetReference()
|
||||
// If the reference of the selected node is nil, it must be the direct messages node.
|
||||
if ref == nil {
|
||||
cs, err := gt.app.State.Cabinet.PrivateChannels()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if mainText == "Direct Messages" {
|
||||
cs := gl.app.Session.State.PrivateChannels
|
||||
sort.Slice(cs, func(i, j int) bool {
|
||||
return cs[i].LastMessageID > cs[j].LastMessageID
|
||||
})
|
||||
|
||||
for _, c := range cs {
|
||||
channelTreeNode := tview.NewTreeNode(discord.ChannelToString(c)).
|
||||
SetReference(c.ID)
|
||||
rootTreeNode.AddChild(channelTreeNode)
|
||||
channelNode := tview.NewTreeNode(c.Name)
|
||||
channelNode.SetReference(c.ID)
|
||||
rootNode.AddChild(channelNode)
|
||||
}
|
||||
} else { // Guild
|
||||
// Decrement the index of the selected item by one since the first item in the list is always "Direct Messages".
|
||||
cs := gl.app.Session.State.Guilds[idx-1].Channels
|
||||
cs, err := gt.app.State.Cabinet.Channels(ref.(dsc.GuildID))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sort.Slice(cs, func(i, j int) bool {
|
||||
return cs[i].Position < cs[j].Position
|
||||
})
|
||||
|
||||
for _, c := range cs {
|
||||
if (c.Type == astatine.ChannelTypeGuildText || c.Type == astatine.ChannelTypeGuildNews) && (c.ParentID == "") {
|
||||
channelTreeNode := tview.NewTreeNode(discord.ChannelToString(c)).
|
||||
SetReference(c.ID)
|
||||
rootTreeNode.AddChild(channelTreeNode)
|
||||
if (c.Type == dsc.GuildText || c.Type == dsc.GuildNews) && (!c.ParentID.IsValid()) {
|
||||
channelNode := tview.NewTreeNode(channelToString(c))
|
||||
channelNode.SetReference(c.ID)
|
||||
rootNode.AddChild(channelNode)
|
||||
}
|
||||
}
|
||||
|
||||
CATEGORY:
|
||||
for _, c := range cs {
|
||||
if c.Type == astatine.ChannelTypeGuildCategory {
|
||||
if c.Type == dsc.GuildCategory {
|
||||
for _, nestedChannel := range cs {
|
||||
if nestedChannel.ParentID == c.ID {
|
||||
channelTreeNode := tview.NewTreeNode(c.Name).
|
||||
SetReference(c.ID)
|
||||
rootTreeNode.AddChild(channelTreeNode)
|
||||
channelNode := tview.NewTreeNode(c.Name)
|
||||
channelNode.SetReference(c.ID)
|
||||
rootNode.AddChild(channelNode)
|
||||
continue CATEGORY
|
||||
}
|
||||
}
|
||||
|
||||
channelTreeNode := tview.NewTreeNode(c.Name).
|
||||
SetReference(c.ID)
|
||||
rootTreeNode.AddChild(channelTreeNode)
|
||||
channelNode := tview.NewTreeNode(channelToString(c))
|
||||
channelNode.SetReference(c.ID)
|
||||
rootNode.AddChild(channelNode)
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range cs {
|
||||
if (c.Type == astatine.ChannelTypeGuildText || c.Type == astatine.ChannelTypeGuildNews) && (c.ParentID != "") {
|
||||
var parentTreeNode *tview.TreeNode
|
||||
rootTreeNode.Walk(func(node, _ *tview.TreeNode) bool {
|
||||
if (c.Type == dsc.GuildText || c.Type == dsc.GuildNews) && (c.ParentID.IsValid()) {
|
||||
var parentNode *tview.TreeNode
|
||||
rootNode.Walk(func(node, _ *tview.TreeNode) bool {
|
||||
if node.GetReference() == c.ParentID {
|
||||
parentTreeNode = node
|
||||
parentNode = node
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
if parentTreeNode != nil {
|
||||
channelTreeNode := tview.NewTreeNode(discord.ChannelToString(c)).
|
||||
SetReference(c.ID)
|
||||
parentTreeNode.AddChild(channelTreeNode)
|
||||
if parentNode != nil {
|
||||
channelNode := tview.NewTreeNode(channelToString(c))
|
||||
channelNode.SetReference(c.ID)
|
||||
parentNode.AddChild(channelNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gl.app.ChannelsTreeView.SetCurrentNode(rootTreeNode)
|
||||
gl.app.SetFocus(gl.app.ChannelsTreeView)
|
||||
gt.app.ChannelsTree.SetCurrentNode(rootNode)
|
||||
gt.app.SetFocus(gt.app.ChannelsTree)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package discord
|
||||
package ui
|
||||
|
||||
import "regexp"
|
||||
|
||||
|
|
@ -9,7 +9,7 @@ var (
|
|||
strikeThroughRegex = regexp.MustCompile(`(?ms)~~(.*?)~~`)
|
||||
)
|
||||
|
||||
func ParseMarkdown(md string) string {
|
||||
func parseMarkdown(md string) string {
|
||||
var res string
|
||||
res = boldRegex.ReplaceAllString(md, "[::b]$1[::-]")
|
||||
res = italicRegex.ReplaceAllString(res, "[::i]$1[::-]")
|
||||
142
ui/messages.go
142
ui/messages.go
|
|
@ -2,7 +2,6 @@ package ui
|
|||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
|
@ -11,8 +10,9 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/atotto/clipboard"
|
||||
"github.com/ayntgl/astatine"
|
||||
"github.com/ayntgl/discordo/discord"
|
||||
"github.com/diamondburned/arikawa/v3/api"
|
||||
"github.com/diamondburned/arikawa/v3/discord"
|
||||
"github.com/diamondburned/arikawa/v3/utils/json/option"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/rivo/tview"
|
||||
"github.com/skratchdot/open-golang/open"
|
||||
|
|
@ -34,13 +34,15 @@ func NewMessagesTextView(app *App) *MessagesTextView {
|
|||
mtv.SetDynamicColors(true)
|
||||
mtv.SetRegions(true)
|
||||
mtv.SetWordWrap(true)
|
||||
mtv.SetInputCapture(mtv.onInputCapture)
|
||||
mtv.SetChangedFunc(func() {
|
||||
mtv.app.Draw()
|
||||
})
|
||||
|
||||
mtv.SetTitleAlign(tview.AlignLeft)
|
||||
mtv.SetBorder(true)
|
||||
mtv.SetBorderPadding(0, 0, 1, 1)
|
||||
mtv.SetInputCapture(mtv.onInputCapture)
|
||||
|
||||
return mtv
|
||||
}
|
||||
|
||||
|
|
@ -49,50 +51,57 @@ func (mtv *MessagesTextView) onInputCapture(e *tcell.EventKey) *tcell.EventKey {
|
|||
return nil
|
||||
}
|
||||
|
||||
ms := mtv.app.SelectedChannel.Messages
|
||||
if len(ms) == 0 {
|
||||
// Messages should return messages ordered from latest to earliest.
|
||||
ms, err := mtv.app.State.Cabinet.Messages(mtv.app.SelectedChannel.ID)
|
||||
if err != nil || len(ms) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch e.Name() {
|
||||
case mtv.app.Config.Keys.SelectPreviousMessage:
|
||||
// If there are no highlighted regions, select the latest (last) message in the messages TextView.
|
||||
if len(mtv.app.MessagesTextView.GetHighlights()) == 0 {
|
||||
mtv.app.SelectedMessage = len(ms) - 1
|
||||
mtv.app.SelectedMessage = 0
|
||||
} else {
|
||||
mtv.app.SelectedMessage--
|
||||
if mtv.app.SelectedMessage < 0 {
|
||||
// If the selected message is the oldest (first) message, select the latest (last) message in the messages TextView.
|
||||
if mtv.app.SelectedMessage == len(ms)-1 {
|
||||
mtv.app.SelectedMessage = 0
|
||||
} else {
|
||||
mtv.app.SelectedMessage++
|
||||
}
|
||||
}
|
||||
|
||||
mtv.app.MessagesTextView.
|
||||
Highlight(ms[mtv.app.SelectedMessage].ID).
|
||||
Highlight(ms[mtv.app.SelectedMessage].ID.String()).
|
||||
ScrollToHighlight()
|
||||
return nil
|
||||
case mtv.app.Config.Keys.SelectNextMessage:
|
||||
// If there are no highlighted regions, select the latest (last) message in the messages TextView.
|
||||
if len(mtv.app.MessagesTextView.GetHighlights()) == 0 {
|
||||
mtv.app.SelectedMessage = len(ms) - 1
|
||||
mtv.app.SelectedMessage = 0
|
||||
} else {
|
||||
mtv.app.SelectedMessage++
|
||||
if mtv.app.SelectedMessage >= len(ms) {
|
||||
// If the selected message is the latest (last) message, select the oldest (first) message in the messages TextView.
|
||||
if mtv.app.SelectedMessage == 0 {
|
||||
mtv.app.SelectedMessage = len(ms) - 1
|
||||
} else {
|
||||
mtv.app.SelectedMessage--
|
||||
}
|
||||
}
|
||||
|
||||
mtv.app.MessagesTextView.
|
||||
Highlight(ms[mtv.app.SelectedMessage].ID).
|
||||
Highlight(ms[mtv.app.SelectedMessage].ID.String()).
|
||||
ScrollToHighlight()
|
||||
return nil
|
||||
case mtv.app.Config.Keys.SelectFirstMessage:
|
||||
mtv.app.SelectedMessage = 0
|
||||
mtv.app.SelectedMessage = len(ms) - 1
|
||||
mtv.app.MessagesTextView.
|
||||
Highlight(ms[mtv.app.SelectedMessage].ID).
|
||||
Highlight(ms[mtv.app.SelectedMessage].ID.String()).
|
||||
ScrollToHighlight()
|
||||
return nil
|
||||
case mtv.app.Config.Keys.SelectLastMessage:
|
||||
mtv.app.SelectedMessage = len(ms) - 1
|
||||
mtv.app.SelectedMessage = 0
|
||||
mtv.app.MessagesTextView.
|
||||
Highlight(ms[mtv.app.SelectedMessage].ID).
|
||||
Highlight(ms[mtv.app.SelectedMessage].ID.String()).
|
||||
ScrollToHighlight()
|
||||
return nil
|
||||
case mtv.app.Config.Keys.OpenMessageActionsList:
|
||||
|
|
@ -101,7 +110,12 @@ func (mtv *MessagesTextView) onInputCapture(e *tcell.EventKey) *tcell.EventKey {
|
|||
return nil
|
||||
}
|
||||
|
||||
_, m := discord.FindMessageByID(mtv.app.SelectedChannel.Messages, hs[0])
|
||||
mID, err := discord.ParseSnowflake(hs[0])
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, m := findMessageByID(ms, discord.MessageID(mID))
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -119,16 +133,16 @@ func (mtv *MessagesTextView) onInputCapture(e *tcell.EventKey) *tcell.EventKey {
|
|||
actionsList.SetBorderPadding(0, 0, 1, 1)
|
||||
|
||||
// If the client user has `SEND_MESSAGES` permission, add a new action to reply to the message.
|
||||
if discord.HasPermission(mtv.app.Session.State, mtv.app.SelectedChannel.ID, astatine.PermissionSendMessages) {
|
||||
if hasPermission(mtv.app.State, mtv.app.SelectedChannel.ID, discord.PermissionSendMessages) {
|
||||
actionsList.AddItem("Reply", "", 'r', func() {
|
||||
mtv.app.MessageInputField.SetTitle("Replying to " + m.Author.String())
|
||||
mtv.app.MessageInputField.SetTitle("Replying to " + m.Author.Tag())
|
||||
mtv.app.
|
||||
SetRoot(mtv.app.MainFlex, true).
|
||||
SetFocus(mtv.app.MessageInputField)
|
||||
})
|
||||
|
||||
actionsList.AddItem("Mention Reply", "", 'R', func() {
|
||||
mtv.app.MessageInputField.SetTitle("[@] Replying to " + m.Author.String())
|
||||
mtv.app.MessageInputField.SetTitle("[@] Replying to " + m.Author.Tag())
|
||||
mtv.app.
|
||||
SetRoot(mtv.app.MainFlex, true).
|
||||
SetFocus(mtv.app.MessageInputField)
|
||||
|
|
@ -136,9 +150,9 @@ func (mtv *MessagesTextView) onInputCapture(e *tcell.EventKey) *tcell.EventKey {
|
|||
}
|
||||
|
||||
// If the client user has the `MANAGE_MESSAGES` permission, add a new action to delete the message.
|
||||
if discord.HasPermission(mtv.app.Session.State, mtv.app.SelectedChannel.ID, astatine.PermissionManageMessages) {
|
||||
if hasPermission(mtv.app.State, mtv.app.SelectedChannel.ID, discord.PermissionManageMessages) {
|
||||
actionsList.AddItem("Delete", "", 'd', func() {
|
||||
go mtv.deleteMessage(m)
|
||||
go mtv.deleteMessage(*m)
|
||||
mtv.app.
|
||||
SetRoot(mtv.app.MainFlex, true).
|
||||
SetFocus(mtv.app.MessagesTextView)
|
||||
|
|
@ -148,9 +162,9 @@ func (mtv *MessagesTextView) onInputCapture(e *tcell.EventKey) *tcell.EventKey {
|
|||
// If the referenced message exists, add a new action to select the reply.
|
||||
if m.ReferencedMessage != nil {
|
||||
actionsList.AddItem("Select Reply", "", 'm', func() {
|
||||
mtv.app.SelectedMessage, _ = discord.FindMessageByID(mtv.app.SelectedChannel.Messages, m.ReferencedMessage.ID)
|
||||
mtv.app.SelectedMessage, _ = findMessageByID(ms, m.ReferencedMessage.ID)
|
||||
mtv.app.MessagesTextView.
|
||||
Highlight(m.ReferencedMessage.ID).
|
||||
Highlight(m.ReferencedMessage.ID.String()).
|
||||
ScrollToHighlight()
|
||||
mtv.app.
|
||||
SetRoot(mtv.app.MainFlex, true).
|
||||
|
|
@ -189,7 +203,7 @@ func (mtv *MessagesTextView) onInputCapture(e *tcell.EventKey) *tcell.EventKey {
|
|||
mtv.app.SetFocus(mtv.app.MessagesTextView)
|
||||
})
|
||||
actionsList.AddItem("Copy ID", "", 'i', func() {
|
||||
if err := clipboard.WriteAll(m.ID); err != nil {
|
||||
if err := clipboard.WriteAll(m.ID.String()); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -211,7 +225,7 @@ func (mtv *MessagesTextView) onInputCapture(e *tcell.EventKey) *tcell.EventKey {
|
|||
return e
|
||||
}
|
||||
|
||||
func (mtv *MessagesTextView) downloadAttachment(as []*astatine.MessageAttachment) error {
|
||||
func (mtv *MessagesTextView) downloadAttachment(as []discord.Attachment) error {
|
||||
for _, a := range as {
|
||||
f, err := os.Create(filepath.Join(mtv.app.Config.AttachmentDownloadsDir, a.Filename))
|
||||
if err != nil {
|
||||
|
|
@ -224,7 +238,7 @@ func (mtv *MessagesTextView) downloadAttachment(as []*astatine.MessageAttachment
|
|||
return err
|
||||
}
|
||||
|
||||
d, err := ioutil.ReadAll(resp.Body)
|
||||
d, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -235,7 +249,7 @@ func (mtv *MessagesTextView) downloadAttachment(as []*astatine.MessageAttachment
|
|||
return nil
|
||||
}
|
||||
|
||||
func (mtv *MessagesTextView) openAttachment(as []*astatine.MessageAttachment) error {
|
||||
func (mtv *MessagesTextView) openAttachment(as []discord.Attachment) error {
|
||||
for _, a := range as {
|
||||
cacheDirPath, _ := os.UserCacheDir()
|
||||
f, err := os.Create(filepath.Join(cacheDirPath, a.Filename))
|
||||
|
|
@ -249,7 +263,7 @@ func (mtv *MessagesTextView) openAttachment(as []*astatine.MessageAttachment) er
|
|||
return err
|
||||
}
|
||||
|
||||
d, err := ioutil.ReadAll(resp.Body)
|
||||
d, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -261,28 +275,40 @@ func (mtv *MessagesTextView) openAttachment(as []*astatine.MessageAttachment) er
|
|||
return nil
|
||||
}
|
||||
|
||||
func (mtv *MessagesTextView) deleteMessage(m *astatine.Message) {
|
||||
func (mtv *MessagesTextView) deleteMessage(m discord.Message) {
|
||||
mtv.Clear()
|
||||
|
||||
mtv.app.SelectedChannel.Messages = append(mtv.app.SelectedChannel.Messages[:mtv.app.SelectedMessage], mtv.app.SelectedChannel.Messages[mtv.app.SelectedMessage+1:]...)
|
||||
|
||||
err := mtv.app.Session.ChannelMessageDelete(m.ChannelID, m.ID)
|
||||
err := mtv.app.State.MessageRemove(m.ChannelID, m.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, m := range mtv.app.SelectedChannel.Messages {
|
||||
mtv.app.ChannelsTreeView.drawMessage(m)
|
||||
err = mtv.app.State.DeleteMessage(m.ChannelID, m.ID, "Unknown")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// The returned slice will be sorted from latest to oldest.
|
||||
ms, err := mtv.app.State.Messages(m.ChannelID, mtv.app.Config.MessagesLimit)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for i := len(ms) - 1; i >= 0; i-- {
|
||||
_, err = mtv.app.MessagesTextView.Write(buildMessage(mtv.app, ms[i]))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type MessageInputField struct {
|
||||
type MessageInput struct {
|
||||
*tview.InputField
|
||||
app *App
|
||||
}
|
||||
|
||||
func NewMessageInputField(app *App) *MessageInputField {
|
||||
mi := &MessageInputField{
|
||||
func NewMessageInput(app *App) *MessageInput {
|
||||
mi := &MessageInput{
|
||||
InputField: tview.NewInputField(),
|
||||
app: app,
|
||||
}
|
||||
|
|
@ -297,7 +323,7 @@ func NewMessageInputField(app *App) *MessageInputField {
|
|||
return mi
|
||||
}
|
||||
|
||||
func (mi *MessageInputField) onInputCapture(e *tcell.EventKey) *tcell.EventKey {
|
||||
func (mi *MessageInput) onInputCapture(e *tcell.EventKey) *tcell.EventKey {
|
||||
switch e.Name() {
|
||||
case "Enter":
|
||||
if mi.app.SelectedChannel == nil {
|
||||
|
|
@ -309,27 +335,37 @@ func (mi *MessageInputField) onInputCapture(e *tcell.EventKey) *tcell.EventKey {
|
|||
return nil
|
||||
}
|
||||
|
||||
ms, err := mi.app.State.Messages(mi.app.SelectedChannel.ID, mi.app.Config.MessagesLimit)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(mi.app.MessagesTextView.GetHighlights()) != 0 {
|
||||
_, m := discord.FindMessageByID(mi.app.SelectedChannel.Messages, mi.app.MessagesTextView.GetHighlights()[0])
|
||||
d := &astatine.MessageSend{
|
||||
Content: t,
|
||||
Reference: m.Reference(),
|
||||
AllowedMentions: &astatine.MessageAllowedMentions{RepliedUser: false},
|
||||
}
|
||||
if strings.HasPrefix(mi.app.MessageInputField.GetTitle(), "[@]") {
|
||||
d.AllowedMentions.RepliedUser = true
|
||||
} else {
|
||||
d.AllowedMentions.RepliedUser = false
|
||||
mID, err := discord.ParseSnowflake(mi.app.MessagesTextView.GetHighlights()[0])
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
go mi.app.Session.ChannelMessageSendComplex(m.ChannelID, d)
|
||||
_, m := findMessageByID(ms, discord.MessageID(mID))
|
||||
d := api.SendMessageData{
|
||||
Content: t,
|
||||
Reference: m.Reference,
|
||||
AllowedMentions: &api.AllowedMentions{RepliedUser: option.False},
|
||||
}
|
||||
|
||||
// If the title of the message InputField widget has "[@]" as a prefix, send the message as a reply and mention the replied user.
|
||||
if strings.HasPrefix(mi.app.MessageInputField.GetTitle(), "[@]") {
|
||||
d.AllowedMentions.RepliedUser = option.True
|
||||
}
|
||||
|
||||
go mi.app.State.SendMessageComplex(m.ChannelID, d)
|
||||
|
||||
mi.app.SelectedMessage = -1
|
||||
mi.app.MessagesTextView.Highlight()
|
||||
|
||||
mi.app.MessageInputField.SetTitle("")
|
||||
} else {
|
||||
go mi.app.Session.ChannelMessageSend(mi.app.SelectedChannel.ID, t)
|
||||
go mi.app.State.SendMessage(mi.app.SelectedChannel.ID, t)
|
||||
}
|
||||
|
||||
mi.app.MessageInputField.SetText("")
|
||||
|
|
|
|||
46
ui/util.go
Normal file
46
ui/util.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/diamondburned/arikawa/v3/discord"
|
||||
"github.com/diamondburned/arikawa/v3/state"
|
||||
)
|
||||
|
||||
func channelToString(c discord.Channel) string {
|
||||
var repr string
|
||||
if c.Name != "" {
|
||||
repr = "#" + c.Name
|
||||
} else if len(c.DMRecipients) == 1 {
|
||||
rp := c.DMRecipients[0]
|
||||
repr = rp.Username + "#" + rp.Discriminator
|
||||
} else {
|
||||
rps := make([]string, len(c.DMRecipients))
|
||||
for i, r := range c.DMRecipients {
|
||||
rps[i] = r.Username + "#" + r.Discriminator
|
||||
}
|
||||
|
||||
repr = strings.Join(rps, ", ")
|
||||
}
|
||||
|
||||
return repr
|
||||
}
|
||||
|
||||
func findMessageByID(ms []discord.Message, mID discord.MessageID) (int, *discord.Message) {
|
||||
for i, m := range ms {
|
||||
if m.ID == mID {
|
||||
return i, &m
|
||||
}
|
||||
}
|
||||
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func hasPermission(s *state.State, cID discord.ChannelID, p discord.Permissions) bool {
|
||||
perm, err := s.Permissions(cID, s.Ready().User.ID)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return perm&p == p
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue