ci: add golangci-lint
This commit is contained in:
parent
9320e20983
commit
39222f1073
17 changed files with 104 additions and 63 deletions
11
.github/workflows/ci.yml
vendored
11
.github/workflows/ci.yml
vendored
|
|
@ -2,6 +2,17 @@ name: ci
|
|||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
- uses: golangci/golangci-lint-action@v9
|
||||
with:
|
||||
version: latest
|
||||
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ func Run() error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("failed to load logger: %w", err)
|
||||
}
|
||||
defer logFile.Close()
|
||||
defer logFile.Close() //nolint:errcheck
|
||||
|
||||
cfg, err := config.Load(*configPath)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -291,7 +291,7 @@ func Load(path string) (*Config, error) {
|
|||
case err != nil:
|
||||
return nil, fmt.Errorf("failed to open config file: %w", err)
|
||||
default:
|
||||
defer file.Close()
|
||||
defer file.Close() //nolint:errcheck
|
||||
if _, err := toml.NewDecoder(file).Decode(&cfg); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode config: %w", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/ayn2op/arikawa/v3/discord"
|
||||
"github.com/ayn2op/tview"
|
||||
"github.com/gdamore/tcell/v3"
|
||||
"github.com/gdamore/tcell/v3/color"
|
||||
)
|
||||
|
||||
var errInvalidType = errors.New("invalid type")
|
||||
|
|
@ -89,7 +90,7 @@ func (sw *StyleWrapper) parseAttr(s string) {
|
|||
}
|
||||
// An unknown attribute parses to no attribute.
|
||||
attr, _ := attributes.parse(s)
|
||||
sw.Style = sw.Attributes(sw.GetAttributes() | attr)
|
||||
sw.Style = sw.Attributes(sw.GetAttributes() | attr) //nolint:staticcheck
|
||||
}
|
||||
|
||||
// MarshalTOML writes the style as the inline table UnmarshalTOML reads.
|
||||
|
|
@ -254,14 +255,14 @@ func defaultTheme() ThemeConfig {
|
|||
dim = StyleWrapper{plain.Dim(true)}
|
||||
bold = plain.Bold(true)
|
||||
italic = plain.Italic(true)
|
||||
blue = plain.Foreground(tcell.ColorBlue)
|
||||
focus = StyleThemeConfig{dim, StyleWrapper{bold.Foreground(tcell.ColorGreen)}}
|
||||
blue = plain.Foreground(color.Blue)
|
||||
focus = StyleThemeConfig{dim, StyleWrapper{bold.Foreground(color.Green)}}
|
||||
tree = TreeThemeConfig{
|
||||
GraphicsColor: "default",
|
||||
OnlineStyle: StyleWrapper{plain.Foreground(tcell.ColorGreen)},
|
||||
IdleStyle: StyleWrapper{plain.Foreground(tcell.ColorYellow)},
|
||||
DNDStyle: StyleWrapper{plain.Foreground(tcell.ColorRed)},
|
||||
OfflineStyle: StyleWrapper{plain.Foreground(tcell.ColorGray)},
|
||||
OnlineStyle: StyleWrapper{plain.Foreground(color.Green)},
|
||||
IdleStyle: StyleWrapper{plain.Foreground(color.Yellow)},
|
||||
DNDStyle: StyleWrapper{plain.Foreground(color.Red)},
|
||||
OfflineStyle: StyleWrapper{plain.Foreground(color.Gray)},
|
||||
}
|
||||
)
|
||||
return ThemeConfig{
|
||||
|
|
@ -270,16 +271,16 @@ func defaultTheme() ThemeConfig {
|
|||
Border: focus,
|
||||
GuildsTree: GuildsTreeThemeConfig{
|
||||
TreeThemeConfig: tree,
|
||||
SpeakingStyle: StyleWrapper{plain.Foreground(tcell.ColorGreen)},
|
||||
VoiceLimitStyle: StyleWrapper{plain.Foreground(tcell.ColorDodgerBlue)},
|
||||
SpeakingStyle: StyleWrapper{plain.Foreground(color.Green)},
|
||||
VoiceLimitStyle: StyleWrapper{plain.Foreground(color.DodgerBlue)},
|
||||
},
|
||||
MembersTree: tree,
|
||||
ScrollBar: ScrollBarThemeConfig{TrackStyle: dim},
|
||||
MessagesList: MessagesListThemeConfig{
|
||||
MentionStyle: StyleWrapper{blue.Bold(true)},
|
||||
EmojiStyle: StyleWrapper{plain.Foreground(tcell.ColorGreen)},
|
||||
EmojiStyle: StyleWrapper{plain.Foreground(color.Green)},
|
||||
URLStyle: StyleWrapper{blue},
|
||||
AttachmentStyle: StyleWrapper{plain.Foreground(tcell.ColorYellow)},
|
||||
AttachmentStyle: StyleWrapper{plain.Foreground(color.Yellow)},
|
||||
ReactionStyle: dim,
|
||||
OwnReactionStyle: StyleWrapper{bold},
|
||||
SelectedMessageStyle: StyleWrapper{plain.Reverse(true)},
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
//go:build no_spoof_tls_fingerprint
|
||||
|
||||
package http
|
||||
|
||||
import (
|
||||
|
|
|
|||
|
|
@ -361,7 +361,7 @@ func (m Model) processText(channel *discord.Channel, src []byte) string {
|
|||
canMention = true
|
||||
)
|
||||
|
||||
ast.Walk(md.Parse(src), func(node ast.Node, enter bool) (ast.WalkStatus, error) {
|
||||
_ = ast.Walk(md.Parse(src), func(node ast.Node, enter bool) (ast.WalkStatus, error) {
|
||||
switch node := node.(type) {
|
||||
case *ast.CodeBlock, *ast.FencedCodeBlock:
|
||||
canMention = !enter
|
||||
|
|
@ -403,7 +403,7 @@ func (m Model) expandMentions(channel *discord.Channel, src []byte) []byte {
|
|||
}
|
||||
return output
|
||||
}
|
||||
state.MemberStore.Each(channel.GuildID, func(m *discord.Member) bool {
|
||||
_ = state.MemberStore.Each(channel.GuildID, func(m *discord.Member) bool {
|
||||
if strings.EqualFold(m.User.Username, name) {
|
||||
if channelHasUser(state, channel.ID, m.User.ID) {
|
||||
output = []byte(m.User.ID.Mention())
|
||||
|
|
|
|||
|
|
@ -40,12 +40,12 @@ func TestModelSetChannel(t *testing.T) {
|
|||
channel := discord.Channel{ID: 10, GuildID: 1}
|
||||
thread := discord.Channel{ID: 11, GuildID: 1, ParentID: 10, Type: discord.GuildPublicThread}
|
||||
for _, err := range []error{
|
||||
state.Cabinet.MyselfSet(discord.User{ID: 3}, false),
|
||||
state.Cabinet.GuildSet(&discord.Guild{ID: 1}, false),
|
||||
state.Cabinet.MemberSet(1, &discord.Member{User: discord.User{ID: 3}}, false),
|
||||
state.Cabinet.RoleSet(1, &discord.Role{ID: 1, Permissions: discord.PermissionSendMessagesInThreads}, false),
|
||||
state.Cabinet.ChannelSet(&channel, false),
|
||||
state.Cabinet.ChannelSet(&thread, false),
|
||||
state.MyselfSet(discord.User{ID: 3}, false),
|
||||
state.GuildSet(&discord.Guild{ID: 1}, false),
|
||||
state.MemberSet(1, &discord.Member{User: discord.User{ID: 3}}, false),
|
||||
state.RoleSet(1, &discord.Role{ID: 1, Permissions: discord.PermissionSendMessagesInThreads}, false),
|
||||
state.ChannelSet(&channel, false),
|
||||
state.ChannelSet(&thread, false),
|
||||
} {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -168,13 +168,13 @@ func TestModelSearchMember(t *testing.T) {
|
|||
|
||||
func TestModelTabSuggest(t *testing.T) {
|
||||
c, _ := newTestModel(t)
|
||||
if err := c.state.Cabinet.MyselfSet(discord.User{ID: 1, Username: "1"}, false); err != nil {
|
||||
if err := c.state.MyselfSet(discord.User{ID: 1, Username: "1"}, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
channel := discord.Channel{ID: 1, Type: discord.DirectMessage}
|
||||
for id, author := range []discord.UserID{2, 3, 2, 1} {
|
||||
message := discord.Message{ID: discord.MessageID(id + 1), ChannelID: 1, Author: discord.User{ID: author, Username: author.String()}}
|
||||
if err := c.state.Cabinet.MessageSet(&message, false); err != nil {
|
||||
if err := c.state.MessageSet(&message, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,9 @@ func closeFiles(files []sendpart.File) tview.Cmd {
|
|||
return func() tview.Msg {
|
||||
for _, file := range files {
|
||||
if closer, ok := file.Reader.(io.Closer); ok {
|
||||
closer.Close()
|
||||
if err := closer.Close(); err != nil {
|
||||
slog.Error("failed to close file", "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
@ -121,7 +123,9 @@ func (m *Model) sendTyping() tview.Cmd {
|
|||
}
|
||||
channelID := selectedChannel.ID
|
||||
return func() tview.Msg {
|
||||
m.state.Typing(channelID)
|
||||
if err := m.state.Typing(channelID); err != nil {
|
||||
slog.Error("failed to send typing indicator", "err", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
@ -327,7 +331,11 @@ func (m Model) openEditor() tview.Cmd {
|
|||
return nil
|
||||
}
|
||||
name := file.Name()
|
||||
defer os.Remove(name)
|
||||
defer func() {
|
||||
if err := os.Remove(name); err != nil {
|
||||
slog.Error("failed to remove tmp file", "err", err)
|
||||
}
|
||||
}()
|
||||
_, _ = file.WriteString(text)
|
||||
_ = file.Close()
|
||||
|
||||
|
|
|
|||
|
|
@ -250,7 +250,7 @@ func (m Model) createChannelNode(parent *tree.Node, channel discord.Channel) {
|
|||
|
||||
// showVoiceMembers lists who is in each voice channel of the guild under it.
|
||||
func (m *Model) showVoiceMembers(guildID discord.GuildID) {
|
||||
states, _ := m.state.Cabinet.VoiceStates(guildID)
|
||||
states, _ := m.state.VoiceStates(guildID)
|
||||
slices.SortFunc(states, func(a, b discord.VoiceState) int { return cmp.Compare(a.UserID, b.UserID) })
|
||||
|
||||
channels, _ := m.state.Cabinet.Channels(guildID)
|
||||
|
|
@ -340,7 +340,7 @@ func (m Model) voiceFooter() string {
|
|||
|
||||
name := ui.ChannelToString(*channel, m.cfg.Icons, m.state)
|
||||
if me, _ := m.state.Cabinet.Me(); me != nil {
|
||||
if state, _ := m.state.Cabinet.VoiceState(channel.GuildID, me.ID); state != nil {
|
||||
if state, _ := m.state.VoiceState(channel.GuildID, me.ID); state != nil {
|
||||
name += m.voiceMarkers(*state)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,16 +29,22 @@ func newTestModel(t *testing.T) Model {
|
|||
func setGuildMembers(t *testing.T, m Model, items string) *discord.Channel {
|
||||
t.Helper()
|
||||
channel := &discord.Channel{ID: channelID, GuildID: guildID, Type: discord.GuildText}
|
||||
m.state.Cabinet.ChannelSet(channel, false)
|
||||
m.state.Cabinet.RoleSet(guildID, &discord.Role{ID: guildID, Permissions: discord.PermissionViewChannel}, false)
|
||||
m.state.Cabinet.RoleSet(guildID, &discord.Role{ID: 3, Name: "Mods"}, false)
|
||||
if err := m.state.ChannelSet(channel, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := m.state.RoleSet(guildID, &discord.Role{ID: guildID, Permissions: discord.PermissionViewChannel}, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := m.state.RoleSet(guildID, &discord.Role{ID: 3, Name: "Mods"}, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
event := &gateway.GuildMemberListUpdateEvent{ID: "everyone", GuildID: guildID}
|
||||
event.Ops = []gateway.GuildMemberListOp{{Op: "SYNC", Range: [2]int{0, 99}}}
|
||||
if err := json.Unmarshal([]byte(items), &event.Ops[0].Items); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m.state.State.Handler.Call(event)
|
||||
m.state.State.Call(event)
|
||||
return channel
|
||||
}
|
||||
|
||||
|
|
@ -106,8 +112,12 @@ func TestModelRebuild(t *testing.T) {
|
|||
|
||||
t.Run("group dm recipients", func(t *testing.T) {
|
||||
m := newTestModel(t)
|
||||
m.state.Cabinet.MyselfSet(discord.User{ID: 13, Username: "me"}, false)
|
||||
m.state.Cabinet.PresenceSet(discord.NullGuildID, &discord.Presence{User: discord.User{ID: 11}, Status: discord.OnlineStatus}, false)
|
||||
if err := m.state.MyselfSet(discord.User{ID: 13, Username: "me"}, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := m.state.PresenceSet(discord.NullGuildID, &discord.Presence{User: discord.User{ID: 11}, Status: discord.OnlineStatus}, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m.channel = &discord.Channel{Type: discord.GroupDM, DMRecipients: []discord.User{
|
||||
{ID: 10, Username: "a"},
|
||||
{ID: 11, Username: "c"},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package messageslist
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
|
|
@ -264,7 +265,7 @@ func (m Model) buildItem(index int) tview.Widget {
|
|||
item := &m.items[index]
|
||||
if item.separator {
|
||||
date := m.items[index+1].message.Timestamp.Time().In(time.Local).Format(m.cfg.DateSeparator.Format)
|
||||
return dateSeparator{date: date, fill: m.cfg.DateSeparator.Character, style: m.cfg.Theme.MessagesList.MessageStyle.Style.Dim(true)}
|
||||
return dateSeparator{date: date, fill: m.cfg.DateSeparator.Character, style: m.cfg.Theme.MessagesList.MessageStyle.Dim(true)}
|
||||
}
|
||||
|
||||
message := &item.message
|
||||
|
|
@ -1234,7 +1235,7 @@ func extractURLs(content string) []string {
|
|||
// src must be the byte slice the node was parsed from (AutoLink resolves its URL against it).
|
||||
func urlsFromAST(node ast.Node, src []byte) []string {
|
||||
var urls []string
|
||||
ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
_ = ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
if entering {
|
||||
switch n := n.(type) {
|
||||
case *ast.AutoLink:
|
||||
|
|
@ -1297,7 +1298,7 @@ func (m Model) get(url string) (*http.Response, error) {
|
|||
if err != nil || resp.StatusCode == http.StatusOK {
|
||||
return resp, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
resp.Body.Close() //nolint:errcheck
|
||||
|
||||
var links struct {
|
||||
URLs []struct {
|
||||
|
|
@ -1313,7 +1314,7 @@ func (m Model) get(url string) (*http.Response, error) {
|
|||
if resp, err = http.Get(links.URLs[0].Refreshed); err != nil || resp.StatusCode == http.StatusOK {
|
||||
return resp, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
resp.Body.Close() //nolint:errcheck
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unexpected HTTP status: %s", resp.Status)
|
||||
|
|
@ -1324,14 +1325,13 @@ func (m Model) downloadAttachment(attachment discord.Attachment, destination str
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer resp.Body.Close() //nolint:errcheck
|
||||
file, err := os.Create(destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
_, err = io.Copy(file, resp.Body)
|
||||
return err
|
||||
return errors.Join(err, file.Close())
|
||||
}
|
||||
|
||||
// SelectLastOwn selects the last message in the selected channel that the user can edit, and returns it.
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ func testConfig(t *testing.T) *config.Config {
|
|||
func newTestModel(t *testing.T, cfg *config.Config) *Model {
|
||||
t.Helper()
|
||||
ml := NewModel(cfg, ningen.FromState(state.New("")))
|
||||
if err := ml.state.Cabinet.MyselfSet(discord.User{ID: 1}, false); err != nil {
|
||||
if err := ml.state.MyselfSet(discord.User{ID: 1}, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ml.channel = &discord.Channel{ID: 1}
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ func (m Model) playVoiceMessage(attachment discord.Attachment) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer resp.Body.Close() //nolint:errcheck
|
||||
|
||||
// It is read whole, for playing it not to wait for the network.
|
||||
audio, err := io.ReadAll(resp.Body)
|
||||
|
|
@ -305,13 +305,13 @@ func (m Model) openDownloadedAttachment(attachment discord.Attachment) tview.Cmd
|
|||
if err != nil {
|
||||
return ui.ErrorDialog("create attachment file", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
path := file.Name()
|
||||
if err := file.Close(); err != nil {
|
||||
return ui.ErrorDialog("create attachment file", err)
|
||||
}
|
||||
|
||||
if err := m.downloadAttachment(attachment, path); err != nil {
|
||||
os.Remove(path)
|
||||
return ui.ErrorDialog("download attachment", err)
|
||||
return ui.ErrorDialog("download attachment", errors.Join(err, os.Remove(path)))
|
||||
} else if err := open.Start(path); err != nil {
|
||||
return ui.ErrorDialog("open attachment file", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ func (m Model) downloadPreview(proxy discord.URL, bounds image.Rectangle) (image
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer resp.Body.Close() //nolint:errcheck
|
||||
img, _, err := image.Decode(resp.Body)
|
||||
return img, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ func serveGateway(t *testing.T, acks int) http.HandlerFunc {
|
|||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
defer conn.Close() //nolint:errcheck
|
||||
|
||||
if err := conn.WriteJSON(map[string]any{"op": "hello", "heartbeat_interval": 1}); err != nil {
|
||||
return
|
||||
|
|
@ -85,7 +85,7 @@ func TestModelGateway(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { conn.Close() })
|
||||
t.Cleanup(func() { conn.Close() }) //nolint:errcheck
|
||||
|
||||
screen, err := tcell.NewTerminfoScreenFromTty(vt.NewMockTerm(vt.MockOptSize{X: 80, Y: 24}))
|
||||
if err != nil {
|
||||
|
|
@ -117,7 +117,7 @@ func TestCloseConn(t *testing.T) {
|
|||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
defer conn.Close() //nolint:errcheck
|
||||
_, _, _ = conn.ReadMessage()
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ package voice
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -130,7 +131,9 @@ func (p *Player) seek(position time.Duration) error {
|
|||
}
|
||||
|
||||
// Nothing is left to play if position is past the end.
|
||||
io.CopyN(io.Discard, decoder, (sample-first)*int64(decoder.Info().Channels)*2)
|
||||
if _, err := io.CopyN(io.Discard, decoder, (sample-first)*int64(decoder.Info().Channels)*2); err != nil && !errors.Is(err, io.EOF) {
|
||||
return err
|
||||
}
|
||||
p.decoder, p.samples = decoder, sample
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -189,16 +189,18 @@ func (s *Session) setSpeaking(userID discord.UserID, speaking bool) {
|
|||
}
|
||||
|
||||
// setDevice starts device, which then calls data, if on, and stops it otherwise.
|
||||
func setDevice(device **malgo.Device, on bool, kind malgo.DeviceType, data func(output, input []byte)) (err error) {
|
||||
func setDevice(device **malgo.Device, on bool, kind malgo.DeviceType, data func(output, input []byte)) error {
|
||||
switch {
|
||||
case on && *device == nil:
|
||||
var err error
|
||||
*device, err = startDevice(kind, channels, data)
|
||||
return err
|
||||
case !on && *device != nil:
|
||||
(*device).Uninit()
|
||||
*device = nil
|
||||
}
|
||||
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
// setDevices captures what the user says unless mute, and plays what is said unless deafen.
|
||||
|
|
@ -217,12 +219,14 @@ func (s *Session) setDevices(mute, deafen bool) error {
|
|||
|
||||
// left stops the devices, in no voice channel.
|
||||
func (s *Session) left() {
|
||||
s.setDevices(true, true)
|
||||
if err := s.setDevices(true, true); err != nil {
|
||||
slog.Error("failed to stop voice devices", "err", err)
|
||||
}
|
||||
s.setStatus(Status{})
|
||||
}
|
||||
|
||||
// Join joins the voice channel, muted, leaving the one that the user is in.
|
||||
func (s *Session) Join(channelID discord.ChannelID) (err error) {
|
||||
func (s *Session) Join(channelID discord.ChannelID) error {
|
||||
if !s.mu.TryLock() {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -230,18 +234,22 @@ func (s *Session) Join(channelID discord.ChannelID) (err error) {
|
|||
|
||||
s.setStatus(Status{ChannelID: channelID})
|
||||
|
||||
defer func() {
|
||||
if err := s.join(channelID); err != nil {
|
||||
// Discord may have the user in the voice channel regardless, and joining it again would then do nothing.
|
||||
if err != nil {
|
||||
s.leave()
|
||||
}
|
||||
}()
|
||||
return errors.Join(err, s.leave())
|
||||
}
|
||||
|
||||
s.setStatus(Status{ChannelID: channelID, Joined: true})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Session) join(channelID discord.ChannelID) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
session := s.session.Load()
|
||||
if session == nil {
|
||||
var err error
|
||||
if session, err = voice.NewSession(s.state, s.state.Handler); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -267,8 +275,6 @@ func (s *Session) Join(channelID discord.ChannelID) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
s.setStatus(Status{ChannelID: channelID, Joined: true})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue