refactor: modularize into packages

This commit is contained in:
ayntgl 2022-01-08 21:04:02 +04:00
parent 2a40b68648
commit 548c9f0007
11 changed files with 167 additions and 298 deletions

View file

@ -38,11 +38,9 @@ sudo mv ./discordo /usr/local/bin
1. Run the `discordo` executable with no arguments.
> On first startup, a default configuration file will be created at `$HOME/.config/discordo.toml` on Unix, `%AppData%/discordo.toml` on Windows, and `$HOME/Library/Application Support/discordo.toml` on Darwin.
2. Log in using the account email and password (first-time login) and click on the "Login" button to continue.
> Note: by default, Discordo utilizes OS-specific keyring to store credentials such as client authentication token. However, if you prefer not to use a keyring (not recommended), you may set the `token` field in the configuration file and Discordo will prioritize the usage of the provided token to login instead of keyring.
> Note: by default, Discordo utilizes OS-specific keyring to store credentials such as client authentication token. However, if you prefer not to use a keyring (not recommended), you may set the `DISCORDO_TOKEN` environment variable and Discordo will prioritize the usage of the provided token to login instead of keyring.
### Clipboard support

41
config/config.go Normal file
View file

@ -0,0 +1,41 @@
package config
var General = struct {
UserAgent string
Mouse bool
Notifications bool
FetchMessagesLimit int
}{
UserAgent: "Mozilla/5.0 (X11; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0",
Mouse: true,
Notifications: true,
FetchMessagesLimit: 50,
}
var Keybindings = struct {
FocusChannelsTreeView []string
FocusMessagesView []string
FocusMessageInputField []string
SelectPreviousMessage []string
SelectNextMessage []string
SelectFirstMessage []string
SelectLastMessage []string
SelectMessageReference []string
ReplySelectedMessage []string
MentionReplySelectedMessage []string
CopySelectedMessage []string
}{
FocusChannelsTreeView: []string{"Alt+Left"},
FocusMessagesView: []string{"Alt+Right"},
FocusMessageInputField: []string{"Alt+Down"},
SelectPreviousMessage: []string{"Up"},
SelectNextMessage: []string{"Down"},
SelectFirstMessage: []string{"Home"},
SelectLastMessage: []string{"End"},
ReplySelectedMessage: []string{"Rune[r]"},
MentionReplySelectedMessage: []string{"Rune[R]"},
CopySelectedMessage: []string{"Rune[c]"},
SelectMessageReference: []string{"Rune[m]"},
}

View file

@ -8,6 +8,7 @@ import (
"strings"
"github.com/ayntgl/discordgo"
"github.com/ayntgl/discordo/config"
"github.com/gen2brain/beeep"
"github.com/rivo/tview"
)
@ -19,26 +20,6 @@ var (
strikeThroughRegex = regexp.MustCompile(`(?m)~~(.*?)~~`)
)
func newSession() *discordgo.Session {
s, err := discordgo.New()
if err != nil {
panic(err)
}
s.UserAgent = conf.UserAgent
s.Identify.Compress = false
s.Identify.Intents = 0
s.Identify.LargeThreshold = 0
s.Identify.Properties.Device = ""
s.Identify.Properties.Browser = "Chrome"
s.Identify.Properties.OS = "Linux"
s.AddHandlerOnce(onSessionReady)
s.AddHandler(onSessionMessageCreate)
return s
}
func onSessionReady(_ *discordgo.Session, r *discordgo.Ready) {
dmNode := tview.NewTreeNode("Direct Messages").
Collapse()
@ -74,15 +55,15 @@ func onSessionReady(_ *discordgo.Session, r *discordgo.Ready) {
func onSessionMessageCreate(_ *discordgo.Session, m *discordgo.MessageCreate) {
if selectedChannel == nil || selectedChannel.ID != m.ChannelID {
if conf.Notifications {
if config.General.Notifications {
for _, u := range m.Mentions {
if u.ID == session.State.User.ID {
g, err := session.State.Guild(m.GuildID)
if u.ID == app.Session.State.User.ID {
g, err := app.Session.State.Guild(m.GuildID)
if err != nil {
return
}
c, err := session.State.Channel(m.ChannelID)
c, err := app.Session.State.Channel(m.ChannelID)
if err != nil {
return
}
@ -114,7 +95,7 @@ func login(email, password string) (*loginResponse, error) {
Email string `json:"email"`
Password string `json:"password"`
}{email, password}
resp, err := session.RequestWithBucketID(
resp, err := app.Session.RequestWithBucketID(
"POST",
discordgo.EndpointLogin,
data,
@ -139,7 +120,7 @@ func totp(code, ticket string) (*loginResponse, error) {
Ticket string `json:"ticket"`
}{code, ticket}
e := discordgo.EndpointAuth + "mfa/totp"
resp, err := session.RequestWithBucketID("POST", e, data, e)
resp, err := app.Session.RequestWithBucketID("POST", e, data, e)
if err != nil {
return nil, err
}
@ -314,7 +295,7 @@ func buildAttachments(b *strings.Builder, as []*discordgo.MessageAttachment) {
func buildMentions(content string, mentions []*discordgo.User) string {
for _, mUser := range mentions {
var color string
if mUser.ID == session.State.User.ID {
if mUser.ID == app.Session.State.User.ID {
color = "[:#5865F2]"
} else {
color = "[#EB459E]"
@ -334,7 +315,7 @@ func buildMentions(content string, mentions []*discordgo.User) string {
}
func buildAuthor(b *strings.Builder, u *discordgo.User) {
if u.ID == session.State.User.ID {
if u.ID == app.Session.State.User.ID {
b.WriteString("[#57F287]")
} else {
b.WriteString("[#ED4245]")

99
main.go
View file

@ -1,9 +1,11 @@
package main
import (
"os"
"github.com/ayntgl/discordgo"
"github.com/ayntgl/discordo/config"
"github.com/ayntgl/discordo/ui"
"github.com/ayntgl/discordo/util"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
"github.com/zalando/go-keyring"
@ -12,63 +14,49 @@ import (
const service = "discordo"
var (
app *tview.Application
app *ui.App
loginForm *tview.Form
channelsTreeView *tview.TreeView
messagesTextView *tview.TextView
messageInputField *tview.InputField
mainFlex *tview.Flex
conf *util.Config
session *discordgo.Session
selectedChannel *discordgo.Channel
selectedMessage int = -1
)
func main() {
conf = util.LoadConfig()
tview.Borders.Horizontal = conf.Borders.Horizontal
tview.Borders.Vertical = conf.Borders.Vertical
tview.Borders.TopLeft = conf.Borders.TopLeft
tview.Borders.TopRight = conf.Borders.TopRight
tview.Borders.BottomLeft = conf.Borders.BottomLeft
tview.Borders.BottomRight = conf.Borders.BottomRight
tview.Borders.HorizontalFocus = conf.Borders.HorizontalFocus
tview.Borders.VerticalFocus = conf.Borders.VerticalFocus
tview.Borders.TopLeftFocus = conf.Borders.TopLeftFocus
tview.Borders.TopRightFocus = conf.Borders.TopRightFocus
tview.Borders.BottomLeftFocus = conf.Borders.BottomLeftFocus
tview.Borders.BottomRightFocus = conf.Borders.BottomRightFocus
tview.Styles.PrimitiveBackgroundColor = tcell.GetColor(conf.Theme.Background)
tview.Styles.ContrastBackgroundColor = tcell.GetColor(conf.Theme.Background)
tview.Styles.MoreContrastBackgroundColor = tcell.GetColor(conf.Theme.Background)
tview.Styles.BorderColor = tcell.GetColor(conf.Theme.Border)
tview.Styles.TitleColor = tcell.GetColor(conf.Theme.Title)
tview.Styles.GraphicsColor = tcell.GetColor(conf.Theme.Graphics)
tview.Styles.PrimaryTextColor = tcell.GetColor(conf.Theme.Text)
tview.Styles.SecondaryTextColor = tcell.GetColor(conf.Theme.Text)
tview.Styles.TertiaryTextColor = tcell.GetColor(conf.Theme.Text)
tview.Styles.InverseTextColor = tcell.GetColor(conf.Theme.Text)
tview.Styles.ContrastSecondaryTextColor = tcell.GetColor(conf.Theme.Text)
app = tview.NewApplication()
app = ui.NewApp()
app.
EnableMouse(conf.Mouse).
EnableMouse(config.General.Mouse).
SetInputCapture(onAppInputCapture)
channelsTreeView = ui.NewChannelsTreeView()
channelsTreeView.SetSelectedFunc(onChannelsTreeSelected)
app.ChannelsTreeView.
SetTopLevel(1).
SetRoot(tview.NewTreeNode("")).
SetSelectedFunc(onChannelsTreeSelected).
SetTitleAlign(tview.AlignLeft).
SetBorder(true).
SetBorderPadding(0, 0, 1, 0)
messagesTextView = ui.NewMessagesTextView()
messagesTextView.
app.MessagesTextView.
SetRegions(true).
SetDynamicColors(true).
SetWordWrap(true).
SetChangedFunc(func() { app.Draw() }).
SetInputCapture(onMessagesViewInputCapture)
SetTitleAlign(tview.AlignLeft).
SetInputCapture(onMessagesViewInputCapture).
SetBorder(true).
SetBorderPadding(0, 0, 1, 0)
messageInputField = ui.NewMessageInputField()
messageInputField.SetInputCapture(onMessageInputFieldInputCapture)
app.MessageInputField.
SetPlaceholder("Message...").
SetPlaceholderTextColor(tcell.ColorWhite).
SetFieldBackgroundColor(tview.Styles.PrimitiveBackgroundColor).
SetInputCapture(onMessageInputFieldInputCapture).
SetTitleAlign(tview.AlignLeft).
SetBorder(true).
SetBorderPadding(0, 0, 1, 0)
rightFlex := tview.NewFlex().
SetDirection(tview.FlexRow).
@ -78,9 +66,9 @@ func main() {
AddItem(channelsTreeView, 0, 1, false).
AddItem(rightFlex, 0, 4, false)
token, err := keyring.Get(service, "token")
if err != nil {
token = conf.Token
token := os.Getenv("DISCORDO_TOKEN")
if token == "" {
token, _ = keyring.Get(service, "token")
}
if token != "" {
@ -88,10 +76,10 @@ func main() {
SetRoot(mainFlex, true).
SetFocus(channelsTreeView)
session = newSession()
session.Token = token
session.Identify.Token = token
if err := session.Open(); err != nil {
app.Session.AddHandlerOnce(onSessionReady)
app.Session.AddHandler(onSessionMessageCreate)
err := app.Connect(token)
if err != nil {
panic(err)
}
} else {
@ -111,7 +99,6 @@ func onLoginFormLoginButtonSelected() {
return
}
session = newSession()
// Login using the email and password
lr, err := login(email, password)
if err != nil {
@ -123,9 +110,10 @@ func onLoginFormLoginButtonSelected() {
SetRoot(mainFlex, true).
SetFocus(channelsTreeView)
session.Token = lr.Token
session.Identify.Token = lr.Token
if err = session.Open(); err != nil {
app.Session.AddHandlerOnce(onSessionReady)
app.Session.AddHandler(onSessionMessageCreate)
err = app.Connect(lr.Token)
if err != nil {
panic(err)
}
@ -147,9 +135,10 @@ func onLoginFormLoginButtonSelected() {
SetRoot(mainFlex, true).
SetFocus(channelsTreeView)
session.Token = lr.Token
session.Identify.Token = lr.Token
if err = session.Open(); err != nil {
app.Session.AddHandlerOnce(onSessionReady)
app.Session.AddHandler(onSessionMessageCreate)
err = app.Connect(lr.Token)
if err != nil {
panic(err)
}

47
ui.go
View file

@ -6,19 +6,20 @@ import (
"github.com/atotto/clipboard"
"github.com/ayntgl/discordgo"
"github.com/ayntgl/discordo/config"
"github.com/ayntgl/discordo/util"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
func onAppInputCapture(e *tcell.EventKey) *tcell.EventKey {
if util.HasKeybinding(conf.Keybindings.FocusChannelsTree, e.Name()) {
if util.HasKeybinding(config.Keybindings.FocusChannelsTreeView, e.Name()) {
app.SetFocus(channelsTreeView)
return nil
} else if util.HasKeybinding(conf.Keybindings.FocusMessagesView, e.Name()) {
} else if util.HasKeybinding(config.Keybindings.FocusMessagesView, e.Name()) {
app.SetFocus(messagesTextView)
return nil
} else if util.HasKeybinding(conf.Keybindings.FocusMessageInputField, e.Name()) {
} else if util.HasKeybinding(config.Keybindings.FocusMessageInputField, e.Name()) {
app.SetFocus(messageInputField)
return nil
}
@ -42,14 +43,14 @@ func onChannelsTreeSelected(n *tview.TreeNode) {
if len(n.GetChildren()) == 0 {
// If the reference of the selected `*TreeNode` is `nil`, it is the direct messages `*TreeNode`.
if id == nil {
cs := session.State.PrivateChannels
cs := app.Session.State.PrivateChannels
sort.Slice(cs, func(i, j int) bool {
return cs[i].LastMessageID > cs[j].LastMessageID
})
for _, c := range cs {
tag := "[::d]"
if util.ChannelIsUnread(session.State, c) {
if util.ChannelIsUnread(app.Session.State, c) {
tag = "[::b]"
}
@ -59,7 +60,7 @@ func onChannelsTreeSelected(n *tview.TreeNode) {
n.AddChild(cn)
}
} else {
g, err := session.State.Guild(id.(string))
g, err := app.Session.State.Guild(id.(string))
if err != nil {
return
}
@ -69,17 +70,17 @@ func onChannelsTreeSelected(n *tview.TreeNode) {
})
// Top-level channels
createTopLevelChannelsNodes(channelsTreeView, session.State, n, g.Channels)
createTopLevelChannelsNodes(channelsTreeView, app.Session.State, n, g.Channels)
// Category channels
createCategoryChannelsNodes(channelsTreeView, session.State, n, g.Channels)
createCategoryChannelsNodes(channelsTreeView, app.Session.State, n, g.Channels)
// Second-level channels
createSecondLevelChannelsNodes(channelsTreeView, session.State, g.Channels)
createSecondLevelChannelsNodes(channelsTreeView, app.Session.State, g.Channels)
}
}
n.SetExpanded(!n.IsExpanded())
default: // Channels
c, err := session.State.Channel(id.(string))
c, err := app.Session.State.Channel(id.(string))
if err != nil {
return
}
@ -104,7 +105,7 @@ func onChannelsTreeSelected(n *tview.TreeNode) {
}
go func() {
ms, err := session.ChannelMessages(c.ID, conf.GetMessagesLimit, "", "", "")
ms, err := app.Session.ChannelMessages(c.ID, config.General.FetchMessagesLimit, "", "", "")
if err != nil {
return
}
@ -116,8 +117,8 @@ func onChannelsTreeSelected(n *tview.TreeNode) {
// Scroll to the end of the text after the messages have been written to the TextView.
messagesTextView.ScrollToEnd()
if len(ms) != 0 && util.ChannelIsUnread(session.State, c) {
session.ChannelMessageAck(c.ID, c.LastMessageID, "")
if len(ms) != 0 && util.ChannelIsUnread(app.Session.State, c) {
app.Session.ChannelMessageAck(c.ID, c.LastMessageID, "")
}
}()
}
@ -186,7 +187,7 @@ func onMessagesViewInputCapture(e *tcell.EventKey) *tcell.EventKey {
return nil
}
if util.HasKeybinding(conf.Keybindings.SelectPreviousMessage, e.Name()) {
if util.HasKeybinding(config.Keybindings.SelectPreviousMessage, e.Name()) {
if len(messagesTextView.GetHighlights()) == 0 {
selectedMessage = len(ms) - 1
} else {
@ -200,7 +201,7 @@ func onMessagesViewInputCapture(e *tcell.EventKey) *tcell.EventKey {
Highlight(ms[selectedMessage].ID).
ScrollToHighlight()
return nil
} else if util.HasKeybinding(conf.Keybindings.SelectNextMessage, e.Name()) {
} else if util.HasKeybinding(config.Keybindings.SelectNextMessage, e.Name()) {
if len(messagesTextView.GetHighlights()) == 0 {
selectedMessage = len(ms) - 1
} else {
@ -214,19 +215,19 @@ func onMessagesViewInputCapture(e *tcell.EventKey) *tcell.EventKey {
Highlight(ms[selectedMessage].ID).
ScrollToHighlight()
return nil
} else if util.HasKeybinding(conf.Keybindings.SelectFirstMessage, e.Name()) {
} else if util.HasKeybinding(config.Keybindings.SelectFirstMessage, e.Name()) {
selectedMessage = 0
messagesTextView.
Highlight(ms[selectedMessage].ID).
ScrollToHighlight()
return nil
} else if util.HasKeybinding(conf.Keybindings.SelectLastMessage, e.Name()) {
} else if util.HasKeybinding(config.Keybindings.SelectLastMessage, e.Name()) {
selectedMessage = len(ms) - 1
messagesTextView.
Highlight(ms[selectedMessage].ID).
ScrollToHighlight()
return nil
} else if util.HasKeybinding(conf.Keybindings.SelectMessageReference, e.Name()) {
} else if util.HasKeybinding(config.Keybindings.SelectMessageReference, e.Name()) {
hs := messagesTextView.GetHighlights()
if len(hs) == 0 {
return nil
@ -241,7 +242,7 @@ func onMessagesViewInputCapture(e *tcell.EventKey) *tcell.EventKey {
}
return nil
} else if util.HasKeybinding(conf.Keybindings.ReplySelectedMessage, e.Name()) {
} else if util.HasKeybinding(config.Keybindings.ReplySelectedMessage, e.Name()) {
hs := messagesTextView.GetHighlights()
if len(hs) == 0 {
return nil
@ -251,7 +252,7 @@ func onMessagesViewInputCapture(e *tcell.EventKey) *tcell.EventKey {
messageInputField.SetTitle("Replying to " + m.Author.String())
app.SetFocus(messageInputField)
return nil
} else if util.HasKeybinding(conf.Keybindings.MentionReplySelectedMessage, e.Name()) {
} else if util.HasKeybinding(config.Keybindings.MentionReplySelectedMessage, e.Name()) {
hs := messagesTextView.GetHighlights()
if len(hs) == 0 {
return nil
@ -261,7 +262,7 @@ func onMessagesViewInputCapture(e *tcell.EventKey) *tcell.EventKey {
messageInputField.SetTitle("[@] Replying to " + m.Author.String())
app.SetFocus(messageInputField)
return nil
} else if util.HasKeybinding(conf.Keybindings.CopySelectedMessage, e.Name()) {
} else if util.HasKeybinding(config.Keybindings.CopySelectedMessage, e.Name()) {
hs := messagesTextView.GetHighlights()
if len(hs) == 0 {
return nil
@ -304,14 +305,14 @@ func onMessageInputFieldInputCapture(e *tcell.EventKey) *tcell.EventKey {
d.AllowedMentions.RepliedUser = false
}
go session.ChannelMessageSendComplex(m.ChannelID, d)
go app.Session.ChannelMessageSendComplex(m.ChannelID, d)
selectedMessage = -1
messagesTextView.Highlight()
messageInputField.SetTitle("")
} else {
go session.ChannelMessageSend(selectedChannel.ID, t)
go app.Session.ChannelMessageSend(selectedChannel.ID, t)
}
messageInputField.SetText("")

48
ui/app.go Normal file
View file

@ -0,0 +1,48 @@
package ui
import (
"github.com/ayntgl/discordgo"
"github.com/ayntgl/discordo/config"
"github.com/rivo/tview"
)
type App struct {
*tview.Application
ChannelsTreeView *tview.TreeView
MessagesTextView *tview.TextView
MessageInputField *tview.InputField
Session *discordgo.Session
}
func NewApp() *App {
s, _ := discordgo.New()
return &App{
Application: tview.NewApplication(),
ChannelsTreeView: tview.NewTreeView(),
MessagesTextView: tview.NewTextView(),
MessageInputField: tview.NewInputField(),
Session: s,
}
}
func (app *App) Connect(token string) error {
app.Session.Token = token
app.Session.UserAgent = config.General.UserAgent
app.Session.Identify.Token = token
app.Session.Identify.Compress = false
app.Session.Identify.Intents = 0
app.Session.Identify.LargeThreshold = 0
app.Session.Identify.Properties.Device = ""
app.Session.Identify.Properties.Browser = "Firefox"
app.Session.Identify.Properties.OS = "Linux"
// app.Session.AddHandlerOnce(onSessionReady)
// app.Session.AddHandler(onSessionMessageCreate)
return app.Session.Open()
}

View file

@ -1,15 +0,0 @@
package ui
import "github.com/rivo/tview"
func NewChannelsTreeView() *tview.TreeView {
v := tview.NewTreeView()
v.
SetTopLevel(1).
SetRoot(tview.NewTreeNode("")).
SetTitleAlign(tview.AlignLeft).
SetBorder(true).
SetBorderPadding(0, 0, 1, 0)
return v
}

View file

@ -1,19 +0,0 @@
package ui
import (
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
func NewMessageInputField() *tview.InputField {
i := tview.NewInputField()
i.
SetPlaceholder("Message...").
SetPlaceholderTextColor(tcell.ColorWhite).
SetFieldBackgroundColor(tview.Styles.PrimitiveBackgroundColor).
SetTitleAlign(tview.AlignLeft).
SetBorder(true).
SetBorderPadding(0, 0, 1, 0)
return i
}

View file

@ -1,16 +0,0 @@
package ui
import "github.com/rivo/tview"
func NewMessagesTextView() *tview.TextView {
v := tview.NewTextView()
v.
SetRegions(true).
SetDynamicColors(true).
SetWordWrap(true).
SetTitleAlign(tview.AlignLeft).
SetBorder(true).
SetBorderPadding(0, 0, 1, 0)
return v
}

View file

@ -1,139 +0,0 @@
package util
import (
"os"
"github.com/BurntSushi/toml"
"github.com/rivo/tview"
)
const userAgent = "" +
"Mozilla/5.0 (X11; Linux x86_64) " +
"AppleWebKit/537.36 (KHTML, like Gecko) " +
"Chrome/92.0.4515.131 Safari/537.36"
type keybindings struct {
FocusChannelsTree []string `toml:"focus_channels_tree"`
FocusMessagesView []string `toml:"focus_messages_view"`
FocusMessageInputField []string `toml:"focus_message_input_field"`
SelectPreviousMessage []string `toml:"select_previous_message"`
SelectNextMessage []string `toml:"select_next_message"`
SelectFirstMessage []string `toml:"select_first_message"`
SelectLastMessage []string `toml:"select_last_message"`
SelectMessageReference []string `toml:"select_message_reference"`
ReplySelectedMessage []string `toml:"reply_selected_message"`
MentionReplySelectedMessage []string `toml:"mention_reply_selected_message"`
CopySelectedMessage []string `toml:"copy_selected_message"`
}
type theme struct {
Background string `toml:"background"`
Border string `toml:"border"`
Title string `toml:"title"`
Graphics string `toml:"graphics"`
Text string `toml:"text"`
}
type borders struct {
Horizontal rune
Vertical rune
TopLeft rune
TopRight rune
BottomLeft rune
BottomRight rune
HorizontalFocus rune
VerticalFocus rune
TopLeftFocus rune
TopRightFocus rune
BottomLeftFocus rune
BottomRightFocus rune
}
type Config struct {
Token string `toml:"token"`
UserAgent string `toml:"user_agent"`
Mouse bool `toml:"mouse"`
Notifications bool `toml:"notifications"`
GetMessagesLimit int `toml:"get_messages_limit"`
Theme theme `toml:"theme"`
Keybindings keybindings `toml:"keybindings"`
Borders borders `toml:"borders"`
}
// LoadConfig loads the configuration file, if the configuration file exists or creates a new one if not, and returns it.
func LoadConfig() *Config {
configPath, err := os.UserConfigDir()
if err != nil {
panic(err)
}
configPath += "/discordo.toml"
var c Config
if _, err = os.Stat(configPath); os.IsNotExist(err) {
f, err := os.Create(configPath)
if err != nil {
panic(err)
}
defer f.Close()
c = Config{
Mouse: true,
Notifications: true,
UserAgent: userAgent,
GetMessagesLimit: 50,
Borders: borders{
Horizontal: 0,
Vertical: 0,
TopLeft: 0,
TopRight: 0,
BottomLeft: 0,
BottomRight: 0,
HorizontalFocus: tview.BoxDrawingsLightHorizontal,
VerticalFocus: tview.BoxDrawingsLightVertical,
TopLeftFocus: tview.BoxDrawingsLightDownAndRight,
TopRightFocus: tview.BoxDrawingsLightDownAndLeft,
BottomLeftFocus: tview.BoxDrawingsLightUpAndRight,
BottomRightFocus: tview.BoxDrawingsLightUpAndLeft,
},
Theme: theme{
Background: "black",
Border: "white",
Title: "white",
Graphics: "white",
Text: "white",
},
Keybindings: keybindings{
FocusChannelsTree: []string{"Alt+Left"},
FocusMessagesView: []string{"Alt+Right"},
FocusMessageInputField: []string{"Alt+Down"},
SelectPreviousMessage: []string{"Up"},
SelectNextMessage: []string{"Down"},
SelectFirstMessage: []string{"Home"},
SelectLastMessage: []string{"End"},
ReplySelectedMessage: []string{"Rune[r]"},
MentionReplySelectedMessage: []string{"Rune[R]"},
CopySelectedMessage: []string{"Rune[c]"},
SelectMessageReference: []string{"Rune[m]"},
},
}
err = toml.NewEncoder(f).Encode(c)
if err != nil {
panic(err)
}
} else {
_, err = toml.DecodeFile(configPath, &c)
if err != nil {
panic(err)
}
}
return &c
}