Sponsored Message: Show MV only for video format (#5020)

This commit is contained in:
Alexander Zinchuk 2024-09-27 16:11:44 +02:00
parent 7859bb97fa
commit 94c2f113d6
15 changed files with 95 additions and 39 deletions

View File

@ -5,6 +5,7 @@ import type { ThreadId } from '../../../types';
import type {
ApiAttachment,
ApiChat,
ApiClickSponsoredMessage,
ApiContact,
ApiFormattedText,
ApiGlobalMessageSearchType,
@ -1673,8 +1674,12 @@ export async function viewSponsoredMessage({ chat, random }: { chat: ApiChat; ra
}));
}
export function clickSponsoredMessage({ chat, random }: { chat: ApiChat; random: string }) {
export function clickSponsoredMessage({
chat, random, isMedia, isFullscreen,
}: ApiClickSponsoredMessage) {
return invokeRequest(new GramJs.channels.ClickSponsoredMessage({
media: isMedia || undefined,
fullscreen: isFullscreen || undefined,
channel: buildInputPeer(chat.id, chat.accessHash),
randomId: deserializeBytes(random),
}));

View File

@ -957,6 +957,13 @@ export type ApiSponsoredMessageReportResult = {
}[];
};
export type ApiClickSponsoredMessage = {
chat: ApiChat;
random: string;
isMedia?: boolean;
isFullscreen?: boolean;
};
export const MAIN_THREAD_ID = -1;
// `Symbol` can not be transferred from worker

View File

@ -253,10 +253,10 @@ const MediaViewer = ({
}
});
const onSponsoredButtonClick = useLastCallback(() => {
const handleSponsoredClick = useLastCallback((isFromMedia?: boolean) => {
if (!sponsoredMessage || !chatId) return;
clickSponsoredMessage({ chatId });
clickSponsoredMessage({ isMedia: isFromMedia, isFullscreen: true, chatId });
openUrl({ url: sponsoredMessage!.url });
closeMediaViewer();
});
@ -468,7 +468,7 @@ const MediaViewer = ({
selectItem={openMediaViewerItem}
isHidden={isHidden}
onFooterClick={handleFooterClick}
onSponsoredButtonClick={onSponsoredButtonClick}
handleSponsoredClick={handleSponsoredClick}
/>
</ShowTransition>
);

View File

@ -36,7 +36,7 @@ type OwnProps = {
isMoving?: boolean;
onClose: () => void;
onFooterClick: () => void;
onSponsoredButtonClick: () => void;
handleSponsoredClick: () => void;
};
type StateProps = {
@ -66,11 +66,12 @@ const MediaViewerContent = ({
isMoving,
onClose,
onFooterClick,
onSponsoredButtonClick,
handleSponsoredClick,
}: OwnProps & StateProps) => {
const lang = useOldLang();
const isAvatar = item.type === 'avatar';
const isSponsoredMessage = item.type === 'sponsoredMessage';
const { media } = getViewableMedia(item) || {};
const {
@ -132,6 +133,8 @@ const MediaViewerContent = ({
volume={0}
isClickDisabled={isMoving}
playbackRate={1}
isSponsoredMessage={isSponsoredMessage}
handleSponsoredClick={handleSponsoredClick}
/>
</div>
);
@ -182,6 +185,8 @@ const MediaViewerContent = ({
volume={volume}
isClickDisabled={isMoving}
playbackRate={playbackRate}
isSponsoredMessage={isSponsoredMessage}
handleSponsoredClick={handleSponsoredClick}
/>
))}
{textParts && (
@ -192,7 +197,7 @@ const MediaViewerContent = ({
isProtected={isProtected}
isForceMobileVersion={isForceMobileVersion}
isForVideo={isVideo && !isGif}
onButtonClick={onSponsoredButtonClick}
handleSponsoredClick={handleSponsoredClick}
/>
)}
</div>

View File

@ -98,6 +98,9 @@
}
.media-viewer-button {
border-radius: 0.5rem;
max-width: 20rem;
margin-top: 0.5rem;
height: 2.8125rem;
border-radius: 0.6875rem;
}
}

View File

@ -10,6 +10,7 @@ import { REM } from '../common/helpers/mediaDimensions';
import useAppLayout from '../../hooks/useAppLayout';
import useDerivedState from '../../hooks/useDerivedState';
import useLastCallback from '../../hooks/useLastCallback';
import useControlsSignal from './hooks/useControlsSignal';
import Button from '../ui/Button';
@ -22,14 +23,14 @@ type OwnProps = {
text: TextPart | TextPart[];
buttonText?: string;
onClick: () => void;
onButtonClick: () => void;
handleSponsoredClick: (isFromMedia?: boolean) => void;
isForVideo: boolean;
isForceMobileVersion?: boolean;
isProtected?: boolean;
};
const MediaViewerFooter: FC<OwnProps> = ({
text = '', buttonText, isForVideo, onClick, onButtonClick, isProtected, isForceMobileVersion,
text = '', buttonText, isForVideo, onClick, handleSponsoredClick, isProtected, isForceMobileVersion,
}) => {
const [isMultiline, setIsMultiline] = useState(false);
const { isMobile } = useAppLayout();
@ -63,6 +64,10 @@ const MediaViewerFooter: FC<OwnProps> = ({
}
}
const onButtonClick = useLastCallback(() => {
handleSponsoredClick();
});
const classNames = buildClassName(
'MediaViewerFooter',
isForVideo && 'is-for-video',
@ -84,7 +89,7 @@ const MediaViewerFooter: FC<OwnProps> = ({
<Button
className={buildClassName('media-viewer-footer-content', 'media-viewer-button')}
size="default"
color="translucent"
color="primary"
isRectangular
onClick={onButtonClick}
>

View File

@ -56,7 +56,7 @@ type OwnProps = {
selectItem: (item: MediaViewerItem) => void;
loadMoreItemsIfNeeded: (item: MediaViewerItem) => void;
onFooterClick: () => void;
onSponsoredButtonClick: () => void;
handleSponsoredClick: (isFromMedia?: boolean) => void;
onClose: () => void;
};
@ -100,7 +100,7 @@ const MediaViewerSlides: FC<OwnProps> = ({
selectItem,
onClose,
onFooterClick,
onSponsoredButtonClick,
handleSponsoredClick,
}) => {
// eslint-disable-next-line no-null/no-null
const containerRef = useRef<HTMLDivElement>(null);
@ -731,7 +731,7 @@ const MediaViewerSlides: FC<OwnProps> = ({
item={prevItem}
onClose={onClose}
onFooterClick={onFooterClick}
onSponsoredButtonClick={onSponsoredButtonClick}
handleSponsoredClick={handleSponsoredClick}
/>
)}
</div>
@ -751,7 +751,7 @@ const MediaViewerSlides: FC<OwnProps> = ({
isMoving={isMoving}
onClose={onClose}
onFooterClick={onFooterClick}
onSponsoredButtonClick={onSponsoredButtonClick}
handleSponsoredClick={handleSponsoredClick}
/>
</div>
<div className="MediaViewerSlide" ref={rightSlideRef}>
@ -762,7 +762,7 @@ const MediaViewerSlides: FC<OwnProps> = ({
item={nextItem}
onClose={onClose}
onFooterClick={onFooterClick}
onSponsoredButtonClick={onSponsoredButtonClick}
handleSponsoredClick={handleSponsoredClick}
/>
)}
</div>

View File

@ -48,6 +48,8 @@ type OwnProps = {
isForceMobileVersion?: boolean;
onClose: (e: React.MouseEvent<HTMLElement, MouseEvent>) => void;
isClickDisabled?: boolean;
isSponsoredMessage?: boolean;
handleSponsoredClick?: (isFromMedia?: boolean) => void;
};
const MAX_LOOP_DURATION = 30; // Seconds
@ -72,6 +74,8 @@ const VideoPlayer: FC<OwnProps> = ({
isProtected,
isClickDisabled,
isPreviewDisabled,
isSponsoredMessage,
handleSponsoredClick,
}) => {
const {
setMediaViewerVolume,
@ -171,6 +175,10 @@ const VideoPlayer: FC<OwnProps> = ({
});
const handleClick = useLastCallback((e: React.MouseEvent<HTMLVideoElement, MouseEvent>) => {
if (isSponsoredMessage) {
handleSponsoredClick?.(true);
onClose(e);
}
if (isClickDisabled) {
return;
}
@ -332,7 +340,7 @@ const VideoPlayer: FC<OwnProps> = ({
/>
</div>
)}
{!isGif && !isUnsupported && (
{!isGif && !isSponsoredMessage && !isUnsupported && (
<VideoPlayerControls
url={url}
isPlaying={isPlaying}

View File

@ -124,14 +124,22 @@ const SponsoredMessage: FC<OwnProps & StateProps> = ({
hideSponsoredMessages();
});
const {
photo, video,
} = message ? getMessageContent(message) : { photo: undefined, video: undefined };
const isGif = video?.isGif;
const hasMedia = Boolean(photo || video);
const handleClick = useLastCallback(() => {
if (!message) return;
clickSponsoredMessage({ chatId });
clickSponsoredMessage({ isMedia: photo || isGif ? true : undefined, chatId });
openUrl({ url: message!.url, shouldSkipModal: true });
});
const handleOpenMedia = useLastCallback(() => {
clickSponsoredMessage({ isMedia: true, chatId });
openMediaViewer({
origin: MediaViewerOrigin.SponsoredMessage,
chatId,
@ -139,12 +147,6 @@ const SponsoredMessage: FC<OwnProps & StateProps> = ({
});
});
const {
photo, video,
} = message ? getMessageContent(message) : { photo: undefined, video: undefined };
const hasMedia = Boolean(photo || video);
const extraPadding = 0;
const sizeCalculations = useMemo(() => {
@ -233,7 +235,7 @@ const SponsoredMessage: FC<OwnProps & StateProps> = ({
isDownloading={isDownloading}
observeIntersection={observeIntersectionForLoading}
noAvatars
onClick={handleOpenMedia}
onClick={handleClick}
forcedWidth={contentWidth}
/>
);
@ -248,7 +250,7 @@ const SponsoredMessage: FC<OwnProps & StateProps> = ({
canAutoLoad={canAutoLoadMedia}
canAutoPlay={canAutoPlayMedia}
isDownloading={isDownloading}
onClick={handleOpenMedia}
onClick={isGif ? handleClick : handleOpenMedia}
forcedWidth={contentWidth}
/>
);

View File

@ -1583,14 +1583,16 @@ addActionHandler('viewSponsoredMessage', (global, actions, payload): ActionRetur
});
addActionHandler('clickSponsoredMessage', (global, actions, payload): ActionReturnType => {
const { chatId } = payload;
const { chatId, isMedia, isFullscreen } = payload;
const chat = selectChat(global, chatId);
const message = selectSponsoredMessage(global, chatId);
if (!chat || !message) {
return;
}
void callApi('clickSponsoredMessage', { chat, random: message.randomId });
void callApi('clickSponsoredMessage', {
chat, random: message.randomId, isMedia, isFullscreen,
});
});
addActionHandler('reportSponsoredMessage', async (global, actions, payload): Promise<void> => {

View File

@ -1659,6 +1659,8 @@ export interface ActionPayloads {
};
clickSponsoredMessage: {
chatId: string;
isMedia?: boolean;
isFullscreen?: boolean;
};
reportSponsoredMessage: {
chatId: string;

View File

@ -1,6 +1,6 @@
const api = require('./api');
const LAYER = 187;
const LAYER = 188;
const tlobjects = {};
for (const tl of Object.values(api)) {

View File

@ -116,7 +116,7 @@ namespace Api {
export type TypeStickerSet = StickerSet;
export type TypeBotCommand = BotCommand;
export type TypeBotInfo = BotInfo;
export type TypeKeyboardButton = KeyboardButton | KeyboardButtonUrl | KeyboardButtonCallback | KeyboardButtonRequestPhone | KeyboardButtonRequestGeoLocation | KeyboardButtonSwitchInline | KeyboardButtonGame | KeyboardButtonBuy | KeyboardButtonUrlAuth | InputKeyboardButtonUrlAuth | KeyboardButtonRequestPoll | InputKeyboardButtonUserProfile | KeyboardButtonUserProfile | KeyboardButtonWebView | KeyboardButtonSimpleWebView | KeyboardButtonRequestPeer | InputKeyboardButtonRequestPeer;
export type TypeKeyboardButton = KeyboardButton | KeyboardButtonUrl | KeyboardButtonCallback | KeyboardButtonRequestPhone | KeyboardButtonRequestGeoLocation | KeyboardButtonSwitchInline | KeyboardButtonGame | KeyboardButtonBuy | KeyboardButtonUrlAuth | InputKeyboardButtonUrlAuth | KeyboardButtonRequestPoll | InputKeyboardButtonUserProfile | KeyboardButtonUserProfile | KeyboardButtonWebView | KeyboardButtonSimpleWebView | KeyboardButtonRequestPeer | InputKeyboardButtonRequestPeer | KeyboardButtonCopy;
export type TypeKeyboardButtonRow = KeyboardButtonRow;
export type TypeReplyMarkup = ReplyKeyboardHide | ReplyKeyboardForceReply | ReplyKeyboardMarkup | ReplyInlineMarkup;
export type TypeMessageEntity = MessageEntityUnknown | MessageEntityMention | MessageEntityHashtag | MessageEntityBotCommand | MessageEntityUrl | MessageEntityEmail | MessageEntityBold | MessageEntityItalic | MessageEntityCode | MessageEntityPre | MessageEntityTextUrl | MessageEntityMentionName | InputMessageEntityMentionName | MessageEntityPhone | MessageEntityCashtag | MessageEntityUnderline | MessageEntityStrike | MessageEntityBankCard | MessageEntitySpoiler | MessageEntityCustomEmoji | MessageEntityBlockquote;
@ -403,7 +403,7 @@ namespace Api {
export type TypeAccessPointRule = AccessPointRule;
export type TypeTlsClientHello = TlsClientHello;
export type TypeTlsBlock = TlsBlockString | TlsBlockRandom | TlsBlockZero | TlsBlockDomain | TlsBlockGrease | TlsBlockScope;
export namespace storage {
export type TypeFileType = storage.FileUnknown | storage.FilePartial | storage.FileJpeg | storage.FileGif | storage.FilePng | storage.FilePdf | storage.FileMp3 | storage.FileMov | storage.FileMp4 | storage.FileWebp;
@ -4567,6 +4567,7 @@ namespace Api {
h: int;
preloadPrefixSize?: int;
videoStartTs?: double;
videoCodec?: string;
}> {
// flags: undefined;
roundMessage?: true;
@ -4577,6 +4578,7 @@ namespace Api {
h: int;
preloadPrefixSize?: int;
videoStartTs?: double;
videoCodec?: string;
};
export class DocumentAttributeAudio extends VirtualClass<{
// flags: undefined;
@ -5067,6 +5069,13 @@ namespace Api {
peerType: Api.TypeRequestPeerType;
maxQuantity: int;
};
export class KeyboardButtonCopy extends VirtualClass<{
text: string;
copyText: string;
}> {
text: string;
copyText: string;
};
export class KeyboardButtonRow extends VirtualClass<{
buttons: Api.TypeKeyboardButton[];
}> {
@ -10782,7 +10791,7 @@ namespace Api {
}> {
entries: Api.TypeTlsBlock[];
};
export namespace storage {
export class FileUnknown extends VirtualClass<void> {};
@ -13393,7 +13402,7 @@ namespace Api {
}>, Api.TypeDestroySessionRes> {
sessionId: long;
};
export namespace auth {
export class SendCode extends Request<Partial<{
@ -17356,9 +17365,15 @@ namespace Api {
enabled: Bool;
};
export class ClickSponsoredMessage extends Request<Partial<{
// flags: undefined;
media?: true;
fullscreen?: true;
channel: Api.TypeInputChannel;
randomId: bytes;
}>, Bool> {
// flags: undefined;
media?: true;
fullscreen?: true;
channel: Api.TypeInputChannel;
randomId: bytes;
};

View File

@ -99,7 +99,7 @@ messageMediaPhoto#695150d7 flags:# spoiler:flags.3?true photo:flags.0?Photo ttl_
messageMediaGeo#56e0d474 geo:GeoPoint = MessageMedia;
messageMediaContact#70322949 phone_number:string first_name:string last_name:string vcard:string user_id:long = MessageMedia;
messageMediaUnsupported#9f84f49e = MessageMedia;
messageMediaDocument#4cf4d72d flags:# nopremium:flags.3?true spoiler:flags.4?true video:flags.6?true round:flags.7?true voice:flags.8?true document:flags.0?Document alt_document:flags.5?Document ttl_seconds:flags.2?int = MessageMedia;
messageMediaDocument#dd570bd5 flags:# nopremium:flags.3?true spoiler:flags.4?true video:flags.6?true round:flags.7?true voice:flags.8?true document:flags.0?Document alt_documents:flags.5?Vector<Document> ttl_seconds:flags.2?int = MessageMedia;
messageMediaWebPage#ddf10c3b flags:# force_large_media:flags.0?true force_small_media:flags.1?true manual:flags.3?true safe:flags.4?true webpage:WebPage = MessageMedia;
messageMediaVenue#2ec0533f geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string = MessageMedia;
messageMediaGame#fdb19008 game:Game = MessageMedia;
@ -489,7 +489,7 @@ accountDaysTTL#b8d0afdf days:int = AccountDaysTTL;
documentAttributeImageSize#6c37c15c w:int h:int = DocumentAttribute;
documentAttributeAnimated#11b58939 = DocumentAttribute;
documentAttributeSticker#6319d612 flags:# mask:flags.1?true alt:string stickerset:InputStickerSet mask_coords:flags.0?MaskCoords = DocumentAttribute;
documentAttributeVideo#17399fad flags:# round_message:flags.0?true supports_streaming:flags.1?true nosound:flags.3?true duration:double w:int h:int preload_prefix_size:flags.2?int video_start_ts:flags.4?double = DocumentAttribute;
documentAttributeVideo#43c57c48 flags:# round_message:flags.0?true supports_streaming:flags.1?true nosound:flags.3?true duration:double w:int h:int preload_prefix_size:flags.2?int video_start_ts:flags.4?double video_codec:flags.5?string = DocumentAttribute;
documentAttributeAudio#9852f9c6 flags:# voice:flags.10?true duration:int title:flags.0?string performer:flags.1?string waveform:flags.2?bytes = DocumentAttribute;
documentAttributeFilename#15590068 file_name:string = DocumentAttribute;
documentAttributeHasStickers#9801d2f7 = DocumentAttribute;
@ -549,6 +549,7 @@ keyboardButtonWebView#13767230 text:string url:string = KeyboardButton;
keyboardButtonSimpleWebView#a0c0505c text:string url:string = KeyboardButton;
keyboardButtonRequestPeer#53d7bfd8 text:string button_id:int peer_type:RequestPeerType max_quantity:int = KeyboardButton;
inputKeyboardButtonRequestPeer#c9662d05 flags:# name_requested:flags.0?true username_requested:flags.1?true photo_requested:flags.2?true text:string button_id:int peer_type:RequestPeerType max_quantity:int = KeyboardButton;
keyboardButtonCopy#75d2698e text:string copy_text:string = KeyboardButton;
keyboardButtonRow#77608b83 buttons:Vector<KeyboardButton> = KeyboardButtonRow;
replyKeyboardHide#a03e5b85 flags:# selective:flags.2?true = ReplyMarkup;
replyKeyboardForceReply#86b40b08 flags:# single_use:flags.1?true selective:flags.2?true placeholder:flags.3?string = ReplyMarkup;
@ -1623,7 +1624,7 @@ channels.editForumTopic#f4dfa185 flags:# channel:InputChannel topic_id:int title
channels.updatePinnedForumTopic#6c2d9026 channel:InputChannel topic_id:int pinned:Bool = Updates;
channels.deleteTopicHistory#34435f2d channel:InputChannel top_msg_id:int = messages.AffectedHistory;
channels.toggleParticipantsHidden#6a6e7854 channel:InputChannel enabled:Bool = Updates;
channels.clickSponsoredMessage#18afbc93 channel:InputChannel random_id:bytes = Bool;
channels.clickSponsoredMessage#1445d75 flags:# media:flags.0?true fullscreen:flags.1?true channel:InputChannel random_id:bytes = Bool;
channels.toggleViewForumAsMessages#9738bb15 channel:InputChannel enabled:Bool = Updates;
channels.getChannelRecommendations#25a71742 flags:# channel:flags.0?InputChannel = messages.Chats;
channels.reportSponsoredMessage#af8ff6b9 channel:InputChannel random_id:bytes option:bytes = channels.SponsoredMessageReportResult;

View File

@ -124,7 +124,7 @@ messageMediaPhoto#695150d7 flags:# spoiler:flags.3?true photo:flags.0?Photo ttl_
messageMediaGeo#56e0d474 geo:GeoPoint = MessageMedia;
messageMediaContact#70322949 phone_number:string first_name:string last_name:string vcard:string user_id:long = MessageMedia;
messageMediaUnsupported#9f84f49e = MessageMedia;
messageMediaDocument#4cf4d72d flags:# nopremium:flags.3?true spoiler:flags.4?true video:flags.6?true round:flags.7?true voice:flags.8?true document:flags.0?Document alt_document:flags.5?Document ttl_seconds:flags.2?int = MessageMedia;
messageMediaDocument#dd570bd5 flags:# nopremium:flags.3?true spoiler:flags.4?true video:flags.6?true round:flags.7?true voice:flags.8?true document:flags.0?Document alt_documents:flags.5?Vector<Document> ttl_seconds:flags.2?int = MessageMedia;
messageMediaWebPage#ddf10c3b flags:# force_large_media:flags.0?true force_small_media:flags.1?true manual:flags.3?true safe:flags.4?true webpage:WebPage = MessageMedia;
messageMediaVenue#2ec0533f geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string = MessageMedia;
messageMediaGame#fdb19008 game:Game = MessageMedia;
@ -573,7 +573,7 @@ accountDaysTTL#b8d0afdf days:int = AccountDaysTTL;
documentAttributeImageSize#6c37c15c w:int h:int = DocumentAttribute;
documentAttributeAnimated#11b58939 = DocumentAttribute;
documentAttributeSticker#6319d612 flags:# mask:flags.1?true alt:string stickerset:InputStickerSet mask_coords:flags.0?MaskCoords = DocumentAttribute;
documentAttributeVideo#17399fad flags:# round_message:flags.0?true supports_streaming:flags.1?true nosound:flags.3?true duration:double w:int h:int preload_prefix_size:flags.2?int video_start_ts:flags.4?double = DocumentAttribute;
documentAttributeVideo#43c57c48 flags:# round_message:flags.0?true supports_streaming:flags.1?true nosound:flags.3?true duration:double w:int h:int preload_prefix_size:flags.2?int video_start_ts:flags.4?double video_codec:flags.5?string = DocumentAttribute;
documentAttributeAudio#9852f9c6 flags:# voice:flags.10?true duration:int title:flags.0?string performer:flags.1?string waveform:flags.2?bytes = DocumentAttribute;
documentAttributeFilename#15590068 file_name:string = DocumentAttribute;
documentAttributeHasStickers#9801d2f7 = DocumentAttribute;
@ -653,6 +653,7 @@ keyboardButtonWebView#13767230 text:string url:string = KeyboardButton;
keyboardButtonSimpleWebView#a0c0505c text:string url:string = KeyboardButton;
keyboardButtonRequestPeer#53d7bfd8 text:string button_id:int peer_type:RequestPeerType max_quantity:int = KeyboardButton;
inputKeyboardButtonRequestPeer#c9662d05 flags:# name_requested:flags.0?true username_requested:flags.1?true photo_requested:flags.2?true text:string button_id:int peer_type:RequestPeerType max_quantity:int = KeyboardButton;
keyboardButtonCopy#75d2698e text:string copy_text:string = KeyboardButton;
keyboardButtonRow#77608b83 buttons:Vector<KeyboardButton> = KeyboardButtonRow;
@ -2358,7 +2359,7 @@ channels.reorderPinnedForumTopics#2950a18f flags:# force:flags.0?true channel:In
channels.toggleAntiSpam#68f3e4eb channel:InputChannel enabled:Bool = Updates;
channels.reportAntiSpamFalsePositive#a850a693 channel:InputChannel msg_id:int = Bool;
channels.toggleParticipantsHidden#6a6e7854 channel:InputChannel enabled:Bool = Updates;
channels.clickSponsoredMessage#18afbc93 channel:InputChannel random_id:bytes = Bool;
channels.clickSponsoredMessage#1445d75 flags:# media:flags.0?true fullscreen:flags.1?true channel:InputChannel random_id:bytes = Bool;
channels.updateColor#d8aa3671 flags:# for_profile:flags.1?true channel:InputChannel color:flags.2?int background_emoji_id:flags.0?long = Updates;
channels.toggleViewForumAsMessages#9738bb15 channel:InputChannel enabled:Bool = Updates;
channels.getChannelRecommendations#25a71742 flags:# channel:flags.0?InputChannel = messages.Chats;