2020-06-16 03:57:33 +00:00
|
|
|
package discord
|
|
|
|
|
|
|
|
import (
|
2020-06-30 03:14:44 +00:00
|
|
|
"net/url"
|
2020-07-01 18:27:05 +00:00
|
|
|
"strconv"
|
2020-06-16 03:57:33 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/diamondburned/arikawa/discord"
|
|
|
|
"github.com/diamondburned/arikawa/gateway"
|
|
|
|
"github.com/diamondburned/cchat"
|
|
|
|
"github.com/diamondburned/cchat-discord/segments"
|
|
|
|
"github.com/diamondburned/cchat/text"
|
|
|
|
)
|
|
|
|
|
|
|
|
type messageHeader struct {
|
|
|
|
id discord.Snowflake
|
|
|
|
time discord.Timestamp
|
|
|
|
channelID discord.Snowflake
|
|
|
|
guildID discord.Snowflake
|
|
|
|
nonce string
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ cchat.MessageHeader = (*messageHeader)(nil)
|
|
|
|
|
|
|
|
func newHeader(msg discord.Message) messageHeader {
|
|
|
|
var h = messageHeader{
|
|
|
|
id: msg.ID,
|
|
|
|
time: msg.Timestamp,
|
|
|
|
channelID: msg.ChannelID,
|
|
|
|
guildID: msg.GuildID,
|
|
|
|
nonce: msg.Nonce,
|
|
|
|
}
|
|
|
|
if msg.EditedTimestamp.Valid() {
|
|
|
|
h.time = msg.EditedTimestamp
|
|
|
|
}
|
|
|
|
return h
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewHeaderDelete(d *gateway.MessageDeleteEvent) messageHeader {
|
|
|
|
return messageHeader{
|
|
|
|
id: d.ID,
|
|
|
|
time: discord.Timestamp(time.Now()),
|
|
|
|
channelID: d.ChannelID,
|
|
|
|
guildID: d.GuildID,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m messageHeader) ID() string {
|
|
|
|
return m.id.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m messageHeader) Time() time.Time {
|
|
|
|
return m.time.Time()
|
|
|
|
}
|
|
|
|
|
2020-06-30 03:14:44 +00:00
|
|
|
// AvatarURL wraps the URL with URL queries for the avatar.
|
|
|
|
func AvatarURL(URL string) string {
|
2020-07-01 18:27:05 +00:00
|
|
|
return URLSized(URL, 64)
|
|
|
|
}
|
|
|
|
|
|
|
|
// URLSized wraps the URL with the size query.
|
|
|
|
func URLSized(URL string, size int) string {
|
2020-06-30 03:14:44 +00:00
|
|
|
u, err := url.Parse(URL)
|
|
|
|
if err != nil {
|
|
|
|
return URL
|
|
|
|
}
|
|
|
|
|
|
|
|
q := u.Query()
|
2020-07-01 18:27:05 +00:00
|
|
|
q.Set("size", strconv.Itoa(size))
|
2020-06-30 03:14:44 +00:00
|
|
|
u.RawQuery = q.Encode()
|
|
|
|
|
|
|
|
return u.String()
|
|
|
|
}
|
|
|
|
|
2020-06-16 03:57:33 +00:00
|
|
|
type Author struct {
|
|
|
|
id discord.Snowflake
|
|
|
|
name text.Rich
|
|
|
|
avatar string
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewUser(u discord.User) Author {
|
|
|
|
return Author{
|
|
|
|
id: u.ID,
|
|
|
|
name: text.Rich{Content: u.Username},
|
2020-06-30 03:14:44 +00:00
|
|
|
avatar: AvatarURL(u.AvatarURL()),
|
2020-06-16 03:57:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewGuildMember(m discord.Member, g discord.Guild) Author {
|
2020-06-29 02:31:32 +00:00
|
|
|
return Author{
|
|
|
|
id: m.User.ID,
|
|
|
|
name: RenderMemberName(m, g),
|
2020-06-30 03:14:44 +00:00
|
|
|
avatar: AvatarURL(m.User.AvatarURL()),
|
2020-06-29 02:31:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func RenderMemberName(m discord.Member, g discord.Guild) text.Rich {
|
2020-06-16 03:57:33 +00:00
|
|
|
var name = text.Rich{
|
|
|
|
Content: m.User.Username,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the nickname.
|
|
|
|
if m.Nick != "" {
|
|
|
|
name.Content = m.Nick
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the color.
|
|
|
|
if c := discord.MemberColor(g, m); c > 0 {
|
|
|
|
name.Segments = []text.Segment{
|
|
|
|
segments.NewColored(len(name.Content), c.Uint32()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-29 02:31:32 +00:00
|
|
|
return name
|
2020-06-16 03:57:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (a Author) ID() string {
|
|
|
|
return a.id.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a Author) Name() text.Rich {
|
|
|
|
return a.name
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a Author) Avatar() string {
|
|
|
|
return a.avatar
|
|
|
|
}
|
|
|
|
|
|
|
|
type Message struct {
|
|
|
|
messageHeader
|
|
|
|
|
|
|
|
author Author
|
|
|
|
content text.Rich
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
mentioned bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewMessageUpdateContent(msg discord.Message) Message {
|
|
|
|
return Message{
|
|
|
|
messageHeader: newHeader(msg),
|
|
|
|
content: text.Rich{Content: msg.Content},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewMessageUpdateAuthor(msg discord.Message, member discord.Member, g discord.Guild) Message {
|
|
|
|
return Message{
|
|
|
|
messageHeader: newHeader(msg),
|
|
|
|
author: NewGuildMember(member, g),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-17 07:20:46 +00:00
|
|
|
// NewMessageCreate uses the session to create a message. It does not do
|
2020-06-16 03:57:33 +00:00
|
|
|
// API calls. Member is optional.
|
2020-06-17 07:20:46 +00:00
|
|
|
func NewMessageCreate(c *gateway.MessageCreateEvent, s *Session) Message {
|
2020-06-16 03:57:33 +00:00
|
|
|
// This should not error.
|
2020-06-17 07:20:46 +00:00
|
|
|
g, err := s.Store.Guild(c.GuildID)
|
2020-06-16 03:57:33 +00:00
|
|
|
if err != nil {
|
2020-06-19 01:00:24 +00:00
|
|
|
return NewMessage(c.Message, s, NewUser(c.Author))
|
2020-06-16 03:57:33 +00:00
|
|
|
}
|
|
|
|
|
2020-06-17 07:20:46 +00:00
|
|
|
if c.Member == nil {
|
|
|
|
c.Member, _ = s.Store.Member(c.GuildID, c.Author.ID)
|
2020-06-16 03:57:33 +00:00
|
|
|
}
|
2020-06-17 07:20:46 +00:00
|
|
|
if c.Member == nil {
|
|
|
|
s.Members.RequestMember(c.GuildID, c.Author.ID)
|
2020-06-19 01:00:24 +00:00
|
|
|
return NewMessage(c.Message, s, NewUser(c.Author))
|
2020-06-16 03:57:33 +00:00
|
|
|
}
|
|
|
|
|
2020-06-19 01:00:24 +00:00
|
|
|
return NewMessage(c.Message, s, NewGuildMember(*c.Member, *g))
|
2020-06-16 03:57:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewBacklogMessage uses the session to create a message fetched from the
|
|
|
|
// backlog. It takes in an existing guild and tries to fetch a new member, if
|
|
|
|
// it's nil.
|
|
|
|
func NewBacklogMessage(m discord.Message, s *Session, g discord.Guild) Message {
|
|
|
|
// If the message doesn't have a guild, then we don't need all the
|
|
|
|
// complicated member fetching process.
|
|
|
|
if !m.GuildID.Valid() {
|
2020-06-19 01:00:24 +00:00
|
|
|
return NewMessage(m, s, NewUser(m.Author))
|
2020-06-16 03:57:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mem, err := s.Store.Member(m.GuildID, m.Author.ID)
|
|
|
|
if err != nil {
|
|
|
|
s.Members.RequestMember(m.GuildID, m.Author.ID)
|
2020-06-19 01:00:24 +00:00
|
|
|
return NewMessage(m, s, NewUser(m.Author))
|
2020-06-16 03:57:33 +00:00
|
|
|
}
|
|
|
|
|
2020-06-19 01:00:24 +00:00
|
|
|
return NewMessage(m, s, NewGuildMember(*mem, g))
|
2020-06-16 03:57:33 +00:00
|
|
|
}
|
|
|
|
|
2020-06-19 01:00:24 +00:00
|
|
|
func NewDirectMessage(m discord.Message, s *Session) Message {
|
|
|
|
return NewMessage(m, s, NewUser(m.Author))
|
2020-06-16 03:57:33 +00:00
|
|
|
}
|
|
|
|
|
2020-06-19 01:00:24 +00:00
|
|
|
func NewMessage(m discord.Message, s *Session, author Author) Message {
|
2020-06-16 03:57:33 +00:00
|
|
|
return Message{
|
|
|
|
messageHeader: newHeader(m),
|
|
|
|
author: author,
|
2020-06-19 01:00:24 +00:00
|
|
|
content: segments.ParseMessage(&m, s.Store),
|
2020-06-16 03:57:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m Message) Author() cchat.MessageAuthor {
|
|
|
|
return m.author
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m Message) Content() text.Rich {
|
|
|
|
return m.content
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m Message) Nonce() string {
|
|
|
|
return m.nonce
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m Message) Mentioned() bool {
|
|
|
|
return m.mentioned
|
|
|
|
}
|