You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

338 lines
8.3 KiB
Lua

--[=[
@c TextChannel x Channel
@t abc
@d Defines the base methods and properties for all Discord text channels.
]=]
local pathjoin = require('pathjoin')
local Channel = require('containers/abstract/Channel')
local Message = require('containers/Message')
local WeakCache = require('iterables/WeakCache')
local SecondaryCache = require('iterables/SecondaryCache')
local Resolver = require('client/Resolver')
local fs = require('fs')
local splitPath = pathjoin.splitPath
local insert, remove, concat = table.insert, table.remove, table.concat
local format = string.format
local readFileSync = fs.readFileSync
local TextChannel, get = require('class')('TextChannel', Channel)
function TextChannel:__init(data, parent)
Channel.__init(self, data, parent)
self._messages = WeakCache({}, Message, self)
end
--[=[
@m getMessage
@t http
@p id Message-ID-Resolvable
@r Message
@d Gets a message object by ID. If the object is already cached, then the cached
object will be returned; otherwise, an HTTP request is made.
]=]
function TextChannel:getMessage(id)
id = Resolver.messageId(id)
local message = self._messages:get(id)
if message then
return message
else
local data, err = self.client._api:getChannelMessage(self._id, id)
if data then
return self._messages:_insert(data)
else
return nil, err
end
end
end
--[=[
@m getFirstMessage
@t http
@r Message
@d Returns the first message found in the channel, if any exist. This is not a
cache shortcut; an HTTP request is made each time this method is called.
]=]
function TextChannel:getFirstMessage()
local data, err = self.client._api:getChannelMessages(self._id, {after = self._id, limit = 1})
if data then
if data[1] then
return self._messages:_insert(data[1])
else
return nil, 'Channel has no messages'
end
else
return nil, err
end
end
--[=[
@m getLastMessage
@t http
@r Message
@d Returns the last message found in the channel, if any exist. This is not a
cache shortcut; an HTTP request is made each time this method is called.
]=]
function TextChannel:getLastMessage()
local data, err = self.client._api:getChannelMessages(self._id, {limit = 1})
if data then
if data[1] then
return self._messages:_insert(data[1])
else
return nil, 'Channel has no messages'
end
else
return nil, err
end
end
local function getMessages(self, query)
local data, err = self.client._api:getChannelMessages(self._id, query)
if data then
return SecondaryCache(data, self._messages)
else
return nil, err
end
end
--[=[
@m getMessages
@t http
@op limit number
@r SecondaryCache
@d Returns a newly constructed cache of between 1 and 100 (default = 50) message
objects found in the channel. While the cache will never automatically gain or
lose objects, the objects that it contains may be updated by gateway events.
]=]
function TextChannel:getMessages(limit)
return getMessages(self, limit and {limit = limit})
end
--[=[
@m getMessagesAfter
@t http
@p id Message-ID-Resolvable
@op limit number
@r SecondaryCache
@d Returns a newly constructed cache of between 1 and 100 (default = 50) message
objects found in the channel after a specific id. While the cache will never
automatically gain or lose objects, the objects that it contains may be updated
by gateway events.
]=]
function TextChannel:getMessagesAfter(id, limit)
id = Resolver.messageId(id)
return getMessages(self, {after = id, limit = limit})
end
--[=[
@m getMessagesBefore
@t http
@p id Message-ID-Resolvable
@op limit number
@r SecondaryCache
@d Returns a newly constructed cache of between 1 and 100 (default = 50) message
objects found in the channel before a specific id. While the cache will never
automatically gain or lose objects, the objects that it contains may be updated
by gateway events.
]=]
function TextChannel:getMessagesBefore(id, limit)
id = Resolver.messageId(id)
return getMessages(self, {before = id, limit = limit})
end
--[=[
@m getMessagesAround
@t http
@p id Message-ID-Resolvable
@op limit number
@r SecondaryCache
@d Returns a newly constructed cache of between 1 and 100 (default = 50) message
objects found in the channel around a specific point. While the cache will never
automatically gain or lose objects, the objects that it contains may be updated
by gateway events.
]=]
function TextChannel:getMessagesAround(id, limit)
id = Resolver.messageId(id)
return getMessages(self, {around = id, limit = limit})
end
--[=[
@m getPinnedMessages
@t http
@r SecondaryCache
@d Returns a newly constructed cache of up to 50 messages that are pinned in the
channel. While the cache will never automatically gain or lose objects, the
objects that it contains may be updated by gateway events.
]=]
function TextChannel:getPinnedMessages()
local data, err = self.client._api:getPinnedMessages(self._id)
if data then
return SecondaryCache(data, self._messages)
else
return nil, err
end
end
--[=[
@m broadcastTyping
@t http
@r boolean
@d Indicates in the channel that the client's user "is typing".
]=]
function TextChannel:broadcastTyping()
local data, err = self.client._api:triggerTypingIndicator(self._id)
if data then
return true
else
return false, err
end
end
local function parseFile(obj, files)
if type(obj) == 'string' then
local data, err = readFileSync(obj)
if not data then
return nil, err
end
files = files or {}
insert(files, {remove(splitPath(obj)), data})
elseif type(obj) == 'table' and type(obj[1]) == 'string' and type(obj[2]) == 'string' then
files = files or {}
insert(files, obj)
else
return nil, 'Invalid file object: ' .. tostring(obj)
end
return files
end
local function parseMention(obj, mentions)
if type(obj) == 'table' and obj.mentionString then
mentions = mentions or {}
insert(mentions, obj.mentionString)
else
return nil, 'Unmentionable object: ' .. tostring(obj)
end
return mentions
end
--[=[
@m send
@t http
@p content string/table
@r Message
@d Sends a message to the channel. If `content` is a string, then this is simply
sent as the message content. If it is a table, more advanced formatting is
allowed. See [[managing messages]] for more information.
]=]
function TextChannel:send(content)
local data, err
if type(content) == 'table' then
local tbl = content
content = tbl.content
if type(tbl.code) == 'string' then
content = format('```%s\n%s\n```', tbl.code, content)
elseif tbl.code == true then
content = format('```\n%s\n```', content)
end
local mentions
if tbl.mention then
mentions, err = parseMention(tbl.mention)
if err then
return nil, err
end
end
if type(tbl.mentions) == 'table' then
for _, mention in ipairs(tbl.mentions) do
mentions, err = parseMention(mention, mentions)
if err then
return nil, err
end
end
end
if mentions then
insert(mentions, content)
content = concat(mentions, ' ')
end
local files
if tbl.file then
files, err = parseFile(tbl.file)
if err then
return nil, err
end
end
if type(tbl.files) == 'table' then
for _, file in ipairs(tbl.files) do
files, err = parseFile(file, files)
if err then
return nil, err
end
end
end
local refMessage, refMention
if tbl.reference then
refMessage = {message_id = Resolver.messageId(tbl.reference.message)}
refMention = {
parse = {'users', 'roles', 'everyone'},
replied_user = not not tbl.reference.mention,
}
end
data, err = self.client._api:createMessage(self._id, {
content = content,
tts = tbl.tts,
nonce = tbl.nonce,
embed = tbl.embed,
message_reference = refMessage,
allowed_mentions = refMention,
}, files)
else
data, err = self.client._api:createMessage(self._id, {content = content})
end
if data then
return self._messages:_insert(data)
else
return nil, err
end
end
--[=[
@m sendf
@t http
@p content string
@p ... *
@r Message
@d Sends a message to the channel with content formatted with `...` via `string.format`
]=]
function TextChannel:sendf(content, ...)
local data, err = self.client._api:createMessage(self._id, {content = format(content, ...)})
if data then
return self._messages:_insert(data)
else
return nil, err
end
end
--[=[@p messages WeakCache An iterable weak cache of all messages that are
visible to the client. Messages that are not referenced elsewhere are eventually
garbage collected. To access a message that may exist but is not cached,
use `TextChannel:getMessage`.]=]
function get.messages(self)
return self._messages
end
return TextChannel