feat: forums
closes MTQ-77
This commit is contained in:
parent
b2fccf4978
commit
00f30a6495
27 changed files with 1133 additions and 71 deletions
|
@ -100,6 +100,17 @@ import { _normalizeInputFile } from './methods/files/normalize-input-file'
|
||||||
import { _normalizeInputMedia } from './methods/files/normalize-input-media'
|
import { _normalizeInputMedia } from './methods/files/normalize-input-media'
|
||||||
import { uploadFile } from './methods/files/upload-file'
|
import { uploadFile } from './methods/files/upload-file'
|
||||||
import { uploadMedia } from './methods/files/upload-media'
|
import { uploadMedia } from './methods/files/upload-media'
|
||||||
|
import { createForumTopic } from './methods/forums/create-forum-topic'
|
||||||
|
import { deleteForumTopicHistory } from './methods/forums/delete-forum-topic-history'
|
||||||
|
import { editForumTopic } from './methods/forums/edit-forum-topic'
|
||||||
|
import { getForumTopics, GetForumTopicsOffset } from './methods/forums/get-forum-topics'
|
||||||
|
import { getForumTopicsById } from './methods/forums/get-forum-topics-by-id'
|
||||||
|
import { iterForumTopics } from './methods/forums/iter-forum-topics'
|
||||||
|
import { reorderPinnedForumTopics } from './methods/forums/reorder-pinned-forum-topics'
|
||||||
|
import { toggleForum } from './methods/forums/toggle-forum'
|
||||||
|
import { toggleForumTopicClosed } from './methods/forums/toggle-forum-topic-closed'
|
||||||
|
import { toggleForumTopicPinned } from './methods/forums/toggle-forum-topic-pinned'
|
||||||
|
import { toggleGeneralTopicHidden } from './methods/forums/toggle-general-topic-hidden'
|
||||||
import { createInviteLink } from './methods/invite-links/create-invite-link'
|
import { createInviteLink } from './methods/invite-links/create-invite-link'
|
||||||
import { editInviteLink } from './methods/invite-links/edit-invite-link'
|
import { editInviteLink } from './methods/invite-links/edit-invite-link'
|
||||||
import { exportInviteLink } from './methods/invite-links/export-invite-link'
|
import { exportInviteLink } from './methods/invite-links/export-invite-link'
|
||||||
|
@ -221,6 +232,7 @@ import {
|
||||||
Dialog,
|
Dialog,
|
||||||
FileDownloadParameters,
|
FileDownloadParameters,
|
||||||
FormattedString,
|
FormattedString,
|
||||||
|
ForumTopic,
|
||||||
GameHighScore,
|
GameHighScore,
|
||||||
HistoryReadUpdate,
|
HistoryReadUpdate,
|
||||||
IMessageEntityParser,
|
IMessageEntityParser,
|
||||||
|
@ -1045,11 +1057,19 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
/**
|
/**
|
||||||
* Create a new broadcast channel
|
* Create a new broadcast channel
|
||||||
*
|
*
|
||||||
* @param title Channel title
|
|
||||||
* @param description (default: `''`) Channel description
|
|
||||||
* @returns Newly created channel
|
* @returns Newly created channel
|
||||||
*/
|
*/
|
||||||
createChannel(title: string, description?: string): Promise<Chat>
|
createChannel(params: {
|
||||||
|
/**
|
||||||
|
* Channel title
|
||||||
|
*/
|
||||||
|
title: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Channel description
|
||||||
|
*/
|
||||||
|
description?: string
|
||||||
|
}): Promise<Chat>
|
||||||
/**
|
/**
|
||||||
* Create a legacy group chat
|
* Create a legacy group chat
|
||||||
*
|
*
|
||||||
|
@ -1065,10 +1085,31 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
/**
|
/**
|
||||||
* Create a new supergroup
|
* Create a new supergroup
|
||||||
*
|
*
|
||||||
* @param title Title of the supergroup
|
* @returns Newly created supergroup
|
||||||
* @param description (default: `''`) Description of the supergroup
|
|
||||||
*/
|
*/
|
||||||
createSupergroup(title: string, description?: string): Promise<Chat>
|
createSupergroup(params: {
|
||||||
|
/**
|
||||||
|
* Supergroup title
|
||||||
|
*/
|
||||||
|
title: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supergroup description
|
||||||
|
*/
|
||||||
|
description?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to create a forum
|
||||||
|
*/
|
||||||
|
forum?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TTL period (in seconds) for the newly created channel
|
||||||
|
*
|
||||||
|
* @default 0 (i.e. messages don't expire)
|
||||||
|
*/
|
||||||
|
ttlPeriod?: number
|
||||||
|
}): Promise<Chat>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a channel or a supergroup
|
* Delete a channel or a supergroup
|
||||||
|
@ -1916,6 +1957,193 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
progressCallback?: (uploaded: number, total: number) => void
|
progressCallback?: (uploaded: number, total: number) => void
|
||||||
},
|
},
|
||||||
): Promise<Extract<MessageMedia, Photo | RawDocument>>
|
): Promise<Extract<MessageMedia, Photo | RawDocument>>
|
||||||
|
/**
|
||||||
|
* Create a topic in a forum
|
||||||
|
*
|
||||||
|
* Only admins with `manageTopics` permission can do this.
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
* @returns Service message for the created topic
|
||||||
|
*/
|
||||||
|
createForumTopic(
|
||||||
|
chatId: InputPeerLike,
|
||||||
|
params: {
|
||||||
|
/**
|
||||||
|
* Topic title
|
||||||
|
*/
|
||||||
|
title: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Icon of the topic.
|
||||||
|
*
|
||||||
|
* Can be a number (color in RGB, see {@link ForumTopic} static members for allowed values)
|
||||||
|
* or a custom emoji ID.
|
||||||
|
*
|
||||||
|
* Icon color can't be changed after the topic is created.
|
||||||
|
*/
|
||||||
|
icon?: number | tl.Long
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send as a specific channel
|
||||||
|
*/
|
||||||
|
sendAs?: InputPeerLike
|
||||||
|
},
|
||||||
|
): Promise<Message>
|
||||||
|
/**
|
||||||
|
* Delete a forum topic and all its history
|
||||||
|
*
|
||||||
|
* @param chat Chat or user ID, username, phone number, `"me"` or `"self"`
|
||||||
|
* @param topicId ID of the topic (i.e. its top message ID)
|
||||||
|
*/
|
||||||
|
deleteForumTopicHistory(chat: InputPeerLike, topicId: number): Promise<void>
|
||||||
|
/**
|
||||||
|
* Modify a topic in a forum
|
||||||
|
*
|
||||||
|
* Only admins with `manageTopics` permission can do this.
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
* @param topicId ID of the topic (i.e. its top message ID)
|
||||||
|
* @returns Service message about the modification
|
||||||
|
*/
|
||||||
|
editForumTopic(
|
||||||
|
chatId: InputPeerLike,
|
||||||
|
topicId: number,
|
||||||
|
params: {
|
||||||
|
/**
|
||||||
|
* New topic title
|
||||||
|
*/
|
||||||
|
title?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New icon of the topic.
|
||||||
|
*
|
||||||
|
* Can be a custom emoji ID, or `null` to remove the icon
|
||||||
|
* and use static color instead
|
||||||
|
*/
|
||||||
|
icon?: tl.Long | null
|
||||||
|
},
|
||||||
|
): Promise<Message>
|
||||||
|
/**
|
||||||
|
* Get a single forum topic by its ID
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
*/
|
||||||
|
getForumTopicsById(chatId: InputPeerLike, ids: number): Promise<ForumTopic>
|
||||||
|
/**
|
||||||
|
* Get forum topics by their IDs
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
*/
|
||||||
|
getForumTopicsById(chatId: InputPeerLike, ids: number[]): Promise<ForumTopic[]>
|
||||||
|
/**
|
||||||
|
* Get forum topics
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
*/
|
||||||
|
getForumTopics(
|
||||||
|
chatId: InputPeerLike,
|
||||||
|
params?: {
|
||||||
|
/**
|
||||||
|
* Search query
|
||||||
|
*/
|
||||||
|
query?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset for pagination
|
||||||
|
*/
|
||||||
|
offset?: GetForumTopicsOffset
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of topics to return.
|
||||||
|
*
|
||||||
|
* @default 100
|
||||||
|
*/
|
||||||
|
limit?: number
|
||||||
|
},
|
||||||
|
): Promise<ArrayPaginated<ForumTopic, GetForumTopicsOffset>>
|
||||||
|
/**
|
||||||
|
* Iterate over forum topics. Wrapper over {@link getForumTopics}.
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
*/
|
||||||
|
iterForumTopics(
|
||||||
|
chatId: InputPeerLike,
|
||||||
|
params?: Parameters<TelegramClient['getForumTopics']>[1] & {
|
||||||
|
/**
|
||||||
|
* Maximum number of topics to return.
|
||||||
|
*
|
||||||
|
* @default `Infinity`, i.e. return all topics
|
||||||
|
*/
|
||||||
|
limit?: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chunk size. Usually you shouldn't care about this.
|
||||||
|
*/
|
||||||
|
chunkSize?: number
|
||||||
|
},
|
||||||
|
): AsyncIterableIterator<ForumTopic>
|
||||||
|
/**
|
||||||
|
* Reorder pinned forum topics
|
||||||
|
*
|
||||||
|
* Only admins with `manageTopics` permission can do this.
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
* @param topicId ID of the topic (i.e. its top message ID)
|
||||||
|
*/
|
||||||
|
reorderPinnedForumTopics(
|
||||||
|
chatId: InputPeerLike,
|
||||||
|
params: {
|
||||||
|
/**
|
||||||
|
* Order of the pinned topics
|
||||||
|
*/
|
||||||
|
order: number[]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to un-pin topics not present in the order
|
||||||
|
*/
|
||||||
|
force?: boolean
|
||||||
|
},
|
||||||
|
): Promise<void>
|
||||||
|
/**
|
||||||
|
* Toggle open/close status of a topic in a forum
|
||||||
|
*
|
||||||
|
* Only admins with `manageTopics` permission can do this.
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
* @param topicId ID of the topic (i.e. its top message ID)
|
||||||
|
* @param closed Whether the topic should be closed
|
||||||
|
* @returns Service message about the modification
|
||||||
|
*/
|
||||||
|
toggleForumTopicClosed(chatId: InputPeerLike, topicId: number, closed: boolean): Promise<Message>
|
||||||
|
/**
|
||||||
|
* Toggle whether a topic in a forum is pinned
|
||||||
|
*
|
||||||
|
* Only admins with `manageTopics` permission can do this.
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
* @param topicId ID of the topic (i.e. its top message ID)
|
||||||
|
* @param pinned Whether the topic should be pinned
|
||||||
|
*/
|
||||||
|
toggleForumTopicPinned(chatId: InputPeerLike, topicId: number, pinned: boolean): Promise<void>
|
||||||
|
/**
|
||||||
|
* Set whether a supergroup is a forum.
|
||||||
|
*
|
||||||
|
* Only owner of the supergroup can change this setting.
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
* @param enabled (default: `false`) Whether the supergroup should be a forum
|
||||||
|
*/
|
||||||
|
toggleForum(chatId: InputPeerLike, enabled?: boolean): Promise<void>
|
||||||
|
/**
|
||||||
|
* Toggle whether "General" topic in a forum is hidden or not
|
||||||
|
*
|
||||||
|
* Only admins with `manageTopics` permission can do this.
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
* @param hidden Whether the topic should be hidden
|
||||||
|
* @returns Service message about the modification
|
||||||
|
*/
|
||||||
|
toggleGeneralTopicHidden(chatId: InputPeerLike, hidden: boolean): Promise<Message>
|
||||||
/**
|
/**
|
||||||
* Create an additional invite link for the chat.
|
* Create an additional invite link for the chat.
|
||||||
*
|
*
|
||||||
|
@ -3061,6 +3289,8 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message to reply to. Either a message object or message ID.
|
* Message to reply to. Either a message object or message ID.
|
||||||
|
*
|
||||||
|
* For forums - can also be an ID of the topic (i.e. its top message ID)
|
||||||
*/
|
*/
|
||||||
replyTo?: number | Message
|
replyTo?: number | Message
|
||||||
|
|
||||||
|
@ -3116,6 +3346,8 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
params?: {
|
params?: {
|
||||||
/**
|
/**
|
||||||
* Message to reply to. Either a message object or message ID.
|
* Message to reply to. Either a message object or message ID.
|
||||||
|
*
|
||||||
|
* For forums - can also be an ID of the topic (i.e. its top message ID)
|
||||||
*/
|
*/
|
||||||
replyTo?: number | Message
|
replyTo?: number | Message
|
||||||
|
|
||||||
|
@ -3226,6 +3458,8 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message to reply to. Either a message object or message ID.
|
* Message to reply to. Either a message object or message ID.
|
||||||
|
*
|
||||||
|
* For forums - can also be an ID of the topic (i.e. its top message ID)
|
||||||
*/
|
*/
|
||||||
replyTo?: number | Message
|
replyTo?: number | Message
|
||||||
|
|
||||||
|
@ -3353,6 +3587,8 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
params?: {
|
params?: {
|
||||||
/**
|
/**
|
||||||
* Message to reply to. Either a message object or message ID.
|
* Message to reply to. Either a message object or message ID.
|
||||||
|
*
|
||||||
|
* For forums - can also be an ID of the topic (i.e. its top message ID)
|
||||||
*/
|
*/
|
||||||
replyTo?: number | Message
|
replyTo?: number | Message
|
||||||
|
|
||||||
|
@ -3509,7 +3745,15 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
*
|
*
|
||||||
* @param chatId Chat or user ID
|
* @param chatId Chat or user ID
|
||||||
*/
|
*/
|
||||||
unpinAllMessages(chatId: InputPeerLike): Promise<void>
|
unpinAllMessages(
|
||||||
|
chatId: InputPeerLike,
|
||||||
|
params?: {
|
||||||
|
/**
|
||||||
|
* For forums - unpin only messages from the given topic
|
||||||
|
*/
|
||||||
|
topicId?: number
|
||||||
|
},
|
||||||
|
): Promise<void>
|
||||||
/**
|
/**
|
||||||
* Unpin a message in a group, supergroup, channel or PM.
|
* Unpin a message in a group, supergroup, channel or PM.
|
||||||
*
|
*
|
||||||
|
@ -4195,6 +4439,17 @@ export class TelegramClient extends BaseTelegramClient {
|
||||||
_normalizeInputMedia = _normalizeInputMedia
|
_normalizeInputMedia = _normalizeInputMedia
|
||||||
uploadFile = uploadFile
|
uploadFile = uploadFile
|
||||||
uploadMedia = uploadMedia
|
uploadMedia = uploadMedia
|
||||||
|
createForumTopic = createForumTopic
|
||||||
|
deleteForumTopicHistory = deleteForumTopicHistory
|
||||||
|
editForumTopic = editForumTopic
|
||||||
|
getForumTopicsById = getForumTopicsById
|
||||||
|
getForumTopics = getForumTopics
|
||||||
|
iterForumTopics = iterForumTopics
|
||||||
|
reorderPinnedForumTopics = reorderPinnedForumTopics
|
||||||
|
toggleForumTopicClosed = toggleForumTopicClosed
|
||||||
|
toggleForumTopicPinned = toggleForumTopicPinned
|
||||||
|
toggleForum = toggleForum
|
||||||
|
toggleGeneralTopicHidden = toggleGeneralTopicHidden
|
||||||
createInviteLink = createInviteLink
|
createInviteLink = createInviteLink
|
||||||
editInviteLink = editInviteLink
|
editInviteLink = editInviteLink
|
||||||
exportInviteLink = exportInviteLink
|
exportInviteLink = exportInviteLink
|
||||||
|
|
|
@ -31,6 +31,7 @@ import {
|
||||||
Dialog,
|
Dialog,
|
||||||
FileDownloadParameters,
|
FileDownloadParameters,
|
||||||
FormattedString,
|
FormattedString,
|
||||||
|
ForumTopic,
|
||||||
GameHighScore,
|
GameHighScore,
|
||||||
HistoryReadUpdate,
|
HistoryReadUpdate,
|
||||||
IMessageEntityParser,
|
IMessageEntityParser,
|
||||||
|
|
|
@ -5,12 +5,25 @@ import { assertIsUpdatesGroup } from '../../utils/updates-utils'
|
||||||
/**
|
/**
|
||||||
* Create a new broadcast channel
|
* Create a new broadcast channel
|
||||||
*
|
*
|
||||||
* @param title Channel title
|
|
||||||
* @param description Channel description
|
|
||||||
* @returns Newly created channel
|
* @returns Newly created channel
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export async function createChannel(this: TelegramClient, title: string, description = ''): Promise<Chat> {
|
export async function createChannel(
|
||||||
|
this: TelegramClient,
|
||||||
|
params: {
|
||||||
|
/**
|
||||||
|
* Channel title
|
||||||
|
*/
|
||||||
|
title: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Channel description
|
||||||
|
*/
|
||||||
|
description?: string
|
||||||
|
},
|
||||||
|
): Promise<Chat> {
|
||||||
|
const { title, description = '' } = params
|
||||||
|
|
||||||
const res = await this.call({
|
const res = await this.call({
|
||||||
_: 'channels.createChannel',
|
_: 'channels.createChannel',
|
||||||
title,
|
title,
|
||||||
|
|
|
@ -5,16 +5,44 @@ import { assertIsUpdatesGroup } from '../../utils/updates-utils'
|
||||||
/**
|
/**
|
||||||
* Create a new supergroup
|
* Create a new supergroup
|
||||||
*
|
*
|
||||||
* @param title Title of the supergroup
|
* @returns Newly created supergroup
|
||||||
* @param description Description of the supergroup
|
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export async function createSupergroup(this: TelegramClient, title: string, description = ''): Promise<Chat> {
|
export async function createSupergroup(
|
||||||
|
this: TelegramClient,
|
||||||
|
params: {
|
||||||
|
/**
|
||||||
|
* Supergroup title
|
||||||
|
*/
|
||||||
|
title: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supergroup description
|
||||||
|
*/
|
||||||
|
description?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to create a forum
|
||||||
|
*/
|
||||||
|
forum?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TTL period (in seconds) for the newly created channel
|
||||||
|
*
|
||||||
|
* @default 0 (i.e. messages don't expire)
|
||||||
|
*/
|
||||||
|
ttlPeriod?: number
|
||||||
|
},
|
||||||
|
): Promise<Chat> {
|
||||||
|
const { title, description = '', forum, ttlPeriod = 0 } = params
|
||||||
|
|
||||||
const res = await this.call({
|
const res = await this.call({
|
||||||
_: 'channels.createChannel',
|
_: 'channels.createChannel',
|
||||||
title,
|
title,
|
||||||
about: description,
|
about: description,
|
||||||
megagroup: true,
|
megagroup: true,
|
||||||
|
forum,
|
||||||
|
ttlPeriod,
|
||||||
})
|
})
|
||||||
|
|
||||||
assertIsUpdatesGroup('channels.createChannel', res)
|
assertIsUpdatesGroup('channels.createChannel', res)
|
||||||
|
|
55
packages/client/src/methods/forums/create-forum-topic.ts
Normal file
55
packages/client/src/methods/forums/create-forum-topic.ts
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import { tl } from '@mtcute/core'
|
||||||
|
import { randomLong } from '@mtcute/core/utils'
|
||||||
|
|
||||||
|
import { TelegramClient } from '../../client'
|
||||||
|
import { InputPeerLike, Message } from '../../types'
|
||||||
|
import { normalizeToInputChannel } from '../../utils/peer-utils'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a topic in a forum
|
||||||
|
*
|
||||||
|
* Only admins with `manageTopics` permission can do this.
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
* @returns Service message for the created topic
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export async function createForumTopic(
|
||||||
|
this: TelegramClient,
|
||||||
|
chatId: InputPeerLike,
|
||||||
|
params: {
|
||||||
|
/**
|
||||||
|
* Topic title
|
||||||
|
*/
|
||||||
|
title: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Icon of the topic.
|
||||||
|
*
|
||||||
|
* Can be a number (color in RGB, see {@link ForumTopic} static members for allowed values)
|
||||||
|
* or a custom emoji ID.
|
||||||
|
*
|
||||||
|
* Icon color can't be changed after the topic is created.
|
||||||
|
*/
|
||||||
|
icon?: number | tl.Long
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send as a specific channel
|
||||||
|
*/
|
||||||
|
sendAs?: InputPeerLike
|
||||||
|
},
|
||||||
|
): Promise<Message> {
|
||||||
|
const { title, icon, sendAs } = params
|
||||||
|
|
||||||
|
const res = await this.call({
|
||||||
|
_: 'channels.createForumTopic',
|
||||||
|
channel: normalizeToInputChannel(await this.resolvePeer(chatId), chatId),
|
||||||
|
title,
|
||||||
|
iconColor: typeof icon === 'number' ? icon : undefined,
|
||||||
|
iconEmojiId: typeof icon !== 'number' ? icon : undefined,
|
||||||
|
sendAs: sendAs ? await this.resolvePeer(sendAs) : undefined,
|
||||||
|
randomId: randomLong(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return this._findMessageInUpdate(res)
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { assertTypeIsNot } from '@mtcute/core/utils'
|
||||||
|
|
||||||
|
import { TelegramClient } from '../../client'
|
||||||
|
import { InputPeerLike } from '../../types'
|
||||||
|
import { normalizeToInputChannel } from '../../utils/peer-utils'
|
||||||
|
import { createDummyUpdate } from '../../utils/updates-utils'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a forum topic and all its history
|
||||||
|
*
|
||||||
|
* @param chat Chat or user ID, username, phone number, `"me"` or `"self"`
|
||||||
|
* @param topicId ID of the topic (i.e. its top message ID)
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export async function deleteForumTopicHistory(
|
||||||
|
this: TelegramClient,
|
||||||
|
chat: InputPeerLike,
|
||||||
|
topicId: number,
|
||||||
|
): Promise<void> {
|
||||||
|
const channel = normalizeToInputChannel(await this.resolvePeer(chat), chat)
|
||||||
|
assertTypeIsNot('deleteForumTopicHistory', channel, 'inputChannelEmpty')
|
||||||
|
|
||||||
|
const res = await this.call({
|
||||||
|
_: 'channels.deleteTopicHistory',
|
||||||
|
channel,
|
||||||
|
topMsgId: topicId,
|
||||||
|
})
|
||||||
|
|
||||||
|
this._handleUpdate(createDummyUpdate(res.pts, res.ptsCount, channel.channelId))
|
||||||
|
}
|
49
packages/client/src/methods/forums/edit-forum-topic.ts
Normal file
49
packages/client/src/methods/forums/edit-forum-topic.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import Long from 'long'
|
||||||
|
|
||||||
|
import { tl } from '@mtcute/core'
|
||||||
|
|
||||||
|
import { TelegramClient } from '../../client'
|
||||||
|
import { InputPeerLike, Message } from '../../types'
|
||||||
|
import { normalizeToInputChannel } from '../../utils/peer-utils'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modify a topic in a forum
|
||||||
|
*
|
||||||
|
* Only admins with `manageTopics` permission can do this.
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
* @param topicId ID of the topic (i.e. its top message ID)
|
||||||
|
* @returns Service message about the modification
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export async function editForumTopic(
|
||||||
|
this: TelegramClient,
|
||||||
|
chatId: InputPeerLike,
|
||||||
|
topicId: number,
|
||||||
|
params: {
|
||||||
|
/**
|
||||||
|
* New topic title
|
||||||
|
*/
|
||||||
|
title?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New icon of the topic.
|
||||||
|
*
|
||||||
|
* Can be a custom emoji ID, or `null` to remove the icon
|
||||||
|
* and use static color instead
|
||||||
|
*/
|
||||||
|
icon?: tl.Long | null
|
||||||
|
},
|
||||||
|
): Promise<Message> {
|
||||||
|
const { title, icon } = params
|
||||||
|
|
||||||
|
const res = await this.call({
|
||||||
|
_: 'channels.editForumTopic',
|
||||||
|
channel: normalizeToInputChannel(await this.resolvePeer(chatId), chatId),
|
||||||
|
topicId,
|
||||||
|
title,
|
||||||
|
iconEmojiId: icon ? icon ?? Long.ZERO : undefined,
|
||||||
|
})
|
||||||
|
|
||||||
|
return this._findMessageInUpdate(res)
|
||||||
|
}
|
50
packages/client/src/methods/forums/get-forum-topics-by-id.ts
Normal file
50
packages/client/src/methods/forums/get-forum-topics-by-id.ts
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import { MaybeArray } from '@mtcute/core'
|
||||||
|
|
||||||
|
import { TelegramClient } from '../../client'
|
||||||
|
import { ForumTopic, InputPeerLike } from '../../types'
|
||||||
|
import { normalizeToInputChannel } from '../../utils'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a single forum topic by its ID
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export async function getForumTopicsById(this: TelegramClient, chatId: InputPeerLike, ids: number): Promise<ForumTopic>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get forum topics by their IDs
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export async function getForumTopicsById(
|
||||||
|
this: TelegramClient,
|
||||||
|
chatId: InputPeerLike,
|
||||||
|
ids: number[],
|
||||||
|
): Promise<ForumTopic[]>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get forum topics by their IDs
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export async function getForumTopicsById(
|
||||||
|
this: TelegramClient,
|
||||||
|
chatId: InputPeerLike,
|
||||||
|
ids: MaybeArray<number>,
|
||||||
|
): Promise<MaybeArray<ForumTopic>> {
|
||||||
|
const single = !Array.isArray(ids)
|
||||||
|
if (single) ids = [ids as number]
|
||||||
|
|
||||||
|
const res = await this.call({
|
||||||
|
_: 'channels.getForumTopicsByID',
|
||||||
|
channel: normalizeToInputChannel(await this.resolvePeer(chatId)),
|
||||||
|
topics: ids as number[],
|
||||||
|
})
|
||||||
|
|
||||||
|
const topics = ForumTopic.parseTlForumTopics(this, res)
|
||||||
|
|
||||||
|
return single ? topics[0] : topics
|
||||||
|
}
|
77
packages/client/src/methods/forums/get-forum-topics.ts
Normal file
77
packages/client/src/methods/forums/get-forum-topics.ts
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
import { TelegramClient } from '../../client'
|
||||||
|
import { ArrayPaginated, ForumTopic, InputPeerLike } from '../../types'
|
||||||
|
import { makeArrayPaginated } from '../../utils'
|
||||||
|
import { normalizeToInputChannel } from '../../utils/peer-utils'
|
||||||
|
|
||||||
|
// @exported
|
||||||
|
export interface GetForumTopicsOffset {
|
||||||
|
date: number
|
||||||
|
id: number
|
||||||
|
topic: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultOffset: GetForumTopicsOffset = {
|
||||||
|
date: 0,
|
||||||
|
id: 0,
|
||||||
|
topic: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get forum topics
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export async function getForumTopics(
|
||||||
|
this: TelegramClient,
|
||||||
|
chatId: InputPeerLike,
|
||||||
|
params?: {
|
||||||
|
/**
|
||||||
|
* Search query
|
||||||
|
*/
|
||||||
|
query?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset for pagination
|
||||||
|
*/
|
||||||
|
offset?: GetForumTopicsOffset
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of topics to return.
|
||||||
|
*
|
||||||
|
* @default 100
|
||||||
|
*/
|
||||||
|
limit?: number
|
||||||
|
},
|
||||||
|
): Promise<ArrayPaginated<ForumTopic, GetForumTopicsOffset>> {
|
||||||
|
if (!params) params = {}
|
||||||
|
|
||||||
|
const {
|
||||||
|
query,
|
||||||
|
offset: { date: offsetDate, id: offsetId, topic: offsetTopic } = defaultOffset,
|
||||||
|
limit = 100,
|
||||||
|
} = params
|
||||||
|
|
||||||
|
const res = await this.call({
|
||||||
|
_: 'channels.getForumTopics',
|
||||||
|
channel: normalizeToInputChannel(await this.resolvePeer(chatId), chatId),
|
||||||
|
q: query,
|
||||||
|
offsetDate,
|
||||||
|
offsetId,
|
||||||
|
offsetTopic,
|
||||||
|
limit,
|
||||||
|
})
|
||||||
|
|
||||||
|
const topics = ForumTopic.parseTlForumTopics(this, res)
|
||||||
|
|
||||||
|
const last = topics[topics.length - 1]
|
||||||
|
const next = last ?
|
||||||
|
{
|
||||||
|
date: res.orderByCreateDate ? last.raw.date : last.lastMessage.raw.date,
|
||||||
|
id: last.raw.topMessage,
|
||||||
|
topic: last.raw.id,
|
||||||
|
} :
|
||||||
|
undefined
|
||||||
|
|
||||||
|
return makeArrayPaginated(topics, res.count, next)
|
||||||
|
}
|
53
packages/client/src/methods/forums/iter-forum-topics.ts
Normal file
53
packages/client/src/methods/forums/iter-forum-topics.ts
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import { TelegramClient } from '../../client'
|
||||||
|
import { ForumTopic, InputPeerLike } from '../../types'
|
||||||
|
import { normalizeToInputChannel } from '../../utils/peer-utils'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate over forum topics. Wrapper over {@link getForumTopics}.
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export async function* iterForumTopics(
|
||||||
|
this: TelegramClient,
|
||||||
|
chatId: InputPeerLike,
|
||||||
|
params?: Parameters<TelegramClient['getForumTopics']>[1] & {
|
||||||
|
/**
|
||||||
|
* Maximum number of topics to return.
|
||||||
|
*
|
||||||
|
* @default `Infinity`, i.e. return all topics
|
||||||
|
*/
|
||||||
|
limit?: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chunk size. Usually you shouldn't care about this.
|
||||||
|
*/
|
||||||
|
chunkSize?: number
|
||||||
|
},
|
||||||
|
): AsyncIterableIterator<ForumTopic> {
|
||||||
|
if (!params) params = {}
|
||||||
|
|
||||||
|
const { query, limit = Infinity, chunkSize = 100 } = params
|
||||||
|
|
||||||
|
const peer = normalizeToInputChannel(await this.resolvePeer(chatId))
|
||||||
|
|
||||||
|
let { offset } = params
|
||||||
|
let current = 0
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
const res = await this.getForumTopics(peer, {
|
||||||
|
query,
|
||||||
|
offset,
|
||||||
|
limit: Math.min(chunkSize, limit - current),
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const topic of res) {
|
||||||
|
yield topic
|
||||||
|
|
||||||
|
if (++current >= limit) return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res.next) return
|
||||||
|
offset = res.next
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { TelegramClient } from '../../client'
|
||||||
|
import { InputPeerLike } from '../../types'
|
||||||
|
import { normalizeToInputChannel } from '../../utils/peer-utils'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reorder pinned forum topics
|
||||||
|
*
|
||||||
|
* Only admins with `manageTopics` permission can do this.
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
* @param topicId ID of the topic (i.e. its top message ID)
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export async function reorderPinnedForumTopics(
|
||||||
|
this: TelegramClient,
|
||||||
|
chatId: InputPeerLike,
|
||||||
|
params: {
|
||||||
|
/**
|
||||||
|
* Order of the pinned topics
|
||||||
|
*/
|
||||||
|
order: number[]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to un-pin topics not present in the order
|
||||||
|
*/
|
||||||
|
force?: boolean
|
||||||
|
},
|
||||||
|
): Promise<void> {
|
||||||
|
const { order, force } = params
|
||||||
|
await this.call({
|
||||||
|
_: 'channels.reorderPinnedForumTopics',
|
||||||
|
channel: normalizeToInputChannel(await this.resolvePeer(chatId), chatId),
|
||||||
|
order,
|
||||||
|
force,
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { TelegramClient } from '../../client'
|
||||||
|
import { InputPeerLike, Message } from '../../types'
|
||||||
|
import { normalizeToInputChannel } from '../../utils/peer-utils'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle open/close status of a topic in a forum
|
||||||
|
*
|
||||||
|
* Only admins with `manageTopics` permission can do this.
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
* @param topicId ID of the topic (i.e. its top message ID)
|
||||||
|
* @param closed Whether the topic should be closed
|
||||||
|
* @returns Service message about the modification
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export async function toggleForumTopicClosed(
|
||||||
|
this: TelegramClient,
|
||||||
|
chatId: InputPeerLike,
|
||||||
|
topicId: number,
|
||||||
|
closed: boolean,
|
||||||
|
): Promise<Message> {
|
||||||
|
const res = await this.call({
|
||||||
|
_: 'channels.editForumTopic',
|
||||||
|
channel: normalizeToInputChannel(await this.resolvePeer(chatId), chatId),
|
||||||
|
topicId,
|
||||||
|
closed,
|
||||||
|
})
|
||||||
|
|
||||||
|
return this._findMessageInUpdate(res)
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { TelegramClient } from '../../client'
|
||||||
|
import { InputPeerLike } from '../../types'
|
||||||
|
import { normalizeToInputChannel } from '../../utils/peer-utils'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle whether a topic in a forum is pinned
|
||||||
|
*
|
||||||
|
* Only admins with `manageTopics` permission can do this.
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
* @param topicId ID of the topic (i.e. its top message ID)
|
||||||
|
* @param pinned Whether the topic should be pinned
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export async function toggleForumTopicPinned(
|
||||||
|
this: TelegramClient,
|
||||||
|
chatId: InputPeerLike,
|
||||||
|
topicId: number,
|
||||||
|
pinned: boolean,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.call({
|
||||||
|
_: 'channels.updatePinnedForumTopic',
|
||||||
|
channel: normalizeToInputChannel(await this.resolvePeer(chatId), chatId),
|
||||||
|
topicId,
|
||||||
|
pinned,
|
||||||
|
})
|
||||||
|
}
|
21
packages/client/src/methods/forums/toggle-forum.ts
Normal file
21
packages/client/src/methods/forums/toggle-forum.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { TelegramClient } from '../../client'
|
||||||
|
import { InputPeerLike } from '../../types'
|
||||||
|
import { normalizeToInputChannel } from '../../utils/peer-utils'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether a supergroup is a forum.
|
||||||
|
*
|
||||||
|
* Only owner of the supergroup can change this setting.
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
* @param enabled Whether the supergroup should be a forum
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export async function toggleForum(this: TelegramClient, chatId: InputPeerLike, enabled = false): Promise<void> {
|
||||||
|
const res = await this.call({
|
||||||
|
_: 'channels.toggleForum',
|
||||||
|
channel: normalizeToInputChannel(await this.resolvePeer(chatId), chatId),
|
||||||
|
enabled,
|
||||||
|
})
|
||||||
|
this._handleUpdate(res)
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { TelegramClient } from '../../client'
|
||||||
|
import { InputPeerLike, Message } from '../../types'
|
||||||
|
import { normalizeToInputChannel } from '../../utils/peer-utils'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle whether "General" topic in a forum is hidden or not
|
||||||
|
*
|
||||||
|
* Only admins with `manageTopics` permission can do this.
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID or username
|
||||||
|
* @param hidden Whether the topic should be hidden
|
||||||
|
* @returns Service message about the modification
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export async function toggleGeneralTopicHidden(
|
||||||
|
this: TelegramClient,
|
||||||
|
chatId: InputPeerLike,
|
||||||
|
hidden: boolean,
|
||||||
|
): Promise<Message> {
|
||||||
|
const res = await this.call({
|
||||||
|
_: 'channels.editForumTopic',
|
||||||
|
channel: normalizeToInputChannel(await this.resolvePeer(chatId), chatId),
|
||||||
|
topicId: 1,
|
||||||
|
hidden,
|
||||||
|
})
|
||||||
|
|
||||||
|
return this._findMessageInUpdate(res)
|
||||||
|
}
|
|
@ -57,6 +57,8 @@ export async function sendCopy(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message to reply to. Either a message object or message ID.
|
* Message to reply to. Either a message object or message ID.
|
||||||
|
*
|
||||||
|
* For forums - can also be an ID of the topic (i.e. its top message ID)
|
||||||
*/
|
*/
|
||||||
replyTo?: number | Message
|
replyTo?: number | Message
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,8 @@ export async function sendMediaGroup(
|
||||||
params?: {
|
params?: {
|
||||||
/**
|
/**
|
||||||
* Message to reply to. Either a message object or message ID.
|
* Message to reply to. Either a message object or message ID.
|
||||||
|
*
|
||||||
|
* For forums - can also be an ID of the topic (i.e. its top message ID)
|
||||||
*/
|
*/
|
||||||
replyTo?: number | Message
|
replyTo?: number | Message
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,8 @@ export async function sendMedia(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message to reply to. Either a message object or message ID.
|
* Message to reply to. Either a message object or message ID.
|
||||||
|
*
|
||||||
|
* For forums - can also be an ID of the topic (i.e. its top message ID)
|
||||||
*/
|
*/
|
||||||
replyTo?: number | Message
|
replyTo?: number | Message
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,8 @@ export async function sendText(
|
||||||
params?: {
|
params?: {
|
||||||
/**
|
/**
|
||||||
* Message to reply to. Either a message object or message ID.
|
* Message to reply to. Either a message object or message ID.
|
||||||
|
*
|
||||||
|
* For forums - can also be an ID of the topic (i.e. its top message ID)
|
||||||
*/
|
*/
|
||||||
replyTo?: number | Message
|
replyTo?: number | Message
|
||||||
|
|
||||||
|
|
|
@ -9,12 +9,24 @@ import { createDummyUpdate } from '../../utils/updates-utils'
|
||||||
* @param chatId Chat or user ID
|
* @param chatId Chat or user ID
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export async function unpinAllMessages(this: TelegramClient, chatId: InputPeerLike): Promise<void> {
|
export async function unpinAllMessages(
|
||||||
|
this: TelegramClient,
|
||||||
|
chatId: InputPeerLike,
|
||||||
|
params?: {
|
||||||
|
/**
|
||||||
|
* For forums - unpin only messages from the given topic
|
||||||
|
*/
|
||||||
|
topicId?: number
|
||||||
|
},
|
||||||
|
): Promise<void> {
|
||||||
|
const { topicId } = params ?? {}
|
||||||
|
|
||||||
const peer = await this.resolvePeer(chatId)
|
const peer = await this.resolvePeer(chatId)
|
||||||
|
|
||||||
const res = await this.call({
|
const res = await this.call({
|
||||||
_: 'messages.unpinAllMessages',
|
_: 'messages.unpinAllMessages',
|
||||||
peer,
|
peer,
|
||||||
|
topMsgId: topicId,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (isInputPeerChannel(peer)) {
|
if (isInputPeerChannel(peer)) {
|
||||||
|
|
|
@ -25,7 +25,7 @@ export class Dialog {
|
||||||
readonly client: TelegramClient,
|
readonly client: TelegramClient,
|
||||||
readonly raw: tl.RawDialog,
|
readonly raw: tl.RawDialog,
|
||||||
readonly _peers: PeersIndex,
|
readonly _peers: PeersIndex,
|
||||||
readonly _messages: Record<number, tl.TypeMessage>,
|
readonly _messages: Map<number, tl.TypeMessage>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,11 +44,11 @@ export class Dialog {
|
||||||
|
|
||||||
const peers = PeersIndex.from(dialogs)
|
const peers = PeersIndex.from(dialogs)
|
||||||
|
|
||||||
const messages: Record<number, tl.TypeMessage> = {}
|
const messages = new Map<number, tl.TypeMessage>()
|
||||||
dialogs.messages.forEach((msg) => {
|
dialogs.messages.forEach((msg) => {
|
||||||
if (!msg.peerId) return
|
if (!msg.peerId) return
|
||||||
|
|
||||||
messages[getMarkedPeerId(msg.peerId)] = msg
|
messages.set(getMarkedPeerId(msg.peerId), msg)
|
||||||
})
|
})
|
||||||
|
|
||||||
const arr = dialogs.dialogs
|
const arr = dialogs.dialogs
|
||||||
|
@ -228,8 +228,8 @@ export class Dialog {
|
||||||
if (!this._lastMessage) {
|
if (!this._lastMessage) {
|
||||||
const cid = this.chat.id
|
const cid = this.chat.id
|
||||||
|
|
||||||
if (cid in this._messages) {
|
if (this._messages.has(cid)) {
|
||||||
this._lastMessage = new Message(this.client, this._messages[cid], this._peers)
|
this._lastMessage = new Message(this.client, this._messages.get(cid)!, this._peers)
|
||||||
} else {
|
} else {
|
||||||
throw new MtMessageNotFoundError(cid, 0)
|
throw new MtMessageNotFoundError(cid, 0)
|
||||||
}
|
}
|
||||||
|
@ -267,12 +267,19 @@ export class Dialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of unread messages
|
* Number of unread mentions
|
||||||
*/
|
*/
|
||||||
get unreadMentionsCount(): number {
|
get unreadMentionsCount(): number {
|
||||||
return this.raw.unreadMentionsCount
|
return this.raw.unreadMentionsCount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of unread reactions
|
||||||
|
*/
|
||||||
|
get unreadReactionsCount(): number {
|
||||||
|
return this.raw.unreadReactionsCount
|
||||||
|
}
|
||||||
|
|
||||||
private _draftMessage?: DraftMessage | null
|
private _draftMessage?: DraftMessage | null
|
||||||
/**
|
/**
|
||||||
* Draft message in this dialog
|
* Draft message in this dialog
|
||||||
|
@ -280,7 +287,7 @@ export class Dialog {
|
||||||
get draftMessage(): DraftMessage | null {
|
get draftMessage(): DraftMessage | null {
|
||||||
if (this._draftMessage === undefined) {
|
if (this._draftMessage === undefined) {
|
||||||
if (this.raw.draft?._ === 'draftMessage') {
|
if (this.raw.draft?._ === 'draftMessage') {
|
||||||
this._draftMessage = new DraftMessage(this.client, this.raw.draft, this.chat.inputPeer)
|
this._draftMessage = new DraftMessage(this.client, this.raw.draft)
|
||||||
} else {
|
} else {
|
||||||
this._draftMessage = null
|
this._draftMessage = null
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,16 +2,13 @@ import { tl } from '@mtcute/core'
|
||||||
|
|
||||||
import { TelegramClient } from '../../client'
|
import { TelegramClient } from '../../client'
|
||||||
import { makeInspectable } from '../../utils'
|
import { makeInspectable } from '../../utils'
|
||||||
import { InputMediaLike } from '../media'
|
|
||||||
import { InputPeerLike } from '../peers'
|
|
||||||
import { Message } from './message'
|
|
||||||
import { MessageEntity } from './message-entity'
|
import { MessageEntity } from './message-entity'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A draft message
|
* A draft message
|
||||||
*/
|
*/
|
||||||
export class DraftMessage {
|
export class DraftMessage {
|
||||||
constructor(readonly client: TelegramClient, readonly raw: tl.RawDraftMessage, readonly _chatId: InputPeerLike) {}
|
constructor(readonly client: TelegramClient, readonly raw: tl.RawDraftMessage) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Text of the draft message
|
* Text of the draft message
|
||||||
|
@ -59,45 +56,6 @@ export class DraftMessage {
|
||||||
|
|
||||||
return this._entities
|
return this._entities
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Send this draft as a message.
|
|
||||||
* Calling this method will clear current draft.
|
|
||||||
*
|
|
||||||
* @param params Additional sending parameters
|
|
||||||
* @link TelegramClient.sendText
|
|
||||||
*/
|
|
||||||
send(params?: Parameters<TelegramClient['sendText']>[2]): Promise<Message> {
|
|
||||||
return this.client.sendText(this._chatId, this.raw.message, {
|
|
||||||
clearDraft: true,
|
|
||||||
disableWebPreview: this.raw.noWebpage,
|
|
||||||
entities: this.raw.entities,
|
|
||||||
replyTo: this.raw.replyToMsgId,
|
|
||||||
...(params || {}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send this draft as a message with media.
|
|
||||||
* Calling this method will clear current draft.
|
|
||||||
*
|
|
||||||
* If passed media does not have an
|
|
||||||
* explicit caption, it will be set to {@link text},
|
|
||||||
* and its entities to {@link entities}
|
|
||||||
*
|
|
||||||
* @param media Media to be sent
|
|
||||||
* @param params Additional sending parameters
|
|
||||||
* @link TelegramClient.sendMedia
|
|
||||||
*/
|
|
||||||
sendWithMedia(media: InputMediaLike, params?: Parameters<TelegramClient['sendMedia']>[2]): Promise<Message> {
|
|
||||||
return this.client.sendMedia(this._chatId, media, {
|
|
||||||
clearDraft: true,
|
|
||||||
replyTo: this.raw.replyToMsgId,
|
|
||||||
caption: this.raw.message,
|
|
||||||
entities: this.raw.entities,
|
|
||||||
...(params || {}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
makeInspectable(DraftMessage)
|
makeInspectable(DraftMessage)
|
||||||
|
|
|
@ -249,6 +249,37 @@ export interface ActionSetTtl {
|
||||||
readonly period: number
|
readonly period: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Forum topic was created */
|
||||||
|
export interface ActionTopicCreated {
|
||||||
|
readonly type: 'topic_created'
|
||||||
|
|
||||||
|
/** Title of the topic */
|
||||||
|
title: string
|
||||||
|
|
||||||
|
/** Icon color of the topic */
|
||||||
|
iconColor: number
|
||||||
|
|
||||||
|
/** Icon emoji of the topic */
|
||||||
|
iconCustomEmoji?: tl.Long
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Forum topic was modified */
|
||||||
|
export interface ActionTopicEdited {
|
||||||
|
readonly type: 'topic_edited'
|
||||||
|
|
||||||
|
/** New title of the topic */
|
||||||
|
title?: string
|
||||||
|
|
||||||
|
/** New icon emoji of the topic (may be empty) */
|
||||||
|
iconCustomEmoji?: tl.Long
|
||||||
|
|
||||||
|
/** Whether the topic was opened/closed */
|
||||||
|
closed?: boolean
|
||||||
|
|
||||||
|
/** Whether the topic was (un-)hidden - only for "General" topic (`id=1`) */
|
||||||
|
hidden?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export type MessageAction =
|
export type MessageAction =
|
||||||
| ActionChatCreated
|
| ActionChatCreated
|
||||||
| ActionChannelCreated
|
| ActionChannelCreated
|
||||||
|
@ -275,6 +306,8 @@ export type MessageAction =
|
||||||
| ActionGroupCallEnded
|
| ActionGroupCallEnded
|
||||||
| ActionGroupInvite
|
| ActionGroupInvite
|
||||||
| ActionSetTtl
|
| ActionSetTtl
|
||||||
|
| ActionTopicCreated
|
||||||
|
| ActionTopicEdited
|
||||||
| null
|
| null
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
|
@ -426,6 +459,21 @@ export function _messageActionFromTl(this: Message, act: tl.TypeMessageAction):
|
||||||
type: 'set_ttl',
|
type: 'set_ttl',
|
||||||
period: act.period,
|
period: act.period,
|
||||||
}
|
}
|
||||||
|
case 'messageActionTopicCreate':
|
||||||
|
return {
|
||||||
|
type: 'topic_created',
|
||||||
|
title: act.title,
|
||||||
|
iconColor: act.iconColor,
|
||||||
|
iconCustomEmoji: act.iconEmojiId,
|
||||||
|
}
|
||||||
|
case 'messageActionTopicEdit':
|
||||||
|
return {
|
||||||
|
type: 'topic_edited',
|
||||||
|
title: act.title,
|
||||||
|
iconCustomEmoji: act.iconEmojiId,
|
||||||
|
closed: act.closed,
|
||||||
|
hidden: act.hidden,
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { tl } from '@mtcute/core'
|
import { tl } from '@mtcute/core'
|
||||||
|
import { assertTypeIs } from '@mtcute/core/utils'
|
||||||
|
|
||||||
import { PeersIndex, TelegramClient, toggleChannelIdMark } from '../../..'
|
import { ForumTopic, PeersIndex, TelegramClient, toggleChannelIdMark } from '../../..'
|
||||||
import { Photo } from '../../media'
|
import { Photo } from '../../media'
|
||||||
import { Message } from '../../messages'
|
import { Message } from '../../messages'
|
||||||
import { ChatInviteLink } from '../chat-invite-link'
|
import { ChatInviteLink } from '../chat-invite-link'
|
||||||
|
@ -297,6 +298,41 @@ export interface ChatActionTtlChanged {
|
||||||
new: number
|
new: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Forum has been toggled */
|
||||||
|
export interface ChatActionForumToggled {
|
||||||
|
type: 'forum_toggled'
|
||||||
|
|
||||||
|
/** New status */
|
||||||
|
enabled: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Forum topic has been created */
|
||||||
|
export interface ChatActionTopicCreated {
|
||||||
|
type: 'topic_created'
|
||||||
|
|
||||||
|
/** Topic that has been created */
|
||||||
|
topic: ForumTopic
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Forum topic has been edited */
|
||||||
|
export interface ChatActionTopicEdited {
|
||||||
|
type: 'topic_edited'
|
||||||
|
|
||||||
|
/** Old topic info */
|
||||||
|
old: ForumTopic
|
||||||
|
|
||||||
|
/** New topic info */
|
||||||
|
new: ForumTopic
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Forum topic has been edited */
|
||||||
|
export interface ChatActionTopicDeleted {
|
||||||
|
type: 'topic_deleted'
|
||||||
|
|
||||||
|
/** Old topic info */
|
||||||
|
topic: ForumTopic
|
||||||
|
}
|
||||||
|
|
||||||
/** Chat event action (`null` if unsupported) */
|
/** Chat event action (`null` if unsupported) */
|
||||||
export type ChatAction =
|
export type ChatAction =
|
||||||
| ChatActionUserJoined
|
| ChatActionUserJoined
|
||||||
|
@ -329,6 +365,10 @@ export type ChatAction =
|
||||||
| ChatActionInviteLinkRevoked
|
| ChatActionInviteLinkRevoked
|
||||||
| ChatActionUserJoinedApproved
|
| ChatActionUserJoinedApproved
|
||||||
| ChatActionTtlChanged
|
| ChatActionTtlChanged
|
||||||
|
| ChatActionForumToggled
|
||||||
|
| ChatActionTopicCreated
|
||||||
|
| ChatActionTopicEdited
|
||||||
|
| ChatActionTopicDeleted
|
||||||
| null
|
| null
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
|
@ -341,12 +381,6 @@ export function _actionFromTl(
|
||||||
// channelAdminLogEventActionToggleNoForwards#cb2ac766 new_value:Bool = ChannelAdminLogEventAction;
|
// channelAdminLogEventActionToggleNoForwards#cb2ac766 new_value:Bool = ChannelAdminLogEventAction;
|
||||||
// todo - MTQ-57
|
// todo - MTQ-57
|
||||||
// channelAdminLogEventActionChangeUsernames#f04fb3a9 prev_value:Vector<string> new_value:Vector<string>
|
// channelAdminLogEventActionChangeUsernames#f04fb3a9 prev_value:Vector<string> new_value:Vector<string>
|
||||||
// todo - MTQ-77
|
|
||||||
// channelAdminLogEventActionToggleForum#2cc6383 new_value:Bool = ChannelAdminLogEventAction;
|
|
||||||
// channelAdminLogEventActionCreateTopic#58707d28 topic:ForumTopic = ChannelAdminLogEventAction;
|
|
||||||
// channelAdminLogEventActionEditTopic#f06fe208 prev_topic:ForumTopic new_topic:ForumTopic
|
|
||||||
// channelAdminLogEventActionDeleteTopic#ae168909 topic:ForumTopic = ChannelAdminLogEventAction;
|
|
||||||
// channelAdminLogEventActionPinTopic#5d8d353b flags:# prev_topic:flags.0?ForumTopic new_topic:flags.1?ForumTopic
|
|
||||||
// todo - MTQ-72
|
// todo - MTQ-72
|
||||||
// channelAdminLogEventActionSendMessage#278f2868 message:Message = ChannelAdminLogEventAction;
|
// channelAdminLogEventActionSendMessage#278f2868 message:Message = ChannelAdminLogEventAction;
|
||||||
// channelAdminLogEventActionChangeAvailableReactions#be4e0ef8 prev_value:ChatReactions new_value:ChatReactions
|
// channelAdminLogEventActionChangeAvailableReactions#be4e0ef8 prev_value:ChatReactions new_value:ChatReactions
|
||||||
|
@ -520,6 +554,39 @@ export function _actionFromTl(
|
||||||
link: new ChatInviteLink(client, e.invite, peers),
|
link: new ChatInviteLink(client, e.invite, peers),
|
||||||
approvedBy: new User(client, peers.user(e.approvedBy)),
|
approvedBy: new User(client, peers.user(e.approvedBy)),
|
||||||
}
|
}
|
||||||
|
// channelAdminLogEventActionCreateTopic#58707d28 topic:ForumTopic = ChannelAdminLogEventAction;
|
||||||
|
// channelAdminLogEventActionEditTopic#f06fe208 prev_topic:ForumTopic new_topic:ForumTopic
|
||||||
|
// channelAdminLogEventActionDeleteTopic#ae168909 topic:ForumTopic = ChannelAdminLogEventAction;
|
||||||
|
case 'channelAdminLogEventActionToggleForum':
|
||||||
|
return {
|
||||||
|
type: 'forum_toggled',
|
||||||
|
enabled: e.newValue,
|
||||||
|
}
|
||||||
|
case 'channelAdminLogEventActionCreateTopic':
|
||||||
|
assertTypeIs('ChannelAdminLogEventActionCreateTopic#topic', e.topic, 'forumTopic')
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'topic_created',
|
||||||
|
topic: new ForumTopic(client, e.topic, peers),
|
||||||
|
}
|
||||||
|
case 'channelAdminLogEventActionEditTopic':
|
||||||
|
assertTypeIs('ChannelAdminLogEventActionCreateTopic#topic', e.prevTopic, 'forumTopic')
|
||||||
|
assertTypeIs('ChannelAdminLogEventActionCreateTopic#topic', e.newTopic, 'forumTopic')
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'topic_edited',
|
||||||
|
old: new ForumTopic(client, e.prevTopic, peers),
|
||||||
|
new: new ForumTopic(client, e.newTopic, peers),
|
||||||
|
}
|
||||||
|
case 'channelAdminLogEventActionDeleteTopic':
|
||||||
|
assertTypeIs('ChannelAdminLogEventActionCreateTopic#topic', e.topic, 'forumTopic')
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'topic_deleted',
|
||||||
|
topic: new ForumTopic(client, e.topic, peers),
|
||||||
|
}
|
||||||
|
// case 'channelAdminLogEventActionPinTopic'
|
||||||
|
// ^ looks like it is not used, and pinned topics are not at all presented in the event log
|
||||||
default:
|
default:
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@ export function normalizeChatEventFilters(input: InputChatEventFilters): ChatEve
|
||||||
case 'history_toggled':
|
case 'history_toggled':
|
||||||
case 'signatures_toggled':
|
case 'signatures_toggled':
|
||||||
case 'def_perms_changed':
|
case 'def_perms_changed':
|
||||||
|
case 'forum_toggled':
|
||||||
serverFilter.settings = true
|
serverFilter.settings = true
|
||||||
break
|
break
|
||||||
case 'msg_pinned':
|
case 'msg_pinned':
|
||||||
|
@ -94,6 +95,11 @@ export function normalizeChatEventFilters(input: InputChatEventFilters): ChatEve
|
||||||
case 'invite_revoked':
|
case 'invite_revoked':
|
||||||
serverFilter.invites = true
|
serverFilter.invites = true
|
||||||
break
|
break
|
||||||
|
case 'topic_created':
|
||||||
|
case 'topic_edited':
|
||||||
|
case 'topic_deleted':
|
||||||
|
serverFilter.forums = true
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
assertNever(type)
|
assertNever(type)
|
||||||
}
|
}
|
||||||
|
|
202
packages/client/src/types/peers/forum-topic.ts
Normal file
202
packages/client/src/types/peers/forum-topic.ts
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
import { MtTypeAssertionError, tl } from '@mtcute/core'
|
||||||
|
|
||||||
|
import { TelegramClient } from '../../client'
|
||||||
|
import { hasValueAtKey, makeInspectable } from '../../utils'
|
||||||
|
import { MtMessageNotFoundError } from '../errors'
|
||||||
|
import { DraftMessage, Message } from '../messages'
|
||||||
|
import { Chat } from './chat'
|
||||||
|
import { PeersIndex } from './peers-index'
|
||||||
|
import { User } from './user'
|
||||||
|
|
||||||
|
export class ForumTopic {
|
||||||
|
static COLOR_BLUE = 0x6fb9f0
|
||||||
|
static COLOR_YELLOW = 0xffd67e
|
||||||
|
static COLOR_PURPLE = 0xcb86db
|
||||||
|
static COLOR_GREEN = 0x8eee98
|
||||||
|
static COLOR_PINK = 0xff93b2
|
||||||
|
static COLOR_RED = 0xfb6f5f
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly client: TelegramClient,
|
||||||
|
readonly raw: tl.RawForumTopic,
|
||||||
|
readonly _peers: PeersIndex,
|
||||||
|
readonly _messages?: Map<number, tl.TypeMessage>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
static parseTlForumTopics(client: TelegramClient, topics: tl.messages.TypeForumTopics): ForumTopic[] {
|
||||||
|
const peers = PeersIndex.from(topics)
|
||||||
|
const messages = new Map<number, tl.TypeMessage>()
|
||||||
|
|
||||||
|
topics.messages.forEach((msg) => {
|
||||||
|
if (!msg.peerId) return
|
||||||
|
|
||||||
|
messages.set(msg.id, msg)
|
||||||
|
})
|
||||||
|
|
||||||
|
return topics.topics
|
||||||
|
.filter(hasValueAtKey('_', 'forumTopic'))
|
||||||
|
.map((it) => new ForumTopic(client, it, peers, messages))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the topic was created by the current user
|
||||||
|
*/
|
||||||
|
get isMy(): boolean {
|
||||||
|
return this.raw.my!
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the topic is closed
|
||||||
|
*/
|
||||||
|
get isClosed(): boolean {
|
||||||
|
return this.raw.closed!
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the topic is pinned
|
||||||
|
*/
|
||||||
|
get isPinned(): boolean {
|
||||||
|
return this.raw.pinned!
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this constructor is a reduced version of the full topic information.
|
||||||
|
*
|
||||||
|
* If `true`, only {@link isMy}, {@link isClosed}, {@link id}, {@link date},
|
||||||
|
* {@link title}, {@link iconColor}, {@link iconCustomEmoji} and {@link creator}
|
||||||
|
* parameters will contain valid information.
|
||||||
|
*/
|
||||||
|
get isShort(): boolean {
|
||||||
|
return this.raw.short!
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID of the topic
|
||||||
|
*/
|
||||||
|
get id(): number {
|
||||||
|
return this.raw.id
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Date when the topic was created
|
||||||
|
*/
|
||||||
|
get date(): Date {
|
||||||
|
return new Date(this.raw.date * 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Title of the topic
|
||||||
|
*/
|
||||||
|
get title(): string {
|
||||||
|
return this.raw.title
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Color of the topic's icon, used as a fallback
|
||||||
|
* in case {@link iconEmoji} is not set.
|
||||||
|
*
|
||||||
|
* One of the static `COLOR_*` fields.
|
||||||
|
*/
|
||||||
|
get iconColor(): number | null {
|
||||||
|
return this.raw.iconColor ?? null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emoji used as the topic's icon.
|
||||||
|
*/
|
||||||
|
get iconCustomEmoji(): tl.Long | null {
|
||||||
|
return this.raw.iconEmojiId ?? null
|
||||||
|
}
|
||||||
|
|
||||||
|
private _creator?: User | Chat
|
||||||
|
/**
|
||||||
|
* Creator of the topic
|
||||||
|
*/
|
||||||
|
get creator(): User | Chat {
|
||||||
|
if (this._creator) return this._creator
|
||||||
|
|
||||||
|
switch (this.raw.fromId._) {
|
||||||
|
case 'peerUser':
|
||||||
|
return (this._creator = new User(this.client, this._peers.user(this.raw.fromId.userId)))
|
||||||
|
case 'peerChat':
|
||||||
|
return (this._creator = new Chat(this.client, this._peers.chat(this.raw.fromId.chatId)))
|
||||||
|
default:
|
||||||
|
throw new MtTypeAssertionError('ForumTopic#creator', 'peerUser | peerChat', this.raw.fromId._)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _lastMessage?: Message
|
||||||
|
/**
|
||||||
|
* The latest message sent in this topic
|
||||||
|
*/
|
||||||
|
get lastMessage(): Message {
|
||||||
|
if (!this._lastMessage) {
|
||||||
|
const id = this.raw.topMessage
|
||||||
|
|
||||||
|
if (this._messages?.has(id)) {
|
||||||
|
this._lastMessage = new Message(this.client, this._messages.get(id)!, this._peers)
|
||||||
|
} else {
|
||||||
|
throw new MtMessageNotFoundError(0, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._lastMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID of the last read outgoing message in this topic
|
||||||
|
*/
|
||||||
|
get lastReadIngoing(): number {
|
||||||
|
return this.raw.readInboxMaxId
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID of the last read ingoing message in this topic
|
||||||
|
*/
|
||||||
|
get lastReadOutgoing(): number {
|
||||||
|
return this.raw.readOutboxMaxId
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID of the last read message in this topic
|
||||||
|
*/
|
||||||
|
get lastRead(): number {
|
||||||
|
return Math.max(this.raw.readOutboxMaxId, this.raw.readInboxMaxId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of unread messages in the topic
|
||||||
|
*/
|
||||||
|
get unreadCount(): number {
|
||||||
|
return this.raw.unreadCount
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of unread mentions in the topic
|
||||||
|
*/
|
||||||
|
get unreadMentionsCount(): number {
|
||||||
|
return this.raw.unreadMentionsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of unread reactions in the topic
|
||||||
|
*/
|
||||||
|
get unreadReactionsCount(): number {
|
||||||
|
return this.raw.unreadReactionsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
private _draftMessage?: DraftMessage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draft message in the topic
|
||||||
|
*/
|
||||||
|
get draftMessage(): DraftMessage | null {
|
||||||
|
if (this._draftMessage) return this._draftMessage
|
||||||
|
|
||||||
|
if (!this.raw.draft || this.raw.draft._ === 'draftMessageEmpty') return null
|
||||||
|
|
||||||
|
return (this._draftMessage = new DraftMessage(this.client, this.raw.draft))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
makeInspectable(ForumTopic)
|
|
@ -9,6 +9,7 @@ export * from './chat-member'
|
||||||
export * from './chat-permissions'
|
export * from './chat-permissions'
|
||||||
export * from './chat-photo'
|
export * from './chat-photo'
|
||||||
export * from './chat-preview'
|
export * from './chat-preview'
|
||||||
|
export * from './forum-topic'
|
||||||
export * from './peers-index'
|
export * from './peers-index'
|
||||||
export * from './typing-status'
|
export * from './typing-status'
|
||||||
export * from './user'
|
export * from './user'
|
||||||
|
|
Loading…
Reference in a new issue