[Refactoring] Remove big-integer library (#6294)

This commit is contained in:
zubiden 2025-10-11 19:07:50 +02:00 committed by Alexander Zinchuk
parent 8ab7da8c8a
commit 1be16bf765
78 changed files with 935 additions and 593 deletions

10
package-lock.json generated
View File

@ -16,7 +16,7 @@
"@tauri-apps/plugin-shell": "^2.3.1", "@tauri-apps/plugin-shell": "^2.3.1",
"@tauri-apps/plugin-updater": "^2.9.0", "@tauri-apps/plugin-updater": "^2.9.0",
"async-mutex": "^0.5.0", "async-mutex": "^0.5.0",
"big-integer": "github:painor/BigInteger.js", "browserslist-config-baseline": "^0.5.0",
"emoji-data-ios": "git+https://github.com/korenskoy/emoji-data-ios#443f1c9d7b16a82e7ee53f7f226d7d9a9920a105", "emoji-data-ios": "git+https://github.com/korenskoy/emoji-data-ios#443f1c9d7b16a82e7ee53f7f226d7d9a9920a105",
"idb-keyval": "^6.2.2", "idb-keyval": "^6.2.2",
"lowlight": "^3.3.0", "lowlight": "^3.3.0",
@ -7330,14 +7330,6 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/big-integer": {
"version": "1.7.0",
"resolved": "git+ssh://git@github.com/painor/BigInteger.js.git#4e2a4f1c1e4bd1d7c5d65e08aab51ac9ec939601",
"license": "Unlicense",
"engines": {
"node": ">=0.6"
}
},
"node_modules/binary-extensions": { "node_modules/binary-extensions": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",

View File

@ -132,7 +132,7 @@
"@tauri-apps/plugin-shell": "^2.3.1", "@tauri-apps/plugin-shell": "^2.3.1",
"@tauri-apps/plugin-updater": "^2.9.0", "@tauri-apps/plugin-updater": "^2.9.0",
"async-mutex": "^0.5.0", "async-mutex": "^0.5.0",
"big-integer": "github:painor/BigInteger.js", "browserslist-config-baseline": "^0.5.0",
"emoji-data-ios": "git+https://github.com/korenskoy/emoji-data-ios#443f1c9d7b16a82e7ee53f7f226d7d9a9920a105", "emoji-data-ios": "git+https://github.com/korenskoy/emoji-data-ios#443f1c9d7b16a82e7ee53f7f226d7d9a9920a105",
"idb-keyval": "^6.2.2", "idb-keyval": "^6.2.2",
"lowlight": "^3.3.0", "lowlight": "^3.3.0",

View File

@ -1,4 +1,3 @@
import BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import type { ApiAppConfig, ApiLimitType, ApiPremiumSection } from '../../types'; import type { ApiAppConfig, ApiLimitType, ApiPremiumSection } from '../../types';
@ -131,7 +130,7 @@ function buildEmojiSounds(appConfig: GramJsAppConfig) {
dcId: 1, dcId: 1,
mimeType: 'audio/ogg', mimeType: 'audio/ogg',
fileReference: Buffer.alloc(0), fileReference: Buffer.alloc(0),
size: BigInt(0), size: 0n,
} as GramJs.Document); } as GramJs.Document);
acc[key] = l.id; acc[key] = l.id;

View File

@ -24,6 +24,7 @@ import type {
import { numberToHexColor } from '../../../util/colors'; import { numberToHexColor } from '../../../util/colors';
import { pick } from '../../../util/iteratees'; import { pick } from '../../../util/iteratees';
import { toJSNumber } from '../../../util/numbers';
import { addDocumentToLocalDb } from '../helpers/localDb'; import { addDocumentToLocalDb } from '../helpers/localDb';
import { serializeBytes } from '../helpers/misc'; import { serializeBytes } from '../helpers/misc';
import { buildApiMessageEntity, buildApiPhoto } from './common'; import { buildApiMessageEntity, buildApiPhoto } from './common';
@ -252,7 +253,7 @@ export function buildBotInlineMessage(
description: sendMessage.description, description: sendMessage.description,
photo: buildApiWebDocument(sendMessage.photo), photo: buildApiWebDocument(sendMessage.photo),
currency: sendMessage.currency, currency: sendMessage.currency,
amount: sendMessage.totalAmount.toJSNumber(), amount: toJSNumber(sendMessage.totalAmount),
}; };
} }

View File

@ -24,6 +24,7 @@ import type {
} from '../../types'; } from '../../types';
import { pickTruthy } from '../../../util/iteratees'; import { pickTruthy } from '../../../util/iteratees';
import { toJSNumber } from '../../../util/numbers';
import { getServerTimeOffset } from '../../../util/serverTime'; import { getServerTimeOffset } from '../../../util/serverTime';
import { addPhotoToLocalDb, addUserToLocalDb } from '../helpers/localDb'; import { addPhotoToLocalDb, addUserToLocalDb } from '../helpers/localDb';
import { serializeBytes } from '../helpers/misc'; import { serializeBytes } from '../helpers/misc';
@ -113,7 +114,8 @@ function buildApiChatFieldsFromPeerEntity(
isJoinRequest: channel?.joinRequest, isJoinRequest: channel?.joinRequest,
isForum: channel?.forum, isForum: channel?.forum,
isMonoforum: channel?.monoforum, isMonoforum: channel?.monoforum,
linkedMonoforumId: channel?.linkedMonoforumId && buildApiPeerId(channel.linkedMonoforumId, 'channel'), linkedMonoforumId: channel?.linkedMonoforumId !== undefined
? buildApiPeerId(channel.linkedMonoforumId, 'channel') : undefined,
areChannelMessagesAllowed: channel?.broadcastMessagesAllowed, areChannelMessagesAllowed: channel?.broadcastMessagesAllowed,
areStoriesHidden, areStoriesHidden,
maxStoryId, maxStoryId,
@ -123,7 +125,7 @@ function buildApiChatFieldsFromPeerEntity(
botVerificationIconId, botVerificationIconId,
hasGeo: channel?.hasGeo, hasGeo: channel?.hasGeo,
subscriptionUntil: channel?.subscriptionUntilDate, subscriptionUntil: channel?.subscriptionUntilDate,
paidMessagesStars: paidMessagesStars?.toJSNumber(), paidMessagesStars: toJSNumber(paidMessagesStars),
level: channel?.level, level: channel?.level,
hasAutoTranslation: channel?.autotranslation, hasAutoTranslation: channel?.autotranslation,
withForumTabs: channel?.forumTabs, withForumTabs: channel?.forumTabs,
@ -713,7 +715,7 @@ export function buildApiStarsSubscriptionPricing(
): ApiStarsSubscriptionPricing { ): ApiStarsSubscriptionPricing {
return { return {
period: pricing.period, period: pricing.period,
amount: pricing.amount.toJSNumber(), amount: toJSNumber(pricing.amount),
}; };
} }

View File

@ -1,4 +1,3 @@
import bigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import type { import type {
@ -14,6 +13,7 @@ import type {
} from '../../types'; } from '../../types';
import { numberToHexColor } from '../../../util/colors'; import { numberToHexColor } from '../../../util/colors';
import { toJSNumber } from '../../../util/numbers';
import { buildApiChatFromPreview } from '../apiBuilders/chats'; import { buildApiChatFromPreview } from '../apiBuilders/chats';
import { addDocumentToLocalDb } from '../helpers/localDb'; import { addDocumentToLocalDb } from '../helpers/localDb';
import { buildApiFormattedText } from './common'; import { buildApiFormattedText } from './common';
@ -47,7 +47,7 @@ export function buildApiStarGift(starGift: GramJs.TypeStarGift): ApiStarGift {
requirePremium, requirePremium,
resaleTonOnly, resaleTonOnly,
valueCurrency, valueCurrency,
valueAmount: valueAmount?.toJSNumber(), valueAmount: toJSNumber(valueAmount),
regularGiftId: giftId.toString(), regularGiftId: giftId.toString(),
}; };
} }
@ -67,19 +67,19 @@ export function buildApiStarGift(starGift: GramJs.TypeStarGift): ApiStarGift {
id: id.toString(), id: id.toString(),
isLimited: limited, isLimited: limited,
sticker, sticker,
stars: stars.toJSNumber(), stars: toJSNumber(stars),
availabilityRemains, availabilityRemains,
availabilityTotal, availabilityTotal,
starsToConvert: convertStars.toJSNumber(), starsToConvert: toJSNumber(convertStars),
firstSaleDate, firstSaleDate,
lastSaleDate, lastSaleDate,
isSoldOut: soldOut, isSoldOut: soldOut,
isBirthday: birthday, isBirthday: birthday,
upgradeStars: upgradeStars?.toJSNumber(), upgradeStars: upgradeStars !== undefined ? toJSNumber(upgradeStars) : undefined,
title, title,
resellMinStars: resellMinStars?.toJSNumber(), resellMinStars: resellMinStars !== undefined ? toJSNumber(resellMinStars) : undefined,
releasedByPeerId: releasedBy && getApiChatIdFromMtpPeer(releasedBy), releasedByPeerId: releasedBy && getApiChatIdFromMtpPeer(releasedBy),
availabilityResale: availabilityResale?.toJSNumber(), availabilityResale: availabilityResale !== undefined ? toJSNumber(availabilityResale) : undefined,
requirePremium, requirePremium,
limitedPerUser, limitedPerUser,
perUserTotal, perUserTotal,
@ -168,15 +168,15 @@ export function buildApiSavedStarGift(userStarGift: GramJs.SavedStarGift, peerId
return { return {
gift: buildApiStarGift(gift), gift: buildApiStarGift(gift),
date, date,
starsToConvert: convertStars?.toJSNumber(), starsToConvert: toJSNumber(convertStars),
fromId: fromId && getApiChatIdFromMtpPeer(fromId), fromId: fromId && getApiChatIdFromMtpPeer(fromId),
message: message && buildApiFormattedText(message), message: message && buildApiFormattedText(message),
messageId: msgId, messageId: msgId,
isNameHidden: nameHidden, isNameHidden: nameHidden,
isUnsaved: unsaved, isUnsaved: unsaved,
canUpgrade, canUpgrade,
alreadyPaidUpgradeStars: upgradeStars?.toJSNumber(), alreadyPaidUpgradeStars: toJSNumber(upgradeStars),
transferStars: transferStars?.toJSNumber(), transferStars: toJSNumber(transferStars),
inputGift, inputGift,
savedId: savedId?.toString(), savedId: savedId?.toString(),
canExportAt, canExportAt,
@ -279,16 +279,19 @@ GramJs.TypeStarGiftAttributeId[] {
return attributes.map((attr) => { return attributes.map((attr) => {
switch (attr.type) { switch (attr.type) {
case 'model': case 'model':
return new GramJs.StarGiftAttributeIdModel({ documentId: bigInt(attr.documentId) }); return new GramJs.StarGiftAttributeIdModel({ documentId: BigInt(attr.documentId) });
case 'pattern': case 'pattern':
return new GramJs.StarGiftAttributeIdPattern({ documentId: bigInt(attr.documentId) }); return new GramJs.StarGiftAttributeIdPattern({ documentId: BigInt(attr.documentId) });
case 'backdrop': case 'backdrop':
return new GramJs.StarGiftAttributeIdBackdrop({ backdropId: attr.backdropId }); return new GramJs.StarGiftAttributeIdBackdrop({ backdropId: attr.backdropId });
default: default: {
throw new Error(`Unknown attribute type: ${(attr as any).type}`); // Exhaustive check
const _exhaustive: never = attr;
return _exhaustive;
}
} }
}); });
} }

View File

@ -3,6 +3,7 @@ import { Api as GramJs } from '../../../lib/gramjs';
import type { ApiPhoneCallDiscardReason } from '../../types'; import type { ApiPhoneCallDiscardReason } from '../../types';
import type { ApiMessageAction } from '../../types/messageActions'; import type { ApiMessageAction } from '../../types/messageActions';
import { toJSNumber } from '../../../util/numbers';
import { buildApiBotApp } from './bots'; import { buildApiBotApp } from './bots';
import { buildApiFormattedText, buildApiPhoto } from './common'; import { buildApiFormattedText, buildApiPhoto } from './common';
import { buildApiStarGift } from './gifts'; import { buildApiStarGift } from './gifts';
@ -128,7 +129,7 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
isRecurringInit: recurringInit, isRecurringInit: recurringInit,
isRecurringUsed: recurringUsed, isRecurringUsed: recurringUsed,
currency, currency,
totalAmount: totalAmount.toJSNumber(), totalAmount: toJSNumber(totalAmount),
invoiceSlug, invoiceSlug,
subscriptionUntilDate, subscriptionUntilDate,
}; };
@ -254,10 +255,10 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
mediaType: 'action', mediaType: 'action',
type: 'giftPremium', type: 'giftPremium',
currency, currency,
amount: amount.toJSNumber(), amount: toJSNumber(amount),
months, months,
cryptoCurrency, cryptoCurrency,
cryptoAmount: cryptoAmount?.toJSNumber(), cryptoAmount: toJSNumber(cryptoAmount),
message: message && buildApiFormattedText(message), message: message && buildApiFormattedText(message),
}; };
} }
@ -308,9 +309,9 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
months, months,
slug, slug,
currency, currency,
amount: amount?.toJSNumber(), amount: toJSNumber(amount),
cryptoCurrency, cryptoCurrency,
cryptoAmount: cryptoAmount?.toJSNumber(), cryptoAmount: toJSNumber(cryptoAmount),
message: message && buildApiFormattedText(message), message: message && buildApiFormattedText(message),
}; };
} }
@ -319,7 +320,7 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
return { return {
mediaType: 'action', mediaType: 'action',
type: 'giveawayLaunch', type: 'giveawayLaunch',
stars: stars?.toJSNumber(), stars: toJSNumber(stars),
}; };
} }
if (action instanceof GramJs.MessageActionGiveawayResults) { if (action instanceof GramJs.MessageActionGiveawayResults) {
@ -341,7 +342,7 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
type: 'paymentRefunded', type: 'paymentRefunded',
peerId: getApiChatIdFromMtpPeer(peer), peerId: getApiChatIdFromMtpPeer(peer),
currency, currency,
totalAmount: totalAmount.toJSNumber(), totalAmount: toJSNumber(totalAmount),
}; };
} }
if (action instanceof GramJs.MessageActionGiftStars) { if (action instanceof GramJs.MessageActionGiftStars) {
@ -352,10 +353,10 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
mediaType: 'action', mediaType: 'action',
type: 'giftStars', type: 'giftStars',
currency, currency,
amount: amount.toJSNumber(), amount: toJSNumber(amount),
stars: stars.toJSNumber(), stars: toJSNumber(stars),
cryptoCurrency, cryptoCurrency,
cryptoAmount: cryptoAmount?.toJSNumber(), cryptoAmount: toJSNumber(cryptoAmount),
transactionId, transactionId,
}; };
} }
@ -367,9 +368,9 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
mediaType: 'action', mediaType: 'action',
type: 'giftTon', type: 'giftTon',
currency, currency,
amount: amount.toJSNumber(), amount: toJSNumber(amount),
cryptoCurrency, cryptoCurrency,
cryptoAmount: cryptoAmount.toJSNumber(), cryptoAmount: toJSNumber(cryptoAmount),
transactionId, transactionId,
}; };
} }
@ -381,7 +382,7 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
mediaType: 'action', mediaType: 'action',
type: 'prizeStars', type: 'prizeStars',
isUnclaimed: unclaimed, isUnclaimed: unclaimed,
stars: stars.toJSNumber(), stars: toJSNumber(stars),
transactionId, transactionId,
boostPeerId: getApiChatIdFromMtpPeer(boostPeer), boostPeerId: getApiChatIdFromMtpPeer(boostPeer),
giveawayMsgId, giveawayMsgId,
@ -407,12 +408,12 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
canUpgrade, canUpgrade,
gift: starGift, gift: starGift,
message: message && buildApiFormattedText(message), message: message && buildApiFormattedText(message),
starsToConvert: convertStars?.toJSNumber(), starsToConvert: toJSNumber(convertStars),
upgradeMsgId, upgradeMsgId,
alreadyPaidUpgradeStars: upgradeStars?.toJSNumber(), alreadyPaidUpgradeStars: toJSNumber(upgradeStars),
fromId: fromId && getApiChatIdFromMtpPeer(fromId), fromId: fromId && getApiChatIdFromMtpPeer(fromId),
peerId: peer && getApiChatIdFromMtpPeer(peer), peerId: peer && getApiChatIdFromMtpPeer(peer),
savedId: savedId && buildApiPeerId(savedId, 'user'), savedId: savedId !== undefined ? buildApiPeerId(savedId, 'user') : undefined,
}; };
} }
if (action instanceof GramJs.MessageActionStarGiftUnique) { if (action instanceof GramJs.MessageActionStarGiftUnique) {
@ -433,10 +434,10 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
isRefunded: refunded, isRefunded: refunded,
gift: starGift, gift: starGift,
canExportAt, canExportAt,
transferStars: transferStars?.toJSNumber(), transferStars: toJSNumber(transferStars),
fromId: fromId && getApiChatIdFromMtpPeer(fromId), fromId: fromId && getApiChatIdFromMtpPeer(fromId),
peerId: peer && getApiChatIdFromMtpPeer(peer), peerId: peer && getApiChatIdFromMtpPeer(peer),
savedId: savedId && buildApiPeerId(savedId, 'user'), savedId: savedId !== undefined ? buildApiPeerId(savedId, 'user') : undefined,
resaleAmount: resaleAmount ? buildApiCurrencyAmount(resaleAmount) : undefined, resaleAmount: resaleAmount ? buildApiCurrencyAmount(resaleAmount) : undefined,
}; };
} }
@ -448,7 +449,7 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
mediaType: 'action', mediaType: 'action',
type: 'paidMessagesPrice', type: 'paidMessagesPrice',
isAllowedInChannel: broadcastMessagesAllowed, isAllowedInChannel: broadcastMessagesAllowed,
stars: stars.toJSNumber(), stars: toJSNumber(stars),
}; };
} }
if (action instanceof GramJs.MessageActionPaidMessagesRefunded) { if (action instanceof GramJs.MessageActionPaidMessagesRefunded) {
@ -458,7 +459,7 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
return { return {
mediaType: 'action', mediaType: 'action',
type: 'paidMessagesRefunded', type: 'paidMessagesRefunded',
stars: stars.toJSNumber(), stars: toJSNumber(stars),
count, count,
}; };
} }

View File

@ -35,6 +35,7 @@ import { SUPPORTED_PHOTO_CONTENT_TYPES, SUPPORTED_VIDEO_CONTENT_TYPES, VIDEO_WEB
import { addTimestampEntities } from '../../../util/dates/timestamp'; import { addTimestampEntities } from '../../../util/dates/timestamp';
import { generateWaveform } from '../../../util/generateWaveform'; import { generateWaveform } from '../../../util/generateWaveform';
import { pick } from '../../../util/iteratees'; import { pick } from '../../../util/iteratees';
import { toJSNumber } from '../../../util/numbers';
import { import {
addMediaToLocalDb, addStoryToLocalDb, addWebPageMediaToLocalDb, type MediaRepairContext, addMediaToLocalDb, addStoryToLocalDb, addWebPageMediaToLocalDb, type MediaRepairContext,
} from '../helpers/localDb'; } from '../helpers/localDb';
@ -260,7 +261,7 @@ export function buildVideoFromDocument(document: GramJs.Document, params?: {
isRound, isRound,
isGif: Boolean(gifAttr), isGif: Boolean(gifAttr),
thumbnail: buildApiThumbnailFromStripped(thumbs), thumbnail: buildApiThumbnailFromStripped(thumbs),
size: size.toJSNumber(), size: toJSNumber(size),
isSpoiler, isSpoiler,
timestamp, timestamp,
hasVideoPreview, hasVideoPreview,
@ -300,7 +301,7 @@ export function buildAudioFromDocument(document: GramJs.Document): ApiAudio | un
fileName: getFilenameFromDocument(document, 'audio'), fileName: getFilenameFromDocument(document, 'audio'),
title, title,
performer, performer,
size: size.toJSNumber(), size: toJSNumber(size),
}; };
} }
@ -359,7 +360,7 @@ function buildAudio(media: GramJs.TypeMessageMedia): ApiAudio | undefined {
id: String(media.document.id), id: String(media.document.id),
fileName: getFilenameFromDocument(media.document, 'audio'), fileName: getFilenameFromDocument(media.document, 'audio'),
thumbnailSizes, thumbnailSizes,
size: media.document.size.toJSNumber(), size: toJSNumber(media.document.size),
...pick(media.document, ['mimeType']), ...pick(media.document, ['mimeType']),
...pick(audioAttribute, ['duration', 'performer', 'title']), ...pick(audioAttribute, ['duration', 'performer', 'title']),
}; };
@ -402,7 +403,7 @@ function buildVoice(media: GramJs.TypeMessageMedia): ApiVoice | undefined {
return { return {
mediaType: 'voice', mediaType: 'voice',
id: String(media.document.id), id: String(media.document.id),
size: media.document.size.toJSNumber(), size: toJSNumber(media.document.size),
duration, duration,
waveform: waveform ? Array.from(waveform) : undefined, waveform: waveform ? Array.from(waveform) : undefined,
}; };
@ -475,7 +476,7 @@ export function buildApiDocument(document: GramJs.TypeDocument): ApiDocument | u
return { return {
mediaType: 'document', mediaType: 'document',
id: String(id), id: String(id),
size: size.toJSNumber(), size: toJSNumber(size),
mimeType, mimeType,
timestamp: date, timestamp: date,
fileName: getFilenameFromDocument(document), fileName: getFilenameFromDocument(document),
@ -644,7 +645,7 @@ function buildGiveaway(media: GramJs.MessageMediaGiveaway): ApiGiveaway | undefi
mediaType: 'giveaway', mediaType: 'giveaway',
channelIds, channelIds,
months, months,
stars: stars?.toJSNumber(), stars: toJSNumber(stars),
quantity, quantity,
untilDate, untilDate,
countries: countriesIso2, countries: countriesIso2,
@ -770,7 +771,7 @@ export function buildMediaInvoice(media: GramJs.MessageMediaInvoice): ApiMediaIn
description, description,
photo: buildApiWebDocument(photo), photo: buildApiWebDocument(photo),
receiptMessageId: receiptMsgId, receiptMessageId: receiptMsgId,
amount: totalAmount.toJSNumber(), amount: toJSNumber(totalAmount),
currency, currency,
isTest: test, isTest: test,
extendedMedia: preview, extendedMedia: preview,
@ -932,7 +933,7 @@ function buildPaidMedia(media: GramJs.TypeMessageMedia): ApiPaidMedia | undefine
if (isBought) { if (isBought) {
return { return {
mediaType: 'paidMedia', mediaType: 'paidMedia',
starsAmount: starsAmount.toJSNumber(), starsAmount: toJSNumber(starsAmount),
isBought, isBought,
extendedMedia: buildBoughtMediaContent(extendedMedia)!, extendedMedia: buildBoughtMediaContent(extendedMedia)!,
}; };
@ -940,7 +941,7 @@ function buildPaidMedia(media: GramJs.TypeMessageMedia): ApiPaidMedia | undefine
return { return {
mediaType: 'paidMedia', mediaType: 'paidMedia',
starsAmount: starsAmount.toJSNumber(), starsAmount: toJSNumber(starsAmount),
extendedMedia: extendedMedia extendedMedia: extendedMedia
.filter((paidMedia): paidMedia is GramJs.MessageExtendedMediaPreview => ( .filter((paidMedia): paidMedia is GramJs.MessageExtendedMediaPreview => (
paidMedia instanceof GramJs.MessageExtendedMediaPreview paidMedia instanceof GramJs.MessageExtendedMediaPreview

View File

@ -45,6 +45,7 @@ import {
import { getEmojiOnlyCountForMessage } from '../../../global/helpers/getEmojiOnlyCountForMessage'; import { getEmojiOnlyCountForMessage } from '../../../global/helpers/getEmojiOnlyCountForMessage';
import { addTimestampEntities } from '../../../util/dates/timestamp'; import { addTimestampEntities } from '../../../util/dates/timestamp';
import { omitUndefined, pick } from '../../../util/iteratees'; import { omitUndefined, pick } from '../../../util/iteratees';
import { toJSNumber } from '../../../util/numbers';
import { getServerTime, getServerTimeOffset } from '../../../util/serverTime'; import { getServerTime, getServerTimeOffset } from '../../../util/serverTime';
import { interpolateArray } from '../../../util/waveform'; import { interpolateArray } from '../../../util/waveform';
import { import {
@ -218,7 +219,7 @@ export function buildApiMessageWithChatId(
mtpMessage.media instanceof GramJs.MessageMediaInvoice ? mtpMessage.media.receiptMsgId : undefined, mtpMessage.media instanceof GramJs.MessageMediaInvoice ? mtpMessage.media.receiptMsgId : undefined,
) || {}; ) || {};
const { mediaUnread: isMediaUnread, postAuthor } = mtpMessage; const { mediaUnread: isMediaUnread, postAuthor } = mtpMessage;
const groupedId = mtpMessage.groupedId && String(mtpMessage.groupedId); const groupedId = mtpMessage.groupedId !== undefined ? String(mtpMessage.groupedId) : undefined;
const isInAlbum = Boolean(groupedId) && !(content.document || content.audio || content.sticker); const isInAlbum = Boolean(groupedId) && !(content.document || content.audio || content.sticker);
const shouldHideKeyboardButtons = mtpMessage.replyMarkup instanceof GramJs.ReplyKeyboardHide; const shouldHideKeyboardButtons = mtpMessage.replyMarkup instanceof GramJs.ReplyKeyboardHide;
const isHideKeyboardSelective = mtpMessage.replyMarkup instanceof GramJs.ReplyKeyboardHide const isHideKeyboardSelective = mtpMessage.replyMarkup instanceof GramJs.ReplyKeyboardHide
@ -285,7 +286,7 @@ export function buildApiMessageWithChatId(
isInvertedMedia, isInvertedMedia,
isVideoProcessingPending, isVideoProcessingPending,
reportDeliveryUntilDate: mtpMessage.reportDeliveryUntilDate, reportDeliveryUntilDate: mtpMessage.reportDeliveryUntilDate,
paidMessageStars: mtpMessage.paidMessageStars?.toJSNumber(), paidMessageStars: toJSNumber(mtpMessage.paidMessageStars),
restrictionReasons, restrictionReasons,
}; };
} }
@ -835,6 +836,6 @@ export function buildApiSearchPostsFlood(
totalDaily: searchFlood.totalDaily, totalDaily: searchFlood.totalDaily,
remains: searchFlood.remains, remains: searchFlood.remains,
waitTill: searchFlood.waitTill, waitTill: searchFlood.waitTill,
starsAmount: searchFlood.starsAmount.toJSNumber(), starsAmount: toJSNumber(searchFlood.starsAmount),
}; };
} }

View File

@ -24,6 +24,7 @@ import { numberToHexColor } from '../../../util/colors';
import { import {
buildCollectionByCallback, omit, omitUndefined, pick, buildCollectionByCallback, omit, omitUndefined, pick,
} from '../../../util/iteratees'; } from '../../../util/iteratees';
import { toJSNumber } from '../../../util/numbers';
import { addUserToLocalDb } from '../helpers/localDb'; import { addUserToLocalDb } from '../helpers/localDb';
import { omitVirtualClassFields } from './helpers'; import { omitVirtualClassFields } from './helpers';
import { buildApiDocument, buildMessageTextContent } from './messageContent'; import { buildApiDocument, buildMessageTextContent } from './messageContent';
@ -362,9 +363,9 @@ export function buildApiCollectibleInfo(info: GramJs.fragment.TypeCollectibleInf
} = info; } = info;
return { return {
amount: amount.toJSNumber(), amount: toJSNumber(amount),
currency, currency,
cryptoAmount: cryptoAmount.toJSNumber(), cryptoAmount: toJSNumber(cryptoAmount),
cryptoCurrency, cryptoCurrency,
purchaseDate, purchaseDate,
url, url,

View File

@ -1,4 +1,3 @@
import bigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import type { import type {
@ -31,6 +30,7 @@ import type {
} from '../../types'; } from '../../types';
import { STARS_CURRENCY_CODE, TON_CURRENCY_CODE } from '../../../config'; import { STARS_CURRENCY_CODE, TON_CURRENCY_CODE } from '../../../config';
import { toJSNumber } from '../../../util/numbers';
import { addWebDocumentToLocalDb } from '../helpers/localDb'; import { addWebDocumentToLocalDb } from '../helpers/localDb';
import { buildApiStarsSubscriptionPricing } from './chats'; import { buildApiStarsSubscriptionPricing } from './chats';
import { buildApiMessageEntity } from './common'; import { buildApiMessageEntity } from './common';
@ -45,15 +45,16 @@ export function buildShippingOptions(shippingOptions: GramJs.ShippingOption[] |
return undefined; return undefined;
} }
return Object.values(shippingOptions).map((option) => { return shippingOptions.map((option) => {
return { return {
id: option.id, id: option.id,
title: option.title, title: option.title,
amount: option.prices.reduce((ac, cur) => ac + cur.amount.toJSNumber(), 0),
amount: option.prices.reduce((ac, cur) => ac + toJSNumber(cur.amount), 0),
prices: option.prices.map(({ label, amount }) => { prices: option.prices.map(({ label, amount }) => {
return { return {
label, label,
amount: amount.toJSNumber(), amount: toJSNumber(amount),
}; };
}), }),
}; };
@ -79,7 +80,7 @@ export function buildApiReceipt(receipt: GramJs.payments.TypePaymentReceipt): Ap
botId: buildApiPeerId(botId, 'user'), botId: buildApiPeerId(botId, 'user'),
description, description,
title, title,
totalAmount: -totalAmount.toJSNumber(), totalAmount: -toJSNumber(totalAmount),
transactionId, transactionId,
photo: buildApiWebDocument(photo), photo: buildApiWebDocument(photo),
invoice: buildApiInvoice(invoice), invoice: buildApiInvoice(invoice),
@ -110,7 +111,7 @@ export function buildApiReceipt(receipt: GramJs.payments.TypePaymentReceipt): Ap
shippingPrices = shipping.prices.map(({ label, amount }) => { shippingPrices = shipping.prices.map(({ label, amount }) => {
return { return {
label, label,
amount: amount.toJSNumber(), amount: toJSNumber(amount),
}; };
}); });
shippingMethod = shipping.title; shippingMethod = shipping.title;
@ -119,13 +120,13 @@ export function buildApiReceipt(receipt: GramJs.payments.TypePaymentReceipt): Ap
return { return {
type: 'regular', type: 'regular',
info: { shippingAddress, phone, name }, info: { shippingAddress, phone, name },
totalAmount: totalAmount.toJSNumber(), totalAmount: toJSNumber(totalAmount),
currency, currency,
date, date,
credentialsTitle, credentialsTitle,
shippingPrices, shippingPrices,
shippingMethod, shippingMethod,
tipAmount: tipAmount ? tipAmount.toJSNumber() : 0, tipAmount: toJSNumber(tipAmount) || 0,
title, title,
description, description,
botId: buildApiPeerId(botId, 'user'), botId: buildApiPeerId(botId, 'user'),
@ -240,10 +241,10 @@ export function buildApiInvoice(invoice: GramJs.Invoice): ApiInvoice {
const mappedPrices: ApiLabeledPrice[] = prices.map(({ label, amount }) => ({ const mappedPrices: ApiLabeledPrice[] = prices.map(({ label, amount }) => ({
label, label,
amount: amount.toJSNumber(), amount: toJSNumber(amount),
})); }));
const totalAmount = prices.reduce((acc, cur) => acc.add(cur.amount), bigInt(0)).toJSNumber(); const totalAmount = prices.reduce((acc, cur) => acc + toJSNumber(cur.amount), 0);
return { return {
totalAmount, totalAmount,
@ -252,8 +253,8 @@ export function buildApiInvoice(invoice: GramJs.Invoice): ApiInvoice {
isRecurring: recurring, isRecurring: recurring,
termsUrl, termsUrl,
prices: mappedPrices, prices: mappedPrices,
maxTipAmount: maxTipAmount?.toJSNumber(), maxTipAmount: toJSNumber(maxTipAmount),
suggestedTipAmounts: suggestedTipAmounts?.map((tip) => tip.toJSNumber()), suggestedTipAmounts: suggestedTipAmounts?.map((tip) => toJSNumber(tip)),
isEmailRequested: emailRequested, isEmailRequested: emailRequested,
isEmailSentToProvider: emailToProvider, isEmailSentToProvider: emailToProvider,
isNameRequested: nameRequested, isNameRequested: nameRequested,
@ -288,7 +289,7 @@ function buildApiPremiumSubscriptionOption(option: GramJs.PremiumSubscriptionOpt
isCurrent: current, isCurrent: current,
canPurchaseUpgrade, canPurchaseUpgrade,
currency, currency,
amount: amount.toJSNumber(), amount: toJSNumber(amount),
botUrl, botUrl,
months, months,
}; };
@ -314,7 +315,7 @@ export function buildPrepaidGiveaway(
return { return {
type: 'starsGiveaway', type: 'starsGiveaway',
id: interaction.id.toString(), id: interaction.id.toString(),
stars: interaction.stars.toJSNumber(), stars: toJSNumber(interaction.stars),
quantity: interaction.quantity, quantity: interaction.quantity,
boosts: interaction.boosts, boosts: interaction.boosts,
date: interaction.date, date: interaction.date,
@ -336,8 +337,8 @@ export function buildApiBoostsStatus(boostStatus: GramJs.premium.BoostsStatus):
boostUrl, boostUrl,
giftBoosts, giftBoosts,
nextLevelBoosts, nextLevelBoosts,
...(premiumAudience && { premiumSubscribers: buildStatisticsPercentage(premiumAudience) }), premiumSubscribers: premiumAudience && buildStatisticsPercentage(premiumAudience),
...(prepaidGiveaways && { prepaidGiveaways: prepaidGiveaways.map((m) => buildPrepaidGiveaway(m)) }), prepaidGiveaways: prepaidGiveaways?.map((m) => buildPrepaidGiveaway(m)),
}; };
} }
@ -352,12 +353,12 @@ export function buildApiBoost(boost: GramJs.Boost): ApiBoost {
} = boost; } = boost;
return { return {
userId: userId && buildApiPeerId(userId, 'user'), userId: userId !== undefined ? buildApiPeerId(userId, 'user') : undefined,
multiplier, multiplier,
expires, expires,
isFromGiveaway: giveaway, isFromGiveaway: giveaway,
isGift: gift, isGift: gift,
stars: stars?.toJSNumber(), stars: toJSNumber(stars),
}; };
} }
@ -390,7 +391,8 @@ export function buildApiGiveawayInfo(info: GramJs.payments.TypeGiveawayInfo): Ap
type: 'active', type: 'active',
startDate, startDate,
isParticipating: participating, isParticipating: participating,
adminDisallowedChatId: adminDisallowedChatId && buildApiPeerId(adminDisallowedChatId, 'channel'), adminDisallowedChatId: adminDisallowedChatId !== undefined
? buildApiPeerId(adminDisallowedChatId, 'channel') : undefined,
disallowedCountry, disallowedCountry,
joinedTooEarlyDate, joinedTooEarlyDate,
isPreparingResults: preparingResults, isPreparingResults: preparingResults,
@ -416,7 +418,7 @@ export function buildApiGiveawayInfo(info: GramJs.payments.TypeGiveawayInfo): Ap
giftCodeSlug, giftCodeSlug,
isRefunded: refunded, isRefunded: refunded,
isWinner: winner, isWinner: winner,
starsPrize: starsPrize?.toJSNumber(), starsPrize: toJSNumber(starsPrize),
}; };
} }
} }
@ -429,7 +431,7 @@ export function buildApiCheckedGiftCode(giftcode: GramJs.payments.TypeCheckedGif
return { return {
date, date,
months, months,
toId: toId && buildApiPeerId(toId, 'user'), toId: toId !== undefined ? buildApiPeerId(toId, 'user') : undefined,
fromId: fromId && getApiChatIdFromMtpPeer(fromId), fromId: fromId && getApiChatIdFromMtpPeer(fromId),
usedAt: usedDate, usedAt: usedDate,
isFromGiveaway: viaGiveaway, isFromGiveaway: viaGiveaway,
@ -443,7 +445,7 @@ export function buildApiPremiumGiftCodeOption(option: GramJs.PremiumGiftCodeOpti
} = option; } = option;
return { return {
amount: amount.toJSNumber(), amount: toJSNumber(amount),
currency, currency,
months, months,
users, users,
@ -457,17 +459,17 @@ export function buildApiStarsGiftOptions(option: GramJs.StarsGiftOption): ApiSta
return { return {
isExtended: extended, isExtended: extended,
stars: stars.toJSNumber(), stars: toJSNumber(stars),
amount: amount.toJSNumber(), amount: toJSNumber(amount),
currency, currency,
}; };
} }
export function buildApiCurrencyAmount(amount: GramJs.TypeStarsAmount): ApiTypeCurrencyAmount | undefined { export function buildApiCurrencyAmount(amount: GramJs.TypeStarsAmount): ApiTypeCurrencyAmount {
if (amount instanceof GramJs.StarsAmount) { if (amount instanceof GramJs.StarsAmount) {
return { return {
currency: STARS_CURRENCY_CODE, currency: STARS_CURRENCY_CODE,
amount: amount.amount.toJSNumber(), amount: toJSNumber(amount.amount),
nanos: amount.nanos, nanos: amount.nanos,
}; };
} }
@ -475,11 +477,12 @@ export function buildApiCurrencyAmount(amount: GramJs.TypeStarsAmount): ApiTypeC
if (amount instanceof GramJs.StarsTonAmount) { if (amount instanceof GramJs.StarsTonAmount) {
return { return {
currency: TON_CURRENCY_CODE, currency: TON_CURRENCY_CODE,
amount: amount.amount.toJSNumber(), amount: toJSNumber(amount.amount),
}; };
} }
return undefined; const _exhaustive: never = amount;
return _exhaustive;
} }
export function buildApiStarsGiveawayWinnersOption( export function buildApiStarsGiveawayWinnersOption(
@ -492,7 +495,7 @@ export function buildApiStarsGiveawayWinnersOption(
return { return {
isDefault, isDefault,
users, users,
perUserStars: perUserStars.toJSNumber(), perUserStars: toJSNumber(perUserStars),
}; };
} }
@ -507,8 +510,8 @@ export function buildApiStarsGiveawayOptions(option: GramJs.StarsGiveawayOption)
isExtended: extended, isExtended: extended,
isDefault, isDefault,
yearlyBoosts, yearlyBoosts,
stars: stars.toJSNumber(), stars: toJSNumber(stars),
amount: amount.toJSNumber(), amount: toJSNumber(amount),
currency, currency,
winners: winnerList, winners: winnerList,
}; };
@ -625,9 +628,9 @@ export function buildApiStarTopupOption(option: GramJs.TypeStarsTopupOption): Ap
} = option; } = option;
return { return {
amount: amount.toJSNumber(), amount: toJSNumber(amount),
currency, currency,
stars: stars.toJSNumber(), stars: toJSNumber(stars),
isExtended: extended, isExtended: extended,
}; };
} }
@ -644,14 +647,14 @@ export function buildApiUniqueStarGiftValueInfo(
isLastSaleOnFragment: lastSaleOnFragment, isLastSaleOnFragment: lastSaleOnFragment,
isValueAverage: valueIsAverage, isValueAverage: valueIsAverage,
currency, currency,
value: value.toJSNumber(), value: toJSNumber(value),
initialSaleDate, initialSaleDate,
initialSaleStars: initialSaleStars.toJSNumber(), initialSaleStars: toJSNumber(initialSaleStars),
initialSalePrice: initialSalePrice.toJSNumber(), initialSalePrice: toJSNumber(initialSalePrice),
lastSaleDate, lastSaleDate,
lastSalePrice: lastSalePrice?.toJSNumber(), lastSalePrice: toJSNumber(lastSalePrice),
floorPrice: floorPrice?.toJSNumber(), floorPrice: toJSNumber(floorPrice),
averagePrice: averagePrice?.toJSNumber(), averagePrice: toJSNumber(averagePrice),
listedCount, listedCount,
fragmentListedCount, fragmentListedCount,
fragmentListedUrl, fragmentListedUrl,

View File

@ -1,4 +1,3 @@
import type BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import type { ApiEmojiStatusType, ApiPeerColor } from '../../types'; import type { ApiEmojiStatusType, ApiPeerColor } from '../../types';
@ -20,16 +19,16 @@ export function isMtpPeerChannel(peer: TypePeerOrInput): peer is GramJs.PeerChan
return peer.hasOwnProperty('channelId'); return peer.hasOwnProperty('channelId');
} }
export function buildApiPeerId(id: BigInt.BigInteger, type: 'user' | 'chat' | 'channel') { export function buildApiPeerId(id: bigint, type: 'user' | 'chat' | 'channel') {
if (type === 'user') { if (type === 'user') {
return id.toString(); return id.toString();
} }
if (type === 'channel') { if (type === 'channel') {
return id.add(CHANNEL_ID_BASE).negate().toString(); return ((id + CHANNEL_ID_BASE) * -1n).toString();
} }
return id.negate().toString(); return (id * -1n).toString();
} }
export function getApiChatIdFromMtpPeer(peer: TypePeerOrInput) { export function getApiChatIdFromMtpPeer(peer: TypePeerOrInput) {

View File

@ -17,10 +17,9 @@ import type {
} from '../../types'; } from '../../types';
import { buildApiUsernames, buildAvatarPhotoId } from './common'; import { buildApiUsernames, buildAvatarPhotoId } from './common';
import { buildApiCurrencyAmount } from './payments';
import { buildApiPeerId, getApiChatIdFromMtpPeer } from './peers'; import { buildApiPeerId, getApiChatIdFromMtpPeer } from './peers';
const DECIMALS = 10 ** 9;
export function buildChannelStatistics(stats: GramJs.stats.BroadcastStats): ApiChannelStatistics { export function buildChannelStatistics(stats: GramJs.stats.BroadcastStats): ApiChannelStatistics {
return { return {
type: 'channel', type: 'channel',
@ -275,16 +274,16 @@ function buildApiMessagePublicForward(message: GramJs.TypeMessage, chats: GramJs
}; };
} }
function buildChannelMonetizationBalances({ function buildChannelMonetizationBalances(revenueStatus: GramJs.StarsRevenueStatus): ChannelMonetizationBalances {
currentBalance, const currentBalance = buildApiCurrencyAmount(revenueStatus.currentBalance);
availableBalance, const availableBalance = buildApiCurrencyAmount(revenueStatus.availableBalance);
overallRevenue, const overallRevenue = buildApiCurrencyAmount(revenueStatus.overallRevenue);
withdrawalEnabled, const withdrawalEnabled = revenueStatus.withdrawalEnabled;
}: GramJs.StarsRevenueStatus): ChannelMonetizationBalances {
return { return {
currentBalance: Number(currentBalance) / DECIMALS, currentBalance,
availableBalance: Number(availableBalance) / DECIMALS, availableBalance,
overallRevenue: Number(overallRevenue) / DECIMALS, overallRevenue,
isWithdrawalEnabled: withdrawalEnabled, isWithdrawalEnabled: withdrawalEnabled,
}; };
} }

View File

@ -115,7 +115,7 @@ export function buildStickerSet(set: GramJs.StickerSet): ApiStickerSet {
const hasStaticThumb = thumbs?.some((thumb) => thumb.type === 's'); const hasStaticThumb = thumbs?.some((thumb) => thumb.type === 's');
const hasAnimatedThumb = thumbs?.some((thumb) => thumb.type === 'a'); const hasAnimatedThumb = thumbs?.some((thumb) => thumb.type === 'a');
const hasVideoThumb = thumbs?.some((thumb) => thumb.type === 'v'); const hasVideoThumb = thumbs?.some((thumb) => thumb.type === 'v');
const thumbCustomEmojiId = thumbDocumentId && String(thumbDocumentId); const thumbCustomEmojiId = thumbDocumentId !== undefined ? String(thumbDocumentId) : undefined;
const hasThumbnail = hasStaticThumb || hasAnimatedThumb || hasVideoThumb || Boolean(thumbCustomEmojiId); const hasThumbnail = hasStaticThumb || hasAnimatedThumb || hasVideoThumb || Boolean(thumbCustomEmojiId);

View File

@ -10,6 +10,7 @@ import type {
ApiUserType, ApiUserType,
} from '../../types'; } from '../../types';
import { toJSNumber } from '../../../util/numbers';
import { buildApiBotInfo } from './bots'; import { buildApiBotInfo } from './bots';
import { buildApiBusinessIntro, buildApiBusinessLocation, buildApiBusinessWorkHours } from './business'; import { buildApiBusinessIntro, buildApiBusinessLocation, buildApiBusinessWorkHours } from './business';
import { import {
@ -54,7 +55,8 @@ export function buildApiUserFullInfo(mtpUserFull: GramJs.users.UserFull): ApiUse
businessLocation: businessLocation && buildApiBusinessLocation(businessLocation), businessLocation: businessLocation && buildApiBusinessLocation(businessLocation),
businessWorkHours: businessWorkHours && buildApiBusinessWorkHours(businessWorkHours), businessWorkHours: businessWorkHours && buildApiBusinessWorkHours(businessWorkHours),
businessIntro: businessIntro && buildApiBusinessIntro(businessIntro), businessIntro: businessIntro && buildApiBusinessIntro(businessIntro),
personalChannelId: personalChannelId && buildApiPeerId(personalChannelId, 'channel'), personalChannelId: personalChannelId !== undefined
? buildApiPeerId(personalChannelId, 'channel') : undefined,
personalChannelMessageId: personalChannelMessage, personalChannelMessageId: personalChannelMessage,
botVerification: botVerification && buildApiBotVerification(botVerification), botVerification: botVerification && buildApiBotVerification(botVerification),
areAdsEnabled: sponsoredEnabled, areAdsEnabled: sponsoredEnabled,
@ -64,7 +66,7 @@ export function buildApiUserFullInfo(mtpUserFull: GramJs.users.UserFull): ApiUse
starsMyPendingRatingDate, starsMyPendingRatingDate,
isBotCanManageEmojiStatus: botCanManageEmojiStatus, isBotCanManageEmojiStatus: botCanManageEmojiStatus,
hasScheduledMessages: hasScheduled, hasScheduledMessages: hasScheduled,
paidMessagesStars: sendPaidMessagesStars?.toJSNumber(), paidMessagesStars: toJSNumber(sendPaidMessagesStars),
settings: buildApiPeerSettings(settings), settings: buildApiPeerSettings(settings),
}; };
} }
@ -89,7 +91,7 @@ export function buildApiPeerSettings({
phoneCountry, phoneCountry,
nameChangeDate, nameChangeDate,
photoChangeDate, photoChangeDate,
chargedPaidMessageStars: chargePaidMessageStars?.toJSNumber(), chargedPaidMessageStars: toJSNumber(chargePaidMessageStars),
}; };
} }
@ -143,7 +145,7 @@ export function buildApiUser(mtpUser: GramJs.TypeUser): ApiUser | undefined {
botVerificationIconId: botVerificationIcon?.toString(), botVerificationIconId: botVerificationIcon?.toString(),
color: mtpUser.color && buildApiPeerColor(mtpUser.color), color: mtpUser.color && buildApiPeerColor(mtpUser.color),
profileColor: profileColor && buildApiPeerColor(profileColor), profileColor: profileColor && buildApiPeerColor(profileColor),
paidMessagesStars: sendPaidMessagesStars?.toJSNumber(), paidMessagesStars: toJSNumber(sendPaidMessagesStars),
}; };
} }
@ -193,8 +195,8 @@ export function buildApiBirthday(birthday: GramJs.TypeBirthday): ApiBirthday {
export function buildApiStarsRating(starsRating: GramJs.StarsRating): ApiStarsRating { export function buildApiStarsRating(starsRating: GramJs.StarsRating): ApiStarsRating {
return { return {
level: starsRating.level, level: starsRating.level,
currentLevelStars: starsRating.currentLevelStars.toJSNumber(), currentLevelStars: toJSNumber(starsRating.currentLevelStars),
stars: starsRating.stars.toJSNumber(), stars: toJSNumber(starsRating.stars),
nextLevelStars: starsRating.nextLevelStars?.toJSNumber(), nextLevelStars: toJSNumber(starsRating.nextLevelStars),
}; };
} }

View File

@ -1,6 +1,5 @@
import BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import { generateRandomBytes, readBigIntFromBuffer } from '../../../lib/gramjs/Helpers'; import { generateRandomBigInt, generateRandomBytes, readBigIntFromBuffer } from '../../../lib/gramjs/Helpers';
import type { import type {
ApiBotApp, ApiBotApp,
@ -47,13 +46,13 @@ import localDb from '../localDb';
export const DEFAULT_PRIMITIVES = { export const DEFAULT_PRIMITIVES = {
INT: 0, INT: 0,
BIGINT: BigInt(0), BIGINT: 0n,
STRING: '', STRING: '',
} as const; } as const;
export function getEntityTypeById(peerId: string) { export function getEntityTypeById(peerId: string) {
const n = Number(peerId); const n = BigInt(peerId);
if (n > 0) { if (n > 0n) {
return 'user'; return 'user';
} }
@ -143,7 +142,7 @@ export function buildInputPaidReactionPrivacy(isPrivate?: boolean, peerId?: stri
export function buildInputPeerFromLocalDb(chatOrUserId: string): GramJs.TypeInputPeer | undefined { export function buildInputPeerFromLocalDb(chatOrUserId: string): GramJs.TypeInputPeer | undefined {
const type = getEntityTypeById(chatOrUserId); const type = getEntityTypeById(chatOrUserId);
let accessHash: BigInt.BigInteger | undefined; let accessHash: bigint | undefined;
if (type === 'user') { if (type === 'user') {
accessHash = localDb.users[chatOrUserId]?.accessHash; accessHash = localDb.users[chatOrUserId]?.accessHash;
@ -207,7 +206,7 @@ export function buildInputMediaDocument(media: ApiSticker | ApiVideo) {
return new GramJs.InputMediaDocument({ id: inputDocument }); return new GramJs.InputMediaDocument({ id: inputDocument });
} }
export function buildInputPoll(pollParams: ApiNewPoll, randomId: BigInt.BigInteger) { export function buildInputPoll(pollParams: ApiNewPoll, randomId: bigint) {
const { summary, quiz } = pollParams; const { summary, quiz } = pollParams;
const poll = new GramJs.Poll({ const poll = new GramJs.Poll({
@ -359,10 +358,6 @@ export function buildInputStory(story: ApiStory | ApiStorySkipped) {
}); });
} }
export function generateRandomBigInt() {
return readBigIntFromBuffer(generateRandomBytes(8), true, true);
}
export function generateRandomTimestampedBigInt() { export function generateRandomTimestampedBigInt() {
// 32 bits for timestamp, 32 bits are random // 32 bits for timestamp, 32 bits are random
const buffer = generateRandomBytes(8); const buffer = generateRandomBytes(8);
@ -372,10 +367,6 @@ export function generateRandomTimestampedBigInt() {
return readBigIntFromBuffer(buffer, true, true); return readBigIntFromBuffer(buffer, true, true);
} }
export function generateRandomInt() {
return readBigIntFromBuffer(generateRandomBytes(4), true, true).toJSNumber();
}
export function buildMessageFromUpdate( export function buildMessageFromUpdate(
id: number, id: number,
chatId: string, chatId: string,
@ -470,7 +461,7 @@ export function buildInputContact({
lastName: string; lastName: string;
}) { }) {
return new GramJs.InputPhoneContact({ return new GramJs.InputPhoneContact({
clientId: BigInt(1), clientId: 1n,
phone, phone,
firstName, firstName,
lastName, lastName,
@ -597,17 +588,16 @@ export function buildInputThemeParams(params: ApiThemeParameters) {
} }
export function buildMtpPeerId(id: string, type: 'user' | 'chat' | 'channel') { export function buildMtpPeerId(id: string, type: 'user' | 'chat' | 'channel') {
const n = BigInt(id);
if (type === 'user') { if (type === 'user') {
return BigInt(id); return n;
} }
const n = Number(id);
if (type === 'channel') { if (type === 'channel') {
return BigInt(-n - CHANNEL_ID_BASE); return -n - CHANNEL_ID_BASE;
} }
return BigInt(n * -1); return n * -1n;
} }
export function buildInputGroupCall(groupCall: Partial<ApiGroupCall>) { export function buildInputGroupCall(groupCall: Partial<ApiGroupCall>) {

View File

@ -1,4 +1,3 @@
import BigInt from 'big-integer';
import { Api as GramJs } from '../../lib/gramjs'; import { Api as GramJs } from '../../lib/gramjs';
import { DEBUG } from '../../config'; import { DEBUG } from '../../config';

View File

@ -1,4 +1,3 @@
import BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import type { import type {

View File

@ -1,5 +1,5 @@
import BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import { generateRandomBigInt } from '../../../lib/gramjs/Helpers';
import type { import type {
ApiBotApp, ApiBotApp,
@ -33,7 +33,6 @@ import {
buildInputThemeParams, buildInputThemeParams,
buildInputUser, buildInputUser,
DEFAULT_PRIMITIVES, DEFAULT_PRIMITIVES,
generateRandomBigInt,
} from '../gramjsBuilders'; } from '../gramjsBuilders';
import { import {
addDocumentToLocalDb, addDocumentToLocalDb,

View File

@ -1,5 +1,5 @@
import BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import { generateRandomInt32 } from '../../../lib/gramjs/Helpers';
import type { JoinGroupCallPayload } from '../../../lib/secret-sauce'; import type { JoinGroupCallPayload } from '../../../lib/secret-sauce';
import type { import type {
@ -13,7 +13,7 @@ import {
buildPhoneCall, buildPhoneCall,
} from '../apiBuilders/calls'; } from '../apiBuilders/calls';
import { import {
buildInputGroupCall, buildInputPeer, buildInputPhoneCall, buildInputUser, DEFAULT_PRIMITIVES, generateRandomInt, buildInputGroupCall, buildInputPeer, buildInputPhoneCall, buildInputUser, DEFAULT_PRIMITIVES,
} from '../gramjsBuilders'; } from '../gramjsBuilders';
import { sendApiUpdate } from '../updates/apiUpdateEmitter'; import { sendApiUpdate } from '../updates/apiUpdateEmitter';
import { invokeRequest, invokeRequestBeacon } from './client'; import { invokeRequest, invokeRequestBeacon } from './client';
@ -182,7 +182,7 @@ export async function createGroupCall({
}: { }: {
peer: ApiChat; peer: ApiChat;
}) { }) {
const randomId = generateRandomInt(); const randomId = generateRandomInt32();
const result = await invokeRequest(new GramJs.phone.CreateGroupCall({ const result = await invokeRequest(new GramJs.phone.CreateGroupCall({
peer: buildInputPeer(peer.id, peer.accessHash), peer: buildInputPeer(peer.id, peer.accessHash),
randomId, randomId,
@ -284,7 +284,7 @@ export async function requestCall({
user: ApiUser; gAHash: number[]; isVideo?: boolean; user: ApiUser; gAHash: number[]; isVideo?: boolean;
}) { }) {
const result = await invokeRequest(new GramJs.phone.RequestCall({ const result = await invokeRequest(new GramJs.phone.RequestCall({
randomId: generateRandomInt(), randomId: generateRandomInt32(),
userId: buildInputUser(user.id, user.accessHash), userId: buildInputUser(user.id, user.accessHash),
gAHash: Buffer.from(gAHash), gAHash: Buffer.from(gAHash),
...(isVideo && { video: true }), ...(isVideo && { video: true }),

View File

@ -1,6 +1,6 @@
import BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import { RPCError } from '../../../lib/gramjs/errors'; import { RPCError } from '../../../lib/gramjs/errors';
import { generateRandomBigInt } from '../../../lib/gramjs/Helpers';
import type { ChatListType } from '../../../types'; import type { ChatListType } from '../../../types';
import type { import type {
@ -71,7 +71,6 @@ import {
buildInputUser, buildInputUser,
buildMtpMessageEntity, buildMtpMessageEntity,
DEFAULT_PRIMITIVES, DEFAULT_PRIMITIVES,
generateRandomBigInt,
getEntityTypeById, getEntityTypeById,
} from '../gramjsBuilders'; } from '../gramjsBuilders';
import { import {

View File

@ -1,4 +1,3 @@
import bigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import type { SizeType, TelegramClient } from '../../../lib/gramjs'; import type { SizeType, TelegramClient } from '../../../lib/gramjs';
@ -15,6 +14,7 @@ import {
MEDIA_CACHE_NAME_AVATARS, MEDIA_CACHE_NAME_AVATARS,
} from '../../../config'; } from '../../../config';
import * as cacheApi from '../../../util/cacheApi'; import * as cacheApi from '../../../util/cacheApi';
import { toJSNumber } from '../../../util/numbers';
import { getEntityTypeById } from '../gramjsBuilders'; import { getEntityTypeById } from '../gramjsBuilders';
import localDb from '../localDb'; import localDb from '../localDb';
@ -89,7 +89,7 @@ async function download(
} = parsed; } = parsed;
if (entityType === 'staticMap') { if (entityType === 'staticMap') {
const accessHash = bigInt(entityId); const accessHash = BigInt(entityId);
const parsedParams = new URLSearchParams(params); const parsedParams = new URLSearchParams(params);
const long = Number(parsedParams.get('long')); const long = Number(parsedParams.get('long'));
const lat = Number(parsedParams.get('lat')); const lat = Number(parsedParams.get('lat'));
@ -162,7 +162,7 @@ async function download(
fullSize = entity.size; fullSize = entity.size;
} else if (entity instanceof GramJs.Document) { } else if (entity instanceof GramJs.Document) {
mimeType = entity.mimeType; mimeType = entity.mimeType;
fullSize = entity.size.toJSNumber(); fullSize = toJSNumber(entity.size);
} }
// Prevent HTML-in-video attacks // Prevent HTML-in-video attacks

View File

@ -1,6 +1,6 @@
import BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import { RPCError } from '../../../lib/gramjs/errors'; import { RPCError } from '../../../lib/gramjs/errors';
import { generateRandomBigInt } from '../../../lib/gramjs/Helpers';
import type { import type {
ForwardMessagesParams, ForwardMessagesParams,
@ -98,7 +98,6 @@ import {
buildPeer, buildPeer,
buildSendMessageAction, buildSendMessageAction,
DEFAULT_PRIMITIVES, DEFAULT_PRIMITIVES,
generateRandomBigInt,
getEntityTypeById, getEntityTypeById,
} from '../gramjsBuilders'; } from '../gramjsBuilders';
import { import {
@ -1888,7 +1887,7 @@ export async function forwardApiMessages(params: ForwardMessagesParams) {
const priceInStars = messagePriceInStars ? messagePriceInStars * messageIds.length : undefined; const priceInStars = messagePriceInStars ? messagePriceInStars * messageIds.length : undefined;
const randomIds = messageIds.map(generateRandomBigInt); const randomIds = messageIds.map(() => generateRandomBigInt());
try { try {
const update = await invokeRequest(new GramJs.messages.ForwardMessages({ const update = await invokeRequest(new GramJs.messages.ForwardMessages({
fromPeer: buildInputPeer(fromChat.id, fromChat.accessHash), fromPeer: buildInputPeer(fromChat.id, fromChat.accessHash),
@ -2498,7 +2497,7 @@ export async function sendQuickReply({
if (!messages || messages instanceof GramJs.messages.MessagesNotModified) return; if (!messages || messages instanceof GramJs.messages.MessagesNotModified) return;
const ids = messages.messages.map((m) => m.id); const ids = messages.messages.map((m) => m.id);
const randomIds = ids.map(generateRandomBigInt); const randomIds = ids.map(() => generateRandomBigInt());
const result = await invokeRequest(new GramJs.messages.SendQuickReplyMessages({ const result = await invokeRequest(new GramJs.messages.SendQuickReplyMessages({
peer: buildInputPeer(chat.id, chat.accessHash), peer: buildInputPeer(chat.id, chat.accessHash),

View File

@ -1,4 +1,3 @@
import BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import { RPCError } from '../../../lib/gramjs/errors'; import { RPCError } from '../../../lib/gramjs/errors';

View File

@ -1,5 +1,3 @@
import type bigInt from 'big-integer';
import BigInt from 'big-integer';
import { AuthKey } from '../../../lib/gramjs/crypto/AuthKey'; import { AuthKey } from '../../../lib/gramjs/crypto/AuthKey';
import { Logger } from '../../../lib/gramjs/extensions'; import { Logger } from '../../../lib/gramjs/extensions';
import { import {
@ -20,13 +18,13 @@ class PhoneCallState {
private seq = 0; private seq = 0;
private gA?: bigInt.BigInteger; private gA?: bigint;
private gB: any; private gB?: bigint;
private p?: bigInt.BigInteger; private p?: bigint;
private random?: bigInt.BigInteger; private random?: bigint;
private waitForState: Promise<void>; private waitForState: Promise<void>;
@ -55,8 +53,8 @@ class PhoneCallState {
} }
acceptCall({ p, g, random }: DhConfig) { acceptCall({ p, g, random }: DhConfig) {
const pLast = readBigIntFromBuffer(p, false); const pLast = readBigIntFromBuffer(Buffer.from(p), false);
const randomLast = readBigIntFromBuffer(random, false); const randomLast = readBigIntFromBuffer(Buffer.from(random), false);
const gB = modExp(BigInt(g), randomLast, pLast); const gB = modExp(BigInt(g), randomLast, pLast);
this.gB = gB; this.gB = gB;
@ -77,7 +75,7 @@ class PhoneCallState {
this.gA = readBigIntFromBuffer(Buffer.from(gAOrB), false); this.gA = readBigIntFromBuffer(Buffer.from(gAOrB), false);
} }
const authKey = modExp( const authKey = modExp(
!this.isOutgoing ? this.gA : this.gB, (!this.isOutgoing ? this.gA : this.gB)!,
this.random, this.random,
this.p, this.p,
); );
@ -125,14 +123,14 @@ class PhoneCallState {
// https://github.com/TelegramV/App/blob/ead52320975362139cabad18cf8346f98c349a22/src/js/MTProto/Calls/Internal.js#L72 // https://github.com/TelegramV/App/blob/ead52320975362139cabad18cf8346f98c349a22/src/js/MTProto/Calls/Internal.js#L72
function computeEmojiIndex(bytes: Uint8Array) { function computeEmojiIndex(bytes: Uint8Array) {
return ((BigInt(bytes[0]).and(0x7F)).shiftLeft(56)) return ((BigInt(bytes[0]) & 0x7Fn) << 56n)
.or((BigInt(bytes[1]).shiftLeft(48))) | ((BigInt(bytes[1]) << 48n))
.or((BigInt(bytes[2]).shiftLeft(40))) | ((BigInt(bytes[2]) << 40n))
.or((BigInt(bytes[3]).shiftLeft(32))) | ((BigInt(bytes[3]) << 32n))
.or((BigInt(bytes[4]).shiftLeft(24))) | ((BigInt(bytes[4]) << 24n))
.or((BigInt(bytes[5]).shiftLeft(16))) | ((BigInt(bytes[5]) << 16n))
.or((BigInt(bytes[6]).shiftLeft(8))) | ((BigInt(bytes[6]) << 8n))
.or((BigInt(bytes[7]))); | ((BigInt(bytes[7])));
} }
async function generateEmojiFingerprint( async function generateEmojiFingerprint(
@ -144,7 +142,7 @@ async function generateEmojiFingerprint(
const kPartSize = 8; const kPartSize = 8;
for (let partOffset = 0; partOffset !== hash.byteLength; partOffset += kPartSize) { for (let partOffset = 0; partOffset !== hash.byteLength; partOffset += kPartSize) {
const value = computeEmojiIndex(hash.subarray(partOffset, partOffset + kPartSize)); const value = computeEmojiIndex(hash.subarray(partOffset, partOffset + kPartSize));
const index = value.modPow(1, emojiCount).toJSNumber(); const index = Number(value % BigInt(emojiCount));
const offset = emojiOffsets[index]; const offset = emojiOffsets[index];
const size = emojiOffsets[index + 1] - offset; const size = emojiOffsets[index + 1] - offset;
result.push(String.fromCharCode(...emojiData.subarray(offset, offset + size))); result.push(String.fromCharCode(...emojiData.subarray(offset, offset + size)));

View File

@ -1,4 +1,3 @@
import BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import type { import type {

View File

@ -1,4 +1,3 @@
import BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import { RPCError } from '../../../lib/gramjs/errors'; import { RPCError } from '../../../lib/gramjs/errors';
@ -23,6 +22,7 @@ import {
UNMUTE_TIMESTAMP, UNMUTE_TIMESTAMP,
} from '../../../config'; } from '../../../config';
import { buildCollectionByKey } from '../../../util/iteratees'; import { buildCollectionByKey } from '../../../util/iteratees';
import { toJSNumber } from '../../../util/numbers';
import { BLOCKED_LIST_LIMIT } from '../../../limits'; import { BLOCKED_LIST_LIMIT } from '../../../limits';
import { buildAppConfig } from '../apiBuilders/appConfig'; import { buildAppConfig } from '../apiBuilders/appConfig';
import { buildApiPhoto, buildPrivacyRules } from '../apiBuilders/common'; import { buildApiPhoto, buildPrivacyRules } from '../apiBuilders/common';
@ -686,7 +686,7 @@ export async function fetchGlobalPrivacySettings() {
shouldArchiveAndMuteNewNonContact: Boolean(result.archiveAndMuteNewNoncontactPeers), shouldArchiveAndMuteNewNonContact: Boolean(result.archiveAndMuteNewNoncontactPeers),
shouldHideReadMarks: Boolean(result.hideReadMarks), shouldHideReadMarks: Boolean(result.hideReadMarks),
shouldNewNonContactPeersRequirePremium: Boolean(result.newNoncontactPeersRequirePremium), shouldNewNonContactPeersRequirePremium: Boolean(result.newNoncontactPeersRequirePremium),
nonContactPeersPaidStars: Number(result.noncontactPeersPaidStars), nonContactPeersPaidStars: toJSNumber(result.noncontactPeersPaidStars),
shouldDisplayGiftsButton: Boolean(result.displayGiftsButton), shouldDisplayGiftsButton: Boolean(result.displayGiftsButton),
disallowedGifts: result.disallowedGifts && buildApiDisallowedGiftsSettings(result.disallowedGifts), disallowedGifts: result.disallowedGifts && buildApiDisallowedGiftsSettings(result.disallowedGifts),
}; };
@ -726,7 +726,7 @@ export async function updateGlobalPrivacySettings({
shouldArchiveAndMuteNewNonContact: Boolean(result.archiveAndMuteNewNoncontactPeers), shouldArchiveAndMuteNewNonContact: Boolean(result.archiveAndMuteNewNoncontactPeers),
shouldHideReadMarks: Boolean(result.hideReadMarks), shouldHideReadMarks: Boolean(result.hideReadMarks),
shouldNewNonContactPeersRequirePremium: Boolean(result.newNoncontactPeersRequirePremium), shouldNewNonContactPeersRequirePremium: Boolean(result.newNoncontactPeersRequirePremium),
nonContactPeersPaidStars: Number(result.noncontactPeersPaidStars), nonContactPeersPaidStars: toJSNumber(result.noncontactPeersPaidStars),
shouldDisplayGiftsButton, shouldDisplayGiftsButton,
disallowedGifts, disallowedGifts,
}; };

View File

@ -1,4 +1,3 @@
import bigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import type { GiftProfileFilterOptions, ResaleGiftsFilterOptions } from '../../../types'; import type { GiftProfileFilterOptions, ResaleGiftsFilterOptions } from '../../../types';
@ -39,7 +38,7 @@ import { getPassword } from './twoFaSettings';
export async function fetchCheckCanSendGift({ giftId }: { giftId: string }) { export async function fetchCheckCanSendGift({ giftId }: { giftId: string }) {
const result = await invokeRequest(new GramJs.payments.CheckCanSendGift({ const result = await invokeRequest(new GramJs.payments.CheckCanSendGift({
giftId: bigInt(giftId), giftId: BigInt(giftId),
})); }));
if (!result) { if (!result) {
@ -112,10 +111,10 @@ export async function fetchResaleGifts({
]; ];
const params: GetResaleStarGifts = { const params: GetResaleStarGifts = {
giftId: bigInt(giftId), giftId: BigInt(giftId),
offset, offset,
limit, limit,
attributesHash: attributesHash ? bigInt(attributesHash) : DEFAULT_PRIMITIVES.BIGINT, attributesHash: attributesHash ? BigInt(attributesHash) : DEFAULT_PRIMITIVES.BIGINT,
attributes: buildInputResaleGiftsAttributes(attributes), attributes: buildInputResaleGiftsAttributes(attributes),
...(filter && { ...(filter && {
sortByPrice: filter.sortType === 'byPrice' || undefined, sortByPrice: filter.sortType === 'byPrice' || undefined,
@ -200,7 +199,7 @@ export function convertStarGift({
})); }));
} }
export async function getStarsGiftOptions({ export async function fetchStarsGiftOptions({
chat, chat,
}: { }: {
chat?: ApiChat; chat?: ApiChat;
@ -397,7 +396,7 @@ export async function fetchStarGiftUpgradePreview({
giftId: string; giftId: string;
}) { }) {
const result = await invokeRequest(new GramJs.payments.GetStarGiftUpgradePreview({ const result = await invokeRequest(new GramJs.payments.GetStarGiftUpgradePreview({
giftId: bigInt(giftId), giftId: BigInt(giftId),
})); }));
if (!result) { if (!result) {
@ -527,7 +526,7 @@ export async function fetchStarGiftCollections({
}) { }) {
const result = await invokeRequest(new GramJs.payments.GetStarGiftCollections({ const result = await invokeRequest(new GramJs.payments.GetStarGiftCollections({
peer: buildInputPeer(peer.id, peer.accessHash), peer: buildInputPeer(peer.id, peer.accessHash),
hash: hash ? bigInt(hash) : DEFAULT_PRIMITIVES.BIGINT, hash: hash ? BigInt(hash) : DEFAULT_PRIMITIVES.BIGINT,
})); }));
if (!result || result instanceof GramJs.payments.StarGiftCollectionsNotModified) { if (!result || result instanceof GramJs.payments.StarGiftCollectionsNotModified) {

View File

@ -1,4 +1,3 @@
import BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import type { import type {

View File

@ -1,4 +1,3 @@
import BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import type { import type {
@ -109,8 +108,14 @@ export async function fetchFeaturedStickers({ hash }: { hash?: string }) {
}; };
} }
export async function fetchFeaturedEmojiStickers() { export async function fetchFeaturedEmojiStickers({
const result = await invokeRequest(new GramJs.messages.GetFeaturedEmojiStickers({ hash: BigInt(0) })); hash,
}: {
hash?: string;
}) {
const result = await invokeRequest(new GramJs.messages.GetFeaturedEmojiStickers({
hash: hash ? BigInt(hash) : DEFAULT_PRIMITIVES.BIGINT,
}));
if (!result || result instanceof GramJs.messages.FeaturedStickersNotModified) { if (!result || result instanceof GramJs.messages.FeaturedStickersNotModified) {
return undefined; return undefined;

View File

@ -1,9 +1,9 @@
import BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import type { ApiEmojiStatusType, ApiPeer, ApiUser, import type { ApiEmojiStatusType, ApiPeer, ApiUser,
} from '../../types'; } from '../../types';
import { toJSNumber } from '../../../util/numbers';
import { buildApiChatFromPreview } from '../apiBuilders/chats'; import { buildApiChatFromPreview } from '../apiBuilders/chats';
import { buildApiPhoto } from '../apiBuilders/common'; import { buildApiPhoto } from '../apiBuilders/common';
import { buildApiPeerId } from '../apiBuilders/peers'; import { buildApiPeerId } from '../apiBuilders/peers';
@ -88,7 +88,7 @@ export async function fetchFullUser({
}; };
} }
export async function fetchCommonChats(user: ApiUser, maxId?: string) { export async function fetchCommonChats({ user, maxId }: { user: ApiUser; maxId?: string }) {
const result = await invokeRequest(new GramJs.messages.GetCommonChats({ const result = await invokeRequest(new GramJs.messages.GetCommonChats({
userId: buildInputUser(user.id, user.accessHash), userId: buildInputUser(user.id, user.accessHash),
maxId: maxId maxId: maxId
@ -112,12 +112,13 @@ export async function fetchPaidMessagesStarsAmount(user: ApiUser) {
id: [buildInputUser(user.id, user.accessHash)], id: [buildInputUser(user.id, user.accessHash)],
})); }));
if (!result) { if (!result?.[0]) {
return undefined; return undefined;
} }
const requirement = result[0];
if (result[0] instanceof GramJs.RequirementToContactPaidMessages) { if (requirement instanceof GramJs.RequirementToContactPaidMessages) {
return result[0].starsAmount?.toJSNumber(); return toJSNumber(requirement.starsAmount);
} }
return undefined; return undefined;
@ -149,7 +150,7 @@ export async function fetchTopUsers() {
} }
export async function fetchContactList() { export async function fetchContactList() {
const result = await invokeRequest(new GramJs.contacts.GetContacts({ hash: BigInt('0') })); const result = await invokeRequest(new GramJs.contacts.GetContacts({ hash: DEFAULT_PRIMITIVES.BIGINT }));
if (!result || result instanceof GramJs.contacts.ContactsNotModified) { if (!result || result instanceof GramJs.contacts.ContactsNotModified) {
return undefined; return undefined;
} }
@ -224,7 +225,7 @@ export function updateContact({
firstName, firstName,
lastName, lastName,
phone: phoneNumber, phone: phoneNumber,
...(shouldSharePhoneNumber && { addPhonePrivacyException: shouldSharePhoneNumber }), addPhonePrivacyException: shouldSharePhoneNumber || undefined,
}), { }), {
shouldReturnTrue: true, shouldReturnTrue: true,
}); });
@ -273,7 +274,7 @@ export async function fetchPaidMessagesRevenue({ user }: {
userId: buildInputUser(user.id, user.accessHash), userId: buildInputUser(user.id, user.accessHash),
})); }));
if (!result) return undefined; if (!result) return undefined;
return result.starsAmount.toJSNumber(); return toJSNumber(result.starsAmount);
} }
export async function fetchProfilePhotos({ export async function fetchProfilePhotos({
@ -294,7 +295,7 @@ export async function fetchProfilePhotos({
userId: buildInputUser(id, accessHash), userId: buildInputUser(id, accessHash),
limit, limit,
offset, offset,
maxId: BigInt('0'), maxId: DEFAULT_PRIMITIVES.BIGINT,
})); }));
if (!result) { if (!result) {

View File

@ -1,15 +1,13 @@
import type { BigInteger } from 'big-integer';
export class LocalUpdatePts { export class LocalUpdatePts {
constructor(public pts: number, public ptsCount: number) {} constructor(public pts: number, public ptsCount: number) {}
} }
export class LocalUpdateChannelPts { export class LocalUpdateChannelPts {
constructor(public channelId: BigInteger, public pts: number, public ptsCount: number) {} constructor(public channelId: bigint, public pts: number, public ptsCount: number) {}
} }
export type UpdatePts = LocalUpdatePts | LocalUpdateChannelPts; export type UpdatePts = LocalUpdatePts | LocalUpdateChannelPts;
export function buildLocalUpdatePts(pts: number, ptsCount: number, channelId?: BigInteger) { export function buildLocalUpdatePts(pts: number, ptsCount: number, channelId?: bigint) {
return channelId ? new LocalUpdateChannelPts(channelId, pts, ptsCount) : new LocalUpdatePts(pts, ptsCount); return channelId ? new LocalUpdateChannelPts(channelId, pts, ptsCount) : new LocalUpdatePts(pts, ptsCount);
} }

View File

@ -1,5 +1,6 @@
import type { ApiChat } from './chats'; import type { ApiChat } from './chats';
import type { ApiTypePrepaidGiveaway } from './payments'; import type { ApiTypePrepaidGiveaway } from './payments';
import type { ApiTypeCurrencyAmount } from './stars';
export interface ApiChannelStatistics { export interface ApiChannelStatistics {
type: 'channel'; type: 'channel';
@ -153,8 +154,8 @@ export interface StatisticsStoryInteractionCounter {
} }
export interface ChannelMonetizationBalances { export interface ChannelMonetizationBalances {
currentBalance: number; currentBalance: ApiTypeCurrencyAmount;
availableBalance: number; availableBalance: ApiTypeCurrencyAmount;
overallRevenue: number; overallRevenue: ApiTypeCurrencyAmount;
isWithdrawalEnabled?: boolean; isWithdrawalEnabled?: boolean;
} }

View File

@ -20,6 +20,7 @@ import {
} from '../../../util/browser/windowEnvironment'; } from '../../../util/browser/windowEnvironment';
import buildClassName from '../../../util/buildClassName'; import buildClassName from '../../../util/buildClassName';
import { formatMediaDuration } from '../../../util/dates/dateFormat'; import { formatMediaDuration } from '../../../util/dates/dateFormat';
import { getServerTime } from '../../../util/serverTime';
import { LOCAL_TGS_URLS } from '../../common/helpers/animatedAssets'; import { LOCAL_TGS_URLS } from '../../common/helpers/animatedAssets';
import renderText from '../../common/helpers/renderText'; import renderText from '../../common/helpers/renderText';
@ -215,7 +216,7 @@ const PhoneCall: FC<StateProps> = ({
setTimeout(stopFlipping, 250); setTimeout(stopFlipping, 250);
}, [startFlipping, stopFlipping]); }, [startFlipping, stopFlipping]);
const timeElapsed = phoneCall?.startDate && (Number(new Date()) / 1000 - phoneCall.startDate); const timeElapsed = phoneCall?.startDate && (getServerTime() - phoneCall.startDate);
useEffect(() => { useEffect(() => {
if (phoneCall?.state === 'discarded') { if (phoneCall?.state === 'discarded') {

View File

@ -7,6 +7,7 @@ import type { ApiChannelMonetizationStatistics } from '../../../api/types';
import { selectChat, selectChatFullInfo, selectTabState } from '../../../global/selectors'; import { selectChat, selectChatFullInfo, selectTabState } from '../../../global/selectors';
import buildClassName from '../../../util/buildClassName'; import buildClassName from '../../../util/buildClassName';
import { convertTonFromNanos } from '../../../util/formatCurrency';
import renderText from '../../common/helpers/renderText'; import renderText from '../../common/helpers/renderText';
import { isGraph } from './helpers/isGraph'; import { isGraph } from './helpers/isGraph';
@ -144,9 +145,10 @@ const MonetizationStatistics = ({
}, [isReady, statistics, oldLang, chatId, dcId, forceUpdate]); }, [isReady, statistics, oldLang, chatId, dcId, forceUpdate]);
function renderAvailableReward() { function renderAvailableReward() {
const [integerTonPart, decimalTonPart] = availableBalance ? availableBalance.toFixed(4).split('.') : [0]; const tonAmount = availableBalance ? convertTonFromNanos(availableBalance.amount) : 0;
const [integerTonPart, decimalTonPart] = tonAmount.toFixed(4).split('.');
const [integerUsdPart, decimalUsdPart] = availableBalance const [integerUsdPart, decimalUsdPart] = availableBalance
&& statistics?.usdRate ? (availableBalance * statistics.usdRate).toFixed(2).split('.') : [0]; && statistics?.usdRate ? (tonAmount * statistics.usdRate).toFixed(2).split('.') : [0];
return ( return (
<div className={styles.availableReward}> <div className={styles.availableReward}>

View File

@ -9,6 +9,7 @@ import type {
import buildClassName from '../../../util/buildClassName'; import buildClassName from '../../../util/buildClassName';
import { formatFullDate } from '../../../util/dates/dateFormat'; import { formatFullDate } from '../../../util/dates/dateFormat';
import { convertTonFromNanos } from '../../../util/formatCurrency';
import { formatInteger, formatIntegerCompact } from '../../../util/textFormat'; import { formatInteger, formatIntegerCompact } from '../../../util/textFormat';
import useLang from '../../../hooks/useLang'; import useLang from '../../../hooks/useLang';
@ -206,9 +207,21 @@ const StatisticsOverview: FC<OwnProps> = ({
{isToncoin ? ( {isToncoin ? (
<tr> <tr>
<td className={styles.tableCell}> <td className={styles.tableCell}>
{renderBalanceCell(balances?.availableBalance || 0, usdRate || 0, 'lng_channel_earn_available')} {renderBalanceCell(
{renderBalanceCell(balances?.currentBalance || 0, usdRate || 0, 'lng_channel_earn_reward')} balances?.availableBalance ? convertTonFromNanos(balances.availableBalance.amount) : 0,
{renderBalanceCell(balances?.overallRevenue || 0, usdRate || 0, 'lng_channel_earn_total')} usdRate || 0,
'lng_channel_earn_available',
)}
{renderBalanceCell(
balances?.currentBalance ? convertTonFromNanos(balances.currentBalance.amount) : 0,
usdRate || 0,
'lng_channel_earn_reward',
)}
{renderBalanceCell(
balances?.overallRevenue ? convertTonFromNanos(balances.overallRevenue.amount) : 0,
usdRate || 0,
'lng_channel_earn_total',
)}
</td> </td>
</tr> </tr>
) : schema.map((row) => ( ) : schema.map((row) => (

View File

@ -338,13 +338,13 @@ export const REPLIES_USER_ID = '1271266957'; // TODO For Test connection ID must
export const VERIFICATION_CODES_USER_ID = '489000'; export const VERIFICATION_CODES_USER_ID = '489000';
export const ANONYMOUS_USER_ID = '2666000'; export const ANONYMOUS_USER_ID = '2666000';
export const RESTRICTED_EMOJI_SET_ID = '7173162320003080'; export const RESTRICTED_EMOJI_SET_ID = '7173162320003080';
export const CHANNEL_ID_BASE = 10 ** 12; export const CHANNEL_ID_BASE = 10n ** 12n;
export const DEFAULT_GIF_SEARCH_BOT_USERNAME = 'gif'; export const DEFAULT_GIF_SEARCH_BOT_USERNAME = 'gif';
export const ALL_FOLDER_ID = 0; export const ALL_FOLDER_ID = 0;
export const ARCHIVED_FOLDER_ID = 1; export const ARCHIVED_FOLDER_ID = 1;
export const SAVED_FOLDER_ID = -1; export const SAVED_FOLDER_ID = -1;
export const FOLDER_TITLE_MAX_LENGTH = 12; export const FOLDER_TITLE_MAX_LENGTH = 12;
export const DELETED_COMMENTS_CHANNEL_ID = '-1000000000777'; export const DELETED_COMMENTS_CHANNEL_ID = (-CHANNEL_ID_BASE - 777n).toString();
export const MAX_MEDIA_FILES_FOR_ALBUM = 10; export const MAX_MEDIA_FILES_FOR_ALBUM = 10;
export const MAX_ACTIVE_PINNED_CHATS = 5; export const MAX_ACTIVE_PINNED_CHATS = 5;
export const SCHEDULED_WHEN_ONLINE = 0x7FFFFFFE; export const SCHEDULED_WHEN_ONLINE = 0x7FFFFFFE;

View File

@ -608,7 +608,12 @@ addActionHandler('openStarsGiftModal', async (global, actions, payload): Promise
return; return;
} }
const starsGiftOptions = await callApi('getStarsGiftOptions', {}); const chat = forUserId ? selectChat(global, forUserId) : undefined;
if (forUserId && !chat) return;
const starsGiftOptions = await callApi('fetchStarsGiftOptions', {
chat,
});
global = getGlobal(); global = getGlobal();
global = updateTabState(global, { global = updateTabState(global, {

View File

@ -787,7 +787,7 @@ addActionHandler('clearCustomEmojiForEmoji', (global): ActionReturnType => {
}); });
addActionHandler('loadFeaturedEmojiStickers', async (global): Promise<void> => { addActionHandler('loadFeaturedEmojiStickers', async (global): Promise<void> => {
const featuredStickers = await callApi('fetchFeaturedEmojiStickers'); const featuredStickers = await callApi('fetchFeaturedEmojiStickers', {});
if (!featuredStickers) { if (!featuredStickers) {
return; return;
} }

View File

@ -174,7 +174,10 @@ addActionHandler('loadCommonChats', async (global, actions, payload): Promise<vo
return; return;
} }
const result = await callApi('fetchCommonChats', user, commonChats?.maxId); const result = await callApi('fetchCommonChats', {
user,
maxId: commonChats?.maxId,
});
if (!result) { if (!result) {
return; return;
} }

View File

@ -28,7 +28,7 @@ import {
VIDEO_STICKER_MIME_TYPE, VIDEO_STICKER_MIME_TYPE,
} from '../../config'; } from '../../config';
import { areDeepEqual } from '../../util/areDeepEqual'; import { areDeepEqual } from '../../util/areDeepEqual';
import { getCleanPeerId, isUserId } from '../../util/entities/ids'; import { getRawPeerId, isUserId } from '../../util/entities/ids';
import { areSortedArraysIntersecting, unique } from '../../util/iteratees'; import { areSortedArraysIntersecting, unique } from '../../util/iteratees';
import { isLocalMessageId } from '../../util/keys/messageKey'; import { isLocalMessageId } from '../../util/keys/messageKey';
import { getServerTime } from '../../util/serverTime'; import { getServerTime } from '../../util/serverTime';
@ -385,7 +385,7 @@ export function isUploadingFileSticker(attachment: ApiAttachment) {
export function getMessageLink(peer: ApiPeer, topicId?: ThreadId, messageId?: number) { export function getMessageLink(peer: ApiPeer, topicId?: ThreadId, messageId?: number) {
const chatUsername = getMainUsername(peer); const chatUsername = getMainUsername(peer);
const normalizedId = getCleanPeerId(peer.id); const normalizedId = getRawPeerId(peer.id).toString();
const chatPart = chatUsername || `c/${normalizedId}`; const chatPart = chatUsername || `c/${normalizedId}`;
const topicPart = topicId && topicId !== MAIN_THREAD_ID ? `/${topicId}` : ''; const topicPart = topicId && topicId !== MAIN_THREAD_ID ? `/${topicId}` : '';

View File

@ -1,65 +1,92 @@
import BigInt from 'big-integer';
import { createHash, randomBytes } from './crypto/crypto'; import { createHash, randomBytes } from './crypto/crypto';
export function readBigIntFromBuffer(buffer: Buffer | number[], little = true, signed = false): BigInt.BigInteger { export function readBigIntFromBuffer(buffer: Buffer, little = true, signed = false): bigint {
let randBuffer = Buffer.from(buffer); const len = buffer.length;
const bytesNumber = randBuffer.length; if (len === 0) return 0n;
if (little) {
randBuffer = randBuffer.reverse();
}
let bigInt = BigInt(randBuffer.toString('hex'), 16);
if (signed && Math.floor(bigInt.toString(2).length / 8) >= bytesNumber) {
bigInt = bigInt.subtract(BigInt(2)
.pow(BigInt(bytesNumber * 8)));
}
return bigInt;
}
export function toSignedLittleBuffer(big: BigInt.BigInteger, number = 8) { // Hot path for longs
const bigNumber = BigInt(big); if (len === 8) {
const byteArray: number[] = []; if (signed) {
for (let i = 0; i < number; i++) { return little ? buffer.readBigInt64LE(0) : buffer.readBigInt64BE(0);
byteArray[i] = bigNumber.shiftRight(8 * i) } else {
.and(255) return little ? buffer.readBigUInt64LE(0) : buffer.readBigUInt64BE(0);
.toJSNumber();
}
return Buffer.from(byteArray);
}
export function readBufferFromBigInt(bigInt: BigInt.BigInteger, bytesNumber: number, little = true, signed = false) {
const bitLength = bigInt.bitLength().toJSNumber();
const bytes = Math.ceil(bitLength / 8);
if (bytesNumber < bytes) {
throw new Error('OverflowError: int too big to convert');
}
if (!signed && bigInt.lesser(BigInt(0))) {
throw new Error('Cannot convert to unsigned');
}
let below = false;
if (bigInt.lesser(BigInt(0))) {
below = true;
bigInt = bigInt.abs();
}
const hex = bigInt.toString(16).padStart(bytesNumber * 2, '0');
let buffer = Buffer.from(hex, 'hex');
if (signed && below) {
buffer[buffer.length - 1] = 256 - buffer[buffer.length - 1];
for (let i = 0; i < buffer.length - 1; i++) {
buffer[i] = 255 - buffer[i];
} }
} }
// Parse unsigned value
let x = 0n;
if (little) { if (little) {
buffer = buffer.reverse(); for (let i = len - 1; i >= 0; i--) x = (x << 8n) | BigInt(buffer[i]);
} else {
for (let i = 0; i < len; i++) x = (x << 8n) | BigInt(buffer[i]);
}
// Apply two's-complement decode if signed and sign bit is set
if (signed) {
const signBit = 1n << BigInt(len * 8 - 1);
if ((x & signBit) !== 0n) x -= 1n << BigInt(len * 8);
}
return x;
}
export function toSignedLittleBuffer(big: bigint, number = 8) {
const buffer = Buffer.allocUnsafe(number);
// Use Buffer method for 8-byte buffers
if (number === 8) {
buffer.writeBigInt64LE(big);
return buffer;
}
// For other sizes, extract bytes manually
for (let i = 0; i < number; i++) {
buffer[i] = Number((big >> BigInt(8 * i)) & 0xFFn);
} }
return buffer; return buffer;
} }
export function readBufferFromBigInt(
value: bigint,
bytesNumber: number,
little = true,
signed = false,
): Buffer {
if (!Number.isInteger(bytesNumber) || bytesNumber <= 0) {
throw new RangeError('bytesNumber must be a positive integer');
}
if (!signed && value < 0n) {
throw new RangeError('Cannot convert negative to unsigned');
}
const bits = 8n * BigInt(bytesNumber);
const min = signed ? -(1n << (bits - 1n)) : 0n;
const max = signed ? (1n << (bits - 1n)) - 1n : (1n << bits) - 1n;
if (value < min || value > max) {
throw new RangeError(
`Value ${value} does not fit in ${bytesNumber} ${signed ? 'signed' : 'unsigned'} bytes`,
);
}
// Two's complement encode if negative
let v = signed && value < 0n ? (1n << bits) + value : value;
const buf = Buffer.allocUnsafe(bytesNumber);
if (little) {
for (let i = 0; i < bytesNumber; i++) {
buf[i] = Number(v & 0xFFn);
v >>= 8n;
}
} else {
for (let i = bytesNumber - 1; i >= 0; i--) {
buf[i] = Number(v & 0xFFn);
v >>= 8n;
}
}
return buf;
}
export function generateRandomLong(signed = true) { export function generateRandomLong(signed = true) {
return readBigIntFromBuffer(generateRandomBytes(8), true, signed); return readBigIntFromBuffer(generateRandomBytes(8), true, signed);
} }
@ -68,16 +95,24 @@ export function mod(n: number, m: number) {
return ((n % m) + m) % m; return ((n % m) + m) % m;
} }
export function bigIntMod(n: BigInt.BigInteger, m: BigInt.BigInteger) { export function bigIntMod(n: bigint, m: bigint) {
return ((n.remainder(m)).add(m)).remainder(m); return ((n % m) + m) % m;
} }
export function generateRandomBytes(count: number) { export function generateRandomBytes(count: number) {
return Buffer.from(randomBytes(count)); return Buffer.from(randomBytes(count));
} }
export function generateRandomBigInt(bytes: number = 8) {
return readBigIntFromBuffer(generateRandomBytes(bytes), true, true);
}
export function generateRandomInt32() {
return Number(readBigIntFromBuffer(generateRandomBytes(4), true, true));
}
export async function generateKeyDataFromNonce( export async function generateKeyDataFromNonce(
serverNonceBigInt: BigInt.BigInteger, newNonceBigInt: BigInt.BigInteger, serverNonceBigInt: bigint, newNonceBigInt: bigint,
) { ) {
const serverNonce = toSignedLittleBuffer(serverNonceBigInt, 16); const serverNonce = toSignedLittleBuffer(serverNonceBigInt, 16);
const newNonce = toSignedLittleBuffer(newNonceBigInt, 32); const newNonce = toSignedLittleBuffer(newNonceBigInt, 32);
@ -116,36 +151,76 @@ export function sha256(data: Buffer): Promise<Buffer> {
} }
export function modExp( export function modExp(
a: bigInt.BigInteger, a: bigint,
b: bigInt.BigInteger, b: bigint,
n: bigInt.BigInteger, n: bigint,
) { ) {
a = a.remainder(n); a = a % n;
let result = BigInt.one; let result = 1n;
let x = a; let x = a;
while (b.greater(BigInt.zero)) { while (b > 0n) {
const leastSignificantBit = b.remainder(BigInt(2)); const leastSignificantBit = b % 2n;
b = b.divide(BigInt(2)); b = b / 2n;
if (leastSignificantBit.eq(BigInt.one)) { if (leastSignificantBit === 1n) {
result = result.multiply(x); result = result * x;
result = result.remainder(n); result = result % n;
} }
x = x.multiply(x); x = x * x;
x = x.remainder(n); x = x % n;
} }
return result; return result;
} }
export function getByteArray(integer: BigInt.BigInteger, signed = false) { export function getByteArray(integer: bigint, signed = false): Buffer {
const bits = integer.toString(2).length; if (!signed && integer < 0n) {
const byteLength = Math.floor((bits + 8 - 1) / 8); throw new RangeError('Cannot convert negative to unsigned');
return readBufferFromBigInt(BigInt(integer), byteLength, false, signed); }
let bytes: number;
if (signed) {
if (integer >= 0n) {
const bits = bitLength(integer) + 1;
bytes = Math.max(1, Math.ceil(bits / 8));
} else {
const bits = bitLength(-integer - 1n) + 1;
bytes = Math.max(1, Math.ceil(bits / 8));
}
} else {
const bits = bitLength(integer);
bytes = Math.max(1, Math.ceil(bits / 8));
}
return readBufferFromBigInt(integer, bytes, false, signed);
} }
export function getRandomInt(min: number, max: number) { export function randomBits(k: number): bigint {
min = Math.ceil(min); if (k <= 0) return 0n;
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min; const bytes = randomBytes(Math.ceil(k / 8));
let r = 0n;
for (let i = 0; i < bytes.length; i++) {
r = (r << 8n) | BigInt(bytes[i]);
}
return r & ((1n << BigInt(k)) - 1n);
}
export function randBetweenBigInt(a: bigint, b: bigint): bigint {
const low = a < b ? a : b;
const high = a < b ? b : a;
const range = high - low + 1n;
if (range <= 1n) return low;
const k = bitLength(range - 1n);
const twoPowK = 1n << BigInt(k);
const limit = (twoPowK / range) * range;
for (;;) {
const r = randomBits(k);
if (r < limit) return low + (r % range);
}
} }
export function sleep(ms: number) { export function sleep(ms: number) {
@ -188,3 +263,73 @@ export function crc32(buf: Buffer | string) {
} }
return (crc ^ (-1)) >>> 0; return (crc ^ (-1)) >>> 0;
} }
const testersCoeff: number[] = [];
const testersBigCoeff: bigint[] = [];
const testers: bigint[] = [];
let testersN = 0;
// https://stackoverflow.com/a/76616288
export function bitLength(x: bigint) {
let k = 0;
while (true) {
if (testersN === k) {
testersCoeff.push(32 << testersN);
testersBigCoeff.push(BigInt(testersCoeff[testersN]));
testers.push(1n << testersBigCoeff[testersN]);
testersN++;
}
if (x < testers[k]) break;
k++;
}
if (!k) return 32 - Math.clz32(Number(x));
// determine length by bisection
k--;
let i = testersCoeff[k];
let a = x >> testersBigCoeff[k];
while (k--) {
const b = a >> testersBigCoeff[k];
if (b) {
i += testersCoeff[k];
a = b;
}
}
return i + 32 - Math.clz32(Number(a));
}
export const BigMath = {
abs(x: bigint) {
return x < 0n ? -x : x;
},
sign(x: bigint) {
if (x === 0n) return 0n;
return x < 0n ? -1n : 1n;
},
pow(base: bigint, exponent: bigint) {
return base ** exponent;
},
min(value: bigint, ...values: bigint[]) {
for (const v of values) {
if (v < value) value = v;
}
return value;
},
max(value: bigint, ...values: bigint[]) {
for (const v of values) {
if (v > value) value = v;
}
return value;
},
};
export function jsonStringifyWithBigInt(obj: any) {
return JSON.stringify(obj, (key, value) => {
if (typeof value === 'bigint') {
return value.toString();
}
return value;
});
}

View File

@ -1,10 +1,9 @@
import BigInt from 'big-integer';
import { pbkdf2 } from './crypto/crypto'; import { pbkdf2 } from './crypto/crypto';
import Api from './tl/api'; import Api from './tl/api';
import { import {
bigIntMod, bigIntMod,
bitLength,
generateRandomBytes, generateRandomBytes,
modExp, modExp,
readBigIntFromBuffer, readBigIntFromBuffer,
@ -14,59 +13,6 @@ import {
const SIZE_FOR_HASH = 256; const SIZE_FOR_HASH = 256;
/**
*
*
* @param prime{BigInteger}
* @param g{BigInteger}
*/
/*
We don't support changing passwords yet
function checkPrimeAndGoodCheck(prime, g) {
console.error('Unsupported function `checkPrimeAndGoodCheck` call. Arguments:', prime, g)
const goodPrimeBitsCount = 2048
if (prime < 0 || prime.bitLength() !== goodPrimeBitsCount) {
throw new Error(`bad prime count ${prime.bitLength()},expected ${goodPrimeBitsCount}`)
}
// TODO this is kinda slow
if (Factorizator.factorize(prime)[0] !== 1) {
throw new Error('give "prime" is not prime')
}
if (g.eq(BigInt(2))) {
if ((prime.remainder(BigInt(8))).neq(BigInt(7))) {
throw new Error(`bad g ${g}, mod8 ${prime % 8}`)
}
} else if (g.eq(BigInt(3))) {
if ((prime.remainder(BigInt(3))).neq(BigInt(2))) {
throw new Error(`bad g ${g}, mod3 ${prime % 3}`)
}
// eslint-disable-next-line no-empty
} else if (g.eq(BigInt(4))) {
} else if (g.eq(BigInt(5))) {
if (!([ BigInt(1), BigInt(4) ].includes(prime.remainder(BigInt(5))))) {
throw new Error(`bad g ${g}, mod8 ${prime % 5}`)
}
} else if (g.eq(BigInt(6))) {
if (!([ BigInt(19), BigInt(23) ].includes(prime.remainder(BigInt(24))))) {
throw new Error(`bad g ${g}, mod8 ${prime % 24}`)
}
} else if (g.eq(BigInt(7))) {
if (!([ BigInt(3), BigInt(5), BigInt(6) ].includes(prime.remainder(BigInt(7))))) {
throw new Error(`bad g ${g}, mod8 ${prime % 7}`)
}
} else {
throw new Error(`bad g ${g}`)
}
const primeSub1Div2 = (prime.subtract(BigInt(1))).divide(BigInt(2))
if (Factorizator.factorize(primeSub1Div2)[0] !== 1) {
throw new Error('(prime - 1) // 2 is not prime')
}
}
*/
function checkPrimeAndGood(primeBytes: Buffer, g: number) { function checkPrimeAndGood(primeBytes: Buffer, g: number) {
const goodPrime = Buffer.from([ const goodPrime = Buffer.from([
0xC7, 0x1C, 0xAE, 0xB9, 0xC6, 0xB1, 0xC9, 0x04, 0x8E, 0x6C, 0x52, 0x2F, 0x70, 0xF1, 0x3F, 0x73, 0xC7, 0x1C, 0xAE, 0xB9, 0xC6, 0xB1, 0xC9, 0x04, 0x8E, 0x6C, 0x52, 0x2F, 0x70, 0xF1, 0x3F, 0x73,
@ -95,30 +41,29 @@ function checkPrimeAndGood(primeBytes: Buffer, g: number) {
// checkPrimeAndGoodCheck(readBigIntFromBuffer(primeBytes, false), g) // checkPrimeAndGoodCheck(readBigIntFromBuffer(primeBytes, false), g)
} }
function isGoodLarge(number: BigInt.BigInteger, p: BigInt.BigInteger): boolean { function isGoodLarge(number: bigint, p: bigint): boolean {
return (number.greater(BigInt(0)) && (p.subtract(number) return number > 0n && number < p;
.greater(BigInt(0))));
} }
function numBytesForHash(number: Buffer): Buffer { function numBytesForHash(number: Buffer): Buffer {
return Buffer.concat([Buffer.alloc(SIZE_FOR_HASH - number.length), number]); return Buffer.concat([Buffer.alloc(SIZE_FOR_HASH - number.length), number]);
} }
function bigNumForHash(g: BigInt.BigInteger) { function bigNumForHash(g: bigint) {
return readBufferFromBigInt(g, SIZE_FOR_HASH, false); return readBufferFromBigInt(g, SIZE_FOR_HASH, false);
} }
function isGoodModExpFirst(modexp: BigInt.BigInteger, prime: BigInt.BigInteger): boolean { function isGoodModExpFirst(modexp: bigint, prime: bigint): boolean {
const diff = prime.subtract(modexp); const diff = prime - modexp;
const minDiffBitsCount = 2048 - 64; const minDiffBitsCount = 2048 - 64;
const maxModExpSize = 256; const maxModExpSize = 256;
return !( return !(
diff.lesser(BigInt(0)) diff < 0n
|| diff.bitLength().toJSNumber() < minDiffBitsCount || bitLength(diff) < minDiffBitsCount
|| modexp.bitLength().toJSNumber() < minDiffBitsCount || bitLength(modexp) < minDiffBitsCount
|| Math.floor((modexp.bitLength().toJSNumber() + 7) / 8) > maxModExpSize || Math.floor((bitLength(modexp) + 7) / 8) > maxModExpSize
); );
} }
@ -189,7 +134,7 @@ export async function computeCheck(request: Api.account.Password, password: stri
try { try {
checkPrimeAndGood(algo.p, g); checkPrimeAndGood(algo.p, g);
} catch (e) { } catch (e) {
throw new Error('bad /g in password'); throw new Error('bad p/g in password');
} }
if (!isGoodLarge(B, p)) { if (!isGoodLarge(B, p)) {
throw new Error('bad b in check'); throw new Error('bad b in check');
@ -200,7 +145,7 @@ export async function computeCheck(request: Api.account.Password, password: stri
const bForHash = numBytesForHash(srpB); const bForHash = numBytesForHash(srpB);
const gX = modExp(BigInt(g), x, p); const gX = modExp(BigInt(g), x, p);
const k = readBigIntFromBuffer(await sha256(Buffer.concat([pForHash, gForHash])), false); const k = readBigIntFromBuffer(await sha256(Buffer.concat([pForHash, gForHash])), false);
const kgX = bigIntMod(k.multiply(gX), p); const kgX = bigIntMod(k * gX, p);
const generateAndCheckRandom = async () => { const generateAndCheckRandom = async () => {
const randomSize = 256; const randomSize = 256;
@ -211,20 +156,20 @@ export async function computeCheck(request: Api.account.Password, password: stri
if (isGoodModExpFirst(A, p)) { if (isGoodModExpFirst(A, p)) {
const aForHash = bigNumForHash(A); const aForHash = bigNumForHash(A);
const u = readBigIntFromBuffer(await sha256(Buffer.concat([aForHash, bForHash])), false); const u = readBigIntFromBuffer(await sha256(Buffer.concat([aForHash, bForHash])), false);
if (u.greater(BigInt(0))) { if (u > 0n) {
return { a, aForHash, u }; return { a, aForHash, u };
} }
} }
} }
}; };
const { a, aForHash, u } = await generateAndCheckRandom(); const { a, aForHash, u } = await generateAndCheckRandom();
const gB = bigIntMod(B.subtract(kgX), p); const gB = bigIntMod(B - kgX, p);
if (!isGoodModExpFirst(gB, p)) { if (!isGoodModExpFirst(gB, p)) {
throw new Error('bad gB'); throw new Error('bad gB');
} }
const ux = u.multiply(x); const ux = u * x;
const aUx = a.add(ux); const aUx = a + ux;
const S = modExp(gB, aUx, p); const S = modExp(gB, aUx, p);
const [K, pSha, gSha, salt1Sha, salt2Sha] = await Promise.all([ const [K, pSha, gSha, salt1Sha, salt2Sha] = await Promise.all([
sha256(bigNumForHash(S)), sha256(bigNumForHash(S)),
@ -244,8 +189,7 @@ export async function computeCheck(request: Api.account.Password, password: stri
return new Api.InputCheckPasswordSRP({ return new Api.InputCheckPasswordSRP({
srpId, srpId,
A: Buffer.from(aForHash), A: aForHash,
M1, M1,
}); });
} }

View File

@ -1,10 +1,9 @@
import BigInt from 'big-integer';
import type { DownloadFileWithDcParams } from './downloadFile'; import type { DownloadFileWithDcParams } from './downloadFile';
import type { MockTypes } from './mockUtils/MockTypes'; import type { MockTypes } from './mockUtils/MockTypes';
import type { SizeType } from './TelegramClient'; import type { SizeType } from './TelegramClient';
import { GENERAL_TOPIC_ID } from '../../../config'; import { GENERAL_TOPIC_ID } from '../../../config';
import { toJSNumber } from '../../../util/numbers';
import { Logger } from '../extensions'; import { Logger } from '../extensions';
import { UpdateConnectionState } from '../network'; import { UpdateConnectionState } from '../network';
import Api from '../tl/api'; import Api from '../tl/api';
@ -195,7 +194,7 @@ class TelegramClient {
about: 'lol', about: 'lol',
settings: new Api.PeerSettings({}), settings: new Api.PeerSettings({}),
notifySettings: new Api.PeerNotifySettings({}), notifySettings: new Api.PeerNotifySettings({}),
id: BigInt(1), id: 1n,
commonChatsCount: 0, commonChatsCount: 0,
}), }),
chats: [], chats: [],
@ -381,7 +380,7 @@ class TelegramClient {
thumbSize: size ? size.type : '', thumbSize: size ? size.type : '',
}), }),
{ {
fileSize: size ? size.size : doc.size.toJSNumber(), fileSize: size ? size.size : toJSNumber(doc.size),
progressCallback: args.progressCallback, progressCallback: args.progressCallback,
start: args.start, start: args.start,
end: args.end, end: args.end,

View File

@ -1,4 +1,3 @@
import bigInt from 'big-integer';
import os from 'os'; import os from 'os';
import type LocalUpdatePremiumFloodWait from '../../../api/gramjs/updates/UpdatePremiumFloodWait'; import type LocalUpdatePremiumFloodWait from '../../../api/gramjs/updates/UpdatePremiumFloodWait';
@ -16,6 +15,7 @@ import type { DownloadFileParams, DownloadFileWithDcParams, DownloadMediaParams
import type { UploadFileParams } from './uploadFile'; import type { UploadFileParams } from './uploadFile';
import Deferred from '../../../util/Deferred'; import Deferred from '../../../util/Deferred';
import { toJSNumber } from '../../../util/numbers';
import { import {
FloodTestPhoneWaitError, FloodTestPhoneWaitError,
FloodWaitError, FloodWaitError,
@ -44,7 +44,7 @@ import { authFlow, checkAuthorization } from './auth';
import { downloadFile } from './downloadFile'; import { downloadFile } from './downloadFile';
import { uploadFile } from './uploadFile'; import { uploadFile } from './uploadFile';
import { getRandomInt, sleep } from '../Helpers'; import { generateRandomBigInt, sleep } from '../Helpers';
import RequestState from '../network/RequestState'; import RequestState from '../network/RequestState';
import Session from '../sessions/Abstract'; import Session from '../sessions/Abstract';
import MemorySession from '../sessions/Memory'; import MemorySession from '../sessions/Memory';
@ -411,7 +411,7 @@ class TelegramClient {
return undefined; return undefined;
} }
return sender.send(new Api.PingDelayDisconnect({ return sender.send(new Api.PingDelayDisconnect({
pingId: bigInt(getRandomInt(Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER)), pingId: generateRandomBigInt(),
disconnectDelay: PING_DISCONNECT_DELAY, disconnectDelay: PING_DISCONNECT_DELAY,
})); }));
}; };
@ -877,7 +877,7 @@ class TelegramClient {
thumbSize: '', thumbSize: '',
}), }),
{ {
fileSize: doc.size.toJSNumber(), fileSize: toJSNumber(doc.size),
dcId: doc.dcId, dcId: doc.dcId,
}) as Promise<Buffer | undefined>; // Sticker thumb cannot be larger than 2GB, right? }) as Promise<Buffer | undefined>; // Sticker thumb cannot be larger than 2GB, right?
}); });
@ -995,7 +995,7 @@ class TelegramClient {
thumbSize: size && 'type' in size ? size.type : '', thumbSize: size && 'type' in size ? size.type : '',
}), }),
{ {
fileSize: size && 'size' in size ? size.size : doc.size.toJSNumber(), fileSize: size && 'size' in size ? size.size : toJSNumber(doc.size),
progressCallback: args.progressCallback, progressCallback: args.progressCallback,
start: args.start, start: args.start,
end: args.end, end: args.end,
@ -1055,7 +1055,7 @@ class TelegramClient {
} }
async downloadStaticMap( async downloadStaticMap(
accessHash: bigInt.BigInteger, accessHash: bigint,
long: number, long: number,
lat: number, lat: number,
w: number, w: number,

View File

@ -1,8 +1,7 @@
import bigInt from 'big-integer';
import type TelegramClient from './TelegramClient'; import type TelegramClient from './TelegramClient';
import type { Update } from './TelegramClient'; import type { Update } from './TelegramClient';
import { tryParseBigInt } from '../../../util/numbers';
import { getServerTime } from '../../../util/serverTime'; import { getServerTime } from '../../../util/serverTime';
import { DEFAULT_PRIMITIVES } from '../../../api/gramjs/gramjsBuilders'; import { DEFAULT_PRIMITIVES } from '../../../api/gramjs/gramjsBuilders';
import { RPCError } from '../errors'; import { RPCError } from '../errors';
@ -234,6 +233,7 @@ async function signInUserWithQrCode(
let isScanningComplete = false; let isScanningComplete = false;
const { apiId, apiHash } = apiCredentials; const { apiId, apiHash } = apiCredentials;
const exceptIds = authParams.accountIds?.map((id) => tryParseBigInt(id)).filter(Boolean) || [];
const inputPromise = (async () => { const inputPromise = (async () => {
// eslint-disable-next-line no-constant-condition // eslint-disable-next-line no-constant-condition
@ -245,7 +245,7 @@ async function signInUserWithQrCode(
const result = await client.invoke(new Api.auth.ExportLoginToken({ const result = await client.invoke(new Api.auth.ExportLoginToken({
apiId, apiId,
apiHash, apiHash,
exceptIds: authParams.accountIds?.map((id) => bigInt(id)) || [], exceptIds,
})); }));
if (!(result instanceof Api.auth.LoginToken)) { if (!(result instanceof Api.auth.LoginToken)) {
throw new Error('Unexpected'); throw new Error('Unexpected');
@ -286,7 +286,7 @@ async function signInUserWithQrCode(
const result2 = await client.invoke(new Api.auth.ExportLoginToken({ const result2 = await client.invoke(new Api.auth.ExportLoginToken({
apiId, apiId,
apiHash, apiHash,
exceptIds: authParams.accountIds?.map((id) => bigInt(id)) || [], exceptIds,
})); }));
if (result2 instanceof Api.auth.LoginTokenSuccess && result2.authorization instanceof Api.auth.Authorization) { if (result2 instanceof Api.auth.LoginTokenSuccess && result2.authorization instanceof Api.auth.Authorization) {

View File

@ -1,5 +1,3 @@
import BigInt from 'big-integer';
import type TelegramClient from './TelegramClient'; import type TelegramClient from './TelegramClient';
import type { SizeType } from './TelegramClient'; import type { SizeType } from './TelegramClient';

View File

@ -1,5 +1,3 @@
import type bigInt from 'big-integer';
import type { GramJsAppConfig } from '../../../../api/gramjs/apiBuilders/appConfig'; import type { GramJsAppConfig } from '../../../../api/gramjs/apiBuilders/appConfig';
import type { ApiAvailableReaction } from '../../../../api/types'; import type { ApiAvailableReaction } from '../../../../api/types';
import type Api from '../../tl/api'; import type Api from '../../tl/api';
@ -62,7 +60,7 @@ export type MockMessageReactions = {
export type MockDocument = Partial<Api.Document> & { export type MockDocument = Partial<Api.Document> & {
id: number; id: number;
mimeType: string; mimeType: string;
size: bigInt.BigInteger; size: bigint;
url: string; url: string;
bytes: Buffer; bytes: Buffer;
}; };

View File

@ -1,7 +1,6 @@
import BigInt from 'big-integer';
import type { MockTypes } from './MockTypes'; import type { MockTypes } from './MockTypes';
import { CHANNEL_ID_BASE } from '../../../../config';
import Api from '../../tl/api'; import Api from '../../tl/api';
import createMockedChatAdminRights from './createMockedChatAdminRights'; import createMockedChatAdminRights from './createMockedChatAdminRights';
import createMockedChatBannedRights from './createMockedChatBannedRights'; import createMockedChatBannedRights from './createMockedChatBannedRights';
@ -14,7 +13,7 @@ export default function createMockedChannel(id: string, mockData: MockTypes): Ap
if (!channel) throw Error('No such channel ' + id); if (!channel) throw Error('No such channel ' + id);
const { const {
accessHash = BigInt(1), accessHash = 1n,
title = 'Channel', title = 'Channel',
date = MOCK_STARTING_DATE, date = MOCK_STARTING_DATE,
bannedRights = createMockedChatBannedRights(id, mockData), bannedRights = createMockedChatBannedRights(id, mockData),
@ -24,7 +23,7 @@ export default function createMockedChannel(id: string, mockData: MockTypes): Ap
return new Api.Channel({ return new Api.Channel({
...rest, ...rest,
id: BigInt(Number(id) + 1000000000), id: -BigInt(id) - CHANNEL_ID_BASE,
accessHash, accessHash,
title, title,
bannedRights, bannedRights,

View File

@ -1,5 +1,3 @@
import BigInt from 'big-integer';
import type { MockTypes } from './MockTypes'; import type { MockTypes } from './MockTypes';
import Api from '../../tl/api'; import Api from '../../tl/api';

View File

@ -1,5 +1,3 @@
import BigInt from 'big-integer';
import type { MockTypes } from './MockTypes'; import type { MockTypes } from './MockTypes';
import Api from '../../tl/api'; import Api from '../../tl/api';
@ -12,7 +10,7 @@ export default function createMockedDocument(documentId: number, mockData: MockT
if (!document) throw Error('No such document ' + documentId); if (!document) throw Error('No such document ' + documentId);
const { const {
accessHash = BigInt(1), accessHash = 1n,
fileReference = Buffer.from([0]), fileReference = Buffer.from([0]),
date = MOCK_STARTING_DATE, date = MOCK_STARTING_DATE,
dcId = 2, dcId = 2,

View File

@ -1,7 +1,6 @@
import BigInt from 'big-integer';
import type { MockTypes } from './MockTypes'; import type { MockTypes } from './MockTypes';
import { toJSNumber } from '../../../../util/numbers';
import Api from '../../tl/api'; import Api from '../../tl/api';
import { MOCK_STARTING_DATE } from './MockTypes'; import { MOCK_STARTING_DATE } from './MockTypes';
@ -12,7 +11,7 @@ export default function createMockedPhoto(documentId: number, mockData: MockType
if (!document) throw Error('No such document ' + documentId); if (!document) throw Error('No such document ' + documentId);
const { const {
accessHash = BigInt(1), accessHash = 1n,
fileReference = Buffer.from([0]), fileReference = Buffer.from([0]),
date = MOCK_STARTING_DATE, date = MOCK_STARTING_DATE,
dcId = 2, dcId = 2,
@ -32,13 +31,13 @@ export default function createMockedPhoto(documentId: number, mockData: MockType
type: 'm', type: 'm',
w: 100, w: 100,
h: 100, h: 100,
size: size.toJSNumber(), size: toJSNumber(size),
}), }),
new Api.PhotoSize({ new Api.PhotoSize({
type: 'x', type: 'x',
w: 100, w: 100,
h: 100, h: 100,
size: size.toJSNumber(), size: toJSNumber(size),
}), }),
], ],
// thumbs?: Api.TypePhotoSize[]; // thumbs?: Api.TypePhotoSize[];

View File

@ -1,7 +1,6 @@
import BigInt from 'big-integer';
import type { MockTypes } from './MockTypes'; import type { MockTypes } from './MockTypes';
import { CHANNEL_ID_BASE } from '../../../../config';
import Api from '../../tl/api'; import Api from '../../tl/api';
export default function createMockedReplies(chatId: string, id: number, mockData: MockTypes) { export default function createMockedReplies(chatId: string, id: number, mockData: MockTypes) {
@ -19,7 +18,7 @@ export default function createMockedReplies(chatId: string, id: number, mockData
comments: true, comments: true,
replies: replies.replies, replies: replies.replies,
repliesPts: 1, repliesPts: 1,
channelId: BigInt(1000000000 + 2), channelId: -2n - CHANNEL_ID_BASE,
// recentRepliers?: Api.TypePeer[]; // recentRepliers?: Api.TypePeer[];
}); });
} }

View File

@ -1,7 +1,6 @@
import BigInt from 'big-integer';
import type { MockTypes } from './MockTypes'; import type { MockTypes } from './MockTypes';
import { CHANNEL_ID_BASE } from '../../../../config';
import Api from '../../tl/api'; import Api from '../../tl/api';
export default function createMockedTypeInputPeer(id: string, mockData: MockTypes): Api.TypeInputPeer { export default function createMockedTypeInputPeer(id: string, mockData: MockTypes): Api.TypeInputPeer {
@ -9,7 +8,7 @@ export default function createMockedTypeInputPeer(id: string, mockData: MockType
if (user) { if (user) {
return new Api.InputPeerUser({ return new Api.InputPeerUser({
userId: BigInt(id), userId: BigInt(id),
accessHash: BigInt(1), accessHash: 1n,
}); });
} }
@ -23,8 +22,8 @@ export default function createMockedTypeInputPeer(id: string, mockData: MockType
const channel = mockData.channels.find((c) => c.id === id); const channel = mockData.channels.find((c) => c.id === id);
if (channel) { if (channel) {
return new Api.InputPeerChannel({ return new Api.InputPeerChannel({
channelId: BigInt(Number(id) + 1000000000), channelId: -BigInt(id) - CHANNEL_ID_BASE,
accessHash: BigInt(1), accessHash: 1n,
}); });
} }

View File

@ -1,7 +1,6 @@
import BigInt from 'big-integer';
import type { MockTypes } from './MockTypes'; import type { MockTypes } from './MockTypes';
import { CHANNEL_ID_BASE } from '../../../../config';
import Api from '../../tl/api'; import Api from '../../tl/api';
export default function createMockedTypePeer(id: string, mockData: MockTypes): Api.TypePeer { export default function createMockedTypePeer(id: string, mockData: MockTypes): Api.TypePeer {
@ -22,7 +21,7 @@ export default function createMockedTypePeer(id: string, mockData: MockTypes): A
const channel = mockData.channels.find((c) => c.id === id); const channel = mockData.channels.find((c) => c.id === id);
if (channel) { if (channel) {
return new Api.PeerChannel({ return new Api.PeerChannel({
channelId: BigInt(Number(id) + 1000000000), channelId: -BigInt(id) - CHANNEL_ID_BASE,
}); });
} }

View File

@ -1,5 +1,3 @@
import BigInt from 'big-integer';
import type { MockTypes } from './MockTypes'; import type { MockTypes } from './MockTypes';
import Api from '../../tl/api'; import Api from '../../tl/api';
@ -12,7 +10,7 @@ export default function createMockedUser(id: string, mockData: MockTypes): Api.U
const { const {
firstName = 'John', firstName = 'John',
lastName = 'Doe', lastName = 'Doe',
accessHash = BigInt(1), accessHash = 1n,
...rest ...rest
} = user; } = user;

View File

@ -1,13 +1,14 @@
import { toJSNumber } from '../../../../util/numbers';
import Api from '../../tl/api'; import Api from '../../tl/api';
export default function getDocumentIdFromLocation(location: Api.TypeInputFileLocation): number { export default function getDocumentIdFromLocation(location: Api.TypeInputFileLocation): number {
if (location instanceof Api.InputDocumentFileLocation) { if (location instanceof Api.InputDocumentFileLocation) {
return location.id.toJSNumber(); return toJSNumber(location.id);
} }
if (location instanceof Api.InputPhotoFileLocation) { if (location instanceof Api.InputPhotoFileLocation) {
return location.id.toJSNumber(); return toJSNumber(location.id);
} }
throw Error('Unsupported input file location type ' + location.className); throw new Error(`Unsupported input file location type ${location.className}`);
} }

View File

@ -1,8 +1,9 @@
import { CHANNEL_ID_BASE } from '../../../../config';
import Api from '../../tl/api'; import Api from '../../tl/api';
export default function getIdFromInputPeer(peer: Api.TypeInputPeer | Api.TypeInputChannel) { export default function getIdFromInputPeer(peer: Api.TypeInputPeer | Api.TypeInputChannel) {
if (peer instanceof Api.InputPeerChannel || peer instanceof Api.InputChannel) { if (peer instanceof Api.InputPeerChannel || peer instanceof Api.InputChannel) {
return (Number(peer.channelId.toString()) - 1000000000).toString(); return (-peer.channelId - CHANNEL_ID_BASE).toString();
} }
if (peer instanceof Api.InputPeerUser) { if (peer instanceof Api.InputPeerUser) {
@ -10,7 +11,7 @@ export default function getIdFromInputPeer(peer: Api.TypeInputPeer | Api.TypeInp
} }
if (peer instanceof Api.InputPeerChat) { if (peer instanceof Api.InputPeerChat) {
return peer.chatId.toString(); return (-peer.chatId).toString();
} }
throw Error(`Unknown peer type${peer.className}`); throw Error(`Unknown peer type${peer.className}`);

View File

@ -1,5 +1,3 @@
import type BigInt from 'big-integer';
import { BinaryReader } from '../extensions'; import { BinaryReader } from '../extensions';
import { import {
@ -15,9 +13,9 @@ export class AuthKey {
_hash?: Buffer; _hash?: Buffer;
private auxHash?: BigInt.BigInteger; private auxHash?: bigint;
keyId?: BigInt.BigInteger; keyId?: bigint;
constructor(value?: Buffer, hash?: Buffer) { constructor(value?: Buffer, hash?: Buffer) {
if (!hash || !value) { if (!hash || !value) {
@ -55,7 +53,7 @@ export class AuthKey {
} }
async waitForKey() { async waitForKey() {
while (!this.keyId) { while (this.keyId === undefined) {
await sleep(20); await sleep(20);
} }
} }
@ -70,13 +68,13 @@ export class AuthKey {
* Calculates the new nonce hash based on the current class fields' values * Calculates the new nonce hash based on the current class fields' values
* @param newNonce * @param newNonce
* @param number * @param number
* @returns {BigInt.BigInteger} * @returns {bigint}
*/ */
async calcNewNonceHash( async calcNewNonceHash(
newNonce: BigInt.BigInteger, newNonce: bigint,
number: number, number: number,
): Promise<BigInt.BigInteger> { ): Promise<bigint> {
if (!this.auxHash) { if (this.auxHash === undefined) {
throw new Error('Auth key not set'); throw new Error('Auth key not set');
} }

View File

@ -1,18 +1,16 @@
import BigInt from 'big-integer'; import { BigMath, modExp, randBetweenBigInt } from '../Helpers';
import { modExp } from '../Helpers';
export class Factorizator { export class Factorizator {
/** /**
* Calculates the greatest common divisor * Calculates the greatest common divisor
* @param a {BigInteger} * @param a {bigint}
* @param b {BigInteger} * @param b {bigint}
* @returns {BigInteger} * @returns {bigint}
*/ */
static gcd(a: BigInt.BigInteger, b: BigInt.BigInteger) { static gcd(a: bigint, b: bigint) {
while (b.neq(BigInt.zero)) { while (b !== 0n) {
const temp = b; const temp = b;
b = a.remainder(b); b = a % b;
a = temp; a = temp;
} }
return a; return a;
@ -20,57 +18,57 @@ export class Factorizator {
/** /**
* Factorizes the given number and returns both the divisor and the number divided by the divisor * Factorizes the given number and returns both the divisor and the number divided by the divisor
* @param pq {BigInteger} * @param pq {bigint}
* @returns {{p: *, q: *}} * @returns {{p: *, q: *}}
*/ */
static factorize(pq: BigInt.BigInteger) { static factorize(pq: bigint) {
if (pq.remainder(2).equals(BigInt.zero)) { if (pq % 2n === 0n) {
return { p: BigInt(2), q: pq.divide(BigInt(2)) }; return { p: 2n, q: pq / 2n };
} }
let y = BigInt.randBetween(BigInt(1), pq.minus(1)); let y = randBetweenBigInt(1n, pq - 1n);
const c = BigInt.randBetween(BigInt(1), pq.minus(1)); const c = randBetweenBigInt(1n, pq - 1n);
const m = BigInt.randBetween(BigInt(1), pq.minus(1)); const m = randBetweenBigInt(1n, pq - 1n);
let g = BigInt.one; let g = 1n;
let r = BigInt.one; let r = 1n;
let q = BigInt.one; let q = 1n;
let x = BigInt.zero; let x = 0n;
let ys = BigInt.zero; let ys = 0n;
let k; let k: bigint;
while (g.eq(BigInt.one)) { while (g === 1n) {
x = y; x = y;
for (let i = 0; BigInt(i).lesser(r); i++) { for (let i = 0n; i < r; i++) {
y = modExp(y, BigInt(2), pq).add(c).remainder(pq); y = (modExp(y, 2n, pq) + c) % pq;
} }
k = BigInt.zero; k = 0n;
while (k.lesser(r) && g.eq(BigInt.one)) { while (k < r && g === 1n) {
ys = y; ys = y;
const condition = BigInt.min(m, r.minus(k)); const condition = BigMath.min(m, r - k);
for (let i = 0; BigInt(i).lesser(condition); i++) { for (let i = 0n; i < condition; i++) {
y = modExp(y, BigInt(2), pq).add(c).remainder(pq); y = (modExp(y, 2n, pq) + c) % pq;
q = q.multiply(x.minus(y).abs()).remainder(pq); q = (q * BigMath.abs(x - y)) % pq;
} }
g = Factorizator.gcd(q, pq); g = Factorizator.gcd(q, pq);
k = k.add(m); k = k + m;
} }
r = r.multiply(2); r = r * 2n;
} }
if (g.eq(pq)) { if (g === pq) {
while (true) { while (true) {
ys = modExp(ys, BigInt(2), pq).add(c).remainder(pq); ys = (modExp(ys, 2n, pq) + c) % pq;
g = Factorizator.gcd(x.minus(ys).abs(), pq); g = Factorizator.gcd(BigMath.abs(x - ys), pq);
if (g.greater(1)) { if (g > 1n) {
break; break;
} }
} }
} }
const p = g; const p = g;
q = pq.divide(g); q = pq / g;
return p < q ? { p, q } : { p: q, q: p }; return p < q ? { p, q } : { p: q, q: p };
} }
} }

View File

@ -1,5 +1,3 @@
import bigInt from 'big-integer';
import { import {
generateRandomBytes, generateRandomBytes,
modExp, modExp,
@ -10,8 +8,8 @@ import {
export const SERVER_KEYS = [ export const SERVER_KEYS = [
{ {
fingerprint: bigInt('-3414540481677951611'), fingerprint: BigInt('-3414540481677951611'),
n: bigInt( n: BigInt(
'2937959817066933702298617714945612856538843112005886376816255642404751219133084745514657634448776440866' '2937959817066933702298617714945612856538843112005886376816255642404751219133084745514657634448776440866'
+ '1701890505066208632169112269581063774293102577308490531282748465986139880977280302242772832972539403531' + '1701890505066208632169112269581063774293102577308490531282748465986139880977280302242772832972539403531'
+ '3160108704012876427630091361567343395380424193887227773571344877461690935390938502512438971889287359033' + '3160108704012876427630091361567343395380424193887227773571344877461690935390938502512438971889287359033'
@ -22,8 +20,8 @@ export const SERVER_KEYS = [
e: 65537, e: 65537,
}, },
{ {
fingerprint: bigInt('-5595554452916591101'), fingerprint: BigInt('-5595554452916591101'),
n: bigInt( n: BigInt(
'2534288944884041556497168959071347320689884775908477905258202659454602246385394058588521595116849196570' '2534288944884041556497168959071347320689884775908477905258202659454602246385394058588521595116849196570'
+ '8222649399180603818074200620463776135424884632162512403163793083921641631564740959529419359595852941166' + '8222649399180603818074200620463776135424884632162512403163793083921641631564740959529419359595852941166'
+ '8489405859523376133330223960965841179548922160312292373029437018775884567383353986024616752250817918203' + '8489405859523376133330223960965841179548922160312292373029437018775884567383353986024616752250817918203'
@ -34,9 +32,9 @@ export const SERVER_KEYS = [
e: 65537, e: 65537,
}, },
].reduce((acc, { fingerprint, ...keyInfo }) => { ].reduce((acc, { fingerprint, ...keyInfo }) => {
acc.set(fingerprint.toString(), keyInfo); acc.set(fingerprint, keyInfo);
return acc; return acc;
}, new Map<string, { n: bigInt.BigInteger; e: number }>()); }, new Map<bigint, { n: bigint; e: number }>());
/** /**
* Encrypts the given data known the fingerprint to be used * Encrypts the given data known the fingerprint to be used
@ -46,8 +44,8 @@ export const SERVER_KEYS = [
* @param data the data to be encrypted. * @param data the data to be encrypted.
* @returns {Buffer|*|undefined} the cipher text, or undefined if no key matching this fingerprint is found. * @returns {Buffer|*|undefined} the cipher text, or undefined if no key matching this fingerprint is found.
*/ */
export async function encrypt(fingerprint: bigInt.BigInteger, data: Buffer) { export async function encrypt(fingerprint: bigint, data: Buffer) {
const key = SERVER_KEYS.get(fingerprint.toString()); const key = SERVER_KEYS.get(fingerprint);
if (!key) { if (!key) {
return undefined; return undefined;
} }
@ -60,7 +58,7 @@ export async function encrypt(fingerprint: bigInt.BigInteger, data: Buffer) {
// rsa module rsa.encrypt adds 11 bits for padding which we don't want // rsa module rsa.encrypt adds 11 bits for padding which we don't want
// rsa module uses rsa.transform.bytes2int(to_encrypt), easier way: // rsa module uses rsa.transform.bytes2int(to_encrypt), easier way:
const payload = readBigIntFromBuffer(toEncrypt, false); const payload = readBigIntFromBuffer(toEncrypt, false);
const encrypted = modExp(payload, bigInt(key.e), key.n); const encrypted = modExp(payload, BigInt(key.e), key.n);
// rsa module uses transform.int2bytes(encrypted, keylength), easier: // rsa module uses transform.int2bytes(encrypted, keylength), easier:
return readBufferFromBigInt(encrypted, 256, false); return readBufferFromBigInt(encrypted, 256, false);
} }

View File

@ -50,7 +50,7 @@ export default class BinaryReader {
/** /**
* Reads a long integer (8 bytes or 64 bits) value. * Reads a long integer (8 bytes or 64 bits) value.
* @param signed * @param signed
* @returns {BigInteger} * @returns {bigint}
*/ */
readLong(signed = true) { readLong(signed = true) {
return this.readLargeInt(64, signed); return this.readLargeInt(64, signed);
@ -66,7 +66,7 @@ export default class BinaryReader {
/** /**
* Reads a real floating point (8 bytes) value. * Reads a real floating point (8 bytes) value.
* @returns {BigInteger} * @returns {number}
*/ */
readDouble() { readDouble() {
// was this a bug ? it should have been <d // was this a bug ? it should have been <d

View File

@ -1,23 +1,21 @@
import type BigInt from 'big-integer';
import type RequestState from '../network/RequestState'; import type RequestState from '../network/RequestState';
export default class PendingState { export default class PendingState {
_pending: Map<string, RequestState>; _pending: Map<bigint, RequestState>;
constructor() { constructor() {
this._pending = new Map(); this._pending = new Map();
} }
set(msgId: BigInt.BigInteger, state: RequestState) { set(msgId: bigint, state: RequestState) {
this._pending.set(msgId.toString(), state); this._pending.set(msgId, state);
} }
get(msgId: BigInt.BigInteger) { get(msgId: bigint) {
return this._pending.get(msgId.toString()); return this._pending.get(msgId);
} }
getAndDelete(msgId: BigInt.BigInteger) { getAndDelete(msgId: bigint) {
const state = this.get(msgId); const state = this.get(msgId);
this.delete(msgId); this.delete(msgId);
return state; return state;
@ -27,8 +25,8 @@ export default class PendingState {
return Array.from(this._pending.values()); return Array.from(this._pending.values());
} }
delete(msgId: BigInt.BigInteger) { delete(msgId: bigint) {
this._pending.delete(msgId.toString()); return this._pending.delete(msgId);
} }
clear() { clear() {

View File

@ -0,0 +1,247 @@
import {
readBigIntFromBuffer,
readBufferFromBigInt,
toSignedLittleBuffer,
} from './Helpers';
describe('readBigIntFromBuffer', () => {
describe('little endian unsigned', () => {
it('should read 8-byte buffer using native method', () => {
const buffer = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]);
const result = readBigIntFromBuffer(buffer, true, false);
expect(result).toBe(0x0807060504030201n);
});
it('should read 4-byte buffer', () => {
const buffer = Buffer.from([0xFF, 0xFF, 0xFF, 0xFF]);
const result = readBigIntFromBuffer(buffer, true, false);
expect(result).toBe(0xFFFFFFFFn);
});
it('should read 2-byte buffer', () => {
const buffer = Buffer.from([0x34, 0x12]);
const result = readBigIntFromBuffer(buffer, true, false);
expect(result).toBe(0x1234n);
});
});
describe('big endian unsigned', () => {
it('should read 8-byte buffer using native method', () => {
const buffer = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]);
const result = readBigIntFromBuffer(buffer, false, false);
expect(result).toBe(0x0102030405060708n);
});
it('should read 4-byte buffer', () => {
const buffer = Buffer.from([0x12, 0x34, 0x56, 0x78]);
const result = readBigIntFromBuffer(buffer, false, false);
expect(result).toBe(0x12345678n);
});
});
describe('signed values', () => {
it('should read positive signed 8-byte value', () => {
const buffer = Buffer.from([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
const result = readBigIntFromBuffer(buffer, true, true);
expect(result).toBe(1n);
});
it('should read negative signed 8-byte value', () => {
const buffer = Buffer.from([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]);
const result = readBigIntFromBuffer(buffer, true, true);
expect(result).toBe(-1n);
});
it('should read negative signed 4-byte value', () => {
const buffer = Buffer.from([0xFF, 0xFF, 0xFF, 0xFF]);
const result = readBigIntFromBuffer(buffer, true, true);
expect(result).toBe(-1n);
});
it('should read max signed 8-byte value', () => {
const buffer = Buffer.from([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F]);
const result = readBigIntFromBuffer(buffer, true, true);
expect(result).toBe(0x7FFFFFFFFFFFFFFFn);
});
it('should read min signed 8-byte value', () => {
const buffer = Buffer.from([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]);
const result = readBigIntFromBuffer(buffer, true, true);
expect(result).toBe(-0x8000000000000000n);
});
});
});
describe('toSignedLittleBuffer', () => {
describe('8-byte buffers (native method)', () => {
it('should convert positive value', () => {
const buffer = toSignedLittleBuffer(0x0102030405060708n, 8);
expect(buffer).toEqual(Buffer.from([0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]));
});
it('should convert negative value', () => {
const buffer = toSignedLittleBuffer(-1n, 8);
expect(buffer).toEqual(Buffer.from([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]));
});
it('should convert zero', () => {
const buffer = toSignedLittleBuffer(0n, 8);
expect(buffer).toEqual(Buffer.from([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]));
});
it('should convert max signed 64-bit value', () => {
const buffer = toSignedLittleBuffer(0x7FFFFFFFFFFFFFFFn, 8);
expect(buffer).toEqual(Buffer.from([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F]));
});
it('should convert min signed 64-bit value', () => {
const buffer = toSignedLittleBuffer(-0x8000000000000000n, 8);
expect(buffer).toEqual(Buffer.from([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]));
});
});
describe('non-8-byte buffers', () => {
it('should convert 4-byte value', () => {
const buffer = toSignedLittleBuffer(0x12345678n, 4);
expect(buffer).toEqual(Buffer.from([0x78, 0x56, 0x34, 0x12]));
});
it('should convert 2-byte value', () => {
const buffer = toSignedLittleBuffer(0x1234n, 2);
expect(buffer).toEqual(Buffer.from([0x34, 0x12]));
});
it('should convert 16-byte value', () => {
const buffer = toSignedLittleBuffer(0x0102030405060708090A0B0C0D0E0F10n, 16);
expect(buffer).toEqual(Buffer.from([
0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09,
0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
]));
});
it('should handle negative values for non-8-byte buffers', () => {
const buffer = toSignedLittleBuffer(-1n, 4);
expect(buffer).toEqual(Buffer.from([0xFF, 0xFF, 0xFF, 0xFF]));
});
});
});
describe('readBufferFromBigInt', () => {
describe('8-byte buffers (native methods)', () => {
it('should write unsigned little endian', () => {
const buffer = readBufferFromBigInt(0x0102030405060708n, 8, true, false);
expect(buffer).toEqual(Buffer.from([0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]));
});
it('should write unsigned big endian', () => {
const buffer = readBufferFromBigInt(0x0102030405060708n, 8, false, false);
expect(buffer).toEqual(Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]));
});
it('should write signed positive little endian', () => {
const buffer = readBufferFromBigInt(1234567890n, 8, true, true);
const read = readBigIntFromBuffer(buffer, true, true);
expect(read).toBe(1234567890n);
});
it('should write signed negative little endian', () => {
const buffer = readBufferFromBigInt(-1n, 8, true, true);
expect(buffer).toEqual(Buffer.from([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]));
});
it('should write signed negative big endian', () => {
const buffer = readBufferFromBigInt(-1n, 8, false, true);
expect(buffer).toEqual(Buffer.from([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]));
});
});
describe('non-8-byte buffers', () => {
it('should write 4-byte unsigned little endian', () => {
const buffer = readBufferFromBigInt(0x12345678n, 4, true, false);
expect(buffer).toEqual(Buffer.from([0x78, 0x56, 0x34, 0x12]));
});
it('should write 4-byte unsigned big endian', () => {
const buffer = readBufferFromBigInt(0x12345678n, 4, false, false);
expect(buffer).toEqual(Buffer.from([0x12, 0x34, 0x56, 0x78]));
});
it('should write 2-byte value', () => {
const buffer = readBufferFromBigInt(0x1234n, 2, true, false);
expect(buffer).toEqual(Buffer.from([0x34, 0x12]));
});
it('should write 16-byte value', () => {
const buffer = readBufferFromBigInt(0x0102030405060708090A0B0C0D0E0F10n, 16, false, false);
expect(buffer).toEqual(Buffer.from([
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
]));
});
it('should write signed negative 4-byte value', () => {
const buffer = readBufferFromBigInt(-1n, 4, true, true);
expect(buffer).toEqual(Buffer.from([0xFF, 0xFF, 0xFF, 0xFF]));
});
it('should write signed negative 4-byte value big endian', () => {
const buffer = readBufferFromBigInt(-1n, 4, false, true);
expect(buffer).toEqual(Buffer.from([0xFF, 0xFF, 0xFF, 0xFF]));
});
it('should pad with zeros for smaller values', () => {
const buffer = readBufferFromBigInt(0xFFn, 4, true, false);
expect(buffer).toEqual(Buffer.from([0xFF, 0x00, 0x00, 0x00]));
});
});
describe('error handling', () => {
it('should throw when converting negative to unsigned', () => {
expect(() => readBufferFromBigInt(-1n, 8, true, false))
.toThrow('Cannot convert negative to unsigned');
});
it('should throw when value is too large for buffer', () => {
const largeValue = 0x1FFFFFFFFFFFFFFFFn; // Too large for 8 bytes
expect(() => readBufferFromBigInt(largeValue, 8, true, false))
.toThrow('Value 36893488147419103231 does not fit in 8 unsigned bytes');
});
});
describe('round-trip consistency', () => {
it('should maintain value through read/write cycle (unsigned)', () => {
const original = 0x123456789ABCDEFn;
const buffer = readBufferFromBigInt(original, 8, true, false);
const result = readBigIntFromBuffer(buffer, true, false);
expect(result).toBe(original);
});
it('should maintain value through read/write cycle (signed positive)', () => {
const original = 0x123456789ABCDEFn;
const buffer = readBufferFromBigInt(original, 8, true, true);
const result = readBigIntFromBuffer(buffer, true, true);
expect(result).toBe(original);
});
it('should maintain value through read/write cycle (signed negative)', () => {
const original = -0x123456789ABCDEFn;
const buffer = readBufferFromBigInt(original, 8, true, true);
const result = readBigIntFromBuffer(buffer, true, true);
expect(result).toBe(original);
});
it('should maintain value through read/write cycle (big endian)', () => {
const original = 0x123456789ABCDEFn;
const buffer = readBufferFromBigInt(original, 8, false, false);
const result = readBigIntFromBuffer(buffer, false, false);
expect(result).toBe(original);
});
it('should maintain value for 4-byte buffers', () => {
const original = 0x12345678n;
const buffer = readBufferFromBigInt(original, 4, true, false);
const result = readBigIntFromBuffer(buffer, true, false);
expect(result).toBe(original);
});
});
});

View File

@ -5,8 +5,6 @@
* @returns {Promise<{authKey: *, timeOffset: *}>} * @returns {Promise<{authKey: *, timeOffset: *}>}
*/ */
import bigInt from 'big-integer';
import type MTProtoPlainSender from './MTProtoPlainSender'; import type MTProtoPlainSender from './MTProtoPlainSender';
import { IGE } from '../crypto/IGE'; import { IGE } from '../crypto/IGE';
@ -43,7 +41,7 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
if (!(resPQ instanceof Api.ResPQ)) { if (!(resPQ instanceof Api.ResPQ)) {
throw new SecurityError(`Step 1 answer was ${resPQ}`); throw new SecurityError(`Step 1 answer was ${resPQ}`);
} }
if (resPQ.nonce.neq(nonce)) { if (resPQ.nonce !== nonce) {
throw new SecurityError('Step 1 invalid nonce from server'); throw new SecurityError('Step 1 invalid nonce from server');
} }
const pq = readBigIntFromBuffer(resPQ.pq, false, true); const pq = readBigIntFromBuffer(resPQ.pq, false, true);
@ -70,7 +68,7 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
let targetFingerprint; let targetFingerprint;
let targetKey; let targetKey;
for (const fingerprint of resPQ.serverPublicKeyFingerprints) { for (const fingerprint of resPQ.serverPublicKeyFingerprints) {
targetKey = SERVER_KEYS.get(fingerprint.toString()); targetKey = SERVER_KEYS.get(fingerprint);
if (targetKey !== undefined) { if (targetKey !== undefined) {
targetFingerprint = fingerprint; targetFingerprint = fingerprint;
break; break;
@ -98,11 +96,11 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
const keyAesEncrypted = Buffer.concat([tempKeyXor, aesEncrypted]); const keyAesEncrypted = Buffer.concat([tempKeyXor, aesEncrypted]);
const keyAesEncryptedInt = readBigIntFromBuffer(keyAesEncrypted, false, false); const keyAesEncryptedInt = readBigIntFromBuffer(keyAesEncrypted, false, false);
if (keyAesEncryptedInt.greaterOrEquals(targetKey.n)) { if (keyAesEncryptedInt >= targetKey.n) {
log.debug('Aes key greater than RSA. retrying'); log.debug('Aes key greater than RSA. retrying');
continue; continue;
} }
const encryptedDataBuffer = modExp(keyAesEncryptedInt, bigInt(targetKey.e), targetKey.n); const encryptedDataBuffer = modExp(keyAesEncryptedInt, BigInt(targetKey.e), targetKey.n);
encryptedData = readBufferFromBigInt(encryptedDataBuffer, 256, false, false); encryptedData = readBufferFromBigInt(encryptedDataBuffer, 256, false, false);
break; break;
@ -133,11 +131,11 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
) { ) {
throw new Error(`Step 2.1 answer was ${serverDhParams}`); throw new Error(`Step 2.1 answer was ${serverDhParams}`);
} }
if (serverDhParams.nonce.neq(resPQ.nonce)) { if (serverDhParams.nonce !== resPQ.nonce) {
throw new SecurityError('Step 2 invalid nonce from server'); throw new SecurityError('Step 2 invalid nonce from server');
} }
if (serverDhParams.serverNonce.neq(resPQ.serverNonce)) { if (serverDhParams.serverNonce !== resPQ.serverNonce) {
throw new SecurityError('Step 2 invalid server nonce from server'); throw new SecurityError('Step 2 invalid server nonce from server');
} }
@ -146,7 +144,7 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
toSignedLittleBuffer(newNonce, 32).slice(4, 20), toSignedLittleBuffer(newNonce, 32).slice(4, 20),
); );
const nnh = readBigIntFromBuffer(sh, true, true); const nnh = readBigIntFromBuffer(sh, true, true);
if (serverDhParams.newNonceHash.neq(nnh)) { if (serverDhParams.newNonceHash !== nnh) {
throw new SecurityError('Step 2 invalid DH fail nonce from server'); throw new SecurityError('Step 2 invalid DH fail nonce from server');
} }
} }
@ -178,10 +176,10 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
throw new SecurityError('Step 3 Invalid hash answer'); throw new SecurityError('Step 3 Invalid hash answer');
} }
if (serverDhInner.nonce.neq(resPQ.nonce)) { if (serverDhInner.nonce !== resPQ.nonce) {
throw new SecurityError('Step 3 Invalid nonce in encrypted answer'); throw new SecurityError('Step 3 Invalid nonce in encrypted answer');
} }
if (serverDhInner.serverNonce.neq(resPQ.serverNonce)) { if (serverDhInner.serverNonce !== resPQ.serverNonce) {
throw new SecurityError( throw new SecurityError(
'Step 3 Invalid server nonce in encrypted answer', 'Step 3 Invalid server nonce in encrypted answer',
); );
@ -207,26 +205,26 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
false, false,
false, false,
); );
const gb = modExp(bigInt(serverDhInner.g), b, dhPrime); const gb = modExp(BigInt(serverDhInner.g), b, dhPrime);
const gab = modExp(ga, b, dhPrime); const gab = modExp(ga, b, dhPrime);
if (ga.lesserOrEquals(1)) { if (ga <= 1n) {
throw new SecurityError('Step 3 failed ga > 1 check'); throw new SecurityError('Step 3 failed ga > 1 check');
} }
if (gb.lesserOrEquals(1)) { if (gb <= 1n) {
throw new SecurityError('Step 3 failed gb > 1 check'); throw new SecurityError('Step 3 failed gb > 1 check');
} }
if (ga.greater(dhPrime.minus(1))) { if (ga >= (dhPrime - 1n)) {
throw new SecurityError('Step 3 failed ga > dh_prime - 1 check'); throw new SecurityError('Step 3 failed ga < dh_prime - 1 check');
} }
const toCheckAgainst = bigInt(2).pow(2048 - 64); const toCheckAgainst = 2n ** (2048n - 64n);
if (!(ga.greaterOrEquals(toCheckAgainst) && ga.lesserOrEquals(dhPrime.minus(toCheckAgainst)))) { if (!(ga > toCheckAgainst && ga < (dhPrime - toCheckAgainst))) {
throw new SecurityError('Step 3 failed dh_prime - 2^{2048-64} < ga < 2^{2048-64} check'); throw new SecurityError('Step 3 failed dh_prime - 2^{2048-64} < ga < 2^{2048-64} check');
} }
if (!(gb.greaterOrEquals(toCheckAgainst) && gb.lesserOrEquals(dhPrime.minus(toCheckAgainst)))) { if (!(gb > toCheckAgainst && gb < (dhPrime - toCheckAgainst))) {
throw new SecurityError('Step 3 failed dh_prime - 2^{2048-64} < gb < 2^{2048-64} check'); throw new SecurityError('Step 3 failed dh_prime - 2^{2048-64} < gb < 2^{2048-64} check');
} }
@ -234,7 +232,7 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
const clientDhInner = new Api.ClientDHInnerData({ const clientDhInner = new Api.ClientDHInnerData({
nonce: resPQ.nonce, nonce: resPQ.nonce,
serverNonce: resPQ.serverNonce, serverNonce: resPQ.serverNonce,
retryId: bigInt.zero, // TODO Actual retry ID retryId: 0n, // TODO Actual retry ID
gB: getByteArray(gb, false), gB: getByteArray(gb, false),
}).getBytes(); }).getBytes();
@ -265,10 +263,10 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
throw new Error(`Step 3.1 answer was ${dhGen}`); throw new Error(`Step 3.1 answer was ${dhGen}`);
} }
const { name } = dhGen.constructor; const { name } = dhGen.constructor;
if (dhGen.nonce.neq(resPQ.nonce)) { if (dhGen.nonce !== resPQ.nonce) {
throw new SecurityError(`Step 3 invalid ${name} nonce from server`); throw new SecurityError(`Step 3 invalid ${name} nonce from server`);
} }
if (dhGen.serverNonce.neq(resPQ.serverNonce)) { if (dhGen.serverNonce !== resPQ.serverNonce) {
throw new SecurityError( throw new SecurityError(
`Step 3 invalid ${name} server nonce from server`, `Step 3 invalid ${name} server nonce from server`,
); );
@ -279,10 +277,10 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
const nonceNumber = 1 + nonceTypesString.indexOf(dhGen.className); const nonceNumber = 1 + nonceTypesString.indexOf(dhGen.className);
const newNonceHash = await authKey.calcNewNonceHash(newNonce, nonceNumber); const newNonceHash = await authKey.calcNewNonceHash(newNonce, nonceNumber);
// @ts-ignore // @ts-expect-error
const dhHash = dhGen[`newNonceHash${nonceNumber}`]; const dhHash = dhGen[`newNonceHash${nonceNumber}`] as bigint;
if (dhHash.neq(newNonceHash)) { if (dhHash !== newNonceHash) {
throw new SecurityError('Step 3 invalid new nonce hash'); throw new SecurityError('Step 3 invalid new nonce hash');
} }

View File

@ -2,7 +2,6 @@
* This module contains the class used to communicate with Telegram's servers * This module contains the class used to communicate with Telegram's servers
* in plain text, when no authorization key has been created yet. * in plain text, when no authorization key has been created yet.
*/ */
import BigInt from 'big-integer';
import type { Logger } from '../extensions'; import type { Logger } from '../extensions';
import type { Api } from '../tl'; import type { Api } from '../tl';
@ -53,11 +52,11 @@ export default class MTProtoPlainSender {
} }
const reader = new BinaryReader(body); const reader = new BinaryReader(body);
const authKeyId = reader.readLong(); const authKeyId = reader.readLong();
if (authKeyId.neq(BigInt(0))) { if (authKeyId !== 0n) {
throw new Error('Bad authKeyId'); throw new Error('Bad authKeyId');
} }
msgId = reader.readLong(); msgId = reader.readLong();
if (msgId.eq(BigInt(0))) { if (msgId === 0n) {
throw new Error('Bad msgId'); throw new Error('Bad msgId');
} }
/** ^ We should make sure that the read ``msg_id`` is greater /** ^ We should make sure that the read ``msg_id`` is greater

View File

@ -1,5 +1,3 @@
import type BigInt from 'big-integer';
import type { TLMessage } from '../tl/core'; import type { TLMessage } from '../tl/core';
import { RPCError, RPCMessageToError } from '../errors'; import { RPCError, RPCMessageToError } from '../errors';
@ -18,7 +16,7 @@ import {
BadMessageError, InvalidBufferError, SecurityError, TypeNotFoundError, BadMessageError, InvalidBufferError, SecurityError, TypeNotFoundError,
} from '../errors/Common'; } from '../errors/Common';
import PendingState from '../extensions/PendingState'; import PendingState from '../extensions/PendingState';
import { sleep } from '../Helpers'; import { jsonStringifyWithBigInt, sleep } from '../Helpers';
import MessageContainer from '../tl/core/MessageContainer'; import MessageContainer from '../tl/core/MessageContainer';
import { doAuthentication } from './Authenticator'; import { doAuthentication } from './Authenticator';
import MtProtoPlainSender from './MTProtoPlainSender'; import MtProtoPlainSender from './MTProtoPlainSender';
@ -823,16 +821,16 @@ export default class MTProtoSender {
* @returns {*[]} * @returns {*[]}
* @private * @private
*/ */
_popStates(msgId: BigInt.BigInteger) { _popStates(msgId: bigint) {
const state = this._pendingState.getAndDelete(msgId); const state = this._pendingState.getAndDelete(msgId);
if (state) { if (state) {
return [state]; return [state];
} }
const toPop: BigInt.BigInteger[] = []; const toPop: bigint[] = [];
for (const pendingState of this._pendingState.values()) { for (const pendingState of this._pendingState.values()) {
if (pendingState.containerId?.equals(msgId)) { if (pendingState.containerId === msgId) {
toPop.push(pendingState.msgId!); toPop.push(pendingState.msgId!);
} }
} }
@ -1005,7 +1003,7 @@ export default class MTProtoSender {
_handleBadNotification(message: TLMessage) { _handleBadNotification(message: TLMessage) {
const badMsg = message.obj; const badMsg = message.obj;
const states = this._popStates(badMsg.badMsgId); const states = this._popStates(badMsg.badMsgId);
this._log.debug(`Handling bad msg ${JSON.stringify(badMsg)}`); this._log.debug(`Handling bad msg ${jsonStringifyWithBigInt(badMsg)}`);
if ([16, 17].includes(badMsg.errorCode)) { if ([16, 17].includes(badMsg.errorCode)) {
// Sent msg_id too low or too high (respectively). // Sent msg_id too low or too high (respectively).
// Use the current msg_id to determine the right time offset. // Use the current msg_id to determine the right time offset.

View File

@ -1,5 +1,3 @@
import BigInt from 'big-integer';
import type { AuthKey } from '../crypto/AuthKey'; import type { AuthKey } from '../crypto/AuthKey';
import { CTR } from '../crypto/CTR'; import { CTR } from '../crypto/CTR';
@ -27,9 +25,9 @@ export default class MTProtoState {
timeOffset: number; timeOffset: number;
salt: bigInt.BigInteger; salt: bigint;
private id: bigInt.BigInteger; private id: bigint;
_sequence: number; _sequence: number;
@ -37,7 +35,7 @@ export default class MTProtoState {
_isOutgoing: boolean; _isOutgoing: boolean;
private _lastMsgId: bigInt.BigInteger; private _lastMsgId: bigint;
private msgIds: string[]; private msgIds: string[];
@ -74,11 +72,11 @@ export default class MTProtoState {
this._isCall = isCall; this._isCall = isCall;
this._isOutgoing = isOutgoing; this._isOutgoing = isOutgoing;
this.timeOffset = 0; this.timeOffset = 0;
this.salt = BigInt.zero; this.salt = 0n;
this.id = BigInt.zero; this.id = 0n;
this._sequence = 0; this._sequence = 0;
this._lastMsgId = BigInt.zero; this._lastMsgId = 0n;
this.msgIds = []; this.msgIds = [];
this.reset(); this.reset();
} }
@ -90,7 +88,7 @@ export default class MTProtoState {
// Session IDs can be random on every connection // Session IDs can be random on every connection
this.id = generateRandomLong(true); this.id = generateRandomLong(true);
this._sequence = 0; this._sequence = 0;
this._lastMsgId = BigInt(0); this._lastMsgId = 0n;
this.msgIds = []; this.msgIds = [];
} }
@ -142,11 +140,13 @@ export default class MTProtoState {
* @param contentRelated * @param contentRelated
* @param afterId * @param afterId
*/ */
async writeDataAsMessage(buffer: BinaryWriter, data: Buffer, contentRelated: boolean, afterId?: BigInt.BigInteger) { async writeDataAsMessage(
buffer: BinaryWriter, data: Buffer, contentRelated: boolean, afterId?: bigint,
): Promise<bigint> {
const msgId = this._getNewMsgId(); const msgId = this._getNewMsgId();
const seqNo = this._getSeqNo(contentRelated); const seqNo = this._getSeqNo(contentRelated);
let body; let body;
if (!afterId) { if (afterId === undefined) {
body = await GZIPPacked.gzipIfSmaller(contentRelated, data); body = await GZIPPacked.gzipIfSmaller(contentRelated, data);
} else { } else {
// Invoke query expects a query with a getBytes func // Invoke query expects a query with a getBytes func
@ -185,7 +185,7 @@ export default class MTProtoState {
throw new Error('Auth key unset'); throw new Error('Auth key unset');
} }
if (!this.salt || !this.id || !authKey || !this.authKey.keyId) { if (this.salt === undefined || this.authKey.keyId === undefined) {
throw new Error('Unset params'); throw new Error('Unset params');
} }
@ -255,7 +255,7 @@ export default class MTProtoState {
if (!this._isCall) { if (!this._isCall) {
const keyId = readBigIntFromBuffer(body.slice(0, 8)); const keyId = readBigIntFromBuffer(body.slice(0, 8));
if (!this.authKey.keyId || keyId.neq(this.authKey.keyId)) { if (keyId !== this.authKey.keyId) {
throw new SecurityError('Server replied with an invalid auth key'); throw new SecurityError('Server replied with an invalid auth key');
} }
} }
@ -304,7 +304,7 @@ export default class MTProtoState {
} else { } else {
reader.readLong(); // removeSalt reader.readLong(); // removeSalt
const serverId = reader.readLong(); const serverId = reader.readLong();
if (!serverId.eq(this.id)) { if (serverId !== this.id) {
throw new SecurityError('Server replied with a wrong session ID'); throw new SecurityError('Server replied with a wrong session ID');
} }
@ -358,11 +358,9 @@ export default class MTProtoState {
_getNewMsgId() { _getNewMsgId() {
const now = Date.now() / 1000 + this.timeOffset; const now = Date.now() / 1000 + this.timeOffset;
const nanoseconds = Math.floor((now - Math.floor(now)) * 1e9); const nanoseconds = Math.floor((now - Math.floor(now)) * 1e9);
let newMsgId = (BigInt(Math.floor(now)) let newMsgId = (BigInt(Math.floor(now)) << 32n) | (BigInt(nanoseconds) << 2n);
.shiftLeft(BigInt(32))).or(BigInt(nanoseconds) if (this._lastMsgId >= newMsgId) {
.shiftLeft(BigInt(2))); newMsgId = this._lastMsgId + 4n;
if (this._lastMsgId.greaterOrEquals(newMsgId)) {
newMsgId = this._lastMsgId.add(BigInt(4));
} }
this._lastMsgId = newMsgId; this._lastMsgId = newMsgId;
return newMsgId; return newMsgId;
@ -371,28 +369,28 @@ export default class MTProtoState {
/** /**
* Returns the understood time by the message id (server time + local offset) * Returns the understood time by the message id (server time + local offset)
*/ */
getMsgIdTimeLocal(msgId: BigInt.BigInteger) { getMsgIdTimeLocal(msgId: bigint) {
if (this._lastMsgId.eq(0)) { if (this._lastMsgId === 0n) {
// this means it's the first message sent/received so don't check yet // this means it's the first message sent/received so don't check yet
return false; return undefined;
} }
return msgId.shiftRight(BigInt(32)).toJSNumber() - this.timeOffset; return Number(msgId >> 32n) - this.timeOffset;
} }
/** /**
* Updates the time offset to the correct * Updates the time offset to the correct
* one given a known valid message ID. * one given a known valid message ID.
* @param correctMsgId {BigInteger} * @param correctMsgId {bigint}
*/ */
updateTimeOffset(correctMsgId: BigInt.BigInteger) { updateTimeOffset(correctMsgId: bigint) {
const bad = this._getNewMsgId(); const bad = this._getNewMsgId();
const old = this.timeOffset; const old = this.timeOffset;
const now = Math.floor(Date.now() / 1000); const now = Math.floor(Date.now() / 1000);
const correct = correctMsgId.shiftRight(BigInt(32)).toJSNumber(); const correct = Number(correctMsgId >> 32n);
this.timeOffset = correct - now; this.timeOffset = correct - now;
if (this.timeOffset !== old) { if (this.timeOffset !== old) {
this._lastMsgId = BigInt(0); this._lastMsgId = 0n;
this._log.debug( this._log.debug(
// eslint-disable-next-line @stylistic/max-len // eslint-disable-next-line @stylistic/max-len
`Updated time offset (old offset ${old}, bad ${bad.toString()}, good ${correctMsgId.toString()}, new ${this.timeOffset})`, `Updated time offset (old offset ${old}, bad ${bad.toString()}, good ${correctMsgId.toString()}, new ${this.timeOffset})`,

View File

@ -1,5 +1,3 @@
import type BigInt from 'big-integer';
import type { Api } from '../tl'; import type { Api } from '../tl';
import Deferred from '../../../util/Deferred'; import Deferred from '../../../util/Deferred';
@ -8,9 +6,9 @@ export type CallableRequest = Api.AnyRequest | Api.MsgsAck | Api.MsgsStateInfo |
type RequestResponse<T> = T extends { __response: infer R } ? R : void; type RequestResponse<T> = T extends { __response: infer R } ? R : void;
export default class RequestState<T extends CallableRequest = CallableRequest> { export default class RequestState<T extends CallableRequest = CallableRequest> {
public containerId?: BigInt.BigInteger; public containerId?: bigint;
public msgId?: BigInt.BigInteger; public msgId?: bigint;
public request: any; public request: any;

View File

@ -1,5 +1,3 @@
import BigInt from 'big-integer';
import type { PromisedWebSockets } from '../../extensions'; import type { PromisedWebSockets } from '../../extensions';
import { readBufferFromBigInt } from '../../Helpers'; import { readBufferFromBigInt } from '../../Helpers';

View File

@ -3,7 +3,7 @@ export abstract class MTProtoRequest {
private sequence: number; private sequence: number;
private msgId: number; private msgId: bigint;
private readonly dirty: boolean; private readonly dirty: boolean;
@ -19,7 +19,7 @@ export abstract class MTProtoRequest {
constructor() { constructor() {
this.sent = false; this.sent = false;
this.msgId = 0; // long this.msgId = 0n; // long
this.sequence = 0; this.sequence = 0;
this.dirty = false; this.dirty = false;

View File

@ -1,8 +1,6 @@
// This file is autogenerated. All changes will be overwritten. // This file is autogenerated. All changes will be overwritten.
import { BigInteger } from 'big-integer';
export default Api; export default Api;
namespace Api { namespace Api {
@ -21,9 +19,9 @@ namespace Api {
type Bool = boolean; type Bool = boolean;
type int = number; type int = number;
type double = number; type double = number;
type int128 = BigInteger; type int128 = bigint;
type int256 = BigInteger; type int256 = bigint;
type long = BigInteger; type long = bigint;
type bytes = Buffer; type bytes = Buffer;
class VirtualClass<Args extends AnyLiteral> { class VirtualClass<Args extends AnyLiteral> {

View File

@ -1,4 +1,3 @@
import type BigInt from 'big-integer';
import type { BinaryReader } from '../../extensions'; import type { BinaryReader } from '../../extensions';
@ -13,7 +12,7 @@ export default class RPCResult {
private CONSTRUCTOR_ID: number; private CONSTRUCTOR_ID: number;
private reqMsgId: BigInt.BigInteger; private reqMsgId: bigint;
private body?: Buffer; private body?: Buffer;
@ -22,7 +21,7 @@ export default class RPCResult {
private classType: string; private classType: string;
constructor( constructor(
reqMsgId: BigInt.BigInteger, reqMsgId: bigint,
body?: Buffer, body?: Buffer,
error?: Api.RpcError, error?: Api.RpcError,
) { ) {

View File

@ -1,17 +1,15 @@
import type BigInt from 'big-integer';
export default class TLMessage { export default class TLMessage {
static SIZE_OVERHEAD = 12; static SIZE_OVERHEAD = 12;
static classType = 'constructor'; static classType = 'constructor';
msgId: BigInt.BigInteger; msgId: bigint;
private seqNo: number; private seqNo: number;
obj: any; obj: any;
constructor(msgId: bigInt.BigInteger, seqNo: number, obj: any) { constructor(msgId: bigint, seqNo: number, obj: any) {
this.msgId = msgId; this.msgId = msgId;
this.seqNo = seqNo; this.seqNo = seqNo;
this.obj = obj; this.obj = obj;

View File

@ -158,8 +158,6 @@ ${indent}}`.trim();
return ` return `
// This file is autogenerated. All changes will be overwritten. // This file is autogenerated. All changes will be overwritten.
import { BigInteger } from 'big-integer';
export default Api; export default Api;
namespace Api { namespace Api {
@ -178,9 +176,9 @@ namespace Api {
type Bool = boolean; type Bool = boolean;
type int = number; type int = number;
type double = number; type double = number;
type int128 = BigInteger; type int128 = bigint;
type int256 = BigInteger; type int256 = bigint;
type long = BigInteger; type long = bigint;
type bytes = Buffer; type bytes = Buffer;
class VirtualClass<Args extends AnyLiteral> { class VirtualClass<Args extends AnyLiteral> {

View File

@ -8,7 +8,7 @@ export type FullEntity =
| Api.ChannelFull; | Api.ChannelFull;
export type EntityLike = export type EntityLike =
| bigInt.BigInteger | bigint
| string | string
| Api.TypePeer | Api.TypePeer
| Api.TypeInputPeer | Api.TypeInputPeer

View File

@ -1,26 +1,33 @@
import { CHANNEL_ID_BASE } from '../../config'; import { CHANNEL_ID_BASE } from '../../config';
import { toJSNumber } from '../numbers';
export function isUserId(entityId: string) { export function isUserId(entityId: string) {
return !entityId.startsWith('-'); return !entityId.startsWith('-');
} }
export function isChannelId(entityId: string) { export function isChannelId(entityId: string) {
const n = Number(entityId); const n = BigInt(entityId);
return n < -CHANNEL_ID_BASE; return n < -CHANNEL_ID_BASE;
} }
export function toChannelId(mtpId: string) { export function toChannelId(mtpId: string) {
const n = Number(mtpId); const n = BigInt(mtpId);
return String(-CHANNEL_ID_BASE - n); return String(-CHANNEL_ID_BASE - n);
} }
export function getCleanPeerId(peerId: string) { export function getRawPeerId(id: string) {
return isChannelId(peerId) const n = BigInt(id);
// Remove -1 and leading zeros if (isUserId(id)) {
? peerId.replace(/^-10+/, '') return n;
: peerId.replace('-', ''); }
if (isChannelId(id)) {
return -n - CHANNEL_ID_BASE;
}
return n * -1n;
} }
export function getPeerIdDividend(peerId: string) { export function getPeerIdDividend(peerId: string) {
return Math.abs(Number(getCleanPeerId(peerId))); return toJSNumber(getRawPeerId(peerId));
} }

27
src/util/numbers.ts Normal file
View File

@ -0,0 +1,27 @@
import { DEBUG } from '../config';
export function toJSNumber(value: undefined): undefined;
export function toJSNumber(value: bigint): number;
export function toJSNumber(value?: bigint): number | undefined;
export function toJSNumber(value?: bigint): number | undefined {
if (value === undefined) return undefined;
if (DEBUG && (value < Number.MIN_SAFE_INTEGER || value > Number.MAX_SAFE_INTEGER)) {
// eslint-disable-next-line no-console
console.error('Unsafe BigInt conversion', value);
}
return Number(value);
}
export function tryParseBigInt(value: string): bigint | undefined {
try {
return BigInt(value);
} catch (error) {
if (DEBUG) {
// eslint-disable-next-line no-console
console.error('Error parsing BigInt', value, error);
}
return undefined;
}
}