feat: contexts
This commit is contained in:
parent
6629d91274
commit
337418a34c
51 changed files with 1550 additions and 1078 deletions
|
@ -343,7 +343,7 @@ async function addSingleMethod(state, fileName) {
|
||||||
state.imports[module] = new Set()
|
state.imports[module] = new Set()
|
||||||
}
|
}
|
||||||
|
|
||||||
state.imports[module].add(name)
|
if (!isManual) state.imports[module].add(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (stmt.kind === ts.SyntaxKind.InterfaceDeclaration) {
|
} else if (stmt.kind === ts.SyntaxKind.InterfaceDeclaration) {
|
||||||
|
|
|
@ -26,7 +26,7 @@ function parseUpdateTypes() {
|
||||||
const ret = []
|
const ret = []
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
const m = line.match(/^([a-z_]+)(?:: ([a-zA-Z]+))? = ([a-zA-Z]+(?:\[\])?)( \+ State)?$/)
|
const m = line.match(/^([a-z_]+)(?:: ([a-zA-Z]+))? = ([a-zA-Z]+(?:\[\])?)( \+ State)?(?: in ([a-zA-Z]+))?$/)
|
||||||
if (!m) throw new Error(`invalid syntax: ${line}`)
|
if (!m) throw new Error(`invalid syntax: ${line}`)
|
||||||
ret.push({
|
ret.push({
|
||||||
typeName: m[1],
|
typeName: m[1],
|
||||||
|
@ -34,6 +34,7 @@ function parseUpdateTypes() {
|
||||||
updateType: m[3],
|
updateType: m[3],
|
||||||
funcName: m[2] ? m[2][0].toLowerCase() + m[2].substr(1) : snakeToCamel(m[1]),
|
funcName: m[2] ? m[2][0].toLowerCase() + m[2].substr(1) : snakeToCamel(m[1]),
|
||||||
state: Boolean(m[4]),
|
state: Boolean(m[4]),
|
||||||
|
context: m[5] ?? `UpdateContext<${m[3]}>`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
# format: type_name[: handler_type_name] = update_type[ + State]
|
# format: type_name[: handler_type_name] = update_type[ + State][in ContextClassName]
|
||||||
new_message = Message + State
|
new_message = Message + State in MessageContext
|
||||||
edit_message = Message + State
|
edit_message = Message + State in MessageContext
|
||||||
message_group = Message[] + State
|
message_group = Message[] + State in MessageContext
|
||||||
delete_message = DeleteMessageUpdate
|
delete_message = DeleteMessageUpdate
|
||||||
chat_member: ChatMemberUpdate = ChatMemberUpdate
|
chat_member: ChatMemberUpdate = ChatMemberUpdate
|
||||||
inline_query = InlineQuery
|
inline_query = InlineQuery in InlineQueryContext
|
||||||
chosen_inline_result = ChosenInlineResult
|
chosen_inline_result = ChosenInlineResult in ChosenInlineResultContext
|
||||||
callback_query = CallbackQuery + State
|
callback_query = CallbackQuery + State in CallbackQueryContext
|
||||||
poll: PollUpdate = PollUpdate
|
poll: PollUpdate = PollUpdate
|
||||||
poll_vote = PollVoteUpdate
|
poll_vote = PollVoteUpdate
|
||||||
user_status: UserStatusUpdate = UserStatusUpdate
|
user_status: UserStatusUpdate = UserStatusUpdate
|
||||||
user_typing = UserTypingUpdate
|
user_typing = UserTypingUpdate
|
||||||
history_read = HistoryReadUpdate
|
history_read = HistoryReadUpdate
|
||||||
bot_stopped = BotStoppedUpdate
|
bot_stopped = BotStoppedUpdate
|
||||||
bot_chat_join_request = BotChatJoinRequestUpdate
|
bot_chat_join_request = BotChatJoinRequestUpdate in ChatJoinRequestUpdateContext
|
||||||
chat_join_request = ChatJoinRequestUpdate
|
chat_join_request = ChatJoinRequestUpdate
|
||||||
pre_checkout_query = PreCheckoutQuery
|
pre_checkout_query = PreCheckoutQuery in PreCheckoutQueryContext
|
||||||
story: StoryUpdate = StoryUpdate
|
story: StoryUpdate = StoryUpdate
|
||||||
delete_story = DeleteStoryUpdate
|
delete_story = DeleteStoryUpdate
|
|
@ -20,7 +20,7 @@ import { getPasswordHint } from './methods/auth/get-password-hint'
|
||||||
import { logOut } from './methods/auth/log-out'
|
import { logOut } from './methods/auth/log-out'
|
||||||
import { recoverPassword } from './methods/auth/recover-password'
|
import { recoverPassword } from './methods/auth/recover-password'
|
||||||
import { resendCode } from './methods/auth/resend-code'
|
import { resendCode } from './methods/auth/resend-code'
|
||||||
import { run } from './methods/auth/run'
|
import {} from './methods/auth/run'
|
||||||
import { sendCode } from './methods/auth/send-code'
|
import { sendCode } from './methods/auth/send-code'
|
||||||
import { sendRecoveryCode } from './methods/auth/send-recovery-code'
|
import { sendRecoveryCode } from './methods/auth/send-recovery-code'
|
||||||
import { signIn } from './methods/auth/sign-in'
|
import { signIn } from './methods/auth/sign-in'
|
||||||
|
@ -141,6 +141,7 @@ import { getMessageReactions, getMessageReactionsById } from './methods/messages
|
||||||
import { getMessages } from './methods/messages/get-messages'
|
import { getMessages } from './methods/messages/get-messages'
|
||||||
import { getMessagesUnsafe } from './methods/messages/get-messages-unsafe'
|
import { getMessagesUnsafe } from './methods/messages/get-messages-unsafe'
|
||||||
import { getReactionUsers, GetReactionUsersOffset } from './methods/messages/get-reaction-users'
|
import { getReactionUsers, GetReactionUsersOffset } from './methods/messages/get-reaction-users'
|
||||||
|
import { getReplyTo } from './methods/messages/get-reply-to'
|
||||||
import { getScheduledMessages } from './methods/messages/get-scheduled-messages'
|
import { getScheduledMessages } from './methods/messages/get-scheduled-messages'
|
||||||
import { iterHistory } from './methods/messages/iter-history'
|
import { iterHistory } from './methods/messages/iter-history'
|
||||||
import { iterReactionUsers } from './methods/messages/iter-reaction-users'
|
import { iterReactionUsers } from './methods/messages/iter-reaction-users'
|
||||||
|
@ -152,10 +153,15 @@ import { readHistory } from './methods/messages/read-history'
|
||||||
import { readReactions } from './methods/messages/read-reactions'
|
import { readReactions } from './methods/messages/read-reactions'
|
||||||
import { searchGlobal, SearchGlobalOffset } from './methods/messages/search-global'
|
import { searchGlobal, SearchGlobalOffset } from './methods/messages/search-global'
|
||||||
import { searchMessages, SearchMessagesOffset } from './methods/messages/search-messages'
|
import { searchMessages, SearchMessagesOffset } from './methods/messages/search-messages'
|
||||||
|
import { answerMedia, answerMediaGroup, answerText } from './methods/messages/send-answer'
|
||||||
|
import { commentMedia, commentMediaGroup, commentText } from './methods/messages/send-comment'
|
||||||
|
import { CommonSendParams } from './methods/messages/send-common'
|
||||||
import { sendCopy, SendCopyParams } from './methods/messages/send-copy'
|
import { sendCopy, SendCopyParams } from './methods/messages/send-copy'
|
||||||
|
import { sendCopyGroup, SendCopyGroupParams } from './methods/messages/send-copy-group'
|
||||||
import { sendMedia } from './methods/messages/send-media'
|
import { sendMedia } from './methods/messages/send-media'
|
||||||
import { sendMediaGroup } from './methods/messages/send-media-group'
|
import { sendMediaGroup } from './methods/messages/send-media-group'
|
||||||
import { sendReaction } from './methods/messages/send-reaction'
|
import { sendReaction } from './methods/messages/send-reaction'
|
||||||
|
import { replyMedia, replyMediaGroup, replyText } from './methods/messages/send-reply'
|
||||||
import { sendScheduled } from './methods/messages/send-scheduled'
|
import { sendScheduled } from './methods/messages/send-scheduled'
|
||||||
import { sendText } from './methods/messages/send-text'
|
import { sendText } from './methods/messages/send-text'
|
||||||
import { sendTyping } from './methods/messages/send-typing'
|
import { sendTyping } from './methods/messages/send-typing'
|
||||||
|
@ -180,7 +186,7 @@ import { removeCloudPassword } from './methods/password/remove-cloud-password'
|
||||||
import { addStickerToSet } from './methods/stickers/add-sticker-to-set'
|
import { addStickerToSet } from './methods/stickers/add-sticker-to-set'
|
||||||
import { createStickerSet } from './methods/stickers/create-sticker-set'
|
import { createStickerSet } from './methods/stickers/create-sticker-set'
|
||||||
import { deleteStickerFromSet } from './methods/stickers/delete-sticker-from-set'
|
import { deleteStickerFromSet } from './methods/stickers/delete-sticker-from-set'
|
||||||
import { getCustomEmojis } from './methods/stickers/get-custom-emojis'
|
import { getCustomEmojis, getCustomEmojisFromMessages } from './methods/stickers/get-custom-emojis'
|
||||||
import { getInstalledStickers } from './methods/stickers/get-installed-stickers'
|
import { getInstalledStickers } from './methods/stickers/get-installed-stickers'
|
||||||
import { getStickerSet } from './methods/stickers/get-sticker-set'
|
import { getStickerSet } from './methods/stickers/get-sticker-set'
|
||||||
import { moveStickerInSet } from './methods/stickers/move-sticker-in-set'
|
import { moveStickerInSet } from './methods/stickers/move-sticker-in-set'
|
||||||
|
@ -287,6 +293,7 @@ import {
|
||||||
MessageEntity,
|
MessageEntity,
|
||||||
MessageMedia,
|
MessageMedia,
|
||||||
MessageReactions,
|
MessageReactions,
|
||||||
|
ParametersSkip2,
|
||||||
ParsedUpdate,
|
ParsedUpdate,
|
||||||
PeerReaction,
|
PeerReaction,
|
||||||
PeersIndex,
|
PeersIndex,
|
||||||
|
@ -547,6 +554,7 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
*
|
*
|
||||||
* @param params Parameters to be passed to {@link start}
|
* @param params Parameters to be passed to {@link start}
|
||||||
* @param then Function to be called after {@link start} returns
|
* @param then Function to be called after {@link start} returns
|
||||||
|
* @manual
|
||||||
*/
|
*/
|
||||||
run(params: Parameters<typeof start>[1], then?: (user: User) => void | Promise<void>): void
|
run(params: Parameters<typeof start>[1], then?: (user: User) => void | Promise<void>): void
|
||||||
/**
|
/**
|
||||||
|
@ -2713,7 +2721,7 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
*/
|
*/
|
||||||
getPrimaryInviteLink(chatId: InputPeerLike): Promise<ChatInviteLink>
|
getPrimaryInviteLink(chatId: InputPeerLike): Promise<ChatInviteLink>
|
||||||
/**
|
/**
|
||||||
* Approve or deny multiple join requests to a chat.
|
* Approve or decline multiple join requests to a chat.
|
||||||
* **Available**: 👤 users only
|
* **Available**: 👤 users only
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -2721,14 +2729,14 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
/** Chat/channel ID */
|
/** Chat/channel ID */
|
||||||
chatId: InputPeerLike
|
chatId: InputPeerLike
|
||||||
|
|
||||||
/** Whether to approve or deny the join requests */
|
/** Whether to approve or decline the join requests */
|
||||||
action: 'approve' | 'deny'
|
action: 'approve' | 'decline'
|
||||||
|
|
||||||
/** Invite link to target */
|
/** Invite link to target */
|
||||||
link?: string | ChatInviteLink
|
link?: string | ChatInviteLink
|
||||||
}): Promise<void>
|
}): Promise<void>
|
||||||
/**
|
/**
|
||||||
* Approve or deny join request to a chat.
|
* Approve or decline join request to a chat.
|
||||||
* **Available**: ✅ both users and bots
|
* **Available**: ✅ both users and bots
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -2737,8 +2745,8 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
chatId: InputPeerLike
|
chatId: InputPeerLike
|
||||||
/** User ID */
|
/** User ID */
|
||||||
user: InputPeerLike
|
user: InputPeerLike
|
||||||
/** Whether to approve or deny the join request */
|
/** Whether to approve or decline the join request */
|
||||||
action: 'approve' | 'deny'
|
action: 'approve' | 'decline'
|
||||||
}): Promise<void>
|
}): Promise<void>
|
||||||
/**
|
/**
|
||||||
* Iterate over users who have joined
|
* Iterate over users who have joined
|
||||||
|
@ -3215,6 +3223,16 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
offset?: GetReactionUsersOffset
|
offset?: GetReactionUsersOffset
|
||||||
},
|
},
|
||||||
): Promise<ArrayPaginated<PeerReaction, GetReactionUsersOffset>>
|
): Promise<ArrayPaginated<PeerReaction, GetReactionUsersOffset>>
|
||||||
|
/**
|
||||||
|
* For messages containing a reply, fetch the message that is being replied.
|
||||||
|
*
|
||||||
|
* Note that even if a message has {@link replyToMessageId},
|
||||||
|
* the message itself may have been deleted, in which case
|
||||||
|
* this method will also return `null`.
|
||||||
|
* **Available**: ✅ both users and bots
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
getReplyTo(message: Message): Promise<Message | null>
|
||||||
/**
|
/**
|
||||||
* Get scheduled messages in chat by their IDs
|
* Get scheduled messages in chat by their IDs
|
||||||
*
|
*
|
||||||
|
@ -3549,6 +3567,76 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
*/
|
*/
|
||||||
fromUser?: InputPeerLike
|
fromUser?: InputPeerLike
|
||||||
}): Promise<ArrayPaginated<Message, SearchMessagesOffset>>
|
}): Promise<ArrayPaginated<Message, SearchMessagesOffset>>
|
||||||
|
/** Send a text to the same chat (and topic, if applicable) as a given message */
|
||||||
|
answerText(message: Message, ...params: ParametersSkip2<typeof sendText>): ReturnType<typeof sendText>
|
||||||
|
/** Send a media to the same chat (and topic, if applicable) as a given message */
|
||||||
|
answerMedia(message: Message, ...params: ParametersSkip2<typeof sendMedia>): ReturnType<typeof sendMedia>
|
||||||
|
/** Send a media group to the same chat (and topic, if applicable) as a given message */
|
||||||
|
answerMediaGroup(
|
||||||
|
message: Message,
|
||||||
|
...params: ParametersSkip2<typeof sendMediaGroup>
|
||||||
|
): ReturnType<typeof sendMediaGroup>
|
||||||
|
/**
|
||||||
|
* Send a text comment to a given message.
|
||||||
|
*
|
||||||
|
* If this is a normal message (not a channel post),
|
||||||
|
* a simple reply will be sent.
|
||||||
|
*
|
||||||
|
* **Available**: ✅ both users and bots
|
||||||
|
*
|
||||||
|
* @throws MtArgumentError
|
||||||
|
* If this is a channel post which does not have comments section.
|
||||||
|
* To check if a post has comments, use {@link Message#replies}.hasComments
|
||||||
|
*/
|
||||||
|
commentText(message: Message, ...params: ParametersSkip2<typeof sendText>): ReturnType<typeof sendText>
|
||||||
|
/**
|
||||||
|
* Send a text comment to a given message.
|
||||||
|
*
|
||||||
|
* If this is a normal message (not a channel post),
|
||||||
|
* a simple reply will be sent.
|
||||||
|
*
|
||||||
|
* **Available**: ✅ both users and bots
|
||||||
|
*
|
||||||
|
* @throws MtArgumentError
|
||||||
|
* If this is a channel post which does not have comments section.
|
||||||
|
* To check if a post has comments, use {@link Message#replies}.hasComments
|
||||||
|
*/
|
||||||
|
commentMedia(message: Message, ...params: ParametersSkip2<typeof sendMedia>): ReturnType<typeof sendMedia>
|
||||||
|
/**
|
||||||
|
* Send a text comment to a given message.
|
||||||
|
*
|
||||||
|
* If this is a normal message (not a channel post),
|
||||||
|
* a simple reply will be sent.
|
||||||
|
*
|
||||||
|
* **Available**: ✅ both users and bots
|
||||||
|
*
|
||||||
|
* @throws MtArgumentError
|
||||||
|
* If this is a channel post which does not have comments section.
|
||||||
|
* To check if a post has comments, use {@link Message#replies}.hasComments
|
||||||
|
*/
|
||||||
|
commentMediaGroup(
|
||||||
|
message: Message,
|
||||||
|
...params: ParametersSkip2<typeof sendMediaGroup>
|
||||||
|
): ReturnType<typeof sendMediaGroup>
|
||||||
|
/**
|
||||||
|
* Copy a message group (i.e. send the same message group, but do not forward it).
|
||||||
|
*
|
||||||
|
* Note that all the provided messages must be in the same message group
|
||||||
|
* **Available**: ✅ both users and bots
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
sendCopyGroup(
|
||||||
|
params: SendCopyGroupParams &
|
||||||
|
(
|
||||||
|
| {
|
||||||
|
/** Source chat ID */
|
||||||
|
fromChatId: InputPeerLike
|
||||||
|
/** Message IDs to forward */
|
||||||
|
messages: number[]
|
||||||
|
}
|
||||||
|
| { messages: Message[] }
|
||||||
|
),
|
||||||
|
): Promise<Message[]>
|
||||||
/**
|
/**
|
||||||
* Copy a message (i.e. send the same message, but do not forward it).
|
* Copy a message (i.e. send the same message, but do not forward it).
|
||||||
*
|
*
|
||||||
|
@ -3556,10 +3644,8 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
* it will be copied simply as a text message,
|
* it will be copied simply as a text message,
|
||||||
* and if the message contains an invoice,
|
* and if the message contains an invoice,
|
||||||
* it can't be copied.
|
* it can't be copied.
|
||||||
*
|
|
||||||
* **Available**: ✅ both users and bots
|
* **Available**: ✅ both users and bots
|
||||||
*
|
*
|
||||||
* @param params
|
|
||||||
*/
|
*/
|
||||||
sendCopy(
|
sendCopy(
|
||||||
params: SendCopyParams &
|
params: SendCopyParams &
|
||||||
|
@ -3589,56 +3675,7 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
sendMediaGroup(
|
sendMediaGroup(
|
||||||
chatId: InputPeerLike,
|
chatId: InputPeerLike,
|
||||||
medias: (InputMediaLike | string)[],
|
medias: (InputMediaLike | string)[],
|
||||||
params?: {
|
params?: CommonSendParams & {
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to throw an error if {@link replyTo}
|
|
||||||
* message does not exist.
|
|
||||||
*
|
|
||||||
* If that message was not found, `NotFoundError` is thrown,
|
|
||||||
* with `text` set to `MESSAGE_NOT_FOUND`.
|
|
||||||
*
|
|
||||||
* Incurs an additional request, so only use when really needed.
|
|
||||||
*
|
|
||||||
* Defaults to `false`
|
|
||||||
*/
|
|
||||||
mustReply?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message to comment to. Either a message object or message ID.
|
|
||||||
*
|
|
||||||
* This overwrites `replyTo` if it was passed
|
|
||||||
*/
|
|
||||||
commentTo?: number | Message
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse mode to use to parse entities before sending
|
|
||||||
* the message. Defaults to current default parse mode (if any).
|
|
||||||
*
|
|
||||||
* Passing `null` will explicitly disable formatting.
|
|
||||||
*/
|
|
||||||
parseMode?: string | null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to send this message silently.
|
|
||||||
*/
|
|
||||||
silent?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If set, the message will be scheduled to this date.
|
|
||||||
* When passing a number, a UNIX time in ms is expected.
|
|
||||||
*
|
|
||||||
* You can also pass `0x7FFFFFFE`, this will send the message
|
|
||||||
* once the peer is online
|
|
||||||
*/
|
|
||||||
schedule?: Date | number
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function that will be called after some part has been uploaded.
|
* Function that will be called after some part has been uploaded.
|
||||||
* Only used when a file that requires uploading is passed,
|
* Only used when a file that requires uploading is passed,
|
||||||
|
@ -3649,26 +3686,6 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
* @param total Total file size
|
* @param total Total file size
|
||||||
*/
|
*/
|
||||||
progressCallback?: (index: number, uploaded: number, total: number) => void
|
progressCallback?: (index: number, uploaded: number, total: number) => void
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to clear draft after sending this message.
|
|
||||||
*
|
|
||||||
* Defaults to `false`
|
|
||||||
*/
|
|
||||||
clearDraft?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to disallow further forwards of this message.
|
|
||||||
*
|
|
||||||
* Only for bots, works even if the target chat does not
|
|
||||||
* have content protection.
|
|
||||||
*/
|
|
||||||
forbidForwards?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Peer to use when sending the message.
|
|
||||||
*/
|
|
||||||
sendAs?: InputPeerLike
|
|
||||||
},
|
},
|
||||||
): Promise<Message[]>
|
): Promise<Message[]>
|
||||||
/**
|
/**
|
||||||
|
@ -3687,7 +3704,13 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
sendMedia(
|
sendMedia(
|
||||||
chatId: InputPeerLike,
|
chatId: InputPeerLike,
|
||||||
media: InputMediaLike | string,
|
media: InputMediaLike | string,
|
||||||
params?: {
|
params?: CommonSendParams & {
|
||||||
|
/**
|
||||||
|
* For bots: inline or reply markup or an instruction
|
||||||
|
* to hide a reply keyboard or to force a reply.
|
||||||
|
*/
|
||||||
|
replyMarkup?: ReplyMarkup
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override caption for `media`.
|
* Override caption for `media`.
|
||||||
*
|
*
|
||||||
|
@ -3704,61 +3727,6 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
*/
|
*/
|
||||||
entities?: tl.TypeMessageEntity[]
|
entities?: tl.TypeMessageEntity[]
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to throw an error if {@link replyTo}
|
|
||||||
* message does not exist.
|
|
||||||
*
|
|
||||||
* If that message was not found, `NotFoundError` is thrown,
|
|
||||||
* with `text` set to `MESSAGE_NOT_FOUND`.
|
|
||||||
*
|
|
||||||
* Incurs an additional request, so only use when really needed.
|
|
||||||
*
|
|
||||||
* Defaults to `false`
|
|
||||||
*/
|
|
||||||
mustReply?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message to comment to. Either a message object or message ID.
|
|
||||||
*
|
|
||||||
* This overwrites `replyTo` if it was passed
|
|
||||||
*/
|
|
||||||
commentTo?: number | Message
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse mode to use to parse entities before sending
|
|
||||||
* the message. Defaults to current default parse mode (if any).
|
|
||||||
*
|
|
||||||
* Passing `null` will explicitly disable formatting.
|
|
||||||
*/
|
|
||||||
parseMode?: string | null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to send this message silently.
|
|
||||||
*/
|
|
||||||
silent?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If set, the message will be scheduled to this date.
|
|
||||||
* When passing a number, a UNIX time in ms is expected.
|
|
||||||
*
|
|
||||||
* You can also pass `0x7FFFFFFE`, this will send the message
|
|
||||||
* once the peer is online
|
|
||||||
*/
|
|
||||||
schedule?: Date | number
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For bots: inline or reply markup or an instruction
|
|
||||||
* to hide a reply keyboard or to force a reply.
|
|
||||||
*/
|
|
||||||
replyMarkup?: ReplyMarkup
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function that will be called after some part has been uploaded.
|
* Function that will be called after some part has been uploaded.
|
||||||
* Only used when a file that requires uploading is passed,
|
* Only used when a file that requires uploading is passed,
|
||||||
|
@ -3768,32 +3736,6 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
* @param total Total file size
|
* @param total Total file size
|
||||||
*/
|
*/
|
||||||
progressCallback?: (uploaded: number, total: number) => void
|
progressCallback?: (uploaded: number, total: number) => void
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to clear draft after sending this message.
|
|
||||||
*
|
|
||||||
* Defaults to `false`
|
|
||||||
*/
|
|
||||||
clearDraft?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to disallow further forwards of this message.
|
|
||||||
*
|
|
||||||
* Only for bots, works even if the target chat does not
|
|
||||||
* have content protection.
|
|
||||||
*/
|
|
||||||
forbidForwards?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Peer to use when sending the message.
|
|
||||||
*/
|
|
||||||
sendAs?: InputPeerLike
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to dispatch the returned message
|
|
||||||
* to the client's update handler.
|
|
||||||
*/
|
|
||||||
shouldDispatch?: true
|
|
||||||
},
|
},
|
||||||
): Promise<Message>
|
): Promise<Message>
|
||||||
/**
|
/**
|
||||||
|
@ -3817,6 +3759,15 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
shouldDispatch?: true
|
shouldDispatch?: true
|
||||||
},
|
},
|
||||||
): Promise<Message>
|
): Promise<Message>
|
||||||
|
/** Send a text in reply to a given message */
|
||||||
|
replyText(message: Message, ...params: ParametersSkip2<typeof sendText>): ReturnType<typeof sendText>
|
||||||
|
/** Send a media in reply to a given message */
|
||||||
|
replyMedia(message: Message, ...params: ParametersSkip2<typeof sendMedia>): ReturnType<typeof sendMedia>
|
||||||
|
/** Send a media group in reply to a given message */
|
||||||
|
replyMediaGroup(
|
||||||
|
message: Message,
|
||||||
|
...params: ParametersSkip2<typeof sendMediaGroup>
|
||||||
|
): ReturnType<typeof sendMediaGroup>
|
||||||
/**
|
/**
|
||||||
* Send previously scheduled message(s)
|
* Send previously scheduled message(s)
|
||||||
*
|
*
|
||||||
|
@ -3842,41 +3793,12 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
sendText(
|
sendText(
|
||||||
chatId: InputPeerLike,
|
chatId: InputPeerLike,
|
||||||
text: string | FormattedString<string>,
|
text: string | FormattedString<string>,
|
||||||
params?: {
|
params?: CommonSendParams & {
|
||||||
/**
|
/**
|
||||||
* Message to reply to. Either a message object or message ID.
|
* For bots: inline or reply markup or an instruction
|
||||||
*
|
* to hide a reply keyboard or to force a reply.
|
||||||
* For forums - can also be an ID of the topic (i.e. its top message ID)
|
|
||||||
*/
|
*/
|
||||||
replyTo?: number | Message
|
replyMarkup?: ReplyMarkup
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to throw an error if {@link replyTo}
|
|
||||||
* message does not exist.
|
|
||||||
*
|
|
||||||
* If that message was not found, `NotFoundError` is thrown,
|
|
||||||
* with `text` set to `MESSAGE_NOT_FOUND`.
|
|
||||||
*
|
|
||||||
* Incurs an additional request, so only use when really needed.
|
|
||||||
*
|
|
||||||
* Defaults to `false`
|
|
||||||
*/
|
|
||||||
mustReply?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message to comment to. Either a message object or message ID.
|
|
||||||
*
|
|
||||||
* This overwrites `replyTo` if it was passed
|
|
||||||
*/
|
|
||||||
commentTo?: number | Message
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse mode to use to parse entities before sending
|
|
||||||
* the message. Defaults to current default parse mode (if any).
|
|
||||||
*
|
|
||||||
* Passing `null` will explicitly disable formatting.
|
|
||||||
*/
|
|
||||||
parseMode?: string | null
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of formatting entities to use instead of parsing via a
|
* List of formatting entities to use instead of parsing via a
|
||||||
|
@ -3890,52 +3812,6 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
* Whether to disable links preview in this message
|
* Whether to disable links preview in this message
|
||||||
*/
|
*/
|
||||||
disableWebPreview?: boolean
|
disableWebPreview?: boolean
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to send this message silently.
|
|
||||||
*/
|
|
||||||
silent?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If set, the message will be scheduled to this date.
|
|
||||||
* When passing a number, a UNIX time in ms is expected.
|
|
||||||
*
|
|
||||||
* You can also pass `0x7FFFFFFE`, this will send the message
|
|
||||||
* once the peer is online
|
|
||||||
*/
|
|
||||||
schedule?: Date | number
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For bots: inline or reply markup or an instruction
|
|
||||||
* to hide a reply keyboard or to force a reply.
|
|
||||||
*/
|
|
||||||
replyMarkup?: ReplyMarkup
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to clear draft after sending this message.
|
|
||||||
*
|
|
||||||
* Defaults to `false`
|
|
||||||
*/
|
|
||||||
clearDraft?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to disallow further forwards of this message.
|
|
||||||
*
|
|
||||||
* Only for bots, works even if the target chat does not
|
|
||||||
* have content protection.
|
|
||||||
*/
|
|
||||||
forbidForwards?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Peer to use when sending the message.
|
|
||||||
*/
|
|
||||||
sendAs?: InputPeerLike
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to dispatch the returned message
|
|
||||||
* to the client's update handler.
|
|
||||||
*/
|
|
||||||
shouldDispatch?: true
|
|
||||||
},
|
},
|
||||||
): Promise<Message>
|
): Promise<Message>
|
||||||
/**
|
/**
|
||||||
|
@ -4279,6 +4155,12 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
* @param ids IDs of the stickers (as defined in {@link MessageEntity.emojiId})
|
* @param ids IDs of the stickers (as defined in {@link MessageEntity.emojiId})
|
||||||
*/
|
*/
|
||||||
getCustomEmojis(ids: tl.Long[]): Promise<Sticker[]>
|
getCustomEmojis(ids: tl.Long[]): Promise<Sticker[]>
|
||||||
|
/**
|
||||||
|
* Given one or more messages, extract all unique custom emojis from it and fetch them
|
||||||
|
* **Available**: ✅ both users and bots
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
getCustomEmojisFromMessages(messages: MaybeArray<Message>): Promise<Sticker[]>
|
||||||
/**
|
/**
|
||||||
* Get a list of all installed sticker packs
|
* Get a list of all installed sticker packs
|
||||||
*
|
*
|
||||||
|
@ -5259,6 +5141,11 @@ export class TelegramClient extends BaseTelegramClient {
|
||||||
} else {
|
} else {
|
||||||
this.start = start.bind(null, this)
|
this.start = start.bind(null, this)
|
||||||
}
|
}
|
||||||
|
this.run = (params, then) => {
|
||||||
|
this.start(params)
|
||||||
|
.then(then)
|
||||||
|
.catch((err) => this._emitError(err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
getAuthState = getAuthState.bind(null, this)
|
getAuthState = getAuthState.bind(null, this)
|
||||||
_onAuthorization = _onAuthorization.bind(null, this)
|
_onAuthorization = _onAuthorization.bind(null, this)
|
||||||
|
@ -5267,7 +5154,6 @@ export class TelegramClient extends BaseTelegramClient {
|
||||||
logOut = logOut.bind(null, this)
|
logOut = logOut.bind(null, this)
|
||||||
recoverPassword = recoverPassword.bind(null, this)
|
recoverPassword = recoverPassword.bind(null, this)
|
||||||
resendCode = resendCode.bind(null, this)
|
resendCode = resendCode.bind(null, this)
|
||||||
run = run.bind(null, this)
|
|
||||||
sendCode = sendCode.bind(null, this)
|
sendCode = sendCode.bind(null, this)
|
||||||
sendRecoveryCode = sendRecoveryCode.bind(null, this)
|
sendRecoveryCode = sendRecoveryCode.bind(null, this)
|
||||||
signInBot = signInBot.bind(null, this)
|
signInBot = signInBot.bind(null, this)
|
||||||
|
@ -5396,6 +5282,7 @@ export class TelegramClient extends BaseTelegramClient {
|
||||||
getMessagesUnsafe = getMessagesUnsafe.bind(null, this)
|
getMessagesUnsafe = getMessagesUnsafe.bind(null, this)
|
||||||
getMessages = getMessages.bind(null, this)
|
getMessages = getMessages.bind(null, this)
|
||||||
getReactionUsers = getReactionUsers.bind(null, this)
|
getReactionUsers = getReactionUsers.bind(null, this)
|
||||||
|
getReplyTo = getReplyTo.bind(null, this)
|
||||||
getScheduledMessages = getScheduledMessages.bind(null, this)
|
getScheduledMessages = getScheduledMessages.bind(null, this)
|
||||||
iterHistory = iterHistory.bind(null, this)
|
iterHistory = iterHistory.bind(null, this)
|
||||||
iterReactionUsers = iterReactionUsers.bind(null, this)
|
iterReactionUsers = iterReactionUsers.bind(null, this)
|
||||||
|
@ -5407,10 +5294,20 @@ export class TelegramClient extends BaseTelegramClient {
|
||||||
readReactions = readReactions.bind(null, this)
|
readReactions = readReactions.bind(null, this)
|
||||||
searchGlobal = searchGlobal.bind(null, this)
|
searchGlobal = searchGlobal.bind(null, this)
|
||||||
searchMessages = searchMessages.bind(null, this)
|
searchMessages = searchMessages.bind(null, this)
|
||||||
|
answerText = answerText.bind(null, this)
|
||||||
|
answerMedia = answerMedia.bind(null, this)
|
||||||
|
answerMediaGroup = answerMediaGroup.bind(null, this)
|
||||||
|
commentText = commentText.bind(null, this)
|
||||||
|
commentMedia = commentMedia.bind(null, this)
|
||||||
|
commentMediaGroup = commentMediaGroup.bind(null, this)
|
||||||
|
sendCopyGroup = sendCopyGroup.bind(null, this)
|
||||||
sendCopy = sendCopy.bind(null, this)
|
sendCopy = sendCopy.bind(null, this)
|
||||||
sendMediaGroup = sendMediaGroup.bind(null, this)
|
sendMediaGroup = sendMediaGroup.bind(null, this)
|
||||||
sendMedia = sendMedia.bind(null, this)
|
sendMedia = sendMedia.bind(null, this)
|
||||||
sendReaction = sendReaction.bind(null, this)
|
sendReaction = sendReaction.bind(null, this)
|
||||||
|
replyText = replyText.bind(null, this)
|
||||||
|
replyMedia = replyMedia.bind(null, this)
|
||||||
|
replyMediaGroup = replyMediaGroup.bind(null, this)
|
||||||
sendScheduled = sendScheduled.bind(null, this)
|
sendScheduled = sendScheduled.bind(null, this)
|
||||||
sendText = sendText.bind(null, this)
|
sendText = sendText.bind(null, this)
|
||||||
sendTyping = sendTyping.bind(null, this)
|
sendTyping = sendTyping.bind(null, this)
|
||||||
|
@ -5436,6 +5333,7 @@ export class TelegramClient extends BaseTelegramClient {
|
||||||
createStickerSet = createStickerSet.bind(null, this)
|
createStickerSet = createStickerSet.bind(null, this)
|
||||||
deleteStickerFromSet = deleteStickerFromSet.bind(null, this)
|
deleteStickerFromSet = deleteStickerFromSet.bind(null, this)
|
||||||
getCustomEmojis = getCustomEmojis.bind(null, this)
|
getCustomEmojis = getCustomEmojis.bind(null, this)
|
||||||
|
getCustomEmojisFromMessages = getCustomEmojisFromMessages.bind(null, this)
|
||||||
getInstalledStickers = getInstalledStickers.bind(null, this)
|
getInstalledStickers = getInstalledStickers.bind(null, this)
|
||||||
getStickerSet = getStickerSet.bind(null, this)
|
getStickerSet = getStickerSet.bind(null, this)
|
||||||
moveStickerInSet = moveStickerInSet.bind(null, this)
|
moveStickerInSet = moveStickerInSet.bind(null, this)
|
||||||
|
|
|
@ -53,6 +53,7 @@ import {
|
||||||
MessageEntity,
|
MessageEntity,
|
||||||
MessageMedia,
|
MessageMedia,
|
||||||
MessageReactions,
|
MessageReactions,
|
||||||
|
ParametersSkip2,
|
||||||
ParsedUpdate,
|
ParsedUpdate,
|
||||||
PeerReaction,
|
PeerReaction,
|
||||||
PeersIndex,
|
PeersIndex,
|
||||||
|
|
|
@ -45,4 +45,9 @@ function _initializeClient(this: TelegramClient, opts: TelegramClientOptions) {
|
||||||
} else {
|
} else {
|
||||||
this.start = start.bind(null, this)
|
this.start = start.bind(null, this)
|
||||||
}
|
}
|
||||||
|
this.run = (params, then) => {
|
||||||
|
this.start(params)
|
||||||
|
.then(then)
|
||||||
|
.catch((err) => this._emitError(err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { start } from './start'
|
||||||
*
|
*
|
||||||
* @param params Parameters to be passed to {@link start}
|
* @param params Parameters to be passed to {@link start}
|
||||||
* @param then Function to be called after {@link start} returns
|
* @param then Function to be called after {@link start} returns
|
||||||
|
* @manual
|
||||||
*/
|
*/
|
||||||
export function run(
|
export function run(
|
||||||
client: BaseTelegramClient,
|
client: BaseTelegramClient,
|
||||||
|
|
|
@ -4,7 +4,7 @@ import type { ChatInviteLink, InputPeerLike } from '../../types'
|
||||||
import { resolvePeer } from '../users/resolve-peer'
|
import { resolvePeer } from '../users/resolve-peer'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Approve or deny multiple join requests to a chat.
|
* Approve or decline multiple join requests to a chat.
|
||||||
*/
|
*/
|
||||||
export async function hideAllJoinRequests(
|
export async function hideAllJoinRequests(
|
||||||
client: BaseTelegramClient,
|
client: BaseTelegramClient,
|
||||||
|
@ -12,8 +12,8 @@ export async function hideAllJoinRequests(
|
||||||
/** Chat/channel ID */
|
/** Chat/channel ID */
|
||||||
chatId: InputPeerLike
|
chatId: InputPeerLike
|
||||||
|
|
||||||
/** Whether to approve or deny the join requests */
|
/** Whether to approve or decline the join requests */
|
||||||
action: 'approve' | 'deny'
|
action: 'approve' | 'decline'
|
||||||
|
|
||||||
/** Invite link to target */
|
/** Invite link to target */
|
||||||
link?: string | ChatInviteLink
|
link?: string | ChatInviteLink
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { normalizeToInputUser } from '../../utils/peer-utils'
|
||||||
import { resolvePeer } from '../users/resolve-peer'
|
import { resolvePeer } from '../users/resolve-peer'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Approve or deny join request to a chat.
|
* Approve or decline join request to a chat.
|
||||||
*/
|
*/
|
||||||
export async function hideJoinRequest(
|
export async function hideJoinRequest(
|
||||||
client: BaseTelegramClient,
|
client: BaseTelegramClient,
|
||||||
|
@ -14,8 +14,8 @@ export async function hideJoinRequest(
|
||||||
chatId: InputPeerLike
|
chatId: InputPeerLike
|
||||||
/** User ID */
|
/** User ID */
|
||||||
user: InputPeerLike
|
user: InputPeerLike
|
||||||
/** Whether to approve or deny the join request */
|
/** Whether to approve or decline the join request */
|
||||||
action: 'approve' | 'deny'
|
action: 'approve' | 'decline'
|
||||||
},
|
},
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { chatId, user, action } = params
|
const { chatId, user, action } = params
|
||||||
|
|
28
packages/client/src/methods/messages/get-reply-to.ts
Normal file
28
packages/client/src/methods/messages/get-reply-to.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { BaseTelegramClient } from '@mtcute/core'
|
||||||
|
|
||||||
|
import { Message } from '../../types/messages'
|
||||||
|
import { getMessages } from './get-messages'
|
||||||
|
import { getMessagesUnsafe } from './get-messages-unsafe'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For messages containing a reply, fetch the message that is being replied.
|
||||||
|
*
|
||||||
|
* Note that even if a message has {@link replyToMessageId},
|
||||||
|
* the message itself may have been deleted, in which case
|
||||||
|
* this method will also return `null`.
|
||||||
|
*/
|
||||||
|
export async function getReplyTo(client: BaseTelegramClient, message: Message): Promise<Message | null> {
|
||||||
|
if (!message.replyToMessageId) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.raw.peerId._ === 'peerChannel') {
|
||||||
|
const [msg] = await getMessages(client, message.chat.inputPeer, message.id, true)
|
||||||
|
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
const [msg] = await getMessagesUnsafe(client, message.id, true)
|
||||||
|
|
||||||
|
return msg
|
||||||
|
}
|
55
packages/client/src/methods/messages/send-answer.ts
Normal file
55
packages/client/src/methods/messages/send-answer.ts
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import { BaseTelegramClient } from '@mtcute/core'
|
||||||
|
|
||||||
|
import { Message } from '../../types/messages/message'
|
||||||
|
import { ParametersSkip2 } from '../../types/utils'
|
||||||
|
import { sendMedia } from './send-media'
|
||||||
|
import { sendMediaGroup } from './send-media-group'
|
||||||
|
import { sendText } from './send-text'
|
||||||
|
|
||||||
|
/** Send a text to the same chat (and topic, if applicable) as a given message */
|
||||||
|
export function answerText(
|
||||||
|
client: BaseTelegramClient,
|
||||||
|
message: Message,
|
||||||
|
...params: ParametersSkip2<typeof sendText>
|
||||||
|
): ReturnType<typeof sendText> {
|
||||||
|
if (!message.isTopicMessage || !message.replyToThreadId) {
|
||||||
|
return sendText(client, message.chat.inputPeer, ...params)
|
||||||
|
}
|
||||||
|
|
||||||
|
const [text, params_ = {}] = params
|
||||||
|
params_.replyTo = message.replyToThreadId
|
||||||
|
|
||||||
|
return sendText(client, message.chat.inputPeer, text, params_)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Send a media to the same chat (and topic, if applicable) as a given message */
|
||||||
|
export function answerMedia(
|
||||||
|
client: BaseTelegramClient,
|
||||||
|
message: Message,
|
||||||
|
...params: ParametersSkip2<typeof sendMedia>
|
||||||
|
): ReturnType<typeof sendMedia> {
|
||||||
|
if (!message.isTopicMessage || !message.replyToThreadId) {
|
||||||
|
return sendMedia(client, message.chat.inputPeer, ...params)
|
||||||
|
}
|
||||||
|
|
||||||
|
const [media, params_ = {}] = params
|
||||||
|
params_.replyTo = message.replyToThreadId
|
||||||
|
|
||||||
|
return sendMedia(client, message.chat.inputPeer, media, params_)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Send a media group to the same chat (and topic, if applicable) as a given message */
|
||||||
|
export function answerMediaGroup(
|
||||||
|
client: BaseTelegramClient,
|
||||||
|
message: Message,
|
||||||
|
...params: ParametersSkip2<typeof sendMediaGroup>
|
||||||
|
): ReturnType<typeof sendMediaGroup> {
|
||||||
|
if (!message.isTopicMessage || !message.replyToThreadId) {
|
||||||
|
return sendMediaGroup(client, message.chat.inputPeer, ...params)
|
||||||
|
}
|
||||||
|
|
||||||
|
const [media, params_ = {}] = params
|
||||||
|
params_.replyTo = message.replyToThreadId
|
||||||
|
|
||||||
|
return sendMediaGroup(client, message.chat.inputPeer, media, params_)
|
||||||
|
}
|
95
packages/client/src/methods/messages/send-comment.ts
Normal file
95
packages/client/src/methods/messages/send-comment.ts
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
import { BaseTelegramClient, MtArgumentError } from '@mtcute/core'
|
||||||
|
|
||||||
|
import { Message } from '../../types/messages/message'
|
||||||
|
import { ParametersSkip2 } from '../../types/utils'
|
||||||
|
import { sendMedia } from './send-media'
|
||||||
|
import { sendMediaGroup } from './send-media-group'
|
||||||
|
import { replyMedia, replyMediaGroup, replyText } from './send-reply'
|
||||||
|
import { sendText } from './send-text'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a text comment to a given message.
|
||||||
|
*
|
||||||
|
* If this is a normal message (not a channel post),
|
||||||
|
* a simple reply will be sent.
|
||||||
|
*
|
||||||
|
* @throws MtArgumentError
|
||||||
|
* If this is a channel post which does not have comments section.
|
||||||
|
* To check if a post has comments, use {@link Message#replies}.hasComments
|
||||||
|
*/
|
||||||
|
export function commentText(
|
||||||
|
client: BaseTelegramClient,
|
||||||
|
message: Message,
|
||||||
|
...params: ParametersSkip2<typeof sendText>
|
||||||
|
): ReturnType<typeof sendText> {
|
||||||
|
if (message.chat.chatType !== 'channel') {
|
||||||
|
return replyText(client, message, ...params)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!message.replies || !message.replies.hasComments) {
|
||||||
|
throw new MtArgumentError('This message does not have comments section')
|
||||||
|
}
|
||||||
|
|
||||||
|
const [text, params_ = {}] = params
|
||||||
|
params_.commentTo = message.id
|
||||||
|
|
||||||
|
return sendText(client, message.chat.inputPeer, text, params_)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a text comment to a given message.
|
||||||
|
*
|
||||||
|
* If this is a normal message (not a channel post),
|
||||||
|
* a simple reply will be sent.
|
||||||
|
*
|
||||||
|
* @throws MtArgumentError
|
||||||
|
* If this is a channel post which does not have comments section.
|
||||||
|
* To check if a post has comments, use {@link Message#replies}.hasComments
|
||||||
|
*/
|
||||||
|
export function commentMedia(
|
||||||
|
client: BaseTelegramClient,
|
||||||
|
message: Message,
|
||||||
|
...params: ParametersSkip2<typeof sendMedia>
|
||||||
|
): ReturnType<typeof sendMedia> {
|
||||||
|
if (message.chat.chatType !== 'channel') {
|
||||||
|
return replyMedia(client, message, ...params)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!message.replies || !message.replies.hasComments) {
|
||||||
|
throw new MtArgumentError('This message does not have comments section')
|
||||||
|
}
|
||||||
|
|
||||||
|
const [media, params_ = {}] = params
|
||||||
|
params_.commentTo = message.id
|
||||||
|
|
||||||
|
return sendMedia(client, message.chat.inputPeer, media, params_)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a text comment to a given message.
|
||||||
|
*
|
||||||
|
* If this is a normal message (not a channel post),
|
||||||
|
* a simple reply will be sent.
|
||||||
|
*
|
||||||
|
* @throws MtArgumentError
|
||||||
|
* If this is a channel post which does not have comments section.
|
||||||
|
* To check if a post has comments, use {@link Message#replies}.hasComments
|
||||||
|
*/
|
||||||
|
export function commentMediaGroup(
|
||||||
|
client: BaseTelegramClient,
|
||||||
|
message: Message,
|
||||||
|
...params: ParametersSkip2<typeof sendMediaGroup>
|
||||||
|
): ReturnType<typeof sendMediaGroup> {
|
||||||
|
if (message.chat.chatType !== 'channel') {
|
||||||
|
return replyMediaGroup(client, message, ...params)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!message.replies || !message.replies.hasComments) {
|
||||||
|
throw new MtArgumentError('This message does not have comments section')
|
||||||
|
}
|
||||||
|
|
||||||
|
const [media, params_ = {}] = params
|
||||||
|
params_.commentTo = message.id
|
||||||
|
|
||||||
|
return sendMediaGroup(client, message.chat.inputPeer, media, params_)
|
||||||
|
}
|
119
packages/client/src/methods/messages/send-common.ts
Normal file
119
packages/client/src/methods/messages/send-common.ts
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
import { BaseTelegramClient, getMarkedPeerId, MtArgumentError } from '@mtcute/core'
|
||||||
|
|
||||||
|
import { MtMessageNotFoundError } from '../../types/errors'
|
||||||
|
import { Message } from '../../types/messages/message'
|
||||||
|
import { InputPeerLike } from '../../types/peers'
|
||||||
|
import { normalizeMessageId } from '../../utils'
|
||||||
|
import { resolvePeer } from '../users/resolve-peer'
|
||||||
|
import { _getDiscussionMessage } from './get-discussion-message'
|
||||||
|
import { getMessages } from './get-messages'
|
||||||
|
|
||||||
|
// @exported
|
||||||
|
export interface CommonSendParams {
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to throw an error if {@link replyTo}
|
||||||
|
* message does not exist.
|
||||||
|
*
|
||||||
|
* If that message was not found, `NotFoundError` is thrown,
|
||||||
|
* with `text` set to `MESSAGE_NOT_FOUND`.
|
||||||
|
*
|
||||||
|
* Incurs an additional request, so only use when really needed.
|
||||||
|
*
|
||||||
|
* Defaults to `false`
|
||||||
|
*/
|
||||||
|
mustReply?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message to comment to. Either a message object or message ID.
|
||||||
|
*
|
||||||
|
* This overwrites `replyTo` if it was passed
|
||||||
|
*/
|
||||||
|
commentTo?: number | Message
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse mode to use to parse entities before sending
|
||||||
|
* the message. Defaults to current default parse mode (if any).
|
||||||
|
*
|
||||||
|
* Passing `null` will explicitly disable formatting.
|
||||||
|
*/
|
||||||
|
parseMode?: string | null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to send this message silently.
|
||||||
|
*/
|
||||||
|
silent?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set, the message will be scheduled to this date.
|
||||||
|
* When passing a number, a UNIX time in ms is expected.
|
||||||
|
*
|
||||||
|
* You can also pass `0x7FFFFFFE`, this will send the message
|
||||||
|
* once the peer is online
|
||||||
|
*/
|
||||||
|
schedule?: Date | number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to clear draft after sending this message.
|
||||||
|
*
|
||||||
|
* Defaults to `false`
|
||||||
|
*/
|
||||||
|
clearDraft?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to disallow further forwards of this message.
|
||||||
|
*
|
||||||
|
* Only for bots, works even if the target chat does not
|
||||||
|
* have content protection.
|
||||||
|
*/
|
||||||
|
forbidForwards?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Peer to use when sending the message.
|
||||||
|
*/
|
||||||
|
sendAs?: InputPeerLike
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to dispatch the returned message
|
||||||
|
* to the client's update handler.
|
||||||
|
*/
|
||||||
|
shouldDispatch?: true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* @noemit
|
||||||
|
*/
|
||||||
|
export async function _processCommonSendParameters(
|
||||||
|
client: BaseTelegramClient,
|
||||||
|
chatId: InputPeerLike,
|
||||||
|
params: CommonSendParams,
|
||||||
|
) {
|
||||||
|
let peer = await resolvePeer(client, chatId)
|
||||||
|
|
||||||
|
let replyTo = normalizeMessageId(params.replyTo)
|
||||||
|
|
||||||
|
if (params.commentTo) {
|
||||||
|
[peer, replyTo] = await _getDiscussionMessage(client, peer, normalizeMessageId(params.commentTo)!)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.mustReply) {
|
||||||
|
if (!replyTo) {
|
||||||
|
throw new MtArgumentError('mustReply used, but replyTo was not passed')
|
||||||
|
}
|
||||||
|
|
||||||
|
const msg = await getMessages(client, peer, replyTo)
|
||||||
|
|
||||||
|
if (!msg) {
|
||||||
|
throw new MtMessageNotFoundError(getMarkedPeerId(peer), replyTo, 'to reply to')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { peer, replyTo }
|
||||||
|
}
|
70
packages/client/src/methods/messages/send-copy-group.ts
Normal file
70
packages/client/src/methods/messages/send-copy-group.ts
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import { BaseTelegramClient, MtArgumentError, tl } from '@mtcute/core'
|
||||||
|
import { isPresent } from '@mtcute/core/utils'
|
||||||
|
|
||||||
|
import { Message } from '../../types/messages/message'
|
||||||
|
import { InputPeerLike } from '../../types/peers'
|
||||||
|
import { resolvePeer } from '../users/resolve-peer'
|
||||||
|
import { getMessages } from './get-messages'
|
||||||
|
import { CommonSendParams } from './send-common'
|
||||||
|
import { sendMediaGroup } from './send-media-group'
|
||||||
|
|
||||||
|
// @exported
|
||||||
|
export interface SendCopyGroupParams extends CommonSendParams {
|
||||||
|
/** Destination chat ID */
|
||||||
|
toChatId: InputPeerLike
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy a message group (i.e. send the same message group, but do not forward it).
|
||||||
|
*
|
||||||
|
* Note that all the provided messages must be in the same message group
|
||||||
|
*/
|
||||||
|
export async function sendCopyGroup(
|
||||||
|
client: BaseTelegramClient,
|
||||||
|
params: SendCopyGroupParams &
|
||||||
|
(
|
||||||
|
| {
|
||||||
|
/** Source chat ID */
|
||||||
|
fromChatId: InputPeerLike
|
||||||
|
/** Message IDs to forward */
|
||||||
|
messages: number[]
|
||||||
|
}
|
||||||
|
| { messages: Message[] }
|
||||||
|
),
|
||||||
|
): Promise<Message[]> {
|
||||||
|
const { toChatId, ...rest } = params
|
||||||
|
|
||||||
|
let msgs
|
||||||
|
|
||||||
|
if ('fromChatId' in params) {
|
||||||
|
const fromPeer = await resolvePeer(client, params.fromChatId)
|
||||||
|
|
||||||
|
msgs = await getMessages(client, fromPeer, params.messages).then((r) => r.filter(isPresent))
|
||||||
|
} else {
|
||||||
|
msgs = params.messages
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageGroupId = msgs[0].groupedId!
|
||||||
|
|
||||||
|
for (let i = 1; i < msgs.length; i++) {
|
||||||
|
if (!msgs[i].groupedId?.eq(messageGroupId) || !msgs[i].media) {
|
||||||
|
throw new MtArgumentError('All messages must be in the same message group')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sendMediaGroup(
|
||||||
|
client,
|
||||||
|
toChatId,
|
||||||
|
msgs.map((msg) => {
|
||||||
|
const raw = msg.raw as tl.RawMessage
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'auto',
|
||||||
|
file: msg.media!.inputMedia,
|
||||||
|
caption: raw.message,
|
||||||
|
entities: raw.entities,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
rest,
|
||||||
|
)
|
||||||
|
}
|
|
@ -3,28 +3,15 @@ import { BaseTelegramClient, getMarkedPeerId, MtArgumentError, tl } from '@mtcut
|
||||||
import { FormattedString, InputPeerLike, Message, MtMessageNotFoundError, ReplyMarkup } from '../../types'
|
import { FormattedString, InputPeerLike, Message, MtMessageNotFoundError, ReplyMarkup } from '../../types'
|
||||||
import { resolvePeer } from '../users/resolve-peer'
|
import { resolvePeer } from '../users/resolve-peer'
|
||||||
import { getMessages } from './get-messages'
|
import { getMessages } from './get-messages'
|
||||||
|
import { CommonSendParams } from './send-common'
|
||||||
import { sendMedia } from './send-media'
|
import { sendMedia } from './send-media'
|
||||||
import { sendText } from './send-text'
|
import { sendText } from './send-text'
|
||||||
|
|
||||||
// @exported
|
// @exported
|
||||||
export interface SendCopyParams {
|
export interface SendCopyParams extends CommonSendParams {
|
||||||
/** Target chat ID */
|
/** Target chat ID */
|
||||||
toChatId: InputPeerLike
|
toChatId: InputPeerLike
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to send this message silently.
|
|
||||||
*/
|
|
||||||
silent?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If set, the message will be scheduled to this date.
|
|
||||||
* When passing a number, a UNIX time in ms is expected.
|
|
||||||
*
|
|
||||||
* You can also pass `0x7FFFFFFE`, this will send the message
|
|
||||||
* once the peer is online
|
|
||||||
*/
|
|
||||||
schedule?: Date | number
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* New message caption (only used for media)
|
* New message caption (only used for media)
|
||||||
*/
|
*/
|
||||||
|
@ -38,26 +25,6 @@ export interface SendCopyParams {
|
||||||
*/
|
*/
|
||||||
parseMode?: string | null
|
parseMode?: string | null
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to throw an error if {@link replyTo}
|
|
||||||
* message does not exist.
|
|
||||||
*
|
|
||||||
* If that message was not found, `NotFoundError` is thrown,
|
|
||||||
* with `text` set to `MESSAGE_NOT_FOUND`.
|
|
||||||
*
|
|
||||||
* Incurs an additional request, so only use when really needed.
|
|
||||||
*
|
|
||||||
* Defaults to `false`
|
|
||||||
*/
|
|
||||||
mustReply?: boolean
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of formatting entities to use instead of parsing via a
|
* List of formatting entities to use instead of parsing via a
|
||||||
* parse mode.
|
* parse mode.
|
||||||
|
@ -71,13 +38,6 @@ export interface SendCopyParams {
|
||||||
* to hide a reply keyboard or to force a reply.
|
* to hide a reply keyboard or to force a reply.
|
||||||
*/
|
*/
|
||||||
replyMarkup?: ReplyMarkup
|
replyMarkup?: ReplyMarkup
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to clear draft after sending this message.
|
|
||||||
*
|
|
||||||
* Defaults to `false`
|
|
||||||
*/
|
|
||||||
clearDraft?: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,8 +47,6 @@ export interface SendCopyParams {
|
||||||
* it will be copied simply as a text message,
|
* it will be copied simply as a text message,
|
||||||
* and if the message contains an invoice,
|
* and if the message contains an invoice,
|
||||||
* it can't be copied.
|
* it can't be copied.
|
||||||
*
|
|
||||||
* @param params
|
|
||||||
*/
|
*/
|
||||||
export async function sendCopy(
|
export async function sendCopy(
|
||||||
client: BaseTelegramClient,
|
client: BaseTelegramClient,
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
import { BaseTelegramClient, getMarkedPeerId, MtArgumentError, tl } from '@mtcute/core'
|
import { BaseTelegramClient, tl } from '@mtcute/core'
|
||||||
import { randomLong } from '@mtcute/core/utils'
|
import { randomLong } from '@mtcute/core/utils'
|
||||||
|
|
||||||
import { MtMessageNotFoundError } from '../../types/errors'
|
|
||||||
import { InputMediaLike } from '../../types/media/input-media'
|
import { InputMediaLike } from '../../types/media/input-media'
|
||||||
import { Message } from '../../types/messages/message'
|
import { Message } from '../../types/messages/message'
|
||||||
import { InputPeerLike, PeersIndex } from '../../types/peers'
|
import { InputPeerLike, PeersIndex } from '../../types/peers'
|
||||||
import { normalizeDate, normalizeMessageId } from '../../utils/misc-utils'
|
import { normalizeDate } from '../../utils/misc-utils'
|
||||||
import { assertIsUpdatesGroup } from '../../utils/updates-utils'
|
import { assertIsUpdatesGroup } from '../../utils/updates-utils'
|
||||||
import { _normalizeInputMedia } from '../files/normalize-input-media'
|
import { _normalizeInputMedia } from '../files/normalize-input-media'
|
||||||
import { resolvePeer } from '../users/resolve-peer'
|
import { resolvePeer } from '../users/resolve-peer'
|
||||||
import { _getDiscussionMessage } from './get-discussion-message'
|
import { _getDiscussionMessage } from './get-discussion-message'
|
||||||
import { getMessages } from './get-messages'
|
|
||||||
import { _parseEntities } from './parse-entities'
|
import { _parseEntities } from './parse-entities'
|
||||||
|
import { _processCommonSendParameters, CommonSendParams } from './send-common'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a group of media.
|
* Send a group of media.
|
||||||
|
@ -28,56 +27,7 @@ export async function sendMediaGroup(
|
||||||
client: BaseTelegramClient,
|
client: BaseTelegramClient,
|
||||||
chatId: InputPeerLike,
|
chatId: InputPeerLike,
|
||||||
medias: (InputMediaLike | string)[],
|
medias: (InputMediaLike | string)[],
|
||||||
params?: {
|
params?: CommonSendParams & {
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to throw an error if {@link replyTo}
|
|
||||||
* message does not exist.
|
|
||||||
*
|
|
||||||
* If that message was not found, `NotFoundError` is thrown,
|
|
||||||
* with `text` set to `MESSAGE_NOT_FOUND`.
|
|
||||||
*
|
|
||||||
* Incurs an additional request, so only use when really needed.
|
|
||||||
*
|
|
||||||
* Defaults to `false`
|
|
||||||
*/
|
|
||||||
mustReply?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message to comment to. Either a message object or message ID.
|
|
||||||
*
|
|
||||||
* This overwrites `replyTo` if it was passed
|
|
||||||
*/
|
|
||||||
commentTo?: number | Message
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse mode to use to parse entities before sending
|
|
||||||
* the message. Defaults to current default parse mode (if any).
|
|
||||||
*
|
|
||||||
* Passing `null` will explicitly disable formatting.
|
|
||||||
*/
|
|
||||||
parseMode?: string | null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to send this message silently.
|
|
||||||
*/
|
|
||||||
silent?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If set, the message will be scheduled to this date.
|
|
||||||
* When passing a number, a UNIX time in ms is expected.
|
|
||||||
*
|
|
||||||
* You can also pass `0x7FFFFFFE`, this will send the message
|
|
||||||
* once the peer is online
|
|
||||||
*/
|
|
||||||
schedule?: Date | number
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function that will be called after some part has been uploaded.
|
* Function that will be called after some part has been uploaded.
|
||||||
* Only used when a file that requires uploading is passed,
|
* Only used when a file that requires uploading is passed,
|
||||||
|
@ -88,49 +38,11 @@ export async function sendMediaGroup(
|
||||||
* @param total Total file size
|
* @param total Total file size
|
||||||
*/
|
*/
|
||||||
progressCallback?: (index: number, uploaded: number, total: number) => void
|
progressCallback?: (index: number, uploaded: number, total: number) => void
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to clear draft after sending this message.
|
|
||||||
*
|
|
||||||
* Defaults to `false`
|
|
||||||
*/
|
|
||||||
clearDraft?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to disallow further forwards of this message.
|
|
||||||
*
|
|
||||||
* Only for bots, works even if the target chat does not
|
|
||||||
* have content protection.
|
|
||||||
*/
|
|
||||||
forbidForwards?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Peer to use when sending the message.
|
|
||||||
*/
|
|
||||||
sendAs?: InputPeerLike
|
|
||||||
},
|
},
|
||||||
): Promise<Message[]> {
|
): Promise<Message[]> {
|
||||||
if (!params) params = {}
|
if (!params) params = {}
|
||||||
|
|
||||||
let peer = await resolvePeer(client, chatId)
|
const { peer, replyTo } = await _processCommonSendParameters(client, chatId, params)
|
||||||
|
|
||||||
let replyTo = normalizeMessageId(params.replyTo)
|
|
||||||
|
|
||||||
if (params.commentTo) {
|
|
||||||
[peer, replyTo] = await _getDiscussionMessage(client, peer, normalizeMessageId(params.commentTo)!)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.mustReply) {
|
|
||||||
if (!replyTo) {
|
|
||||||
throw new MtArgumentError('mustReply used, but replyTo was not passed')
|
|
||||||
}
|
|
||||||
|
|
||||||
const msg = await getMessages(client, peer, replyTo)
|
|
||||||
|
|
||||||
if (!msg) {
|
|
||||||
throw new MtMessageNotFoundError(getMarkedPeerId(peer), replyTo, 'to reply to')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const multiMedia: tl.RawInputSingleMedia[] = []
|
const multiMedia: tl.RawInputSingleMedia[] = []
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
import { BaseTelegramClient, getMarkedPeerId, MtArgumentError, tl } from '@mtcute/core'
|
import { BaseTelegramClient, tl } from '@mtcute/core'
|
||||||
import { randomLong } from '@mtcute/core/utils'
|
import { randomLong } from '@mtcute/core/utils'
|
||||||
|
|
||||||
import { BotKeyboard, ReplyMarkup } from '../../types/bots/keyboards'
|
import { BotKeyboard, ReplyMarkup } from '../../types/bots/keyboards'
|
||||||
import { MtMessageNotFoundError } from '../../types/errors'
|
|
||||||
import { InputMediaLike } from '../../types/media/input-media'
|
import { InputMediaLike } from '../../types/media/input-media'
|
||||||
import { Message } from '../../types/messages/message'
|
import { Message } from '../../types/messages/message'
|
||||||
import { FormattedString } from '../../types/parser'
|
import { FormattedString } from '../../types/parser'
|
||||||
import { InputPeerLike } from '../../types/peers'
|
import { InputPeerLike } from '../../types/peers'
|
||||||
import { normalizeDate, normalizeMessageId } from '../../utils/misc-utils'
|
import { normalizeDate } from '../../utils/misc-utils'
|
||||||
import { _normalizeInputMedia } from '../files/normalize-input-media'
|
import { _normalizeInputMedia } from '../files/normalize-input-media'
|
||||||
import { resolvePeer } from '../users/resolve-peer'
|
import { resolvePeer } from '../users/resolve-peer'
|
||||||
import { _findMessageInUpdate } from './find-in-update'
|
import { _findMessageInUpdate } from './find-in-update'
|
||||||
import { _getDiscussionMessage } from './get-discussion-message'
|
import { _getDiscussionMessage } from './get-discussion-message'
|
||||||
import { getMessages } from './get-messages'
|
|
||||||
import { _parseEntities } from './parse-entities'
|
import { _parseEntities } from './parse-entities'
|
||||||
|
import { _processCommonSendParameters, CommonSendParams } from './send-common'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a single media (a photo or a document-based media)
|
* Send a single media (a photo or a document-based media)
|
||||||
|
@ -30,7 +29,13 @@ export async function sendMedia(
|
||||||
client: BaseTelegramClient,
|
client: BaseTelegramClient,
|
||||||
chatId: InputPeerLike,
|
chatId: InputPeerLike,
|
||||||
media: InputMediaLike | string,
|
media: InputMediaLike | string,
|
||||||
params?: {
|
params?: CommonSendParams & {
|
||||||
|
/**
|
||||||
|
* For bots: inline or reply markup or an instruction
|
||||||
|
* to hide a reply keyboard or to force a reply.
|
||||||
|
*/
|
||||||
|
replyMarkup?: ReplyMarkup
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override caption for `media`.
|
* Override caption for `media`.
|
||||||
*
|
*
|
||||||
|
@ -47,61 +52,6 @@ export async function sendMedia(
|
||||||
*/
|
*/
|
||||||
entities?: tl.TypeMessageEntity[]
|
entities?: tl.TypeMessageEntity[]
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to throw an error if {@link replyTo}
|
|
||||||
* message does not exist.
|
|
||||||
*
|
|
||||||
* If that message was not found, `NotFoundError` is thrown,
|
|
||||||
* with `text` set to `MESSAGE_NOT_FOUND`.
|
|
||||||
*
|
|
||||||
* Incurs an additional request, so only use when really needed.
|
|
||||||
*
|
|
||||||
* Defaults to `false`
|
|
||||||
*/
|
|
||||||
mustReply?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message to comment to. Either a message object or message ID.
|
|
||||||
*
|
|
||||||
* This overwrites `replyTo` if it was passed
|
|
||||||
*/
|
|
||||||
commentTo?: number | Message
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse mode to use to parse entities before sending
|
|
||||||
* the message. Defaults to current default parse mode (if any).
|
|
||||||
*
|
|
||||||
* Passing `null` will explicitly disable formatting.
|
|
||||||
*/
|
|
||||||
parseMode?: string | null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to send this message silently.
|
|
||||||
*/
|
|
||||||
silent?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If set, the message will be scheduled to this date.
|
|
||||||
* When passing a number, a UNIX time in ms is expected.
|
|
||||||
*
|
|
||||||
* You can also pass `0x7FFFFFFE`, this will send the message
|
|
||||||
* once the peer is online
|
|
||||||
*/
|
|
||||||
schedule?: Date | number
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For bots: inline or reply markup or an instruction
|
|
||||||
* to hide a reply keyboard or to force a reply.
|
|
||||||
*/
|
|
||||||
replyMarkup?: ReplyMarkup
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function that will be called after some part has been uploaded.
|
* Function that will be called after some part has been uploaded.
|
||||||
* Only used when a file that requires uploading is passed,
|
* Only used when a file that requires uploading is passed,
|
||||||
|
@ -111,32 +61,6 @@ export async function sendMedia(
|
||||||
* @param total Total file size
|
* @param total Total file size
|
||||||
*/
|
*/
|
||||||
progressCallback?: (uploaded: number, total: number) => void
|
progressCallback?: (uploaded: number, total: number) => void
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to clear draft after sending this message.
|
|
||||||
*
|
|
||||||
* Defaults to `false`
|
|
||||||
*/
|
|
||||||
clearDraft?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to disallow further forwards of this message.
|
|
||||||
*
|
|
||||||
* Only for bots, works even if the target chat does not
|
|
||||||
* have content protection.
|
|
||||||
*/
|
|
||||||
forbidForwards?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Peer to use when sending the message.
|
|
||||||
*/
|
|
||||||
sendAs?: InputPeerLike
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to dispatch the returned message
|
|
||||||
* to the client's update handler.
|
|
||||||
*/
|
|
||||||
shouldDispatch?: true
|
|
||||||
},
|
},
|
||||||
): Promise<Message> {
|
): Promise<Message> {
|
||||||
if (!params) params = {}
|
if (!params) params = {}
|
||||||
|
@ -160,26 +84,8 @@ export async function sendMedia(
|
||||||
params.entities || (media as Extract<typeof media, { entities?: unknown }>).entities,
|
params.entities || (media as Extract<typeof media, { entities?: unknown }>).entities,
|
||||||
)
|
)
|
||||||
|
|
||||||
let peer = await resolvePeer(client, chatId)
|
|
||||||
const replyMarkup = BotKeyboard._convertToTl(params.replyMarkup)
|
const replyMarkup = BotKeyboard._convertToTl(params.replyMarkup)
|
||||||
|
const { peer, replyTo } = await _processCommonSendParameters(client, chatId, params)
|
||||||
let replyTo = normalizeMessageId(params.replyTo)
|
|
||||||
|
|
||||||
if (params.commentTo) {
|
|
||||||
[peer, replyTo] = await _getDiscussionMessage(client, peer, normalizeMessageId(params.commentTo)!)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.mustReply) {
|
|
||||||
if (!replyTo) {
|
|
||||||
throw new MtArgumentError('mustReply used, but replyTo was not passed')
|
|
||||||
}
|
|
||||||
|
|
||||||
const msg = await getMessages(client, peer, replyTo)
|
|
||||||
|
|
||||||
if (!msg) {
|
|
||||||
throw new MtMessageNotFoundError(getMarkedPeerId(peer), replyTo, 'to reply to')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await client.call({
|
const res = await client.call({
|
||||||
_: 'messages.sendMedia',
|
_: 'messages.sendMedia',
|
||||||
|
|
43
packages/client/src/methods/messages/send-reply.ts
Normal file
43
packages/client/src/methods/messages/send-reply.ts
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import { BaseTelegramClient } from '@mtcute/core'
|
||||||
|
|
||||||
|
import { Message } from '../../types/messages/message'
|
||||||
|
import { ParametersSkip2 } from '../../types/utils'
|
||||||
|
import { sendMedia } from './send-media'
|
||||||
|
import { sendMediaGroup } from './send-media-group'
|
||||||
|
import { sendText } from './send-text'
|
||||||
|
|
||||||
|
/** Send a text in reply to a given message */
|
||||||
|
export function replyText(
|
||||||
|
client: BaseTelegramClient,
|
||||||
|
message: Message,
|
||||||
|
...params: ParametersSkip2<typeof sendText>
|
||||||
|
): ReturnType<typeof sendText> {
|
||||||
|
const [text, params_ = {}] = params
|
||||||
|
params_.replyTo = message.id
|
||||||
|
|
||||||
|
return sendText(client, message.chat.inputPeer, text, params_)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Send a media in reply to a given message */
|
||||||
|
export function replyMedia(
|
||||||
|
client: BaseTelegramClient,
|
||||||
|
message: Message,
|
||||||
|
...params: ParametersSkip2<typeof sendMedia>
|
||||||
|
): ReturnType<typeof sendMedia> {
|
||||||
|
const [media, params_ = {}] = params
|
||||||
|
params_.replyTo = message.id
|
||||||
|
|
||||||
|
return sendMedia(client, message.chat.inputPeer, media, params_)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Send a media group in reply to a given message */
|
||||||
|
export function replyMediaGroup(
|
||||||
|
client: BaseTelegramClient,
|
||||||
|
message: Message,
|
||||||
|
...params: ParametersSkip2<typeof sendMediaGroup>
|
||||||
|
): ReturnType<typeof sendMediaGroup> {
|
||||||
|
const [media, params_ = {}] = params
|
||||||
|
params_.replyTo = message.id
|
||||||
|
|
||||||
|
return sendMediaGroup(client, message.chat.inputPeer, media, params_)
|
||||||
|
}
|
|
@ -1,20 +1,19 @@
|
||||||
import { BaseTelegramClient, getMarkedPeerId, MtArgumentError, MtTypeAssertionError, tl } from '@mtcute/core'
|
import { BaseTelegramClient, getMarkedPeerId, MtTypeAssertionError, tl } from '@mtcute/core'
|
||||||
import { randomLong } from '@mtcute/core/utils'
|
import { randomLong } from '@mtcute/core/utils'
|
||||||
|
|
||||||
import { BotKeyboard, ReplyMarkup } from '../../types/bots/keyboards'
|
import { BotKeyboard, ReplyMarkup } from '../../types/bots/keyboards'
|
||||||
import { MtMessageNotFoundError } from '../../types/errors'
|
|
||||||
import { Message } from '../../types/messages/message'
|
import { Message } from '../../types/messages/message'
|
||||||
import { FormattedString } from '../../types/parser'
|
import { FormattedString } from '../../types/parser'
|
||||||
import { InputPeerLike, PeersIndex } from '../../types/peers'
|
import { InputPeerLike, PeersIndex } from '../../types/peers'
|
||||||
import { normalizeDate, normalizeMessageId } from '../../utils/misc-utils'
|
import { normalizeDate } from '../../utils/misc-utils'
|
||||||
import { inputPeerToPeer } from '../../utils/peer-utils'
|
import { inputPeerToPeer } from '../../utils/peer-utils'
|
||||||
import { createDummyUpdate } from '../../utils/updates-utils'
|
import { createDummyUpdate } from '../../utils/updates-utils'
|
||||||
import { getAuthState } from '../auth/_state'
|
import { getAuthState } from '../auth/_state'
|
||||||
import { resolvePeer } from '../users/resolve-peer'
|
import { resolvePeer } from '../users/resolve-peer'
|
||||||
import { _findMessageInUpdate } from './find-in-update'
|
import { _findMessageInUpdate } from './find-in-update'
|
||||||
import { _getDiscussionMessage } from './get-discussion-message'
|
import { _getDiscussionMessage } from './get-discussion-message'
|
||||||
import { getMessages } from './get-messages'
|
|
||||||
import { _parseEntities } from './parse-entities'
|
import { _parseEntities } from './parse-entities'
|
||||||
|
import { _processCommonSendParameters, CommonSendParams } from './send-common'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a text message
|
* Send a text message
|
||||||
|
@ -27,41 +26,12 @@ export async function sendText(
|
||||||
client: BaseTelegramClient,
|
client: BaseTelegramClient,
|
||||||
chatId: InputPeerLike,
|
chatId: InputPeerLike,
|
||||||
text: string | FormattedString<string>,
|
text: string | FormattedString<string>,
|
||||||
params?: {
|
params?: CommonSendParams & {
|
||||||
/**
|
/**
|
||||||
* Message to reply to. Either a message object or message ID.
|
* For bots: inline or reply markup or an instruction
|
||||||
*
|
* to hide a reply keyboard or to force a reply.
|
||||||
* For forums - can also be an ID of the topic (i.e. its top message ID)
|
|
||||||
*/
|
*/
|
||||||
replyTo?: number | Message
|
replyMarkup?: ReplyMarkup
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to throw an error if {@link replyTo}
|
|
||||||
* message does not exist.
|
|
||||||
*
|
|
||||||
* If that message was not found, `NotFoundError` is thrown,
|
|
||||||
* with `text` set to `MESSAGE_NOT_FOUND`.
|
|
||||||
*
|
|
||||||
* Incurs an additional request, so only use when really needed.
|
|
||||||
*
|
|
||||||
* Defaults to `false`
|
|
||||||
*/
|
|
||||||
mustReply?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message to comment to. Either a message object or message ID.
|
|
||||||
*
|
|
||||||
* This overwrites `replyTo` if it was passed
|
|
||||||
*/
|
|
||||||
commentTo?: number | Message
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse mode to use to parse entities before sending
|
|
||||||
* the message. Defaults to current default parse mode (if any).
|
|
||||||
*
|
|
||||||
* Passing `null` will explicitly disable formatting.
|
|
||||||
*/
|
|
||||||
parseMode?: string | null
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of formatting entities to use instead of parsing via a
|
* List of formatting entities to use instead of parsing via a
|
||||||
|
@ -75,78 +45,14 @@ export async function sendText(
|
||||||
* Whether to disable links preview in this message
|
* Whether to disable links preview in this message
|
||||||
*/
|
*/
|
||||||
disableWebPreview?: boolean
|
disableWebPreview?: boolean
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to send this message silently.
|
|
||||||
*/
|
|
||||||
silent?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If set, the message will be scheduled to this date.
|
|
||||||
* When passing a number, a UNIX time in ms is expected.
|
|
||||||
*
|
|
||||||
* You can also pass `0x7FFFFFFE`, this will send the message
|
|
||||||
* once the peer is online
|
|
||||||
*/
|
|
||||||
schedule?: Date | number
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For bots: inline or reply markup or an instruction
|
|
||||||
* to hide a reply keyboard or to force a reply.
|
|
||||||
*/
|
|
||||||
replyMarkup?: ReplyMarkup
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to clear draft after sending this message.
|
|
||||||
*
|
|
||||||
* Defaults to `false`
|
|
||||||
*/
|
|
||||||
clearDraft?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to disallow further forwards of this message.
|
|
||||||
*
|
|
||||||
* Only for bots, works even if the target chat does not
|
|
||||||
* have content protection.
|
|
||||||
*/
|
|
||||||
forbidForwards?: boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Peer to use when sending the message.
|
|
||||||
*/
|
|
||||||
sendAs?: InputPeerLike
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to dispatch the returned message
|
|
||||||
* to the client's update handler.
|
|
||||||
*/
|
|
||||||
shouldDispatch?: true
|
|
||||||
},
|
},
|
||||||
): Promise<Message> {
|
): Promise<Message> {
|
||||||
if (!params) params = {}
|
if (!params) params = {}
|
||||||
|
|
||||||
const [message, entities] = await _parseEntities(client, text, params.parseMode, params.entities)
|
const [message, entities] = await _parseEntities(client, text, params.parseMode, params.entities)
|
||||||
|
|
||||||
let peer = await resolvePeer(client, chatId)
|
|
||||||
const replyMarkup = BotKeyboard._convertToTl(params.replyMarkup)
|
const replyMarkup = BotKeyboard._convertToTl(params.replyMarkup)
|
||||||
|
const { peer, replyTo } = await _processCommonSendParameters(client, chatId, params)
|
||||||
let replyTo = normalizeMessageId(params.replyTo)
|
|
||||||
|
|
||||||
if (params.commentTo) {
|
|
||||||
[peer, replyTo] = await _getDiscussionMessage(client, peer, normalizeMessageId(params.commentTo)!)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.mustReply) {
|
|
||||||
if (!replyTo) {
|
|
||||||
throw new MtArgumentError('mustReply used, but replyTo was not passed')
|
|
||||||
}
|
|
||||||
|
|
||||||
const msg = await getMessages(client, peer, replyTo)
|
|
||||||
|
|
||||||
if (!msg) {
|
|
||||||
throw new MtMessageNotFoundError(getMarkedPeerId(peer), replyTo, 'to reply to')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await client.call({
|
const res = await client.call({
|
||||||
_: 'messages.sendMessage',
|
_: 'messages.sendMessage',
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { BaseTelegramClient, MtTypeAssertionError, tl } from '@mtcute/core'
|
import { BaseTelegramClient, MaybeArray, MtTypeAssertionError, tl } from '@mtcute/core'
|
||||||
import { assertTypeIs } from '@mtcute/core/utils'
|
import { assertTypeIs, LongSet } from '@mtcute/core/utils'
|
||||||
|
|
||||||
import { Sticker } from '../../types'
|
import { Message, Sticker } from '../../types'
|
||||||
import { parseDocument } from '../../types/media/document-utils'
|
import { parseDocument } from '../../types/media/document-utils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,3 +27,30 @@ export async function getCustomEmojis(client: BaseTelegramClient, ids: tl.Long[]
|
||||||
return doc
|
return doc
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given one or more messages, extract all unique custom emojis from it and fetch them
|
||||||
|
*/
|
||||||
|
export async function getCustomEmojisFromMessages(
|
||||||
|
client: BaseTelegramClient,
|
||||||
|
messages: MaybeArray<Message>,
|
||||||
|
): Promise<Sticker[]> {
|
||||||
|
const set = new LongSet()
|
||||||
|
|
||||||
|
if (!Array.isArray(messages)) messages = [messages]
|
||||||
|
|
||||||
|
for (const { raw } of messages) {
|
||||||
|
if (raw._ === 'messageService' || !raw.entities) continue
|
||||||
|
|
||||||
|
for (const entity of raw.entities) {
|
||||||
|
if (entity._ === 'messageEntityCustomEmoji') {
|
||||||
|
set.add(entity.documentId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const arr = set.toArray()
|
||||||
|
if (!arr.length) return []
|
||||||
|
|
||||||
|
return getCustomEmojis(client, arr)
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,9 @@ import type { Message } from './message'
|
||||||
*/
|
*/
|
||||||
export type InputMessageId = { chatId: InputPeerLike; message: number } | { message: Message }
|
export type InputMessageId = { chatId: InputPeerLike; message: number } | { message: Message }
|
||||||
|
|
||||||
|
/** Remove {@link InputMessageId} type from the given type */
|
||||||
|
export type OmitInputMessageId<T> = Omit<T, 'chatId' | 'message'>
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export function normalizeInputMessageId(id: InputMessageId) {
|
export function normalizeInputMessageId(id: InputMessageId) {
|
||||||
if ('chatId' in id) return id
|
if ('chatId' in id) return id
|
||||||
|
|
|
@ -47,9 +47,10 @@ export interface MessageForwardInfo {
|
||||||
/** Information about replies to a message */
|
/** Information about replies to a message */
|
||||||
export interface MessageRepliesInfo {
|
export interface MessageRepliesInfo {
|
||||||
/**
|
/**
|
||||||
* Whether this is a comments thread under a channel post
|
* Whether this message is a channel post that has a comments thread
|
||||||
|
* in the linked discussion group
|
||||||
*/
|
*/
|
||||||
isComments: false
|
hasComments: false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Total number of replies
|
* Total number of replies
|
||||||
|
@ -75,7 +76,8 @@ export interface MessageRepliesInfo {
|
||||||
/** Information about comments to a channel post */
|
/** Information about comments to a channel post */
|
||||||
export interface MessageCommentsInfo extends Omit<MessageRepliesInfo, 'isComments'> {
|
export interface MessageCommentsInfo extends Omit<MessageRepliesInfo, 'isComments'> {
|
||||||
/**
|
/**
|
||||||
* Whether this is a comments thread under a channel post
|
* Whether this message is a channel post that has a comments thread
|
||||||
|
* in the linked discussion group
|
||||||
*/
|
*/
|
||||||
isComments: true
|
isComments: true
|
||||||
|
|
||||||
|
@ -303,7 +305,7 @@ export class Message {
|
||||||
if (!this._replies) {
|
if (!this._replies) {
|
||||||
const r = this.raw.replies
|
const r = this.raw.replies
|
||||||
const obj: MessageRepliesInfo = {
|
const obj: MessageRepliesInfo = {
|
||||||
isComments: r.comments as false,
|
hasComments: r.comments as false,
|
||||||
count: r.replies,
|
count: r.replies,
|
||||||
hasUnread: r.readMaxId !== undefined && r.readMaxId !== r.maxId,
|
hasUnread: r.readMaxId !== undefined && r.readMaxId !== r.maxId,
|
||||||
lastMessageId: r.maxId,
|
lastMessageId: r.maxId,
|
||||||
|
|
|
@ -68,13 +68,6 @@ export class BotChatJoinRequestUpdate {
|
||||||
get invite(): ChatInviteLink {
|
get invite(): ChatInviteLink {
|
||||||
return (this._invite ??= new ChatInviteLink(this.raw.invite))
|
return (this._invite ??= new ChatInviteLink(this.raw.invite))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Approve or deny the request.
|
|
||||||
*/
|
|
||||||
// hide(action: Parameters<TelegramClient['hideJoinRequest']>[1]['action']): Promise<void> {
|
|
||||||
// return this.client.hideJoinRequest(this.chat.inputPeer, { action, user: this.user.inputPeer })
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
makeInspectable(BotChatJoinRequestUpdate)
|
makeInspectable(BotChatJoinRequestUpdate)
|
||||||
|
|
|
@ -47,23 +47,6 @@ export class ChatJoinRequestUpdate {
|
||||||
get totalPending(): number {
|
get totalPending(): number {
|
||||||
return this.raw.requestsPending
|
return this.raw.requestsPending
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Approve or deny the last requested user
|
|
||||||
*/
|
|
||||||
// hideLast(action: Parameters<TelegramClient['hideJoinRequest']>[1]['action']): Promise<void> {
|
|
||||||
// return this.client.hideJoinRequest(this.chatId, { user: this.raw.recentRequesters[0], action })
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Approve or deny all recent requests
|
|
||||||
* (the ones available in {@link recentRequesters})
|
|
||||||
*/
|
|
||||||
// async hideAllRecent(action: Parameters<TelegramClient['hideJoinRequest']>[1]['action']): Promise<void> {
|
|
||||||
// for (const id of this.raw.recentRequesters) {
|
|
||||||
// await this.client.hideJoinRequest(this.chatId, { user: id, action })
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
makeInspectable(ChatJoinRequestUpdate)
|
makeInspectable(ChatJoinRequestUpdate)
|
||||||
|
|
|
@ -76,14 +76,6 @@ export class ChosenInlineResult {
|
||||||
|
|
||||||
return encodeInlineMessageId(this.raw.msgId)
|
return encodeInlineMessageId(this.raw.msgId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// async editMessage(params: Parameters<TelegramClient['editInlineMessage']>[1]): Promise<void> {
|
|
||||||
// if (!this.raw.msgId) {
|
|
||||||
// throw new MtArgumentError('No message ID, make sure you have included reply markup!')
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return this.client.editInlineMessage(this.raw.msgId, params)
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
makeInspectable(ChosenInlineResult)
|
makeInspectable(ChosenInlineResult)
|
||||||
|
|
|
@ -24,12 +24,18 @@ export class PollUpdate {
|
||||||
return this.raw.pollId
|
return this.raw.pollId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this is a shortened version of update, not containing the poll itself.
|
||||||
|
*/
|
||||||
|
get isShort(): boolean {
|
||||||
|
return this.raw.poll === undefined
|
||||||
|
}
|
||||||
|
|
||||||
private _poll?: Poll
|
private _poll?: Poll
|
||||||
/**
|
/**
|
||||||
* The poll.
|
* The poll.
|
||||||
*
|
*
|
||||||
* Note that sometimes the update does not have the poll
|
* When {@link isShort} is set, mtcute creates a stub poll
|
||||||
* (Telegram limitation), and mtcute creates a stub poll
|
|
||||||
* with empty question, answers and flags
|
* with empty question, answers and flags
|
||||||
* (like `quiz`, `public`, etc.)
|
* (like `quiz`, `public`, etc.)
|
||||||
*
|
*
|
||||||
|
@ -39,8 +45,7 @@ export class PollUpdate {
|
||||||
*
|
*
|
||||||
* Bot API and TDLib do basically the same internally,
|
* Bot API and TDLib do basically the same internally,
|
||||||
* and thus are able to always provide them,
|
* and thus are able to always provide them,
|
||||||
* but mtcute tries to keep it simple in terms of local
|
* but mtcute currently does not have a way to do that.
|
||||||
* storage and only stores the necessary information.
|
|
||||||
*/
|
*/
|
||||||
get poll(): Poll {
|
get poll(): Poll {
|
||||||
if (!this._poll) {
|
if (!this._poll) {
|
||||||
|
|
|
@ -159,4 +159,14 @@ export class LongSet {
|
||||||
clear() {
|
clear() {
|
||||||
this._set.clear()
|
this._set.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toArray() {
|
||||||
|
const arr: Long[] = []
|
||||||
|
|
||||||
|
for (const v of this._set) {
|
||||||
|
arr.push(longFromFastString(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,19 +8,21 @@ function generateHandler() {
|
||||||
|
|
||||||
types.forEach((type) => {
|
types.forEach((type) => {
|
||||||
lines.push(
|
lines.push(
|
||||||
`export type ${type.handlerTypeName}Handler<T = ${type.updateType}` +
|
`export type ${type.handlerTypeName}Handler<T = ${type.context}` +
|
||||||
`${type.state ? ', S = never' : ''}> = ParsedUpdateHandler<` +
|
`${type.state ? ', S = never' : ''}> = ParsedUpdateHandler<` +
|
||||||
`'${type.typeName}', T${type.state ? ', S' : ''}>`,
|
`'${type.typeName}', T${type.state ? ', S' : ''}>`,
|
||||||
)
|
)
|
||||||
names.push(`${type.handlerTypeName}Handler`)
|
names.push(`${type.handlerTypeName}Handler`)
|
||||||
})
|
})
|
||||||
|
|
||||||
replaceSections('handler.ts', {
|
replaceSections(
|
||||||
codegen:
|
'handler.ts',
|
||||||
lines.join('\n') +
|
{
|
||||||
'\n\nexport type UpdateHandler = \n' +
|
codegen:
|
||||||
names.map((i) => ` | ${i}\n`).join(''),
|
lines.join('\n') + '\n\nexport type UpdateHandler = \n' + names.map((i) => ` | ${i}\n`).join(''),
|
||||||
}, __dirname)
|
},
|
||||||
|
__dirname,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateDispatcher() {
|
function generateDispatcher() {
|
||||||
|
@ -37,9 +39,13 @@ function generateDispatcher() {
|
||||||
* @param handler ${toSentence(type, 'full')}
|
* @param handler ${toSentence(type, 'full')}
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
on${type.handlerTypeName}(handler: ${type.handlerTypeName}Handler${type.state ? `<${type.updateType}, State extends never ? never : UpdateState<State, SceneName>>` : ''}['callback'], group?: number): void
|
on${type.handlerTypeName}(handler: ${type.handlerTypeName}Handler${
|
||||||
|
type.state ? `<${type.context}, State extends never ? never : UpdateState<State, SceneName>>` : ''
|
||||||
|
}['callback'], group?: number): void
|
||||||
|
|
||||||
${type.state ? `
|
${
|
||||||
|
type.state ?
|
||||||
|
`
|
||||||
/**
|
/**
|
||||||
* Register ${toSentence(type)} with a filter
|
* Register ${toSentence(type)} with a filter
|
||||||
*
|
*
|
||||||
|
@ -48,11 +54,15 @@ ${type.state ? `
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
on${type.handlerTypeName}<Mod>(
|
on${type.handlerTypeName}<Mod>(
|
||||||
filter: UpdateFilter<${type.updateType}, Mod, State>,
|
filter: UpdateFilter<${type.context}, Mod, State>,
|
||||||
handler: ${type.handlerTypeName}Handler<filters.Modify<${type.updateType}, Mod>, State extends never ? never : UpdateState<State, SceneName>>['callback'],
|
handler: ${type.handlerTypeName}Handler<filters.Modify<${
|
||||||
|
type.context
|
||||||
|
}, Mod>, State extends never ? never : UpdateState<State, SceneName>>['callback'],
|
||||||
group?: number
|
group?: number
|
||||||
): void
|
): void
|
||||||
` : ''}
|
` :
|
||||||
|
''
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register ${toSentence(type)} with a filter
|
* Register ${toSentence(type)} with a filter
|
||||||
|
@ -62,8 +72,10 @@ ${type.state ? `
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
on${type.handlerTypeName}<Mod>(
|
on${type.handlerTypeName}<Mod>(
|
||||||
filter: UpdateFilter<${type.updateType}, Mod>,
|
filter: UpdateFilter<${type.context}, Mod>,
|
||||||
handler: ${type.handlerTypeName}Handler<filters.Modify<${type.updateType}, Mod>${type.state ? ', State extends never ? never : UpdateState<State, SceneName>' : ''}>['callback'],
|
handler: ${type.handlerTypeName}Handler<filters.Modify<${type.context}, Mod>${
|
||||||
|
type.state ? ', State extends never ? never : UpdateState<State, SceneName>' : ''
|
||||||
|
}>['callback'],
|
||||||
group?: number
|
group?: number
|
||||||
): void
|
): void
|
||||||
|
|
||||||
|
@ -74,13 +86,20 @@ ${type.state ? `
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
replaceSections('dispatcher.ts', {
|
replaceSections(
|
||||||
codegen: lines.join('\n'),
|
'dispatcher.ts',
|
||||||
'codegen-imports':
|
{
|
||||||
'import {\n' +
|
codegen: lines.join('\n'),
|
||||||
imports.sort().map((i) => ` ${i},\n`).join('') +
|
'codegen-imports':
|
||||||
"} from './handler'",
|
'import {\n' +
|
||||||
}, __dirname)
|
imports
|
||||||
|
.sort()
|
||||||
|
.map((i) => ` ${i},\n`)
|
||||||
|
.join('') +
|
||||||
|
"} from './handler'",
|
||||||
|
},
|
||||||
|
__dirname,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
|
|
8
packages/dispatcher/src/context/base.ts
Normal file
8
packages/dispatcher/src/context/base.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { ParsedUpdate, TelegramClient } from '@mtcute/client'
|
||||||
|
|
||||||
|
export type UpdateContext<T> = T & {
|
||||||
|
client: TelegramClient
|
||||||
|
_name: Extract<ParsedUpdate, { data: T }>['name']
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UpdateContextDistributed<T> = T extends never ? never : UpdateContext<T>
|
64
packages/dispatcher/src/context/callback-query.ts
Normal file
64
packages/dispatcher/src/context/callback-query.ts
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import { CallbackQuery, getMarkedPeerId, MtArgumentError, MtMessageNotFoundError, TelegramClient } from '@mtcute/client'
|
||||||
|
|
||||||
|
import { UpdateContext } from './base'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context of a callback query update.
|
||||||
|
*
|
||||||
|
* This is a subclass of {@link CallbackQuery}, so all its fields are also available.
|
||||||
|
*/
|
||||||
|
export class CallbackQueryContext extends CallbackQuery implements UpdateContext<CallbackQuery> {
|
||||||
|
readonly _name = 'callback_query'
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly client: TelegramClient,
|
||||||
|
query: CallbackQuery,
|
||||||
|
) {
|
||||||
|
super(query.raw, query._peers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Answer to this callback query */
|
||||||
|
answer(params: Parameters<TelegramClient['answerCallbackQuery']>[1]) {
|
||||||
|
return this.client.answerCallbackQuery(this.id, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * Message that contained the callback button that was clicked.
|
||||||
|
*
|
||||||
|
* Note that the message may have been deleted, in which case
|
||||||
|
* `MessageNotFoundError` is thrown.
|
||||||
|
*
|
||||||
|
* Can only be used if `isInline = false`
|
||||||
|
*/
|
||||||
|
async getMessage() {
|
||||||
|
if (this.raw._ !== 'updateBotCallbackQuery') {
|
||||||
|
throw new MtArgumentError('Cannot get message for inline callback query')
|
||||||
|
}
|
||||||
|
|
||||||
|
const [msg] = await this.client.getMessages(this.raw.peer, this.raw.msgId)
|
||||||
|
|
||||||
|
if (!msg) {
|
||||||
|
throw new MtMessageNotFoundError(getMarkedPeerId(this.raw.peer), this.raw.msgId, 'Message not found')
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit the message that contained the callback button that was clicked.
|
||||||
|
*/
|
||||||
|
async editMessage(params: Omit<Parameters<TelegramClient['editInlineMessage']>[0], 'messageId'>) {
|
||||||
|
if (this.raw._ === 'updateInlineBotCallbackQuery') {
|
||||||
|
return this.client.editInlineMessage({
|
||||||
|
messageId: this.raw.msgId,
|
||||||
|
...params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.client.editMessage({
|
||||||
|
chatId: this.raw.peer,
|
||||||
|
message: this.raw.msgId,
|
||||||
|
...params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
39
packages/dispatcher/src/context/chat-join-request.ts
Normal file
39
packages/dispatcher/src/context/chat-join-request.ts
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import { BotChatJoinRequestUpdate, TelegramClient } from '@mtcute/client'
|
||||||
|
|
||||||
|
import { UpdateContext } from './base'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context of a chat join request update (for bots).
|
||||||
|
*
|
||||||
|
* This is a subclass of {@link BotChatJoinRequestUpdate}, so all its fields are also available.
|
||||||
|
*/
|
||||||
|
export class ChatJoinRequestUpdateContext
|
||||||
|
extends BotChatJoinRequestUpdate
|
||||||
|
implements UpdateContext<BotChatJoinRequestUpdate> {
|
||||||
|
readonly _name = 'bot_chat_join_request'
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly client: TelegramClient,
|
||||||
|
update: BotChatJoinRequestUpdate,
|
||||||
|
) {
|
||||||
|
super(update.raw, update._peers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Approve the request */
|
||||||
|
approve(): Promise<void> {
|
||||||
|
return this.client.hideJoinRequest({
|
||||||
|
action: 'approve',
|
||||||
|
user: this.user.inputPeer,
|
||||||
|
chatId: this.chat.inputPeer,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Decline the request */
|
||||||
|
decline(): Promise<void> {
|
||||||
|
return this.client.hideJoinRequest({
|
||||||
|
action: 'decline',
|
||||||
|
user: this.user.inputPeer,
|
||||||
|
chatId: this.chat.inputPeer,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
38
packages/dispatcher/src/context/chosen-inline-result.ts
Normal file
38
packages/dispatcher/src/context/chosen-inline-result.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { ChosenInlineResult, MtArgumentError, TelegramClient } from '@mtcute/client'
|
||||||
|
|
||||||
|
import { UpdateContext } from './base'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context of a chosen inline result update.
|
||||||
|
*
|
||||||
|
* This is a subclass of {@link ChosenInlineResult}, so all its fields are also available.
|
||||||
|
*
|
||||||
|
* > **Note**: To receive these updates, you must enable
|
||||||
|
* > Inline feedback in [@BotFather](//t.me/botfather)
|
||||||
|
*/
|
||||||
|
export class ChosenInlineResultContext extends ChosenInlineResult implements UpdateContext<ChosenInlineResult> {
|
||||||
|
readonly _name = 'chosen_inline_result'
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly client: TelegramClient,
|
||||||
|
result: ChosenInlineResult,
|
||||||
|
) {
|
||||||
|
super(result.raw, result._peers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit the message that was sent when this inline result that was chosen.
|
||||||
|
*
|
||||||
|
* > **Note**: This method can only be used if the message contained a reply markup
|
||||||
|
*/
|
||||||
|
async editMessage(params: Parameters<TelegramClient['editInlineMessage']>[0]): Promise<void> {
|
||||||
|
if (!this.raw.msgId) {
|
||||||
|
throw new MtArgumentError('No message ID, make sure you have included reply markup!')
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.client.editInlineMessage({
|
||||||
|
...params,
|
||||||
|
messageId: this.raw.msgId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
7
packages/dispatcher/src/context/index.ts
Normal file
7
packages/dispatcher/src/context/index.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
export * from './base'
|
||||||
|
export * from './callback-query'
|
||||||
|
export * from './chat-join-request'
|
||||||
|
export * from './chosen-inline-result'
|
||||||
|
export * from './inline-query'
|
||||||
|
export * from './message'
|
||||||
|
export * from './pre-checkout-query'
|
24
packages/dispatcher/src/context/inline-query.ts
Normal file
24
packages/dispatcher/src/context/inline-query.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { InlineQuery, ParametersSkip1, TelegramClient } from '@mtcute/client'
|
||||||
|
|
||||||
|
import { UpdateContext } from './base'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context of an inline query update.
|
||||||
|
*
|
||||||
|
* This is a subclass of {@link InlineQuery}, so all its fields are also available.
|
||||||
|
*/
|
||||||
|
export class InlineQueryContext extends InlineQuery implements UpdateContext<InlineQuery> {
|
||||||
|
readonly _name = 'inline_query'
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly client: TelegramClient,
|
||||||
|
query: InlineQuery,
|
||||||
|
) {
|
||||||
|
super(query.raw, query._peers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Answer to this inline query */
|
||||||
|
answer(...params: ParametersSkip1<TelegramClient['answerInlineQuery']>) {
|
||||||
|
return this.client.answerInlineQuery(this.id, ...params)
|
||||||
|
}
|
||||||
|
}
|
170
packages/dispatcher/src/context/message.ts
Normal file
170
packages/dispatcher/src/context/message.ts
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
import { Message, OmitInputMessageId, ParametersSkip1, TelegramClient } from '@mtcute/client'
|
||||||
|
import { DeleteMessagesParams } from '@mtcute/client/src/methods/messages/delete-messages'
|
||||||
|
import { ForwardMessageOptions } from '@mtcute/client/src/methods/messages/forward-messages'
|
||||||
|
import { SendCopyParams } from '@mtcute/client/src/methods/messages/send-copy'
|
||||||
|
import { SendCopyGroupParams } from '@mtcute/client/src/methods/messages/send-copy-group'
|
||||||
|
|
||||||
|
import { UpdateContext } from './base'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context of a message-related update.
|
||||||
|
*
|
||||||
|
* This is a subclass of {@link Message}, so all fields
|
||||||
|
* of the message are available.
|
||||||
|
*
|
||||||
|
* For message groups, own fields are related to the last message
|
||||||
|
* in the group. To access all messages, use {@link MessageContext#messages}.
|
||||||
|
*/
|
||||||
|
export class MessageContext extends Message implements UpdateContext<Message> {
|
||||||
|
// this is primarily for proper types in filters, so don't bother much with actual value
|
||||||
|
readonly _name = 'new_message'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of messages in the message group.
|
||||||
|
*
|
||||||
|
* For other updates, this is a list with a single element (`this`).
|
||||||
|
*/
|
||||||
|
readonly messages: MessageContext[]
|
||||||
|
|
||||||
|
/** Whether this update is about a message group */
|
||||||
|
readonly isMessageGroup: boolean
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly client: TelegramClient,
|
||||||
|
message: Message | Message[],
|
||||||
|
) {
|
||||||
|
const msg = Array.isArray(message) ? message[message.length - 1] : message
|
||||||
|
super(msg.raw, msg._peers, msg.isScheduled)
|
||||||
|
|
||||||
|
this.messages = Array.isArray(message) ? message.map((it) => new MessageContext(client, it)) : [this]
|
||||||
|
this.isMessageGroup = Array.isArray(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a message that this message is a reply to */
|
||||||
|
getReplyTo() {
|
||||||
|
return this.client.getReplyTo(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** If this is a channel post, get its automatic forward in the discussion group */
|
||||||
|
getDiscussionMessage() {
|
||||||
|
return this.client.getDiscussionMessage({ chatId: this.chat.inputPeer, message: this.id })
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get all custom emojis contained in this message (message group), if any */
|
||||||
|
getCustomEmojis() {
|
||||||
|
return this.client.getCustomEmojisFromMessages(this.messages)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Send a text message to the same chat (and topic, if applicable) as a given message */
|
||||||
|
answerText(...params: ParametersSkip1<TelegramClient['answerText']>) {
|
||||||
|
return this.client.answerText(this, ...params)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Send a media to the same chat (and topic, if applicable) as a given message */
|
||||||
|
answerMedia(...params: ParametersSkip1<TelegramClient['answerMedia']>) {
|
||||||
|
return this.client.answerMedia(this, ...params)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Send a media group to the same chat (and topic, if applicable) as a given message */
|
||||||
|
answerMediaGroup(...params: ParametersSkip1<TelegramClient['answerMediaGroup']>) {
|
||||||
|
return this.client.answerMediaGroup(this, ...params)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Send a text message in reply to this message */
|
||||||
|
replyText(...params: ParametersSkip1<TelegramClient['replyText']>) {
|
||||||
|
return this.client.replyText(this, ...params)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Send a media in reply to this message */
|
||||||
|
replyMedia(...params: ParametersSkip1<TelegramClient['replyMedia']>) {
|
||||||
|
return this.client.replyMedia(this, ...params)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Send a media group in reply to this message */
|
||||||
|
replyMediaGroup(...params: ParametersSkip1<TelegramClient['replyMediaGroup']>) {
|
||||||
|
return this.client.replyMediaGroup(this, ...params)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Send a text as a comment to this message */
|
||||||
|
commentText(...params: ParametersSkip1<TelegramClient['commentText']>) {
|
||||||
|
return this.client.commentText(this, ...params)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Send a media as a comment to this message */
|
||||||
|
commentMedia(...params: ParametersSkip1<TelegramClient['commentMedia']>) {
|
||||||
|
return this.client.commentMedia(this, ...params)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Send a media group as a comment to this message */
|
||||||
|
commentMediaGroup(...params: ParametersSkip1<TelegramClient['commentMediaGroup']>) {
|
||||||
|
return this.client.commentMediaGroup(this, ...params)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Delete this message (message group) */
|
||||||
|
delete(params?: DeleteMessagesParams) {
|
||||||
|
return this.client.deleteMessagesById(
|
||||||
|
this.chat.inputPeer,
|
||||||
|
this.messages.map((it) => it.id),
|
||||||
|
params,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Pin this message */
|
||||||
|
pin(params?: OmitInputMessageId<Parameters<TelegramClient['pinMessage']>[0]>) {
|
||||||
|
return this.client.pinMessage({
|
||||||
|
chatId: this.chat.inputPeer,
|
||||||
|
message: this.id,
|
||||||
|
...params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Unpin this message */
|
||||||
|
unpin() {
|
||||||
|
return this.client.unpinMessage({
|
||||||
|
chatId: this.chat.inputPeer,
|
||||||
|
message: this.id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Edit this message */
|
||||||
|
edit(params: OmitInputMessageId<Parameters<TelegramClient['editMessage']>[0]>) {
|
||||||
|
return this.client.editMessage({
|
||||||
|
chatId: this.chat.inputPeer,
|
||||||
|
message: this.id,
|
||||||
|
...params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Forward this message (message group) */
|
||||||
|
forwardTo(params: ForwardMessageOptions) {
|
||||||
|
return this.client.forwardMessagesById({
|
||||||
|
fromChatId: this.chat.inputPeer,
|
||||||
|
messages: this.messages.map((it) => it.id),
|
||||||
|
...params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Send a copy of this message (message group) */
|
||||||
|
copy(params: SendCopyParams & SendCopyGroupParams) {
|
||||||
|
if (this.isMessageGroup) {
|
||||||
|
return this.client.sendCopyGroup({
|
||||||
|
messages: this.messages,
|
||||||
|
...params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.client.sendCopy({
|
||||||
|
message: this,
|
||||||
|
...params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** React to this message */
|
||||||
|
react(params: OmitInputMessageId<Parameters<TelegramClient['sendReaction']>[0]>) {
|
||||||
|
return this.client.sendReaction({
|
||||||
|
chatId: this.chat.inputPeer,
|
||||||
|
message: this.id,
|
||||||
|
...params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
37
packages/dispatcher/src/context/parse.ts
Normal file
37
packages/dispatcher/src/context/parse.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { ParsedUpdate, TelegramClient } from '@mtcute/client'
|
||||||
|
|
||||||
|
import { UpdateContext } from './base'
|
||||||
|
import { CallbackQueryContext } from './callback-query'
|
||||||
|
import { ChatJoinRequestUpdateContext } from './chat-join-request'
|
||||||
|
import { ChosenInlineResultContext } from './chosen-inline-result'
|
||||||
|
import { InlineQueryContext } from './inline-query'
|
||||||
|
import { MessageContext } from './message'
|
||||||
|
import { PreCheckoutQueryContext } from './pre-checkout-query'
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
export function _parsedUpdateToContext(client: TelegramClient, update: ParsedUpdate) {
|
||||||
|
switch (update.name) {
|
||||||
|
case 'new_message':
|
||||||
|
case 'edit_message':
|
||||||
|
case 'message_group':
|
||||||
|
return new MessageContext(client, update.data)
|
||||||
|
case 'inline_query':
|
||||||
|
return new InlineQueryContext(client, update.data)
|
||||||
|
case 'chosen_inline_result':
|
||||||
|
return new ChosenInlineResultContext(client, update.data)
|
||||||
|
case 'callback_query':
|
||||||
|
return new CallbackQueryContext(client, update.data)
|
||||||
|
case 'bot_chat_join_request':
|
||||||
|
return new ChatJoinRequestUpdateContext(client, update.data)
|
||||||
|
case 'pre_checkout_query':
|
||||||
|
return new PreCheckoutQueryContext(client, update.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
const _update = update.data as UpdateContext<typeof update.data>
|
||||||
|
_update.client = client
|
||||||
|
_update._name = update.name
|
||||||
|
|
||||||
|
return _update
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UpdateContextType = ReturnType<typeof _parsedUpdateToContext>
|
29
packages/dispatcher/src/context/pre-checkout-query.ts
Normal file
29
packages/dispatcher/src/context/pre-checkout-query.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { PreCheckoutQuery, TelegramClient } from '@mtcute/client'
|
||||||
|
|
||||||
|
import { UpdateContext } from './base'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context of a pre-checkout query update
|
||||||
|
*
|
||||||
|
* This is a subclass of {@link PreCheckoutQuery}, so all its fields are also available.
|
||||||
|
*/
|
||||||
|
export class PreCheckoutQueryContext extends PreCheckoutQuery implements UpdateContext<PreCheckoutQuery> {
|
||||||
|
readonly _name = 'pre_checkout_query'
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly client: TelegramClient,
|
||||||
|
query: PreCheckoutQuery,
|
||||||
|
) {
|
||||||
|
super(query.raw, query._peers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Approve the query */
|
||||||
|
approve(): Promise<void> {
|
||||||
|
return this.client.answerPreCheckoutQuery(this.raw.queryId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Decline the query, optionally with an error */
|
||||||
|
decline(error = ''): Promise<void> {
|
||||||
|
return this.client.answerPreCheckoutQuery(this.raw.queryId, { error })
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,32 +1,38 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable @typescript-eslint/unified-signatures,@typescript-eslint/no-unsafe-assignment */
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-argument,@typescript-eslint/no-unsafe-call,max-depth,dot-notation */
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types */
|
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types */
|
||||||
// ^^ will be looked into in MTQ-29
|
// ^^ will be looked into in MTQ-29
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BotChatJoinRequestUpdate,
|
|
||||||
BotStoppedUpdate,
|
BotStoppedUpdate,
|
||||||
CallbackQuery,
|
|
||||||
ChatJoinRequestUpdate,
|
ChatJoinRequestUpdate,
|
||||||
ChatMemberUpdate,
|
ChatMemberUpdate,
|
||||||
ChosenInlineResult,
|
|
||||||
DeleteMessageUpdate,
|
DeleteMessageUpdate,
|
||||||
|
DeleteStoryUpdate,
|
||||||
HistoryReadUpdate,
|
HistoryReadUpdate,
|
||||||
InlineQuery,
|
|
||||||
MaybeAsync,
|
MaybeAsync,
|
||||||
Message,
|
|
||||||
ParsedUpdate,
|
ParsedUpdate,
|
||||||
PeersIndex,
|
PeersIndex,
|
||||||
PollUpdate,
|
PollUpdate,
|
||||||
PollVoteUpdate,
|
PollVoteUpdate,
|
||||||
PreCheckoutQuery,
|
|
||||||
StoryUpdate,
|
StoryUpdate,
|
||||||
DeleteStoryUpdate,
|
|
||||||
TelegramClient,
|
TelegramClient,
|
||||||
|
tl,
|
||||||
UserStatusUpdate,
|
UserStatusUpdate,
|
||||||
UserTypingUpdate,
|
UserTypingUpdate,
|
||||||
tl,
|
|
||||||
} from '@mtcute/client'
|
} from '@mtcute/client'
|
||||||
|
import { MtArgumentError } from '@mtcute/core'
|
||||||
|
|
||||||
|
import {
|
||||||
|
CallbackQueryContext,
|
||||||
|
ChatJoinRequestUpdateContext,
|
||||||
|
ChosenInlineResultContext,
|
||||||
|
InlineQueryContext,
|
||||||
|
MessageContext,
|
||||||
|
PreCheckoutQueryContext,
|
||||||
|
} from './context'
|
||||||
|
import { UpdateContext } from './context/base'
|
||||||
|
import { _parsedUpdateToContext, UpdateContextType } from './context/parse'
|
||||||
import { filters, UpdateFilter } from './filters'
|
import { filters, UpdateFilter } from './filters'
|
||||||
// begin-codegen-imports
|
// begin-codegen-imports
|
||||||
import {
|
import {
|
||||||
|
@ -55,7 +61,6 @@ import {
|
||||||
// end-codegen-imports
|
// end-codegen-imports
|
||||||
import { PropagationAction } from './propagation'
|
import { PropagationAction } from './propagation'
|
||||||
import { defaultStateKeyDelegate, IStateStorage, StateKeyDelegate, UpdateState } from './state'
|
import { defaultStateKeyDelegate, IStateStorage, StateKeyDelegate, UpdateState } from './state'
|
||||||
import { MtArgumentError } from '@mtcute/core'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates dispatcher
|
* Updates dispatcher
|
||||||
|
@ -193,7 +198,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
|
|
||||||
// order does not matter in the dispatcher,
|
// order does not matter in the dispatcher,
|
||||||
// so we can handle each update in its own task
|
// so we can handle each update in its own task
|
||||||
this.dispatchRawUpdateNow(update, peers).catch((err) => this._client!['_emitError'](err))
|
this.dispatchRawUpdateNow(update, peers).catch((err) => this._client!._emitError(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -263,7 +268,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
|
|
||||||
// order does not matter in the dispatcher,
|
// order does not matter in the dispatcher,
|
||||||
// so we can handle each update in its own task
|
// so we can handle each update in its own task
|
||||||
this.dispatchUpdateNow(update).catch((err) => this._client!['_emitError'](err))
|
this.dispatchUpdateNow(update).catch((err) => this._client!._emitError(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -287,6 +292,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
parsedState?: UpdateState<State, SceneName> | null,
|
parsedState?: UpdateState<State, SceneName> | null,
|
||||||
parsedScene?: string | null,
|
parsedScene?: string | null,
|
||||||
forceScene?: true,
|
forceScene?: true,
|
||||||
|
parsedContext?: UpdateContextType,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
if (!this._client) return false
|
if (!this._client) return false
|
||||||
|
|
||||||
|
@ -301,9 +307,8 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
) {
|
) {
|
||||||
// no need to fetch scene if there are no registered scenes
|
// no need to fetch scene if there are no registered scenes
|
||||||
|
|
||||||
const key = await this._stateKeyDelegate!(
|
if (!parsedContext) parsedContext = _parsedUpdateToContext(this._client, update)
|
||||||
update.name === 'message_group' ? update.data[0] : update.data,
|
const key = await this._stateKeyDelegate!(parsedContext as any)
|
||||||
)
|
|
||||||
|
|
||||||
if (key) {
|
if (key) {
|
||||||
parsedScene = await this._storage.getCurrentScene(key)
|
parsedScene = await this._storage.getCurrentScene(key)
|
||||||
|
@ -334,16 +339,20 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
if (parsedState === undefined) {
|
if (parsedState === undefined) {
|
||||||
if (
|
if (
|
||||||
this._storage &&
|
this._storage &&
|
||||||
(update.name === 'new_message' || update.name === 'edit_message' || update.name === 'callback_query')
|
(update.name === 'new_message' ||
|
||||||
|
update.name === 'edit_message' ||
|
||||||
|
update.name === 'callback_query' ||
|
||||||
|
update.name === 'message_group')
|
||||||
) {
|
) {
|
||||||
const key = await this._stateKeyDelegate!(update.data)
|
if (!parsedContext) parsedContext = _parsedUpdateToContext(this._client, update)
|
||||||
|
const key = await this._stateKeyDelegate!(parsedContext as any)
|
||||||
|
|
||||||
if (key) {
|
if (key) {
|
||||||
let customKey
|
let customKey
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!this._customStateKeyDelegate ||
|
!this._customStateKeyDelegate ||
|
||||||
(customKey = await this._customStateKeyDelegate(update.data))
|
(customKey = await this._customStateKeyDelegate(parsedContext as any))
|
||||||
) {
|
) {
|
||||||
parsedState = new UpdateState(
|
parsedState = new UpdateState(
|
||||||
this._storage,
|
this._storage,
|
||||||
|
@ -386,8 +395,9 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
for (const h of handlers) {
|
for (const h of handlers) {
|
||||||
let result: void | PropagationAction
|
let result: void | PropagationAction
|
||||||
|
|
||||||
if (!h.check || (await h.check(update.data as any, parsedState as never))) {
|
if (!parsedContext) parsedContext = _parsedUpdateToContext(this._client, update)
|
||||||
result = await h.callback(update.data as any, parsedState as never)
|
if (!h.check || (await h.check(parsedContext as any, parsedState as never))) {
|
||||||
|
result = await h.callback(parsedContext as any, parsedState as never)
|
||||||
handled = true
|
handled = true
|
||||||
} else continue
|
} else continue
|
||||||
|
|
||||||
|
@ -436,7 +446,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._postUpdateHandler?.(handled, update, parsedState as any)
|
await this._postUpdateHandler?.(handled, update, parsedState as any)
|
||||||
|
|
||||||
return handled
|
return handled
|
||||||
}
|
}
|
||||||
|
@ -601,9 +611,9 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
if (child._client) {
|
if (child._client) {
|
||||||
throw new MtArgumentError(
|
throw new MtArgumentError(
|
||||||
'Provided dispatcher is ' +
|
'Provided dispatcher is ' +
|
||||||
(child._parent
|
(child._parent ?
|
||||||
? 'already a child. Use parent.removeChild() before calling addChild()'
|
'already a child. Use parent.removeChild() before calling addChild()' :
|
||||||
: 'already bound to a client. Use unbind() before calling addChild()'),
|
'already bound to a client. Use unbind() before calling addChild()'),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -953,21 +963,8 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onNewMessage(
|
onNewMessage(
|
||||||
handler: NewMessageHandler<Message, State extends never ? never : UpdateState<State, SceneName>>['callback'],
|
|
||||||
group?: number,
|
|
||||||
): void
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a new message handler with a filter
|
|
||||||
*
|
|
||||||
* @param filter Update filter
|
|
||||||
* @param handler New message handler
|
|
||||||
* @param group Handler group index
|
|
||||||
*/
|
|
||||||
onNewMessage<Mod>(
|
|
||||||
filter: UpdateFilter<Message, Mod, State>,
|
|
||||||
handler: NewMessageHandler<
|
handler: NewMessageHandler<
|
||||||
filters.Modify<Message, Mod>,
|
MessageContext,
|
||||||
State extends never ? never : UpdateState<State, SceneName>
|
State extends never ? never : UpdateState<State, SceneName>
|
||||||
>['callback'],
|
>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
|
@ -981,9 +978,25 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onNewMessage<Mod>(
|
onNewMessage<Mod>(
|
||||||
filter: UpdateFilter<Message, Mod>,
|
filter: UpdateFilter<MessageContext, Mod, State>,
|
||||||
handler: NewMessageHandler<
|
handler: NewMessageHandler<
|
||||||
filters.Modify<Message, Mod>,
|
filters.Modify<MessageContext, Mod>,
|
||||||
|
State extends never ? never : UpdateState<State, SceneName>
|
||||||
|
>['callback'],
|
||||||
|
group?: number,
|
||||||
|
): void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a new message handler with a filter
|
||||||
|
*
|
||||||
|
* @param filter Update filter
|
||||||
|
* @param handler New message handler
|
||||||
|
* @param group Handler group index
|
||||||
|
*/
|
||||||
|
onNewMessage<Mod>(
|
||||||
|
filter: UpdateFilter<MessageContext, Mod>,
|
||||||
|
handler: NewMessageHandler<
|
||||||
|
filters.Modify<MessageContext, Mod>,
|
||||||
State extends never ? never : UpdateState<State, SceneName>
|
State extends never ? never : UpdateState<State, SceneName>
|
||||||
>['callback'],
|
>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
|
@ -1001,21 +1014,8 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onEditMessage(
|
onEditMessage(
|
||||||
handler: EditMessageHandler<Message, State extends never ? never : UpdateState<State, SceneName>>['callback'],
|
|
||||||
group?: number,
|
|
||||||
): void
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register an edit message handler with a filter
|
|
||||||
*
|
|
||||||
* @param filter Update filter
|
|
||||||
* @param handler Edit message handler
|
|
||||||
* @param group Handler group index
|
|
||||||
*/
|
|
||||||
onEditMessage<Mod>(
|
|
||||||
filter: UpdateFilter<Message, Mod, State>,
|
|
||||||
handler: EditMessageHandler<
|
handler: EditMessageHandler<
|
||||||
filters.Modify<Message, Mod>,
|
MessageContext,
|
||||||
State extends never ? never : UpdateState<State, SceneName>
|
State extends never ? never : UpdateState<State, SceneName>
|
||||||
>['callback'],
|
>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
|
@ -1029,9 +1029,25 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onEditMessage<Mod>(
|
onEditMessage<Mod>(
|
||||||
filter: UpdateFilter<Message, Mod>,
|
filter: UpdateFilter<MessageContext, Mod, State>,
|
||||||
handler: EditMessageHandler<
|
handler: EditMessageHandler<
|
||||||
filters.Modify<Message, Mod>,
|
filters.Modify<MessageContext, Mod>,
|
||||||
|
State extends never ? never : UpdateState<State, SceneName>
|
||||||
|
>['callback'],
|
||||||
|
group?: number,
|
||||||
|
): void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an edit message handler with a filter
|
||||||
|
*
|
||||||
|
* @param filter Update filter
|
||||||
|
* @param handler Edit message handler
|
||||||
|
* @param group Handler group index
|
||||||
|
*/
|
||||||
|
onEditMessage<Mod>(
|
||||||
|
filter: UpdateFilter<MessageContext, Mod>,
|
||||||
|
handler: EditMessageHandler<
|
||||||
|
filters.Modify<MessageContext, Mod>,
|
||||||
State extends never ? never : UpdateState<State, SceneName>
|
State extends never ? never : UpdateState<State, SceneName>
|
||||||
>['callback'],
|
>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
|
@ -1050,7 +1066,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
*/
|
*/
|
||||||
onMessageGroup(
|
onMessageGroup(
|
||||||
handler: MessageGroupHandler<
|
handler: MessageGroupHandler<
|
||||||
Message[],
|
MessageContext,
|
||||||
State extends never ? never : UpdateState<State, SceneName>
|
State extends never ? never : UpdateState<State, SceneName>
|
||||||
>['callback'],
|
>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
|
@ -1064,9 +1080,9 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onMessageGroup<Mod>(
|
onMessageGroup<Mod>(
|
||||||
filter: UpdateFilter<Message[], Mod, State>,
|
filter: UpdateFilter<MessageContext, Mod, State>,
|
||||||
handler: MessageGroupHandler<
|
handler: MessageGroupHandler<
|
||||||
filters.Modify<Message[], Mod>,
|
filters.Modify<MessageContext, Mod>,
|
||||||
State extends never ? never : UpdateState<State, SceneName>
|
State extends never ? never : UpdateState<State, SceneName>
|
||||||
>['callback'],
|
>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
|
@ -1080,9 +1096,9 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onMessageGroup<Mod>(
|
onMessageGroup<Mod>(
|
||||||
filter: UpdateFilter<Message[], Mod>,
|
filter: UpdateFilter<MessageContext, Mod>,
|
||||||
handler: MessageGroupHandler<
|
handler: MessageGroupHandler<
|
||||||
filters.Modify<Message[], Mod>,
|
filters.Modify<MessageContext, Mod>,
|
||||||
State extends never ? never : UpdateState<State, SceneName>
|
State extends never ? never : UpdateState<State, SceneName>
|
||||||
>['callback'],
|
>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
|
@ -1109,8 +1125,8 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onDeleteMessage<Mod>(
|
onDeleteMessage<Mod>(
|
||||||
filter: UpdateFilter<DeleteMessageUpdate, Mod>,
|
filter: UpdateFilter<UpdateContext<DeleteMessageUpdate>, Mod>,
|
||||||
handler: DeleteMessageHandler<filters.Modify<DeleteMessageUpdate, Mod>>['callback'],
|
handler: DeleteMessageHandler<filters.Modify<UpdateContext<DeleteMessageUpdate>, Mod>>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
): void
|
): void
|
||||||
|
|
||||||
|
@ -1135,8 +1151,8 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onChatMemberUpdate<Mod>(
|
onChatMemberUpdate<Mod>(
|
||||||
filter: UpdateFilter<ChatMemberUpdate, Mod>,
|
filter: UpdateFilter<UpdateContext<ChatMemberUpdate>, Mod>,
|
||||||
handler: ChatMemberUpdateHandler<filters.Modify<ChatMemberUpdate, Mod>>['callback'],
|
handler: ChatMemberUpdateHandler<filters.Modify<UpdateContext<ChatMemberUpdate>, Mod>>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
): void
|
): void
|
||||||
|
|
||||||
|
@ -1161,8 +1177,8 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onInlineQuery<Mod>(
|
onInlineQuery<Mod>(
|
||||||
filter: UpdateFilter<InlineQuery, Mod>,
|
filter: UpdateFilter<InlineQueryContext, Mod>,
|
||||||
handler: InlineQueryHandler<filters.Modify<InlineQuery, Mod>>['callback'],
|
handler: InlineQueryHandler<filters.Modify<InlineQueryContext, Mod>>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
): void
|
): void
|
||||||
|
|
||||||
|
@ -1187,8 +1203,8 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onChosenInlineResult<Mod>(
|
onChosenInlineResult<Mod>(
|
||||||
filter: UpdateFilter<ChosenInlineResult, Mod>,
|
filter: UpdateFilter<ChosenInlineResultContext, Mod>,
|
||||||
handler: ChosenInlineResultHandler<filters.Modify<ChosenInlineResult, Mod>>['callback'],
|
handler: ChosenInlineResultHandler<filters.Modify<ChosenInlineResultContext, Mod>>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
): void
|
): void
|
||||||
|
|
||||||
|
@ -1205,7 +1221,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
*/
|
*/
|
||||||
onCallbackQuery(
|
onCallbackQuery(
|
||||||
handler: CallbackQueryHandler<
|
handler: CallbackQueryHandler<
|
||||||
CallbackQuery,
|
CallbackQueryContext,
|
||||||
State extends never ? never : UpdateState<State, SceneName>
|
State extends never ? never : UpdateState<State, SceneName>
|
||||||
>['callback'],
|
>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
|
@ -1219,9 +1235,9 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onCallbackQuery<Mod>(
|
onCallbackQuery<Mod>(
|
||||||
filter: UpdateFilter<CallbackQuery, Mod, State>,
|
filter: UpdateFilter<CallbackQueryContext, Mod, State>,
|
||||||
handler: CallbackQueryHandler<
|
handler: CallbackQueryHandler<
|
||||||
filters.Modify<CallbackQuery, Mod>,
|
filters.Modify<CallbackQueryContext, Mod>,
|
||||||
State extends never ? never : UpdateState<State, SceneName>
|
State extends never ? never : UpdateState<State, SceneName>
|
||||||
>['callback'],
|
>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
|
@ -1235,9 +1251,9 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onCallbackQuery<Mod>(
|
onCallbackQuery<Mod>(
|
||||||
filter: UpdateFilter<CallbackQuery, Mod>,
|
filter: UpdateFilter<CallbackQueryContext, Mod>,
|
||||||
handler: CallbackQueryHandler<
|
handler: CallbackQueryHandler<
|
||||||
filters.Modify<CallbackQuery, Mod>,
|
filters.Modify<CallbackQueryContext, Mod>,
|
||||||
State extends never ? never : UpdateState<State, SceneName>
|
State extends never ? never : UpdateState<State, SceneName>
|
||||||
>['callback'],
|
>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
|
@ -1264,8 +1280,8 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onPollUpdate<Mod>(
|
onPollUpdate<Mod>(
|
||||||
filter: UpdateFilter<PollUpdate, Mod>,
|
filter: UpdateFilter<UpdateContext<PollUpdate>, Mod>,
|
||||||
handler: PollUpdateHandler<filters.Modify<PollUpdate, Mod>>['callback'],
|
handler: PollUpdateHandler<filters.Modify<UpdateContext<PollUpdate>, Mod>>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
): void
|
): void
|
||||||
|
|
||||||
|
@ -1290,8 +1306,8 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onPollVote<Mod>(
|
onPollVote<Mod>(
|
||||||
filter: UpdateFilter<PollVoteUpdate, Mod>,
|
filter: UpdateFilter<UpdateContext<PollVoteUpdate>, Mod>,
|
||||||
handler: PollVoteHandler<filters.Modify<PollVoteUpdate, Mod>>['callback'],
|
handler: PollVoteHandler<filters.Modify<UpdateContext<PollVoteUpdate>, Mod>>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
): void
|
): void
|
||||||
|
|
||||||
|
@ -1316,8 +1332,8 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onUserStatusUpdate<Mod>(
|
onUserStatusUpdate<Mod>(
|
||||||
filter: UpdateFilter<UserStatusUpdate, Mod>,
|
filter: UpdateFilter<UpdateContext<UserStatusUpdate>, Mod>,
|
||||||
handler: UserStatusUpdateHandler<filters.Modify<UserStatusUpdate, Mod>>['callback'],
|
handler: UserStatusUpdateHandler<filters.Modify<UpdateContext<UserStatusUpdate>, Mod>>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
): void
|
): void
|
||||||
|
|
||||||
|
@ -1342,8 +1358,8 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onUserTyping<Mod>(
|
onUserTyping<Mod>(
|
||||||
filter: UpdateFilter<UserTypingUpdate, Mod>,
|
filter: UpdateFilter<UpdateContext<UserTypingUpdate>, Mod>,
|
||||||
handler: UserTypingHandler<filters.Modify<UserTypingUpdate, Mod>>['callback'],
|
handler: UserTypingHandler<filters.Modify<UpdateContext<UserTypingUpdate>, Mod>>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
): void
|
): void
|
||||||
|
|
||||||
|
@ -1368,8 +1384,8 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onHistoryRead<Mod>(
|
onHistoryRead<Mod>(
|
||||||
filter: UpdateFilter<HistoryReadUpdate, Mod>,
|
filter: UpdateFilter<UpdateContext<HistoryReadUpdate>, Mod>,
|
||||||
handler: HistoryReadHandler<filters.Modify<HistoryReadUpdate, Mod>>['callback'],
|
handler: HistoryReadHandler<filters.Modify<UpdateContext<HistoryReadUpdate>, Mod>>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
): void
|
): void
|
||||||
|
|
||||||
|
@ -1394,8 +1410,8 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onBotStopped<Mod>(
|
onBotStopped<Mod>(
|
||||||
filter: UpdateFilter<BotStoppedUpdate, Mod>,
|
filter: UpdateFilter<UpdateContext<BotStoppedUpdate>, Mod>,
|
||||||
handler: BotStoppedHandler<filters.Modify<BotStoppedUpdate, Mod>>['callback'],
|
handler: BotStoppedHandler<filters.Modify<UpdateContext<BotStoppedUpdate>, Mod>>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
): void
|
): void
|
||||||
|
|
||||||
|
@ -1420,8 +1436,8 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onBotChatJoinRequest<Mod>(
|
onBotChatJoinRequest<Mod>(
|
||||||
filter: UpdateFilter<BotChatJoinRequestUpdate, Mod>,
|
filter: UpdateFilter<ChatJoinRequestUpdateContext, Mod>,
|
||||||
handler: BotChatJoinRequestHandler<filters.Modify<BotChatJoinRequestUpdate, Mod>>['callback'],
|
handler: BotChatJoinRequestHandler<filters.Modify<ChatJoinRequestUpdateContext, Mod>>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
): void
|
): void
|
||||||
|
|
||||||
|
@ -1446,8 +1462,8 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onChatJoinRequest<Mod>(
|
onChatJoinRequest<Mod>(
|
||||||
filter: UpdateFilter<ChatJoinRequestUpdate, Mod>,
|
filter: UpdateFilter<UpdateContext<ChatJoinRequestUpdate>, Mod>,
|
||||||
handler: ChatJoinRequestHandler<filters.Modify<ChatJoinRequestUpdate, Mod>>['callback'],
|
handler: ChatJoinRequestHandler<filters.Modify<UpdateContext<ChatJoinRequestUpdate>, Mod>>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
): void
|
): void
|
||||||
|
|
||||||
|
@ -1472,8 +1488,8 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onPreCheckoutQuery<Mod>(
|
onPreCheckoutQuery<Mod>(
|
||||||
filter: UpdateFilter<PreCheckoutQuery, Mod>,
|
filter: UpdateFilter<PreCheckoutQueryContext, Mod>,
|
||||||
handler: PreCheckoutQueryHandler<filters.Modify<PreCheckoutQuery, Mod>>['callback'],
|
handler: PreCheckoutQueryHandler<filters.Modify<PreCheckoutQueryContext, Mod>>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
): void
|
): void
|
||||||
|
|
||||||
|
@ -1498,8 +1514,8 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onStoryUpdate<Mod>(
|
onStoryUpdate<Mod>(
|
||||||
filter: UpdateFilter<StoryUpdate, Mod>,
|
filter: UpdateFilter<UpdateContext<StoryUpdate>, Mod>,
|
||||||
handler: StoryUpdateHandler<filters.Modify<StoryUpdate, Mod>>['callback'],
|
handler: StoryUpdateHandler<filters.Modify<UpdateContext<StoryUpdate>, Mod>>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
): void
|
): void
|
||||||
|
|
||||||
|
@ -1524,8 +1540,8 @@ export class Dispatcher<State = never, SceneName extends string = string> {
|
||||||
* @param group Handler group index
|
* @param group Handler group index
|
||||||
*/
|
*/
|
||||||
onDeleteStory<Mod>(
|
onDeleteStory<Mod>(
|
||||||
filter: UpdateFilter<DeleteStoryUpdate, Mod>,
|
filter: UpdateFilter<UpdateContext<DeleteStoryUpdate>, Mod>,
|
||||||
handler: DeleteStoryHandler<filters.Modify<DeleteStoryUpdate, Mod>>['callback'],
|
handler: DeleteStoryHandler<filters.Modify<UpdateContext<DeleteStoryUpdate>, Mod>>['callback'],
|
||||||
group?: number,
|
group?: number,
|
||||||
): void
|
): void
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Message } from '@mtcute/client'
|
import { Message } from '@mtcute/client'
|
||||||
import { MaybeArray, MaybeAsync } from '@mtcute/core'
|
import { MaybeArray, MaybeAsync } from '@mtcute/core'
|
||||||
|
|
||||||
|
import { MessageContext } from '../context'
|
||||||
import { chat } from './chat'
|
import { chat } from './chat'
|
||||||
import { and } from './logic'
|
import { and } from './logic'
|
||||||
import { UpdateFilter } from './types'
|
import { UpdateFilter } from './types'
|
||||||
|
@ -25,7 +26,7 @@ export const command = (
|
||||||
commands: MaybeArray<string | RegExp>,
|
commands: MaybeArray<string | RegExp>,
|
||||||
prefixes: MaybeArray<string> | null = '/',
|
prefixes: MaybeArray<string> | null = '/',
|
||||||
caseSensitive = false,
|
caseSensitive = false,
|
||||||
): UpdateFilter<Message, { command: string[] }> => {
|
): UpdateFilter<MessageContext, { command: string[] }> => {
|
||||||
if (!Array.isArray(commands)) commands = [commands]
|
if (!Array.isArray(commands)) commands = [commands]
|
||||||
|
|
||||||
commands = commands.map((i) => (typeof i === 'string' ? i.toLowerCase() : i))
|
commands = commands.map((i) => (typeof i === 'string' ? i.toLowerCase() : i))
|
||||||
|
@ -44,7 +45,9 @@ export const command = (
|
||||||
|
|
||||||
const _prefixes = prefixes
|
const _prefixes = prefixes
|
||||||
|
|
||||||
const check = (msg: Message): MaybeAsync<boolean> => {
|
const check = (msg: MessageContext): MaybeAsync<boolean> => {
|
||||||
|
if (msg.isMessageGroup) return check(msg.messages[0])
|
||||||
|
|
||||||
for (const pref of _prefixes) {
|
for (const pref of _prefixes) {
|
||||||
if (!msg.text.startsWith(pref)) continue
|
if (!msg.text.startsWith(pref)) continue
|
||||||
|
|
||||||
|
@ -54,17 +57,15 @@ export const command = (
|
||||||
const m = withoutPrefix.match(regex)
|
const m = withoutPrefix.match(regex)
|
||||||
if (!m) continue
|
if (!m) continue
|
||||||
|
|
||||||
// const lastGroup = m[m.length - 1]
|
const lastGroup = m[m.length - 1]
|
||||||
|
|
||||||
// eslint-disable-next-line dot-notation
|
if (lastGroup) {
|
||||||
// todo
|
const state = msg.client.getAuthState()
|
||||||
// if (lastGroup && msg.client['_isBot']) {
|
|
||||||
// // check bot username
|
if (state.isBot && lastGroup !== state.selfUsername) {
|
||||||
// // eslint-disable-next-line dot-notation
|
return false
|
||||||
// if (lastGroup !== msg.client['_selfUsername']) {
|
}
|
||||||
// return false
|
}
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
const match = m.slice(1, -1)
|
const match = m.slice(1, -1)
|
||||||
|
|
||||||
|
@ -74,7 +75,7 @@ export const command = (
|
||||||
|
|
||||||
return ''
|
return ''
|
||||||
})
|
})
|
||||||
;(msg as Message & { command: string[] }).command = match
|
;(msg as MessageContext & { command: string[] }).command = match
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -98,7 +99,7 @@ export const start = and(chat('private'), command('start'))
|
||||||
* If the parameter is a regex, groups are added to `msg.command`,
|
* If the parameter is a regex, groups are added to `msg.command`,
|
||||||
* meaning that the first group is available in `msg.command[2]`.
|
* meaning that the first group is available in `msg.command[2]`.
|
||||||
*/
|
*/
|
||||||
export const deeplink = (params: MaybeArray<string | RegExp>): UpdateFilter<Message, { command: string[] }> => {
|
export const deeplink = (params: MaybeArray<string | RegExp>): UpdateFilter<MessageContext, { command: string[] }> => {
|
||||||
if (!Array.isArray(params)) {
|
if (!Array.isArray(params)) {
|
||||||
return and(start, (_msg: Message) => {
|
return and(start, (_msg: Message) => {
|
||||||
const msg = _msg as Message & { command: string[] }
|
const msg = _msg as Message & { command: string[] }
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
export * from './bots'
|
export * from './bots'
|
||||||
export * from './chat'
|
export * from './chat'
|
||||||
|
export * from './group'
|
||||||
export * from './logic'
|
export * from './logic'
|
||||||
export * from './message'
|
export * from './message'
|
||||||
export * from './state'
|
export * from './state'
|
||||||
|
|
|
@ -1,6 +1,17 @@
|
||||||
import { Chat, ChatType, Message, PollVoteUpdate, User } from '@mtcute/client'
|
import {
|
||||||
|
BotChatJoinRequestUpdate,
|
||||||
|
Chat,
|
||||||
|
ChatMemberUpdate,
|
||||||
|
ChatType,
|
||||||
|
HistoryReadUpdate,
|
||||||
|
Message,
|
||||||
|
PollVoteUpdate,
|
||||||
|
User,
|
||||||
|
UserTypingUpdate,
|
||||||
|
} from '@mtcute/client'
|
||||||
import { MaybeArray } from '@mtcute/core'
|
import { MaybeArray } from '@mtcute/core'
|
||||||
|
|
||||||
|
import { UpdateContextDistributed } from '../context'
|
||||||
import { Modify, UpdateFilter } from './types'
|
import { Modify, UpdateFilter } from './types'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -19,59 +30,68 @@ export const chat =
|
||||||
(msg) =>
|
(msg) =>
|
||||||
msg.chat.chatType === type
|
msg.chat.chatType === type
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
/**
|
/**
|
||||||
* Filter updates by chat ID(s) or username(s)
|
* Filter updates by marked chat ID(s) or username(s)
|
||||||
|
*
|
||||||
|
* Note that only some updates support filtering by username.
|
||||||
|
*
|
||||||
|
* For messages, this filter checks for chat where the message
|
||||||
|
* was sent to, NOT the chat sender.
|
||||||
*/
|
*/
|
||||||
export const chatId = (id: MaybeArray<number | string>): UpdateFilter<Message | PollVoteUpdate> => {
|
export const chatId: {
|
||||||
if (Array.isArray(id)) {
|
(id: MaybeArray<number>): UpdateFilter<UpdateContextDistributed<
|
||||||
const index: Record<number | string, true> = {}
|
| Message
|
||||||
let matchSelf = false
|
| ChatMemberUpdate
|
||||||
id.forEach((id) => {
|
| PollVoteUpdate
|
||||||
if (id === 'me' || id === 'self') {
|
| BotChatJoinRequestUpdate
|
||||||
matchSelf = true
|
>>
|
||||||
} else {
|
(id: MaybeArray<number | string>): UpdateFilter<UpdateContextDistributed<
|
||||||
index[id] = true
|
| Message
|
||||||
}
|
| ChatMemberUpdate
|
||||||
})
|
| UserTypingUpdate
|
||||||
|
| HistoryReadUpdate
|
||||||
|
| PollVoteUpdate
|
||||||
|
| BotChatJoinRequestUpdate
|
||||||
|
>>
|
||||||
|
} = (id) => {
|
||||||
|
const indexId = new Set<number>()
|
||||||
|
const indexUsername = new Set<string>()
|
||||||
|
let matchSelf = false
|
||||||
|
|
||||||
return (upd) => {
|
if (!Array.isArray(id)) id = [id]
|
||||||
if (upd.constructor === PollVoteUpdate) {
|
id.forEach((id) => {
|
||||||
const peer = upd.peer
|
if (id === 'me' || id === 'self') {
|
||||||
|
matchSelf = true
|
||||||
return peer.type === 'chat' && peer.id in index
|
} else if (typeof id === 'number') {
|
||||||
}
|
indexId.add(id)
|
||||||
|
} else {
|
||||||
const chat = (upd as Exclude<typeof upd, PollVoteUpdate>).chat
|
indexUsername.add(id)
|
||||||
|
|
||||||
return (matchSelf && chat.isSelf) || chat.id in index || chat.username! in index
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
if (id === 'me' || id === 'self') {
|
|
||||||
return (upd) => {
|
|
||||||
if (upd.constructor === PollVoteUpdate) {
|
|
||||||
return upd.peer.type === 'chat' && upd.peer.isSelf
|
|
||||||
}
|
|
||||||
|
|
||||||
return (upd as Exclude<typeof upd, PollVoteUpdate>).chat.isSelf
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof id === 'string') {
|
|
||||||
return (upd) => {
|
|
||||||
if (upd.constructor === PollVoteUpdate) {
|
|
||||||
return upd.peer.type === 'chat' && upd.peer.username === id
|
|
||||||
}
|
|
||||||
|
|
||||||
return (upd as Exclude<typeof upd, PollVoteUpdate>).chat.username === id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (upd) => {
|
return (upd) => {
|
||||||
if (upd.constructor === PollVoteUpdate) {
|
switch (upd._name) {
|
||||||
return upd.peer.type === 'chat' && upd.peer.id === id
|
case 'poll_vote': {
|
||||||
|
const peer = upd.peer
|
||||||
|
|
||||||
|
return peer.type === 'chat' && (
|
||||||
|
indexId.has(peer.id) ||
|
||||||
|
Boolean(peer.usernames?.some((u) => indexUsername.has(u.username)))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case 'history_read':
|
||||||
|
case 'user_typing': {
|
||||||
|
const id = upd.chatId
|
||||||
|
|
||||||
|
return (matchSelf && id === upd.client.getAuthState().userId) || indexId.has(id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (upd as Exclude<typeof upd, PollVoteUpdate>).chat.id === id
|
const chat = upd.chat
|
||||||
|
|
||||||
|
return (matchSelf && chat.isSelf) ||
|
||||||
|
indexId.has(chat.id) ||
|
||||||
|
Boolean(chat.usernames?.some((u) => indexUsername.has(u.username)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
88
packages/dispatcher/src/filters/group.ts
Normal file
88
packages/dispatcher/src/filters/group.ts
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
import { Message } from '@mtcute/client'
|
||||||
|
import { MaybeAsync } from '@mtcute/core'
|
||||||
|
|
||||||
|
import { MessageContext } from '../context'
|
||||||
|
import { Modify, UpdateFilter } from './types'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For message groups, apply a filter to every message in the group.
|
||||||
|
* Filter will match if **all** messages match.
|
||||||
|
*
|
||||||
|
* > **Note**: This also applies type modification to every message in the group
|
||||||
|
*
|
||||||
|
* @param filter
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function every<Mod, State>(
|
||||||
|
filter: UpdateFilter<Message, Mod, State>,
|
||||||
|
): UpdateFilter<
|
||||||
|
MessageContext,
|
||||||
|
Mod & {
|
||||||
|
messages: Modify<MessageContext, Mod>[]
|
||||||
|
},
|
||||||
|
State
|
||||||
|
> {
|
||||||
|
return (ctx, state) => {
|
||||||
|
let i = 0
|
||||||
|
const upds = ctx.messages
|
||||||
|
const max = upds.length
|
||||||
|
|
||||||
|
const next = (): MaybeAsync<boolean> => {
|
||||||
|
if (i === max) return true
|
||||||
|
|
||||||
|
const res = filter(upds[i++], state)
|
||||||
|
|
||||||
|
if (typeof res === 'boolean') {
|
||||||
|
if (!res) return false
|
||||||
|
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.then((r: boolean) => {
|
||||||
|
if (!r) return false
|
||||||
|
|
||||||
|
return next()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For message groups, apply a filter to every message in the group.
|
||||||
|
* Filter will match if **any** message matches.
|
||||||
|
*
|
||||||
|
* > **Note**: This *does not* apply type modification to any message
|
||||||
|
*
|
||||||
|
* @param filter
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line
|
||||||
|
export function some<State>(filter: UpdateFilter<Message, any, State>): UpdateFilter<MessageContext, {}, State> {
|
||||||
|
return (ctx, state) => {
|
||||||
|
let i = 0
|
||||||
|
const upds = ctx.messages
|
||||||
|
const max = upds.length
|
||||||
|
|
||||||
|
const next = (): MaybeAsync<boolean> => {
|
||||||
|
if (i === max) return false
|
||||||
|
|
||||||
|
const res = filter(upds[i++], state)
|
||||||
|
|
||||||
|
if (typeof res === 'boolean') {
|
||||||
|
if (res) return true
|
||||||
|
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.then((r: boolean) => {
|
||||||
|
if (r) return true
|
||||||
|
|
||||||
|
return next()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
}
|
|
@ -256,8 +256,6 @@ export function or<
|
||||||
* @param fns Filters to combine
|
* @param fns Filters to combine
|
||||||
*/
|
*/
|
||||||
export function or(...fns: UpdateFilter<any, any, any>[]): UpdateFilter<any, any, any> {
|
export function or(...fns: UpdateFilter<any, any, any>[]): UpdateFilter<any, any, any> {
|
||||||
if (fns.length === 2) return or(fns[0], fns[1])
|
|
||||||
|
|
||||||
return (upd, state) => {
|
return (upd, state) => {
|
||||||
let i = 0
|
let i = 0
|
||||||
const max = fns.length
|
const max = fns.length
|
||||||
|
@ -283,79 +281,3 @@ export function or(...fns: UpdateFilter<any, any, any>[]): UpdateFilter<any, any
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* For updates that contain an array of updates (e.g. `message_group`),
|
|
||||||
* apply a filter to every element of the array.
|
|
||||||
*
|
|
||||||
* Filter will match if **all** elements match.
|
|
||||||
*
|
|
||||||
* > **Note**: This also applies type modification to every element of the array.
|
|
||||||
*
|
|
||||||
* @param filter
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function every<Base, Mod, State>(filter: UpdateFilter<Base, Mod, State>): UpdateFilter<Base[], Mod, State> {
|
|
||||||
return (upds, state) => {
|
|
||||||
let i = 0
|
|
||||||
const max = upds.length
|
|
||||||
|
|
||||||
const next = (): MaybeAsync<boolean> => {
|
|
||||||
if (i === max) return true
|
|
||||||
|
|
||||||
const res = filter(upds[i++], state)
|
|
||||||
|
|
||||||
if (typeof res === 'boolean') {
|
|
||||||
if (!res) return false
|
|
||||||
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.then((r: boolean) => {
|
|
||||||
if (!r) return false
|
|
||||||
|
|
||||||
return next()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For updates that contain an array of updates (e.g. `message_group`),
|
|
||||||
* apply a filter to every element of the array.
|
|
||||||
*
|
|
||||||
* Filter will match if **all** elements match.
|
|
||||||
*
|
|
||||||
* > **Note**: This *does not* apply type modification to any element of the array
|
|
||||||
*
|
|
||||||
* @param filter
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function some<Base, Mod, State>(filter: UpdateFilter<Base, Mod, State>): UpdateFilter<Base[], Mod, State> {
|
|
||||||
return (upds, state) => {
|
|
||||||
let i = 0
|
|
||||||
const max = upds.length
|
|
||||||
|
|
||||||
const next = (): MaybeAsync<boolean> => {
|
|
||||||
if (i === max) return false
|
|
||||||
|
|
||||||
const res = filter(upds[i++], state)
|
|
||||||
|
|
||||||
if (typeof res === 'boolean') {
|
|
||||||
if (res) return true
|
|
||||||
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.then((r: boolean) => {
|
|
||||||
if (r) return true
|
|
||||||
|
|
||||||
return next()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -201,3 +201,10 @@ export const action = <T extends Exclude<MessageAction, null>['type']>(
|
||||||
|
|
||||||
return (msg) => msg.action?.type === type
|
return (msg) => msg.action?.type === type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const sender =
|
||||||
|
<T extends Message['sender']['type']>(
|
||||||
|
type: T,
|
||||||
|
): UpdateFilter<Message, { sender: Extract<Message['sender'], { type: T }> }> =>
|
||||||
|
(msg) =>
|
||||||
|
msg.sender.type === type
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { CallbackQuery, Message } from '@mtcute/client'
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import { MaybeAsync } from '@mtcute/core'
|
import { MaybeAsync } from '@mtcute/core'
|
||||||
|
|
||||||
import { UpdateFilter } from './types'
|
import { UpdateFilter } from './types'
|
||||||
|
@ -6,7 +6,7 @@ import { UpdateFilter } from './types'
|
||||||
/**
|
/**
|
||||||
* Create a filter for the cases when the state is empty
|
* Create a filter for the cases when the state is empty
|
||||||
*/
|
*/
|
||||||
export const stateEmpty: UpdateFilter<Message> = async (upd, state) => {
|
export const stateEmpty: UpdateFilter<any> = async (upd, state) => {
|
||||||
if (!state) return false
|
if (!state) return false
|
||||||
|
|
||||||
return !(await state.get())
|
return !(await state.get())
|
||||||
|
@ -23,7 +23,7 @@ export const stateEmpty: UpdateFilter<Message> = async (upd, state) => {
|
||||||
export const state = <T>(
|
export const state = <T>(
|
||||||
predicate: (state: T) => MaybeAsync<boolean>,
|
predicate: (state: T) => MaybeAsync<boolean>,
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
): UpdateFilter<Message | Message[] | CallbackQuery, {}, T> => {
|
): UpdateFilter<any, {}, T> => {
|
||||||
return async (upd, state) => {
|
return async (upd, state) => {
|
||||||
if (!state) return false
|
if (!state) return false
|
||||||
const data = await state.get()
|
const data = await state.get()
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
// ^^ will be looked into in MTQ-29
|
|
||||||
|
|
||||||
import { CallbackQuery, ChosenInlineResult, InlineQuery, Message } from '@mtcute/client'
|
import { CallbackQuery, ChosenInlineResult, InlineQuery, Message } from '@mtcute/client'
|
||||||
|
|
||||||
|
import { UpdateContextDistributed } from '../context'
|
||||||
import { UpdateFilter } from './types'
|
import { UpdateFilter } from './types'
|
||||||
|
|
||||||
function extractText(obj: Message | InlineQuery | ChosenInlineResult | CallbackQuery): string | null {
|
type UpdatesWithText = UpdateContextDistributed<Message | InlineQuery | ChosenInlineResult | CallbackQuery>
|
||||||
if (obj.constructor === Message) {
|
|
||||||
return obj.text
|
function extractText(obj: UpdatesWithText): string | null {
|
||||||
} else if (obj.constructor === InlineQuery) {
|
switch (obj._name) {
|
||||||
return obj.query
|
case 'new_message':
|
||||||
} else if (obj.constructor === ChosenInlineResult) {
|
return obj.text
|
||||||
return obj.id
|
case 'inline_query':
|
||||||
} else if (obj.constructor === CallbackQuery) {
|
return obj.query
|
||||||
if (obj.raw.data) return obj.dataStr
|
case 'chosen_inline_result':
|
||||||
|
return obj.id
|
||||||
|
case 'callback_query':
|
||||||
|
if (obj.raw.data) return obj.dataStr
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
@ -31,9 +33,7 @@ function extractText(obj: Message | InlineQuery | ChosenInlineResult | CallbackQ
|
||||||
* @param regex Regex to be matched
|
* @param regex Regex to be matched
|
||||||
*/
|
*/
|
||||||
export const regex =
|
export const regex =
|
||||||
(
|
(regex: RegExp): UpdateFilter<UpdatesWithText, { match: RegExpMatchArray }> =>
|
||||||
regex: RegExp,
|
|
||||||
): UpdateFilter<Message | InlineQuery | ChosenInlineResult | CallbackQuery, { match: RegExpMatchArray }> =>
|
|
||||||
(obj) => {
|
(obj) => {
|
||||||
const txt = extractText(obj)
|
const txt = extractText(obj)
|
||||||
if (!txt) return false
|
if (!txt) return false
|
||||||
|
@ -59,10 +59,7 @@ export const regex =
|
||||||
* @param str String to be matched
|
* @param str String to be matched
|
||||||
* @param ignoreCase Whether string case should be ignored
|
* @param ignoreCase Whether string case should be ignored
|
||||||
*/
|
*/
|
||||||
export const equals = (
|
export const equals = (str: string, ignoreCase = false): UpdateFilter<UpdatesWithText> => {
|
||||||
str: string,
|
|
||||||
ignoreCase = false,
|
|
||||||
): UpdateFilter<Message | InlineQuery | ChosenInlineResult | CallbackQuery> => {
|
|
||||||
if (ignoreCase) {
|
if (ignoreCase) {
|
||||||
str = str.toLowerCase()
|
str = str.toLowerCase()
|
||||||
|
|
||||||
|
@ -82,10 +79,7 @@ export const equals = (
|
||||||
* @param str Substring to be matched
|
* @param str Substring to be matched
|
||||||
* @param ignoreCase Whether string case should be ignored
|
* @param ignoreCase Whether string case should be ignored
|
||||||
*/
|
*/
|
||||||
export const contains = (
|
export const contains = (str: string, ignoreCase = false): UpdateFilter<UpdatesWithText> => {
|
||||||
str: string,
|
|
||||||
ignoreCase = false,
|
|
||||||
): UpdateFilter<Message | InlineQuery | ChosenInlineResult | CallbackQuery> => {
|
|
||||||
if (ignoreCase) {
|
if (ignoreCase) {
|
||||||
str = str.toLowerCase()
|
str = str.toLowerCase()
|
||||||
|
|
||||||
|
@ -113,10 +107,7 @@ export const contains = (
|
||||||
* @param str Substring to be matched
|
* @param str Substring to be matched
|
||||||
* @param ignoreCase Whether string case should be ignored
|
* @param ignoreCase Whether string case should be ignored
|
||||||
*/
|
*/
|
||||||
export const startsWith = (
|
export const startsWith = (str: string, ignoreCase = false): UpdateFilter<UpdatesWithText> => {
|
||||||
str: string,
|
|
||||||
ignoreCase = false,
|
|
||||||
): UpdateFilter<Message | InlineQuery | ChosenInlineResult | CallbackQuery> => {
|
|
||||||
if (ignoreCase) {
|
if (ignoreCase) {
|
||||||
str = str.toLowerCase()
|
str = str.toLowerCase()
|
||||||
|
|
||||||
|
@ -144,10 +135,7 @@ export const startsWith = (
|
||||||
* @param str Substring to be matched
|
* @param str Substring to be matched
|
||||||
* @param ignoreCase Whether string case should be ignored
|
* @param ignoreCase Whether string case should be ignored
|
||||||
*/
|
*/
|
||||||
export const endsWith = (
|
export const endsWith = (str: string, ignoreCase = false): UpdateFilter<UpdatesWithText> => {
|
||||||
str: string,
|
|
||||||
ignoreCase = false,
|
|
||||||
): UpdateFilter<Message | InlineQuery | ChosenInlineResult | CallbackQuery> => {
|
|
||||||
if (ignoreCase) {
|
if (ignoreCase) {
|
||||||
str = str.toLowerCase()
|
str = str.toLowerCase()
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable @typescript-eslint/ban-types */
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
// ^^ will be looked into in MTQ-29
|
// ^^ will be looked into in MTQ-29
|
||||||
|
|
||||||
|
@ -73,13 +74,13 @@ import { UpdateState } from '../state'
|
||||||
* > like `and`, `or`, etc. Those are meant to be inferred by the compiler!
|
* > like `and`, `or`, etc. Those are meant to be inferred by the compiler!
|
||||||
*/
|
*/
|
||||||
// we need the second parameter because it carries meta information
|
// we need the second parameter because it carries meta information
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
export type UpdateFilter<Base, Mod = {}, State = never> = (
|
export type UpdateFilter<Base, Mod = {}, State = never> = (
|
||||||
update: Base,
|
update: Base,
|
||||||
state?: UpdateState<State>,
|
state?: UpdateState<State>,
|
||||||
) => MaybeAsync<boolean>
|
) => MaybeAsync<boolean>
|
||||||
|
|
||||||
export type Modify<Base, Mod> = Base extends (infer T)[] ? Modify<T, Mod>[] : Omit<Base, keyof Mod> & Mod
|
export type Modify<Base, Mod> = Omit<Base, keyof Mod> & Mod
|
||||||
export type Invert<Base, Mod> = {
|
export type Invert<Base, Mod> = {
|
||||||
[P in keyof Mod & keyof Base]: Exclude<Base[P], Mod[P]>
|
[P in keyof Mod & keyof Base]: Exclude<Base[P], Mod[P]>
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,15 +3,19 @@ import {
|
||||||
CallbackQuery,
|
CallbackQuery,
|
||||||
ChatMemberUpdate,
|
ChatMemberUpdate,
|
||||||
ChosenInlineResult,
|
ChosenInlineResult,
|
||||||
|
DeleteStoryUpdate,
|
||||||
|
HistoryReadUpdate,
|
||||||
InlineQuery,
|
InlineQuery,
|
||||||
Message,
|
Message,
|
||||||
PollVoteUpdate,
|
PollVoteUpdate,
|
||||||
|
StoryUpdate,
|
||||||
User,
|
User,
|
||||||
UserStatusUpdate,
|
UserStatusUpdate,
|
||||||
UserTypingUpdate,
|
UserTypingUpdate,
|
||||||
} from '@mtcute/client'
|
} from '@mtcute/client'
|
||||||
import { MaybeArray } from '@mtcute/core'
|
import { MaybeArray } from '@mtcute/core'
|
||||||
|
|
||||||
|
import { UpdateContextDistributed } from '../context'
|
||||||
import { UpdateFilter } from './types'
|
import { UpdateFilter } from './types'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,130 +29,95 @@ export const me: UpdateFilter<Message, { sender: User }> = (msg) =>
|
||||||
*/
|
*/
|
||||||
export const bot: UpdateFilter<Message, { sender: User }> = (msg) => msg.sender.constructor === User && msg.sender.isBot
|
export const bot: UpdateFilter<Message, { sender: User }> = (msg) => msg.sender.constructor === User && msg.sender.isBot
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
/**
|
/**
|
||||||
* Filter updates by user ID(s) or username(s)
|
* Filter updates by user ID(s) or username(s)
|
||||||
*
|
*
|
||||||
* Usernames are not supported for UserStatusUpdate
|
* Note that only some updates support filtering by username.
|
||||||
* and UserTypingUpdate.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* For chat member updates, uses `user.id`
|
|
||||||
*/
|
*/
|
||||||
export const userId = (
|
export const userId: {
|
||||||
id: MaybeArray<number | string>,
|
(id: MaybeArray<number>): UpdateFilter<UpdateContextDistributed<
|
||||||
): UpdateFilter<
|
| Message
|
||||||
| Message
|
| StoryUpdate
|
||||||
| UserStatusUpdate
|
| DeleteStoryUpdate
|
||||||
| UserTypingUpdate
|
| InlineQuery
|
||||||
| InlineQuery
|
| ChatMemberUpdate
|
||||||
| ChatMemberUpdate
|
| ChosenInlineResult
|
||||||
| ChosenInlineResult
|
| CallbackQuery
|
||||||
| CallbackQuery
|
| PollVoteUpdate
|
||||||
| PollVoteUpdate
|
| BotChatJoinRequestUpdate
|
||||||
| BotChatJoinRequestUpdate
|
>>
|
||||||
> => {
|
(id: MaybeArray<number | string>): UpdateFilter<UpdateContextDistributed<
|
||||||
if (Array.isArray(id)) {
|
| Message
|
||||||
const index: Record<number | string, true> = {}
|
| UserStatusUpdate
|
||||||
let matchSelf = false
|
| UserTypingUpdate
|
||||||
id.forEach((id) => {
|
| StoryUpdate
|
||||||
if (id === 'me' || id === 'self') {
|
| HistoryReadUpdate
|
||||||
matchSelf = true
|
| DeleteStoryUpdate
|
||||||
} else {
|
| InlineQuery
|
||||||
index[id] = true
|
| ChatMemberUpdate
|
||||||
}
|
| ChosenInlineResult
|
||||||
})
|
| CallbackQuery
|
||||||
|
| PollVoteUpdate
|
||||||
|
| BotChatJoinRequestUpdate
|
||||||
|
>>
|
||||||
|
} = (id) => {
|
||||||
|
const indexId = new Set<number>()
|
||||||
|
const indexUsername = new Set<string>()
|
||||||
|
let matchSelf = false
|
||||||
|
|
||||||
return (upd) => {
|
if (!Array.isArray(id)) id = [id]
|
||||||
const ctor = upd.constructor
|
id.forEach((id) => {
|
||||||
|
if (id === 'me' || id === 'self') {
|
||||||
if (ctor === Message) {
|
matchSelf = true
|
||||||
const sender = (upd as Message).sender
|
} else if (typeof id === 'string') {
|
||||||
|
indexUsername.add(id)
|
||||||
return (matchSelf && sender.isSelf) || sender.id in index || sender.username! in index
|
} else {
|
||||||
} else if (ctor === UserStatusUpdate || ctor === UserTypingUpdate) {
|
indexId.add(id)
|
||||||
// const id = (upd as UserStatusUpdate | UserTypingUpdate).userId
|
|
||||||
|
|
||||||
return false
|
|
||||||
// todo
|
|
||||||
// eslint-disable-next-line dot-notation
|
|
||||||
// (matchSelf && id === upd.client['_userId']) || id in index
|
|
||||||
} else if (ctor === PollVoteUpdate) {
|
|
||||||
const peer = (upd as PollVoteUpdate).peer
|
|
||||||
if (peer.type !== 'user') return false
|
|
||||||
|
|
||||||
return (matchSelf && peer.isSelf) || peer.id in index || peer.username! in index
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = (upd as Exclude<typeof upd, Message | UserStatusUpdate | UserTypingUpdate | PollVoteUpdate>)
|
|
||||||
.user
|
|
||||||
|
|
||||||
return (matchSelf && user.isSelf) || user.id in index || user.username! in index
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
if (id === 'me' || id === 'self') {
|
|
||||||
return (upd) => {
|
|
||||||
const ctor = upd.constructor
|
|
||||||
|
|
||||||
if (ctor === Message) {
|
|
||||||
return (upd as Message).sender.isSelf
|
|
||||||
} else if (ctor === UserStatusUpdate || ctor === UserTypingUpdate) {
|
|
||||||
return false
|
|
||||||
// todo
|
|
||||||
// (upd as UserStatusUpdate | UserTypingUpdate).userId ===
|
|
||||||
// eslint-disable-next-line dot-notation
|
|
||||||
// upd.client['_userId']
|
|
||||||
} else if (ctor === PollVoteUpdate) {
|
|
||||||
const peer = (upd as PollVoteUpdate).peer
|
|
||||||
if (peer.type !== 'user') return false
|
|
||||||
|
|
||||||
return peer.isSelf
|
|
||||||
}
|
|
||||||
|
|
||||||
return (upd as Exclude<typeof upd, Message | UserStatusUpdate | UserTypingUpdate | PollVoteUpdate>).user
|
|
||||||
.isSelf
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof id === 'string') {
|
|
||||||
return (upd) => {
|
|
||||||
const ctor = upd.constructor
|
|
||||||
|
|
||||||
if (ctor === Message) {
|
|
||||||
return (upd as Message).sender.username === id
|
|
||||||
} else if (ctor === UserStatusUpdate || ctor === UserTypingUpdate) {
|
|
||||||
// username is not available
|
|
||||||
return false
|
|
||||||
} else if (ctor === PollVoteUpdate) {
|
|
||||||
const peer = (upd as PollVoteUpdate).peer
|
|
||||||
if (peer.type !== 'user') return false
|
|
||||||
|
|
||||||
return peer.username === id
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
(upd as Exclude<typeof upd, Message | UserStatusUpdate | UserTypingUpdate | PollVoteUpdate>).user
|
|
||||||
.username === id
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (upd) => {
|
return (upd) => {
|
||||||
const ctor = upd.constructor
|
switch (upd._name) {
|
||||||
|
case 'new_message':
|
||||||
|
case 'edit_message': {
|
||||||
|
const sender = upd.sender
|
||||||
|
|
||||||
if (ctor === Message) {
|
return (matchSelf && sender.isSelf) ||
|
||||||
return (upd as Message).sender.id === id
|
indexId.has(sender.id) ||
|
||||||
} else if (ctor === UserStatusUpdate || ctor === UserTypingUpdate) {
|
indexUsername.has(sender.username!)
|
||||||
return (upd as UserStatusUpdate | UserTypingUpdate).userId === id
|
}
|
||||||
} else if (ctor === PollVoteUpdate) {
|
case 'user_status':
|
||||||
const peer = (upd as PollVoteUpdate).peer
|
case 'user_typing': {
|
||||||
if (peer.type !== 'user') return false
|
const id = upd.userId
|
||||||
|
|
||||||
return peer.id === id
|
return (matchSelf && id === upd.client.getAuthState().userId) ||
|
||||||
|
indexId.has(id)
|
||||||
|
}
|
||||||
|
case 'poll_vote':
|
||||||
|
case 'story':
|
||||||
|
case 'delete_story': {
|
||||||
|
const peer = upd.peer
|
||||||
|
if (peer.type !== 'user') return false
|
||||||
|
|
||||||
|
return (matchSelf && peer.isSelf) ||
|
||||||
|
indexId.has(peer.id) ||
|
||||||
|
Boolean(peer.usernames?.some((u) => indexUsername.has(u.username)))
|
||||||
|
}
|
||||||
|
case 'history_read': {
|
||||||
|
const id = upd.chatId
|
||||||
|
|
||||||
|
return (matchSelf && id === upd.client.getAuthState().userId) ||
|
||||||
|
indexId.has(id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const user = upd.user
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(upd as Exclude<typeof upd, Message | UserStatusUpdate | UserTypingUpdate | PollVoteUpdate>).user.id === id
|
(matchSelf && user.isSelf) ||
|
||||||
|
indexId.has(user.id) ||
|
||||||
|
Boolean(user.usernames?.some((u) => indexUsername.has(u.username)))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,14 @@
|
||||||
import {
|
import {
|
||||||
BotChatJoinRequestUpdate,
|
|
||||||
BotStoppedUpdate,
|
BotStoppedUpdate,
|
||||||
CallbackQuery,
|
|
||||||
ChatJoinRequestUpdate,
|
ChatJoinRequestUpdate,
|
||||||
ChatMemberUpdate,
|
ChatMemberUpdate,
|
||||||
ChosenInlineResult,
|
|
||||||
DeleteMessageUpdate,
|
DeleteMessageUpdate,
|
||||||
DeleteStoryUpdate,
|
DeleteStoryUpdate,
|
||||||
HistoryReadUpdate,
|
HistoryReadUpdate,
|
||||||
InlineQuery,
|
|
||||||
MaybeAsync,
|
MaybeAsync,
|
||||||
Message,
|
|
||||||
PeersIndex,
|
PeersIndex,
|
||||||
PollUpdate,
|
PollUpdate,
|
||||||
PollVoteUpdate,
|
PollVoteUpdate,
|
||||||
PreCheckoutQuery,
|
|
||||||
StoryUpdate,
|
StoryUpdate,
|
||||||
TelegramClient,
|
TelegramClient,
|
||||||
tl,
|
tl,
|
||||||
|
@ -22,6 +16,15 @@ import {
|
||||||
UserTypingUpdate,
|
UserTypingUpdate,
|
||||||
} from '@mtcute/client'
|
} from '@mtcute/client'
|
||||||
|
|
||||||
|
import {
|
||||||
|
CallbackQueryContext,
|
||||||
|
ChatJoinRequestUpdateContext,
|
||||||
|
ChosenInlineResultContext,
|
||||||
|
InlineQueryContext,
|
||||||
|
MessageContext,
|
||||||
|
PreCheckoutQueryContext,
|
||||||
|
} from './context'
|
||||||
|
import { UpdateContext } from './context/base'
|
||||||
import { PropagationAction } from './propagation'
|
import { PropagationAction } from './propagation'
|
||||||
|
|
||||||
export interface BaseUpdateHandler<Name, Handler, Checker> {
|
export interface BaseUpdateHandler<Name, Handler, Checker> {
|
||||||
|
@ -48,25 +51,31 @@ export type RawUpdateHandler = BaseUpdateHandler<
|
||||||
>
|
>
|
||||||
|
|
||||||
// begin-codegen
|
// begin-codegen
|
||||||
export type NewMessageHandler<T = Message, S = never> = ParsedUpdateHandler<'new_message', T, S>
|
export type NewMessageHandler<T = MessageContext, S = never> = ParsedUpdateHandler<'new_message', T, S>
|
||||||
export type EditMessageHandler<T = Message, S = never> = ParsedUpdateHandler<'edit_message', T, S>
|
export type EditMessageHandler<T = MessageContext, S = never> = ParsedUpdateHandler<'edit_message', T, S>
|
||||||
export type MessageGroupHandler<T = Message[], S = never> = ParsedUpdateHandler<'message_group', T, S>
|
export type MessageGroupHandler<T = MessageContext, S = never> = ParsedUpdateHandler<'message_group', T, S>
|
||||||
export type DeleteMessageHandler<T = DeleteMessageUpdate> = ParsedUpdateHandler<'delete_message', T>
|
export type DeleteMessageHandler<T = UpdateContext<DeleteMessageUpdate>> = ParsedUpdateHandler<'delete_message', T>
|
||||||
export type ChatMemberUpdateHandler<T = ChatMemberUpdate> = ParsedUpdateHandler<'chat_member', T>
|
export type ChatMemberUpdateHandler<T = UpdateContext<ChatMemberUpdate>> = ParsedUpdateHandler<'chat_member', T>
|
||||||
export type InlineQueryHandler<T = InlineQuery> = ParsedUpdateHandler<'inline_query', T>
|
export type InlineQueryHandler<T = InlineQueryContext> = ParsedUpdateHandler<'inline_query', T>
|
||||||
export type ChosenInlineResultHandler<T = ChosenInlineResult> = ParsedUpdateHandler<'chosen_inline_result', T>
|
export type ChosenInlineResultHandler<T = ChosenInlineResultContext> = ParsedUpdateHandler<'chosen_inline_result', T>
|
||||||
export type CallbackQueryHandler<T = CallbackQuery, S = never> = ParsedUpdateHandler<'callback_query', T, S>
|
export type CallbackQueryHandler<T = CallbackQueryContext, S = never> = ParsedUpdateHandler<'callback_query', T, S>
|
||||||
export type PollUpdateHandler<T = PollUpdate> = ParsedUpdateHandler<'poll', T>
|
export type PollUpdateHandler<T = UpdateContext<PollUpdate>> = ParsedUpdateHandler<'poll', T>
|
||||||
export type PollVoteHandler<T = PollVoteUpdate> = ParsedUpdateHandler<'poll_vote', T>
|
export type PollVoteHandler<T = UpdateContext<PollVoteUpdate>> = ParsedUpdateHandler<'poll_vote', T>
|
||||||
export type UserStatusUpdateHandler<T = UserStatusUpdate> = ParsedUpdateHandler<'user_status', T>
|
export type UserStatusUpdateHandler<T = UpdateContext<UserStatusUpdate>> = ParsedUpdateHandler<'user_status', T>
|
||||||
export type UserTypingHandler<T = UserTypingUpdate> = ParsedUpdateHandler<'user_typing', T>
|
export type UserTypingHandler<T = UpdateContext<UserTypingUpdate>> = ParsedUpdateHandler<'user_typing', T>
|
||||||
export type HistoryReadHandler<T = HistoryReadUpdate> = ParsedUpdateHandler<'history_read', T>
|
export type HistoryReadHandler<T = UpdateContext<HistoryReadUpdate>> = ParsedUpdateHandler<'history_read', T>
|
||||||
export type BotStoppedHandler<T = BotStoppedUpdate> = ParsedUpdateHandler<'bot_stopped', T>
|
export type BotStoppedHandler<T = UpdateContext<BotStoppedUpdate>> = ParsedUpdateHandler<'bot_stopped', T>
|
||||||
export type BotChatJoinRequestHandler<T = BotChatJoinRequestUpdate> = ParsedUpdateHandler<'bot_chat_join_request', T>
|
export type BotChatJoinRequestHandler<T = ChatJoinRequestUpdateContext> = ParsedUpdateHandler<
|
||||||
export type ChatJoinRequestHandler<T = ChatJoinRequestUpdate> = ParsedUpdateHandler<'chat_join_request', T>
|
'bot_chat_join_request',
|
||||||
export type PreCheckoutQueryHandler<T = PreCheckoutQuery> = ParsedUpdateHandler<'pre_checkout_query', T>
|
T
|
||||||
export type StoryUpdateHandler<T = StoryUpdate> = ParsedUpdateHandler<'story', T>
|
>
|
||||||
export type DeleteStoryHandler<T = DeleteStoryUpdate> = ParsedUpdateHandler<'delete_story', T>
|
export type ChatJoinRequestHandler<T = UpdateContext<ChatJoinRequestUpdate>> = ParsedUpdateHandler<
|
||||||
|
'chat_join_request',
|
||||||
|
T
|
||||||
|
>
|
||||||
|
export type PreCheckoutQueryHandler<T = PreCheckoutQueryContext> = ParsedUpdateHandler<'pre_checkout_query', T>
|
||||||
|
export type StoryUpdateHandler<T = UpdateContext<StoryUpdate>> = ParsedUpdateHandler<'story', T>
|
||||||
|
export type DeleteStoryHandler<T = UpdateContext<DeleteStoryUpdate>> = ParsedUpdateHandler<'delete_story', T>
|
||||||
|
|
||||||
export type UpdateHandler =
|
export type UpdateHandler =
|
||||||
| RawUpdateHandler
|
| RawUpdateHandler
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export * from './callback-data-builder'
|
export * from './callback-data-builder'
|
||||||
|
export * from './context'
|
||||||
export * from './dispatcher'
|
export * from './dispatcher'
|
||||||
export * from './filters'
|
export * from './filters'
|
||||||
export * from './handler'
|
export * from './handler'
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { MaybeAsync, Message } from '@mtcute/client'
|
import { MaybeAsync } from '@mtcute/client'
|
||||||
|
|
||||||
|
import { MessageContext } from './context'
|
||||||
import { Dispatcher } from './dispatcher'
|
import { Dispatcher } from './dispatcher'
|
||||||
import { filters } from './filters'
|
import { filters } from './filters'
|
||||||
import { UpdateState } from './state'
|
import { UpdateState } from './state'
|
||||||
|
@ -54,13 +55,13 @@ export class WizardScene<State, SceneName extends string = string> extends Dispa
|
||||||
* Add a step to the wizard
|
* Add a step to the wizard
|
||||||
*/
|
*/
|
||||||
addStep(
|
addStep(
|
||||||
handler: (msg: Message, state: UpdateState<State, SceneName>) => MaybeAsync<WizardSceneAction | number>,
|
handler: (msg: MessageContext, state: UpdateState<State, SceneName>) => MaybeAsync<WizardSceneAction | number>,
|
||||||
): void {
|
): void {
|
||||||
const step = this._steps++
|
const step = this._steps++
|
||||||
|
|
||||||
const filter = filters.state<WizardInternalState>((it) => it.$step === step)
|
const filter = filters.state<WizardInternalState>((it) => it.$step === step)
|
||||||
|
|
||||||
this.onNewMessage(step === 0 ? filters.or(filters.stateEmpty, filter) : filter, async (msg: Message, state) => {
|
this.onNewMessage(step === 0 ? filters.or(filters.stateEmpty, filter) : filter, async (msg, state) => {
|
||||||
const result = await handler(msg, state)
|
const result = await handler(msg, state)
|
||||||
|
|
||||||
if (typeof result === 'number') {
|
if (typeof result === 'number') {
|
||||||
|
|
Loading…
Reference in a new issue