Paid Reactions: Support peers (#5581)

This commit is contained in:
Alexander Zinchuk 2025-03-01 17:58:56 +01:00
parent 47fcfd9e96
commit 8a2f067c9b
24 changed files with 417 additions and 75 deletions

View File

@ -4,6 +4,7 @@ import type {
ApiAvailableEffect,
ApiAvailableReaction,
ApiMessageReactor,
ApiPaidReactionPrivacyType,
ApiPeerReaction,
ApiReaction,
ApiReactionCount,
@ -66,7 +67,7 @@ export function buildApiMessageReactor(reactor: GramJs.MessageReactor): ApiMessa
return {
peerId: peerId && getApiChatIdFromMtpPeer(peerId),
count,
isMe: my,
isMy: my,
isTop: top,
isAnonymous: anonymous,
};
@ -164,3 +165,18 @@ export function buildApiAvailableEffect(availableEffect: GramJs.AvailableEffect)
effectAnimationId: effectAnimationId?.toString(),
};
}
export function buildApiPaidReactionPrivacy(privacy: GramJs.TypePaidReactionPrivacy) : ApiPaidReactionPrivacyType {
if (privacy instanceof GramJs.PaidReactionPrivacyAnonymous) {
return { type: 'anonymous' };
}
if (privacy instanceof GramJs.PaidReactionPrivacyPeer) {
return {
type: 'peer',
peerId: getApiChatIdFromMtpPeer(privacy.peer),
};
}
return { type: 'default' };
}

View File

@ -93,6 +93,19 @@ export function buildInputPeer(chatOrUserId: string, accessHash?: string): GramJ
}
}
export function buildInputPaidReactionPrivacy(isPrivate?: boolean, peerId?: string): GramJs.TypeInputPeer {
if (isPrivate) return new GramJs.PaidReactionPrivacyAnonymous();
if (peerId) {
const peer = buildInputPeerFromLocalDb(peerId);
if (peer) {
return new GramJs.PaidReactionPrivacyPeer({
peer,
});
}
}
return new GramJs.PaidReactionPrivacyDefault();
}
export function buildInputPeerFromLocalDb(chatOrUserId: string): GramJs.TypeInputPeer | undefined {
const type = getEntityTypeById(chatOrUserId);
let accessHash: BigInt.BigInteger | undefined;

View File

@ -1719,10 +1719,13 @@ export async function fetchSeenBy({ chat, messageId }: { chat: ApiChat; messageI
export async function fetchSendAs({
chat,
isForPaidReactions,
}: {
isForPaidReactions?: true;
chat: ApiChat;
}) {
const result = await invokeRequest(new GramJs.channels.GetSendAs({
forPaidReactions: isForPaidReactions,
peer: buildInputPeer(chat.id, chat.accessHash),
}), {
shouldIgnoreErrors: true,
@ -1733,9 +1736,7 @@ export async function fetchSendAs({
return undefined;
}
return {
sendAs: result.peers.map(buildApiSendAsPeerId),
};
return result.peers.map(buildApiSendAsPeerId);
}
export function saveDefaultSendAs({

View File

@ -20,7 +20,9 @@ import {
buildMessagePeerReaction,
} from '../apiBuilders/reactions';
import { buildStickerFromDocument } from '../apiBuilders/symbols';
import { buildInputPeer, buildInputReaction, generateRandomTimestampedBigInt } from '../gramjsBuilders';
import {
buildInputPaidReactionPrivacy, buildInputPeer, buildInputReaction, generateRandomTimestampedBigInt,
} from '../gramjsBuilders';
import localDb from '../localDb';
import { invokeRequest } from './client';
@ -155,18 +157,20 @@ export function sendPaidReaction({
messageId,
count,
isPrivate,
peerId,
}: {
chat: ApiChat;
messageId: number;
count: number;
isPrivate?: boolean;
peerId?: string;
}) {
return invokeRequest(new GramJs.messages.SendPaidReaction({
peer: buildInputPeer(chat.id, chat.accessHash),
msgId: messageId,
randomId: generateRandomTimestampedBigInt(),
count,
private: isPrivate || undefined,
private: buildInputPaidReactionPrivacy(isPrivate, peerId),
}), {
shouldReturnTrue: true,
shouldThrow: true,

View File

@ -55,6 +55,7 @@ import {
import { buildApiStarsAmount } from '../apiBuilders/payments';
import { buildApiEmojiStatus, buildApiPeerId, getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
import {
buildApiPaidReactionPrivacy,
buildApiReaction,
buildMessageReactions,
} from '../apiBuilders/reactions';
@ -1071,7 +1072,7 @@ export function updater(update: Update) {
} else if (update instanceof GramJs.UpdatePaidReactionPrivacy) {
sendApiUpdate({
'@type': 'updatePaidReactionPrivacy',
isPrivate: update.private,
private: buildApiPaidReactionPrivacy(update.private),
});
} else if (update instanceof GramJs.UpdateLangPackTooLong) {
sendApiUpdate({

View File

@ -77,6 +77,8 @@ export interface ApiChat {
isJoinToSend?: boolean;
isJoinRequest?: boolean;
sendAsPeerIds?: ApiSendAsPeerId[];
sendPaidReactionsAsPeerIds?: ApiSendAsPeerId[];
sendPaidReactionsPeer?: ApiSendAsPeerId;
unreadReactions?: number[];
unreadMentions?: number[];

View File

@ -675,7 +675,7 @@ export interface ApiPeerReaction {
export interface ApiMessageReactor {
isTop?: true;
isMe?: true;
isMy?: true;
count: number;
isAnonymous?: true;
peerId?: string;
@ -687,6 +687,7 @@ export interface ApiReactionCount {
reaction: ApiReactionWithPaid;
localAmount?: number;
localIsPrivate?: boolean;
localPeerId?: string;
localPreviousChosenOrder?: number;
}
@ -750,6 +751,22 @@ export type ApiSavedReactionTag = {
count: number;
};
export type ApiPaidReactionPrivacyType = ApiPaidReactionPrivacyDefault |
ApiPaidReactionPrivacyAnonymous | PaidReactionPrivacyPeer;
export type ApiPaidReactionPrivacyDefault = {
type: 'default';
};
export type ApiPaidReactionPrivacyAnonymous = {
type: 'anonymous';
};
export type PaidReactionPrivacyPeer = {
type: 'peer';
peerId: string;
};
interface ApiBaseThreadInfo {
chatId: string;
messagesCount: number;

View File

@ -24,6 +24,7 @@ import type {
ApiFormattedText,
ApiMediaExtendedPreview,
ApiMessage,
ApiPaidReactionPrivacyType,
ApiPhoto,
ApiPoll,
ApiQuickReply,
@ -789,7 +790,7 @@ export type ApiUpdateEntities = {
export type ApiUpdatePaidReactionPrivacy = {
'@type': 'updatePaidReactionPrivacy';
isPrivate: boolean;
private: ApiPaidReactionPrivacyType;
};
export type ApiUpdateLangPackTooLong = {

View File

@ -1633,3 +1633,5 @@
"UniqueStatusProofOfOwnershipDescription" = "Tapping the icon of this item next to your name will show its info and owner.";
"UniqueStatusWearButton" = "Start Wearing";
"CollectibleStatusesCategory" = "Collectibles";
"PeerPersonalAccount" = "personal account";
"PeerChannel" = "channel";

View File

@ -2,6 +2,7 @@
display: flex;
flex-direction: column;
gap: 0.25rem;
max-height: min(92vh, 38rem) !important;
}
.title {
@ -11,7 +12,7 @@
}
.slider {
margin-top: 1.5rem;
margin-top: 2rem;
flex-shrink: 0;
}
@ -23,13 +24,75 @@
text-align: center;
}
.modalBalance {
.itemInfo {
flex-grow: 1;
overflow: hidden;
height: auto;
display: flex;
flex-direction: column;
margin-left: 0.5rem;
}
.itemTitle {
line-height: 1rem;
margin-bottom: 0.125rem;
}
.itemSubtitle {
font-size: 0.9375rem;
color: var(--color-text-secondary);
line-height: 1rem;
}
.itemIcon {
margin-left: 0.5rem;
margin-right: 0 !important;
}
.sendAsPeerMenuButton {
margin-right: 0.25rem;
border-radius: 1.5rem;
padding: 0.25rem !important;
width: auto;
height: auto !important;
background-color: var(--color-background-secondary) !important;
:active,
:hover {
background-color: var(--color-interactive-element-hover) !important;
}
}
.buttonDownIcon {
margin-left: 0.25rem;
margin-right: 0.125rem;
}
.sendAsPeerMenuBubble {
max-width: 16rem;
}
.sendAsPeerMenu {
margin-right: 0.25rem;
}
.headerControlPanel {
display: flex;
align-items: center;
position: absolute;
top: 0.75rem;
right: 1.25rem;
z-index: 3;
}
.separator {
margin-top: 1rem;
}
.checkBox {
margin-block: 0.25rem !important;
}
.topLabel {
background-image: var(--stars-gradient);
color: var(--color-white);
@ -61,5 +124,6 @@
font-size: 0.875rem;
align-self: center;
color: var(--color-text-secondary);
margin-bottom: 0;
margin-top: 0.5rem;
margin-bottom: 0.125rem;
}

View File

@ -1,30 +1,43 @@
import type { FC } from '../../../lib/teact/teact';
import React, {
memo, useEffect, useMemo, useState,
} from '../../../lib/teact/teact';
import { getActions, getGlobal, withGlobal } from '../../../global';
import type {
ApiChat, ApiMessage, ApiStarsAmount, ApiUser,
ApiChat, ApiMessage, ApiPaidReactionPrivacyType,
ApiPeer,
ApiSendAsPeerId,
ApiStarsAmount, ApiUser,
} from '../../../api/types';
import type { TabState } from '../../../global/types';
import type { CustomPeer } from '../../../types';
import { STARS_ICON_PLACEHOLDER } from '../../../config';
import { getChatTitle, getUserFullName } from '../../../global/helpers';
import { selectChat, selectChatMessage, selectUser } from '../../../global/selectors';
import { getPeerTitle } from '../../../global/helpers';
import { isApiPeerUser } from '../../../global/helpers/peers';
import {
selectChat, selectChatMessage, selectPeer, selectUser,
} from '../../../global/selectors';
import buildClassName from '../../../util/buildClassName';
import { formatInteger } from '../../../util/textFormat';
import renderText from '../../common/helpers/renderText';
import useAppLayout from '../../../hooks/useAppLayout';
import useFlag from '../../../hooks/useFlag';
import useLang from '../../../hooks/useLang';
import useLastCallback from '../../../hooks/useLastCallback';
import useOldLang from '../../../hooks/useOldLang';
import Avatar from '../../common/Avatar';
import FullNameTitle from '../../common/FullNameTitle';
import Icon from '../../common/icons/Icon';
import PeerBadge from '../../common/PeerBadge';
import SafeLink from '../../common/SafeLink';
import Button from '../../ui/Button';
import Checkbox from '../../ui/Checkbox';
import DropdownMenu from '../../ui/DropdownMenu';
import MenuItem from '../../ui/MenuItem';
import Modal from '../../ui/Modal';
import Separator from '../../ui/Separator';
import BalanceBlock from '../stars/BalanceBlock';
@ -41,15 +54,18 @@ type StateProps = {
chat?: ApiChat;
maxAmount: number;
starBalance?: ApiStarsAmount;
defaultPrivacy?: boolean;
defaultPrivacy?: ApiPaidReactionPrivacyType;
sendPaidReactionsAsPeerIds?: ApiSendAsPeerId[];
currentUserId: string;
currentUser: ApiUser;
};
type ReactorData = {
amount: number;
localAmount: number;
isMe?: boolean;
isMy?: boolean;
isAnonymous?: boolean;
user?: ApiUser;
user?: ApiPeer;
};
const MAX_TOP_REACTORS = 3;
@ -69,18 +85,27 @@ const PaidReactionModal = ({
maxAmount,
starBalance,
defaultPrivacy,
sendPaidReactionsAsPeerIds,
currentUserId,
currentUser,
}: OwnProps & StateProps) => {
const { closePaidReactionModal, addLocalPaidReaction } = getActions();
const { closePaidReactionModal, addLocalPaidReaction, loadSendPaidReactionsAs } = getActions();
const [starsAmount, setStarsAmount] = useState(DEFAULT_STARS_AMOUNT);
const [isTouched, markTouched, unmarkTouched] = useFlag();
const [shouldShowUp, setShouldShowUp] = useState(true);
const [shouldSendAsAnonymous, setShouldSendAsAnonymous] = useState(true);
const [sendAsPeerId, setSendAsPeerId] = useState(currentUserId);
const chatId = chat?.id;
const senderPeer = sendAsPeerId ? (selectPeer(getGlobal(), sendAsPeerId)) : currentUser;
const oldLang = useOldLang();
const { isMobile } = useAppLayout();
const lang = useLang();
const handleAnonimityChange = useLastCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setShouldShowUp(e.target.checked);
const handleShowInTopSendersChange = useLastCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setShouldSendAsAnonymous(!e.target.checked);
});
const handleAmountChange = useLastCallback((value: number) => {
@ -88,6 +113,21 @@ const PaidReactionModal = ({
markTouched();
});
useEffect(() => {
if (chatId && !sendPaidReactionsAsPeerIds) {
loadSendPaidReactionsAs({ chatId });
}
}, [chatId, sendPaidReactionsAsPeerIds]);
const filteredMyReactorIds = useMemo(() => {
const result = sendPaidReactionsAsPeerIds?.map((peer) => peer.id)
.filter((id) => id !== chatId);
result?.unshift(currentUserId);
return result;
}, [sendPaidReactionsAsPeerIds, chatId, currentUserId]);
const canChangeSendAsPeer = filteredMyReactorIds && filteredMyReactorIds.length > 1;
useEffect(() => {
if (!modal) {
unmarkTouched();
@ -95,14 +135,23 @@ const PaidReactionModal = ({
}, [modal]);
useEffect(() => {
const currentReactor = message?.reactions?.topReactors?.find((reactor) => reactor.isMe);
const currentReactor = message?.reactions?.topReactors?.find((reactor) => reactor.isMy);
if (currentReactor) {
setShouldShowUp(!currentReactor.isAnonymous);
setShouldSendAsAnonymous(Boolean(currentReactor.isAnonymous));
if (currentReactor.peerId) {
setSendAsPeerId(currentReactor.peerId);
}
return;
}
setShouldShowUp(defaultPrivacy || true);
}, [defaultPrivacy, message?.reactions?.topReactors]);
setShouldSendAsAnonymous(defaultPrivacy?.type === 'anonymous' || false);
if (defaultPrivacy?.type === 'peer' && filteredMyReactorIds?.includes(defaultPrivacy.peerId)) {
setSendAsPeerId(defaultPrivacy.peerId);
return;
}
setSendAsPeerId(currentUserId);
}, [defaultPrivacy, message?.reactions?.topReactors, filteredMyReactorIds, currentUserId]);
const handleSend = useLastCallback(() => {
if (!modal) return;
@ -111,11 +160,85 @@ const PaidReactionModal = ({
chatId: modal.chatId,
messageId: modal.messageId,
count: starsAmount,
isPrivate: !shouldShowUp,
isPrivate: shouldSendAsAnonymous,
peerId: shouldSendAsAnonymous || sendAsPeerId === currentUserId ? undefined : sendAsPeerId,
shouldIgnoreDefaultPrivacy: true,
});
closePaidReactionModal();
});
const handleSendAsPeerChange = useLastCallback((peerId: string) => {
setShouldSendAsAnonymous(false);
setSendAsPeerId(peerId);
});
const renderMenuItem = useLastCallback((peerId: string) => {
const peer = selectPeer(getGlobal(), peerId);
const isSelected = sendAsPeerId === peerId && !shouldSendAsAnonymous;
if (!peer) return undefined;
return (
<MenuItem
// eslint-disable-next-line react/jsx-no-bind
onClick={() => handleSendAsPeerChange(peerId)}
>
<Avatar
size="small"
peer={peer}
/>
<div className={buildClassName(styles.itemInfo)}>
<FullNameTitle className={styles.itemTitle} peer={peer} noFake noVerified />
<span className={styles.itemSubtitle}>
{isApiPeerUser(peer) ? lang('PeerPersonalAccount') : lang('PeerChannel')}
</span>
</div>
<Icon
className={styles.itemIcon}
name={isSelected ? 'check' : 'placeholder'}
/>
</MenuItem>
);
});
const SendAsPeerMenuButton: FC<{ onTrigger: () => void; isOpen?: boolean }> = useMemo(() => {
return ({ onTrigger, isOpen }) => (
<Button
ripple={!isMobile}
size="smaller"
color="translucent"
className={buildClassName(styles.sendAsPeerMenuButton, isOpen ? 'active' : '')}
onClick={onTrigger}
ariaLabel={lang('AccDescrOpenMenu2')}
>
<Avatar
size="mini"
peer={shouldSendAsAnonymous ? ANONYMOUS_PEER : senderPeer}
/>
<Icon
name="down"
className={styles.buttonDownIcon}
/>
</Button>
);
}, [isMobile, lang, senderPeer, shouldSendAsAnonymous]);
const sendAsPeersMenu = useMemo(() => {
if (!canChangeSendAsPeer) return undefined;
return (
<DropdownMenu
className={styles.sendAsPeerMenu}
bubbleClassName={styles.sendAsPeerMenuBubble}
trigger={SendAsPeerMenuButton}
positionX="right"
autoClose
>
{filteredMyReactorIds.map((id) => (
renderMenuItem(id)
))}
</DropdownMenu>
);
}, [SendAsPeerMenuButton, filteredMyReactorIds, canChangeSendAsPeer]);
const topReactors = useMemo(() => {
const global = getGlobal();
const all = message?.reactions?.topReactors;
@ -124,41 +247,49 @@ const PaidReactionModal = ({
}
const result: ReactorData[] = [];
let hasMe = false;
let hasCurrentSender = false;
let myReactorAmount = 0;
all.forEach((reactor) => {
const user = reactor.peerId ? selectUser(global, reactor.peerId) : undefined;
if (!user && !reactor.isAnonymous && !reactor.isMe) return;
const peer = reactor.peerId ? selectPeer(global, reactor.peerId) : undefined;
if (!peer && !reactor.isAnonymous && !reactor.isMy) return;
if (reactor.isMy) {
myReactorAmount = reactor.count;
}
if (reactor.isMe) {
hasMe = true;
if (reactor.isMy && (reactor.peerId !== sendAsPeerId || (reactor.isAnonymous && !shouldSendAsAnonymous))) return;
const isCurrentReactor = sendAsPeerId === reactor.peerId || (shouldSendAsAnonymous && reactor.isAnonymous);
if (isCurrentReactor) {
hasCurrentSender = true;
}
result.push({
amount: reactor.count,
localAmount: reactor.isMe && isTouched ? starsAmount : 0,
isMe: reactor.isMe,
localAmount: isCurrentReactor && isTouched ? starsAmount : 0,
isMy: reactor.isMy,
isAnonymous: reactor.isAnonymous,
user,
user: peer,
});
});
if (!hasMe && isTouched) {
const me = selectUser(global, global.currentUserId!);
if (!hasCurrentSender) {
const sender = selectPeer(global, sendAsPeerId);
result.push({
amount: 0,
localAmount: starsAmount,
isMe: true,
user: me,
amount: myReactorAmount,
localAmount: isTouched ? starsAmount : 0,
isMy: true,
user: sender,
});
}
result.sort((a, b) => (b.amount + b.localAmount) - (a.amount + a.localAmount));
return result.slice(0, MAX_TOP_REACTORS);
}, [isTouched, message?.reactions?.topReactors, starsAmount]);
}, [isTouched, message?.reactions?.topReactors, starsAmount, sendAsPeerId, shouldSendAsAnonymous]);
const chatTitle = chat && getChatTitle(oldLang, chat);
const chatTitle = chat && getPeerTitle(oldLang, chat);
return (
<Modal
@ -168,7 +299,10 @@ const PaidReactionModal = ({
hasAbsoluteCloseButton
contentClassName={styles.content}
>
<BalanceBlock balance={starBalance} className={styles.modalBalance} />
<div className={styles.headerControlPanel}>
{sendAsPeersMenu}
<BalanceBlock balance={starBalance} className={styles.modalBalance} />
</div>
<StarSlider
className={styles.slider}
defaultValue={DEFAULT_STARS_AMOUNT}
@ -186,9 +320,10 @@ const PaidReactionModal = ({
<div className={styles.top}>
{topReactors.map((reactor) => {
const countText = formatInteger(reactor.amount + reactor.localAmount);
const peer = (reactor.isAnonymous || !reactor.user || (reactor.isMe && !shouldShowUp))
const peer = (reactor.isAnonymous || !reactor.user || (reactor.isMy && shouldSendAsAnonymous))
? ANONYMOUS_PEER : reactor.user;
const text = 'isCustomPeer' in peer ? oldLang(peer.titleKey) : getUserFullName(peer);
const text = 'isCustomPeer' in peer ? oldLang(peer.titleKey)
: peer && getPeerTitle(oldLang, peer);
return (
<PeerBadge
className={styles.topPeer}
@ -203,10 +338,11 @@ const PaidReactionModal = ({
})}
</div>
)}
{topReactors && (<Separator className={styles.separator} />) }
<Checkbox
className="dialog-checkbox"
checked={shouldShowUp}
onChange={handleAnonimityChange}
className={buildClassName(styles.checkBox, 'dialog-checkbox')}
checked={!shouldSendAsAnonymous}
onChange={handleShowInTopSendersChange}
label={oldLang('StarsReactionShowMeInTopSenders')}
/>
<Button
@ -238,6 +374,9 @@ export default memo(withGlobal<OwnProps>(
const starBalance = global.stars?.balance;
const maxAmount = global.appConfig?.paidReactionMaxAmount || MAX_REACTION_AMOUNT;
const defaultPrivacy = global.settings.paidReactionPrivacy;
const sendPaidReactionsAsPeerIds = chat?.sendPaidReactionsAsPeerIds;
const currentUserId = global.currentUserId!;
const currentUser = selectUser(global, currentUserId)!;
return {
chat,
@ -245,6 +384,9 @@ export default memo(withGlobal<OwnProps>(
starBalance,
maxAmount,
defaultPrivacy,
sendPaidReactionsAsPeerIds,
currentUserId,
currentUser,
};
},
)(PaidReactionModal));

View File

@ -11,6 +11,7 @@ import './DropdownMenu.scss';
type OwnProps = {
className?: string;
bubbleClassName?: string;
trigger?: FC<{ onTrigger: () => void; isOpen?: boolean }>;
transformOriginX?: number;
transformOriginY?: number;
@ -30,6 +31,7 @@ type OwnProps = {
const DropdownMenu: FC<OwnProps> = ({
trigger,
className,
bubbleClassName,
children,
transformOriginX,
transformOriginY,
@ -107,6 +109,7 @@ const DropdownMenu: FC<OwnProps> = ({
ref={menuRef}
isOpen={isOpen || Boolean(forceOpen)}
className={className || ''}
bubbleClassName={bubbleClassName || ''}
transformOriginX={transformOriginX}
transformOriginY={transformOriginY}
positionX={positionX}

View File

@ -1646,7 +1646,30 @@ addActionHandler('loadSendAs', async (global, actions, payload): Promise<void> =
}
global = getGlobal();
global = updateChat(global, chatId, { sendAsPeerIds: result.sendAs });
global = updateChat(global, chatId, { sendAsPeerIds: result });
setGlobal(global);
});
addActionHandler('loadSendPaidReactionsAs', async (global, actions, payload): Promise<void> => {
const { chatId } = payload;
const chat = selectChat(global, chatId);
if (!chat) {
return;
}
const result = await callApi('fetchSendAs', { chat, isForPaidReactions: true });
if (!result) {
global = getGlobal();
global = updateChat(global, chatId, {
sendPaidReactionsAsPeerIds: [],
});
setGlobal(global);
return;
}
global = getGlobal();
global = updateChat(global, chatId, { sendPaidReactionsAsPeerIds: result });
setGlobal(global);
});

View File

@ -246,8 +246,13 @@ addActionHandler('toggleReaction', async (global, actions, payload): Promise<voi
addActionHandler('addLocalPaidReaction', (global, actions, payload): ActionReturnType => {
const {
chatId, messageId, count, isPrivate, tabId = getCurrentTabId(),
chatId, messageId, count, shouldIgnoreDefaultPrivacy = false, tabId = getCurrentTabId(),
} = payload;
const defaultPrivacy = global.settings.paidReactionPrivacy;
const isPrivate = !shouldIgnoreDefaultPrivacy ? defaultPrivacy?.type === 'anonymous' : payload.isPrivate;
const peerId = !shouldIgnoreDefaultPrivacy
? (defaultPrivacy?.type === 'peer' ? defaultPrivacy.peerId : undefined) : payload.peerId;
const chat = selectChat(global, chatId);
const message = selectChatMessage(global, chatId, messageId);
@ -256,7 +261,7 @@ addActionHandler('addLocalPaidReaction', (global, actions, payload): ActionRetur
}
const currentReactions = message.reactions?.results || [];
const newReactions = addPaidReaction(currentReactions, count, isPrivate);
const newReactions = addPaidReaction(currentReactions, count, isPrivate, peerId);
global = updateChatMessage(global, message.chatId, message.id, {
reactions: {
...currentReactions,
@ -301,6 +306,7 @@ addActionHandler('sendPaidReaction', async (global, actions, payload): Promise<v
messageId,
count,
isPrivate: paidReaction?.localIsPrivate,
peerId: paidReaction?.localPeerId,
});
} catch (error) {
if ((error as ApiError).message === 'BALANCE_TOO_LOW') {

View File

@ -857,7 +857,8 @@ function updateReactions<T extends GlobalState>(
const localPaidReaction = currentReactions?.results.find((r) => r.localAmount);
// Save local count on update, but reset if we sent reaction
if (localPaidReaction?.localAmount) {
reactions.results = addPaidReaction(reactions.results, localPaidReaction.localAmount);
const { localIsPrivate: isPrivate, localAmount, localPeerId } = localPaidReaction;
reactions.results = addPaidReaction(reactions.results, localAmount, isPrivate, localPeerId);
}
global = updateChatMessage(global, chatId, id, { reactions });

View File

@ -199,7 +199,7 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
...global,
settings: {
...global.settings,
paidReactionPrivacy: update.isPrivate,
paidReactionPrivacy: update.private,
},
};
setGlobal(global);

View File

@ -122,7 +122,7 @@ export function updateReactionCount(reactionCount: ApiReactionCount[], newReacti
}
export function addPaidReaction(
reactionCount: ApiReactionCount[], count: number, isAnonymous?: boolean,
reactionCount: ApiReactionCount[], count: number, isAnonymous?: boolean, peerId?: string,
): ApiReactionCount[] {
const results: ApiReactionCount[] = [];
const hasPaid = reactionCount.some((current) => current.reaction.type === 'paid');
@ -134,6 +134,7 @@ export function addPaidReaction(
localAmount: (current.localAmount || 0) + count,
chosenOrder: -1,
localIsPrivate: isAnonymous !== undefined ? isAnonymous : current.localIsPrivate,
localPeerId: peerId || current.localPeerId,
localPreviousChosenOrder: current.chosenOrder,
});
return;
@ -152,6 +153,7 @@ export function addPaidReaction(
chosenOrder: -1,
localAmount: count,
localIsPrivate: isAnonymous,
localPeerId: peerId,
},
...reactionCount,
];

View File

@ -536,6 +536,9 @@ export interface ActionPayloads {
loadSendAs: {
chatId: string;
};
loadSendPaidReactionsAs: {
chatId: string;
};
saveDefaultSendAs: {
chatId: string;
sendAsId: string;
@ -1384,7 +1387,9 @@ export interface ActionPayloads {
chatId: string;
messageId: number;
count: number;
peerId?: string;
isPrivate?: boolean;
shouldIgnoreDefaultPrivacy?: boolean;
} & WithTabId;
resetLocalPaidReactions: {
chatId: string;

View File

@ -15,6 +15,7 @@ import type {
ApiGroupCall,
ApiLanguage,
ApiMessage,
ApiPaidReactionPrivacyType,
ApiPeerColors,
ApiPeerPhotos,
ApiPeerStories,
@ -408,7 +409,7 @@ export type GlobalState = {
privacy: Partial<Record<ApiPrivacyKey, ApiPrivacySettings>>;
notifyExceptions?: Record<number, NotifyException>;
lastPremiumBandwithNotificationDate?: number;
paidReactionPrivacy?: boolean;
paidReactionPrivacy?: ApiPaidReactionPrivacyType;
languages?: ApiLanguage[];
botVerificationShownPeerIds: string[];
miniAppsCachedPosition?: Point;

View File

@ -12,5 +12,5 @@ for (const tl of Object.values(Api)) {
}
}
export const LAYER = 198;
export const LAYER = 199;
export { tlobjects };

View File

@ -388,6 +388,7 @@ namespace Api {
export type TypeStarGiftAttribute = StarGiftAttributeModel | StarGiftAttributePattern | StarGiftAttributeBackdrop | StarGiftAttributeOriginalDetails;
export type TypeSavedStarGift = SavedStarGift;
export type TypeInputSavedStarGift = InputSavedStarGiftUser | InputSavedStarGiftChat;
export type TypePaidReactionPrivacy = PaidReactionPrivacyDefault | PaidReactionPrivacyAnonymous | PaidReactionPrivacyPeer;
export type TypeResPQ = ResPQ;
export type TypeP_Q_inner_data = PQInnerData | PQInnerDataDc | PQInnerDataTemp | PQInnerDataTempDc;
export type TypeServer_DH_Params = ServerDHParamsFail | ServerDHParamsOk;
@ -4490,9 +4491,9 @@ namespace Api {
static fromReader(reader: Reader): UpdateBotPurchasedPaidMedia;
}
export class UpdatePaidReactionPrivacy extends VirtualClass<{
private: Bool;
private: Api.TypePaidReactionPrivacy;
}> {
private: Bool;
private: Api.TypePaidReactionPrivacy;
static fromReader(reader: Reader): UpdatePaidReactionPrivacy;
}
export class UpdatesTooLong extends VirtualClass<void> {
@ -5365,6 +5366,7 @@ namespace Api {
export class WebPage extends VirtualClass<{
// flags: Api.Type;
hasLargeMedia?: true;
videoCoverPhoto?: true;
id: long;
url: string;
displayUrl: string;
@ -5386,6 +5388,7 @@ namespace Api {
}> {
// flags: Api.Type;
hasLargeMedia?: true;
videoCoverPhoto?: true;
id: long;
url: string;
displayUrl: string;
@ -12070,6 +12073,7 @@ namespace Api {
attributes: Api.TypeStarGiftAttribute[];
availabilityIssued: int;
availabilityTotal: int;
giftAddress?: string;
}> {
// flags: Api.Type;
id: long;
@ -12082,6 +12086,7 @@ namespace Api {
attributes: Api.TypeStarGiftAttribute[];
availabilityIssued: int;
availabilityTotal: int;
giftAddress?: string;
static fromReader(reader: Reader): StarGiftUnique;
}
export class MessageReportOption extends VirtualClass<{
@ -12297,6 +12302,18 @@ namespace Api {
savedId: long;
static fromReader(reader: Reader): InputSavedStarGiftChat;
}
export class PaidReactionPrivacyDefault extends VirtualClass<void> {
static fromReader(reader: Reader): PaidReactionPrivacyDefault;
}
export class PaidReactionPrivacyAnonymous extends VirtualClass<void> {
static fromReader(reader: Reader): PaidReactionPrivacyAnonymous;
}
export class PaidReactionPrivacyPeer extends VirtualClass<{
peer: Api.TypeInputPeer;
}> {
peer: Api.TypeInputPeer;
static fromReader(reader: Reader): PaidReactionPrivacyPeer;
}
export class ResPQ extends VirtualClass<{
nonce: int128;
serverNonce: int128;
@ -15711,6 +15728,13 @@ namespace Api {
secret: string;
query: X;
}
export class InvokeWithReCaptcha extends Request<Partial<{
token: string;
query: X;
}>, X> {
token: string;
query: X;
}
export class ReqPq extends Request<Partial<{
nonce: int128;
}>, Api.TypeResPQ> {
@ -19013,23 +19037,23 @@ namespace Api {
msgId: int;
count: int;
randomId: long;
private?: Bool;
private?: Api.TypePaidReactionPrivacy;
}>, Api.TypeUpdates> {
// flags: Api.Type;
peer: Api.TypeInputPeer;
msgId: int;
count: int;
randomId: long;
private?: Bool;
private?: Api.TypePaidReactionPrivacy;
}
export class TogglePaidReactionPrivacy extends Request<Partial<{
peer: Api.TypeInputPeer;
msgId: int;
private: Bool;
private: Api.TypePaidReactionPrivacy;
}>, Bool> {
peer: Api.TypeInputPeer;
msgId: int;
private: Bool;
private: Api.TypePaidReactionPrivacy;
}
export class GetPaidReactionPrivacy extends Request<void, Api.TypeUpdates> {}
export class ViewSponsoredMessage extends Request<Partial<{
@ -19682,8 +19706,12 @@ namespace Api {
channel: Api.TypeInputChannel;
}
export class GetSendAs extends Request<Partial<{
// flags: Api.Type;
forPaidReactions?: true;
peer: Api.TypeInputPeer;
}>, channels.TypeSendAsPeers> {
// flags: Api.Type;
forPaidReactions?: true;
peer: Api.TypeInputPeer;
}
export class DeleteParticipantHistory extends Request<Partial<{
@ -21465,7 +21493,7 @@ namespace Api {
}
}
export type AnyRequest = InvokeAfterMsg | InvokeAfterMsgs | InitConnection | InvokeWithLayer | InvokeWithoutUpdates | InvokeWithMessagesRange | InvokeWithTakeout | InvokeWithBusinessConnection | InvokeWithGooglePlayIntegrity | InvokeWithApnsSecret | ReqPq | ReqPqMulti | ReqPqMultiNew | ReqDHParams | SetClientDHParams | DestroyAuthKey | RpcDropAnswer | GetFutureSalts | Ping | PingDelayDisconnect | DestroySession
export type AnyRequest = InvokeAfterMsg | InvokeAfterMsgs | InitConnection | InvokeWithLayer | InvokeWithoutUpdates | InvokeWithMessagesRange | InvokeWithTakeout | InvokeWithBusinessConnection | InvokeWithGooglePlayIntegrity | InvokeWithApnsSecret | InvokeWithReCaptcha | ReqPq | ReqPqMulti | ReqPqMultiNew | ReqDHParams | SetClientDHParams | DestroyAuthKey | RpcDropAnswer | GetFutureSalts | Ping | PingDelayDisconnect | DestroySession
| auth.SendCode | auth.SignUp | auth.SignIn | auth.LogOut | auth.ResetAuthorizations | auth.ExportAuthorization | auth.ImportAuthorization | auth.BindTempAuthKey | auth.ImportBotAuthorization | auth.CheckPassword | auth.RequestPasswordRecovery | auth.RecoverPassword | auth.ResendCode | auth.CancelCode | auth.DropTempAuthKeys | auth.ExportLoginToken | auth.ImportLoginToken | auth.AcceptLoginToken | auth.CheckRecoveryPassword | auth.ImportWebTokenAuthorization | auth.RequestFirebaseSms | auth.ResetLoginEmail | auth.ReportMissingCode
| account.RegisterDevice | account.UnregisterDevice | account.UpdateNotifySettings | account.GetNotifySettings | account.ResetNotifySettings | account.UpdateProfile | account.UpdateStatus | account.GetWallPapers | account.ReportPeer | account.CheckUsername | account.UpdateUsername | account.GetPrivacy | account.SetPrivacy | account.DeleteAccount | account.GetAccountTTL | account.SetAccountTTL | account.SendChangePhoneCode | account.ChangePhone | account.UpdateDeviceLocked | account.GetAuthorizations | account.ResetAuthorization | account.GetPassword | account.GetPasswordSettings | account.UpdatePasswordSettings | account.SendConfirmPhoneCode | account.ConfirmPhone | account.GetTmpPassword | account.GetWebAuthorizations | account.ResetWebAuthorization | account.ResetWebAuthorizations | account.GetAllSecureValues | account.GetSecureValue | account.SaveSecureValue | account.DeleteSecureValue | account.GetAuthorizationForm | account.AcceptAuthorization | account.SendVerifyPhoneCode | account.VerifyPhone | account.SendVerifyEmailCode | account.VerifyEmail | account.InitTakeoutSession | account.FinishTakeoutSession | account.ConfirmPasswordEmail | account.ResendPasswordEmail | account.CancelPasswordEmail | account.GetContactSignUpNotification | account.SetContactSignUpNotification | account.GetNotifyExceptions | account.GetWallPaper | account.UploadWallPaper | account.SaveWallPaper | account.InstallWallPaper | account.ResetWallPapers | account.GetAutoDownloadSettings | account.SaveAutoDownloadSettings | account.UploadTheme | account.CreateTheme | account.UpdateTheme | account.SaveTheme | account.InstallTheme | account.GetTheme | account.GetThemes | account.SetContentSettings | account.GetContentSettings | account.GetMultiWallPapers | account.GetGlobalPrivacySettings | account.SetGlobalPrivacySettings | account.ReportProfilePhoto | account.ResetPassword | account.DeclinePasswordReset | account.GetChatThemes | account.SetAuthorizationTTL | account.ChangeAuthorizationSettings | account.GetSavedRingtones | account.SaveRingtone | account.UploadRingtone | account.UpdateEmojiStatus | account.GetDefaultEmojiStatuses | account.GetRecentEmojiStatuses | account.ClearRecentEmojiStatuses | account.ReorderUsernames | account.ToggleUsername | account.GetDefaultProfilePhotoEmojis | account.GetDefaultGroupPhotoEmojis | account.GetAutoSaveSettings | account.SaveAutoSaveSettings | account.DeleteAutoSaveExceptions | account.InvalidateSignInCodes | account.UpdateColor | account.GetDefaultBackgroundEmojis | account.GetChannelDefaultEmojiStatuses | account.GetChannelRestrictedStatusEmojis | account.UpdateBusinessWorkHours | account.UpdateBusinessLocation | account.UpdateBusinessGreetingMessage | account.UpdateBusinessAwayMessage | account.UpdateConnectedBot | account.GetConnectedBots | account.GetBotBusinessConnection | account.UpdateBusinessIntro | account.ToggleConnectedBotPaused | account.DisablePeerConnectedBot | account.UpdateBirthday | account.CreateBusinessChatLink | account.EditBusinessChatLink | account.DeleteBusinessChatLink | account.GetBusinessChatLinks | account.ResolveBusinessChatLink | account.UpdatePersonalChannel | account.ToggleSponsoredMessages | account.GetReactionsNotifySettings | account.SetReactionsNotifySettings | account.GetCollectibleEmojiStatuses
| users.GetUsers | users.GetFullUser | users.SetSecureValueErrors | users.GetIsPremiumRequiredToContact

View File

@ -373,7 +373,7 @@ updateStarsBalance#4e80a379 balance:StarsAmount = Update;
updateBusinessBotCallbackQuery#1ea2fda7 flags:# query_id:long user_id:long connection_id:string message:Message reply_to_message:flags.2?Message chat_instance:long data:flags.0?bytes = Update;
updateStarsRevenueStatus#a584b019 peer:Peer status:StarsRevenueStatus = Update;
updateBotPurchasedPaidMedia#283bd312 user_id:long payload:string qts:int = Update;
updatePaidReactionPrivacy#51ca7aec private:Bool = Update;
updatePaidReactionPrivacy#8b725fce private:PaidReactionPrivacy = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
updates.differenceEmpty#5d75a138 date:int seq:int = updates.Difference;
updates.difference#f49ca0 new_messages:Vector<Message> new_encrypted_messages:Vector<EncryptedMessage> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> state:updates.State = updates.Difference;
@ -510,7 +510,7 @@ messages.allStickers#cdbbcebb hash:long sets:Vector<StickerSet> = messages.AllSt
messages.affectedMessages#84d19185 pts:int pts_count:int = messages.AffectedMessages;
webPageEmpty#211a1788 flags:# id:long url:flags.0?string = WebPage;
webPagePending#b0d13e47 flags:# id:long url:flags.0?string date:int = WebPage;
webPage#e89c45b2 flags:# has_large_media:flags.13?true id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document cached_page:flags.10?Page attributes:flags.12?Vector<WebPageAttribute> = WebPage;
webPage#e89c45b2 flags:# has_large_media:flags.13?true video_cover_photo:flags.14?true id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document cached_page:flags.10?Page attributes:flags.12?Vector<WebPageAttribute> = WebPage;
webPageNotModified#7311ca11 flags:# cached_page_views:flags.0?int = WebPage;
authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true password_pending:flags.2?true encrypted_requests_disabled:flags.3?true call_requests_disabled:flags.4?true unconfirmed:flags.5?true hash:long device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization;
account.authorizations#4bff8ea0 authorization_ttl_days:int authorizations:Vector<Authorization> = account.Authorizations;
@ -1367,7 +1367,7 @@ messageReactor#4ba3a95a flags:# top:flags.0?true my:flags.1?true anonymous:flags
starsGiveawayOption#94ce852a flags:# extended:flags.0?true default:flags.1?true stars:long yearly_boosts:int store_product:flags.2?string currency:string amount:long winners:Vector<StarsGiveawayWinnersOption> = StarsGiveawayOption;
starsGiveawayWinnersOption#54236209 flags:# default:flags.0?true users:int per_user_stars:long = StarsGiveawayWinnersOption;
starGift#2cc73c8 flags:# limited:flags.0?true sold_out:flags.1?true birthday:flags.2?true id:long sticker:Document stars:long availability_remains:flags.0?int availability_total:flags.0?int convert_stars:long first_sale_date:flags.1?int last_sale_date:flags.1?int upgrade_stars:flags.3?long = StarGift;
starGiftUnique#f2fe7e4a flags:# id:long title:string slug:string num:int owner_id:flags.0?Peer owner_name:flags.1?string owner_address:flags.2?string attributes:Vector<StarGiftAttribute> availability_issued:int availability_total:int = StarGift;
starGiftUnique#5c62d151 flags:# id:long title:string slug:string num:int owner_id:flags.0?Peer owner_name:flags.1?string owner_address:flags.2?string attributes:Vector<StarGiftAttribute> availability_issued:int availability_total:int gift_address:flags.3?string = StarGift;
payments.starGiftsNotModified#a388a368 = payments.StarGifts;
payments.starGifts#901689ea hash:int gifts:Vector<StarGift> = payments.StarGifts;
messageReportOption#7903e3d9 text:string option:bytes = MessageReportOption;
@ -1400,6 +1400,9 @@ payments.savedStarGifts#95f389b1 flags:# count:int chat_notifications_enabled:fl
inputSavedStarGiftUser#69279795 msg_id:int = InputSavedStarGift;
inputSavedStarGiftChat#f101aa7f peer:InputPeer saved_id:long = InputSavedStarGift;
payments.starGiftWithdrawalUrl#84aa3a9c url:string = payments.StarGiftWithdrawalUrl;
paidReactionPrivacyDefault#206ad49e = PaidReactionPrivacy;
paidReactionPrivacyAnonymous#1f0c1ad9 = PaidReactionPrivacy;
paidReactionPrivacyPeer#dc6cfcf0 peer:InputPeer = PaidReactionPrivacy;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
initConnection#c1cd5ea9 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy params:flags.1?JSONValue query:!X = X;
@ -1615,7 +1618,7 @@ messages.sendQuickReplyMessages#6c750de1 peer:InputPeer shortcut_id:int id:Vecto
messages.getAvailableEffects#dea20a39 hash:int = messages.AvailableEffects;
messages.getFactCheck#b9cdc5ee peer:InputPeer msg_id:Vector<int> = Vector<FactCheck>;
messages.requestMainWebView#c9e01e7b flags:# compact:flags.7?true fullscreen:flags.8?true peer:InputPeer bot:InputUser start_param:flags.1?string theme_params:flags.0?DataJSON platform:string = WebViewResult;
messages.sendPaidReaction#9dd6a67b flags:# peer:InputPeer msg_id:int count:int random_id:long private:flags.0?Bool = Updates;
messages.sendPaidReaction#58bbcb50 flags:# peer:InputPeer msg_id:int count:int random_id:long private:flags.0?PaidReactionPrivacy = Updates;
messages.getPaidReactionPrivacy#472455aa = Updates;
messages.viewSponsoredMessage#673ad8f1 peer:InputPeer random_id:bytes = Bool;
messages.clickSponsoredMessage#f093465 flags:# media:flags.0?true fullscreen:flags.1?true peer:InputPeer random_id:bytes = Bool;
@ -1668,7 +1671,7 @@ channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector<int> = Bool
channels.togglePreHistoryHidden#eabbb94c channel:InputChannel enabled:Bool = Updates;
channels.getGroupsForDiscussion#f5dad378 = messages.Chats;
channels.setDiscussionGroup#40582bb2 broadcast:InputChannel group:InputChannel = Bool;
channels.getSendAs#dc770ee peer:InputPeer = channels.SendAsPeers;
channels.getSendAs#e785a43f flags:# for_paid_reactions:flags.0?true peer:InputPeer = channels.SendAsPeers;
channels.deleteParticipantHistory#367544db channel:InputChannel participant:InputPeer = messages.AffectedHistory;
channels.toggleJoinToSend#e4cb9580 channel:InputChannel enabled:Bool = Updates;
channels.toggleJoinRequest#4c2985b6 channel:InputChannel enabled:Bool = Updates;

View File

@ -426,7 +426,7 @@ updateStarsBalance#4e80a379 balance:StarsAmount = Update;
updateBusinessBotCallbackQuery#1ea2fda7 flags:# query_id:long user_id:long connection_id:string message:Message reply_to_message:flags.2?Message chat_instance:long data:flags.0?bytes = Update;
updateStarsRevenueStatus#a584b019 peer:Peer status:StarsRevenueStatus = Update;
updateBotPurchasedPaidMedia#283bd312 user_id:long payload:string qts:int = Update;
updatePaidReactionPrivacy#51ca7aec private:Bool = Update;
updatePaidReactionPrivacy#8b725fce private:PaidReactionPrivacy = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -599,7 +599,7 @@ messages.affectedMessages#84d19185 pts:int pts_count:int = messages.AffectedMess
webPageEmpty#211a1788 flags:# id:long url:flags.0?string = WebPage;
webPagePending#b0d13e47 flags:# id:long url:flags.0?string date:int = WebPage;
webPage#e89c45b2 flags:# has_large_media:flags.13?true id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document cached_page:flags.10?Page attributes:flags.12?Vector<WebPageAttribute> = WebPage;
webPage#e89c45b2 flags:# has_large_media:flags.13?true video_cover_photo:flags.14?true id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document cached_page:flags.10?Page attributes:flags.12?Vector<WebPageAttribute> = WebPage;
webPageNotModified#7311ca11 flags:# cached_page_views:flags.0?int = WebPage;
authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true password_pending:flags.2?true encrypted_requests_disabled:flags.3?true call_requests_disabled:flags.4?true unconfirmed:flags.5?true hash:long device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization;
@ -1877,7 +1877,7 @@ starsGiveawayOption#94ce852a flags:# extended:flags.0?true default:flags.1?true
starsGiveawayWinnersOption#54236209 flags:# default:flags.0?true users:int per_user_stars:long = StarsGiveawayWinnersOption;
starGift#2cc73c8 flags:# limited:flags.0?true sold_out:flags.1?true birthday:flags.2?true id:long sticker:Document stars:long availability_remains:flags.0?int availability_total:flags.0?int convert_stars:long first_sale_date:flags.1?int last_sale_date:flags.1?int upgrade_stars:flags.3?long = StarGift;
starGiftUnique#f2fe7e4a flags:# id:long title:string slug:string num:int owner_id:flags.0?Peer owner_name:flags.1?string owner_address:flags.2?string attributes:Vector<StarGiftAttribute> availability_issued:int availability_total:int = StarGift;
starGiftUnique#5c62d151 flags:# id:long title:string slug:string num:int owner_id:flags.0?Peer owner_name:flags.1?string owner_address:flags.2?string attributes:Vector<StarGiftAttribute> availability_issued:int availability_total:int gift_address:flags.3?string = StarGift;
payments.starGiftsNotModified#a388a368 = payments.StarGifts;
payments.starGifts#901689ea hash:int gifts:Vector<StarGift> = payments.StarGifts;
@ -1934,6 +1934,10 @@ inputSavedStarGiftChat#f101aa7f peer:InputPeer saved_id:long = InputSavedStarGif
payments.starGiftWithdrawalUrl#84aa3a9c url:string = payments.StarGiftWithdrawalUrl;
paidReactionPrivacyDefault#206ad49e = PaidReactionPrivacy;
paidReactionPrivacyAnonymous#1f0c1ad9 = PaidReactionPrivacy;
paidReactionPrivacyPeer#dc6cfcf0 peer:InputPeer = PaidReactionPrivacy;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -1946,6 +1950,7 @@ invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X;
invokeWithBusinessConnection#dd289f8e {X:Type} connection_id:string query:!X = X;
invokeWithGooglePlayIntegrity#1df92984 {X:Type} nonce:string token:string query:!X = X;
invokeWithApnsSecret#0dae54f8 {X:Type} nonce:string secret:string query:!X = X;
invokeWithReCaptcha#adbb0f94 {X:Type} token:string query:!X = X;
auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode;
auth.signUp#aac7b717 flags:# no_joined_notifications:flags.0?true phone_number:string phone_code_hash:string first_name:string last_name:string = auth.Authorization;
@ -2331,8 +2336,8 @@ messages.editFactCheck#589ee75 peer:InputPeer msg_id:int text:TextWithEntities =
messages.deleteFactCheck#d1da940c peer:InputPeer msg_id:int = Updates;
messages.getFactCheck#b9cdc5ee peer:InputPeer msg_id:Vector<int> = Vector<FactCheck>;
messages.requestMainWebView#c9e01e7b flags:# compact:flags.7?true fullscreen:flags.8?true peer:InputPeer bot:InputUser start_param:flags.1?string theme_params:flags.0?DataJSON platform:string = WebViewResult;
messages.sendPaidReaction#9dd6a67b flags:# peer:InputPeer msg_id:int count:int random_id:long private:flags.0?Bool = Updates;
messages.togglePaidReactionPrivacy#849ad397 peer:InputPeer msg_id:int private:Bool = Bool;
messages.sendPaidReaction#58bbcb50 flags:# peer:InputPeer msg_id:int count:int random_id:long private:flags.0?PaidReactionPrivacy = Updates;
messages.togglePaidReactionPrivacy#435885b5 peer:InputPeer msg_id:int private:PaidReactionPrivacy = Bool;
messages.getPaidReactionPrivacy#472455aa = Updates;
messages.viewSponsoredMessage#673ad8f1 peer:InputPeer random_id:bytes = Bool;
messages.clickSponsoredMessage#f093465 flags:# media:flags.0?true fullscreen:flags.1?true peer:InputPeer random_id:bytes = Bool;
@ -2423,7 +2428,7 @@ channels.editLocation#58e63f6d channel:InputChannel geo_point:InputGeoPoint addr
channels.toggleSlowMode#edd49ef0 channel:InputChannel seconds:int = Updates;
channels.getInactiveChannels#11e831ee = messages.InactiveChats;
channels.convertToGigagroup#b290c69 channel:InputChannel = Updates;
channels.getSendAs#dc770ee peer:InputPeer = channels.SendAsPeers;
channels.getSendAs#e785a43f flags:# for_paid_reactions:flags.0?true peer:InputPeer = channels.SendAsPeers;
channels.deleteParticipantHistory#367544db channel:InputChannel participant:InputPeer = messages.AffectedHistory;
channels.toggleJoinToSend#e4cb9580 channel:InputChannel enabled:Bool = Updates;
channels.toggleJoinRequest#4c2985b6 channel:InputChannel enabled:Bool = Updates;

View File

@ -1333,6 +1333,8 @@ export interface LangPair {
'UniqueStatusProofOfOwnershipDescription': undefined;
'UniqueStatusWearButton': undefined;
'CollectibleStatusesCategory': undefined;
'PeerPersonalAccount': undefined;
'PeerChannel': undefined;
}
export interface LangPairWithVariables<V extends unknown = LangVariable> {