DeepLinkParser: Add new types of the links (#4064)
This commit is contained in:
parent
fdd5c31623
commit
275ff8d938
@ -5,7 +5,7 @@ import type { ActionReturnType, GlobalState, TabArgs } from '../../types';
|
|||||||
|
|
||||||
import { GLOBAL_SEARCH_SLICE, GLOBAL_TOPIC_SEARCH_SLICE } from '../../../config';
|
import { GLOBAL_SEARCH_SLICE, GLOBAL_TOPIC_SEARCH_SLICE } from '../../../config';
|
||||||
import { timestampPlusDay } from '../../../util/dateFormat';
|
import { timestampPlusDay } from '../../../util/dateFormat';
|
||||||
import { DeepLinkType, isDeepLink, tryParseDeepLink } from '../../../util/deepLinkParser';
|
import { isDeepLink, tryParseDeepLink } from '../../../util/deepLinkParser';
|
||||||
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
||||||
import { buildCollectionByKey } from '../../../util/iteratees';
|
import { buildCollectionByKey } from '../../../util/iteratees';
|
||||||
import { throttle } from '../../../util/schedulers';
|
import { throttle } from '../../../util/schedulers';
|
||||||
@ -172,9 +172,9 @@ async function searchMessagesGlobal<T extends GlobalState>(
|
|||||||
});
|
});
|
||||||
if (isDeepLink(query)) {
|
if (isDeepLink(query)) {
|
||||||
const link = tryParseDeepLink(query);
|
const link = tryParseDeepLink(query);
|
||||||
if (link?.type === DeepLinkType.PublicMessageLink) {
|
if (link?.type === 'publicMessageLink') {
|
||||||
messageLink = await getMessageByPublicLink(global, link);
|
messageLink = await getMessageByPublicLink(global, link);
|
||||||
} else if (link?.type === DeepLinkType.PrivateMessageLink) {
|
} else if (link?.type === 'privateMessageLink') {
|
||||||
messageLink = await getMessageByPrivateLink(global, link);
|
messageLink = await getMessageByPrivateLink(global, link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,16 +5,8 @@ export type DeepLinkMethod = 'resolve' | 'login' | 'passport' | 'settings' | 'jo
|
|||||||
'setlanguage' | 'addtheme' | 'confirmphone' | 'socks' | 'proxy' | 'privatepost' | 'bg' | 'share' | 'msg' | 'msg_url' |
|
'setlanguage' | 'addtheme' | 'confirmphone' | 'socks' | 'proxy' | 'privatepost' | 'bg' | 'share' | 'msg' | 'msg_url' |
|
||||||
'invoice' | 'addlist' | 'boost' | 'giftcode';
|
'invoice' | 'addlist' | 'boost' | 'giftcode';
|
||||||
|
|
||||||
export enum DeepLinkType {
|
|
||||||
PublicMessageLink = 'PublicMessageLink',
|
|
||||||
PrivateMessageLink = 'PrivateMessageLink',
|
|
||||||
ShareLink = 'ShareLink',
|
|
||||||
ChatFolderLink = 'ChatFolderLink',
|
|
||||||
Unknown = 'Unknown',
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PublicMessageLink {
|
interface PublicMessageLink {
|
||||||
type: DeepLinkType.PublicMessageLink;
|
type: 'publicMessageLink';
|
||||||
username: string;
|
username: string;
|
||||||
messageId: number;
|
messageId: number;
|
||||||
isSingle?: boolean;
|
isSingle?: boolean;
|
||||||
@ -24,7 +16,7 @@ interface PublicMessageLink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface PrivateMessageLink {
|
interface PrivateMessageLink {
|
||||||
type: DeepLinkType.PrivateMessageLink;
|
type: 'privateMessageLink';
|
||||||
channelId: string;
|
channelId: string;
|
||||||
messageId: number;
|
messageId: number;
|
||||||
isSingle?: boolean;
|
isSingle?: boolean;
|
||||||
@ -34,20 +26,42 @@ interface PrivateMessageLink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ShareLink {
|
interface ShareLink {
|
||||||
type: DeepLinkType.ShareLink;
|
type: 'shareLink';
|
||||||
url: string;
|
url: string;
|
||||||
text?: string;
|
text?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ChatFolderLink {
|
interface ChatFolderLink {
|
||||||
type: DeepLinkType.ChatFolderLink;
|
type: 'chatFolderLink';
|
||||||
slug: string;
|
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<T extends DeepLink> = Record<keyof Omit<T, 'type'>, string>;
|
type BuilderParams<T extends DeepLink> = Record<keyof Omit<T, 'type'>, string>;
|
||||||
type BuilderReturnType<T extends DeepLink> = T | undefined;
|
type BuilderReturnType<T extends DeepLink> = T | undefined;
|
||||||
|
type DeepLinkType = DeepLink['type'] | 'unknown';
|
||||||
|
|
||||||
const ELIGIBLE_HOSTNAMES = new Set(['t.me', 'telegram.me', 'telegram.dog']);
|
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);
|
const deepLinkType = getTgDeepLinkType(queryParams, pathParams, method);
|
||||||
switch (deepLinkType) {
|
switch (deepLinkType) {
|
||||||
case DeepLinkType.PublicMessageLink: {
|
case 'publicMessageLink': {
|
||||||
const {
|
const {
|
||||||
domain, post, single, thread, comment, t,
|
domain, post, single, thread, comment, t,
|
||||||
} = queryParams;
|
} = queryParams;
|
||||||
@ -97,7 +111,7 @@ function handleTgLink(url: URL) {
|
|||||||
mediaTimestamp: t,
|
mediaTimestamp: t,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
case DeepLinkType.PrivateMessageLink: {
|
case 'privateMessageLink': {
|
||||||
const {
|
const {
|
||||||
channel, post, single, thread, comment, t,
|
channel, post, single, thread, comment, t,
|
||||||
} = queryParams;
|
} = queryParams;
|
||||||
@ -110,10 +124,21 @@ function handleTgLink(url: URL) {
|
|||||||
mediaTimestamp: t,
|
mediaTimestamp: t,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
case DeepLinkType.ShareLink:
|
case 'shareLink':
|
||||||
return buildShareLink({ text: queryParams.text, url: queryParams.url });
|
return buildShareLink({ text: queryParams.text, url: queryParams.url });
|
||||||
case DeepLinkType.ChatFolderLink:
|
case 'chatFolderLink':
|
||||||
return buildChatFolderLink({ slug: queryParams.slug });
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -129,7 +154,7 @@ function handleHttpLink(url: URL) {
|
|||||||
|
|
||||||
const deepLinkType = getHttpDeepLinkType(queryParams, pathParams);
|
const deepLinkType = getHttpDeepLinkType(queryParams, pathParams);
|
||||||
switch (deepLinkType) {
|
switch (deepLinkType) {
|
||||||
case DeepLinkType.PublicMessageLink: {
|
case 'publicMessageLink': {
|
||||||
const {
|
const {
|
||||||
single, comment, t,
|
single, comment, t,
|
||||||
} = queryParams;
|
} = queryParams;
|
||||||
@ -155,7 +180,7 @@ function handleHttpLink(url: URL) {
|
|||||||
mediaTimestamp: t,
|
mediaTimestamp: t,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
case DeepLinkType.PrivateMessageLink: {
|
case 'privateMessageLink': {
|
||||||
const {
|
const {
|
||||||
single, comment, t,
|
single, comment, t,
|
||||||
} = queryParams;
|
} = queryParams;
|
||||||
@ -181,11 +206,13 @@ function handleHttpLink(url: URL) {
|
|||||||
mediaTimestamp: t,
|
mediaTimestamp: t,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
case DeepLinkType.ShareLink: {
|
case 'shareLink': {
|
||||||
return buildShareLink({ text: queryParams.text, url: queryParams.url });
|
return buildShareLink({ text: queryParams.text, url: queryParams.url });
|
||||||
}
|
}
|
||||||
case DeepLinkType.ChatFolderLink:
|
case 'chatFolderLink':
|
||||||
return buildChatFolderLink({ slug: pathParams[1] });
|
return buildChatFolderLink({ slug: pathParams[1] });
|
||||||
|
case 'loginCodeLink':
|
||||||
|
return buildLoginCodeLink({ code: pathParams[1] });
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -195,63 +222,76 @@ function handleHttpLink(url: URL) {
|
|||||||
function getHttpDeepLinkType(
|
function getHttpDeepLinkType(
|
||||||
queryParams: Record<string, string>,
|
queryParams: Record<string, string>,
|
||||||
pathParams: string[],
|
pathParams: string[],
|
||||||
) {
|
): DeepLinkType {
|
||||||
const len = pathParams.length;
|
const len = pathParams.length;
|
||||||
const method = pathParams[0];
|
const method = pathParams[0];
|
||||||
if (len === 1) {
|
if (len === 1) {
|
||||||
if (method === 'share') {
|
if (method === 'share') {
|
||||||
return DeepLinkType.ShareLink;
|
return 'shareLink';
|
||||||
}
|
}
|
||||||
} else if (len === 2) {
|
} else if (len === 2) {
|
||||||
if (method === 'addlist') {
|
if (method === 'addlist') {
|
||||||
return DeepLinkType.ChatFolderLink;
|
return 'chatFolderLink';
|
||||||
|
}
|
||||||
|
if (method === 'login') {
|
||||||
|
return 'loginCodeLink';
|
||||||
}
|
}
|
||||||
if (isUsernameValid(pathParams[0]) && isNumber(pathParams[1])) {
|
if (isUsernameValid(pathParams[0]) && isNumber(pathParams[1])) {
|
||||||
return DeepLinkType.PublicMessageLink;
|
return 'publicMessageLink';
|
||||||
}
|
}
|
||||||
} else if (len === 3) {
|
} else if (len === 3) {
|
||||||
if (method === 'c' && pathParams.slice(1).every(isNumber)) {
|
if (method === 'c' && pathParams.slice(1).every(isNumber)) {
|
||||||
return DeepLinkType.PrivateMessageLink;
|
return 'privateMessageLink';
|
||||||
}
|
}
|
||||||
if (isUsernameValid(pathParams[0]) && pathParams.slice(1).every(isNumber)) {
|
if (isUsernameValid(pathParams[0]) && pathParams.slice(1).every(isNumber)) {
|
||||||
return DeepLinkType.PublicMessageLink;
|
return 'publicMessageLink';
|
||||||
}
|
}
|
||||||
} else if (len === 4) {
|
} else if (len === 4) {
|
||||||
if (method === 'c' && pathParams.slice(1).every(isNumber)) {
|
if (method === 'c' && pathParams.slice(1).every(isNumber)) {
|
||||||
return DeepLinkType.PrivateMessageLink;
|
return 'privateMessageLink';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return DeepLinkType.Unknown;
|
return 'unknown';
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTgDeepLinkType(
|
function getTgDeepLinkType(
|
||||||
queryParams: Record<string, string>,
|
queryParams: Record<string, string>,
|
||||||
pathParams: string[],
|
pathParams: string[],
|
||||||
method: DeepLinkMethod,
|
method: DeepLinkMethod,
|
||||||
) {
|
): DeepLinkType {
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case 'resolve': {
|
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) {
|
if (domain && post) {
|
||||||
return DeepLinkType.PublicMessageLink;
|
return 'publicMessageLink';
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'privatepost': {
|
case 'privatepost': {
|
||||||
const { channel, post } = queryParams;
|
const { channel, post } = queryParams;
|
||||||
if (channel && post) {
|
if (channel && post) {
|
||||||
return DeepLinkType.PrivateMessageLink;
|
return 'privateMessageLink';
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'msg_url':
|
case 'msg_url':
|
||||||
return DeepLinkType.ShareLink;
|
return 'shareLink';
|
||||||
case 'addlist':
|
case 'addlist':
|
||||||
return DeepLinkType.ChatFolderLink;
|
return 'chatFolderLink';
|
||||||
|
case 'login':
|
||||||
|
return 'loginCodeLink';
|
||||||
|
case 'passport':
|
||||||
|
return 'telegramPassportLink';
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return DeepLinkType.Unknown;
|
return 'unknown';
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildShareLink(params: BuilderParams<ShareLink>): BuilderReturnType<ShareLink> {
|
function buildShareLink(params: BuilderParams<ShareLink>): BuilderReturnType<ShareLink> {
|
||||||
@ -260,7 +300,7 @@ function buildShareLink(params: BuilderParams<ShareLink>): BuilderReturnType<Sha
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: DeepLinkType.ShareLink,
|
type: 'shareLink',
|
||||||
url,
|
url,
|
||||||
text,
|
text,
|
||||||
};
|
};
|
||||||
@ -283,7 +323,7 @@ function buildPublicMessageLink(params: BuilderParams<PublicMessageLink>): Build
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: DeepLinkType.PublicMessageLink,
|
type: 'publicMessageLink',
|
||||||
username,
|
username,
|
||||||
messageId: Number(messageId),
|
messageId: Number(messageId),
|
||||||
isSingle: isSingle === '',
|
isSingle: isSingle === '',
|
||||||
@ -310,7 +350,7 @@ function buildPrivateMessageLink(params: BuilderParams<PrivateMessageLink>): Bui
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: DeepLinkType.PrivateMessageLink,
|
type: 'privateMessageLink',
|
||||||
channelId,
|
channelId,
|
||||||
messageId: Number(messageId),
|
messageId: Number(messageId),
|
||||||
isSingle: isSingle === '',
|
isSingle: isSingle === '',
|
||||||
@ -328,11 +368,49 @@ function buildChatFolderLink(params: BuilderParams<ChatFolderLink>): BuilderRetu
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: DeepLinkType.ChatFolderLink,
|
type: 'chatFolderLink',
|
||||||
slug,
|
slug,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildLoginCodeLink(params: BuilderParams<LoginCodeLink>): BuilderReturnType<LoginCodeLink> {
|
||||||
|
const {
|
||||||
|
code,
|
||||||
|
} = params;
|
||||||
|
if (!code) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: 'loginCodeLink',
|
||||||
|
code,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildTelegramPassportLink(
|
||||||
|
params: BuilderParams<TelegramPassportLink>,
|
||||||
|
): BuilderReturnType<TelegramPassportLink> {
|
||||||
|
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) {
|
function isNumber(s: string) {
|
||||||
return /^-?\d+$/.test(s);
|
return /^-?\d+$/.test(s);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user