fix: added MessageNotFoundError, improved getMessages, fixed methods that use it
This commit is contained in:
parent
36ba4c3b87
commit
d5e595d7cf
13 changed files with 257 additions and 34 deletions
|
@ -97,6 +97,7 @@ import { forwardMessages } from './methods/messages/forward-messages'
|
|||
import { _getDiscussionMessage } from './methods/messages/get-discussion-message'
|
||||
import { getHistory } from './methods/messages/get-history'
|
||||
import { getMessageGroup } from './methods/messages/get-message-group'
|
||||
import { getMessagesUnsafe } from './methods/messages/get-messages-unsafe'
|
||||
import { getMessages } from './methods/messages/get-messages'
|
||||
import { iterHistory } from './methods/messages/iter-history'
|
||||
import { _normalizeInline } from './methods/messages/normalize-inline'
|
||||
|
@ -2101,9 +2102,42 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
*/
|
||||
getMessageGroup(chatId: InputPeerLike, message: number): Promise<Message[]>
|
||||
/**
|
||||
* Get a single message in chat by its ID
|
||||
* Get a single message from PM or legacy group by its ID.
|
||||
* For channels, use {@link getMessages}.
|
||||
*
|
||||
* **Note**: this method might return empty message
|
||||
* Unlike {@link getMessages}, this method does not
|
||||
* check if the message belongs to some chat.
|
||||
*
|
||||
* @param messageId Messages ID
|
||||
* @param [fromReply=false]
|
||||
* Whether the reply to a given message should be fetched
|
||||
* (i.e. `getMessages(msg.chat.id, msg.id, true).id === msg.replyToMessageId`)
|
||||
*/
|
||||
getMessagesUnsafe(
|
||||
messageId: number,
|
||||
fromReply?: boolean
|
||||
): Promise<Message | null>
|
||||
/**
|
||||
* Get messages from PM or legacy group by their IDs.
|
||||
* For channels, use {@link getMessages}.
|
||||
*
|
||||
* Unlike {@link getMessages}, this method does not
|
||||
* check if the message belongs to some chat.
|
||||
*
|
||||
* Fot messages that were not found, `null` will be
|
||||
* returned at that position.
|
||||
*
|
||||
* @param messageIds Messages IDs
|
||||
* @param [fromReply=false]
|
||||
* Whether the reply to a given message should be fetched
|
||||
* (i.e. `getMessages(msg.chat.id, msg.id, true).id === msg.replyToMessageId`)
|
||||
*/
|
||||
getMessagesUnsafe(
|
||||
messageIds: number[],
|
||||
fromReply?: boolean
|
||||
): Promise<(Message | null)[]>
|
||||
/**
|
||||
* Get a single message in chat by its ID
|
||||
*
|
||||
* @param chatId Chat's marked ID, its username, phone or `"me"` or `"self"`
|
||||
* @param messageId Messages ID
|
||||
|
@ -2115,11 +2149,12 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
chatId: InputPeerLike,
|
||||
messageId: number,
|
||||
fromReply?: boolean
|
||||
): Promise<Message>
|
||||
): Promise<Message | null>
|
||||
/**
|
||||
* Get messages in chat by their IDs
|
||||
*
|
||||
* **Note**: this method might return empty messages
|
||||
* Fot messages that were not found, `null` will be
|
||||
* returned at that position.
|
||||
*
|
||||
* @param chatId Chat's marked ID, its username, phone or `"me"` or `"self"`
|
||||
* @param messageIds Messages IDs
|
||||
|
@ -2131,7 +2166,7 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
chatId: InputPeerLike,
|
||||
messageIds: number[],
|
||||
fromReply?: boolean
|
||||
): Promise<Message[]>
|
||||
): Promise<(Message | null)[]>
|
||||
/**
|
||||
* Iterate through a chat history sequentially.
|
||||
*
|
||||
|
@ -2569,6 +2604,19 @@ export interface TelegramClient extends BaseTelegramClient {
|
|||
*/
|
||||
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.
|
||||
*
|
||||
|
@ -3321,6 +3369,7 @@ export class TelegramClient extends BaseTelegramClient {
|
|||
protected _getDiscussionMessage = _getDiscussionMessage
|
||||
getHistory = getHistory
|
||||
getMessageGroup = getMessageGroup
|
||||
getMessagesUnsafe = getMessagesUnsafe
|
||||
getMessages = getMessages
|
||||
iterHistory = iterHistory
|
||||
protected _normalizeInline = _normalizeInline
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import { InputPeerLike, MtCuteArgumentError, Message } from '../../types'
|
||||
import { isInputPeerChannel } from '../../utils/peer-utils'
|
||||
|
||||
/**
|
||||
* Get all messages inside of a message group
|
||||
|
@ -15,16 +16,25 @@ export async function getMessageGroup(
|
|||
): Promise<Message[]> {
|
||||
// awesome hack stolen from pyrogram
|
||||
// groups have no more than 10 items
|
||||
// however, since for non-channels message ids are shared,
|
||||
// we use larger number.
|
||||
// still, this might not be enough :shrug:
|
||||
|
||||
const peer = await this.resolvePeer(chatId)
|
||||
|
||||
const delta = isInputPeerChannel(peer) ? 9 : 19
|
||||
|
||||
const ids: number[] = []
|
||||
for (let i = Math.max(message - 9, 0); i <= message + 9; i++) {
|
||||
for (let i = Math.max(message - delta, 0); i <= message + delta; i++) {
|
||||
ids.push(i)
|
||||
}
|
||||
|
||||
const messages = await this.getMessages(chatId, ids)
|
||||
const groupedId = messages.find((it) => it.id === message)!.groupedId
|
||||
const groupedId = messages.find((it) => it?.id === message)!.groupedId
|
||||
|
||||
if (!groupedId) throw new MtCuteArgumentError('This message is not grouped')
|
||||
|
||||
return messages.filter((it) => it.groupedId?.eq(groupedId))
|
||||
return messages.filter(
|
||||
(it) => it && it.groupedId?.eq(groupedId)
|
||||
) as Message[]
|
||||
}
|
||||
|
|
86
packages/client/src/methods/messages/get-messages-unsafe.ts
Normal file
86
packages/client/src/methods/messages/get-messages-unsafe.ts
Normal file
|
@ -0,0 +1,86 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import { MaybeArray } from '@mtcute/core'
|
||||
import {
|
||||
createUsersChatsIndex,
|
||||
} from '../../utils/peer-utils'
|
||||
import { tl } from '@mtcute/tl'
|
||||
import { Message, MtCuteTypeAssertionError } from '../../types'
|
||||
|
||||
/**
|
||||
* Get a single message from PM or legacy group by its ID.
|
||||
* For channels, use {@link getMessages}.
|
||||
*
|
||||
* Unlike {@link getMessages}, this method does not
|
||||
* check if the message belongs to some chat.
|
||||
*
|
||||
* @param messageId Messages ID
|
||||
* @param [fromReply=false]
|
||||
* Whether the reply to a given message should be fetched
|
||||
* (i.e. `getMessages(msg.chat.id, msg.id, true).id === msg.replyToMessageId`)
|
||||
* @internal
|
||||
*/
|
||||
export async function getMessagesUnsafe(
|
||||
this: TelegramClient,
|
||||
messageId: number,
|
||||
fromReply?: boolean
|
||||
): Promise<Message | null>
|
||||
/**
|
||||
* Get messages from PM or legacy group by their IDs.
|
||||
* For channels, use {@link getMessages}.
|
||||
*
|
||||
* Unlike {@link getMessages}, this method does not
|
||||
* check if the message belongs to some chat.
|
||||
*
|
||||
* Fot messages that were not found, `null` will be
|
||||
* returned at that position.
|
||||
*
|
||||
* @param messageIds Messages IDs
|
||||
* @param [fromReply=false]
|
||||
* Whether the reply to a given message should be fetched
|
||||
* (i.e. `getMessages(msg.chat.id, msg.id, true).id === msg.replyToMessageId`)
|
||||
* @internal
|
||||
*/
|
||||
export async function getMessagesUnsafe(
|
||||
this: TelegramClient,
|
||||
messageIds: number[],
|
||||
fromReply?: boolean
|
||||
): Promise<(Message | null)[]>
|
||||
|
||||
/** @internal */
|
||||
export async function getMessagesUnsafe(
|
||||
this: TelegramClient,
|
||||
messageIds: MaybeArray<number>,
|
||||
fromReply = false
|
||||
): Promise<MaybeArray<Message | null>> {
|
||||
const isSingle = !Array.isArray(messageIds)
|
||||
if (isSingle) messageIds = [messageIds as number]
|
||||
|
||||
const type = fromReply ? 'inputMessageReplyTo' : 'inputMessageID'
|
||||
const ids: tl.TypeInputMessage[] = (messageIds as number[]).map((it) => ({
|
||||
_: type,
|
||||
id: it,
|
||||
}))
|
||||
|
||||
const res = await this.call({
|
||||
_: 'messages.getMessages',
|
||||
id: ids,
|
||||
})
|
||||
|
||||
if (res._ === 'messages.messagesNotModified')
|
||||
throw new MtCuteTypeAssertionError(
|
||||
'getMessages',
|
||||
'!messages.messagesNotModified',
|
||||
res._
|
||||
)
|
||||
|
||||
const { users, chats } = createUsersChatsIndex(res)
|
||||
|
||||
const ret = res.messages
|
||||
.map((msg) => {
|
||||
if (msg._ === 'messageEmpty') return null
|
||||
|
||||
return new Message(this, msg, users, chats)
|
||||
})
|
||||
|
||||
return isSingle ? ret[0] : ret
|
||||
}
|
|
@ -11,8 +11,6 @@ import { Message, InputPeerLike, MtCuteTypeAssertionError } from '../../types'
|
|||
/**
|
||||
* Get a single message in chat by its ID
|
||||
*
|
||||
* **Note**: this method might return empty message
|
||||
*
|
||||
* @param chatId Chat's marked ID, its username, phone or `"me"` or `"self"`
|
||||
* @param messageId Messages ID
|
||||
* @param [fromReply=false]
|
||||
|
@ -25,11 +23,12 @@ export async function getMessages(
|
|||
chatId: InputPeerLike,
|
||||
messageId: number,
|
||||
fromReply?: boolean
|
||||
): Promise<Message>
|
||||
): Promise<Message | null>
|
||||
/**
|
||||
* Get messages in chat by their IDs
|
||||
*
|
||||
* **Note**: this method might return empty messages
|
||||
* Fot messages that were not found, `null` will be
|
||||
* returned at that position.
|
||||
*
|
||||
* @param chatId Chat's marked ID, its username, phone or `"me"` or `"self"`
|
||||
* @param messageIds Messages IDs
|
||||
|
@ -43,7 +42,7 @@ export async function getMessages(
|
|||
chatId: InputPeerLike,
|
||||
messageIds: number[],
|
||||
fromReply?: boolean
|
||||
): Promise<Message[]>
|
||||
): Promise<(Message | null)[]>
|
||||
|
||||
/** @internal */
|
||||
export async function getMessages(
|
||||
|
@ -51,7 +50,7 @@ export async function getMessages(
|
|||
chatId: InputPeerLike,
|
||||
messageIds: MaybeArray<number>,
|
||||
fromReply = false
|
||||
): Promise<MaybeArray<Message>> {
|
||||
): Promise<MaybeArray<Message | null>> {
|
||||
const peer = await this.resolvePeer(chatId)
|
||||
|
||||
const isSingle = !Array.isArray(messageIds)
|
||||
|
@ -63,12 +62,14 @@ export async function getMessages(
|
|||
id: it,
|
||||
}))
|
||||
|
||||
const isChannel = isInputPeerChannel(peer)
|
||||
|
||||
const res = await this.call(
|
||||
isInputPeerChannel(peer)
|
||||
isChannel
|
||||
? {
|
||||
_: 'channels.getMessages',
|
||||
id: ids,
|
||||
channel: normalizeToInputChannel(peer),
|
||||
channel: normalizeToInputChannel(peer)!,
|
||||
}
|
||||
: {
|
||||
_: 'messages.getMessages',
|
||||
|
@ -85,9 +86,31 @@ export async function getMessages(
|
|||
|
||||
const { users, chats } = createUsersChatsIndex(res)
|
||||
|
||||
const ret = res.messages
|
||||
.filter((msg) => msg._ !== 'messageEmpty')
|
||||
.map((msg) => new Message(this, msg, users, chats))
|
||||
const ret = res.messages.map((msg) => {
|
||||
if (msg._ === 'messageEmpty') return null
|
||||
|
||||
if (!isChannel) {
|
||||
// make sure that the messages belong to the given chat
|
||||
// (channels have their own message numbering)
|
||||
switch (peer._) {
|
||||
case 'inputPeerSelf':
|
||||
if (!(msg.peerId._ === 'peerUser' && msg.peerId.userId === this._userId))
|
||||
return null
|
||||
break;
|
||||
case 'inputPeerUser':
|
||||
case 'inputPeerUserFromMessage':
|
||||
if (!(msg.peerId._ === 'peerUser' && msg.peerId.userId === peer.userId))
|
||||
return null
|
||||
break;
|
||||
case 'inputPeerChat':
|
||||
if (!(msg.peerId._ === 'peerChat' && msg.peerId.chatId === peer.chatId))
|
||||
return null
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Message(this, msg, users, chats)
|
||||
})
|
||||
|
||||
return isSingle ? ret[0] : ret
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import { InputPeerLike, Message, ReplyMarkup } from '../../types'
|
||||
import { tl } from '@mtcute/tl'
|
||||
import { MessageNotFoundError } from '@mtcute/tl/errors'
|
||||
|
||||
/**
|
||||
* Copy a message (i.e. send the same message,
|
||||
|
@ -84,5 +85,8 @@ export async function sendCopy(
|
|||
const fromPeer = await this.resolvePeer(fromChatId)
|
||||
|
||||
const msg = await this.getMessages(fromPeer, message)
|
||||
|
||||
if (!msg) throw new MessageNotFoundError()
|
||||
|
||||
return msg.sendCopy(toChatId, params)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { TelegramClient } from '../../client'
|
||||
import { tl } from '@mtcute/tl'
|
||||
import { inputPeerToPeer, normalizeToInputUser } from '../../utils/peer-utils'
|
||||
import { inputPeerToPeer } from '../../utils/peer-utils'
|
||||
import {
|
||||
normalizeDate,
|
||||
normalizeMessageId,
|
||||
|
@ -14,8 +14,9 @@ import {
|
|||
UsersIndex,
|
||||
MtCuteTypeAssertionError,
|
||||
ChatsIndex,
|
||||
MtCuteArgumentError,
|
||||
} from '../../types'
|
||||
import { getMarkedPeerId } from '@mtcute/core'
|
||||
import { getMarkedPeerId, MessageNotFoundError } from '@mtcute/core'
|
||||
import { createDummyUpdate } from '../../utils/updates-utils'
|
||||
|
||||
/**
|
||||
|
@ -36,6 +37,19 @@ export async function sendText(
|
|||
*/
|
||||
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.
|
||||
*
|
||||
|
@ -111,6 +125,18 @@ export async function sendText(
|
|||
)
|
||||
}
|
||||
|
||||
if (params.mustReply) {
|
||||
if (!replyTo)
|
||||
throw new MtCuteArgumentError(
|
||||
'mustReply used, but replyTo was not passed'
|
||||
)
|
||||
|
||||
const msg = await this.getMessages(peer, replyTo)
|
||||
|
||||
if (!msg)
|
||||
throw new MessageNotFoundError()
|
||||
}
|
||||
|
||||
const res = await this.call({
|
||||
_: 'messages.sendMessage',
|
||||
peer,
|
||||
|
@ -124,6 +150,9 @@ export async function sendText(
|
|||
entities,
|
||||
clearDraft: params.clearDraft,
|
||||
})
|
||||
// } catch (e) {
|
||||
//
|
||||
// }
|
||||
|
||||
if (res._ === 'updateShortSentMessage') {
|
||||
const msg: tl.RawMessage = {
|
||||
|
@ -157,7 +186,7 @@ export async function sendText(
|
|||
// we need to do it manually
|
||||
cached = await this.call({
|
||||
_: 'messages.getChats',
|
||||
id: [peer.chatId]
|
||||
id: [peer.chatId],
|
||||
}).then((res) => res.chats[0])
|
||||
break
|
||||
default:
|
||||
|
|
|
@ -5,7 +5,7 @@ import {
|
|||
MtCuteTypeAssertionError,
|
||||
Poll,
|
||||
} from '../../types'
|
||||
import { MaybeArray } from '@mtcute/core'
|
||||
import { MaybeArray, MessageNotFoundError } from '@mtcute/core'
|
||||
import { createUsersChatsIndex } from '../../utils/peer-utils'
|
||||
import { assertTypeIs } from '../../utils/type-assertion'
|
||||
import { assertIsUpdatesGroup } from '../../utils/updates-utils'
|
||||
|
@ -36,6 +36,9 @@ export async function sendVote(
|
|||
let poll: Poll | undefined = undefined
|
||||
if (options.some((it) => typeof it === 'number')) {
|
||||
const msg = await this.getMessages(peer, message)
|
||||
|
||||
if (!msg) throw new MessageNotFoundError()
|
||||
|
||||
if (!(msg.media instanceof Poll))
|
||||
throw new MtCuteArgumentError(
|
||||
'This message does not contain a poll'
|
||||
|
|
|
@ -6,6 +6,7 @@ import { MtCuteArgumentError } from '../errors'
|
|||
import { BasicPeerType, getBasicPeerType, getMarkedPeerId } from '@mtcute/core'
|
||||
import { encodeInlineMessageId } from '../../utils/inline-utils'
|
||||
import { User, UsersIndex } from '../peers'
|
||||
import { MessageNotFoundError } from '@mtcute/core'
|
||||
|
||||
/**
|
||||
* An incoming callback query, originated from a callback button
|
||||
|
@ -179,6 +180,9 @@ export class CallbackQuery {
|
|||
/**
|
||||
* 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(): Promise<Message> {
|
||||
|
@ -187,10 +191,13 @@ export class CallbackQuery {
|
|||
'Cannot get a message for inline callback'
|
||||
)
|
||||
|
||||
return this.client.getMessages(
|
||||
const msg = await this.client.getMessages(
|
||||
getMarkedPeerId(this.raw.peer),
|
||||
this.raw.msgId
|
||||
)
|
||||
if (!msg) throw new MessageNotFoundError()
|
||||
|
||||
return msg
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -69,7 +69,8 @@ export class MtCuteInvalidPeerTypeError extends MtCuteError {
|
|||
}
|
||||
|
||||
/**
|
||||
* Trying to access to some property on an "empty" object.
|
||||
* Trying to access to some property on an object that does not
|
||||
* contain that information.
|
||||
*/
|
||||
export class MtCuteEmptyError extends MtCuteError {
|
||||
constructor() {
|
||||
|
|
|
@ -4,8 +4,7 @@ import { Chat, ChatsIndex, UsersIndex } from '../peers'
|
|||
import { Message } from './message'
|
||||
import { DraftMessage } from './draft-message'
|
||||
import { makeInspectable } from '../utils'
|
||||
import { getMarkedPeerId } from '@mtcute/core'
|
||||
import { MtCuteEmptyError } from '../errors'
|
||||
import { getMarkedPeerId, MessageNotFoundError } from '@mtcute/core'
|
||||
|
||||
/**
|
||||
* A dialog.
|
||||
|
@ -191,6 +190,8 @@ export class Dialog {
|
|||
private _lastMessage?: Message
|
||||
/**
|
||||
* The latest message sent in this chat
|
||||
*
|
||||
* Throws `MessageNotFoundError` if it was not found
|
||||
*/
|
||||
get lastMessage(): Message {
|
||||
if (!this._lastMessage) {
|
||||
|
@ -203,7 +204,7 @@ export class Dialog {
|
|||
this._chats
|
||||
)
|
||||
} else {
|
||||
throw new MtCuteEmptyError()
|
||||
throw new MessageNotFoundError()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ import { BotKeyboard, ReplyMarkup } from '../bots'
|
|||
import { getMarkedPeerId, MAX_CHANNEL_ID } from '@mtcute/core'
|
||||
import {
|
||||
MtCuteArgumentError,
|
||||
MtCuteEmptyError,
|
||||
MtCuteTypeAssertionError,
|
||||
} from '../errors'
|
||||
import { TelegramClient } from '../../client'
|
||||
|
@ -534,13 +533,18 @@ export class Message {
|
|||
/**
|
||||
* For replies, fetch the message that is being replied.
|
||||
*
|
||||
* @throws MtCuteArgumentError In case the message is not a reply
|
||||
* 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`.
|
||||
*/
|
||||
getReplyTo(): Promise<Message> {
|
||||
getReplyTo(): Promise<Message | null> {
|
||||
if (!this.replyToMessageId)
|
||||
throw new MtCuteArgumentError('This message is not a reply!')
|
||||
return Promise.resolve(null)
|
||||
|
||||
return this.client.getMessages(this.chat.inputPeer, this.id, true)
|
||||
if (this.raw.peerId._ === 'peerChannel')
|
||||
return this.client.getMessages(this.chat.inputPeer, this.id, true)
|
||||
|
||||
return this.client.getMessagesUnsafe(this.id, true)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -84,6 +84,12 @@ const customErrors = [
|
|||
name: 'RPC_TIMEOUT',
|
||||
codes: '408',
|
||||
description: 'Timeout of {ms} ms exceeded',
|
||||
},
|
||||
{
|
||||
virtual: true,
|
||||
name: 'MESSAGE_NOT_FOUND',
|
||||
codes: '404',
|
||||
description: 'Message was not found'
|
||||
}
|
||||
]
|
||||
|
||||
|
|
Loading…
Reference in a new issue