diff --git a/src/global/actions/api/globalSearch.ts b/src/global/actions/api/globalSearch.ts index 21767974b..6cd471e42 100644 --- a/src/global/actions/api/globalSearch.ts +++ b/src/global/actions/api/globalSearch.ts @@ -5,7 +5,7 @@ import type { ActionReturnType, GlobalState, TabArgs } from '../../types'; import { GLOBAL_SEARCH_SLICE, GLOBAL_TOPIC_SEARCH_SLICE } from '../../../config'; import { timestampPlusDay } from '../../../util/dateFormat'; -import { DeepLinkType, isDeepLink, tryParseDeepLink } from '../../../util/deepLinkParser'; +import { isDeepLink, tryParseDeepLink } from '../../../util/deepLinkParser'; import { getCurrentTabId } from '../../../util/establishMultitabRole'; import { buildCollectionByKey } from '../../../util/iteratees'; import { throttle } from '../../../util/schedulers'; @@ -172,9 +172,9 @@ async function searchMessagesGlobal( }); if (isDeepLink(query)) { const link = tryParseDeepLink(query); - if (link?.type === DeepLinkType.PublicMessageLink) { + if (link?.type === 'publicMessageLink') { messageLink = await getMessageByPublicLink(global, link); - } else if (link?.type === DeepLinkType.PrivateMessageLink) { + } else if (link?.type === 'privateMessageLink') { messageLink = await getMessageByPrivateLink(global, link); } } diff --git a/src/util/deepLinkParser.ts b/src/util/deepLinkParser.ts index 08ef0be08..cf1b1d536 100644 --- a/src/util/deepLinkParser.ts +++ b/src/util/deepLinkParser.ts @@ -5,16 +5,8 @@ export type DeepLinkMethod = 'resolve' | 'login' | 'passport' | 'settings' | 'jo 'setlanguage' | 'addtheme' | 'confirmphone' | 'socks' | 'proxy' | 'privatepost' | 'bg' | 'share' | 'msg' | 'msg_url' | 'invoice' | 'addlist' | 'boost' | 'giftcode'; -export enum DeepLinkType { - PublicMessageLink = 'PublicMessageLink', - PrivateMessageLink = 'PrivateMessageLink', - ShareLink = 'ShareLink', - ChatFolderLink = 'ChatFolderLink', - Unknown = 'Unknown', -} - interface PublicMessageLink { - type: DeepLinkType.PublicMessageLink; + type: 'publicMessageLink'; username: string; messageId: number; isSingle?: boolean; @@ -24,7 +16,7 @@ interface PublicMessageLink { } interface PrivateMessageLink { - type: DeepLinkType.PrivateMessageLink; + type: 'privateMessageLink'; channelId: string; messageId: number; isSingle?: boolean; @@ -34,20 +26,42 @@ interface PrivateMessageLink { } interface ShareLink { - type: DeepLinkType.ShareLink; + type: 'shareLink'; url: string; text?: string; } interface ChatFolderLink { - type: DeepLinkType.ChatFolderLink; + type: 'chatFolderLink'; slug: string; } -type DeepLink = PublicMessageLink | PrivateMessageLink | ShareLink | ChatFolderLink; +interface LoginCodeLink { + type: 'loginCodeLink'; + code: string; +} + +interface TelegramPassportLink { + type: 'telegramPassportLink'; + botId: number; + scope: string; + publicKey: string; + nonce: string; + callbackUrl?: string; + payload?: string; +} + +type DeepLink = + TelegramPassportLink | + LoginCodeLink | + PublicMessageLink | + PrivateMessageLink | + ShareLink | + ChatFolderLink; type BuilderParams = Record, string>; type BuilderReturnType = T | undefined; +type DeepLinkType = DeepLink['type'] | 'unknown'; const ELIGIBLE_HOSTNAMES = new Set(['t.me', 'telegram.me', 'telegram.dog']); @@ -84,7 +98,7 @@ function handleTgLink(url: URL) { const deepLinkType = getTgDeepLinkType(queryParams, pathParams, method); switch (deepLinkType) { - case DeepLinkType.PublicMessageLink: { + case 'publicMessageLink': { const { domain, post, single, thread, comment, t, } = queryParams; @@ -97,7 +111,7 @@ function handleTgLink(url: URL) { mediaTimestamp: t, }); } - case DeepLinkType.PrivateMessageLink: { + case 'privateMessageLink': { const { channel, post, single, thread, comment, t, } = queryParams; @@ -110,10 +124,21 @@ function handleTgLink(url: URL) { mediaTimestamp: t, }); } - case DeepLinkType.ShareLink: + case 'shareLink': return buildShareLink({ text: queryParams.text, url: queryParams.url }); - case DeepLinkType.ChatFolderLink: + case 'chatFolderLink': return buildChatFolderLink({ slug: queryParams.slug }); + case 'loginCodeLink': + return buildLoginCodeLink({ code: queryParams.code }); + case 'telegramPassportLink': + return buildTelegramPassportLink({ + botId: queryParams.bot_id, + scope: queryParams.scope, + publicKey: queryParams.public_key, + nonce: queryParams.nonce, + callbackUrl: queryParams.callback_url, + payload: queryParams.payload, + }); default: break; } @@ -129,7 +154,7 @@ function handleHttpLink(url: URL) { const deepLinkType = getHttpDeepLinkType(queryParams, pathParams); switch (deepLinkType) { - case DeepLinkType.PublicMessageLink: { + case 'publicMessageLink': { const { single, comment, t, } = queryParams; @@ -155,7 +180,7 @@ function handleHttpLink(url: URL) { mediaTimestamp: t, }); } - case DeepLinkType.PrivateMessageLink: { + case 'privateMessageLink': { const { single, comment, t, } = queryParams; @@ -181,11 +206,13 @@ function handleHttpLink(url: URL) { mediaTimestamp: t, }); } - case DeepLinkType.ShareLink: { + case 'shareLink': { return buildShareLink({ text: queryParams.text, url: queryParams.url }); } - case DeepLinkType.ChatFolderLink: + case 'chatFolderLink': return buildChatFolderLink({ slug: pathParams[1] }); + case 'loginCodeLink': + return buildLoginCodeLink({ code: pathParams[1] }); default: break; } @@ -195,63 +222,76 @@ function handleHttpLink(url: URL) { function getHttpDeepLinkType( queryParams: Record, pathParams: string[], -) { +): DeepLinkType { const len = pathParams.length; const method = pathParams[0]; if (len === 1) { if (method === 'share') { - return DeepLinkType.ShareLink; + return 'shareLink'; } } else if (len === 2) { if (method === 'addlist') { - return DeepLinkType.ChatFolderLink; + return 'chatFolderLink'; + } + if (method === 'login') { + return 'loginCodeLink'; } if (isUsernameValid(pathParams[0]) && isNumber(pathParams[1])) { - return DeepLinkType.PublicMessageLink; + return 'publicMessageLink'; } } else if (len === 3) { if (method === 'c' && pathParams.slice(1).every(isNumber)) { - return DeepLinkType.PrivateMessageLink; + return 'privateMessageLink'; } if (isUsernameValid(pathParams[0]) && pathParams.slice(1).every(isNumber)) { - return DeepLinkType.PublicMessageLink; + return 'publicMessageLink'; } } else if (len === 4) { if (method === 'c' && pathParams.slice(1).every(isNumber)) { - return DeepLinkType.PrivateMessageLink; + return 'privateMessageLink'; } } - return DeepLinkType.Unknown; + return 'unknown'; } function getTgDeepLinkType( queryParams: Record, pathParams: string[], method: DeepLinkMethod, -) { +): DeepLinkType { switch (method) { case 'resolve': { - const { domain, post } = queryParams; + const { + // eslint-disable-next-line @typescript-eslint/naming-convention + domain, post, bot_id, scope, public_key, nonce, + } = queryParams; + if (domain === 'telegrampassport' && bot_id && scope && public_key && nonce) { + return 'telegramPassportLink'; + } if (domain && post) { - return DeepLinkType.PublicMessageLink; + return 'publicMessageLink'; } break; } case 'privatepost': { const { channel, post } = queryParams; if (channel && post) { - return DeepLinkType.PrivateMessageLink; + return 'privateMessageLink'; } break; } case 'msg_url': - return DeepLinkType.ShareLink; + return 'shareLink'; case 'addlist': - return DeepLinkType.ChatFolderLink; + return 'chatFolderLink'; + case 'login': + return 'loginCodeLink'; + case 'passport': + return 'telegramPassportLink'; default: break; } - return DeepLinkType.Unknown; + return 'unknown'; } function buildShareLink(params: BuilderParams): BuilderReturnType { @@ -260,7 +300,7 @@ function buildShareLink(params: BuilderParams): BuilderReturnType): Build return undefined; } return { - type: DeepLinkType.PublicMessageLink, + type: 'publicMessageLink', username, messageId: Number(messageId), isSingle: isSingle === '', @@ -310,7 +350,7 @@ function buildPrivateMessageLink(params: BuilderParams): Bui return undefined; } return { - type: DeepLinkType.PrivateMessageLink, + type: 'privateMessageLink', channelId, messageId: Number(messageId), isSingle: isSingle === '', @@ -328,11 +368,49 @@ function buildChatFolderLink(params: BuilderParams): BuilderRetu return undefined; } return { - type: DeepLinkType.ChatFolderLink, + type: 'chatFolderLink', slug, }; } +function buildLoginCodeLink(params: BuilderParams): BuilderReturnType { + const { + code, + } = params; + if (!code) { + return undefined; + } + return { + type: 'loginCodeLink', + code, + }; +} + +function buildTelegramPassportLink( + params: BuilderParams, +): BuilderReturnType { + const { + botId, + scope, + publicKey, + nonce, + callbackUrl, + payload, + } = params; + if (!botId || !isNumber(botId) || !scope || !publicKey || !nonce) { + return undefined; + } + return { + type: 'telegramPassportLink', + botId: Number(botId), + scope, + publicKey, + nonce, + callbackUrl, + payload, + }; +} + function isNumber(s: string) { return /^-?\d+$/.test(s); }