mirror of
https://github.com/diamondburned/cchat-discord.git
synced 2025-07-12 20:15:36 +00:00
Added completion; bumped cchat to v0.0.35
This commit is contained in:
parent
0abc14f5e5
commit
0a66a65fcf
78
channel.go
78
channel.go
|
@ -63,13 +63,13 @@ type Channel struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ cchat.Server = (*Channel)(nil)
|
_ cchat.Server = (*Channel)(nil)
|
||||||
_ cchat.ServerMessage = (*Channel)(nil)
|
_ cchat.ServerMessage = (*Channel)(nil)
|
||||||
_ cchat.ServerMessageSender = (*Channel)(nil)
|
_ cchat.ServerMessageSender = (*Channel)(nil)
|
||||||
// _ cchat.ServerMessageSendCompleter = (*Channel)(nil)
|
_ cchat.ServerMessageSendCompleter = (*Channel)(nil)
|
||||||
_ cchat.ServerNickname = (*Channel)(nil)
|
_ cchat.ServerNickname = (*Channel)(nil)
|
||||||
_ cchat.ServerMessageEditor = (*Channel)(nil)
|
_ cchat.ServerMessageEditor = (*Channel)(nil)
|
||||||
_ cchat.ServerMessageActioner = (*Channel)(nil)
|
_ cchat.ServerMessageActioner = (*Channel)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewChannel(s *Session, ch discord.Channel) *Channel {
|
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 {
|
func (ch *Channel) ID() string {
|
||||||
return ch.id.String()
|
return ch.id.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch *Channel) Name() text.Rich {
|
func (ch *Channel) Name() text.Rich {
|
||||||
c, err := ch.session.Store.Channel(ch.id)
|
c, err := ch.self()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return text.Rich{Content: ch.id.String()}
|
return text.Rich{Content: ch.id.String()}
|
||||||
}
|
}
|
||||||
|
@ -159,13 +176,13 @@ func (ch *Channel) JoinServer(ctx context.Context, ct cchat.MessagesContainer) (
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
m, err := ch.session.Store.Messages(ch.id)
|
m, err := ch.messages()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: log
|
// TODO: log
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
g, err := ch.session.Store.Guild(c.GuildID)
|
g, err := ch.guild()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -226,6 +243,23 @@ func (ch *Channel) SendMessage(msg cchat.SendableMessage) error {
|
||||||
return err
|
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) {
|
func (ch *Channel) RawMessageContent(id string) (string, error) {
|
||||||
s, err := discord.ParseSnowflake(id)
|
s, err := discord.ParseSnowflake(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -240,6 +274,7 @@ func (ch *Channel) RawMessageContent(id string) (string, error) {
|
||||||
return m.Content, nil
|
return m.Content, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EditMessage edits the message to the given content string.
|
||||||
func (ch *Channel) EditMessage(id, content string) error {
|
func (ch *Channel) EditMessage(id, content string) error {
|
||||||
s, err := discord.ParseSnowflake(id)
|
s, err := discord.ParseSnowflake(id)
|
||||||
if err != nil {
|
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
|
// We need the guild, member and channel to calculate the permission
|
||||||
// overrides.
|
// overrides.
|
||||||
|
|
||||||
g, err := ch.session.Store.Guild(ch.guildID)
|
g, err := ch.guild()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := ch.session.Store.Channel(ch.id)
|
c, err := ch.self()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -335,6 +370,25 @@ func (ch *Channel) canManageMessages(userID discord.Snowflake) bool {
|
||||||
return p.Has(discord.PermissionManageMessages)
|
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() {
|
func newCancels() func(...func()) []func() {
|
||||||
var cancels []func()
|
var cancels []func()
|
||||||
return func(appended ...func()) []func() {
|
return func(appended ...func()) []func() {
|
||||||
|
|
189
channel_completion.go
Normal file
189
channel_completion.go
Normal 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
2
go.mod
|
@ -4,7 +4,7 @@ go 1.14
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/diamondburned/arikawa v0.9.5
|
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/diamondburned/ningen v0.1.1-0.20200621014632-6babb812b249
|
||||||
github.com/go-test/deep v1.0.6
|
github.com/go-test/deep v1.0.6
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -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/arikawa v0.9.5/go.mod h1:nIhVIatzTQhPUa7NB8w4koG1RF9gYbpAr8Fj8sKq660=
|
||||||
github.com/diamondburned/cchat v0.0.34 h1:BGiVxMRA9dmW3rLilIldBvjVan7eTTpaWCCfX9IKBYU=
|
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.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 h1:yP7kJ+xCGpDz6XbcfACJcju4SH1XDPwlrvbofz3lP8I=
|
||||||
github.com/diamondburned/ningen v0.1.1-0.20200621014632-6babb812b249/go.mod h1:xW9hpBZsGi8KpAh10TyP+YQlYBo+Xc+2w4TR6N0951A=
|
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=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
|
14
message.go
14
message.go
|
@ -66,6 +66,14 @@ func NewUser(u discord.User) Author {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGuildMember(m discord.Member, g discord.Guild) 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{
|
var name = text.Rich{
|
||||||
Content: m.User.Username,
|
Content: m.User.Username,
|
||||||
}
|
}
|
||||||
|
@ -82,11 +90,7 @@ func NewGuildMember(m discord.Member, g discord.Guild) Author {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Author{
|
return name
|
||||||
id: m.User.ID,
|
|
||||||
name: name,
|
|
||||||
avatar: m.User.AvatarURL(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a Author) ID() string {
|
func (a Author) ID() string {
|
||||||
|
|
Loading…
Reference in a new issue