From 39236c94c260c53c65254fe5e1d61d9e016975a0 Mon Sep 17 00:00:00 2001 From: teidesu Date: Sun, 25 Apr 2021 17:10:37 +0300 Subject: [PATCH] feat(client): contacts related methods --- packages/client/src/client.ts | 69 +++++++++++++++++ .../src/methods/contacts/add-contact.ts | 61 +++++++++++++++ .../src/methods/contacts/delete-contacts.ts | 76 +++++++++++++++++++ .../src/methods/contacts/get-contacts.ts | 20 +++++ .../src/methods/contacts/import-contacts.ts | 28 +++++++ packages/core/src/types/utils.ts | 1 + 6 files changed, 255 insertions(+) create mode 100644 packages/client/src/methods/contacts/add-contact.ts create mode 100644 packages/client/src/methods/contacts/delete-contacts.ts create mode 100644 packages/client/src/methods/contacts/get-contacts.ts create mode 100644 packages/client/src/methods/contacts/import-contacts.ts diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index 23f4ce43..e7270bca 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -40,6 +40,10 @@ import { setChatTitle } from './methods/chats/set-chat-title' import { setChatUsername } from './methods/chats/set-chat-username' import { setSlowMode } from './methods/chats/set-slow-mode' import { unarchiveChats } from './methods/chats/unarchive-chats' +import { addContact } from './methods/contacts/add-contact' +import { deleteContacts } from './methods/contacts/delete-contacts' +import { getContacts } from './methods/contacts/get-contacts' +import { importContacts } from './methods/contacts/import-contacts' import { createFolder } from './methods/dialogs/create-folder' import { deleteFolder } from './methods/dialogs/delete-folder' import { editFolder } from './methods/dialogs/edit-folder' @@ -707,6 +711,67 @@ export interface TelegramClient extends BaseTelegramClient { * @param chats Chat ID(s), username(s), phone number(s), `"me"` or `"self"` */ unarchiveChats(chats: MaybeArray): Promise + /** + * Add an existing Telegram user as a contact + * + * @param userId User ID, username or phone number + * @param params Contact details + */ + addContact( + userId: InputPeerLike, + params: { + /** + * First name of the contact + */ + firstName: string + + /** + * Last name of the contact + */ + lastName?: string + + /** + * Phone number of the contact, if available + */ + phone?: string + + /** + * Whether to share your own phone number + * with the newly created contact (defaults to `false`) + */ + sharePhone?: boolean + } + ): Promise + /** + * Delete a single contact from your Telegram contacts list + * + * Returns deleted contact's profile or `null` in case + * that user was not in your contacts list + * + * @param userId User ID, username or phone number + */ + deleteContacts(userId: InputPeerLike): Promise + /** + * Delete one or more contacts from your Telegram contacts list + * + * Returns deleted contact's profiles. Does not return + * profiles of users that were not in your contacts list + * + * @param userIds User IDs, usernames or phone numbers + */ + deleteContacts(userIds: InputPeerLike[]): Promise + /** + * Get list of contacts from your Telegram contacts list. + */ + getContacts(): Promise + /** + * Import contacts to your Telegram contacts list. + * + * @param contacts List of contacts + */ + importContacts( + contacts: PartialOnly, 'clientId'>[] + ): Promise /** * Create a folder from given parameters * @@ -1834,6 +1899,10 @@ export class TelegramClient extends BaseTelegramClient { setChatUsername = setChatUsername setSlowMode = setSlowMode unarchiveChats = unarchiveChats + addContact = addContact + deleteContacts = deleteContacts + getContacts = getContacts + importContacts = importContacts createFolder = createFolder deleteFolder = deleteFolder editFolder = editFolder diff --git a/packages/client/src/methods/contacts/add-contact.ts b/packages/client/src/methods/contacts/add-contact.ts new file mode 100644 index 00000000..145d759e --- /dev/null +++ b/packages/client/src/methods/contacts/add-contact.ts @@ -0,0 +1,61 @@ +import { TelegramClient } from '../../client' +import { InputPeerLike, MtCuteInvalidPeerTypeError, MtCuteTypeAssertionError, User } from '../../types' +import { normalizeToInputUser } from '../../utils/peer-utils' +import { tl } from '@mtcute/tl' + +/** + * Add an existing Telegram user as a contact + * + * @param userId User ID, username or phone number + * @param params Contact details + * @internal + */ +export async function addContact( + this: TelegramClient, + userId: InputPeerLike, + params: { + /** + * First name of the contact + */ + firstName: string + + /** + * Last name of the contact + */ + lastName?: string + + /** + * Phone number of the contact, if available + */ + phone?: string + + /** + * Whether to share your own phone number + * with the newly created contact (defaults to `false`) + */ + sharePhone?: boolean + } +): Promise { + const peer = normalizeToInputUser(await this.resolvePeer(userId)) + if (!peer) throw new MtCuteInvalidPeerTypeError(userId, 'user') + + const res = await this.call({ + _: 'contacts.addContact', + id: peer, + firstName: params.firstName, + lastName: params.lastName ?? '', + phone: params.phone ?? '', + addPhonePrivacyException: !!params.sharePhone + }) + + if (!(res._ === 'updates' || res._ === 'updatesCombined')) + throw new MtCuteTypeAssertionError( + 'addContact', + 'updates | updatesCombined', + res._ + ) + + this._handleUpdate(res) + + return new User(this, res.users[0] as tl.RawUser) +} diff --git a/packages/client/src/methods/contacts/delete-contacts.ts b/packages/client/src/methods/contacts/delete-contacts.ts new file mode 100644 index 00000000..05af17a5 --- /dev/null +++ b/packages/client/src/methods/contacts/delete-contacts.ts @@ -0,0 +1,76 @@ +import { TelegramClient } from '../../client' +import { MaybeArray } from '@mtcute/core' +import { InputPeerLike, MtCuteInvalidPeerTypeError, MtCuteTypeAssertionError, User } from '../../types' +import { normalizeToInputUser } from '../../utils/peer-utils' +import { tl } from '@mtcute/tl' + +/** + * Delete a single contact from your Telegram contacts list + * + * Returns deleted contact's profile or `null` in case + * that user was not in your contacts list + * + * @param userId User ID, username or phone number + * @internal + */ +export async function deleteContacts( + this: TelegramClient, + userId: InputPeerLike +): Promise + +/** + * Delete one or more contacts from your Telegram contacts list + * + * Returns deleted contact's profiles. Does not return + * profiles of users that were not in your contacts list + * + * @param userIds User IDs, usernames or phone numbers + * @internal + */ +export async function deleteContacts( + this: TelegramClient, + userIds: InputPeerLike[] +): Promise + +/** @internal */ +export async function deleteContacts( + this: TelegramClient, + userIds: MaybeArray +): Promise | null> { + const single = !Array.isArray(userIds) + if (single) userIds = [userIds as InputPeerLike] + + const inputPeers = (( + await Promise.all( + (userIds as InputPeerLike[]).map((it) => + this.resolvePeer(it).then(normalizeToInputUser) + ) + ) + ).filter(Boolean) as unknown) as tl.TypeInputUser[] + + if (single && !inputPeers.length) + throw new MtCuteInvalidPeerTypeError( + (userIds as InputPeerLike[])[0], + 'user' + ) + + const res = await this.call({ + _: 'contacts.deleteContacts', + id: inputPeers + }) + + if (!(res._ === 'updates' || res._ === 'updatesCombined')) + throw new MtCuteTypeAssertionError( + 'addContact', + 'updates | updatesCombined', + res._ + ) + + if (single && !res.updates.length) return null + + this._handleUpdate(res) + + const users = res.users.map(user => new User(this, user as tl.RawUser)) + + return single ? users[0] : users +} diff --git a/packages/client/src/methods/contacts/get-contacts.ts b/packages/client/src/methods/contacts/get-contacts.ts new file mode 100644 index 00000000..5a929c60 --- /dev/null +++ b/packages/client/src/methods/contacts/get-contacts.ts @@ -0,0 +1,20 @@ +import { TelegramClient } from '../../client' +import { User } from '../../types' +import { assertTypeIs } from '../../utils/type-assertion' +import { tl } from '@mtcute/tl' + +/** + * Get list of contacts from your Telegram contacts list. + * @internal + */ +export async function getContacts( + this: TelegramClient +): Promise { + const res = await this.call({ + _: 'contacts.getContacts', + hash: 0 + }) + assertTypeIs('getContacts', res, 'contacts.contacts') + + return res.users.map((user) => new User(this, user as tl.RawUser)) +} diff --git a/packages/client/src/methods/contacts/import-contacts.ts b/packages/client/src/methods/contacts/import-contacts.ts new file mode 100644 index 00000000..81bb2603 --- /dev/null +++ b/packages/client/src/methods/contacts/import-contacts.ts @@ -0,0 +1,28 @@ +import { TelegramClient } from '../../client' +import { tl } from '@mtcute/tl' +import { PartialOnly } from '@mtcute/core' +import bigInt from 'big-integer' + +/** + * Import contacts to your Telegram contacts list. + * + * @param contacts List of contacts + * @internal + */ +export async function importContacts( + this: TelegramClient, + contacts: PartialOnly, 'clientId'>[] +): Promise { + let seq = bigInt.zero + + const contactsNorm: tl.RawInputPhoneContact[] = contacts.map((input) => ({ + _: 'inputPhoneContact', + clientId: (seq = seq.plus(1)), + ...input, + })) + + return await this.call({ + _: 'contacts.importContacts', + contacts: contactsNorm, + }) +} diff --git a/packages/core/src/types/utils.ts b/packages/core/src/types/utils.ts index dce1b4cc..875068c5 100644 --- a/packages/core/src/types/utils.ts +++ b/packages/core/src/types/utils.ts @@ -1,5 +1,6 @@ export type MaybeAsync = T | Promise export type PartialExcept = Partial> & Pick +export type PartialOnly = Partial> & Omit export type MaybeArray = T | T[]