feat(client): user related methods

This commit is contained in:
teidesu 2021-05-09 14:35:58 +03:00
parent 192c0f773e
commit 87a7df669a
11 changed files with 494 additions and 15 deletions

View file

@ -87,6 +87,14 @@ import {
setDefaultParseMode, setDefaultParseMode,
unregisterParseMode, unregisterParseMode,
} from './methods/parse-modes/parse-modes' } from './methods/parse-modes/parse-modes'
import { changeCloudPassword } from './methods/pasword/change-cloud-password'
import { enableCloudPassword } from './methods/pasword/enable-cloud-password'
import {
cancelPasswordEmail,
resendPasswordEmail,
verifyPasswordEmail,
} from './methods/pasword/password-email'
import { removeCloudPassword } from './methods/pasword/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'
@ -103,11 +111,18 @@ import {
dispatchUpdate, dispatchUpdate,
} from './methods/updates' } from './methods/updates'
import { blockUser } from './methods/users/block-user' import { blockUser } from './methods/users/block-user'
import { deleteProfilePhotos } from './methods/users/delete-profile-photos'
import { getCommonChats } from './methods/users/get-common-chats' import { getCommonChats } from './methods/users/get-common-chats'
import { getMe } from './methods/users/get-me' import { getMe } from './methods/users/get-me'
import { getProfilePhotos } from './methods/users/get-profile-photos'
import { getUsers } from './methods/users/get-users' import { getUsers } from './methods/users/get-users'
import { iterProfilePhotos } from './methods/users/iter-profile-photos'
import { resolvePeer } from './methods/users/resolve-peer' import { resolvePeer } from './methods/users/resolve-peer'
import { setOffline } from './methods/users/set-offline' import { setOffline } from './methods/users/set-offline'
import { setProfilePhoto } from './methods/users/set-profile-photo'
import { unblockUser } from './methods/users/unblock-user'
import { updateProfile } from './methods/users/update-profile'
import { updateUsername } from './methods/users/update-username'
import { IMessageEntityParser } from './parser' import { IMessageEntityParser } from './parser'
import { Readable } from 'stream' import { Readable } from 'stream'
import { import {
@ -126,6 +141,7 @@ import {
Message, Message,
PartialExcept, PartialExcept,
PartialOnly, PartialOnly,
Photo,
Poll, Poll,
ReplyMarkup, ReplyMarkup,
SentCode, SentCode,
@ -2026,6 +2042,57 @@ export interface TelegramClient extends BaseTelegramClient {
* @throws MtCuteError When given parse mode is not registered. * @throws MtCuteError When given parse mode is not registered.
*/ */
setDefaultParseMode(name: string): void setDefaultParseMode(name: string): void
/**
* Change your 2FA password
*
* @param currentPassword Current password as plaintext
* @param newPassword New password as plaintext
* @param hint Hint for the new password
*/
changeCloudPassword(
currentPassword: string,
newPassword: string,
hint?: string
): Promise<void>
/**
* Enable 2FA password on your account
*
* Note that if you pass `email`, `EmailUnconfirmedError` may be
* thrown, and you should use {@link verifyPasswordEmail},
* {@link resendPasswordEmail} or {@link cancelPasswordEmail},
* and the call this method again
*
* @param password 2FA password as plaintext
* @param hint Hint for the new password
* @param email Recovery email
*/
enableCloudPassword(
password: string,
hint?: string,
email?: string
): Promise<void>
/**
* Verify an email to use as 2FA recovery method
*
* @param code Code which was sent via email
*/
verifyPasswordEmail(code: string): Promise<void>
/**
* Resend the code to verify an email to use as 2FA recovery method.
*
*/
resendPasswordEmail(): Promise<void>
/**
* Cancel the code that was sent to verify an email to use as 2FA recovery method
*
*/
cancelPasswordEmail(): Promise<void>
/**
* Remove 2FA password from your account
*
* @param password 2FA password as plaintext
*/
removeCloudPassword(password: string): Promise<void>
/** /**
* Add a sticker to a sticker set. * Add a sticker to a sticker set.
* *
@ -2226,10 +2293,17 @@ export interface TelegramClient extends BaseTelegramClient {
/** /**
* Block a user * Block a user
* *
* @param id User ID, its username or phone number * @param id User ID, username or phone number
* @returns Whether the action was successful
*/ */
blockUser(id: InputPeerLike): Promise<boolean> blockUser(id: InputPeerLike): Promise<void>
/**
* Delete your own profile photos
*
* @param ids ID(s) of the photos. Can be file IDs or raw TL objects
*/
deleteProfilePhotos(
ids: MaybeArray<string | tl.TypeInputPhoto>
): Promise<void>
/** /**
* Get a list of common chats you have with a given user * Get a list of common chats you have with a given user
* *
@ -2242,6 +2316,30 @@ export interface TelegramClient extends BaseTelegramClient {
* *
*/ */
getMe(): Promise<User> getMe(): Promise<User>
/**
* Get a list of profile pictures of a user
*
* @param userId User ID, username, phone number, `"me"` or `"self"`
* @param params
*/
getProfilePhotos(
userId: InputPeerLike,
params?: {
/**
* Offset from which to fetch.
*
* Defaults to `0`
*/
offset?: number
/**
* Maximum number of items to fetch (up to 100)
*
* Defaults to `100`
*/
limit?: number
}
): Promise<Photo[]>
/** /**
* Get information about a single user. * Get information about a single user.
* *
@ -2255,6 +2353,43 @@ export interface TelegramClient extends BaseTelegramClient {
* @param ids Users' identifiers. Can be ID, username, phone number, `"me"`, `"self"` or TL object * @param ids Users' identifiers. Can be ID, username, phone number, `"me"`, `"self"` or TL object
*/ */
getUsers(ids: InputPeerLike[]): Promise<User[]> getUsers(ids: InputPeerLike[]): Promise<User[]>
/**
* Iterate over profile photos
*
* @param userId User ID, username, phone number, `"me"` or `"self"`
* @param params
*/
iterProfilePhotos(
userId: InputPeerLike,
params?: {
/**
* Offset from which to fetch.
*
* Defaults to `0`
*/
offset?: number
/**
* Maximum number of items to fetch
*
* Defaults to `Infinity`, i.e. all items are fetched
*/
limit?: number
/**
* Size of chunks which are fetched. Usually not needed.
*
* Defaults to `100`
*/
chunkSize?: number
/**
* If set, the method will return only photos
* with IDs less than the set one
*/
maxId?: tl.Long
}
): AsyncIterableIterator<Photo>
/** /**
* Get the `InputPeer` of a known peer id. * Get the `InputPeer` of a known peer id.
* Useful when an `InputPeer` is needed. * Useful when an `InputPeer` is needed.
@ -2270,6 +2405,58 @@ export interface TelegramClient extends BaseTelegramClient {
* @param offline (default: `true`) Whether the user is currently offline * @param offline (default: `true`) Whether the user is currently offline
*/ */
setOffline(offline?: boolean): Promise<void> setOffline(offline?: boolean): Promise<void>
/**
* Set a new profile photo or video.
*
* @param type Media type (photo or video)
* @param media Input media file
* @param previewSec
* When `type = video`, timestamp in seconds which will be shown
* as a static preview.
*/
setProfilePhoto(
type: 'photo' | 'video',
media: InputFileLike,
previewSec?: number
): Promise<Photo>
/**
* Unblock a user
*
* @param id User ID, username or phone number
*/
unblockUser(id: InputPeerLike): Promise<void>
/**
* Update your profile details.
*
* Only pass fields that you want to change.
*
* @param params
*/
updateProfile(params: {
/**
* New first name
*/
firstName?: string
/**
* New last name. Pass `''` (empty string) to remove it
*/
lastName?: string
/**
* New bio (max 70 chars). Pass `''` (empty string) to remove it
*/
bio?: string
}): Promise<User>
/**
* Change username of the current user.
*
* Note that bots usernames must be changed through
* bot support or re-created from scratch.
*
* @param username New username (5-32 chars, allowed chars: `a-zA-Z0-9_`), or `null` to remove
*/
updateUsername(username: string | null): Promise<User>
} }
/** @internal */ /** @internal */
export class TelegramClient extends BaseTelegramClient { export class TelegramClient extends BaseTelegramClient {
@ -2386,6 +2573,12 @@ export class TelegramClient extends BaseTelegramClient {
unregisterParseMode = unregisterParseMode unregisterParseMode = unregisterParseMode
getParseMode = getParseMode getParseMode = getParseMode
setDefaultParseMode = setDefaultParseMode setDefaultParseMode = setDefaultParseMode
changeCloudPassword = changeCloudPassword
enableCloudPassword = enableCloudPassword
verifyPasswordEmail = verifyPasswordEmail
resendPasswordEmail = resendPasswordEmail
cancelPasswordEmail = cancelPasswordEmail
removeCloudPassword = removeCloudPassword
addStickerToSet = addStickerToSet addStickerToSet = addStickerToSet
createStickerSet = createStickerSet createStickerSet = createStickerSet
deleteStickerFromSet = deleteStickerFromSet deleteStickerFromSet = deleteStickerFromSet
@ -2400,9 +2593,16 @@ export class TelegramClient extends BaseTelegramClient {
protected _handleUpdate = _handleUpdate protected _handleUpdate = _handleUpdate
catchUp = catchUp catchUp = catchUp
blockUser = blockUser blockUser = blockUser
deleteProfilePhotos = deleteProfilePhotos
getCommonChats = getCommonChats getCommonChats = getCommonChats
getMe = getMe getMe = getMe
getProfilePhotos = getProfilePhotos
getUsers = getUsers getUsers = getUsers
iterProfilePhotos = iterProfilePhotos
resolvePeer = resolvePeer resolvePeer = resolvePeer
setOffline = setOffline setOffline = setOffline
setProfilePhoto = setProfilePhoto
unblockUser = unblockUser
updateProfile = updateProfile
updateUsername = updateUsername
} }

View file

@ -32,7 +32,8 @@ import {
TakeoutSession, TakeoutSession,
StickerSet, StickerSet,
Poll, Poll,
TypingStatus TypingStatus,
Photo
} from '../types' } from '../types'
// @copy // @copy

View file

@ -6,7 +6,10 @@ import {
MtCuteArgumentError, MtCuteArgumentError,
MtCuteInvalidPeerTypeError, MtCuteInvalidPeerTypeError,
} from '../../types' } from '../../types'
import { normalizeToInputChannel, normalizeToInputPeer } from '../../utils/peer-utils' import {
normalizeToInputChannel,
normalizeToInputPeer,
} from '../../utils/peer-utils'
import { tl } from '@mtcute/tl' import { tl } from '@mtcute/tl'
import { fileIdToInputPhoto, tdFileId } from '@mtcute/file-id' import { fileIdToInputPhoto, tdFileId } from '@mtcute/file-id'
@ -31,7 +34,13 @@ export async function setChatPhoto(
previewSec?: number previewSec?: number
): Promise<void> { ): Promise<void> {
const chat = normalizeToInputPeer(await this.resolvePeer(chatId)) const chat = normalizeToInputPeer(await this.resolvePeer(chatId))
if (!(chat._ === 'inputPeerChat' || chat._ === 'inputPeerChannel')) if (
!(
chat._ === 'inputPeerChat' ||
chat._ === 'inputPeerChannel' ||
chat._ === 'inputPeerChannelFromMessage'
)
)
throw new MtCuteInvalidPeerTypeError(chatId, 'chat or channel') throw new MtCuteInvalidPeerTypeError(chatId, 'chat or channel')
let photo: tl.TypeInputChatPhoto | undefined = undefined let photo: tl.TypeInputChatPhoto | undefined = undefined
@ -49,11 +58,16 @@ export async function setChatPhoto(
const input = fileIdToInputPhoto(media) const input = fileIdToInputPhoto(media)
photo = { photo = {
_: 'inputChatPhoto', _: 'inputChatPhoto',
id: input id: input,
} }
} }
} else if (typeof media === 'object' && tl.isAnyInputMedia(media)) { } else if (typeof media === 'object' && tl.isAnyInputMedia(media)) {
throw new MtCuteArgumentError("Chat photo can't be InputMedia") if (media._ === 'inputMediaPhoto') {
photo = {
_: 'inputChatPhoto',
id: media.id,
}
} else throw new MtCuteArgumentError("Chat photo can't be InputMedia")
} else if (isUploadedFile(media)) { } else if (isUploadedFile(media)) {
inputFile = media.inputFile inputFile = media.inputFile
} else if (typeof media === 'object' && tl.isAnyInputFile(media)) { } else if (typeof media === 'object' && tl.isAnyInputFile(media)) {
@ -69,7 +83,7 @@ export async function setChatPhoto(
photo = { photo = {
_: 'inputChatUploadedPhoto', _: 'inputChatUploadedPhoto',
[type === 'photo' ? 'file' : 'video']: inputFile!, [type === 'photo' ? 'file' : 'video']: inputFile!,
videoStartTs: previewSec videoStartTs: previewSec,
} }
} }
@ -78,13 +92,13 @@ export async function setChatPhoto(
res = await this.call({ res = await this.call({
_: 'messages.editChatPhoto', _: 'messages.editChatPhoto',
chatId: chat.chatId, chatId: chat.chatId,
photo photo,
}) })
} else { } else {
res = await this.call({ res = await this.call({
_: 'channels.editPhoto', _: 'channels.editPhoto',
channel: normalizeToInputChannel(chat)!, channel: normalizeToInputChannel(chat)!,
photo photo,
}) })
} }
this._handleUpdate(res) this._handleUpdate(res)

View file

@ -5,15 +5,14 @@ import { normalizeToInputPeer } from '../../utils/peer-utils'
/** /**
* Block a user * Block a user
* *
* @param id User ID, its username or phone number * @param id User ID, username or phone number
* @returns Whether the action was successful
* @internal * @internal
*/ */
export async function blockUser( export async function blockUser(
this: TelegramClient, this: TelegramClient,
id: InputPeerLike id: InputPeerLike
): Promise<boolean> { ): Promise<void> {
return this.call({ await this.call({
_: 'contacts.block', _: 'contacts.block',
id: normalizeToInputPeer(await this.resolvePeer(id)), id: normalizeToInputPeer(await this.resolvePeer(id)),
}) })

View file

@ -0,0 +1,30 @@
import { TelegramClient } from '../../client'
import { MaybeArray } from '@mtcute/core'
import { tl } from '@mtcute/tl'
import { fileIdToInputPhoto } from '../../../../file-id'
/**
* Delete your own profile photos
*
* @param ids ID(s) of the photos. Can be file IDs or raw TL objects
* @internal
*/
export async function deleteProfilePhotos(
this: TelegramClient,
ids: MaybeArray<string | tl.TypeInputPhoto>
): Promise<void> {
if (!Array.isArray(ids)) ids = [ids]
const photos = ids.map((id) => {
if (typeof id === 'string') {
return fileIdToInputPhoto(id)
}
return id
})
await this.call({
_: 'photos.deletePhotos',
id: photos
})
}

View file

@ -0,0 +1,47 @@
import { TelegramClient } from '../../client'
import { InputPeerLike, MtCuteInvalidPeerTypeError, Photo } from '../../types'
import { normalizeToInputUser } from '../../utils/peer-utils'
import bigInt from 'big-integer'
import { tl } from '@mtcute/tl'
/**
* Get a list of profile pictures of a user
*
* @param userId User ID, username, phone number, `"me"` or `"self"`
* @param params
* @internal
*/
export async function getProfilePhotos(
this: TelegramClient,
userId: InputPeerLike,
params?: {
/**
* Offset from which to fetch.
*
* Defaults to `0`
*/
offset?: number
/**
* Maximum number of items to fetch (up to 100)
*
* Defaults to `100`
*/
limit?: number
}
): Promise<Photo[]> {
if (!params) params = {}
const peer = normalizeToInputUser(await this.resolvePeer(userId))
if (!peer) throw new MtCuteInvalidPeerTypeError(userId, 'user')
const res = await this.call({
_: 'photos.getUserPhotos',
userId: peer,
offset: params.offset ?? 0,
limit: params.limit ?? 100,
maxId: bigInt.zero
})
return res.photos.map((it) => new Photo(this, it as tl.RawPhoto))
}

View file

@ -0,0 +1,77 @@
import { TelegramClient } from '../../client'
import { InputPeerLike, MtCuteInvalidPeerTypeError, Photo } from '../../types'
import { normalizeToInputUser } from '../../utils/peer-utils'
import { tl } from '@mtcute/tl'
import bigInt from 'big-integer'
/**
* Iterate over profile photos
*
* @param userId User ID, username, phone number, `"me"` or `"self"`
* @param params
* @internal
*/
export async function* iterProfilePhotos(
this: TelegramClient,
userId: InputPeerLike,
params?: {
/**
* Offset from which to fetch.
*
* Defaults to `0`
*/
offset?: number
/**
* Maximum number of items to fetch
*
* Defaults to `Infinity`, i.e. all items are fetched
*/
limit?: number
/**
* Size of chunks which are fetched. Usually not needed.
*
* Defaults to `100`
*/
chunkSize?: number
/**
* If set, the method will return only photos
* with IDs less than the set one
*/
maxId?: tl.Long
}
): AsyncIterableIterator<Photo> {
if (!params) params = {}
const peer = normalizeToInputUser(await this.resolvePeer(userId))
if (!peer) throw new MtCuteInvalidPeerTypeError(userId, 'user')
let offset = params.offset || 0
let current = 0
const total = params.limit || Infinity
const limit = Math.min(params.chunkSize || 100, total)
const maxId = params.maxId || bigInt.zero
for (;;) {
const res = await this.call({
_: 'photos.getUserPhotos',
userId: peer,
limit: Math.min(limit, total - current),
offset,
maxId
})
if (!res.photos.length) break
offset += res.photos.length
yield* res.photos.map((it) => new Photo(this, it as tl.RawPhoto))
current += res.photos.length
if (current >= total) break
}
}

View file

@ -0,0 +1,28 @@
import { TelegramClient } from '../../client'
import { InputFileLike, Photo } from '../../types'
import { tl } from '@mtcute/tl'
/**
* Set a new profile photo or video.
*
* @param type Media type (photo or video)
* @param media Input media file
* @param previewSec
* When `type = video`, timestamp in seconds which will be shown
* as a static preview.
* @internal
*/
export async function setProfilePhoto(
this: TelegramClient,
type: 'photo' | 'video',
media: InputFileLike,
previewSec?: number
): Promise<Photo> {
const res = await this.call({
_: 'photos.uploadProfilePhoto',
[type === 'photo' ? 'file' : 'video']: await this._normalizeInputFile(media, {}),
videoStartTs: previewSec
})
return new Photo(this, res.photo as tl.RawPhoto)
}

View file

@ -0,0 +1,19 @@
import { TelegramClient } from '../../client'
import { InputPeerLike } from '../../types'
import { normalizeToInputPeer } from '../../utils/peer-utils'
/**
* Unblock a user
*
* @param id User ID, username or phone number
* @internal
*/
export async function unblockUser(
this: TelegramClient,
id: InputPeerLike
): Promise<void> {
await this.call({
_: 'contacts.unblock',
id: normalizeToInputPeer(await this.resolvePeer(id)),
})
}

View file

@ -0,0 +1,39 @@
import { TelegramClient } from '../../client'
import { User } from '../../types'
/**
* Update your profile details.
*
* Only pass fields that you want to change.
*
* @param params
* @internal
*/
export async function updateProfile(
this: TelegramClient,
params: {
/**
* New first name
*/
firstName?: string
/**
* New last name. Pass `''` (empty string) to remove it
*/
lastName?: string
/**
* New bio (max 70 chars). Pass `''` (empty string) to remove it
*/
bio?: string
}
): Promise<User> {
const res = await this.call({
_: 'account.updateProfile',
firstName: params.firstName,
lastName: params.lastName,
about: params.bio
})
return new User(this, res)
}

View file

@ -0,0 +1,25 @@
import { TelegramClient } from '../../client'
import { User } from '../../types'
/**
* Change username of the current user.
*
* Note that bots usernames must be changed through
* bot support or re-created from scratch.
*
* @param username New username (5-32 chars, allowed chars: `a-zA-Z0-9_`), or `null` to remove
* @internal
*/
export async function updateUsername(
this: TelegramClient,
username: string | null
): Promise<User> {
if (username === null) username = ''
const res = await this.call({
_: 'account.updateUsername',
username
})
return new User(this, res)
}