fix(composer): check the thread send permission in a thread

This commit is contained in:
ayn2op 2026-10-13 21:21:00 -04:00
parent d54c2eb23f
commit 4b3e457e7c
2 changed files with 48 additions and 1 deletions

View file

@ -94,7 +94,14 @@ func NewModel(cfg *config.Config, state *ningen.State) Model {
func (m *Model) SetChannel(channel *discord.Channel) { func (m *Model) SetChannel(channel *discord.Channel) {
m.channel, m.typingUntil = channel, time.Time{} m.channel, m.typingUntil = channel, time.Time{}
isDM := channel.Type == discord.DirectMessage || channel.Type == discord.GroupDM isDM := channel.Type == discord.DirectMessage || channel.Type == discord.GroupDM
m.disabled = !isDM && !m.state.HasPermissions(channel.ID, discord.PermissionSendMessages)
// Sending in a thread takes another permission than in the channel it is in.
sendPerm := discord.PermissionSendMessages
if channel.Type.IsThread() {
sendPerm = discord.PermissionSendMessagesInThreads
}
m.disabled = !isDM && !m.state.HasPermissions(channel.ID, sendPerm)
} }
func (m Model) Disabled() bool { func (m Model) Disabled() bool {

View file

@ -8,6 +8,7 @@ import (
"github.com/ayn2op/arikawa/v3/discord" "github.com/ayn2op/arikawa/v3/discord"
"github.com/ayn2op/arikawa/v3/gateway" "github.com/ayn2op/arikawa/v3/gateway"
"github.com/ayn2op/arikawa/v3/state" "github.com/ayn2op/arikawa/v3/state"
"github.com/ayn2op/arikawa/v3/state/store/defaultstore"
"github.com/ayn2op/arikawa/v3/utils/sendpart" "github.com/ayn2op/arikawa/v3/utils/sendpart"
"github.com/ayn2op/discordo/internal/config" "github.com/ayn2op/discordo/internal/config"
"github.com/ayn2op/ningen/v3" "github.com/ayn2op/ningen/v3"
@ -28,6 +29,45 @@ func newTestModel(t *testing.T) (*Model, *config.Config) {
return &c, cfg return &c, cfg
} }
func TestModelSetChannel(t *testing.T) {
// Everyone in guild 1 may send in threads, but not in channels.
cfg, err := config.Load(filepath.Join(t.TempDir(), "config.toml"))
if err != nil {
t.Fatal(err)
}
state := ningen.FromState(state.NewWithStore("", defaultstore.New()))
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),
} {
if err != nil {
t.Fatal(err)
}
}
c := NewModel(cfg, state)
for name, tt := range map[string]struct {
channel discord.Channel
disabled bool
}{
"a channel that cannot be sent in disables it": {channel, true},
"a thread that can be sent in enables it": {thread, false},
} {
t.Run(name, func(t *testing.T) {
if c.SetChannel(&tt.channel); c.Disabled() != tt.disabled {
t.Fatalf("disabled = %v, want %v", c.Disabled(), tt.disabled)
}
})
}
}
func TestModelUpdate(t *testing.T) { func TestModelUpdate(t *testing.T) {
up := tcell.NewEventKey(tcell.KeyUp, "", tcell.ModNone) up := tcell.NewEventKey(tcell.KeyUp, "", tcell.ModNone)
for _, tt := range []struct { for _, tt := range []struct {