From f3e4a34eab5894c931bc0ac791fa56d69453cf00 Mon Sep 17 00:00:00 2001 From: teidesu Date: Mon, 10 May 2021 14:27:57 +0300 Subject: [PATCH] feat(client): deleteUserHistory method, also properly handle messages.affectedHistory i suppose? this is an incredibly bad hack but i guess it works so who cares? --- packages/client/src/client.ts | 16 +++++++- .../src/methods/chats/delete-history.ts | 17 ++++++-- .../src/methods/chats/delete-user-history.ts | 41 +++++++++++++++++++ .../src/methods/messages/delete-messages.ts | 19 +++++---- packages/client/src/methods/updates.ts | 9 ++-- packages/client/src/utils/misc-utils.ts | 4 +- packages/client/src/utils/updates-utils.ts | 36 ++++++++++++++++ 7 files changed, 125 insertions(+), 17 deletions(-) create mode 100644 packages/client/src/methods/chats/delete-user-history.ts create mode 100644 packages/client/src/utils/updates-utils.ts diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index d0bd294e..b9ec3fd6 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -26,6 +26,7 @@ import { deleteChannel } from './methods/chats/delete-channel' import { deleteChatPhoto } from './methods/chats/delete-chat-photo' import { deleteGroup } from './methods/chats/delete-group' import { deleteHistory } from './methods/chats/delete-history' +import { deleteUserHistory } from './methods/chats/delete-user-history' import { getChatMember } from './methods/chats/get-chat-member' import { getChatMembers } from './methods/chats/get-chat-members' import { getChatPreview } from './methods/chats/get-chat-preview' @@ -662,6 +663,16 @@ export interface TelegramClient extends BaseTelegramClient { mode?: 'delete' | 'clear' | 'revoke', maxId?: number ): Promise + /** + * Delete all messages of a user in a supergroup + * + * @param chatId Chat ID + * @param userId User ID + */ + deleteUserHistory( + chatId: InputPeerLike, + userId: InputPeerLike + ): Promise /** * Get information about a single chat member * @@ -1271,7 +1282,7 @@ export interface TelegramClient extends BaseTelegramClient { * > **Note**: each administrator has their own primary invite link, * > and bots by default don't have one. * - * @param chatId Chat ID + * @param chatId Chat IDs */ exportInviteLink(chatId: InputPeerLike): Promise /** @@ -1369,7 +1380,7 @@ export interface TelegramClient extends BaseTelegramClient { chatId: InputPeerLike, ids: MaybeArray, revoke?: boolean - ): Promise + ): Promise /** * Edit sent inline message text, media and reply markup. * @@ -2670,6 +2681,7 @@ export class TelegramClient extends BaseTelegramClient { deleteChatPhoto = deleteChatPhoto deleteGroup = deleteGroup deleteHistory = deleteHistory + deleteUserHistory = deleteUserHistory getChatMember = getChatMember getChatMembers = getChatMembers getChatPreview = getChatPreview diff --git a/packages/client/src/methods/chats/delete-history.ts b/packages/client/src/methods/chats/delete-history.ts index 11510612..885a96c6 100644 --- a/packages/client/src/methods/chats/delete-history.ts +++ b/packages/client/src/methods/chats/delete-history.ts @@ -1,6 +1,8 @@ import { TelegramClient } from '../../client' import { InputPeerLike } from '../../types' -import { normalizeToInputPeer } from '../../utils/peer-utils' +import { normalizeToInputChannel, normalizeToInputPeer } from '../../utils/peer-utils' +import { createDummyUpdate } from '../../utils/updates-utils' +import { tl } from '@mtcute/tl' /** * Delete communication history (for private chats @@ -23,11 +25,20 @@ export async function deleteHistory( mode: 'delete' | 'clear' | 'revoke' = 'delete', maxId = 0 ): Promise { - await this.call({ + const peer = normalizeToInputPeer(await this.resolvePeer(chat)) + + const res = await this.call({ _: 'messages.deleteHistory', justClear: mode === 'clear', revoke: mode === 'revoke', - peer: normalizeToInputPeer(await this.resolvePeer(chat)), + peer, maxId }) + + const channel = normalizeToInputChannel(peer) + if (channel) { + this._handleUpdate(createDummyUpdate(res.pts, res.ptsCount, (channel as tl.RawInputChannel).channelId)) + } else { + this._handleUpdate(createDummyUpdate(res.pts, res.ptsCount)) + } } diff --git a/packages/client/src/methods/chats/delete-user-history.ts b/packages/client/src/methods/chats/delete-user-history.ts new file mode 100644 index 00000000..0b6da81e --- /dev/null +++ b/packages/client/src/methods/chats/delete-user-history.ts @@ -0,0 +1,41 @@ +import { TelegramClient } from '../../client' +import { InputPeerLike, MtCuteInvalidPeerTypeError } from '../../types' +import { + normalizeToInputChannel, + normalizeToInputUser, +} from '../../utils/peer-utils' +import { tl } from '@mtcute/tl' +import { createDummyUpdate } from '../../utils/updates-utils' + +/** + * Delete all messages of a user in a supergroup + * + * @param chatId Chat ID + * @param userId User ID + * @internal + */ +export async function deleteUserHistory( + this: TelegramClient, + chatId: InputPeerLike, + userId: InputPeerLike +): Promise { + const channel = normalizeToInputChannel(await this.resolvePeer(chatId)) + if (!channel) throw new MtCuteInvalidPeerTypeError(chatId, 'channel') + + const user = normalizeToInputUser(await this.resolvePeer(userId)) + if (!user) throw new MtCuteInvalidPeerTypeError(userId, 'user') + + const res = await this.call({ + _: 'channels.deleteUserHistory', + channel, + userId: user, + }) + + this._handleUpdate( + createDummyUpdate( + res.pts, + res.ptsCount, + (channel as tl.RawInputChannel).channelId + ) + ) +} diff --git a/packages/client/src/methods/messages/delete-messages.ts b/packages/client/src/methods/messages/delete-messages.ts index 81a783e1..fec3e4f6 100644 --- a/packages/client/src/methods/messages/delete-messages.ts +++ b/packages/client/src/methods/messages/delete-messages.ts @@ -2,6 +2,8 @@ import { TelegramClient } from '../../client' import { InputPeerLike } from '../../types' import { MaybeArray } from '@mtcute/core' import { normalizeToInputChannel, normalizeToInputPeer } from '../../utils/peer-utils' +import { createDummyUpdate } from '../../utils/updates-utils' +import { tl } from '@mtcute/tl' /** * Delete messages, including service messages. @@ -16,28 +18,29 @@ export async function deleteMessages( chatId: InputPeerLike, ids: MaybeArray, revoke = true -): Promise { +): Promise { if (!Array.isArray(ids)) ids = [ids] const peer = await this.resolvePeer(chatId) const inputPeer = normalizeToInputPeer(peer) - let res + let upd if (inputPeer._ === 'inputPeerChannel') { - res = await this.call({ + const channel = normalizeToInputChannel(peer)! + const res = await this.call({ _: 'channels.deleteMessages', - channel: normalizeToInputChannel(peer)!, + channel, id: ids }) + upd = createDummyUpdate(res.pts, res.ptsCount, (channel as tl.RawInputChannel).channelId) } else { - res = await this.call({ + const res = await this.call({ _: 'messages.deleteMessages', id: ids, revoke }) + upd = createDummyUpdate(res.pts, res.ptsCount) } - this._pts = res.pts - - return !!res.ptsCount + this._handleUpdate(upd) } diff --git a/packages/client/src/methods/updates.ts b/packages/client/src/methods/updates.ts index 263c159e..98181595 100644 --- a/packages/client/src/methods/updates.ts +++ b/packages/client/src/methods/updates.ts @@ -10,6 +10,7 @@ import { extractChannelIdFromUpdate } from '../utils/misc-utils' import { Lock } from '../utils/lock' import bigInt from 'big-integer' import { MAX_CHANNEL_ID } from '@mtcute/core' +import { isDummyUpdate, isDummyUpdates } from '../utils/updates-utils' const debug = require('debug')('mtcute:upds') @@ -488,7 +489,7 @@ export function _handleUpdate( } } - if (!noDispatch) { + if (!isDummyUpdate(upd) && !noDispatch) { this.dispatchUpdate(upd, users, chats) } @@ -502,8 +503,10 @@ export function _handleUpdate( } } - // this._seq = update.seq - this._date = update.date + if (!isDummyUpdates(update)) { + // this._seq = update.seq + this._date = update.date + } } else if (update._ === 'updateShort') { const upd = update.update if (upd._ === 'updateDcOptions' && this._config) { diff --git a/packages/client/src/utils/misc-utils.ts b/packages/client/src/utils/misc-utils.ts index ccc043f4..0a00a134 100644 --- a/packages/client/src/utils/misc-utils.ts +++ b/packages/client/src/utils/misc-utils.ts @@ -25,7 +25,7 @@ export function extractChannelIdFromUpdate( upd: tl.TypeUpdate ): number | undefined { // holy shit - return 'channelId' in upd + const res = 'channelId' in upd ? upd.channelId : 'message' in upd && typeof upd.message !== 'string' && @@ -34,6 +34,8 @@ export function extractChannelIdFromUpdate( 'channelId' in upd.message.peerId ? upd.message.peerId.channelId : undefined + if (res === 0) return undefined + return res } export function normalizeDate( diff --git a/packages/client/src/utils/updates-utils.ts b/packages/client/src/utils/updates-utils.ts new file mode 100644 index 00000000..c437a6ea --- /dev/null +++ b/packages/client/src/utils/updates-utils.ts @@ -0,0 +1,36 @@ +import { tl } from '@mtcute/tl' + +// dummy updates which are used for methods that return messages.affectedHistory. +// that is not an update, but it carries info about pts, and we need to handle it + +/** @internal */ +export function createDummyUpdate(pts: number, ptsCount: number, channelId = 0): tl.TypeUpdates { + return { + _: 'updates', + seq: 0, + date: 0, + chats: [], + users: [], + updates: [ + { + _: 'updatePinnedChannelMessages', + channelId, + pts, + ptsCount, + // since message id cant be negative, using negative 42 + // here makes it distinctive from real updates + messages: [-42] + } + ] + } +} + +/** @internal */ +export function isDummyUpdate(upd: tl.TypeUpdate): boolean { + return upd._ === 'updatePinnedChannelMessages' && upd.messages.length === 1 && upd.messages[0] === -42 +} + +/** @internal */ +export function isDummyUpdates(upd: tl.TypeUpdates): boolean { + return upd._ === 'updates' && upd.updates.length === 1 && isDummyUpdate(upd.updates[0]) +}