Added member list support
This commit is contained in:
parent
cdd9b68043
commit
9647ad0709
|
@ -110,7 +110,7 @@ func (ch *Channel) messages() ([]discord.Message, error) {
|
|||
|
||||
func (ch *Channel) guild() (*discord.Guild, error) {
|
||||
if ch.guildID.IsValid() {
|
||||
return ch.session.Guild(ch.guildID)
|
||||
return ch.session.Store.Guild(ch.guildID)
|
||||
}
|
||||
return nil, errors.New("channel not in a guild")
|
||||
}
|
||||
|
|
|
@ -3,18 +3,24 @@ package discord
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/diamondburned/arikawa/discord"
|
||||
"github.com/diamondburned/arikawa/gateway"
|
||||
"github.com/diamondburned/cchat"
|
||||
"github.com/diamondburned/cchat-discord/segments"
|
||||
"github.com/diamondburned/cchat-discord/urlutils"
|
||||
"github.com/diamondburned/cchat/text"
|
||||
"github.com/diamondburned/ningen/states/member"
|
||||
)
|
||||
|
||||
func seekPrevGroup(l *member.List, ix int) (item, group gateway.GuildMemberListOpItem) {
|
||||
l.ViewItems(func(items []gateway.GuildMemberListOpItem) {
|
||||
// Bound check.
|
||||
if ix >= len(items) {
|
||||
return
|
||||
}
|
||||
|
||||
item = items[ix]
|
||||
|
||||
// Search backwards.
|
||||
|
@ -40,6 +46,10 @@ func (ch *Channel) ListMembers(ctx context.Context, c cchat.MemberListContainer)
|
|||
return // wat
|
||||
}
|
||||
|
||||
if l.GuildID() != u.GuildID || l.ID() != u.ID {
|
||||
return
|
||||
}
|
||||
|
||||
for _, ev := range u.Ops {
|
||||
switch ev.Op {
|
||||
case "SYNC":
|
||||
|
@ -48,19 +58,22 @@ func (ch *Channel) ListMembers(ctx context.Context, c cchat.MemberListContainer)
|
|||
case "INSERT", "UPDATE":
|
||||
item, group := seekPrevGroup(l, ev.Index)
|
||||
if item.Member != nil && group.Group != nil {
|
||||
c.SetMember(group.Group.ID, NewListMember(ev.Index, ch, item))
|
||||
c.SetMember(group.Group.ID, NewListMember(ch, item))
|
||||
ch.flushMemberGroups(l, c)
|
||||
}
|
||||
|
||||
case "DELETE":
|
||||
_, group := seekPrevGroup(l, ev.Index-1)
|
||||
if group.Group != nil {
|
||||
c.RemoveMember(group.Group.ID, strconv.Itoa(ev.Index))
|
||||
if group.Group != nil && ev.Item.Member != nil {
|
||||
c.RemoveMember(group.Group.ID, ev.Item.Member.User.ID.String())
|
||||
ch.flushMemberGroups(l, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
ch.session.MemberState.RequestMemberList(ch.guildID, ch.id, 0)
|
||||
ch.checkSync(c)
|
||||
|
||||
return cancel, nil
|
||||
}
|
||||
|
||||
|
@ -71,86 +84,101 @@ func (ch *Channel) checkSync(c cchat.MemberListContainer) {
|
|||
return
|
||||
}
|
||||
|
||||
var sectionKeys []string
|
||||
var sectionsMap map[string]*ListSection
|
||||
|
||||
l.ViewGroups(func(groups []gateway.GuildMemberListGroup) {
|
||||
sectionKeys = make([]string, 0, len(groups))
|
||||
sectionsMap = make(map[string]*ListSection, len(groups))
|
||||
|
||||
for _, group := range groups {
|
||||
sectionKeys = append(sectionKeys, group.ID)
|
||||
sectionsMap[group.ID] = NewListSection(l.ID(), ch, group)
|
||||
}
|
||||
|
||||
var sections = make([]cchat.MemberListSection, len(sectionKeys))
|
||||
for i, key := range sectionKeys {
|
||||
sections[i] = sectionsMap[key]
|
||||
}
|
||||
|
||||
c.SetSections(sections)
|
||||
})
|
||||
ch.flushMemberGroups(l, c)
|
||||
|
||||
l.ViewItems(func(items []gateway.GuildMemberListOpItem) {
|
||||
var group gateway.GuildMemberListGroup
|
||||
|
||||
for i, item := range items {
|
||||
for _, item := range items {
|
||||
switch {
|
||||
case item.Group != nil:
|
||||
group = *item.Group
|
||||
|
||||
case item.Member != nil:
|
||||
c.SetMember(group.ID, NewListMember(i, ch, item))
|
||||
c.SetMember(group.ID, NewListMember(ch, item))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type ListMember struct {
|
||||
ix int // experiment with this being the index
|
||||
func (ch *Channel) flushMemberGroups(l *member.List, c cchat.MemberListContainer) {
|
||||
l.ViewGroups(func(groups []gateway.GuildMemberListGroup) {
|
||||
var sections = make([]cchat.MemberListSection, len(groups))
|
||||
for i, group := range groups {
|
||||
sections[i] = NewListSection(l.ID(), ch, group)
|
||||
}
|
||||
|
||||
c.SetSections(sections)
|
||||
})
|
||||
}
|
||||
|
||||
type ListMember struct {
|
||||
// Keep stateful references to do on-demand loading.
|
||||
channel *Channel
|
||||
|
||||
// constant states
|
||||
userID discord.UserID
|
||||
roleID discord.RoleID
|
||||
origName string // use if cache is stale
|
||||
}
|
||||
|
||||
var _ cchat.ListMember = (*ListMember)(nil)
|
||||
var (
|
||||
_ cchat.ListMember = (*ListMember)(nil)
|
||||
_ cchat.Icon = (*ListMember)(nil)
|
||||
)
|
||||
|
||||
// NewListMember creates a new list member. it.Member must not be nil.
|
||||
func NewListMember(ix int, ch *Channel, it gateway.GuildMemberListOpItem) *ListMember {
|
||||
roleID, _ := discord.ParseSnowflake(it.Member.HoistedRole)
|
||||
|
||||
func NewListMember(ch *Channel, it gateway.GuildMemberListOpItem) *ListMember {
|
||||
return &ListMember{
|
||||
ix: ix,
|
||||
channel: ch,
|
||||
userID: it.Member.User.ID,
|
||||
roleID: discord.RoleID(roleID),
|
||||
channel: ch,
|
||||
userID: it.Member.User.ID,
|
||||
origName: it.Member.User.Username,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *ListMember) ID() string {
|
||||
return strconv.Itoa(l.ix)
|
||||
return l.userID.String()
|
||||
}
|
||||
|
||||
func (l *ListMember) Name() text.Rich {
|
||||
n, err := l.channel.session.MemberDisplayName(l.channel.guildID, l.userID)
|
||||
g, err := l.channel.guild()
|
||||
if err != nil {
|
||||
return text.Plain(l.origName)
|
||||
}
|
||||
|
||||
r, err := l.channel.session.State.Role(l.channel.guildID, l.roleID)
|
||||
m, err := l.channel.session.Member(l.channel.guildID, l.userID)
|
||||
if err != nil {
|
||||
return text.Plain(l.origName)
|
||||
}
|
||||
|
||||
return text.Rich{
|
||||
Content: n,
|
||||
Segments: []text.Segment{segments.NewColored(len(n), uint32(r.Color))},
|
||||
var name = m.User.Username
|
||||
if m.Nick != "" {
|
||||
name = m.Nick
|
||||
}
|
||||
|
||||
mention := segments.MemberSegment(0, len(name), *g, *m)
|
||||
mention.WithState(l.channel.session.State)
|
||||
|
||||
var txt = text.Rich{
|
||||
Content: name,
|
||||
Segments: []text.Segment{mention},
|
||||
}
|
||||
|
||||
if c := discord.MemberColor(*g, *m); c != discord.DefaultMemberColor {
|
||||
txt.Segments = append(txt.Segments, segments.NewColored(len(name), uint32(c)))
|
||||
}
|
||||
|
||||
return txt
|
||||
}
|
||||
|
||||
func (l *ListMember) Icon(ctx context.Context, c cchat.IconContainer) (func(), error) {
|
||||
m, err := l.channel.session.Member(l.channel.guildID, l.userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.SetIcon(urlutils.AvatarURL(m.User.AvatarURL()))
|
||||
|
||||
return func() {}, nil
|
||||
}
|
||||
|
||||
func (l *ListMember) Status() cchat.UserStatus {
|
||||
|
@ -180,16 +208,57 @@ func (l *ListMember) Secondary() text.Rich {
|
|||
}
|
||||
|
||||
if p.Game != nil {
|
||||
return segments.FormatActivity(*p.Game)
|
||||
return formatSmallActivity(*p.Game)
|
||||
}
|
||||
|
||||
if len(p.Activities) > 0 {
|
||||
return segments.FormatActivity(p.Activities[0])
|
||||
return formatSmallActivity(p.Activities[0])
|
||||
}
|
||||
|
||||
return text.Plain("")
|
||||
}
|
||||
|
||||
func formatSmallActivity(ac discord.Activity) text.Rich {
|
||||
switch ac.Type {
|
||||
case discord.GameActivity:
|
||||
return text.Plain(fmt.Sprintf("Playing %s", ac.Name))
|
||||
|
||||
case discord.ListeningActivity:
|
||||
return text.Plain(fmt.Sprintf("Listening to %s", ac.Name))
|
||||
|
||||
case discord.StreamingActivity:
|
||||
return text.Plain(fmt.Sprintf("Streaming on %s", ac.Name))
|
||||
|
||||
case discord.CustomActivity:
|
||||
var status strings.Builder
|
||||
var segmts []text.Segment
|
||||
|
||||
if ac.Emoji != nil {
|
||||
if !ac.Emoji.ID.IsValid() {
|
||||
status.WriteString(ac.Emoji.Name)
|
||||
status.WriteByte(' ')
|
||||
} else {
|
||||
segmts = append(segmts, segments.EmojiSegment{
|
||||
Start: status.Len(),
|
||||
Name: ac.Emoji.Name,
|
||||
EmojiURL: ac.Emoji.EmojiURL() + "?size=64",
|
||||
Large: ac.State == "",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
status.WriteString(ac.State)
|
||||
|
||||
return text.Rich{
|
||||
Content: status.String(),
|
||||
Segments: segmts,
|
||||
}
|
||||
|
||||
default:
|
||||
return text.Rich{}
|
||||
}
|
||||
}
|
||||
|
||||
type ListSection struct {
|
||||
// constant states
|
||||
listID string
|
||||
|
|
2
go.mod
2
go.mod
|
@ -5,7 +5,7 @@ go 1.14
|
|||
require (
|
||||
github.com/diamondburned/arikawa v1.1.6
|
||||
github.com/diamondburned/cchat v0.0.48
|
||||
github.com/diamondburned/ningen v0.1.1-0.20200815214034-638820c48066
|
||||
github.com/diamondburned/ningen v0.1.1-0.20200816192443-0b6a02d498d2
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/go-test/deep v1.0.6
|
||||
github.com/pkg/errors v0.9.1
|
||||
|
|
8
go.sum
8
go.sum
|
@ -101,6 +101,14 @@ github.com/diamondburned/ningen v0.1.1-0.20200815192814-e630cb3debe2 h1:9GZLZhVl
|
|||
github.com/diamondburned/ningen v0.1.1-0.20200815192814-e630cb3debe2/go.mod h1:PIsJWdDhjgN9OiR+qrDPD8KGQ8UyFuRVrgs3Ewu6a3c=
|
||||
github.com/diamondburned/ningen v0.1.1-0.20200815214034-638820c48066 h1:TOLTl0zLJ+idYB7i6C4oGfmVF1Y0AFoNfVQWU3hmc4A=
|
||||
github.com/diamondburned/ningen v0.1.1-0.20200815214034-638820c48066/go.mod h1:PIsJWdDhjgN9OiR+qrDPD8KGQ8UyFuRVrgs3Ewu6a3c=
|
||||
github.com/diamondburned/ningen v0.1.1-0.20200816035718-70361fb41b6f h1:Wzns8I0VjMrcsH7N5lqHKTRkgBt2aCwx+7wAxvSFGjU=
|
||||
github.com/diamondburned/ningen v0.1.1-0.20200816035718-70361fb41b6f/go.mod h1:PIsJWdDhjgN9OiR+qrDPD8KGQ8UyFuRVrgs3Ewu6a3c=
|
||||
github.com/diamondburned/ningen v0.1.1-0.20200816040753-9595e584e6bc h1:pxFVcg7J7D9hYVVReXLdJurgv4uAo1a6jNHcYpdeZsE=
|
||||
github.com/diamondburned/ningen v0.1.1-0.20200816040753-9595e584e6bc/go.mod h1:PIsJWdDhjgN9OiR+qrDPD8KGQ8UyFuRVrgs3Ewu6a3c=
|
||||
github.com/diamondburned/ningen v0.1.1-0.20200816040956-857988325ce0 h1:c3G8NjcS7JZvQvY549r993C0P1ltEf7sFpfRD21HFhk=
|
||||
github.com/diamondburned/ningen v0.1.1-0.20200816040956-857988325ce0/go.mod h1:PIsJWdDhjgN9OiR+qrDPD8KGQ8UyFuRVrgs3Ewu6a3c=
|
||||
github.com/diamondburned/ningen v0.1.1-0.20200816192443-0b6a02d498d2 h1:PodX8lfv7ZffUHUsaQUdIjZ50ONY8uCCXXUL7yzoSMQ=
|
||||
github.com/diamondburned/ningen v0.1.1-0.20200816192443-0b6a02d498d2/go.mod h1:PIsJWdDhjgN9OiR+qrDPD8KGQ8UyFuRVrgs3Ewu6a3c=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
|
|
|
@ -12,10 +12,10 @@ const (
|
|||
)
|
||||
|
||||
type EmojiSegment struct {
|
||||
start int
|
||||
name string
|
||||
emojiURL string
|
||||
large bool
|
||||
Start int
|
||||
Name string
|
||||
EmojiURL string
|
||||
Large bool
|
||||
}
|
||||
|
||||
var _ text.Imager = (*EmojiSegment)(nil)
|
||||
|
@ -23,10 +23,10 @@ var _ text.Imager = (*EmojiSegment)(nil)
|
|||
func (r *TextRenderer) emoji(n *md.Emoji, enter bool) ast.WalkStatus {
|
||||
if enter {
|
||||
r.append(EmojiSegment{
|
||||
start: r.buf.Len(),
|
||||
name: n.Name,
|
||||
large: n.Large,
|
||||
emojiURL: n.EmojiURL() + "&size=64",
|
||||
Start: r.buf.Len(),
|
||||
Name: n.Name,
|
||||
Large: n.Large,
|
||||
EmojiURL: n.EmojiURL() + "&size=64",
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -34,22 +34,22 @@ func (r *TextRenderer) emoji(n *md.Emoji, enter bool) ast.WalkStatus {
|
|||
}
|
||||
|
||||
func (e EmojiSegment) Bounds() (start, end int) {
|
||||
return e.start, e.start
|
||||
return e.Start, e.Start
|
||||
}
|
||||
|
||||
func (e EmojiSegment) Image() string {
|
||||
return e.emojiURL
|
||||
return e.EmojiURL
|
||||
}
|
||||
|
||||
// TODO: large emoji
|
||||
|
||||
func (e EmojiSegment) ImageSize() (w, h int) {
|
||||
if e.large {
|
||||
if e.Large {
|
||||
return LargeEmojiSize, LargeEmojiSize
|
||||
}
|
||||
return InlineEmojiSize, InlineEmojiSize
|
||||
}
|
||||
|
||||
func (e EmojiSegment) ImageText() string {
|
||||
return ":" + e.name + ":"
|
||||
return ":" + e.Name + ":"
|
||||
}
|
||||
|
|
|
@ -312,16 +312,6 @@ func formatSectionf(segment *text.Rich, content *bytes.Buffer, f string, argv ..
|
|||
segmentadd(segment, InlineSegment{start, end, text.AttrBold | text.AttrUnderline})
|
||||
}
|
||||
|
||||
func FormatActivity(ac discord.Activity) text.Rich {
|
||||
var rich text.Rich
|
||||
var cbuf bytes.Buffer
|
||||
|
||||
formatActivity(&rich, &cbuf, ac)
|
||||
|
||||
rich.Content = cbuf.String()
|
||||
return rich
|
||||
}
|
||||
|
||||
func formatActivity(segment *text.Rich, content *bytes.Buffer, ac discord.Activity) {
|
||||
switch ac.Type {
|
||||
case discord.GameActivity:
|
||||
|
@ -345,10 +335,10 @@ func formatActivity(segment *text.Rich, content *bytes.Buffer, ac discord.Activi
|
|||
content.WriteString(ac.Emoji.Name)
|
||||
} else {
|
||||
segmentadd(segment, EmojiSegment{
|
||||
start: content.Len(),
|
||||
name: ac.Emoji.Name,
|
||||
emojiURL: ac.Emoji.EmojiURL() + "&size=64",
|
||||
large: ac.State == "",
|
||||
Start: content.Len(),
|
||||
Name: ac.Emoji.Name,
|
||||
EmojiURL: ac.Emoji.EmojiURL() + "&size=64",
|
||||
Large: ac.State == "",
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue