[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-updater": "^2.9.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",
"idb-keyval": "^6.2.2",
"lowlight": "^3.3.0",
@ -7330,14 +7330,6 @@
"dev": true,
"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": {
"version": "2.3.0",
"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-updater": "^2.9.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",
"idb-keyval": "^6.2.2",
"lowlight": "^3.3.0",

View File

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

View File

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

View File

@ -24,6 +24,7 @@ import type {
} from '../../types';
import { pickTruthy } from '../../../util/iteratees';
import { toJSNumber } from '../../../util/numbers';
import { getServerTimeOffset } from '../../../util/serverTime';
import { addPhotoToLocalDb, addUserToLocalDb } from '../helpers/localDb';
import { serializeBytes } from '../helpers/misc';
@ -113,7 +114,8 @@ function buildApiChatFieldsFromPeerEntity(
isJoinRequest: channel?.joinRequest,
isForum: channel?.forum,
isMonoforum: channel?.monoforum,
linkedMonoforumId: channel?.linkedMonoforumId && buildApiPeerId(channel.linkedMonoforumId, 'channel'),
linkedMonoforumId: channel?.linkedMonoforumId !== undefined
? buildApiPeerId(channel.linkedMonoforumId, 'channel') : undefined,
areChannelMessagesAllowed: channel?.broadcastMessagesAllowed,
areStoriesHidden,
maxStoryId,
@ -123,7 +125,7 @@ function buildApiChatFieldsFromPeerEntity(
botVerificationIconId,
hasGeo: channel?.hasGeo,
subscriptionUntil: channel?.subscriptionUntilDate,
paidMessagesStars: paidMessagesStars?.toJSNumber(),
paidMessagesStars: toJSNumber(paidMessagesStars),
level: channel?.level,
hasAutoTranslation: channel?.autotranslation,
withForumTabs: channel?.forumTabs,
@ -713,7 +715,7 @@ export function buildApiStarsSubscriptionPricing(
): ApiStarsSubscriptionPricing {
return {
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 type {
@ -14,6 +13,7 @@ import type {
} from '../../types';
import { numberToHexColor } from '../../../util/colors';
import { toJSNumber } from '../../../util/numbers';
import { buildApiChatFromPreview } from '../apiBuilders/chats';
import { addDocumentToLocalDb } from '../helpers/localDb';
import { buildApiFormattedText } from './common';
@ -47,7 +47,7 @@ export function buildApiStarGift(starGift: GramJs.TypeStarGift): ApiStarGift {
requirePremium,
resaleTonOnly,
valueCurrency,
valueAmount: valueAmount?.toJSNumber(),
valueAmount: toJSNumber(valueAmount),
regularGiftId: giftId.toString(),
};
}
@ -67,19 +67,19 @@ export function buildApiStarGift(starGift: GramJs.TypeStarGift): ApiStarGift {
id: id.toString(),
isLimited: limited,
sticker,
stars: stars.toJSNumber(),
stars: toJSNumber(stars),
availabilityRemains,
availabilityTotal,
starsToConvert: convertStars.toJSNumber(),
starsToConvert: toJSNumber(convertStars),
firstSaleDate,
lastSaleDate,
isSoldOut: soldOut,
isBirthday: birthday,
upgradeStars: upgradeStars?.toJSNumber(),
upgradeStars: upgradeStars !== undefined ? toJSNumber(upgradeStars) : undefined,
title,
resellMinStars: resellMinStars?.toJSNumber(),
resellMinStars: resellMinStars !== undefined ? toJSNumber(resellMinStars) : undefined,
releasedByPeerId: releasedBy && getApiChatIdFromMtpPeer(releasedBy),
availabilityResale: availabilityResale?.toJSNumber(),
availabilityResale: availabilityResale !== undefined ? toJSNumber(availabilityResale) : undefined,
requirePremium,
limitedPerUser,
perUserTotal,
@ -168,15 +168,15 @@ export function buildApiSavedStarGift(userStarGift: GramJs.SavedStarGift, peerId
return {
gift: buildApiStarGift(gift),
date,
starsToConvert: convertStars?.toJSNumber(),
starsToConvert: toJSNumber(convertStars),
fromId: fromId && getApiChatIdFromMtpPeer(fromId),
message: message && buildApiFormattedText(message),
messageId: msgId,
isNameHidden: nameHidden,
isUnsaved: unsaved,
canUpgrade,
alreadyPaidUpgradeStars: upgradeStars?.toJSNumber(),
transferStars: transferStars?.toJSNumber(),
alreadyPaidUpgradeStars: toJSNumber(upgradeStars),
transferStars: toJSNumber(transferStars),
inputGift,
savedId: savedId?.toString(),
canExportAt,
@ -279,16 +279,19 @@ GramJs.TypeStarGiftAttributeId[] {
return attributes.map((attr) => {
switch (attr.type) {
case 'model':
return new GramJs.StarGiftAttributeIdModel({ documentId: bigInt(attr.documentId) });
return new GramJs.StarGiftAttributeIdModel({ documentId: BigInt(attr.documentId) });
case 'pattern':
return new GramJs.StarGiftAttributeIdPattern({ documentId: bigInt(attr.documentId) });
return new GramJs.StarGiftAttributeIdPattern({ documentId: BigInt(attr.documentId) });
case 'backdrop':
return new GramJs.StarGiftAttributeIdBackdrop({ backdropId: attr.backdropId });
default:
throw new Error(`Unknown attribute type: ${(attr as any).type}`);
default: {
// 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 { ApiMessageAction } from '../../types/messageActions';
import { toJSNumber } from '../../../util/numbers';
import { buildApiBotApp } from './bots';
import { buildApiFormattedText, buildApiPhoto } from './common';
import { buildApiStarGift } from './gifts';
@ -128,7 +129,7 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
isRecurringInit: recurringInit,
isRecurringUsed: recurringUsed,
currency,
totalAmount: totalAmount.toJSNumber(),
totalAmount: toJSNumber(totalAmount),
invoiceSlug,
subscriptionUntilDate,
};
@ -254,10 +255,10 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
mediaType: 'action',
type: 'giftPremium',
currency,
amount: amount.toJSNumber(),
amount: toJSNumber(amount),
months,
cryptoCurrency,
cryptoAmount: cryptoAmount?.toJSNumber(),
cryptoAmount: toJSNumber(cryptoAmount),
message: message && buildApiFormattedText(message),
};
}
@ -308,9 +309,9 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
months,
slug,
currency,
amount: amount?.toJSNumber(),
amount: toJSNumber(amount),
cryptoCurrency,
cryptoAmount: cryptoAmount?.toJSNumber(),
cryptoAmount: toJSNumber(cryptoAmount),
message: message && buildApiFormattedText(message),
};
}
@ -319,7 +320,7 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
return {
mediaType: 'action',
type: 'giveawayLaunch',
stars: stars?.toJSNumber(),
stars: toJSNumber(stars),
};
}
if (action instanceof GramJs.MessageActionGiveawayResults) {
@ -341,7 +342,7 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
type: 'paymentRefunded',
peerId: getApiChatIdFromMtpPeer(peer),
currency,
totalAmount: totalAmount.toJSNumber(),
totalAmount: toJSNumber(totalAmount),
};
}
if (action instanceof GramJs.MessageActionGiftStars) {
@ -352,10 +353,10 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
mediaType: 'action',
type: 'giftStars',
currency,
amount: amount.toJSNumber(),
stars: stars.toJSNumber(),
amount: toJSNumber(amount),
stars: toJSNumber(stars),
cryptoCurrency,
cryptoAmount: cryptoAmount?.toJSNumber(),
cryptoAmount: toJSNumber(cryptoAmount),
transactionId,
};
}
@ -367,9 +368,9 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
mediaType: 'action',
type: 'giftTon',
currency,
amount: amount.toJSNumber(),
amount: toJSNumber(amount),
cryptoCurrency,
cryptoAmount: cryptoAmount.toJSNumber(),
cryptoAmount: toJSNumber(cryptoAmount),
transactionId,
};
}
@ -381,7 +382,7 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
mediaType: 'action',
type: 'prizeStars',
isUnclaimed: unclaimed,
stars: stars.toJSNumber(),
stars: toJSNumber(stars),
transactionId,
boostPeerId: getApiChatIdFromMtpPeer(boostPeer),
giveawayMsgId,
@ -407,12 +408,12 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
canUpgrade,
gift: starGift,
message: message && buildApiFormattedText(message),
starsToConvert: convertStars?.toJSNumber(),
starsToConvert: toJSNumber(convertStars),
upgradeMsgId,
alreadyPaidUpgradeStars: upgradeStars?.toJSNumber(),
alreadyPaidUpgradeStars: toJSNumber(upgradeStars),
fromId: fromId && getApiChatIdFromMtpPeer(fromId),
peerId: peer && getApiChatIdFromMtpPeer(peer),
savedId: savedId && buildApiPeerId(savedId, 'user'),
savedId: savedId !== undefined ? buildApiPeerId(savedId, 'user') : undefined,
};
}
if (action instanceof GramJs.MessageActionStarGiftUnique) {
@ -433,10 +434,10 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
isRefunded: refunded,
gift: starGift,
canExportAt,
transferStars: transferStars?.toJSNumber(),
transferStars: toJSNumber(transferStars),
fromId: fromId && getApiChatIdFromMtpPeer(fromId),
peerId: peer && getApiChatIdFromMtpPeer(peer),
savedId: savedId && buildApiPeerId(savedId, 'user'),
savedId: savedId !== undefined ? buildApiPeerId(savedId, 'user') : undefined,
resaleAmount: resaleAmount ? buildApiCurrencyAmount(resaleAmount) : undefined,
};
}
@ -448,7 +449,7 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
mediaType: 'action',
type: 'paidMessagesPrice',
isAllowedInChannel: broadcastMessagesAllowed,
stars: stars.toJSNumber(),
stars: toJSNumber(stars),
};
}
if (action instanceof GramJs.MessageActionPaidMessagesRefunded) {
@ -458,7 +459,7 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
return {
mediaType: 'action',
type: 'paidMessagesRefunded',
stars: stars.toJSNumber(),
stars: toJSNumber(stars),
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 { generateWaveform } from '../../../util/generateWaveform';
import { pick } from '../../../util/iteratees';
import { toJSNumber } from '../../../util/numbers';
import {
addMediaToLocalDb, addStoryToLocalDb, addWebPageMediaToLocalDb, type MediaRepairContext,
} from '../helpers/localDb';
@ -260,7 +261,7 @@ export function buildVideoFromDocument(document: GramJs.Document, params?: {
isRound,
isGif: Boolean(gifAttr),
thumbnail: buildApiThumbnailFromStripped(thumbs),
size: size.toJSNumber(),
size: toJSNumber(size),
isSpoiler,
timestamp,
hasVideoPreview,
@ -300,7 +301,7 @@ export function buildAudioFromDocument(document: GramJs.Document): ApiAudio | un
fileName: getFilenameFromDocument(document, 'audio'),
title,
performer,
size: size.toJSNumber(),
size: toJSNumber(size),
};
}
@ -359,7 +360,7 @@ function buildAudio(media: GramJs.TypeMessageMedia): ApiAudio | undefined {
id: String(media.document.id),
fileName: getFilenameFromDocument(media.document, 'audio'),
thumbnailSizes,
size: media.document.size.toJSNumber(),
size: toJSNumber(media.document.size),
...pick(media.document, ['mimeType']),
...pick(audioAttribute, ['duration', 'performer', 'title']),
};
@ -402,7 +403,7 @@ function buildVoice(media: GramJs.TypeMessageMedia): ApiVoice | undefined {
return {
mediaType: 'voice',
id: String(media.document.id),
size: media.document.size.toJSNumber(),
size: toJSNumber(media.document.size),
duration,
waveform: waveform ? Array.from(waveform) : undefined,
};
@ -475,7 +476,7 @@ export function buildApiDocument(document: GramJs.TypeDocument): ApiDocument | u
return {
mediaType: 'document',
id: String(id),
size: size.toJSNumber(),
size: toJSNumber(size),
mimeType,
timestamp: date,
fileName: getFilenameFromDocument(document),
@ -644,7 +645,7 @@ function buildGiveaway(media: GramJs.MessageMediaGiveaway): ApiGiveaway | undefi
mediaType: 'giveaway',
channelIds,
months,
stars: stars?.toJSNumber(),
stars: toJSNumber(stars),
quantity,
untilDate,
countries: countriesIso2,
@ -770,7 +771,7 @@ export function buildMediaInvoice(media: GramJs.MessageMediaInvoice): ApiMediaIn
description,
photo: buildApiWebDocument(photo),
receiptMessageId: receiptMsgId,
amount: totalAmount.toJSNumber(),
amount: toJSNumber(totalAmount),
currency,
isTest: test,
extendedMedia: preview,
@ -932,7 +933,7 @@ function buildPaidMedia(media: GramJs.TypeMessageMedia): ApiPaidMedia | undefine
if (isBought) {
return {
mediaType: 'paidMedia',
starsAmount: starsAmount.toJSNumber(),
starsAmount: toJSNumber(starsAmount),
isBought,
extendedMedia: buildBoughtMediaContent(extendedMedia)!,
};
@ -940,7 +941,7 @@ function buildPaidMedia(media: GramJs.TypeMessageMedia): ApiPaidMedia | undefine
return {
mediaType: 'paidMedia',
starsAmount: starsAmount.toJSNumber(),
starsAmount: toJSNumber(starsAmount),
extendedMedia: extendedMedia
.filter((paidMedia): paidMedia is GramJs.MessageExtendedMediaPreview => (
paidMedia instanceof GramJs.MessageExtendedMediaPreview

View File

@ -45,6 +45,7 @@ import {
import { getEmojiOnlyCountForMessage } from '../../../global/helpers/getEmojiOnlyCountForMessage';
import { addTimestampEntities } from '../../../util/dates/timestamp';
import { omitUndefined, pick } from '../../../util/iteratees';
import { toJSNumber } from '../../../util/numbers';
import { getServerTime, getServerTimeOffset } from '../../../util/serverTime';
import { interpolateArray } from '../../../util/waveform';
import {
@ -218,7 +219,7 @@ export function buildApiMessageWithChatId(
mtpMessage.media instanceof GramJs.MessageMediaInvoice ? mtpMessage.media.receiptMsgId : undefined,
) || {};
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 shouldHideKeyboardButtons = mtpMessage.replyMarkup instanceof GramJs.ReplyKeyboardHide;
const isHideKeyboardSelective = mtpMessage.replyMarkup instanceof GramJs.ReplyKeyboardHide
@ -285,7 +286,7 @@ export function buildApiMessageWithChatId(
isInvertedMedia,
isVideoProcessingPending,
reportDeliveryUntilDate: mtpMessage.reportDeliveryUntilDate,
paidMessageStars: mtpMessage.paidMessageStars?.toJSNumber(),
paidMessageStars: toJSNumber(mtpMessage.paidMessageStars),
restrictionReasons,
};
}
@ -835,6 +836,6 @@ export function buildApiSearchPostsFlood(
totalDaily: searchFlood.totalDaily,
remains: searchFlood.remains,
waitTill: searchFlood.waitTill,
starsAmount: searchFlood.starsAmount.toJSNumber(),
starsAmount: toJSNumber(searchFlood.starsAmount),
};
}

View File

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

View File

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

View File

@ -1,4 +1,3 @@
import type BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs';
import type { ApiEmojiStatusType, ApiPeerColor } from '../../types';
@ -20,16 +19,16 @@ export function isMtpPeerChannel(peer: TypePeerOrInput): peer is GramJs.PeerChan
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') {
return id.toString();
}
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) {

View File

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

View File

@ -115,7 +115,7 @@ export function buildStickerSet(set: GramJs.StickerSet): ApiStickerSet {
const hasStaticThumb = thumbs?.some((thumb) => thumb.type === 's');
const hasAnimatedThumb = thumbs?.some((thumb) => thumb.type === 'a');
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);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
import BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs';
import { RPCError } from '../../../lib/gramjs/errors';
import { generateRandomBigInt } from '../../../lib/gramjs/Helpers';
import type {
ForwardMessagesParams,
@ -98,7 +98,6 @@ import {
buildPeer,
buildSendMessageAction,
DEFAULT_PRIMITIVES,
generateRandomBigInt,
getEntityTypeById,
} from '../gramjsBuilders';
import {
@ -1888,7 +1887,7 @@ export async function forwardApiMessages(params: ForwardMessagesParams) {
const priceInStars = messagePriceInStars ? messagePriceInStars * messageIds.length : undefined;
const randomIds = messageIds.map(generateRandomBigInt);
const randomIds = messageIds.map(() => generateRandomBigInt());
try {
const update = await invokeRequest(new GramJs.messages.ForwardMessages({
fromPeer: buildInputPeer(fromChat.id, fromChat.accessHash),
@ -2498,7 +2497,7 @@ export async function sendQuickReply({
if (!messages || messages instanceof GramJs.messages.MessagesNotModified) return;
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({
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 { 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 { Logger } from '../../../lib/gramjs/extensions';
import {
@ -20,13 +18,13 @@ class PhoneCallState {
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>;
@ -55,8 +53,8 @@ class PhoneCallState {
}
acceptCall({ p, g, random }: DhConfig) {
const pLast = readBigIntFromBuffer(p, false);
const randomLast = readBigIntFromBuffer(random, false);
const pLast = readBigIntFromBuffer(Buffer.from(p), false);
const randomLast = readBigIntFromBuffer(Buffer.from(random), false);
const gB = modExp(BigInt(g), randomLast, pLast);
this.gB = gB;
@ -77,7 +75,7 @@ class PhoneCallState {
this.gA = readBigIntFromBuffer(Buffer.from(gAOrB), false);
}
const authKey = modExp(
!this.isOutgoing ? this.gA : this.gB,
(!this.isOutgoing ? this.gA : this.gB)!,
this.random,
this.p,
);
@ -125,14 +123,14 @@ class PhoneCallState {
// https://github.com/TelegramV/App/blob/ead52320975362139cabad18cf8346f98c349a22/src/js/MTProto/Calls/Internal.js#L72
function computeEmojiIndex(bytes: Uint8Array) {
return ((BigInt(bytes[0]).and(0x7F)).shiftLeft(56))
.or((BigInt(bytes[1]).shiftLeft(48)))
.or((BigInt(bytes[2]).shiftLeft(40)))
.or((BigInt(bytes[3]).shiftLeft(32)))
.or((BigInt(bytes[4]).shiftLeft(24)))
.or((BigInt(bytes[5]).shiftLeft(16)))
.or((BigInt(bytes[6]).shiftLeft(8)))
.or((BigInt(bytes[7])));
return ((BigInt(bytes[0]) & 0x7Fn) << 56n)
| ((BigInt(bytes[1]) << 48n))
| ((BigInt(bytes[2]) << 40n))
| ((BigInt(bytes[3]) << 32n))
| ((BigInt(bytes[4]) << 24n))
| ((BigInt(bytes[5]) << 16n))
| ((BigInt(bytes[6]) << 8n))
| ((BigInt(bytes[7])));
}
async function generateEmojiFingerprint(
@ -144,7 +142,7 @@ async function generateEmojiFingerprint(
const kPartSize = 8;
for (let partOffset = 0; partOffset !== hash.byteLength; 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 size = emojiOffsets[index + 1] - offset;
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 type {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,15 +1,13 @@
import type { BigInteger } from 'big-integer';
export class LocalUpdatePts {
constructor(public pts: number, public ptsCount: number) {}
}
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 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);
}

View File

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

View File

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

View File

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

View File

@ -9,6 +9,7 @@ import type {
import buildClassName from '../../../util/buildClassName';
import { formatFullDate } from '../../../util/dates/dateFormat';
import { convertTonFromNanos } from '../../../util/formatCurrency';
import { formatInteger, formatIntegerCompact } from '../../../util/textFormat';
import useLang from '../../../hooks/useLang';
@ -206,9 +207,21 @@ const StatisticsOverview: FC<OwnProps> = ({
{isToncoin ? (
<tr>
<td className={styles.tableCell}>
{renderBalanceCell(balances?.availableBalance || 0, usdRate || 0, 'lng_channel_earn_available')}
{renderBalanceCell(balances?.currentBalance || 0, usdRate || 0, 'lng_channel_earn_reward')}
{renderBalanceCell(balances?.overallRevenue || 0, usdRate || 0, 'lng_channel_earn_total')}
{renderBalanceCell(
balances?.availableBalance ? convertTonFromNanos(balances.availableBalance.amount) : 0,
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>
</tr>
) : 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 ANONYMOUS_USER_ID = '2666000';
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 ALL_FOLDER_ID = 0;
export const ARCHIVED_FOLDER_ID = 1;
export const SAVED_FOLDER_ID = -1;
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_ACTIVE_PINNED_CHATS = 5;
export const SCHEDULED_WHEN_ONLINE = 0x7FFFFFFE;

View File

@ -608,7 +608,12 @@ addActionHandler('openStarsGiftModal', async (global, actions, payload): Promise
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 = updateTabState(global, {

View File

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

View File

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

View File

@ -28,7 +28,7 @@ import {
VIDEO_STICKER_MIME_TYPE,
} from '../../config';
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 { isLocalMessageId } from '../../util/keys/messageKey';
import { getServerTime } from '../../util/serverTime';
@ -385,7 +385,7 @@ export function isUploadingFileSticker(attachment: ApiAttachment) {
export function getMessageLink(peer: ApiPeer, topicId?: ThreadId, messageId?: number) {
const chatUsername = getMainUsername(peer);
const normalizedId = getCleanPeerId(peer.id);
const normalizedId = getRawPeerId(peer.id).toString();
const chatPart = chatUsername || `c/${normalizedId}`;
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';
export function readBigIntFromBuffer(buffer: Buffer | number[], little = true, signed = false): BigInt.BigInteger {
let randBuffer = Buffer.from(buffer);
const bytesNumber = randBuffer.length;
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 readBigIntFromBuffer(buffer: Buffer, little = true, signed = false): bigint {
const len = buffer.length;
if (len === 0) return 0n;
export function toSignedLittleBuffer(big: BigInt.BigInteger, number = 8) {
const bigNumber = BigInt(big);
const byteArray: number[] = [];
for (let i = 0; i < number; i++) {
byteArray[i] = bigNumber.shiftRight(8 * i)
.and(255)
.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];
// Hot path for longs
if (len === 8) {
if (signed) {
return little ? buffer.readBigInt64LE(0) : buffer.readBigInt64BE(0);
} else {
return little ? buffer.readBigUInt64LE(0) : buffer.readBigUInt64BE(0);
}
}
// Parse unsigned value
let x = 0n;
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;
}
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) {
return readBigIntFromBuffer(generateRandomBytes(8), true, signed);
}
@ -68,16 +95,24 @@ export function mod(n: number, m: number) {
return ((n % m) + m) % m;
}
export function bigIntMod(n: BigInt.BigInteger, m: BigInt.BigInteger) {
return ((n.remainder(m)).add(m)).remainder(m);
export function bigIntMod(n: bigint, m: bigint) {
return ((n % m) + m) % m;
}
export function generateRandomBytes(count: number) {
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(
serverNonceBigInt: BigInt.BigInteger, newNonceBigInt: BigInt.BigInteger,
serverNonceBigInt: bigint, newNonceBigInt: bigint,
) {
const serverNonce = toSignedLittleBuffer(serverNonceBigInt, 16);
const newNonce = toSignedLittleBuffer(newNonceBigInt, 32);
@ -116,36 +151,76 @@ export function sha256(data: Buffer): Promise<Buffer> {
}
export function modExp(
a: bigInt.BigInteger,
b: bigInt.BigInteger,
n: bigInt.BigInteger,
a: bigint,
b: bigint,
n: bigint,
) {
a = a.remainder(n);
let result = BigInt.one;
a = a % n;
let result = 1n;
let x = a;
while (b.greater(BigInt.zero)) {
const leastSignificantBit = b.remainder(BigInt(2));
b = b.divide(BigInt(2));
if (leastSignificantBit.eq(BigInt.one)) {
result = result.multiply(x);
result = result.remainder(n);
while (b > 0n) {
const leastSignificantBit = b % 2n;
b = b / 2n;
if (leastSignificantBit === 1n) {
result = result * x;
result = result % n;
}
x = x.multiply(x);
x = x.remainder(n);
x = x * x;
x = x % n;
}
return result;
}
export function getByteArray(integer: BigInt.BigInteger, signed = false) {
const bits = integer.toString(2).length;
const byteLength = Math.floor((bits + 8 - 1) / 8);
return readBufferFromBigInt(BigInt(integer), byteLength, false, signed);
export function getByteArray(integer: bigint, signed = false): Buffer {
if (!signed && integer < 0n) {
throw new RangeError('Cannot convert negative to unsigned');
}
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) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
export function randomBits(k: number): bigint {
if (k <= 0) return 0n;
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) {
@ -188,3 +263,73 @@ export function crc32(buf: Buffer | string) {
}
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 Api from './tl/api';
import {
bigIntMod,
bitLength,
generateRandomBytes,
modExp,
readBigIntFromBuffer,
@ -14,59 +13,6 @@ import {
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) {
const goodPrime = Buffer.from([
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)
}
function isGoodLarge(number: BigInt.BigInteger, p: BigInt.BigInteger): boolean {
return (number.greater(BigInt(0)) && (p.subtract(number)
.greater(BigInt(0))));
function isGoodLarge(number: bigint, p: bigint): boolean {
return number > 0n && number < p;
}
function numBytesForHash(number: Buffer): Buffer {
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);
}
function isGoodModExpFirst(modexp: BigInt.BigInteger, prime: BigInt.BigInteger): boolean {
const diff = prime.subtract(modexp);
function isGoodModExpFirst(modexp: bigint, prime: bigint): boolean {
const diff = prime - modexp;
const minDiffBitsCount = 2048 - 64;
const maxModExpSize = 256;
return !(
diff.lesser(BigInt(0))
|| diff.bitLength().toJSNumber() < minDiffBitsCount
|| modexp.bitLength().toJSNumber() < minDiffBitsCount
|| Math.floor((modexp.bitLength().toJSNumber() + 7) / 8) > maxModExpSize
diff < 0n
|| bitLength(diff) < minDiffBitsCount
|| bitLength(modexp) < minDiffBitsCount
|| Math.floor((bitLength(modexp) + 7) / 8) > maxModExpSize
);
}
@ -189,7 +134,7 @@ export async function computeCheck(request: Api.account.Password, password: stri
try {
checkPrimeAndGood(algo.p, g);
} catch (e) {
throw new Error('bad /g in password');
throw new Error('bad p/g in password');
}
if (!isGoodLarge(B, p)) {
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 gX = modExp(BigInt(g), x, p);
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 randomSize = 256;
@ -211,20 +156,20 @@ export async function computeCheck(request: Api.account.Password, password: stri
if (isGoodModExpFirst(A, p)) {
const aForHash = bigNumForHash(A);
const u = readBigIntFromBuffer(await sha256(Buffer.concat([aForHash, bForHash])), false);
if (u.greater(BigInt(0))) {
if (u > 0n) {
return { a, aForHash, u };
}
}
}
};
const { a, aForHash, u } = await generateAndCheckRandom();
const gB = bigIntMod(B.subtract(kgX), p);
const gB = bigIntMod(B - kgX, p);
if (!isGoodModExpFirst(gB, p)) {
throw new Error('bad gB');
}
const ux = u.multiply(x);
const aUx = a.add(ux);
const ux = u * x;
const aUx = a + ux;
const S = modExp(gB, aUx, p);
const [K, pSha, gSha, salt1Sha, salt2Sha] = await Promise.all([
sha256(bigNumForHash(S)),
@ -244,8 +189,7 @@ export async function computeCheck(request: Api.account.Password, password: stri
return new Api.InputCheckPasswordSRP({
srpId,
A: Buffer.from(aForHash),
A: aForHash,
M1,
});
}

View File

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

View File

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

View File

@ -1,8 +1,7 @@
import bigInt from 'big-integer';
import type TelegramClient from './TelegramClient';
import type { Update } from './TelegramClient';
import { tryParseBigInt } from '../../../util/numbers';
import { getServerTime } from '../../../util/serverTime';
import { DEFAULT_PRIMITIVES } from '../../../api/gramjs/gramjsBuilders';
import { RPCError } from '../errors';
@ -234,6 +233,7 @@ async function signInUserWithQrCode(
let isScanningComplete = false;
const { apiId, apiHash } = apiCredentials;
const exceptIds = authParams.accountIds?.map((id) => tryParseBigInt(id)).filter(Boolean) || [];
const inputPromise = (async () => {
// eslint-disable-next-line no-constant-condition
@ -245,7 +245,7 @@ async function signInUserWithQrCode(
const result = await client.invoke(new Api.auth.ExportLoginToken({
apiId,
apiHash,
exceptIds: authParams.accountIds?.map((id) => bigInt(id)) || [],
exceptIds,
}));
if (!(result instanceof Api.auth.LoginToken)) {
throw new Error('Unexpected');
@ -286,7 +286,7 @@ async function signInUserWithQrCode(
const result2 = await client.invoke(new Api.auth.ExportLoginToken({
apiId,
apiHash,
exceptIds: authParams.accountIds?.map((id) => bigInt(id)) || [],
exceptIds,
}));
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 { 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 { ApiAvailableReaction } from '../../../../api/types';
import type Api from '../../tl/api';
@ -62,7 +60,7 @@ export type MockMessageReactions = {
export type MockDocument = Partial<Api.Document> & {
id: number;
mimeType: string;
size: bigInt.BigInteger;
size: bigint;
url: string;
bytes: Buffer;
};

View File

@ -1,7 +1,6 @@
import BigInt from 'big-integer';
import type { MockTypes } from './MockTypes';
import { CHANNEL_ID_BASE } from '../../../../config';
import Api from '../../tl/api';
import createMockedChatAdminRights from './createMockedChatAdminRights';
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);
const {
accessHash = BigInt(1),
accessHash = 1n,
title = 'Channel',
date = MOCK_STARTING_DATE,
bannedRights = createMockedChatBannedRights(id, mockData),
@ -24,7 +23,7 @@ export default function createMockedChannel(id: string, mockData: MockTypes): Ap
return new Api.Channel({
...rest,
id: BigInt(Number(id) + 1000000000),
id: -BigInt(id) - CHANNEL_ID_BASE,
accessHash,
title,
bannedRights,

View File

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

View File

@ -1,5 +1,3 @@
import BigInt from 'big-integer';
import type { MockTypes } from './MockTypes';
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);
const {
accessHash = BigInt(1),
accessHash = 1n,
fileReference = Buffer.from([0]),
date = MOCK_STARTING_DATE,
dcId = 2,

View File

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

View File

@ -1,7 +1,6 @@
import BigInt from 'big-integer';
import type { MockTypes } from './MockTypes';
import { CHANNEL_ID_BASE } from '../../../../config';
import Api from '../../tl/api';
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,
replies: replies.replies,
repliesPts: 1,
channelId: BigInt(1000000000 + 2),
channelId: -2n - CHANNEL_ID_BASE,
// recentRepliers?: Api.TypePeer[];
});
}

View File

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

View File

@ -1,7 +1,6 @@
import BigInt from 'big-integer';
import type { MockTypes } from './MockTypes';
import { CHANNEL_ID_BASE } from '../../../../config';
import Api from '../../tl/api';
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);
if (channel) {
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 Api from '../../tl/api';
@ -12,7 +10,7 @@ export default function createMockedUser(id: string, mockData: MockTypes): Api.U
const {
firstName = 'John',
lastName = 'Doe',
accessHash = BigInt(1),
accessHash = 1n,
...rest
} = user;

View File

@ -1,13 +1,14 @@
import { toJSNumber } from '../../../../util/numbers';
import Api from '../../tl/api';
export default function getDocumentIdFromLocation(location: Api.TypeInputFileLocation): number {
if (location instanceof Api.InputDocumentFileLocation) {
return location.id.toJSNumber();
return toJSNumber(location.id);
}
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';
export default function getIdFromInputPeer(peer: Api.TypeInputPeer | Api.TypeInputChannel) {
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) {
@ -10,7 +11,7 @@ export default function getIdFromInputPeer(peer: Api.TypeInputPeer | Api.TypeInp
}
if (peer instanceof Api.InputPeerChat) {
return peer.chatId.toString();
return (-peer.chatId).toString();
}
throw Error(`Unknown peer type${peer.className}`);

View File

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

View File

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

View File

@ -1,5 +1,3 @@
import bigInt from 'big-integer';
import {
generateRandomBytes,
modExp,
@ -10,8 +8,8 @@ import {
export const SERVER_KEYS = [
{
fingerprint: bigInt('-3414540481677951611'),
n: bigInt(
fingerprint: BigInt('-3414540481677951611'),
n: BigInt(
'2937959817066933702298617714945612856538843112005886376816255642404751219133084745514657634448776440866'
+ '1701890505066208632169112269581063774293102577308490531282748465986139880977280302242772832972539403531'
+ '3160108704012876427630091361567343395380424193887227773571344877461690935390938502512438971889287359033'
@ -22,8 +20,8 @@ export const SERVER_KEYS = [
e: 65537,
},
{
fingerprint: bigInt('-5595554452916591101'),
n: bigInt(
fingerprint: BigInt('-5595554452916591101'),
n: BigInt(
'2534288944884041556497168959071347320689884775908477905258202659454602246385394058588521595116849196570'
+ '8222649399180603818074200620463776135424884632162512403163793083921641631564740959529419359595852941166'
+ '8489405859523376133330223960965841179548922160312292373029437018775884567383353986024616752250817918203'
@ -34,9 +32,9 @@ export const SERVER_KEYS = [
e: 65537,
},
].reduce((acc, { fingerprint, ...keyInfo }) => {
acc.set(fingerprint.toString(), keyInfo);
acc.set(fingerprint, keyInfo);
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
@ -46,8 +44,8 @@ export const SERVER_KEYS = [
* @param data the data to be encrypted.
* @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) {
const key = SERVER_KEYS.get(fingerprint.toString());
export async function encrypt(fingerprint: bigint, data: Buffer) {
const key = SERVER_KEYS.get(fingerprint);
if (!key) {
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 uses rsa.transform.bytes2int(to_encrypt), easier way:
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:
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.
* @param signed
* @returns {BigInteger}
* @returns {bigint}
*/
readLong(signed = true) {
return this.readLargeInt(64, signed);
@ -66,7 +66,7 @@ export default class BinaryReader {
/**
* Reads a real floating point (8 bytes) value.
* @returns {BigInteger}
* @returns {number}
*/
readDouble() {
// 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';
export default class PendingState {
_pending: Map<string, RequestState>;
_pending: Map<bigint, RequestState>;
constructor() {
this._pending = new Map();
}
set(msgId: BigInt.BigInteger, state: RequestState) {
this._pending.set(msgId.toString(), state);
set(msgId: bigint, state: RequestState) {
this._pending.set(msgId, state);
}
get(msgId: BigInt.BigInteger) {
return this._pending.get(msgId.toString());
get(msgId: bigint) {
return this._pending.get(msgId);
}
getAndDelete(msgId: BigInt.BigInteger) {
getAndDelete(msgId: bigint) {
const state = this.get(msgId);
this.delete(msgId);
return state;
@ -27,8 +25,8 @@ export default class PendingState {
return Array.from(this._pending.values());
}
delete(msgId: BigInt.BigInteger) {
this._pending.delete(msgId.toString());
delete(msgId: bigint) {
return this._pending.delete(msgId);
}
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: *}>}
*/
import bigInt from 'big-integer';
import type MTProtoPlainSender from './MTProtoPlainSender';
import { IGE } from '../crypto/IGE';
@ -43,7 +41,7 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
if (!(resPQ instanceof Api.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');
}
const pq = readBigIntFromBuffer(resPQ.pq, false, true);
@ -70,7 +68,7 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
let targetFingerprint;
let targetKey;
for (const fingerprint of resPQ.serverPublicKeyFingerprints) {
targetKey = SERVER_KEYS.get(fingerprint.toString());
targetKey = SERVER_KEYS.get(fingerprint);
if (targetKey !== undefined) {
targetFingerprint = fingerprint;
break;
@ -98,11 +96,11 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
const keyAesEncrypted = Buffer.concat([tempKeyXor, aesEncrypted]);
const keyAesEncryptedInt = readBigIntFromBuffer(keyAesEncrypted, false, false);
if (keyAesEncryptedInt.greaterOrEquals(targetKey.n)) {
if (keyAesEncryptedInt >= targetKey.n) {
log.debug('Aes key greater than RSA. retrying');
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);
break;
@ -133,11 +131,11 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
) {
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');
}
if (serverDhParams.serverNonce.neq(resPQ.serverNonce)) {
if (serverDhParams.serverNonce !== resPQ.serverNonce) {
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),
);
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');
}
}
@ -178,10 +176,10 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
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');
}
if (serverDhInner.serverNonce.neq(resPQ.serverNonce)) {
if (serverDhInner.serverNonce !== resPQ.serverNonce) {
throw new SecurityError(
'Step 3 Invalid server nonce in encrypted answer',
);
@ -207,26 +205,26 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
false,
false,
);
const gb = modExp(bigInt(serverDhInner.g), b, dhPrime);
const gb = modExp(BigInt(serverDhInner.g), b, dhPrime);
const gab = modExp(ga, b, dhPrime);
if (ga.lesserOrEquals(1)) {
if (ga <= 1n) {
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');
}
if (ga.greater(dhPrime.minus(1))) {
throw new SecurityError('Step 3 failed ga > dh_prime - 1 check');
if (ga >= (dhPrime - 1n)) {
throw new SecurityError('Step 3 failed ga < dh_prime - 1 check');
}
const toCheckAgainst = bigInt(2).pow(2048 - 64);
if (!(ga.greaterOrEquals(toCheckAgainst) && ga.lesserOrEquals(dhPrime.minus(toCheckAgainst)))) {
const toCheckAgainst = 2n ** (2048n - 64n);
if (!(ga > toCheckAgainst && ga < (dhPrime - toCheckAgainst))) {
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');
}
@ -234,7 +232,7 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
const clientDhInner = new Api.ClientDHInnerData({
nonce: resPQ.nonce,
serverNonce: resPQ.serverNonce,
retryId: bigInt.zero, // TODO Actual retry ID
retryId: 0n, // TODO Actual retry ID
gB: getByteArray(gb, false),
}).getBytes();
@ -265,10 +263,10 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
throw new Error(`Step 3.1 answer was ${dhGen}`);
}
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`);
}
if (dhGen.serverNonce.neq(resPQ.serverNonce)) {
if (dhGen.serverNonce !== resPQ.serverNonce) {
throw new SecurityError(
`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 newNonceHash = await authKey.calcNewNonceHash(newNonce, nonceNumber);
// @ts-ignore
const dhHash = dhGen[`newNonceHash${nonceNumber}`];
// @ts-expect-error
const dhHash = dhGen[`newNonceHash${nonceNumber}`] as bigint;
if (dhHash.neq(newNonceHash)) {
if (dhHash !== newNonceHash) {
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
* in plain text, when no authorization key has been created yet.
*/
import BigInt from 'big-integer';
import type { Logger } from '../extensions';
import type { Api } from '../tl';
@ -53,11 +52,11 @@ export default class MTProtoPlainSender {
}
const reader = new BinaryReader(body);
const authKeyId = reader.readLong();
if (authKeyId.neq(BigInt(0))) {
if (authKeyId !== 0n) {
throw new Error('Bad authKeyId');
}
msgId = reader.readLong();
if (msgId.eq(BigInt(0))) {
if (msgId === 0n) {
throw new Error('Bad msgId');
}
/** ^ 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 { RPCError, RPCMessageToError } from '../errors';
@ -18,7 +16,7 @@ import {
BadMessageError, InvalidBufferError, SecurityError, TypeNotFoundError,
} from '../errors/Common';
import PendingState from '../extensions/PendingState';
import { sleep } from '../Helpers';
import { jsonStringifyWithBigInt, sleep } from '../Helpers';
import MessageContainer from '../tl/core/MessageContainer';
import { doAuthentication } from './Authenticator';
import MtProtoPlainSender from './MTProtoPlainSender';
@ -823,16 +821,16 @@ export default class MTProtoSender {
* @returns {*[]}
* @private
*/
_popStates(msgId: BigInt.BigInteger) {
_popStates(msgId: bigint) {
const state = this._pendingState.getAndDelete(msgId);
if (state) {
return [state];
}
const toPop: BigInt.BigInteger[] = [];
const toPop: bigint[] = [];
for (const pendingState of this._pendingState.values()) {
if (pendingState.containerId?.equals(msgId)) {
if (pendingState.containerId === msgId) {
toPop.push(pendingState.msgId!);
}
}
@ -1005,7 +1003,7 @@ export default class MTProtoSender {
_handleBadNotification(message: TLMessage) {
const badMsg = message.obj;
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)) {
// Sent msg_id too low or too high (respectively).
// 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 { CTR } from '../crypto/CTR';
@ -27,9 +25,9 @@ export default class MTProtoState {
timeOffset: number;
salt: bigInt.BigInteger;
salt: bigint;
private id: bigInt.BigInteger;
private id: bigint;
_sequence: number;
@ -37,7 +35,7 @@ export default class MTProtoState {
_isOutgoing: boolean;
private _lastMsgId: bigInt.BigInteger;
private _lastMsgId: bigint;
private msgIds: string[];
@ -74,11 +72,11 @@ export default class MTProtoState {
this._isCall = isCall;
this._isOutgoing = isOutgoing;
this.timeOffset = 0;
this.salt = BigInt.zero;
this.salt = 0n;
this.id = BigInt.zero;
this.id = 0n;
this._sequence = 0;
this._lastMsgId = BigInt.zero;
this._lastMsgId = 0n;
this.msgIds = [];
this.reset();
}
@ -90,7 +88,7 @@ export default class MTProtoState {
// Session IDs can be random on every connection
this.id = generateRandomLong(true);
this._sequence = 0;
this._lastMsgId = BigInt(0);
this._lastMsgId = 0n;
this.msgIds = [];
}
@ -142,11 +140,13 @@ export default class MTProtoState {
* @param contentRelated
* @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 seqNo = this._getSeqNo(contentRelated);
let body;
if (!afterId) {
if (afterId === undefined) {
body = await GZIPPacked.gzipIfSmaller(contentRelated, data);
} else {
// Invoke query expects a query with a getBytes func
@ -185,7 +185,7 @@ export default class MTProtoState {
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');
}
@ -255,7 +255,7 @@ export default class MTProtoState {
if (!this._isCall) {
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');
}
}
@ -304,7 +304,7 @@ export default class MTProtoState {
} else {
reader.readLong(); // removeSalt
const serverId = reader.readLong();
if (!serverId.eq(this.id)) {
if (serverId !== this.id) {
throw new SecurityError('Server replied with a wrong session ID');
}
@ -358,11 +358,9 @@ export default class MTProtoState {
_getNewMsgId() {
const now = Date.now() / 1000 + this.timeOffset;
const nanoseconds = Math.floor((now - Math.floor(now)) * 1e9);
let newMsgId = (BigInt(Math.floor(now))
.shiftLeft(BigInt(32))).or(BigInt(nanoseconds)
.shiftLeft(BigInt(2)));
if (this._lastMsgId.greaterOrEquals(newMsgId)) {
newMsgId = this._lastMsgId.add(BigInt(4));
let newMsgId = (BigInt(Math.floor(now)) << 32n) | (BigInt(nanoseconds) << 2n);
if (this._lastMsgId >= newMsgId) {
newMsgId = this._lastMsgId + 4n;
}
this._lastMsgId = newMsgId;
return newMsgId;
@ -371,28 +369,28 @@ export default class MTProtoState {
/**
* Returns the understood time by the message id (server time + local offset)
*/
getMsgIdTimeLocal(msgId: BigInt.BigInteger) {
if (this._lastMsgId.eq(0)) {
getMsgIdTimeLocal(msgId: bigint) {
if (this._lastMsgId === 0n) {
// 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
* 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 old = this.timeOffset;
const now = Math.floor(Date.now() / 1000);
const correct = correctMsgId.shiftRight(BigInt(32)).toJSNumber();
const correct = Number(correctMsgId >> 32n);
this.timeOffset = correct - now;
if (this.timeOffset !== old) {
this._lastMsgId = BigInt(0);
this._lastMsgId = 0n;
this._log.debug(
// eslint-disable-next-line @stylistic/max-len
`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 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;
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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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