diff --git a/internal/config/boolstamp.go b/internal/config/boolstamp.go index 1c5a6f9..7244e3d 100644 --- a/internal/config/boolstamp.go +++ b/internal/config/boolstamp.go @@ -14,7 +14,7 @@ type boolStamp struct { var _ customType = (*boolStamp)(nil) -func (bs boolStamp) Marshal() string { +func (bs *boolStamp) Marshal() string { if bs.stamp > 0 { return bs.stamp.String() } diff --git a/internal/config/config.go b/internal/config/config.go index bc00063..bf5c424 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -25,6 +25,8 @@ func (c config) Marshal(dst map[string]string) error { dst[c.Name] = strconv.FormatBool(v) case string: dst[c.Name] = v + case customType: + dst[c.Name] = v.Marshal() default: return cchat.ErrInvalidConfigAtField{ Key: c.Name, @@ -50,7 +52,6 @@ func (c *config) Unmarshal(src map[string]string) (err error) { c.Value = strVal case customType: err = v.Unmarshal(strVal) - c.Value = v default: err = fmt.Errorf("unknown type %T", c.Value) } @@ -81,7 +82,7 @@ func (reg *registry) Configuration() (map[string]string, error) { reg.mutex.RLock() defer reg.mutex.RUnlock() - var configMap = map[string]string{} + configMap := make(map[string]string, len(reg.configs)) for _, config := range reg.configs { if err := config.Marshal(configMap); err != nil { diff --git a/internal/config/world.go b/internal/config/world.go index 4d24193..34225ca 100644 --- a/internal/config/world.go +++ b/internal/config/world.go @@ -17,7 +17,7 @@ var world = ®istry{ // MentionOnReply returns true if message replies should mention users. func MentionOnReply(timestamp time.Time) bool { - v := world.get(0).(boolStamp) + v := world.get(0).(*boolStamp) if v.stamp > 0 { return timestamp.Add(v.stamp).Before(time.Now()) diff --git a/internal/discord/message/author.go b/internal/discord/message/author.go index 165cffc..b68fa8a 100644 --- a/internal/discord/message/author.go +++ b/internal/discord/message/author.go @@ -60,13 +60,25 @@ func richUser(rich *text.Rich, user *mention.User) (start, end int) { return } +// ID returns the author's ID. The ID may not be a valid Discord user ID if the +// user is not a valid (real) user (e.g. webhooks). func (a Author) ID() cchat.ID { - return a.user.UserID().String() + user := a.user.User() + + id := user.ID.String() + + // Treat pseudo-users specially. + if user.Discriminator == "0000" { + id += "_" + user.Username + } + + return id } // Name subscribes the author to the global name label registry. func (a Author) Name(_ context.Context, l cchat.LabelContainer) (func(), error) { - return a.state.Labels.AddMemberLabel(a.user.GuildID(), a.user.UserID(), l), nil + l.SetLabel(a.name) + return func() {}, nil } const authorReplyingTo = " replying to " diff --git a/internal/discord/session/channel/messenger/messenger.go b/internal/discord/session/channel/messenger/messenger.go index 7786f44..91b5e91 100644 --- a/internal/discord/session/channel/messenger/messenger.go +++ b/internal/discord/session/channel/messenger/messenger.go @@ -2,7 +2,6 @@ package messenger import ( "context" - "sort" "github.com/diamondburned/arikawa/v2/discord" "github.com/diamondburned/arikawa/v2/gateway" @@ -38,7 +37,7 @@ func (msgr *Messenger) JoinServer(ctx context.Context, ct cchat.MessagesContaine return nil, err } - var addcancel = funcutil.NewCancels() + addcancel := funcutil.NewCancels() if msgr.GuildID.IsValid() { // Subscribe to typing events. @@ -62,19 +61,9 @@ func (msgr *Messenger) JoinServer(ctx context.Context, ct cchat.MessagesContaine })) } - // Only do all this if we even have any messages. - if len(m) > 0 { - // Sort messages chronologically using the ID so that the oldest messages - // (ones with the smallest snowflake) is in front. - sort.Slice(m, func(i, j int) bool { return m[i].ID < m[j].ID }) - - // Iterate from the earliest messages to the latest messages. - for _, m := range m { - ct.CreateMessage(message.NewBacklogMessage(m, msgr.State)) - } - - // Mark this channel as read. - msgr.State.ReadState.MarkRead(msgr.ID, m[len(m)-1].ID) + // Iterate from the earliest messages to the latest messages. + for _, m := range m { + ct.CreateMessage(message.NewBacklogMessage(m, msgr.State)) } // Bind the handler. @@ -82,7 +71,6 @@ func (msgr *Messenger) JoinServer(ctx context.Context, ct cchat.MessagesContaine msgr.State.AddHandler(func(m *gateway.MessageCreateEvent) { if m.ChannelID == msgr.ID { ct.CreateMessage(message.NewGuildMessageCreate(m, msgr.State)) - msgr.State.ReadState.MarkRead(msgr.ID, m.ID) } }), msgr.State.AddHandler(func(m *gateway.MessageUpdateEvent) { diff --git a/internal/segments/mention/user.go b/internal/segments/mention/user.go index 920f621..555cbcd 100644 --- a/internal/segments/mention/user.go +++ b/internal/segments/mention/user.go @@ -52,13 +52,22 @@ func NewMemberText(s *ningen.State, g discord.GuildID, u discord.UserID) text.Ri // NewUserText creates a new rich text describing a Discord user using the // Presence API. func NewUserText(s *ningen.State, u discord.UserID) text.Rich { + var discordUser discord.User + p, err := s.Presence(0, u) if err != nil { - return text.Plain(u.Mention()) + ready := s.Ready() + if ready.User.ID != u { + return text.Plain("Unknown user " + u.String()) + } + + discordUser = ready.User } - user := NewUser(p.User) - user.WithPresence(*p) + user := NewUser(discordUser) + if p != nil { + user.WithPresence(*p) + } user.WithState(s) user.Prefetch() @@ -71,7 +80,7 @@ func NewUserText(s *ningen.State, u discord.UserID) text.Rich { }, } - if p.User.Bot { + if discordUser.Bot { rich.Content += " " rich.Segments = append(rich.Segments, colored.NewBlurple(segutil.Write(&rich, "[BOT]")), @@ -164,11 +173,8 @@ func (um *User) Prefetch() { // DisplayName returns either the nickname or the username. func (um *User) DisplayName() string { - if um.guildID.IsValid() { - m, err := um.store.Member(um.guildID, um.user.ID) - if err == nil && m.Nick != "" { - return m.Nick - } + if m := um.getMember(); m != nil && m.Nick != "" { + return m.Nick } return um.user.Username @@ -310,7 +316,9 @@ func (um *User) getGuild() *discord.Guild { } func (um *User) getMember() *discord.Member { - if !um.guildID.IsValid() { + // 0000 isn't a valid discriminator; the user is probably a webhook. + // Fallback to the actual user. + if !um.guildID.IsValid() || (um.user.Discriminator == "0000" && um.user.Bot) { return nil } @@ -327,6 +335,7 @@ func (um *User) getMember() *discord.Member { return nil } + um.user = m.User um.member = m return m }