fix(client): automatically derive gallery for inline result

This commit is contained in:
teidesu 2022-07-13 05:09:58 +03:00
parent 2c6238798f
commit 42c5d29167
2 changed files with 290 additions and 264 deletions

View file

@ -28,7 +28,10 @@ export async function answerInlineQuery(
* Whether the results should be displayed as a gallery instead * Whether the results should be displayed as a gallery instead
* of a vertical list. Only applicable to some media types. * of a vertical list. Only applicable to some media types.
* *
* Defaults to `true` * In some cases changing this may lead to the results not being
* displayed by the client.
*
* Default is derived automatically based on result types
*/ */
gallery?: boolean gallery?: boolean
@ -94,16 +97,14 @@ export async function answerInlineQuery(
): Promise<void> { ): Promise<void> {
if (!params) params = {} if (!params) params = {}
const tlResults = await Promise.all( const [gallery, tlResults] = await BotInline._convertToTl(this, results, params!.parseMode)
results.map((it) => BotInline._convertToTl(this, it, params!.parseMode))
)
await this.call({ await this.call({
_: 'messages.setInlineBotResults', _: 'messages.setInlineBotResults',
queryId, queryId,
results: tlResults, results: tlResults,
cacheTime: params.cacheTime ?? 300, cacheTime: params.cacheTime ?? 300,
gallery: params.gallery ?? true, gallery: params.gallery ?? gallery,
private: params.private, private: params.private,
nextOffset: params.nextOffset, nextOffset: params.nextOffset,
switchPm: params.switchPm switchPm: params.switchPm

View file

@ -714,9 +714,9 @@ export namespace BotInline {
/** @internal */ /** @internal */
export async function _convertToTl( export async function _convertToTl(
client: TelegramClient, client: TelegramClient,
obj: InputInlineResult, results: InputInlineResult[],
parseMode?: string | null parseMode?: string | null
): Promise<tl.TypeInputBotInlineResult> { ): Promise<[boolean, tl.TypeInputBotInlineResult[]]> {
const normalizeThumb = ( const normalizeThumb = (
obj: InputInlineResult, obj: InputInlineResult,
fallback?: string fallback?: string
@ -748,306 +748,331 @@ export namespace BotInline {
} }
} }
if (obj.type === 'article') { const items: tl.TypeInputBotInlineResult[] = []
let sendMessage: tl.TypeInputBotInlineMessage
if (obj.message) {
sendMessage = await BotInlineMessage._convertToTl(
client,
obj.message,
parseMode
)
} else {
let message = obj.title
const entities: tl.TypeMessageEntity[] = [
{
_: 'messageEntityBold',
offset: 0,
length: message.length,
},
]
if (obj.url) { let isGallery = false
entities.push({ let forceVertical = false
_: 'messageEntityTextUrl',
url: obj.url,
offset: 0,
length: message.length,
})
}
if (obj.description) { for (const obj of results) {
message += '\n' + obj.description switch (obj.type) {
} case 'article': {
forceVertical = true
sendMessage = { let sendMessage: tl.TypeInputBotInlineMessage
_: 'inputBotInlineMessageText', if (obj.message) {
message, sendMessage = await BotInlineMessage._convertToTl(
entities, client,
} obj.message,
} parseMode
)
} else {
let message = obj.title
const entities: tl.TypeMessageEntity[] = [
{
_: 'messageEntityBold',
offset: 0,
length: message.length,
},
]
return { if (obj.url) {
_: 'inputBotInlineResult', entities.push({
id: obj.id, _: 'messageEntityTextUrl',
type: obj.type, url: obj.url,
title: obj.title, offset: 0,
description: obj.description, length: message.length,
url: obj.hideUrl ? undefined : obj.url, })
content: }
obj.url && obj.hideUrl
? {
_: 'inputWebDocument',
url: obj.url,
mimeType: 'text/html',
size: 0,
attributes: [],
}
: undefined,
thumb:
typeof obj.thumb === 'string'
? normalizeThumb(obj)
: obj.thumb,
sendMessage,
}
}
if (obj.type === 'game') { if (obj.description) {
let sendMessage: tl.TypeInputBotInlineMessage message += '\n' + obj.description
if (obj.message) { }
sendMessage = await BotInlineMessage._convertToTl(
client,
obj.message,
parseMode
)
if (sendMessage._ !== 'inputBotInlineMessageGame') {
throw new MtArgumentError(
'game inline result must contain a game inline message'
)
}
} else {
sendMessage = {
_: 'inputBotInlineMessageGame',
}
}
return { sendMessage = {
_: 'inputBotInlineResultGame', _: 'inputBotInlineMessageText',
id: obj.id, message,
shortName: obj.shortName, entities,
sendMessage, }
} }
}
let sendMessage: tl.TypeInputBotInlineMessage items.push({
if (obj.message) { _: 'inputBotInlineResult',
sendMessage = await BotInlineMessage._convertToTl( id: obj.id,
client, type: obj.type,
obj.message,
parseMode
)
} else {
if (obj.type === 'venue') {
if (obj.latitude && obj.longitude) {
sendMessage = {
_: 'inputBotInlineMessageMediaVenue',
title: obj.title, title: obj.title,
address: obj.address, description: obj.description,
url: obj.hideUrl ? undefined : obj.url,
content:
obj.url && obj.hideUrl
? {
_: 'inputWebDocument',
url: obj.url,
mimeType: 'text/html',
size: 0,
attributes: [],
}
: undefined,
thumb:
typeof obj.thumb === 'string'
? normalizeThumb(obj)
: obj.thumb,
sendMessage,
})
continue
}
case 'game': {
let sendMessage: tl.TypeInputBotInlineMessage
if (obj.message) {
sendMessage = await BotInlineMessage._convertToTl(
client,
obj.message,
parseMode
)
if (sendMessage._ !== 'inputBotInlineMessageGame') {
throw new MtArgumentError(
'game inline result must contain a game inline message'
)
}
} else {
sendMessage = {
_: 'inputBotInlineMessageGame',
}
}
items.push({
_: 'inputBotInlineResultGame',
id: obj.id,
shortName: obj.shortName,
sendMessage,
})
continue
}
case 'gif':
case 'photo':
case 'sticker':
isGallery = true
break
case 'audio':
case 'contact':
case 'voice':
forceVertical = true
}
let sendMessage: tl.TypeInputBotInlineMessage
if (obj.message) {
sendMessage = await BotInlineMessage._convertToTl(
client,
obj.message,
parseMode
)
} else {
if (obj.type === 'venue') {
if (obj.latitude && obj.longitude) {
sendMessage = {
_: 'inputBotInlineMessageMediaVenue',
title: obj.title,
address: obj.address,
geoPoint: {
_: 'inputGeoPoint',
lat: obj.latitude,
long: obj.longitude,
},
provider: '',
venueId: '',
venueType: '',
}
} else {
throw new MtArgumentError(
'message or location (lat&lon) bust be supplied for venue inline result'
)
}
} else if (
obj.type === 'video' &&
obj.isEmbed &&
typeof obj.media === 'string'
) {
sendMessage = {
_: 'inputBotInlineMessageText',
message: obj.media,
}
} else if (obj.type === 'geo') {
sendMessage = {
_: 'inputBotInlineMessageMediaGeo',
geoPoint: { geoPoint: {
_: 'inputGeoPoint', _: 'inputGeoPoint',
lat: obj.latitude, lat: obj.latitude,
long: obj.longitude, long: obj.longitude,
}, },
provider: '', }
venueId: '', } else if (obj.type === 'contact') {
venueType: '', sendMessage = {
_: 'inputBotInlineMessageMediaContact',
phoneNumber: obj.phone,
firstName: obj.firstName,
lastName: obj.lastName ?? '',
vcard: '',
} }
} else { } else {
throw new MtArgumentError( sendMessage = {
'message or location (lat&lon) bust be supplied for venue inline result' _: 'inputBotInlineMessageMediaAuto',
) message: '',
} }
} else if (
obj.type === 'video' &&
obj.isEmbed &&
typeof obj.media === 'string'
) {
sendMessage = {
_: 'inputBotInlineMessageText',
message: obj.media,
}
} else if (obj.type === 'geo') {
sendMessage = {
_: 'inputBotInlineMessageMediaGeo',
geoPoint: {
_: 'inputGeoPoint',
lat: obj.latitude,
long: obj.longitude,
},
}
} else if (obj.type === 'contact') {
sendMessage = {
_: 'inputBotInlineMessageMediaContact',
phoneNumber: obj.phone,
firstName: obj.firstName,
lastName: obj.lastName ?? '',
vcard: '',
}
} else {
sendMessage = {
_: 'inputBotInlineMessageMediaAuto',
message: '',
} }
} }
}
let media: let media:
| tl.TypeInputWebDocument | tl.TypeInputWebDocument
| tl.TypeInputDocument | tl.TypeInputDocument
| tl.TypeInputPhoto | tl.TypeInputPhoto
| undefined = undefined | undefined = undefined
if ( if (
obj.type !== 'geo' && obj.type !== 'geo' &&
obj.type !== 'venue' && obj.type !== 'venue' &&
obj.type !== 'contact' obj.type !== 'contact'
) { ) {
if (typeof obj.media === 'string') { if (typeof obj.media === 'string') {
// file id or url // file id or url
if (obj.media.match(/^https?:\/\//)) { if (obj.media.match(/^https?:\/\//)) {
if (obj.type === 'sticker') if (obj.type === 'sticker')
throw new MtArgumentError(
'sticker inline result cannot contain a URL'
)
let mime: string
if (obj.type === 'video') mime = 'video/mp4'
else if (obj.type === 'audio')
mime = obj.mime ?? 'audio/mpeg'
else if (obj.type === 'gif') mime = obj.mime ?? 'video/mp4'
else if (obj.type === 'voice') mime = 'audio/ogg'
else if (obj.type === 'file') {
if (!obj.mime)
throw new MtArgumentError( throw new MtArgumentError(
'MIME type must be specified for file inline result' 'sticker inline result cannot contain a URL'
) )
mime = obj.mime let mime: string
} else mime = 'image/jpeg' if (obj.type === 'video') mime = 'video/mp4'
else if (obj.type === 'audio')
mime = obj.mime ?? 'audio/mpeg'
else if (obj.type === 'gif') mime = obj.mime ?? 'video/mp4'
else if (obj.type === 'voice') mime = 'audio/ogg'
else if (obj.type === 'file') {
if (!obj.mime)
throw new MtArgumentError(
'MIME type must be specified for file inline result'
)
const attributes: tl.TypeDocumentAttribute[] = [] mime = obj.mime
} else mime = 'image/jpeg'
if ( const attributes: tl.TypeDocumentAttribute[] = []
(obj.type === 'video' ||
obj.type === 'gif' || if (
obj.type === 'photo') && (obj.type === 'video' ||
obj.width && obj.type === 'gif' ||
obj.height obj.type === 'photo') &&
) { obj.width &&
if (obj.type !== 'photo' && obj.duration) { obj.height
) {
if (obj.type !== 'photo' && obj.duration) {
attributes.push({
_: 'documentAttributeVideo',
w: obj.width,
h: obj.height,
duration: obj.duration,
})
} else {
attributes.push({
_: 'documentAttributeImageSize',
w: obj.width,
h: obj.height,
})
}
} else if (obj.type === 'audio' || obj.type === 'voice') {
attributes.push({ attributes.push({
_: 'documentAttributeVideo', _: 'documentAttributeAudio',
w: obj.width, voice: obj.type === 'voice',
h: obj.height, duration: obj.duration ?? 0,
duration: obj.duration, title: obj.type === 'audio' ? obj.title : '',
}) performer:
} else { obj.type === 'audio' ? obj.performer : '',
attributes.push({
_: 'documentAttributeImageSize',
w: obj.width,
h: obj.height,
}) })
} }
} else if (obj.type === 'audio' || obj.type === 'voice') {
attributes.push({ attributes.push({
_: 'documentAttributeAudio', _: 'documentAttributeFilename',
voice: obj.type === 'voice', fileName: extractFileName(obj.media),
duration: obj.duration ?? 0,
title: obj.type === 'audio' ? obj.title : '',
performer:
obj.type === 'audio' ? obj.performer : '',
}) })
}
attributes.push({ media = {
_: 'documentAttributeFilename', _: 'inputWebDocument',
fileName: extractFileName(obj.media), url: obj.media,
}) mimeType: mime,
size: 0,
media = { attributes,
_: 'inputWebDocument', }
url: obj.media, } else if (obj.type === 'photo') {
mimeType: mime, media = fileIdToInputPhoto(obj.media)
size: 0, } else {
attributes, media = fileIdToInputDocument(obj.media)
} }
} else if (obj.type === 'photo') {
media = fileIdToInputPhoto(obj.media)
} else { } else {
media = fileIdToInputDocument(obj.media) media = obj.media
} }
} else {
media = obj.media
} }
}
let title: string | undefined = undefined let title: string | undefined = undefined
let description: string | undefined = undefined let description: string | undefined = undefined
// incredible hacks by durov team. // incredible hacks by durov team.
// i honestly don't understand why didn't they just // i honestly don't understand why didn't they just
// make a bunch of types, as they normally do, // make a bunch of types, as they normally do,
// but whatever. // but whatever.
// ref: https://github.com/tdlib/td/blob/master/td/telegram/InlineQueriesManager.cpp // ref: https://github.com/tdlib/td/blob/master/td/telegram/InlineQueriesManager.cpp
if (obj.type === 'contact') { if (obj.type === 'contact') {
title = obj.lastName?.length title = obj.lastName?.length
? `${obj.firstName} ${obj.lastName}` ? `${obj.firstName} ${obj.lastName}`
: obj.firstName : obj.firstName
} else if (obj.type !== 'sticker') { } else if (obj.type !== 'sticker') {
title = obj.title title = obj.title
} }
if (obj.type === 'audio') { if (obj.type === 'audio') {
description = obj.performer description = obj.performer
} else if (obj.type === 'geo') { } else if (obj.type === 'geo') {
description = `${obj.latitude} ${obj.longitude}` description = `${obj.latitude} ${obj.longitude}`
} else if (obj.type === 'venue') { } else if (obj.type === 'venue') {
description = obj.address description = obj.address
} else if (obj.type === 'contact') { } else if (obj.type === 'contact') {
description = obj.phone description = obj.phone
} else if (obj.type !== 'voice' && obj.type !== 'sticker') { } else if (obj.type !== 'voice' && obj.type !== 'sticker') {
description = obj.description description = obj.description
} }
if (!media || media._ === 'inputWebDocument') { if (!media || media._ === 'inputWebDocument') {
return { items.push({
_: 'inputBotInlineResult', _: 'inputBotInlineResult',
id: obj.id,
type: obj.type,
title,
description,
content: media,
thumb: normalizeThumb(obj, media?.url),
sendMessage,
})
continue
}
if (media._ === 'inputPhoto') {
items.push({
_: 'inputBotInlineResultPhoto',
id: obj.id,
type: obj.type,
photo: media,
sendMessage,
})
continue
}
items.push({
_: 'inputBotInlineResultDocument',
id: obj.id, id: obj.id,
type: obj.type, type: obj.type,
title, title,
description, description,
content: media, document: media,
thumb: normalizeThumb(obj, media?.url),
sendMessage, sendMessage,
} })
} }
if (media._ === 'inputPhoto') { return [isGallery && !forceVertical, items]
return {
_: 'inputBotInlineResultPhoto',
id: obj.id,
type: obj.type,
photo: media,
sendMessage,
}
}
return {
_: 'inputBotInlineResultDocument',
id: obj.id,
type: obj.type,
title,
description,
document: media,
sendMessage,
}
} }
} }