import type { TelegramClient } from "./TelegramClient" ; import type { EntitiesLike , Entity , EntityLike , ValueOf } from "../define" ; import { sleep , getMinBigInt , TotalList , betterConsoleLog } from "../Helpers" ; import { RequestIter } from "../requestIter" ; import { helpers , utils } from "../" ; import { Api } from "../tl" ; import bigInt , { BigInteger } from "big-integer" ; import { inspect } from "util" ; const _MAX_PARTICIPANTS_CHUNK_SIZE = 200 ; const _MAX_ADMIN_LOG_CHUNK_SIZE = 100 ; const _MAX_PROFILE_PHOTO_CHUNK_SIZE = 100 ; interface ChatActionInterface { delay : number ; autoCancel : boolean ; } class _ChatAction { static _str_mapping = { typing : new Api . SendMessageTypingAction ( ) , contact : new Api . SendMessageChooseContactAction ( ) , game : new Api . SendMessageGamePlayAction ( ) , location : new Api . SendMessageGeoLocationAction ( ) , "record-audio" : new Api . SendMessageRecordAudioAction ( ) , "record-voice" : new Api . SendMessageRecordAudioAction ( ) , "record-round" : new Api . SendMessageRecordRoundAction ( ) , "record-video" : new Api . SendMessageRecordVideoAction ( ) , audio : new Api . SendMessageUploadAudioAction ( { progress : 1 } ) , voice : new Api . SendMessageUploadAudioAction ( { progress : 1 } ) , song : new Api . SendMessageUploadAudioAction ( { progress : 1 } ) , round : new Api . SendMessageUploadRoundAction ( { progress : 1 } ) , video : new Api . SendMessageUploadVideoAction ( { progress : 1 } ) , photo : new Api . SendMessageUploadPhotoAction ( { progress : 1 } ) , document : new Api . SendMessageUploadDocumentAction ( { progress : 1 } ) , file : new Api . SendMessageUploadDocumentAction ( { progress : 1 } ) , cancel : new Api . SendMessageCancelAction ( ) , } ; private _client : TelegramClient ; private readonly _chat : EntityLike ; private readonly _action : ValueOf < typeof _ChatAction . _str_mapping > ; private readonly _delay : number ; private readonly autoCancel : boolean ; private _request ? : Api . AnyRequest ; private _task : null ; private _running : boolean ; [ inspect . custom ] ( ) { return betterConsoleLog ( this ) ; } constructor ( client , chat , action , params = { delay : 4 , autoCancel : true , } ) { this . _client = client ; this . _chat = chat ; this . _action = action ; this . _delay = params . delay ; this . autoCancel = params . autoCancel ; this . _request = undefined ; this . _task = null ; this . _running = false ; } async start ( ) { this . _request = new Api . messages . SetTyping ( { peer : this . _chat , action : this . _action , } ) ; this . _running = true ; this . _update ( ) ; } async stop ( ) { this . _running = false ; if ( this . autoCancel ) { await this . _client . invoke ( new Api . messages . SetTyping ( { peer : this . _chat , action : new Api . SendMessageCancelAction ( ) , } ) ) ; } } async _update ( ) { while ( this . _running ) { if ( this . _request != undefined ) { await this . _client . invoke ( this . _request ) ; } await sleep ( this . _delay * 1000 ) ; } } progress ( current , total ) { if ( "progress" in this . _action ) { this . _action . progress = 100 * Math . round ( current / total ) ; } } } interface ParticipantsIterInterface { entity : EntityLike ; filter : any ; search ? : string ; showTotal ? : boolean ; } export class _ParticipantsIter extends RequestIter { private filterEntity : ( ( entity : Entity ) => boolean ) | undefined ; private requests ? : Api . channels . GetParticipants [ ] ; [ inspect . custom ] ( ) { return betterConsoleLog ( this ) ; } async _init ( { entity , filter , search , showTotal , } ) { if ( filter && filter . constructor === Function ) { if ( [ Api . ChannelParticipantsBanned , Api . ChannelParticipantsKicked , Api . ChannelParticipantsSearch , Api . ChannelParticipantsContacts , ] . includes ( filter ) ) { filter = new filter ( { q : "" , } ) ; } else { filter = new filter ( ) ; } } entity = await this . client . getInputEntity ( entity ) ; const ty = helpers . _entityType ( entity ) ; if ( search && ( filter || ty != helpers . _EntityType . CHANNEL ) ) { search = search . toLowerCase ( ) ; this . filterEntity = ( entity ) => { return ( utils . getDisplayName ( entity ) . toLowerCase ( ) . includes ( search ! ) || ( "username" in entity ? entity . username || "" : "" ) . toLowerCase ( ) . includes ( search ! ) ) ; } ; } else { this . filterEntity = ( entity ) => true ; } this . requests = [ ] ; if ( ty == helpers . _EntityType . CHANNEL ) { if ( showTotal ) { const channel = await this . client . invoke ( new Api . channels . GetFullChannel ( { channel : entity , } ) ) ; if ( ! ( channel . fullChat instanceof Api . ChatFull ) ) { this . total = channel . fullChat . participantsCount ; } } if ( this . total && this . total <= 0 ) { return false ; } this . requests . push ( new Api . channels . GetParticipants ( { channel : entity , filter : filter || new Api . ChannelParticipantsSearch ( { q : search || "" , } ) , offset : 0 , limit : _MAX_PARTICIPANTS_CHUNK_SIZE , hash : bigInt . zero , } ) ) ; } else if ( ty == helpers . _EntityType . CHAT ) { if ( ! ( "chatId" in entity ) ) { throw new Error ( "Found chat without id " + JSON . stringify ( entity ) ) ; } const full = await this . client . invoke ( new Api . messages . GetFullChat ( { chatId : entity . chatId , } ) ) ; if ( full . fullChat instanceof Api . ChatFull ) { if ( ! ( full . fullChat . participants instanceof Api . ChatParticipantsForbidden ) ) { this . total = full . fullChat . participants . participants . length ; } else { this . total = 0 ; return false ; } const users = new Map < string , Entity > ( ) ; for ( const user of full . users ) { users . set ( user . id . toString ( ) , user ) ; } for ( const participant of full . fullChat . participants . participants ) { const user = users . get ( participant . userId . toString ( ) ) ! ; if ( ! this . filterEntity ( user ) ) { continue ; } ( user as any ) . participant = participant ; this . buffer ?. push ( user ) ; } return true ; } } else { this . total = 1 ; if ( this . limit != 0 ) { const user = await this . client . getEntity ( entity ) ; if ( this . filterEntity ( user ) ) { ( user as any ) . participant = undefined ; this . buffer ?. push ( user ) ; } } return true ; } } async _loadNextChunk ( ) { if ( ! this . requests ?. length ) { return true ; } this . requests [ 0 ] . limit = Math . min ( this . limit - this . requests [ 0 ] . offset , _MAX_PARTICIPANTS_CHUNK_SIZE ) ; if ( this . requests [ 0 ] . offset > this . limit ) { return true ; } const results = [ ] ; for ( const request of this . requests ) { results . push ( await this . client . invoke ( request ) ) ; } for ( let i = this . requests . length - 1 ; i >= 0 ; i -- ) { const participants = results [ i ] ; if ( participants instanceof Api . channels . ChannelParticipantsNotModified || ! participants . users . length ) { this . requests . splice ( i , 1 ) ; continue ; } this . requests [ i ] . offset += participants . participants . length ; const users = new Map < string , Entity > ( ) ; for ( const user of participants . users ) { users . set ( user . id . toString ( ) , user ) ; } for ( const participant of participants . participants ) { if ( ! ( "userId" in participant ) ) { continue ; } const user = users . get ( participant . userId . toString ( ) ) ! ; if ( this . filterEntity && ! this . filterEntity ( user ) ) { continue ; } ( user as any ) . participant = participant ; this . buffer ?. push ( user ) ; } } return undefined ; } [ Symbol . asyncIterator ] ( ) { return super [ Symbol . asyncIterator ] ( ) ; } } interface _AdminLogFilterInterface { join ? : boolean ; leave ? : boolean ; invite ? : boolean ; restrict ? : boolean ; unrestrict ? : boolean ; ban ? : boolean ; unban ? : boolean ; promote ? : boolean ; demote ? : boolean ; info ? : boolean ; settings ? : boolean ; pinned ? : boolean ; edit ? : boolean ; delete ? : boolean ; groupCall ? : boolean ; } interface _AdminLogSearchInterface { admins ? : EntitiesLike ; search ? : string ; minId ? : BigInteger ; maxId ? : BigInteger ; } class _AdminLogIter extends RequestIter { private entity ? : Api . TypeInputPeer ; private request ? : Api . channels . GetAdminLog ; [ inspect . custom ] ( ) { return betterConsoleLog ( this ) ; } async _init ( entity , searchArgs ? , filterArgs ? ) { let eventsFilter = undefined ; if ( filterArgs && Object . values ( filterArgs ) . find ( ( element ) => element === true ) ) { eventsFilter = new Api . ChannelAdminLogEventsFilter ( { ... filterArgs , } ) ; } this . entity = await this . client . getInputEntity ( entity ) ; const adminList = [ ] ; if ( searchArgs && searchArgs . admins ) { for ( const admin of searchArgs . admins ) { adminList . push ( await this . client . getInputEntity ( admin ) ) ; } } this . request = new Api . channels . GetAdminLog ( { channel : this . entity , q : searchArgs ?. search || "" , minId : searchArgs ?. minId , maxId : searchArgs ?. maxId , limit : 0 , eventsFilter : eventsFilter , admins : adminList || undefined , } ) ; } async _loadNextChunk ( ) { if ( ! this . request ) { return true ; } this . request . limit = Math . min ( this . left , _MAX_ADMIN_LOG_CHUNK_SIZE ) ; const r = await this . client . invoke ( this . request ) ; const entities = new Map ( ) ; for ( const entity of [ ... r . users , ... r . chats ] ) { entities . set ( utils . getPeerId ( entity ) , entity ) ; } const eventIds = [ ] ; for ( const e of r . events ) { eventIds . push ( e . id ) ; } this . request . maxId = getMinBigInt ( [ bigInt . zero , ... eventIds ] ) ; for ( const ev of r . events ) { if ( ev . action instanceof Api . ChannelAdminLogEventActionEditMessage ) { } } } } /**
 * Used in iterParticipant and getParticipant. all params are optional.
 */ export interface IterParticipantsParams { /** how many members to retrieve. defaults to Number.MAX_SAFE_INTEGER (everyone) */ limit ? : number ; /** a query string to filter participants based on their display names and usernames. defaults to "" (everyone) */ search ? : string ; /** optional filter to be used. E.g only admins filter or only banned members filter. PS : some filters need more permissions. */ filter ? : Api . TypeChannelParticipantsFilter ; /** whether to call an extra request (GetFullChannel) to show the total of users in the group/channel. if set to false total will be 0 */ showTotal ? : boolean ; } hidden export function iterParticipants ( client , entity , { limit , search , filter , showTotal = true } ) { return new _ParticipantsIter ( client , limit ?? Number . MAX_SAFE_INTEGER , { } , { entity : entity , filter : filter , search : search , showTotal : showTotal , } ) ; } hidden export async function getParticipants ( client , entity , params ) { const it = client . iterParticipants ( entity , params ) ; return ( await it . collect ( ) ) as TotalList < Api . User > ; }