123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691 |
- const {IS_NODE} = require('./Helpers')
- const { constructors } = require('./tl')
- const { requests } = require('./tl')
- const USERNAME_RE = new RegExp('@|(?:https?:\\/\\/)?(?:www\\.)?' +
- '(?:telegram\\.(?:me|dog)|t\\.me)\\/(@|joinchat\\/)?')
- const JPEG_HEADER = Buffer.from('ffd8ffe000104a46494600010100000100010000ffdb004300281c1e231e19282321232d2b28303c64413c37373c7b585d4964918099968f808c8aa0b4e6c3a0aadaad8a8cc8ffcbdaeef5ffffff9bc1fffffffaffe6fdfff8ffdb0043012b2d2d3c353c76414176f8a58ca5f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8ffc00011080000000003012200021101031101ffc4001f0000010501010101010100000000000000000102030405060708090a0bffc400b5100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9faffc4001f0100030101010101010101010000000000000102030405060708090a0bffc400b51100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00', 'hex')
- const JPEG_FOOTER = Buffer.from('ffd9', 'hex')
- const TG_JOIN_RE = new RegExp('tg:\\/\\/(join)\\?invite=')
- const VALID_USERNAME_RE = new RegExp('^([a-z]((?!__)[\\w\\d]){3,30}[a-z\\d]|gif|vid|' +
- 'pic|bing|wiki|imdb|bold|vote|like|coub)$')
- function _raiseCastFail(entity, target) {
- throw new Error(`Cannot cast ${entity.className} to any kind of ${target}`)
- }
- /**
- Gets the input peer for the given "entity" (user, chat or channel).
- A ``TypeError`` is raised if the given entity isn't a supported type
- or if ``check_hash is True`` but the entity's ``accessHash is None``
- *or* the entity contains ``min`` information. In this case, the hash
- cannot be used for general purposes, and thus is not returned to avoid
- any issues which can derive from invalid access hashes.
- Note that ``check_hash`` **is ignored** if an input peer is already
- passed since in that case we assume the user knows what they're doing.
- This is key to getting entities by explicitly passing ``hash = 0``.
- * @param entity
- * @param allowSelf
- * @param checkHash
- */
- function getInputPeer(entity, allowSelf = true, checkHash = true) {
- if (entity.SUBCLASS_OF_ID === undefined) {
- // e.g. custom.Dialog (can't cyclic import).
- if (allowSelf && 'inputEntity' in entity) {
- return entity.inputEntity
- } else if ('entity' in entity) {
- return getInputPeer(entity.entity)
- } else {
- _raiseCastFail(entity, 'InputPeer')
- }
- }
- if (entity.SUBCLASS_OF_ID === 0xc91c90b6) { // crc32(b'InputPeer')
- return entity
- }
- if (entity instanceof constructors.User) {
- if (entity.isSelf && allowSelf) {
- return new constructors.InputPeerSelf()
- } else if ((entity.accessHash !== undefined && !entity.min) || !checkHash) {
- return new constructors.InputPeerUser({
- userId: entity.id,
- accessHash: entity.accessHash,
- })
- } else {
- throw new Error('User without accessHash or min info cannot be input')
- }
- }
- if (entity instanceof constructors.Chat || entity instanceof constructors.ChatEmpty ||
- entity instanceof constructors.ChatForbidden) {
- return new constructors.InputPeerChat({ chatId: entity.id })
- }
- if (entity instanceof constructors.Channel) {
- if ((entity.accessHash !== undefined && !entity.min) || !checkHash) {
- return new constructors.InputPeerChannel({
- channelId: entity.id,
- accessHash: entity.accessHash,
- })
- } else {
- throw new TypeError('Channel without accessHash or min info cannot be input')
- }
- }
- if (entity instanceof constructors.ChannelForbidden) {
- // "channelForbidden are never min", and since their hash is
- // also not optional, we assume that this truly is the case.
- return new constructors.InputPeerChannel({
- channelId: entity.id,
- accessHash: entity.accessHash,
- })
- }
- if (entity instanceof constructors.InputUser) {
- return new constructors.InputPeerUser({
- userId: entity.userId,
- accessHash: entity.accessHash,
- })
- }
- if (entity instanceof constructors.InputChannel) {
- return new constructors.InputPeerChannel({
- channelId: entity.channelId,
- accessHash: entity.accessHash,
- })
- }
- if (entity instanceof constructors.UserEmpty) {
- return new constructors.InputPeerEmpty()
- }
- if (entity instanceof constructors.UserFull) {
- return getInputPeer(entity.user)
- }
- if (entity instanceof constructors.ChatFull) {
- return new constructors.InputPeerChat({ chatId: entity.id })
- }
- if (entity instanceof constructors.PeerChat) {
- return new constructors.InputPeerChat(entity.chatId)
- }
- _raiseCastFail(entity, 'InputPeer')
- }
- /**
- Similar to :meth:`get_input_peer`, but for :tl:`InputChannel`'s alone.
- .. important::
- This method does not validate for invalid general-purpose access
- hashes, unlike `get_input_peer`. Consider using instead:
- ``get_input_channel(get_input_peer(channel))``.
- * @param entity
- * @returns {InputChannel|*}
- */
- /*CONTEST
- function getInputChannel(entity) {
- if (entity.SUBCLASS_OF_ID === undefined) {
- _raiseCastFail(entity, 'InputChannel')
- }
- if (entity.SUBCLASS_OF_ID === 0x40f202fd) { // crc32(b'InputChannel')
- return entity
- }
- if (entity instanceof constructors.Channel || entity instanceof constructors.ChannelForbidden) {
- return new constructors.InputChannel({
- channelId: entity.id,
- accessHash: entity.accessHash || 0
- })
- }
- if (entity instanceof constructors.InputPeerChannel) {
- return new constructors.InputChannel({
- channelId: entity.channelId,
- accessHash: entity.accessHash
- })
- }
- _raiseCastFail(entity, 'InputChannel')
- }
- */
- /**
- Similar to :meth:`get_input_peer`, but for :tl:`InputUser`'s alone.
- .. important::
- This method does not validate for invalid general-purpose access
- hashes, unlike `get_input_peer`. Consider using instead:
- ``get_input_channel(get_input_peer(channel))``.
- * @param entity
- */
- /*CONTEST
- function getInputUser(entity) {
- if (entity.SUBCLASS_OF_ID === undefined) {
- _raiseCastFail(entity, 'InputUser')
- }
- if (entity.SUBCLASS_OF_ID === 0xe669bf46) { // crc32(b'InputUser')
- return entity
- }
- if (entity instanceof constructors.User) {
- if (entity.isSelf) {
- return new constructors.InputPeerSelf()
- } else {
- return new constructors.InputUser({
- userId: entity.id,
- accessHash: entity.accessHash || 0,
- })
- }
- }
- if (entity instanceof constructors.InputPeerSelf) {
- return new constructors.InputPeerSelf()
- }
- if (entity instanceof constructors.UserEmpty || entity instanceof constructors.InputPeerEmpty) {
- return new constructors.InputUserEmpty()
- }
- if (entity instanceof constructors.UserFull) {
- return getInputUser(entity.user)
- }
- if (entity instanceof constructors.InputPeerUser) {
- return new constructors.InputUser({
- userId: entity.userId,
- accessHash: entity.accessHash
- })
- }
- _raiseCastFail(entity, 'InputUser')
- }
- */
- /**
- Similar to :meth:`get_input_peer`, but for dialogs
- * @param dialog
- */
- /*CONTEST
- function getInputDialog(dialog) {
- try {
- if (dialog.SUBCLASS_OF_ID === 0xa21c9795) { // crc32(b'InputDialogPeer')
- return dialog
- }
- if (dialog.SUBCLASS_OF_ID === 0xc91c90b6) { // crc32(b'InputPeer')
- return new constructors.InputDialogPeer({ peer: dialog })
- }
- } catch (e) {
- _raiseCastFail(dialog, 'InputDialogPeer')
- }
- try {
- return new constructors.InputDialogPeer(getInputPeer(dialog))
- // eslint-disable-next-line no-empty
- } catch (e) {
- }
- _raiseCastFail(dialog, 'InputDialogPeer')
- }
- */
- /*CONTEST
- function getInputMessage(message) {
- try {
- if (typeof message == 'number') { // This case is really common too
- return new constructors.InputMessageID({
- id: message,
- })
- } else if (message.SUBCLASS_OF_ID === 0x54b6bcc5) { // crc32(b'InputMessage')
- return message
- } else if (message.SUBCLASS_OF_ID === 0x790009e3) { // crc32(b'Message')
- return new constructors.InputMessageID(message.id)
- }
- // eslint-disable-next-line no-empty
- } catch (e) {
- }
- _raiseCastFail(message, 'InputMessage')
- }
- */
- /**
- * Adds the JPG header and footer to a stripped image.
- * Ported from https://github.com/telegramdesktop/tdesktop/blob/bec39d89e19670eb436dc794a8f20b657cb87c71/Telegram/SourceFiles/ui/image/image.cpp#L225
- * @param stripped{Buffer}
- * @returns {Buffer}
- */
- function strippedPhotoToJpg(stripped) {
- // Note: Changes here should update _stripped_real_length
- if (stripped.length < 3 || stripped[0] !== 1) {
- return stripped
- }
- const header = Buffer.from(JPEG_HEADER)
- header[164] = stripped[1]
- header[166] = stripped[2]
- return Buffer.concat([header, stripped.slice(3), JPEG_FOOTER])
- }
- /*CONTEST
- function getInputLocation(location) {
- try {
- if (!location.SUBCLASS_OF_ID) {
- throw new Error()
- }
- if (location.SUBCLASS_OF_ID === 0x1523d462) {
- return {
- dcId: null,
- inputLocation: location
- }
- }
- } catch (e) {
- _raiseCastFail(location, 'InputFileLocation')
- }
- if (location instanceof constructors.Message) {
- location = location.media
- }
- if (location instanceof constructors.MessageMediaDocument) {
- location = location.document
- } else if (location instanceof constructors.MessageMediaPhoto) {
- location = location.photo
- }
- if (location instanceof constructors.Document) {
- return {
- dcId: location.dcId,
- inputLocation: new constructors.InputDocumentFileLocation({
- id: location.id,
- accessHash: location.accessHash,
- fileReference: location.fileReference,
- thumbSize: '', // Presumably to download one of its thumbnails
- }),
- }
- } else if (location instanceof constructors.Photo) {
- return {
- dcId: location.dcId,
- inputLocation: new constructors.InputPhotoFileLocation({
- id: location.id,
- accessHash: location.accessHash,
- fileReference: location.fileReference,
- thumbSize: location.sizes[location.sizes.length - 1].type,
- }),
- }
- }
- if (location instanceof constructors.FileLocationToBeDeprecated) {
- throw new Error('Unavailable location cannot be used as input')
- }
- _raiseCastFail(location, 'InputFileLocation')
- }
- */
- /**
- * Gets the appropriated part size when uploading or downloading files,
- * given an initial file size.
- * @param fileSize
- * @returns {Number}
- */
- function getAppropriatedPartSize(fileSize) {
- if (fileSize <= 104857600) { // 100MB
- return 128
- }
- if (fileSize <= 786432000) { // 750MB
- return 256
- }
- if (fileSize <= 1572864000) { // 1500MB
- return 512
- }
- throw new Error('File size too large')
- }
- /*CONTEST
- function getPeer(peer) {
- try {
- if (typeof peer === 'number') {
- const res = resolveId(peer)
- if (res[1] === constructors.PeerChannel) {
- return new res[1]({ channelId: res[0] })
- } else if (res[1] === constructors.PeerChat) {
- return new res[1]({ chatId: res[0] })
- } else {
- return new res[1]({ userId: res[0] })
- }
- }
- if (peer.SUBCLASS_OF_ID === undefined) {
- throw new Error()
- }
- if (peer.SUBCLASS_OF_ID === 0x2d45687) {
- return peer
- } else if (peer instanceof constructors.contacts.ResolvedPeer ||
- peer instanceof constructors.InputNotifyPeer || peer instanceof constructors.TopPeer ||
- peer instanceof constructors.Dialog || peer instanceof constructors.DialogPeer) {
- return peer.peer
- } else if (peer instanceof constructors.ChannelFull) {
- return new constructors.PeerChannel({ channelId: peer.id })
- }
- if (peer.SUBCLASS_OF_ID === 0x7d7c6f86 || peer.SUBCLASS_OF_ID === 0xd9c7fc18) {
- // ChatParticipant, ChannelParticipant
- return new constructors.PeerUser({ userId: peer.userId })
- }
- peer = getInputPeer(peer, false, false)
- if (peer instanceof constructors.InputPeerUser) {
- return new constructors.PeerUser({ userId: peer.userId })
- } else if (peer instanceof constructors.InputPeerChat) {
- return new constructors.PeerChat({ chatId: peer.chatId })
- } else if (peer instanceof constructors.InputPeerChannel) {
- return new constructors.PeerChannel({ channelId: peer.channelId })
- }
- // eslint-disable-next-line no-empty
- } catch (e) {
- console.log(e)
- }
- _raiseCastFail(peer, 'peer')
- }
- */
- /**
- Convert the given peer into its marked ID by default.
- This "mark" comes from the "bot api" format, and with it the peer type
- can be identified back. User ID is left unmodified, chat ID is negated,
- and channel ID is prefixed with -100:
- * ``userId``
- * ``-chatId``
- * ``-100channel_id``
- The original ID and the peer type class can be returned with
- a call to :meth:`resolve_id(marked_id)`.
- * @param peer
- * @param addMark
- */
- /*CONTEST
- function getPeerId(peer, addMark = true) {
- // First we assert it's a Peer TLObject, or early return for integers
- if (typeof peer == 'number') {
- return addMark ? peer : resolveId(peer)[0]
- }
- // Tell the user to use their client to resolve InputPeerSelf if we got one
- if (peer instanceof constructors.InputPeerSelf) {
- _raiseCastFail(peer, 'int (you might want to use client.get_peer_id)')
- }
- try {
- peer = getPeer(peer)
- } catch (e) {
- _raiseCastFail(peer, 'int')
- }
- if (peer instanceof constructors.PeerUser) {
- return peer.userId
- } else if (peer instanceof constructors.PeerChat) {
- // Check in case the user mixed things up to avoid blowing up
- if (!(0 < peer.chatId <= 0x7fffffff)) {
- peer.chatId = resolveId(peer.chatId)[0]
- }
- return addMark ? -(peer.chatId) : peer.chatId
- } else { // if (peer instanceof constructors.PeerChannel)
- // Check in case the user mixed things up to avoid blowing up
- if (!(0 < peer.channelId <= 0x7fffffff)) {
- peer.channelId = resolveId(peer.channelId)[0]
- }
- if (!addMark) {
- return peer.channelId
- }
- // Concat -100 through math tricks, .to_supergroup() on
- // Madeline IDs will be strictly positive -> log works.
- try {
- return -(peer.channelId + Math.pow(10, Math.floor(Math.log10(peer.channelId) + 3)))
- } catch (e) {
- throw new Error('Cannot get marked ID of a channel unless its ID is strictly positive')
- }
- }
- }
- */
- /**
- * Given a marked ID, returns the original ID and its :tl:`Peer` type.
- * @param markedId
- */
- /*CONTEST
- function resolveId(markedId) {
- if (markedId >= 0) {
- return [markedId, constructors.PeerUser]
- }
- // There have been report of chat IDs being 10000xyz, which means their
- // marked version is -10000xyz, which in turn looks like a channel but
- // it becomes 00xyz (= xyz). Hence, we must assert that there are only
- // two zeroes.
- const m = markedId.toString()
- .match(/-100([^0]\d*)/)
- if (m) {
- return [parseInt(m[1]), constructors.PeerChannel]
- }
- return [-markedId, constructors.PeerChat]
- }
- */
- /**
- * returns an entity pair
- * @param entityId
- * @param entities
- * @param cache
- * @param getInputPeer
- * @returns {{inputEntity: *, entity: *}}
- * @private
- */
- /*CONTEST
- function _getEntityPair(entityId, entities, cache, getInputPeer = getInputPeer) {
- const entity = entities.get(entityId)
- let inputEntity = cache[entityId]
- if (inputEntity === undefined) {
- try {
- inputEntity = getInputPeer(inputEntity)
- } catch (e) {
- inputEntity = null
- }
- }
- return {
- entity,
- inputEntity
- }
- }
- */
- function getMessageId(message) {
- if (message === null || message === undefined) {
- return null
- }
- if (typeof message == 'number') {
- return message
- }
- if (message.SUBCLASS_OF_ID === 0x790009e3) { // crc32(b'Message')
- return message.id
- }
- throw new Error(`Invalid message type: ${message.constructor.name}`)
- }
- /**
- * Parses the given phone, or returns `None` if it's invalid.
- * @param phone
- */
- function parsePhone(phone) {
- return phone.toString().replace(/[+()\s-]/gm, '')
- }
- /**
- Parses the given username or channel access hash, given
- a string, username or URL. Returns a tuple consisting of
- both the stripped, lowercase username and whether it is
- a joinchat/ hash (in which case is not lowercase'd).
- Returns ``(None, False)`` if the ``username`` or link is not valid.
- * @param username {string}
- */
- /*CONTEST
- function parseUsername(username) {
- username = username.trim()
- const m = username.match(USERNAME_RE) || username.match(TG_JOIN_RE)
- if (m) {
- username = username.replace(m[0], '')
- if (m[1]) {
- return {
- username: username,
- isInvite: true
- }
- } else {
- username = rtrim(username, '/')
- }
- }
- if (username.match(VALID_USERNAME_RE)) {
- return {
- username: username.toLowerCase(),
- isInvite: false
- }
- } else {
- return {
- username: null,
- isInvite: false
- }
- }
- }
- function rtrim(s, mask) {
- while (~mask.indexOf(s[s.length - 1])) {
- s = s.slice(0, -1)
- }
- return s
- }
- */
- /**
- * Gets the display name for the given :tl:`User`,
- :tl:`Chat` or :tl:`Channel`. Returns an empty string otherwise
- * @param entity
- */
- function getDisplayName(entity) {
- if (entity instanceof constructors.User) {
- if (entity.lastName && entity.firstName) {
- return `${entity.firstName} ${entity.lastName}`
- } else if (entity.firstName) {
- return entity.firstName
- } else if (entity.lastName) {
- return entity.lastName
- } else {
- return ''
- }
- } else if (entity instanceof constructors.Chat || entity instanceof constructors.Channel) {
- return entity.title
- }
- return ''
- }
- /**
- * check if a given item is an array like or not
- * @param item
- * @returns {boolean}
- */
- /*CONTEST
- Duplicate ?
- function isListLike(item) {
- return (
- Array.isArray(item) ||
- (!!item &&
- typeof item === 'object' &&
- typeof (item.length) === 'number' &&
- (item.length === 0 ||
- (item.length > 0 &&
- (item.length - 1) in item)
- )
- )
- )
- }
- */
- async function getDC(dcId,client, cdn = false) {
- if (!IS_NODE){
- switch (dcId) {
- case 1:
- return {
- id: 1,
- ipAddress: IS_NODE?'':'pluto.web.telegram.org',
- port: 443,
- }
- case 2:
- return {
- id: 2,
- ipAddress: 'venus.web.telegram.org',
- port: 443,
- }
- case 3:
- return {
- id: 3,
- ipAddress: 'aurora.web.telegram.org',
- port: 443,
- }
- case 4:
- return {
- id: 4,
- ipAddress: 'vesta.web.telegram.org',
- port: 443,
- }
- case 5:
- return {
- id: 5,
- ipAddress: 'flora.web.telegram.org',
- port: 443,
- }
- default:
- throw new Error(`Cannot find the DC with the ID of ${dcId}`)
- }
- }
- if (!client._config) {
- client._config = await client.invoke(new requests.help.GetConfig())
- }
- if (cdn) {
- throw new Error('CDNs are Not supported')
- }
- for (const DC of client._config.dcOptions) {
- if (DC.id === dcId && Boolean(DC.ipv6) === client._useIPV6 && Boolean(DC.cdn) === cdn) {
- return {
- id:DC.id,
- ipAddress:DC.ipAddress,
- port:443,
- }
- }
- }
- }
- module.exports = {
- getMessageId,
- //_getEntityPair,
- //getInputMessage,
- //getInputDialog,
- //getInputUser,
- //getInputChannel,
- getInputPeer,
- //parsePhone,
- //parseUsername,
- //getPeer,
- //getPeerId,
- getDisplayName,
- //resolveId,
- //isListLike,
- getAppropriatedPartSize,
- //getInputLocation,
- strippedPhotoToJpg,
- getDC,
- }
|