1
0
Fork 0
mirror of https://github.com/diamondburned/cchat-discord.git synced 2025-01-09 20:46:49 +00:00

Added completion; bumped cchat to v0.0.35

This commit is contained in:
diamondburned (Forefront) 2020-06-28 19:31:32 -07:00
parent 0abc14f5e5
commit 0a66a65fcf
5 changed files with 267 additions and 18 deletions

View file

@ -63,13 +63,13 @@ type Channel struct {
}
var (
_ cchat.Server = (*Channel)(nil)
_ cchat.ServerMessage = (*Channel)(nil)
_ cchat.ServerMessageSender = (*Channel)(nil)
// _ cchat.ServerMessageSendCompleter = (*Channel)(nil)
_ cchat.ServerNickname = (*Channel)(nil)
_ cchat.ServerMessageEditor = (*Channel)(nil)
_ cchat.ServerMessageActioner = (*Channel)(nil)
_ cchat.Server = (*Channel)(nil)
_ cchat.ServerMessage = (*Channel)(nil)
_ cchat.ServerMessageSender = (*Channel)(nil)
_ cchat.ServerMessageSendCompleter = (*Channel)(nil)
_ cchat.ServerNickname = (*Channel)(nil)
_ cchat.ServerMessageEditor = (*Channel)(nil)
_ cchat.ServerMessageActioner = (*Channel)(nil)
)
func NewChannel(s *Session, ch discord.Channel) *Channel {
@ -80,12 +80,29 @@ func NewChannel(s *Session, ch discord.Channel) *Channel {
}
}
// self does not do IO.
func (ch *Channel) self() (*discord.Channel, error) {
return ch.session.Store.Channel(ch.id)
}
// messages does not do IO.
func (ch *Channel) messages() ([]discord.Message, error) {
return ch.session.Store.Messages(ch.id)
}
func (ch *Channel) guild() (*discord.Guild, error) {
if ch.guildID.Valid() {
return ch.session.Guild(ch.guildID)
}
return nil, errors.New("channel not in a guild")
}
func (ch *Channel) ID() string {
return ch.id.String()
}
func (ch *Channel) Name() text.Rich {
c, err := ch.session.Store.Channel(ch.id)
c, err := ch.self()
if err != nil {
return text.Rich{Content: ch.id.String()}
}
@ -159,13 +176,13 @@ func (ch *Channel) JoinServer(ctx context.Context, ct cchat.MessagesContainer) (
return
}
m, err := ch.session.Store.Messages(ch.id)
m, err := ch.messages()
if err != nil {
// TODO: log
return
}
g, err := ch.session.Store.Guild(c.GuildID)
g, err := ch.guild()
if err != nil {
return
}
@ -226,6 +243,23 @@ func (ch *Channel) SendMessage(msg cchat.SendableMessage) error {
return err
}
// MessageEditable returns true if the given message ID belongs to the current
// user.
func (ch *Channel) MessageEditable(id string) bool {
s, err := discord.ParseSnowflake(id)
if err != nil {
return false
}
m, err := ch.session.Store.Message(ch.id, s)
if err != nil {
return false
}
return m.Author.ID == ch.session.userID
}
// RawMessageContent returns the raw message content from Discord.
func (ch *Channel) RawMessageContent(id string) (string, error) {
s, err := discord.ParseSnowflake(id)
if err != nil {
@ -240,6 +274,7 @@ func (ch *Channel) RawMessageContent(id string) (string, error) {
return m.Content, nil
}
// EditMessage edits the message to the given content string.
func (ch *Channel) EditMessage(id, content string) error {
s, err := discord.ParseSnowflake(id)
if err != nil {
@ -314,12 +349,12 @@ func (ch *Channel) canManageMessages(userID discord.Snowflake) bool {
// We need the guild, member and channel to calculate the permission
// overrides.
g, err := ch.session.Store.Guild(ch.guildID)
g, err := ch.guild()
if err != nil {
return false
}
c, err := ch.session.Store.Channel(ch.id)
c, err := ch.self()
if err != nil {
return false
}
@ -335,6 +370,25 @@ func (ch *Channel) canManageMessages(userID discord.Snowflake) bool {
return p.Has(discord.PermissionManageMessages)
}
func (ch *Channel) CompleteMessage(words []string, i int) (entries []cchat.CompletionEntry) {
var word = words[i]
// Word should have at least a character for the char check.
if len(word) < 1 {
return
}
switch word[0] {
case '@':
return ch.completeMentions(word[1:])
case '#':
return ch.completeChannels(word[1:])
case ':':
return ch.completeEmojis(word[1:])
}
return
}
func newCancels() func(...func()) []func() {
var cancels []func()
return func(appended ...func()) []func() {

189
channel_completion.go Normal file
View file

@ -0,0 +1,189 @@
package discord
import (
"strings"
"github.com/diamondburned/arikawa/discord"
"github.com/diamondburned/arikawa/state"
"github.com/diamondburned/cchat"
"github.com/diamondburned/cchat/text"
)
const MaxCompletion = 15
func completionUserEntry(s state.Store, u discord.User, g *discord.Guild) cchat.CompletionEntry {
if g != nil {
m, err := s.Member(g.ID, u.ID)
if err == nil {
return cchat.CompletionEntry{
Raw: u.Mention(),
Text: RenderMemberName(*m, *g),
IconURL: u.AvatarURL(),
}
}
}
return cchat.CompletionEntry{
Raw: u.Mention(),
Text: text.Rich{Content: u.Username},
IconURL: u.AvatarURL(),
}
}
func (ch *Channel) completeMentions(word string) (entries []cchat.CompletionEntry) {
// If there is no input, then we should grab the latest messages.
if word == "" {
msgs, _ := ch.messages()
g, _ := ch.guild() // nil is fine
// Keep track of the number of authors.
// TODO: fix excess allocations
var authors = make(map[discord.Snowflake]struct{}, MaxCompletion)
for _, msg := range msgs {
// If we've already added the author into the list, then skip.
if _, ok := authors[msg.Author.ID]; ok {
continue
}
// Record the current author and add the entry to the list.
authors[msg.Author.ID] = struct{}{}
entries = append(entries, completionUserEntry(ch.session.Store, msg.Author, g))
if len(entries) >= MaxCompletion {
return
}
}
return
}
// Lower-case everything for a case-insensitive match. startsWith() should
// do the rest.
var match = strings.ToLower(word)
// If we're not in a guild, then we can check the list of recipients.
if !ch.guildID.Valid() {
c, err := ch.self()
if err == nil {
for _, u := range c.DMRecipients {
if startsWith(match, u.Username) {
entries = append(entries, cchat.CompletionEntry{
Raw: u.Mention(),
Text: text.Rich{Content: u.Username},
IconURL: u.AvatarURL(),
})
if len(entries) >= MaxCompletion {
return
}
}
}
}
return
}
// If we're in a guild, then we should search for (all) members.
m, merr := ch.session.Store.Members(ch.guildID)
g, gerr := ch.guild()
if merr != nil || gerr != nil {
return
}
// If we couldn't find any members, then we can request Discord to
// search for them.
if len(m) == 0 {
ch.session.Members.SearchMember(ch.guildID, word)
return
}
for _, mem := range m {
if startsWith(match, mem.User.Username, mem.Nick) {
entries = append(entries, cchat.CompletionEntry{
Raw: mem.User.Mention(),
Text: RenderMemberName(mem, *g),
IconURL: mem.User.AvatarURL(),
})
if len(entries) >= MaxCompletion {
return
}
}
}
return
}
func (ch *Channel) completeChannels(word string) (entries []cchat.CompletionEntry) {
// Ignore if empty word.
if word == "" {
return
}
// Ignore if we're not in a guild.
if !ch.guildID.Valid() {
return
}
c, err := ch.session.State.Channels(ch.guildID)
if err != nil {
return
}
var match = strings.ToLower(word)
for _, ch := range c {
if startsWith(match, ch.Name) {
entries = append(entries, cchat.CompletionEntry{
Raw: ch.Mention(),
Text: text.Rich{Content: "#" + ch.Name},
})
if len(entries) >= MaxCompletion {
return
}
}
}
return
}
func (ch *Channel) completeEmojis(word string) (entries []cchat.CompletionEntry) {
// Ignore if empty word.
if word == "" {
return
}
e, err := ch.session.Emoji.Get(ch.guildID)
if err != nil {
return
}
var match = strings.ToLower(word)
for _, guild := range e {
for _, emoji := range guild.Emojis {
if startsWith(match, emoji.Name) {
entries = append(entries, cchat.CompletionEntry{
Raw: emoji.String(),
Text: text.Rich{Content: ":" + emoji.Name + ":"},
IconURL: emoji.EmojiURL() + "?size=32", // small
})
if len(entries) >= MaxCompletion {
return
}
}
}
}
return
}
func startsWith(contains string, strs ...string) bool {
for _, str := range strs {
if strings.HasPrefix(strings.ToLower(str), contains) {
return true
}
}
return false
}

2
go.mod
View file

@ -4,7 +4,7 @@ go 1.14
require (
github.com/diamondburned/arikawa v0.9.5
github.com/diamondburned/cchat v0.0.34
github.com/diamondburned/cchat v0.0.35
github.com/diamondburned/ningen v0.1.1-0.20200621014632-6babb812b249
github.com/go-test/deep v1.0.6
github.com/pkg/errors v0.9.1

2
go.sum
View file

@ -27,6 +27,8 @@ github.com/diamondburned/arikawa v0.9.5 h1:P1ffsp+NHT22wWKYFVC8CdlGRLzPuUV9FcCBK
github.com/diamondburned/arikawa v0.9.5/go.mod h1:nIhVIatzTQhPUa7NB8w4koG1RF9gYbpAr8Fj8sKq660=
github.com/diamondburned/cchat v0.0.34 h1:BGiVxMRA9dmW3rLilIldBvjVan7eTTpaWCCfX9IKBYU=
github.com/diamondburned/cchat v0.0.34/go.mod h1:+zXktogE45A0om4fT6B/z6Ii7FXNafjxsNspI0rlhbU=
github.com/diamondburned/cchat v0.0.35 h1:WiMGl8BQJgbP9E4xRxgLGlqUsHpTcJgDKDt8/6a7lBk=
github.com/diamondburned/cchat v0.0.35/go.mod h1:+zXktogE45A0om4fT6B/z6Ii7FXNafjxsNspI0rlhbU=
github.com/diamondburned/ningen v0.1.1-0.20200621014632-6babb812b249 h1:yP7kJ+xCGpDz6XbcfACJcju4SH1XDPwlrvbofz3lP8I=
github.com/diamondburned/ningen v0.1.1-0.20200621014632-6babb812b249/go.mod h1:xW9hpBZsGi8KpAh10TyP+YQlYBo+Xc+2w4TR6N0951A=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=

View file

@ -66,6 +66,14 @@ func NewUser(u discord.User) Author {
}
func NewGuildMember(m discord.Member, g discord.Guild) Author {
return Author{
id: m.User.ID,
name: RenderMemberName(m, g),
avatar: m.User.AvatarURL(),
}
}
func RenderMemberName(m discord.Member, g discord.Guild) text.Rich {
var name = text.Rich{
Content: m.User.Username,
}
@ -82,11 +90,7 @@ func NewGuildMember(m discord.Member, g discord.Guild) Author {
}
}
return Author{
id: m.User.ID,
name: name,
avatar: m.User.AvatarURL(),
}
return name
}
func (a Author) ID() string {