From fc69c603a3c4656a1baddcf3b69a71c968b1f0a6 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Tue, 20 Apr 2021 18:19:58 +0300 Subject: [PATCH] Support `t.me/joinchat` (#1035) --- src/api/gramjs/methods/chats.ts | 16 ++++++++++++++++ src/api/gramjs/methods/index.ts | 2 +- src/components/common/SafeLink.tsx | 4 ++-- src/components/common/WebLink.tsx | 9 +++++++-- src/config.ts | 1 + src/lib/gramjs/tl/apiTl.js | 2 ++ src/lib/gramjs/tl/static/api.reduced.tl | 2 ++ src/modules/actions/api/bots.ts | 4 ++-- src/modules/actions/api/chats.ts | 25 +++++++++++++++++++++---- src/util/getReadableErrorText.ts | 5 +++++ 10 files changed, 59 insertions(+), 11 deletions(-) diff --git a/src/api/gramjs/methods/chats.ts b/src/api/gramjs/methods/chats.ts index ea947fde1..22a72d418 100644 --- a/src/api/gramjs/methods/chats.ts +++ b/src/api/gramjs/methods/chats.ts @@ -885,6 +885,22 @@ export async function migrateChat(chat: ApiChat) { return buildApiChatFromPreview(result.chats[1]); } +export async function openChatByInvite(hash: string) { + const result = await invokeRequest(new GramJs.messages.CheckChatInvite({ hash })); + + if (!result) { + return undefined; + } + + if (result instanceof GramJs.ChatInvite) { + await invokeRequest(new GramJs.messages.ImportChatInvite({ hash }), true); + + return undefined; + } else { + return buildApiChatFromPreview(result.chat); + } +} + function preparePeers( result: GramJs.messages.Dialogs | GramJs.messages.DialogsSlice | GramJs.messages.PeerDialogs, ) { diff --git a/src/api/gramjs/methods/index.ts b/src/api/gramjs/methods/index.ts index cf3793a2b..b343409d5 100644 --- a/src/api/gramjs/methods/index.ts +++ b/src/api/gramjs/methods/index.ts @@ -12,7 +12,7 @@ export { fetchChatFolders, editChatFolder, deleteChatFolder, fetchRecommendedChatFolders, getChatByUsername, togglePreHistoryHidden, updateChatDefaultBannedRights, updateChatMemberBannedRights, updateChatTitle, updateChatAbout, toggleSignatures, updateChatAdmin, fetchGroupsForDiscussion, setDiscussionGroup, - migrateChat, + migrateChat, openChatByInvite, } from './chats'; export { diff --git a/src/components/common/SafeLink.tsx b/src/components/common/SafeLink.tsx index cf2103d33..350bde5eb 100644 --- a/src/components/common/SafeLink.tsx +++ b/src/components/common/SafeLink.tsx @@ -3,7 +3,7 @@ import { withGlobal } from '../../lib/teact/teactn'; import convertPunycode from '../../lib/punycode'; import { GlobalActions } from '../../global/types'; -import { DEBUG, RE_TME_LINK } from '../../config'; +import { DEBUG, RE_TME_INVITE_LINK, RE_TME_LINK } from '../../config'; import { pick } from '../../util/iteratees'; import buildClassName from '../../util/buildClassName'; @@ -26,7 +26,7 @@ const SafeLink: FC = ({ const handleClick = useCallback((e: React.MouseEvent) => { if ( e.ctrlKey || e.altKey || e.shiftKey || e.metaKey - || !url || !url.match(RE_TME_LINK) + || !url || (!url.match(RE_TME_LINK) && !url.match(RE_TME_INVITE_LINK)) ) { return true; } diff --git a/src/components/common/WebLink.tsx b/src/components/common/WebLink.tsx index 788a36600..9bf5153b0 100644 --- a/src/components/common/WebLink.tsx +++ b/src/components/common/WebLink.tsx @@ -10,6 +10,7 @@ import { formatPastTimeShort } from '../../util/dateFormat'; import Media from './Media'; import Link from '../ui/Link'; +import SafeLink from './SafeLink'; import './WebLink.scss'; @@ -75,9 +76,13 @@ const WebLink: FC = ({ message, senderTitle, onMessageClick }) => { {truncatedDescription && ( {renderText(truncatedDescription)} )} - + {url.replace('mailto:', '') || displayUrl} - + {senderTitle &&
{renderText(senderTitle)}
} {senderTitle && ( diff --git a/src/config.ts b/src/config.ts index 8c5bf35eb..fffc6d555 100644 --- a/src/config.ts +++ b/src/config.ts @@ -106,6 +106,7 @@ export const CONTENT_TYPES_FOR_QUICK_UPLOAD = 'image/png,image/gif,image/jpeg,vi // eslint-disable-next-line max-len export const RE_LINK_TEMPLATE = '((ftp|https?):\\/\\/)?((www\\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6})\\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)'; export const RE_TME_LINK = /^(?:https?:\/\/)?(?:t\.me\/)([\d\w_]+)(?:\/([\d]+))?$/gm; +export const RE_TME_INVITE_LINK = /^(?:https?:\/\/)?(?:t\.me\/joinchat\/)([\d\w_]+)?$/gm; // MTProto constants export const SERVICE_NOTIFICATIONS_USER_ID = 777000; diff --git a/src/lib/gramjs/tl/apiTl.js b/src/lib/gramjs/tl/apiTl.js index e79b15331..7180e3fb6 100644 --- a/src/lib/gramjs/tl/apiTl.js +++ b/src/lib/gramjs/tl/apiTl.js @@ -901,6 +901,8 @@ messages.getStickers#43d4f2c emoticon:string hash:int = messages.Stickers; messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers; messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector = MessageMedia; messages.exportChatInvite#df7534c peer:InputPeer = ExportedChatInvite; +messages.checkChatInvite#3eadb1bb hash:string = ChatInvite; +messages.importChatInvite#6c50051c hash:string = Updates; messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet; messages.installStickerSet#c78fe460 stickerset:InputStickerSet archived:Bool = messages.StickerSetInstallResult; messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool; diff --git a/src/lib/gramjs/tl/static/api.reduced.tl b/src/lib/gramjs/tl/static/api.reduced.tl index e573b5d23..cf273263a 100644 --- a/src/lib/gramjs/tl/static/api.reduced.tl +++ b/src/lib/gramjs/tl/static/api.reduced.tl @@ -901,6 +901,8 @@ messages.getStickers#43d4f2c emoticon:string hash:int = messages.Stickers; messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers; messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector = MessageMedia; messages.exportChatInvite#df7534c peer:InputPeer = ExportedChatInvite; +messages.checkChatInvite#3eadb1bb hash:string = ChatInvite; +messages.importChatInvite#6c50051c hash:string = Updates; messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet; messages.installStickerSet#c78fe460 stickerset:InputStickerSet archived:Bool = messages.StickerSetInstallResult; messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool; diff --git a/src/modules/actions/api/bots.ts b/src/modules/actions/api/bots.ts index 75b47d800..1b63430e3 100644 --- a/src/modules/actions/api/bots.ts +++ b/src/modules/actions/api/bots.ts @@ -2,7 +2,7 @@ import { addReducer, getDispatch } from '../../../lib/teact/teactn'; import { ApiChat } from '../../../api/types'; -import { RE_TME_LINK } from '../../../config'; +import { RE_TME_INVITE_LINK, RE_TME_LINK } from '../../../config'; import { callApi } from '../../../api/gramjs'; import { selectChatMessage, selectCurrentChat } from '../../selectors'; @@ -14,7 +14,7 @@ addReducer('clickInlineButton', (global, actions, payload) => { actions.sendBotCommand({ command: button.value }); break; case 'url': - if (button.value.match(RE_TME_LINK)) { + if (button.value.match(RE_TME_INVITE_LINK) || button.value.match(RE_TME_LINK)) { actions.openTelegramLink({ url: button.value }); } else { window.open(button.value); diff --git a/src/modules/actions/api/chats.ts b/src/modules/actions/api/chats.ts index 6ed472aec..ab05e7924 100644 --- a/src/modules/actions/api/chats.ts +++ b/src/modules/actions/api/chats.ts @@ -12,6 +12,7 @@ import { ARCHIVED_FOLDER_ID, TOP_CHAT_MESSAGES_PRELOAD_LIMIT, CHAT_LIST_LOAD_SLICE, + RE_TME_INVITE_LINK, RE_TME_LINK, TIPS_USERNAME, } from '../../../config'; @@ -380,12 +381,28 @@ addReducer('toggleChatUnread', (global, actions, payload) => { addReducer('openTelegramLink', (global, actions, payload) => { const { url } = payload!; - const match = RE_TME_LINK.exec(url)!; + let match = RE_TME_INVITE_LINK.exec(url); - const username = match[1]; - const channelPostId = match[2] ? Number(match[2]) : undefined; + if (match) { + const hash = match[1]; - void openChatByUsername(actions, username, channelPostId); + (async () => { + const chat = await callApi('openChatByInvite', hash); + + if (!chat) { + return; + } + + actions.openChat({ id: chat.id }); + })(); + } else { + match = RE_TME_LINK.exec(url)!; + + const username = match[1]; + const channelPostId = match[2] ? Number(match[2]) : undefined; + + void openChatByUsername(actions, username, channelPostId); + } }); addReducer('openChatByUsername', (global, actions, payload) => { diff --git a/src/util/getReadableErrorText.ts b/src/util/getReadableErrorText.ts index 6e554c819..aaf5ab449 100644 --- a/src/util/getReadableErrorText.ts +++ b/src/util/getReadableErrorText.ts @@ -55,6 +55,11 @@ const READABLE_ERROR_MESSAGES: Record = { USER_KICKED: 'This user was kicked from this supergroup/channel', USER_NOT_MUTUAL_CONTACT: 'The provided user is not a mutual contact', USER_PRIVACY_RESTRICTED: 'The user\'s privacy settings do not allow you to do this', + INVITE_HASH_EMPTY: 'The invite hash is empty', + INVITE_HASH_EXPIRED: 'The invite link has expired', + INVITE_HASH_INVALID: 'The invite hash is invalid', + CHANNELS_TOO_MUCH: 'You have joined too many channels/supergroups', + USER_ALREADY_PARTICIPANT: 'You already in the group', }; export default function getReadableErrorText(error: ApiError) {