Bots: Support similar bots (#5476)
This commit is contained in:
parent
36639f1c2a
commit
ab5742bbc9
@ -4,7 +4,7 @@ src/lib/fasttextweb/fasttext-wasm.js
|
||||
|
||||
src/lib/gramjs/tl/types-generator/template.js
|
||||
src/lib/gramjs/tl/api.d.ts
|
||||
src/lib/gramjs/tl/apiTl.js
|
||||
src/lib/gramjs/tl/apiTl.ts
|
||||
src/lib/gramjs/tl/schemaTl.js
|
||||
|
||||
src/lib/lovely-chart
|
||||
|
||||
@ -693,3 +693,21 @@ export async function fetchPopularAppBots({
|
||||
nextOffset: result.nextOffset,
|
||||
};
|
||||
}
|
||||
|
||||
export async function fetchBotsRecommendations({ user }: { user: ApiChat }) {
|
||||
if (!user) return undefined;
|
||||
const inputUser = buildInputEntity(user.id, user.accessHash) as GramJs.InputUser;
|
||||
const result = await invokeRequest(new GramJs.bots.GetBotRecommendations({
|
||||
bot: inputUser,
|
||||
}));
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const similarBots = result?.users.map(buildApiUser).filter(Boolean);
|
||||
|
||||
return {
|
||||
similarBots,
|
||||
count: result instanceof GramJs.users.UsersSlice ? result.count : similarBots.length,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1491,9 +1491,12 @@
|
||||
"ProfileTabVoice" = "Voice";
|
||||
"ProfileTabSharedGroups" = "Groups";
|
||||
"ProfileTabSimilarChannels" = "Similar Channels";
|
||||
"ProfileTabSimilarBots" = "Similar Bots";
|
||||
"ActionUnsupportedTitle" = "Action not supported yet";
|
||||
"ActionUnsupportedDescription" = "Please, use one of our apps to complete this action.";
|
||||
"LocationPermissionText" = "**{name}** requests access to set your **location**. You will be able to revoke this access in the profile page of **{name}**.";
|
||||
"UnlockMoreSimilarBots" = "Show More Apps";
|
||||
"MoreSimilarBotsText" = "Subscribe to **Telegram Premium** to unlock up to {count} similar apps."
|
||||
"GiftWasNotFound" = "Gift was not found";
|
||||
"ViewButtonRequestJoin" = "REQUEST TO JOIN";
|
||||
"ViewButtonMessage" = "VIEW MESSAGE";
|
||||
@ -1508,5 +1511,4 @@
|
||||
"ViewButtonStory" = "VIEW STORY";
|
||||
"ViewButtonBoost" = "BOOST";
|
||||
"ViewButtonStickerset" = "VIEW STICKERS";
|
||||
"ViewButtonGiftUnique" = "VIEW COLLECTIBLE";
|
||||
|
||||
"ViewButtonGiftUnique" = "VIEW COLLECTIBLE";
|
||||
@ -122,6 +122,7 @@
|
||||
}
|
||||
|
||||
&.similarChannels-list,
|
||||
&.similarBots-list,
|
||||
&.commonChats-list,
|
||||
&.members-list,
|
||||
&.gifts-list {
|
||||
@ -134,11 +135,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.similarBots-list,
|
||||
&.similarChannels-list {
|
||||
.ListItem.blured {
|
||||
filter: opacity(0.8);
|
||||
}
|
||||
|
||||
.show-more-bots,
|
||||
.show-more-channels {
|
||||
width: calc(100% - 1rem);
|
||||
margin: 0 auto;
|
||||
|
||||
@ -39,6 +39,7 @@ import {
|
||||
isChatChannel,
|
||||
isChatGroup,
|
||||
isUserBot,
|
||||
isUserId,
|
||||
isUserRightBanned,
|
||||
} from '../../global/helpers';
|
||||
import {
|
||||
@ -50,6 +51,7 @@ import {
|
||||
selectIsCurrentUserPremium,
|
||||
selectIsRightColumnShown,
|
||||
selectPeerStories,
|
||||
selectSimilarBotsIds,
|
||||
selectSimilarChannelIds,
|
||||
selectTabState,
|
||||
selectTheme,
|
||||
@ -112,6 +114,7 @@ type OwnProps = {
|
||||
type StateProps = {
|
||||
theme: ISettings['theme'];
|
||||
isChannel?: boolean;
|
||||
isBot?: boolean;
|
||||
currentUserId?: string;
|
||||
messagesById?: Record<number, ApiMessage>;
|
||||
foundIds?: number[];
|
||||
@ -142,9 +145,10 @@ type StateProps = {
|
||||
nextProfileTab?: ProfileTabType;
|
||||
shouldWarnAboutSvg?: boolean;
|
||||
similarChannels?: string[];
|
||||
similarBots?: string[];
|
||||
botPreviewMedia? : ApiBotPreviewMedia[];
|
||||
isCurrentUserPremium?: boolean;
|
||||
limitSimilarChannels: number;
|
||||
limitSimilarPeers: number;
|
||||
isTopicInfo?: boolean;
|
||||
isSavedDialog?: boolean;
|
||||
forceScrollProfileTab?: boolean;
|
||||
@ -172,6 +176,7 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
profileState,
|
||||
theme,
|
||||
isChannel,
|
||||
isBot,
|
||||
currentUserId,
|
||||
messagesById,
|
||||
foundIds,
|
||||
@ -203,8 +208,9 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
nextProfileTab,
|
||||
shouldWarnAboutSvg,
|
||||
similarChannels,
|
||||
similarBots,
|
||||
isCurrentUserPremium,
|
||||
limitSimilarChannels,
|
||||
limitSimilarPeers,
|
||||
isTopicInfo,
|
||||
isSavedDialog,
|
||||
forceScrollProfileTab,
|
||||
@ -225,6 +231,7 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
loadStoriesArchive,
|
||||
openPremiumModal,
|
||||
loadChannelRecommendations,
|
||||
loadBotRecommendations,
|
||||
loadPreviewMedias,
|
||||
loadUserGifts,
|
||||
} = getActions();
|
||||
@ -283,13 +290,17 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
arr.push({ type: 'similarChannels', key: 'ProfileTabSimilarChannels' });
|
||||
}
|
||||
|
||||
if (isBot && similarBots?.length) {
|
||||
arr.push({ type: 'similarBots', key: 'ProfileTabSimilarBots' });
|
||||
}
|
||||
|
||||
return arr.map((tab) => ({
|
||||
type: tab.type,
|
||||
title: lang(tab.key),
|
||||
}));
|
||||
}, [
|
||||
isSavedMessages, isSavedDialog, hasStoriesTab, hasGiftsTab, hasMembersTab, hasPreviewMediaTab, isTopicInfo,
|
||||
hasCommonChatsTab, isChannel, similarChannels?.length, lang,
|
||||
hasCommonChatsTab, isChannel, isBot, similarChannels?.length, similarBots?.length, lang,
|
||||
]);
|
||||
|
||||
const initialTab = useMemo(() => {
|
||||
@ -330,6 +341,12 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
}, [chatId, isChannel, similarChannels, isSynced]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isBot && !similarBots && isSynced) {
|
||||
loadBotRecommendations({ userId: chatId });
|
||||
}
|
||||
}, [chatId, isBot, similarBots, isSynced]);
|
||||
|
||||
const giftIds = useMemo(() => {
|
||||
return gifts?.map(({ date, gift, fromId }) => `${date}-${fromId}-${gift.id}`);
|
||||
}, [gifts]);
|
||||
@ -371,6 +388,7 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
pinnedStoryIds,
|
||||
archiveStoryIds,
|
||||
similarChannels,
|
||||
similarBots,
|
||||
});
|
||||
const isFirstTab = (isSavedMessages && resultType === 'dialogs')
|
||||
|| (hasStoriesTab && resultType === 'stories')
|
||||
@ -706,7 +724,49 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
<i className="icon icon-unlock-badge" />
|
||||
</Button>
|
||||
<div className="more-similar">
|
||||
{renderText(oldLang('MoreSimilarText', limitSimilarChannels), ['simple_markdown'])}
|
||||
{renderText(oldLang('MoreSimilarText', limitSimilarPeers), ['simple_markdown'])}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
) : resultType === 'similarBots' ? (
|
||||
<div key={resultType}>
|
||||
{(viewportIds as string[])!.map((userId, i) => (
|
||||
<ListItem
|
||||
key={userId}
|
||||
teactOrderKey={i}
|
||||
className={buildClassName(
|
||||
'chat-item-clickable search-result',
|
||||
!isCurrentUserPremium && i === similarBots!.length - 1 && 'blured',
|
||||
)}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => openChat({ id: userId })}
|
||||
>
|
||||
{isUserId(userId) ? (
|
||||
<PrivateChatInfo
|
||||
userId={userId}
|
||||
avatarSize="medium"
|
||||
/>
|
||||
) : (
|
||||
<GroupChatInfo
|
||||
chatId={userId}
|
||||
avatarSize="medium"
|
||||
/>
|
||||
)}
|
||||
</ListItem>
|
||||
))}
|
||||
{!isCurrentUserPremium && (
|
||||
<>
|
||||
{/* eslint-disable-next-line react/jsx-no-bind */}
|
||||
<Button className="show-more-bots" size="smaller" onClick={() => openPremiumModal()}>
|
||||
{lang('UnlockMoreSimilarBots')}
|
||||
<i className="icon icon-unlock-badge" />
|
||||
</Button>
|
||||
<div className="more-similar">
|
||||
{renderText(lang('MoreSimilarBotsText', { count: limitSimilarPeers }, {
|
||||
withNodes: true,
|
||||
withMarkdown: true,
|
||||
}))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
@ -814,6 +874,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
|
||||
const isGroup = chat && isChatGroup(chat);
|
||||
const isChannel = chat && isChatChannel(chat);
|
||||
const isBot = user && isUserBot(user);
|
||||
const hasMembersTab = !isTopicInfo && !isSavedDialog && (isGroup || (isChannel && isChatAdmin(chat!)));
|
||||
const members = chatFullInfo?.members;
|
||||
const adminMembersById = chatFullInfo?.adminMembersById;
|
||||
@ -825,6 +886,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const canDeleteMembers = hasMembersTab && chat && (getHasAdminRight(chat, 'banUsers') || chat.isCreator);
|
||||
const activeDownloads = selectActiveDownloads(global);
|
||||
const { similarChannelIds } = selectSimilarChannelIds(global, chatId) || {};
|
||||
const { similarBotsIds } = selectSimilarBotsIds(global, chatId) || {};
|
||||
const isCurrentUserPremium = selectIsCurrentUserPremium(global);
|
||||
|
||||
const peer = user || chat;
|
||||
@ -851,6 +913,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
return {
|
||||
theme: selectTheme(global),
|
||||
isChannel,
|
||||
isBot,
|
||||
messagesById,
|
||||
foundIds,
|
||||
mediaSearchType,
|
||||
@ -879,12 +942,13 @@ export default memo(withGlobal<OwnProps>(
|
||||
forceScrollProfileTab: selectTabState(global).forceScrollProfileTab,
|
||||
shouldWarnAboutSvg: global.settings.byKey.shouldWarnAboutSvg,
|
||||
similarChannels: similarChannelIds,
|
||||
similarBots: similarBotsIds,
|
||||
botPreviewMedia,
|
||||
isCurrentUserPremium,
|
||||
isTopicInfo,
|
||||
isSavedDialog,
|
||||
isSynced: global.isSynced,
|
||||
limitSimilarChannels: selectPremiumLimit(global, 'recommendedChannels'),
|
||||
limitSimilarPeers: selectPremiumLimit(global, 'recommendedChannels'),
|
||||
...(hasMembersTab && members && { members, adminMembersById }),
|
||||
...(hasCommonChatsTab && user && { commonChatIds: commonChats?.ids }),
|
||||
};
|
||||
|
||||
@ -34,6 +34,7 @@ export default function useProfileViewportIds({
|
||||
pinnedStoryIds,
|
||||
archiveStoryIds,
|
||||
similarChannels,
|
||||
similarBots,
|
||||
} : {
|
||||
loadMoreMembers: AnyToVoidFunction;
|
||||
loadCommonChats: AnyToVoidFunction;
|
||||
@ -56,6 +57,7 @@ export default function useProfileViewportIds({
|
||||
pinnedStoryIds?: number[];
|
||||
archiveStoryIds?: number[];
|
||||
similarChannels?: string[];
|
||||
similarBots?: string[];
|
||||
}) {
|
||||
const resultType = tabType === 'members' || !mediaSearchType ? tabType : mediaSearchType;
|
||||
|
||||
@ -184,6 +186,9 @@ export default function useProfileViewportIds({
|
||||
case 'similarChannels':
|
||||
viewportIds = similarChannels;
|
||||
break;
|
||||
case 'similarBots':
|
||||
viewportIds = similarBots;
|
||||
break;
|
||||
case 'gifts':
|
||||
viewportIds = giftIds;
|
||||
getMore = loadMoreGifts;
|
||||
|
||||
@ -60,6 +60,7 @@ import {
|
||||
addChatMembers,
|
||||
addChats,
|
||||
addMessages,
|
||||
addSimilarBots,
|
||||
addSimilarChannels,
|
||||
addUsers,
|
||||
addUserStatuses,
|
||||
@ -2694,6 +2695,32 @@ addActionHandler('loadChannelRecommendations', async (global, actions, payload):
|
||||
setGlobal(global);
|
||||
});
|
||||
|
||||
addActionHandler('loadBotRecommendations', async (global, actions, payload): Promise<void> => {
|
||||
const { userId } = payload;
|
||||
const user = selectChat(global, userId);
|
||||
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await callApi('fetchBotsRecommendations', {
|
||||
user,
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { similarBots, count } = result;
|
||||
|
||||
const users = buildCollectionByKey(similarBots, 'id');
|
||||
|
||||
global = getGlobal();
|
||||
global = addUsers(global, users);
|
||||
global = addSimilarBots(global, userId, Object.keys(users), count);
|
||||
setGlobal(global);
|
||||
});
|
||||
|
||||
addActionHandler('toggleChannelRecommendations', (global, actions, payload): ActionReturnType => {
|
||||
const { chatId } = payload;
|
||||
const chat = selectChat(global, chatId);
|
||||
|
||||
@ -220,6 +220,10 @@ function unsafeMigrateCache(cached: GlobalState, initialState: GlobalState) {
|
||||
cached.chats.similarChannelsById = initialState.chats.similarChannelsById;
|
||||
}
|
||||
|
||||
if (!cached.chats.similarBotsById) {
|
||||
cached.chats.similarBotsById = initialState.chats.similarBotsById;
|
||||
}
|
||||
|
||||
if (!cached.chats.lastMessageIds) {
|
||||
cached.chats.lastMessageIds = initialState.chats.lastMessageIds;
|
||||
}
|
||||
@ -472,6 +476,7 @@ function reduceChats<T extends GlobalState>(global: T): GlobalState['chats'] {
|
||||
return {
|
||||
...global.chats,
|
||||
similarChannelsById: {},
|
||||
similarBotsById: {},
|
||||
isFullyLoaded: {},
|
||||
loadingParameters: INITIAL_GLOBAL_STATE.chats.loadingParameters,
|
||||
byId: pickTruthy(global.chats.byId, idsToSave),
|
||||
|
||||
@ -115,6 +115,7 @@ export const INITIAL_GLOBAL_STATE: GlobalState = {
|
||||
byId: {},
|
||||
fullInfoById: {},
|
||||
similarChannelsById: {},
|
||||
similarBotsById: {},
|
||||
topicsInfoById: {},
|
||||
loadingParameters: {
|
||||
active: {},
|
||||
|
||||
@ -463,3 +463,24 @@ export function toggleSimilarChannels<T extends GlobalState>(
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function addSimilarBots<T extends GlobalState>(
|
||||
global: T,
|
||||
chatId: string,
|
||||
similarBotsIds: string[],
|
||||
count?: number,
|
||||
) {
|
||||
return {
|
||||
...global,
|
||||
chats: {
|
||||
...global.chats,
|
||||
similarBotsById: {
|
||||
...global.chats.similarBotsById,
|
||||
[chatId]: {
|
||||
similarBotsIds,
|
||||
count,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -323,6 +323,13 @@ export function selectSimilarChannelIds<T extends GlobalState>(
|
||||
return global.chats.similarChannelsById[chatId];
|
||||
}
|
||||
|
||||
export function selectSimilarBotsIds<T extends GlobalState>(
|
||||
global: T,
|
||||
chatId: string,
|
||||
) {
|
||||
return global.chats.similarBotsById[chatId];
|
||||
}
|
||||
|
||||
export function selectChatLastMessageId<T extends GlobalState>(
|
||||
global: T, chatId: string, listType: 'all' | 'saved' = 'all',
|
||||
) {
|
||||
|
||||
@ -1008,6 +1008,9 @@ export interface ActionPayloads {
|
||||
loadChannelRecommendations: {
|
||||
chatId?: string;
|
||||
};
|
||||
loadBotRecommendations: {
|
||||
userId: string;
|
||||
};
|
||||
toggleChannelRecommendations: {
|
||||
chatId: string;
|
||||
};
|
||||
|
||||
@ -57,6 +57,7 @@ import type {
|
||||
PerformanceType,
|
||||
Point,
|
||||
ServiceNotification,
|
||||
SimilarBotsInfo,
|
||||
Size,
|
||||
StarGiftCategory,
|
||||
StarsSubscriptions,
|
||||
@ -223,6 +224,8 @@ export type GlobalState = {
|
||||
count: number;
|
||||
}
|
||||
>;
|
||||
|
||||
similarBotsById: Record<string, SimilarBotsInfo>;
|
||||
};
|
||||
|
||||
messages: {
|
||||
|
||||
@ -1682,6 +1682,7 @@ channels.getChannelRecommendations#25a71742 flags:# channel:flags.0?InputChannel
|
||||
channels.searchPosts#d19f987b hashtag:string offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
|
||||
bots.setBotInfo#10cf3123 flags:# bot:flags.2?InputUser lang_code:string name:flags.3?string about:flags.0?string description:flags.1?string = Bool;
|
||||
bots.canSendMessage#1359f4e6 bot:InputUser = Bool;
|
||||
bots.getBotRecommendations#a1b70815 bot:InputUser = users.Users;
|
||||
bots.allowSendMessage#f132e3ef bot:InputUser = Updates;
|
||||
bots.invokeWebViewCustomMethod#87fc5e7 bot:InputUser custom_method:string params:DataJSON = DataJSON;
|
||||
bots.getPopularAppBots#c2510192 offset:string limit:int = bots.PopularAppBots;
|
||||
|
||||
@ -271,6 +271,7 @@
|
||||
"channels.getChannelRecommendations",
|
||||
"channels.searchPosts",
|
||||
"channels.reportSpam",
|
||||
"bots.getBotRecommendations",
|
||||
"bots.canSendMessage",
|
||||
"bots.allowSendMessage",
|
||||
"bots.invokeWebViewCustomMethod",
|
||||
|
||||
@ -375,6 +375,7 @@ export type ProfileTabType =
|
||||
| 'stories'
|
||||
| 'storiesArchive'
|
||||
| 'similarChannels'
|
||||
| 'similarBots'
|
||||
| 'dialogs'
|
||||
| 'gifts';
|
||||
export type SharedMediaType = 'media' | 'documents' | 'links' | 'audio' | 'voice';
|
||||
@ -619,6 +620,11 @@ export type ChatRequestedTranslations = {
|
||||
manualMessages?: Record<number, string>;
|
||||
};
|
||||
|
||||
export type SimilarBotsInfo = {
|
||||
similarBotsIds?: string[];
|
||||
count: number;
|
||||
};
|
||||
|
||||
export type ConfettiParams = OptionalCombine<{
|
||||
style?: ConfettiStyle;
|
||||
withStars?: boolean;
|
||||
|
||||
5
src/types/language.d.ts
vendored
5
src/types/language.d.ts
vendored
@ -1220,8 +1220,10 @@ export interface LangPair {
|
||||
'ProfileTabVoice': undefined;
|
||||
'ProfileTabSharedGroups': undefined;
|
||||
'ProfileTabSimilarChannels': undefined;
|
||||
'ProfileTabSimilarBots': undefined;
|
||||
'ActionUnsupportedTitle': undefined;
|
||||
'ActionUnsupportedDescription': undefined;
|
||||
'UnlockMoreSimilarBots': undefined;
|
||||
'GiftWasNotFound': undefined;
|
||||
'ViewButtonRequestJoin': undefined;
|
||||
'ViewButtonMessage': undefined;
|
||||
@ -1705,6 +1707,9 @@ export interface LangPairWithVariables<V extends unknown = LangVariable> {
|
||||
'LocationPermissionText': {
|
||||
'name': V;
|
||||
};
|
||||
'MoreSimilarBotsText': {
|
||||
'count': V;
|
||||
};
|
||||
}
|
||||
|
||||
export interface LangPairPlural {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user