tg:// links
This commit is contained in:
parent
ac61377d7c
commit
015c0b31c4
@ -65,6 +65,8 @@ import { toast, toastNew } from '../../components/toast';
|
|||||||
import debounce from '../../helpers/schedulers/debounce';
|
import debounce from '../../helpers/schedulers/debounce';
|
||||||
import { pause } from '../../helpers/schedulers/pause';
|
import { pause } from '../../helpers/schedulers/pause';
|
||||||
import appMessagesIdsManager from './appMessagesIdsManager';
|
import appMessagesIdsManager from './appMessagesIdsManager';
|
||||||
|
import { InternalLink, InternalLinkTypeMap, INTERNAL_LINK_TYPE } from './internalLink';
|
||||||
|
import RichTextProcessor from '../richtextprocessor';
|
||||||
|
|
||||||
//console.log('appImManager included33!');
|
//console.log('appImManager included33!');
|
||||||
|
|
||||||
@ -265,8 +267,7 @@ export class AppImManager {
|
|||||||
appMessagesManager.sendText(this.chat.peerId, '/' + command + (bot ? '@' + bot : ''));
|
appMessagesManager.sendText(this.chat.peerId, '/' + command + (bot ? '@' + bot : ''));
|
||||||
|
|
||||||
//console.log(command, bot);
|
//console.log(command, bot);
|
||||||
},
|
}
|
||||||
parseUriParams: true
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.addAnchorListener<{uriParams: {hashtag: string}}>({
|
this.addAnchorListener<{uriParams: {hashtag: string}}>({
|
||||||
@ -278,23 +279,193 @@ export class AppImManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.chat.initSearch('#' + hashtag + ' ');
|
this.chat.initSearch('#' + hashtag + ' ');
|
||||||
},
|
}
|
||||||
parseUriParams: true
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.addAnchorListener<{pathnameParams: ['addstickers', string]}>({
|
this.addAnchorListener<{pathnameParams: ['addstickers', string]}>({
|
||||||
name: 'addstickers',
|
name: 'addstickers',
|
||||||
callback: ({pathnameParams}) => {
|
callback: ({pathnameParams}) => {
|
||||||
new PopupStickers({id: pathnameParams[1]}).show();
|
const link: InternalLink = {
|
||||||
},
|
_: INTERNAL_LINK_TYPE.STICKER_SET,
|
||||||
parsePathname: true
|
set: pathnameParams[1]
|
||||||
|
};
|
||||||
|
|
||||||
|
this.processInternalLink(link);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.addAnchorListener<{pathnameParams: ['joinchat', string]}>({
|
this.addAnchorListener<{pathnameParams: ['joinchat', string]}>({
|
||||||
name: 'joinchat',
|
name: 'joinchat',
|
||||||
callback: ({pathnameParams}) => {
|
callback: ({pathnameParams}) => {
|
||||||
|
const link: InternalLink = {
|
||||||
|
_: INTERNAL_LINK_TYPE.JOIN_CHAT,
|
||||||
|
invite: pathnameParams[1]
|
||||||
|
};
|
||||||
|
|
||||||
|
this.processInternalLink(link);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addAnchorListener<{
|
||||||
|
// pathnameParams: ['c', string, string],
|
||||||
|
// uriParams: {thread?: number}
|
||||||
|
// } | {
|
||||||
|
// pathnameParams: [string, string?],
|
||||||
|
// uriParams: {comment?: number}
|
||||||
|
pathnameParams: ['c', string, string] | [string, string?],
|
||||||
|
uriParams: {thread?: string, comment?: string} | {comment?: string}
|
||||||
|
}>({
|
||||||
|
name: 'im',
|
||||||
|
callback: async({pathnameParams, uriParams}) => {
|
||||||
|
let link: InternalLink;
|
||||||
|
if(pathnameParams[0] === 'c') {
|
||||||
|
link = {
|
||||||
|
_: INTERNAL_LINK_TYPE.PRIVATE_POST,
|
||||||
|
channel: pathnameParams[1],
|
||||||
|
post: pathnameParams[2],
|
||||||
|
thread: 'thread' in uriParams ? uriParams.thread : undefined,
|
||||||
|
comment: uriParams.comment
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
link = {
|
||||||
|
_: INTERNAL_LINK_TYPE.MESSAGE,
|
||||||
|
domain: pathnameParams[0],
|
||||||
|
post: pathnameParams[1],
|
||||||
|
comment: uriParams.comment
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.processInternalLink(link);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addAnchorListener<{
|
||||||
|
uriParams: {
|
||||||
|
domain: string,
|
||||||
|
|
||||||
|
// telegrampassport
|
||||||
|
scope?: string,
|
||||||
|
nonce?: string,
|
||||||
|
payload?: string,
|
||||||
|
bot_id?: string,
|
||||||
|
public_key?: string,
|
||||||
|
callback_url?: string,
|
||||||
|
|
||||||
|
// regular
|
||||||
|
start?: string,
|
||||||
|
startgroup?: string,
|
||||||
|
game?: string,
|
||||||
|
voicechat?: string,
|
||||||
|
post?: string,
|
||||||
|
thread?: string,
|
||||||
|
comment?: string
|
||||||
|
}
|
||||||
|
}>({
|
||||||
|
name: 'resolve',
|
||||||
|
protocol: 'tg',
|
||||||
|
callback: ({uriParams}) => {
|
||||||
|
let link: InternalLink;
|
||||||
|
if(uriParams.domain === 'telegrampassport') {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
link = this.makeLink(INTERNAL_LINK_TYPE.MESSAGE, uriParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.processInternalLink(link);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addAnchorListener<{
|
||||||
|
uriParams: {
|
||||||
|
channel: string,
|
||||||
|
post: string,
|
||||||
|
thread?: string,
|
||||||
|
comment?: string
|
||||||
|
}
|
||||||
|
}>({
|
||||||
|
name: 'privatepost',
|
||||||
|
protocol: 'tg',
|
||||||
|
callback: ({uriParams}) => {
|
||||||
|
const link = this.makeLink(INTERNAL_LINK_TYPE.PRIVATE_POST, uriParams);
|
||||||
|
this.processInternalLink(link);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addAnchorListener<{
|
||||||
|
uriParams: {
|
||||||
|
set: string
|
||||||
|
}
|
||||||
|
}>({
|
||||||
|
name: 'addstickers',
|
||||||
|
protocol: 'tg',
|
||||||
|
callback: ({uriParams}) => {
|
||||||
|
const link = this.makeLink(INTERNAL_LINK_TYPE.STICKER_SET, uriParams);
|
||||||
|
this.processInternalLink(link);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addAnchorListener<{
|
||||||
|
uriParams: {
|
||||||
|
invite: string
|
||||||
|
}
|
||||||
|
}>({
|
||||||
|
name: 'joinchat',
|
||||||
|
protocol: 'tg',
|
||||||
|
callback: ({uriParams}) => {
|
||||||
|
const link = this.makeLink(INTERNAL_LINK_TYPE.JOIN_CHAT, uriParams);
|
||||||
|
this.processInternalLink(link);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.onHashChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
private makeLink<T extends INTERNAL_LINK_TYPE>(type: T, uriParams: Omit<InternalLinkTypeMap[T], '_'>) {
|
||||||
|
return {
|
||||||
|
_: type,
|
||||||
|
...uriParams
|
||||||
|
} as any as InternalLinkTypeMap[T];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async processInternalLink(link: InternalLink) {
|
||||||
|
switch(link?._) {
|
||||||
|
case INTERNAL_LINK_TYPE.MESSAGE: {
|
||||||
|
const postId = link.post ? appMessagesIdsManager.generateMessageId(+link.post) : undefined;
|
||||||
|
const commentId = link.comment ? appMessagesIdsManager.generateMessageId(+link.comment) : undefined;
|
||||||
|
|
||||||
|
this.openUsername(link.domain, postId, undefined, commentId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case INTERNAL_LINK_TYPE.PRIVATE_POST: {
|
||||||
|
const peerId = -+link.channel;
|
||||||
|
|
||||||
|
const chat = appChatsManager.getChat(-peerId);
|
||||||
|
if(chat.deleted) {
|
||||||
|
try {
|
||||||
|
await appChatsManager.resolveChannel(-peerId);
|
||||||
|
} catch(err) {
|
||||||
|
toastNew({langPackKey: 'LinkNotFound'});
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const postId = appMessagesIdsManager.generateMessageId(+link.post);
|
||||||
|
const threadId = link.thread ? appMessagesIdsManager.generateMessageId(+link.thread) : undefined;
|
||||||
|
|
||||||
|
if(threadId) this.openThread(peerId, postId, threadId);
|
||||||
|
else this.setInnerPeer(peerId, postId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case INTERNAL_LINK_TYPE.STICKER_SET: {
|
||||||
|
new PopupStickers({id: link.set}).show();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case INTERNAL_LINK_TYPE.JOIN_CHAT: {
|
||||||
apiManager.invokeApi('messages.checkChatInvite', {
|
apiManager.invokeApi('messages.checkChatInvite', {
|
||||||
hash: pathnameParams[1]
|
hash: link.invite
|
||||||
}).then(chatInvite => {
|
}).then(chatInvite => {
|
||||||
if((chatInvite as ChatInvite.chatInvitePeek).chat) {
|
if((chatInvite as ChatInvite.chatInvitePeek).chat) {
|
||||||
appChatsManager.saveApiChat((chatInvite as ChatInvite.chatInvitePeek).chat, true);
|
appChatsManager.saveApiChat((chatInvite as ChatInvite.chatInvitePeek).chat, true);
|
||||||
@ -308,76 +479,39 @@ export class AppImManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
new PopupJoinChatInvite(pathnameParams[1], chatInvite).show();
|
new PopupJoinChatInvite(link.invite, chatInvite).show();
|
||||||
}, (err) => {
|
}, (err) => {
|
||||||
if(err.type === 'INVITE_HASH_EXPIRED') {
|
if(err.type === 'INVITE_HASH_EXPIRED') {
|
||||||
toast(i18n('InviteExpired'));
|
toast(i18n('InviteExpired'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
break;
|
||||||
parsePathname: true
|
|
||||||
});
|
|
||||||
|
|
||||||
this.addAnchorListener<{
|
|
||||||
// pathnameParams: ['c', string, string],
|
|
||||||
// uriParams: {thread?: number}
|
|
||||||
// } | {
|
|
||||||
// pathnameParams: [string, string?],
|
|
||||||
// uriParams: {comment?: number}
|
|
||||||
pathnameParams: ['c', string, string] | [string, string?],
|
|
||||||
uriParams: {thread?: number} | {comment?: number}
|
|
||||||
}>({
|
|
||||||
name: 'im',
|
|
||||||
callback: async(params) => {
|
|
||||||
console.log(params);
|
|
||||||
|
|
||||||
const {pathnameParams, uriParams} = params;
|
|
||||||
if(pathnameParams[0] === 'c') {
|
|
||||||
const peerId = -+pathnameParams[1];
|
|
||||||
|
|
||||||
const chat = appChatsManager.getChat(-peerId);
|
|
||||||
if(chat.deleted) {
|
|
||||||
try {
|
|
||||||
await appChatsManager.resolveChannel(-peerId);
|
|
||||||
} catch(err) {
|
|
||||||
toastNew({langPackKey: 'LinkNotFound'});
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const postId = appMessagesIdsManager.generateMessageId(+pathnameParams[2]);
|
default: {
|
||||||
const threadId = 'thread' in uriParams ? appMessagesIdsManager.generateMessageId(+uriParams.thread) : undefined;
|
this.log.warn('Not supported internal link:', link);
|
||||||
|
break;
|
||||||
if(threadId) this.openThread(peerId, postId, threadId);
|
}
|
||||||
else this.setInnerPeer(peerId, postId);
|
|
||||||
} else {
|
|
||||||
const username = pathnameParams[0];
|
|
||||||
const postId = pathnameParams[1] ? appMessagesIdsManager.generateMessageId(+pathnameParams[1]) : undefined;
|
|
||||||
const commentId = 'comment' in uriParams ? appMessagesIdsManager.generateMessageId(uriParams.comment) : undefined;
|
|
||||||
|
|
||||||
this.openUsername(username, postId, undefined, commentId);
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
parsePathname: true,
|
|
||||||
parseUriParams: true
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private addAnchorListener<Params extends {pathnameParams?: any, uriParams?: any}>(options: {
|
private addAnchorListener<Params extends {pathnameParams?: any, uriParams?: any}>(options: {
|
||||||
name: 'showMaskedAlert' | 'execBotCommand' | 'searchByHashtag' | 'addstickers' | 'joinchat' | 'im',
|
name: 'showMaskedAlert' | 'execBotCommand' | 'searchByHashtag' | 'addstickers' | 'joinchat' | 'im' |
|
||||||
callback: (params: Params, element: HTMLAnchorElement) => boolean | any,
|
'resolve' | 'privatepost' | 'addstickers',
|
||||||
parsePathname?: boolean,
|
protocol?: 'tg',
|
||||||
parseUriParams?: boolean,
|
callback: (params: Params, element?: HTMLAnchorElement) => boolean | any,
|
||||||
|
noPathnameParams?: boolean,
|
||||||
|
noUriParams?: boolean
|
||||||
}) {
|
}) {
|
||||||
(window as any)[options.name] = (element: HTMLAnchorElement/* , e: Event */) => {
|
(window as any)[(options.protocol ? options.protocol + '_' : '') + options.name] = (element?: HTMLAnchorElement/* , e: Event */) => {
|
||||||
cancelEvent(null);
|
cancelEvent(null);
|
||||||
|
|
||||||
const href = element.href;
|
const href = element.href;
|
||||||
let pathnameParams: any[];
|
let pathnameParams: any[];
|
||||||
let uriParams: any;
|
let uriParams: any;
|
||||||
|
|
||||||
if(options.parsePathname) pathnameParams = new URL(element.href).pathname.split('/').slice(1);
|
if(!options.noPathnameParams) pathnameParams = new URL(element.href).pathname.split('/').slice(1);
|
||||||
if(options.parseUriParams) uriParams = this.parseUriParams(href);
|
if(!options.noUriParams) uriParams = this.parseUriParams(href);
|
||||||
|
|
||||||
const res = options.callback({pathnameParams, uriParams} as Params, element);
|
const res = options.callback({pathnameParams, uriParams} as Params, element);
|
||||||
return res === undefined ? res : false;
|
return res === undefined ? res : false;
|
||||||
@ -402,6 +536,17 @@ export class AppImManager {
|
|||||||
|
|
||||||
this.log('hashchange', hash, splitted[0], params);
|
this.log('hashchange', hash, splitted[0], params);
|
||||||
|
|
||||||
|
if(params.tgaddr) {
|
||||||
|
appNavigationController.replaceState();
|
||||||
|
const {onclick} = RichTextProcessor.wrapUrl(params.tgaddr);
|
||||||
|
if(onclick) {
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = params.tgaddr;
|
||||||
|
(window as any)[onclick](a);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch(splitted[0]) {
|
switch(splitted[0]) {
|
||||||
case '#/im': {
|
case '#/im': {
|
||||||
const p = params.p;
|
const p = params.p;
|
||||||
|
48
src/lib/appManagers/internalLink.ts
Normal file
48
src/lib/appManagers/internalLink.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* https://github.com/morethanwords/tweb
|
||||||
|
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||||
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
export enum INTERNAL_LINK_TYPE {
|
||||||
|
MESSAGE,
|
||||||
|
PRIVATE_POST,
|
||||||
|
STICKER_SET,
|
||||||
|
JOIN_CHAT
|
||||||
|
};
|
||||||
|
|
||||||
|
export type InternalLink = InternalLink.InternalLinkMessage | InternalLink.InternalLinkPrivatePost | InternalLink.InternalLinkStickerSet | InternalLink.InternalLinkJoinChat;
|
||||||
|
|
||||||
|
export namespace InternalLink {
|
||||||
|
export interface InternalLinkMessage {
|
||||||
|
_: INTERNAL_LINK_TYPE.MESSAGE,
|
||||||
|
domain: string,
|
||||||
|
post?: string,
|
||||||
|
comment?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InternalLinkPrivatePost {
|
||||||
|
_: INTERNAL_LINK_TYPE.PRIVATE_POST,
|
||||||
|
channel: string,
|
||||||
|
post: string,
|
||||||
|
thread?: string,
|
||||||
|
comment?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InternalLinkStickerSet {
|
||||||
|
_: INTERNAL_LINK_TYPE.STICKER_SET,
|
||||||
|
set: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InternalLinkJoinChat {
|
||||||
|
_: INTERNAL_LINK_TYPE.JOIN_CHAT,
|
||||||
|
invite: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type InternalLinkTypeMap = {
|
||||||
|
[INTERNAL_LINK_TYPE.MESSAGE]: InternalLink.InternalLinkMessage,
|
||||||
|
[INTERNAL_LINK_TYPE.PRIVATE_POST]: InternalLink.InternalLinkPrivatePost,
|
||||||
|
[INTERNAL_LINK_TYPE.STICKER_SET]: InternalLink.InternalLinkStickerSet,
|
||||||
|
[INTERNAL_LINK_TYPE.JOIN_CHAT]: InternalLink.InternalLinkJoinChat
|
||||||
|
};
|
@ -39,9 +39,10 @@ export class TelegramMeWebManager {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const path = `_websync_?authed=${canRedirect ? '1' : '0'}&version=${encodeURIComponent(App.version + ' ' + App.suffix)}`;
|
||||||
const urls = [
|
const urls = [
|
||||||
'//telegram.me/_websync_?authed=' + (canRedirect ? '1' : '0'),
|
'//telegram.me/' + path,
|
||||||
'//t.me/_websync_?authed=' + (canRedirect ? '1' : '0')
|
'//t.me/' + path
|
||||||
];
|
];
|
||||||
|
|
||||||
const promises = urls.map(url => {
|
const promises = urls.map(url => {
|
||||||
|
@ -801,8 +801,7 @@ namespace RichTextProcessor {
|
|||||||
url = 'https://' + url;
|
url = 'https://' + url;
|
||||||
}
|
}
|
||||||
|
|
||||||
let tgMeMatch;
|
let tgMeMatch, telescoPeMatch, tgMatch;
|
||||||
let telescoPeMatch;
|
|
||||||
let onclick: string;
|
let onclick: string;
|
||||||
/* if(unsafe === 2) {
|
/* if(unsafe === 2) {
|
||||||
url = 'tg://unsafe_url?url=' + encodeURIComponent(url);
|
url = 'tg://unsafe_url?url=' + encodeURIComponent(url);
|
||||||
@ -824,7 +823,12 @@ namespace RichTextProcessor {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if((telescoPeMatch = url.match(/^(?:https?:\/\/)?telesco\.pe\/([^/?]+)\/(\d+)/))) {
|
} else if((telescoPeMatch = url.match(/^(?:https?:\/\/)?telesco\.pe\/([^/?]+)\/(\d+)/))) {
|
||||||
url = 'tg://resolve?domain=' + telescoPeMatch[1] + '&post=' + telescoPeMatch[2];
|
onclick = 'im';
|
||||||
|
} else if((tgMatch = url.match(/tg:(?:\/\/)?(.+?)(?:\?|$)/))) {
|
||||||
|
onclick = 'tg_' + tgMatch[1];
|
||||||
|
if(!(window as any)[onclick]) {
|
||||||
|
onclick = undefined;
|
||||||
|
}
|
||||||
}/* else if(unsafe) {
|
}/* else if(unsafe) {
|
||||||
url = 'tg://unsafe_url?url=' + encodeURIComponent(url);
|
url = 'tg://unsafe_url?url=' + encodeURIComponent(url);
|
||||||
} */
|
} */
|
||||||
|
Loading…
Reference in New Issue
Block a user