mtcute/packages/client/src/methods/users/resolve-peer.ts

256 lines
8 KiB
TypeScript
Raw Normal View History

import Long from 'long'
import {
getBasicPeerType,
getMarkedPeerId,
toggleChannelIdMark,
} from '@mtcute/core'
import { tl } from '@mtcute/tl'
2021-04-08 12:19:38 +03:00
import { TelegramClient } from '../../client'
import {
InputPeerLike,
MtNotFoundError,
MtTypeAssertionError,
} from '../../types'
import { normalizeToInputPeer } from '../../utils/peer-utils'
import { assertTypeIs } from '../../utils/type-assertion'
2021-04-08 12:19:38 +03:00
/**
* Get the `InputPeer` of a known peer id.
* Useful when an `InputPeer` is needed.
*
* @param peerId The peer identifier that you want to extract the `InputPeer` from.
* @param force Whether to force re-fetch the peer from the server
2021-04-08 12:19:38 +03:00
* @internal
*/
export async function resolvePeer(
this: TelegramClient,
peerId: InputPeerLike,
force = false,
): Promise<tl.TypeInputPeer> {
2021-04-08 12:19:38 +03:00
// for convenience we also accept tl objects directly
if (typeof peerId === 'object') {
if (tl.isAnyPeer(peerId)) {
peerId = getMarkedPeerId(peerId)
} else {
return normalizeToInputPeer(peerId)
}
}
2021-04-08 12:19:38 +03:00
if (typeof peerId === 'number' && !force) {
2021-04-08 12:19:38 +03:00
const fromStorage = await this.storage.getPeerById(peerId)
if (fromStorage) return fromStorage
}
if (typeof peerId === 'string') {
if (peerId === 'self' || peerId === 'me') return { _: 'inputPeerSelf' }
peerId = peerId.replace(/[@+\s()]/g, '')
2021-04-08 12:19:38 +03:00
if (peerId.match(/^\d+$/)) {
// phone number
const fromStorage = await this.storage.getPeerByPhone(peerId)
if (fromStorage) return fromStorage
const res = await this.call({
_: 'contacts.getContacts',
hash: Long.ZERO,
})
assertTypeIs('contacts.getContacts', res, 'contacts.contacts')
const found = res.users.find(
(it) => (it as tl.RawUser).phone === peerId,
)
if (found && found._ === 'user') {
return {
_: 'inputPeerUser',
userId: found.id,
accessHash: found.accessHash!,
}
}
2021-08-05 20:38:24 +03:00
throw new MtNotFoundError(
`Could not find a peer by phone ${peerId}`,
2021-04-08 12:19:38 +03:00
)
} else {
// username
if (!force) {
const fromStorage = await this.storage.getPeerByUsername(peerId)
if (fromStorage) return fromStorage
}
2021-04-08 12:19:38 +03:00
const res = await this.call({
2021-04-08 12:19:38 +03:00
_: 'contacts.resolveUsername',
username: peerId,
})
if (res.peer._ === 'peerUser') {
const id = res.peer.userId
const found = res.users.find((it) => it.id === id)
if (found && found._ === 'user') {
if (!found.accessHash) {
// no access hash, we can't use it
// this may happen when bot resolves a username
// of a user who hasn't started a conversation with it
throw new MtNotFoundError(
`Peer (user) with username ${peerId} was found, but it has no access hash`,
)
}
return {
_: 'inputPeerUser',
userId: found.id,
accessHash: found.accessHash,
}
}
} else if (res.peer._ === 'peerChannel') {
const id = res.peer.channelId
const found = res.chats.find((it) => it.id === id)
if (found) {
if (
!(
found._ === 'channel' ||
found._ === 'channelForbidden'
)
) {
// chats can't have usernames
// furthermore, our id is a channel id, so it must be a channel
// this should never happen, unless Telegram goes crazy
throw new MtTypeAssertionError(
'contacts.resolveUsername#chats',
'channel',
found._,
)
}
if (!found.accessHash) {
// shouldn't happen? but just in case
throw new MtNotFoundError(
`Peer (channel) with username ${peerId} was found, but it has no access hash`,
)
}
return {
_: 'inputPeerChannel',
channelId: found.id,
accessHash: found.accessHash,
}
}
} else {
// chats can't have usernames
throw new MtTypeAssertionError(
'contacts.resolveUsername',
'user or channel',
res.peer._,
)
}
2021-04-08 12:19:38 +03:00
2021-08-05 20:38:24 +03:00
throw new MtNotFoundError(
`Could not find a peer by username ${peerId}`,
2021-04-08 12:19:38 +03:00
)
}
}
const peerType = getBasicPeerType(peerId)
// try fetching by id, with access_hash set to 0
switch (peerType) {
case 'user': {
const res = await this.call({
_: 'users.getUsers',
id: [
{
_: 'inputUser',
userId: peerId,
accessHash: Long.ZERO,
},
],
})
const found = res.find((it) => it.id === peerId)
if (found && found._ === 'user') {
if (!found.accessHash) {
// shouldn't happen? but just in case
throw new MtNotFoundError(
`Peer (user) with username ${peerId} was found, but it has no access hash`,
)
}
return {
_: 'inputPeerUser',
userId: found.id,
accessHash: found.accessHash,
}
}
break
}
case 'chat': {
// do we really need to make a call?
// const id = -peerId
// const res = await this.call({
// _: 'messages.getChats',
// id: [id],
// })
//
// const found = res.chats.find((it) => it.id === id)
// if (found && (found._ === 'chat' || found._ === 'chatForbidden'))
// return {
// _: 'inputPeerChat',
// chatId: found.id
// }
return {
_: 'inputPeerChat',
chatId: -peerId,
}
// break
}
case 'channel': {
const id = toggleChannelIdMark(peerId)
const res = await this.call({
_: 'channels.getChannels',
id: [
{
_: 'inputChannel',
channelId: id,
accessHash: Long.ZERO,
},
],
})
const found = res.chats.find((it) => it.id === id)
if (
found &&
(found._ === 'channel' || found._ === 'channelForbidden')
) {
if (!found.accessHash) {
// shouldn't happen? but just in case
throw new MtNotFoundError(
`Peer (channel) with username ${peerId} was found, but it has no access hash`,
)
}
return {
_: 'inputPeerChannel',
channelId: found.id,
accessHash: found.accessHash ?? Long.ZERO,
}
}
break
}
2021-04-08 12:19:38 +03:00
}
2021-08-05 20:38:24 +03:00
throw new MtNotFoundError(`Could not find a peer by ID ${peerId}`)
2021-04-08 12:19:38 +03:00
}