From ee4d05c9e525ecde8a5fe0a287950a61381c2815 Mon Sep 17 00:00:00 2001 From: morethanwords Date: Fri, 16 Oct 2020 23:03:30 +0300 Subject: [PATCH] Fix some entities Small refactor of RichTextProcessor --- src/layer.d.ts | 84 +- src/lib/appManagers/appDialogsManager.ts | 26 +- src/lib/appManagers/appMessagesManager.ts | 2 +- src/lib/richtextprocessor.ts | 1303 +++++++++--------- src/lib/services.ts | 9 +- src/scripts/generate_mtproto_types.js | 7 +- src/scripts/in/schema_additional_params.json | 120 ++ 7 files changed, 866 insertions(+), 685 deletions(-) diff --git a/src/layer.d.ts b/src/layer.d.ts index fd094ce7..e161171a 100644 --- a/src/layer.d.ts +++ b/src/layer.d.ts @@ -3795,125 +3795,166 @@ export namespace ReplyMarkup { /** * @link https://core.telegram.org/type/MessageEntity */ -export type MessageEntity = MessageEntity.messageEntityUnknown | MessageEntity.messageEntityMention | MessageEntity.messageEntityHashtag | MessageEntity.messageEntityBotCommand | MessageEntity.messageEntityUrl | MessageEntity.messageEntityEmail | MessageEntity.messageEntityBold | MessageEntity.messageEntityItalic | MessageEntity.messageEntityCode | MessageEntity.messageEntityPre | MessageEntity.messageEntityTextUrl | MessageEntity.messageEntityMentionName | MessageEntity.inputMessageEntityMentionName | MessageEntity.messageEntityPhone | MessageEntity.messageEntityCashtag | MessageEntity.messageEntityUnderline | MessageEntity.messageEntityStrike | MessageEntity.messageEntityBlockquote | MessageEntity.messageEntityBankCard; +export type MessageEntity = MessageEntity.messageEntityUnknown | MessageEntity.messageEntityMention | MessageEntity.messageEntityHashtag | MessageEntity.messageEntityBotCommand | MessageEntity.messageEntityUrl | MessageEntity.messageEntityEmail | MessageEntity.messageEntityBold | MessageEntity.messageEntityItalic | MessageEntity.messageEntityCode | MessageEntity.messageEntityPre | MessageEntity.messageEntityTextUrl | MessageEntity.messageEntityMentionName | MessageEntity.inputMessageEntityMentionName | MessageEntity.messageEntityPhone | MessageEntity.messageEntityCashtag | MessageEntity.messageEntityUnderline | MessageEntity.messageEntityStrike | MessageEntity.messageEntityBlockquote | MessageEntity.messageEntityBankCard | MessageEntity.messageEntityEmoji | MessageEntity.messageEntityHighlight | MessageEntity.messageEntityLinebreak; export namespace MessageEntity { export type messageEntityUnknown = { _: 'messageEntityUnknown', offset: number, - length: number + length: number, + nested?: Array }; export type messageEntityMention = { _: 'messageEntityMention', offset: number, - length: number + length: number, + nested?: Array }; export type messageEntityHashtag = { _: 'messageEntityHashtag', offset: number, - length: number + length: number, + nested?: Array }; export type messageEntityBotCommand = { _: 'messageEntityBotCommand', offset: number, - length: number + length: number, + nested?: Array }; export type messageEntityUrl = { _: 'messageEntityUrl', offset: number, - length: number + length: number, + nested?: Array }; export type messageEntityEmail = { _: 'messageEntityEmail', offset: number, - length: number + length: number, + nested?: Array }; export type messageEntityBold = { _: 'messageEntityBold', offset: number, - length: number + length: number, + nested?: Array }; export type messageEntityItalic = { _: 'messageEntityItalic', offset: number, - length: number + length: number, + nested?: Array }; export type messageEntityCode = { _: 'messageEntityCode', offset: number, - length: number + length: number, + nested?: Array }; export type messageEntityPre = { _: 'messageEntityPre', offset: number, length: number, - language: string + language: string, + nested?: Array }; export type messageEntityTextUrl = { _: 'messageEntityTextUrl', offset: number, length: number, - url: string + url: string, + nested?: Array }; export type messageEntityMentionName = { _: 'messageEntityMentionName', offset: number, length: number, - user_id: number + user_id: number, + nested?: Array }; export type inputMessageEntityMentionName = { _: 'inputMessageEntityMentionName', offset: number, length: number, - user_id: InputUser + user_id: InputUser, + nested?: Array }; export type messageEntityPhone = { _: 'messageEntityPhone', offset: number, - length: number + length: number, + nested?: Array }; export type messageEntityCashtag = { _: 'messageEntityCashtag', offset: number, - length: number + length: number, + nested?: Array }; export type messageEntityUnderline = { _: 'messageEntityUnderline', offset: number, - length: number + length: number, + nested?: Array }; export type messageEntityStrike = { _: 'messageEntityStrike', offset: number, - length: number + length: number, + nested?: Array }; export type messageEntityBlockquote = { _: 'messageEntityBlockquote', offset: number, - length: number + length: number, + nested?: Array }; export type messageEntityBankCard = { _: 'messageEntityBankCard', offset: number, - length: number + length: number, + nested?: Array + }; + + export type messageEntityEmoji = { + _: 'messageEntityEmoji', + offset?: number, + length?: number, + unicode?: string, + nested?: Array + }; + + export type messageEntityHighlight = { + _: 'messageEntityHighlight', + offset?: number, + length?: number, + nested?: Array + }; + + export type messageEntityLinebreak = { + _: 'messageEntityLinebreak', + offset?: number, + length?: number, + nested?: Array }; } @@ -8918,6 +8959,9 @@ export interface ConstructorDeclMap { 'messageReplyHeader': MessageReplyHeader.messageReplyHeader, 'messageReplies': MessageReplies.messageReplies, 'peerBlocked': PeerBlocked.peerBlocked, + 'messageEntityEmoji': MessageEntity.messageEntityEmoji, + 'messageEntityHighlight': MessageEntity.messageEntityHighlight, + 'messageEntityLinebreak': MessageEntity.messageEntityLinebreak, } export type InvokeAfterMsg = { diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index 454cca03..fa2b6096 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -674,20 +674,20 @@ export class AppDialogsManager { let lastMessageText = appMessagesManager.getRichReplyText(lastMessage, ''); let messageText = lastMessage.message; - let entities = RichTextProcessor.parseEntities(messageText.replace(/\n/g, ' '), {noLinebreakers: true}); - let regExp = new RegExp(escapeRegExp(highlightWord), 'gi'); - let match: any; - - if(!entities) entities = []; - let found = false; - while((match = regExp.exec(messageText)) !== null) { - entities.push({_: 'messageEntityHighlight', length: highlightWord.length, offset: match.index}); - found = true; - } + let entities = RichTextProcessor.parseEntities(messageText.replace(/\n/g, ' ')); + let regExp = new RegExp(escapeRegExp(highlightWord), 'gi'); + let match: any; + + if(!entities) entities = []; + let found = false; + while((match = regExp.exec(messageText)) !== null) { + entities.push({_: 'messageEntityHighlight', length: highlightWord.length, offset: match.index}); + found = true; + } - if(found) { - entities.sort((a: any, b: any) => a.offset - b.offset); - } + if(found) { + entities.sort((a: any, b: any) => a.offset - b.offset); + } let messageWrapped = RichTextProcessor.wrapRichText(messageText, { noLinebreaks: true, diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index 90dfc7b6..1f7e39a0 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -2498,7 +2498,7 @@ export class AppMessagesManager { // * 80 for chatlist in landscape orientation text = limitSymbols(text, 75, 80); - let entities = RichTextProcessor.parseEntities(text.replace(/\n/g, ' '), {noLinebreakers: true}); + let entities = RichTextProcessor.parseEntities(text.replace(/\n/g, ' ')); messageWrapped = RichTextProcessor.wrapRichText(text, { noLinebreaks: true, diff --git a/src/lib/richtextprocessor.ts b/src/lib/richtextprocessor.ts index b82335d3..8d351bce 100644 --- a/src/lib/richtextprocessor.ts +++ b/src/lib/richtextprocessor.ts @@ -4,18 +4,17 @@ import Config from './config'; import emojiRegExp from '../emoji/regex'; import { encodeEmoji } from '../emoji'; import { MOUNT_CLASS_TO } from './mtproto/mtproto_config'; +import { MessageEntity } from '../layer'; -var EmojiHelper = { +const EmojiHelper = { emojiMap: (code: string) => { return code; }, shortcuts: [] as any, emojis: [] as any }; -var emojiData = Config.Emoji; -var emojiSupported = navigator.userAgent.search(/OS X|iPhone|iPad|iOS/i) != -1/* && false *//* || true */, - emojiCode; +const emojiData = Config.Emoji; -var alphaCharsRegExp = 'a-z' + +const alphaCharsRegExp = 'a-z' + '\\u00c0-\\u00d6\\u00d8-\\u00f6\\u00f8-\\u00ff' + // Latin-1 '\\u0100-\\u024f' + // Latin Extended A and B '\\u0253\\u0254\\u0256\\u0257\\u0259\\u025b\\u0263\\u0268\\u026f\\u0272\\u0289\\u028b' + // IPA Extensions @@ -40,10 +39,10 @@ var alphaCharsRegExp = 'a-z' + '\\uff21-\\uff3a\\uff41-\\uff5a' + // full width Alphabet '\\uff66-\\uff9f' + // half width Katakana '\\uffa1-\\uffdc'; // half width Hangul (Korean) -var alphaNumericRegExp = '0-9\_' + alphaCharsRegExp; -var domainAddChars = '\u00b7'; +const alphaNumericRegExp = '0-9\_' + alphaCharsRegExp; +const domainAddChars = '\u00b7'; // Based on Regular Expression for URL validation by Diego Perini -var urlRegExp = '((?:https?|ftp)://|mailto:)?' + +const urlRegExp = '((?:https?|ftp)://|mailto:)?' + // user:pass authentication '(?:\\S{1,64}(?::\\S{0,64})?@)?' + '(?:' + @@ -61,742 +60,748 @@ var urlRegExp = '((?:https?|ftp)://|mailto:)?' + '(?::\\d{2,5})?' + // resource path '(?:/(?:\\S{0,255}[^\\s.;,(\\[\\]{}<>"\'])?)?'; -var usernameRegExp = '[a-zA-Z\\d_]{5,32}'; -var botCommandRegExp = '\\/([a-zA-Z\\d_]{1,32})(?:@(' + usernameRegExp + '))?(\\b|$)'; -var fullRegExp = new RegExp('(^| )(@)(' + usernameRegExp + ')|(' + urlRegExp + ')|(\\n)|(' + emojiRegExp + ')|(^|[\\s\\(\\]])(#[' + alphaNumericRegExp + ']{2,64})|(^|\\s)' + botCommandRegExp, 'i') -var emailRegExp = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; -var markdownTestRegExp = /[`_*@]/; -var markdownRegExp = /(^|\s|\n)(````?)([\s\S]+?)(````?)([\s\n\.,:?!;]|$)|(^|\s)(`|\*\*|__)([^\n]+?)\7([\s\.,:?!;]|$)|@(\d+)\s*\((.+?)\)/m; -var siteHashtags: any = { +const usernameRegExp = '[a-zA-Z\\d_]{5,32}'; +const botCommandRegExp = '\\/([a-zA-Z\\d_]{1,32})(?:@(' + usernameRegExp + '))?(\\b|$)'; +const fullRegExp = new RegExp('(^| )(@)(' + usernameRegExp + ')|(' + urlRegExp + ')|(\\n)|(' + emojiRegExp + ')|(^|[\\s\\(\\]])(#[' + alphaNumericRegExp + ']{2,64})|(^|\\s)' + botCommandRegExp, 'i') +const emailRegExp = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; +const markdownTestRegExp = /[`_*@]/; +const markdownRegExp = /(^|\s|\n)(````?)([\s\S]+?)(````?)([\s\n\.,:?!;]|$)|(^|\s)(`|\*\*|__)([^\n]+?)\7([\s\.,:?!;]|$)|@(\d+)\s*\((.+?)\)/m; +const siteHashtags: {[siteName: string]: string} = { Telegram: 'tg://search_hashtag?hashtag={1}', Twitter: 'https://twitter.com/hashtag/{1}', Instagram: 'https://instagram.com/explore/tags/{1}/', 'Google Plus': 'https://plus.google.com/explore/{1}' }; -var siteMentions: any = { +const siteMentions: {[siteName: string]: string} = { Telegram: '#/im?p=%40{1}', Twitter: 'https://twitter.com/{1}', Instagram: 'https://instagram.com/{1}/', GitHub: 'https://github.com/{1}' }; -var markdownEntities = { +const markdownEntities = { '`': 'messageEntityCode', '**': 'messageEntityBold', '__': 'messageEntityItalic' }; -function getEmojiSpritesheetCoords(emojiCode: string) { - let unified = encodeEmoji(emojiCode)/* .replace(/(-fe0f|fe0f)/g, '') */; +namespace RichTextProcessor { + export const emojiSupported = navigator.userAgent.search(/OS X|iPhone|iPad|iOS/i) != -1/* && false *//* || true */; - if(unified == '1f441-200d-1f5e8') { - unified = '1f441-fe0f-200d-1f5e8-fe0f'; - } - - if(!emojiData.hasOwnProperty(unified)/* && !emojiData.hasOwnProperty(unified.replace(/(-fe0f|fe0f)/g, '')) */) { - //if(!emojiData.hasOwnProperty(emojiCode) && !emojiData.hasOwnProperty(emojiCode.replace(/[\ufe0f\u200d]/g, ''))) { - //console.error('lol', unified); - return null; + export function getEmojiSpritesheetCoords(emojiCode: string) { + let unified = encodeEmoji(emojiCode)/* .replace(/(-fe0f|fe0f)/g, '') */; + + if(unified == '1f441-200d-1f5e8') { + unified = '1f441-fe0f-200d-1f5e8-fe0f'; + } + + if(!emojiData.hasOwnProperty(unified)/* && !emojiData.hasOwnProperty(unified.replace(/(-fe0f|fe0f)/g, '')) */) { + //if(!emojiData.hasOwnProperty(emojiCode) && !emojiData.hasOwnProperty(emojiCode.replace(/[\ufe0f\u200d]/g, ''))) { + //console.error('lol', unified); + return null; + } + + return unified.replace(/(-fe0f|fe0f)/g, ''); } - return unified.replace(/(-fe0f|fe0f)/g, ''); -} -function parseEntities(text: string, options = {}) { - var match; - var raw = text, url; - var entities = [], matchIndex; - var rawOffset = 0; - // var start = tsNow() - while((match = raw.match(fullRegExp))) { - matchIndex = rawOffset + match.index; - - //console.log('parseEntities match:', match); - - if(match[3]) { // mentions - entities.push({ - _: 'messageEntityMention', - offset: matchIndex + match[1].length, - length: match[2].length + match[3].length - }); - } else if(match[4]) { - if(emailRegExp.test(match[4])) { // email + export function parseEntities(text: string) { + var match; + var raw = text, url; + const entities: MessageEntity[] = []; + let matchIndex; + var rawOffset = 0; + // var start = tsNow() + while((match = raw.match(fullRegExp))) { + matchIndex = rawOffset + match.index; + + //console.log('parseEntities match:', match); + + if(match[3]) { // mentions entities.push({ - _: 'messageEntityEmail', - offset: matchIndex, - length: match[4].length + _: 'messageEntityMention', + offset: matchIndex + match[1].length, + length: match[2].length + match[3].length }); - } else { - var url: any = false; - var protocol = match[5]; - var tld = match[6]; - var excluded = ''; - if(tld) { // URL - if(!protocol && (tld.substr(0, 4) === 'xn--' || Config.TLD.indexOf(tld.toLowerCase()) !== -1)) { - protocol = 'http://'; - } - - if(protocol) { - var balanced = checkBrackets(match[4]); - if (balanced.length !== match[4].length) { - excluded = match[4].substring(balanced.length); - match[4] = balanced; + } else if(match[4]) { + if(emailRegExp.test(match[4])) { // email + entities.push({ + _: 'messageEntityEmail', + offset: matchIndex, + length: match[4].length + }); + } else { + var url: any = false; + var protocol = match[5]; + var tld = match[6]; + var excluded = ''; + if(tld) { // URL + if(!protocol && (tld.substr(0, 4) === 'xn--' || Config.TLD.indexOf(tld.toLowerCase()) !== -1)) { + protocol = 'http://'; } - - url = (match[5] ? '' : protocol) + match[4]; + + if(protocol) { + var balanced = checkBrackets(match[4]); + if (balanced.length !== match[4].length) { + excluded = match[4].substring(balanced.length); + match[4] = balanced; + } + + url = (match[5] ? '' : protocol) + match[4]; + } + } else { // IP address + url = (match[5] ? '' : 'http://') + match[4]; + } + + if (url) { + entities.push({ + _: 'messageEntityUrl', + offset: matchIndex, + length: match[4].length + }); } - } else { // IP address - url = (match[5] ? '' : 'http://') + match[4]; } - - if (url) { + } else if(match[7]) { // New line + entities.push({ + _: 'messageEntityLinebreak', + offset: matchIndex, + length: 1 + }); + } else if(match[8]) { // Emoji + //console.log('hit', match[8]); + let emojiCoords = getEmojiSpritesheetCoords(match[8]); + if(emojiCoords) { entities.push({ - _: 'messageEntityUrl', + _: 'messageEntityEmoji', offset: matchIndex, - length: match[4].length + length: match[8].length, + unicode: emojiCoords }); } - } - } else if(match[7]) { // New line - entities.push({ - _: 'messageEntityLinebreak', - offset: matchIndex, - length: 1 - }); - } else if(match[8]) { // Emoji - //console.log('hit', match[8]); - let emojiCoords = getEmojiSpritesheetCoords(match[8]); - if(emojiCoords) { + } else if(match[11]) { // Hashtag entities.push({ - _: 'messageEntityEmoji', - offset: matchIndex, - length: match[8].length, - unicode: emojiCoords + _: 'messageEntityHashtag', + offset: matchIndex + (match[10] ? match[10].length : 0), + length: match[11].length + }); + } else if(match[12]) { // Bot command + entities.push({ + _: 'messageEntityBotCommand', + offset: matchIndex + (match[11] ? match[11].length : 0), + length: 1 + match[12].length + (match[13] ? 1 + match[13].length : 0) }); } - } else if(match[10]) { // Hashtag - entities.push({ - _: 'messageEntityHashtag', - offset: matchIndex + (match[9] ? match[9].length : 0), - length: match[10].length - }); - } else if(match[12]) { // Bot command - entities.push({ - _: 'messageEntityBotCommand', - offset: matchIndex + (match[11] ? match[11].length : 0), - length: 1 + match[12].length + (match[13] ? 1 + match[13].length : 0) - }); + + raw = raw.substr(match.index + match[0].length); + rawOffset += match.index + match[0].length; } - - raw = raw.substr(match.index + match[0].length); - rawOffset += match.index + match[0].length; + + // if (entities.length) { + // console.log('parse entities', text, entities.slice()) + // } + return entities; } - // if (entities.length) { - // console.log('parse entities', text, entities.slice()) - // } - return entities -} -function parseEmojis(text: string) { - return text.replace(/:([a-z0-9\-\+\*_]+?):/gi, function (all, shortcut) { - var emojiCode = EmojiHelper.shortcuts[shortcut] - if (emojiCode !== undefined) { - return EmojiHelper.emojis[emojiCode][0] - } - return all - }) -} -function parseMarkdown(text: string, entities: any[], noTrim?: any) { -  if(!markdownTestRegExp.test(text)) { - return noTrim ? text : text.trim(); - } + /* export function parseEmojis(text: string) { + return text.replace(/:([a-z0-9\-\+\*_]+?):/gi, function (all, shortcut) { + var emojiCode = EmojiHelper.shortcuts[shortcut] + if (emojiCode !== undefined) { + return EmojiHelper.emojis[emojiCode][0] + } + return all + }) + } */ - var raw = text; - var match; - var newText: any = []; - var rawOffset = 0; - var matchIndex; - while (match = raw.match(markdownRegExp)) { - matchIndex = rawOffset + match.index - newText.push(raw.substr(0, match.index)) - var text = (match[3] || match[8] || match[11]) - rawOffset -= text.length - text = text.replace(/^\s+|\s+$/g, '') - rawOffset += text.length - if (text.match(/^`*$/)) { - newText.push(match[0]) + export function parseMarkdown(text: string, entities: MessageEntity[], noTrim?: any) { +   if(!markdownTestRegExp.test(text)) { + return noTrim ? text : text.trim(); } - else if (match[3]) { // pre - if (match[5] == '\n') { - match[5] = '' - rawOffset -= 1 + + var raw = text; + var match; + var newText: any = []; + var rawOffset = 0; + var matchIndex; + while (match = raw.match(markdownRegExp)) { + matchIndex = rawOffset + match.index + newText.push(raw.substr(0, match.index)) + var text = (match[3] || match[8] || match[11]) + rawOffset -= text.length + text = text.replace(/^\s+|\s+$/g, '') + rawOffset += text.length + if (text.match(/^`*$/)) { + newText.push(match[0]) } - newText.push(match[1] + text + match[5]) - entities.push({ - _: 'messageEntityPre', - language: '', - offset: matchIndex + match[1].length, - length: text.length - }) - rawOffset -= match[2].length + match[4].length - } else if (match[7]) { // code|italic|bold - newText.push(match[6] + text + match[9]) - entities.push({ - // @ts-ignore - _: markdownEntities[match[7]], - offset: matchIndex + match[6].length, - length: text.length - }) - rawOffset -= match[7].length * 2 - } else if (match[11]) { // custom mention - newText.push(text) - entities.push({ - _: 'messageEntityMentionName', - user_id: match[10], - offset: matchIndex, - length: text.length - }) - rawOffset -= match[0].length - text.length + else if (match[3]) { // pre + if (match[5] == '\n') { + match[5] = '' + rawOffset -= 1 + } + newText.push(match[1] + text + match[5]) + entities.push({ + _: 'messageEntityPre', + language: '', + offset: matchIndex + match[1].length, + length: text.length + }) + rawOffset -= match[2].length + match[4].length + } else if (match[7]) { // code|italic|bold + newText.push(match[6] + text + match[9]) + entities.push({ + // @ts-ignore + _: markdownEntities[match[7]], + offset: matchIndex + match[6].length, + length: text.length + }) + rawOffset -= match[7].length * 2 + } else if (match[11]) { // custom mention + newText.push(text) + entities.push({ + _: 'messageEntityMentionName', + user_id: +match[10], + offset: matchIndex, + length: text.length + }) + rawOffset -= match[0].length - text.length + } + raw = raw.substr(match.index + match[0].length) + rawOffset += match.index + match[0].length } - raw = raw.substr(match.index + match[0].length) - rawOffset += match.index + match[0].length - } - newText.push(raw) - newText = newText.join('') - if (!newText.replace(/\s+/g, '').length) { - newText = text - entities.splice(0, entities.length) - } - if (!entities.length && !noTrim) { - newText = newText.trim() - } - return newText -} -function mergeEntities(currentEntities: any[], newEntities: any[], fromApi: any) { - var totalEntities = newEntities.slice(); - var i; - var len = currentEntities.length; - var j; - var len2 = newEntities.length; - var startJ = 0; - var curEntity; - var newEntity; - var start, end; - var cStart, cEnd; - var bad; - for(i = 0; i < len; i++) { - curEntity = currentEntities[i]; - if (fromApi && - curEntity._ != 'messageEntityLinebreak' && - curEntity._ != 'messageEntityEmoji') { - continue; + newText.push(raw) + newText = newText.join('') + if (!newText.replace(/\s+/g, '').length) { + newText = text + entities.splice(0, entities.length) } - // console.log('s', curEntity, newEntities); - start = curEntity.offset; - end = start + curEntity.length; - bad = false; - for(j = startJ; j < len2; j++) { - newEntity = newEntities[j]; - cStart = newEntity.offset; - cEnd = cStart + newEntity.length; - if(cStart <= start) { - startJ = j; - } + if (!entities.length && !noTrim) { + newText = newText.trim() + } + return newText + } - if(start >= cStart && start < cEnd || - end > cStart && end <= cEnd) { - // console.log('bad', curEntity, newEntity) - if(fromApi && - start >= cStart && end <= cEnd) { - if(newEntity.nested === undefined) { - newEntity.nested = []; + export function mergeEntities(currentEntities: MessageEntity[], newEntities: MessageEntity[], fromApi?: boolean) { + const totalEntities = newEntities.slice(); + const newLength = newEntities.length; + let startJ = 0; + for(let i = 0, length = currentEntities.length; i < length; i++) { + const curEntity = currentEntities[i]; + /* if(fromApi && + curEntity._ != 'messageEntityLinebreak' && + curEntity._ != 'messageEntityEmoji') { + continue; + } */ + // console.log('s', curEntity, newEntities); + const start = curEntity.offset; + const end = start + curEntity.length; + let bad = false; + for(let j = startJ; j < newLength; j++) { + const newEntity = newEntities[j]; + const cStart = newEntity.offset; + const cEnd = cStart + newEntity.length; + if(cStart <= start) { + startJ = j; + } + + if(start >= cStart && start < cEnd || + end > cStart && end <= cEnd) { + // console.log('bad', curEntity, newEntity) + if(fromApi && start >= cStart && end <= cEnd) { + if(newEntity.nested === undefined) { + newEntity.nested = []; + } + + curEntity.offset -= cStart; + newEntity.nested.push(copy(curEntity)); } - - curEntity.offset -= cStart; - newEntity.nested.push(copy(curEntity)); + + bad = true; + break; + } + + if(cStart >= end) { + break; } - - bad = true; - break; } - - if(cStart >= end) { - break; + + if(bad) { + continue; } + + totalEntities.push(curEntity); } + + totalEntities.sort((a, b) => { + return a.offset - b.offset; + }); + // console.log('merge', currentEntities, newEntities, totalEntities) + return totalEntities; + } - if(bad) { - continue; + export function wrapRichNestedText(text: string, nested: MessageEntity[], options: any) { + if(nested === undefined) { + return encodeEntities(text); } - - totalEntities.push(curEntity); + + options.hasNested = true; + return wrapRichText(text, {entities: nested, nested: true}); } - totalEntities.sort((a, b) => { - return a.offset - b.offset; - }); - // console.log('merge', currentEntities, newEntities, totalEntities) - return totalEntities; -} -function wrapRichNestedText(text: string, nested: any, options: any) { - if(nested === undefined) { - return encodeEntities(text); - } + export function wrapRichText(text: string, options: Partial<{ + entities: MessageEntity[], + contextSite: string, + highlightUsername: string, + noLinks: boolean, + noLinebreaks: boolean, + noCommands: boolean, + fromBot: boolean, + noTextFormat: boolean, + nested?: boolean, + contextHashtag?: string + }> = {}) { + if(!text || !text.length) { + return ''; + } + + const entities = options.entities || parseEntities(text); + const contextSite = options.contextSite || 'Telegram'; + const contextExternal = contextSite != 'Telegram'; + + //console.log('wrapRichText got entities:', text, entities); + const html: string[] = []; + let lastOffset = 0; + for(let i = 0, len = entities.length; i < len; i++) { + const entity = entities[i]; + if(entity.offset > lastOffset) { + html.push( + encodeEntities(text.substr(lastOffset, entity.offset - lastOffset)) + ); + } else if(entity.offset < lastOffset) { + continue; + } + + let skipEntity = false; + const entityText = text.substr(entity.offset, entity.length); + switch(entity._) { + case 'messageEntityMention': + var contextUrl = !options.noLinks && siteMentions[contextSite] + if (!contextUrl) { + skipEntity = true + break + } + var username = entityText.substr(1) + var attr = '' + if (options.highlightUsername && + options.highlightUsername.toLowerCase() == username.toLowerCase()) { + attr = 'class="im_message_mymention"' + } + html.push( + '', + encodeEntities(entityText), + '' + ) + break; - options.hasNested = true; - return wrapRichText(text, {entities: nested, nested: true}); -} -function wrapRichText(text: string, options: Partial<{ - entities: any, - contextSite: string, - highlightUsername: string, - noLinks: boolean, - noLinebreaks: boolean, - noCommands: boolean, - fromBot: boolean, - noTextFormat: boolean, - nested?: boolean, - contextHashtag?: string -}> = {}) { - if(!text || !text.length) { - return '' - } + case 'messageEntityMentionName': + if(options.noLinks) { + skipEntity = true; + break; + } - var entities = options.entities; - var contextSite = options.contextSite || 'Telegram'; - var contextExternal = contextSite != 'Telegram'; - var emojiFound = false; - if(entities === undefined) { - entities = parseEntities(text, options); - } + html.push( + '', + encodeEntities(entityText), + '' + ); + break; - //console.log('wrapRichText got entities:', text, entities); - var len = entities.length; - var entity; - var entityText; - var skipEntity; - var url; - var html = []; - var lastOffset = 0; - for(var i = 0; i < len; i++) { - entity = entities[i]; - if(entity.offset > lastOffset) { - html.push( - encodeEntities(text.substr(lastOffset, entity.offset - lastOffset)) - ); - } else if(entity.offset < lastOffset) { - continue; - } + case 'messageEntityHashtag': + var contextUrl = !options.noLinks && siteHashtags[contextSite]; + if(!contextUrl) { + skipEntity = true; + break; + } + + var hashtag = entityText.substr(1); + html.push( + '', + encodeEntities(entityText), + '' + ); + break; + + case 'messageEntityEmail': + if(options.noLinks) { + skipEntity = true; + break; + } - skipEntity = false; - entityText = text.substr(entity.offset, entity.length); - switch(entity._) { - case 'messageEntityMention': - var contextUrl = !options.noLinks && siteMentions[contextSite] - if (!contextUrl) { - skipEntity = true - break - } - var username = entityText.substr(1) - var attr = '' - if (options.highlightUsername && - options.highlightUsername.toLowerCase() == username.toLowerCase()) { - attr = 'class="im_message_mymention"' - } - html.push( - '', - encodeEntities(entityText), - '' - ) - break - case 'messageEntityMentionName': - if (options.noLinks) { - skipEntity = true - break - } - html.push( - '', - encodeEntities(entityText), - '' - ) - break - case 'messageEntityHashtag': - var contextUrl = !options.noLinks && siteHashtags[contextSite] - if (!contextUrl) { - skipEntity = true - break - } - var hashtag = entityText.substr(1) - html.push( - '', - encodeEntities(entityText), - '' - ) - break - case 'messageEntityEmail': - if (options.noLinks) { - skipEntity = true - break - } - html.push( - '', - encodeEntities(entityText), - '' - ) - break - case 'messageEntityUrl': - case 'messageEntityTextUrl': - var inner - if (entity._ == 'messageEntityTextUrl') { - url = entity.url - url = wrapUrl(url, true) - inner = wrapRichNestedText(entityText, entity.nested, options) - } else { - url = wrapUrl(entityText, false) - inner = encodeEntities(replaceUrlEncodings(entityText)) - } - if (options.noLinks) { - html.push(inner); - } else { html.push( '', - inner, + encodeEntities(entityText), '' - ) - } - break - case 'messageEntityLinebreak': - html.push(options.noLinebreaks ? ' ' : '
') - break - case 'messageEntityEmoji': - html.push(emojiSupported ? - `${encodeEntities(entityText)}` : - `${encodeEntities(entityText)}`); - - emojiFound = true; - break - case 'messageEntityBotCommand': - if (options.noLinks || options.noCommands || contextExternal) { - skipEntity = true - break - } - var command = entityText.substr(1) - var bot - var atPos - if ((atPos = command.indexOf('@')) != -1) { - bot = command.substr(atPos + 1) - command = command.substr(0, atPos) - } else { - bot = options.fromBot - } - html.push( - '', - encodeEntities(entityText), - '' - ) - break - case 'messageEntityBold': - if(options.noTextFormat) { - html.push(wrapRichNestedText(entityText, entity.nested, options)); + ); break; - } - - html.push( - '', - wrapRichNestedText(entityText, entity.nested, options), - '' - ) - break - case 'messageEntityItalic': - if(options.noTextFormat) { - html.push(wrapRichNestedText(entityText, entity.nested, options)); + + case 'messageEntityUrl': + case 'messageEntityTextUrl': + let inner: string; + let url: string; + if (entity._ == 'messageEntityTextUrl') { + url = (entity as MessageEntity.messageEntityTextUrl).url; + url = wrapUrl(url, true); + inner = wrapRichNestedText(entityText, entity.nested, options); + } else { + url = wrapUrl(entityText, false); + inner = encodeEntities(replaceUrlEncodings(entityText)); + } + + if(options.noLinks) { + html.push(inner); + } else { + html.push( + '', + inner, + '' + ); + } break; - } - html.push( - '', - wrapRichNestedText(entityText, entity.nested, options), - '' - ) - break - case 'messageEntityHighlight': - html.push( - '', - wrapRichNestedText(entityText, entity.nested, options), - '' - ) - break; - case 'messageEntityCode': - if(options.noTextFormat) { - html.push(encodeEntities(entityText)); + case 'messageEntityLinebreak': + html.push(options.noLinebreaks ? ' ' : '
'); break; - } - html.push( - '', - encodeEntities(entityText), - '' - ) - break - case 'messageEntityPre': - if(options.noTextFormat) { - html.push(encodeEntities(entityText)); + case 'messageEntityEmoji': + html.push(emojiSupported ? + `${encodeEntities(entityText)}` : + `${encodeEntities(entityText)}`); break; - } - - html.push( - '
',
-          encodeEntities(entityText),
-          '
' - ) - break - default: - skipEntity = true - } - lastOffset = entity.offset + (skipEntity ? 0 : entity.length) - } - html.push(encodeEntities(text.substr(lastOffset))); // may be empty string - //console.log(html); - text = html.join('')//$sanitize(html.join('')) - /* if (!options.nested && (emojiFound || options.hasNested)) { - text = text.replace(/\ufe0f|️|�|‍/g, '', text) - var emojiSizeClass = curEmojiSize == 18 ? '' : (' emoji-w' + curEmojiSize) - text = text.replace(/]*)?) class="emoji emoji-(\d)-(\d+)-(\d+)"(.+?)<\/span>/g, - '') - } */ - return text;//$sce.trustAs('html', text) -} -function wrapDraftText (text: string, options: any = {}) { - if(!text || !text.length) { - return ''; - } + case 'messageEntityBotCommand': + if(options.noLinks || options.noCommands || contextExternal) { + skipEntity = true; + break; + } - var entities = options.entities; - if(entities === undefined) { - entities = parseEntities(text, options); - } - var i = 0; - var len = entities.length; - var entity; - var entityText; - var skipEntity; - var code = []; - var lastOffset = 0; - for(i = 0; i < len; i++) { - entity = entities[i]; - if(entity.offset > lastOffset) { - code.push( - text.substr(lastOffset, entity.offset - lastOffset) - ); - } else if(entity.offset < lastOffset) { - continue; - } + var command = entityText.substr(1); + var bot; + var atPos; + if ((atPos = command.indexOf('@')) != -1) { + bot = command.substr(atPos + 1); + command = command.substr(0, atPos); + } else { + bot = options.fromBot; + } - skipEntity = false; - entityText = text.substr(entity.offset, entity.length); - switch(entity._) { - case 'messageEntityEmoji': - code.push( - ':', - entity.title, - ':' - ); - break; + html.push( + '', + encodeEntities(entityText), + '' + ); + break; - case 'messageEntityCode': - code.push( - '`', entityText, '`' - ); - break; + case 'messageEntityBold': + if(options.noTextFormat) { + html.push(wrapRichNestedText(entityText, entity.nested, options)); + break; + } + + html.push( + '', + wrapRichNestedText(entityText, entity.nested, options), + '' + ); + break; - case 'messageEntityBold': - code.push( - '**', entityText, '**' - ); - break; + case 'messageEntityItalic': + if(options.noTextFormat) { + html.push(wrapRichNestedText(entityText, entity.nested, options)); + break; + } + + html.push( + '', + wrapRichNestedText(entityText, entity.nested, options), + '' + ); + break; - case 'messageEntityItalic': - code.push( - '__', entityText, '__' - ); - break; + case 'messageEntityHighlight': + html.push( + '', + wrapRichNestedText(entityText, entity.nested, options), + '' + ); + break; - case 'messageEntityPre': - code.push( - '```', entityText, '```' - ); - break; + case 'messageEntityStrike': + html.push( + '', + wrapRichNestedText(entityText, entity.nested, options), + '' + ); + break; - case 'messageEntityMentionName': - code.push( - '@', entity.user_id, ' (', entityText, ')' - ); - break; + case 'messageEntityCode': + if(options.noTextFormat) { + html.push(encodeEntities(entityText)); + break; + } + + html.push( + '', + encodeEntities(entityText), + '' + ); + break; - default: - skipEntity = true; - } - lastOffset = entity.offset + (skipEntity ? 0 : entity.length); - } - code.push(text.substr(lastOffset)); - return code.join(''); -} -function checkBrackets(url: string) { - var urlLength = url.length; - var urlOpenBrackets = url.split('(').length - 1; - var urlCloseBrackets = url.split(')').length - 1; - while(urlCloseBrackets > urlOpenBrackets && - url.charAt(urlLength - 1) === ')') { - url = url.substr(0, urlLength - 1) - urlCloseBrackets--; - urlLength--; - } - if(urlOpenBrackets > urlCloseBrackets) { - url = url.replace(/\)+$/, ''); - } - return url; -} + case 'messageEntityPre': + if(options.noTextFormat) { + html.push(encodeEntities(entityText)); + break; + } + + html.push( + '
',
+            encodeEntities(entityText),
+            '
' + ); + break; + + default: + skipEntity = true; + } -function replaceUrlEncodings(urlWithEncoded: string) { - return urlWithEncoded.replace(/(%[A-Z\d]{2})+/g, (str) => { - try { - return decodeURIComponent(str); - } catch (e) { - return str; + lastOffset = entity.offset + (skipEntity ? 0 : entity.length); } - }); -} + + html.push(encodeEntities(text.substr(lastOffset))); // may be empty string + //console.log(html); + text = html.join(''); -function wrapPlainText(text: any, options: any = {}) { - if(emojiSupported) { return text; } - if(!text || !text.length) { - return ''; + export function wrapDraftText(text: string, options: any = {}) { + if(!text || !text.length) { + return ''; + } + + var entities = options.entities; + if(entities === undefined) { + entities = parseEntities(text); + } + var i = 0; + var len = entities.length; + var entity; + var entityText; + var skipEntity; + var code = []; + var lastOffset = 0; + for(i = 0; i < len; i++) { + entity = entities[i]; + if(entity.offset > lastOffset) { + code.push( + text.substr(lastOffset, entity.offset - lastOffset) + ); + } else if(entity.offset < lastOffset) { + continue; + } + + skipEntity = false; + entityText = text.substr(entity.offset, entity.length); + switch(entity._) { + case 'messageEntityEmoji': + code.push( + ':', + entity.title, + ':' + ); + break; + + case 'messageEntityCode': + code.push( + '`', entityText, '`' + ); + break; + + case 'messageEntityBold': + code.push( + '**', entityText, '**' + ); + break; + + case 'messageEntityItalic': + code.push( + '__', entityText, '__' + ); + break; + + case 'messageEntityPre': + code.push( + '```', entityText, '```' + ); + break; + + case 'messageEntityMentionName': + code.push( + '@', entity.user_id, ' (', entityText, ')' + ); + break; + + default: + skipEntity = true; + } + lastOffset = entity.offset + (skipEntity ? 0 : entity.length); + } + code.push(text.substr(lastOffset)); + return code.join(''); } - text = text.replace(/\ufe0f/g, '', text); - var match; - var raw = text; - var text: any = [], - emojiTitle; - while((match = raw.match(fullRegExp))) { - text.push(raw.substr(0, match.index)) - if(match[8]) { - // @ts-ignore - if((emojiCode = EmojiHelper.emojiMap[match[8]]) && - // @ts-ignore - (emojiTitle = emojiData[emojiCode][1][0])) { - text.push(':' + emojiTitle + ':'); + export function checkBrackets(url: string) { + var urlLength = url.length; + var urlOpenBrackets = url.split('(').length - 1; + var urlCloseBrackets = url.split(')').length - 1; + while(urlCloseBrackets > urlOpenBrackets && + url.charAt(urlLength - 1) === ')') { + url = url.substr(0, urlLength - 1) + urlCloseBrackets--; + urlLength--; + } + if(urlOpenBrackets > urlCloseBrackets) { + url = url.replace(/\)+$/, ''); + } + return url; + } + + export function replaceUrlEncodings(urlWithEncoded: string) { + return urlWithEncoded.replace(/(%[A-Z\d]{2})+/g, (str) => { + try { + return decodeURIComponent(str); + } catch (e) { + return str; + } + }); + } + + export function wrapPlainText(text: any) { + if(emojiSupported) { + return text; + } + + if(!text || !text.length) { + return ''; + } + + text = text.replace(/\ufe0f/g, '', text); + var match; + var raw = text; + var text: any = [], + emojiTitle; + while((match = raw.match(fullRegExp))) { + text.push(raw.substr(0, match.index)) + if(match[8]) { + // @ts-ignore + const emojiCode = EmojiHelper.emojiMap[match[8]]; + if(emojiCode && + // @ts-ignore + (emojiTitle = emojiData[emojiCode][1][0])) { + text.push(':' + emojiTitle + ':'); + } else { + text.push(match[0]); + } } else { text.push(match[0]); } - } else { - text.push(match[0]); + + raw = raw.substr(match.index + match[0].length); } - - raw = raw.substr(match.index + match[0].length); + text.push(raw); + return text.join(''); } - text.push(raw); - return text.join(''); -} -function wrapEmojiText(text: string) { - if(!text) return ''; - let entities = parseEntities(text).filter(e => e._ == 'messageEntityEmoji'); - return wrapRichText(text, {entities}); -} -function wrapUrl(url: string, unsafe: number | boolean): string { - if(!url.match(/^https?:\/\//i)) { - url = 'http://' + url; + export function wrapEmojiText(text: string) { + if(!text) return ''; + + let entities = parseEntities(text).filter(e => e._ == 'messageEntityEmoji'); + return wrapRichText(text, {entities}); } - var tgMeMatch; - var telescoPeMatch; - if(unsafe == 2) { - url = 'tg://unsafe_url?url=' + encodeURIComponent(url); - } else if((tgMeMatch = url.match(/^https?:\/\/t(?:elegram)?\.me\/(.+)/))) { - var fullPath = tgMeMatch[1]; - var path = fullPath.split('/'); - switch(path[0]) { - case 'joinchat': - url = 'tg://join?invite=' + path[1]; - break; - - case 'addstickers': - url = 'tg://addstickers?set=' + path[1]; - break; - - default: - if(path[1] && path[1].match(/^\d+$/)) { - url = 'tg://resolve?domain=' + path[0] + '&post=' + path[1]; - } else if(path.length == 1) { - var domainQuery = path[0].split('?'); - var domain = domainQuery[0]; - var query = domainQuery[1]; - if(domain == 'iv') { - var match = (query || '').match(/url=([^&=]+)/); - if(match) { - url = match[1]; - try { - url = decodeURIComponent(url); - } catch (e) {} - - return wrapUrl(url, unsafe); + export function wrapUrl(url: string, unsafe: number | boolean): string { + if(!url.match(/^https?:\/\//i)) { + url = 'http://' + url; + } + + var tgMeMatch; + var telescoPeMatch; + if(unsafe == 2) { + url = 'tg://unsafe_url?url=' + encodeURIComponent(url); + } else if((tgMeMatch = url.match(/^https?:\/\/t(?:elegram)?\.me\/(.+)/))) { + var fullPath = tgMeMatch[1]; + var path = fullPath.split('/'); + switch(path[0]) { + case 'joinchat': + url = 'tg://join?invite=' + path[1]; + break; + + case 'addstickers': + url = 'tg://addstickers?set=' + path[1]; + break; + + default: + if(path[1] && path[1].match(/^\d+$/)) { + url = 'tg://resolve?domain=' + path[0] + '&post=' + path[1]; + } else if(path.length == 1) { + var domainQuery = path[0].split('?'); + var domain = domainQuery[0]; + var query = domainQuery[1]; + if(domain == 'iv') { + var match = (query || '').match(/url=([^&=]+)/); + if(match) { + url = match[1]; + try { + url = decodeURIComponent(url); + } catch (e) {} + + return wrapUrl(url, unsafe); + } } + + url = 'tg://resolve?domain=' + domain + (query ? '&' + query : ''); } - - url = 'tg://resolve?domain=' + domain + (query ? '&' + query : ''); - } + } + } else if((telescoPeMatch = url.match(/^https?:\/\/telesco\.pe\/([^/?]+)\/(\d+)/))) { + url = 'tg://resolve?domain=' + telescoPeMatch[1] + '&post=' + telescoPeMatch[2]; + } else if(unsafe) { + url = 'tg://unsafe_url?url=' + encodeURIComponent(url); } - } else if((telescoPeMatch = url.match(/^https?:\/\/telesco\.pe\/([^/?]+)\/(\d+)/))) { - url = 'tg://resolve?domain=' + telescoPeMatch[1] + '&post=' + telescoPeMatch[2]; - } else if(unsafe) { - url = 'tg://unsafe_url?url=' + encodeURIComponent(url); + + return url; + } + + export function matchUrl(text: string) { + return text.match(urlRegExp); } - - return url; -} - -function matchUrl(text: string) { - return text.match(urlRegExp); } -let RichTextProcessor = { - wrapRichText, - wrapPlainText, - wrapDraftText, - wrapUrl, - wrapEmojiText, - parseEntities, - parseMarkdown, - parseEmojis, - mergeEntities, - getEmojiSpritesheetCoords, - emojiSupported, - matchUrl -}; - MOUNT_CLASS_TO && (MOUNT_CLASS_TO.RichTextProcessor = RichTextProcessor); export {RichTextProcessor}; +export default RichTextProcessor; diff --git a/src/lib/services.ts b/src/lib/services.ts index 132dd974..f2660ca8 100644 --- a/src/lib/services.ts +++ b/src/lib/services.ts @@ -34,7 +34,7 @@ const appSidebarLeft = AppSidebarLeft; const appMediaViewer = AppMediaViewer; const appDialogsManager = AppDialogsManager; -(window as any).Services = { +const Services = { appUsersManager, appChatsManager, apiUpdatesManager, @@ -53,3 +53,10 @@ const appDialogsManager = AppDialogsManager; appMediaViewer //appSharedMediaManager }; + +(window as any).Services = Services; + +for(let i in Services) { + // @ts-ignore + (window as any)[i] = Services[i]; +} diff --git a/src/scripts/generate_mtproto_types.js b/src/scripts/generate_mtproto_types.js index 9c034967..6196043e 100644 --- a/src/scripts/generate_mtproto_types.js +++ b/src/scripts/generate_mtproto_types.js @@ -10,7 +10,12 @@ for(const constructor of additional) { param.type = 'flags.-1?' + param.type; }); - const realConstructor = mtproto.constructors.find(c => c.predicate == constructor.predicate); + if(constructor.type) { + mtproto.constructors.push(constructor); + } + + const realConstructor = constructor.type ? constructor : mtproto.constructors.find(c => c.predicate == constructor.predicate); + /* constructor.params.forEach(param => { const index = realConstructor.params.findIndex(_param => _param.predicate == param.predicate); if(index !== -1) { diff --git a/src/scripts/in/schema_additional_params.json b/src/scripts/in/schema_additional_params.json index d9c2125c..bd22aa3f 100644 --- a/src/scripts/in/schema_additional_params.json +++ b/src/scripts/in/schema_additional_params.json @@ -91,4 +91,124 @@ "params": [ {"name": "rAbout", "type": "string"} ] +}, { + "predicate": "messageEntityEmoji", + "params": [ + {"name": "offset", "type": "number"}, + {"name": "length", "type": "number"}, + {"name": "unicode", "type": "string"}, + {"name": "nested", "type": "Array"} + ], + "type": "MessageEntity" +}, { + "predicate": "messageEntityHighlight", + "params": [ + {"name": "offset", "type": "number"}, + {"name": "length", "type": "number"}, + {"name": "nested", "type": "Array"} + ], + "type": "MessageEntity" +}, { + "predicate": "messageEntityLinebreak", + "params": [ + {"name": "offset", "type": "number"}, + {"name": "length", "type": "number"}, + {"name": "nested", "type": "Array"} + ], + "type": "MessageEntity" +}, { + "predicate": "messageEntityUnknown", + "params": [ + {"name": "nested", "type": "Array"} + ] +}, { + "predicate": "messageEntityMention", + "params": [ + {"name": "nested", "type": "Array"} + ] +}, { + "predicate": "messageEntityHashtag", + "params": [ + {"name": "nested", "type": "Array"} + ] +}, { + "predicate": "messageEntityBotCommand", + "params": [ + {"name": "nested", "type": "Array"} + ] +}, { + "predicate": "messageEntityUrl", + "params": [ + {"name": "nested", "type": "Array"} + ] +}, { + "predicate": "messageEntityEmail", + "params": [ + {"name": "nested", "type": "Array"} + ] +}, { + "predicate": "messageEntityBold", + "params": [ + {"name": "nested", "type": "Array"} + ] +}, { + "predicate": "messageEntityItalic", + "params": [ + {"name": "nested", "type": "Array"} + ] +}, { + "predicate": "messageEntityCode", + "params": [ + {"name": "nested", "type": "Array"} + ] +}, { + "predicate": "messageEntityPre", + "params": [ + {"name": "nested", "type": "Array"} + ] +}, { + "predicate": "messageEntityTextUrl", + "params": [ + {"name": "nested", "type": "Array"} + ] +}, { + "predicate": "messageEntityMentionName", + "params": [ + {"name": "nested", "type": "Array"} + ] +}, { + "predicate": "messageEntityPhone", + "params": [ + {"name": "nested", "type": "Array"} + ] +}, { + "predicate": "messageEntityCashtag", + "params": [ + {"name": "nested", "type": "Array"} + ] +}, { + "predicate": "messageEntityUnderline", + "params": [ + {"name": "nested", "type": "Array"} + ] +}, { + "predicate": "messageEntityStrike", + "params": [ + {"name": "nested", "type": "Array"} + ] +}, { + "predicate": "messageEntityBlockquote", + "params": [ + {"name": "nested", "type": "Array"} + ] +}, { + "predicate": "messageEntityBankCard", + "params": [ + {"name": "nested", "type": "Array"} + ] +}, { + "predicate": "inputMessageEntityMentionName", + "params": [ + {"name": "nested", "type": "Array"} + ] }] \ No newline at end of file