2024-07-15 15:52:43 +02:00

585 lines
14 KiB
TypeScript

import BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs';
import type {
ApiBotApp,
ApiChat,
ApiInputMessageReplyInfo,
ApiPeer,
ApiThemeParameters,
ApiUser,
OnApiUpdate,
} from '../../types';
import { WEB_APP_PLATFORM } from '../../../config';
import { buildCollectionByKey } from '../../../util/iteratees';
import {
buildApiAttachBot,
buildApiBotInlineMediaResult,
buildApiBotInlineResult,
buildApiMessagesBotApp,
buildBotSwitchPm,
buildBotSwitchWebview,
} from '../apiBuilders/bots';
import { buildApiChatFromPreview } from '../apiBuilders/chats';
import { omitVirtualClassFields } from '../apiBuilders/helpers';
import { buildApiUrlAuthResult } from '../apiBuilders/misc';
import { buildApiUser } from '../apiBuilders/users';
import {
buildInputBotApp,
buildInputEntity,
buildInputPeer,
buildInputReplyTo,
buildInputThemeParams,
generateRandomBigInt,
} from '../gramjsBuilders';
import {
addDocumentToLocalDb,
addEntitiesToLocalDb,
addPhotoToLocalDb,
addUserToLocalDb,
addWebDocumentToLocalDb,
deserializeBytes,
} from '../helpers';
import { invokeRequest } from './client';
let onUpdate: OnApiUpdate;
export function init(_onUpdate: OnApiUpdate) {
onUpdate = _onUpdate;
}
export async function answerCallbackButton({
chatId, accessHash, messageId, data, isGame,
}: {
chatId: string; accessHash?: string; messageId: number; data?: string; isGame?: boolean;
}) {
const result = await invokeRequest(new GramJs.messages.GetBotCallbackAnswer({
peer: buildInputPeer(chatId, accessHash),
msgId: messageId,
data: data ? deserializeBytes(data) : undefined,
game: isGame || undefined,
}));
return result ? omitVirtualClassFields(result) : undefined;
}
export async function fetchTopInlineBots() {
const topPeers = await invokeRequest(new GramJs.contacts.GetTopPeers({
botsInline: true,
}));
if (!(topPeers instanceof GramJs.contacts.TopPeers)) {
return undefined;
}
const users = topPeers.users.map(buildApiUser).filter(Boolean);
const ids = users.map(({ id }) => id);
return {
ids,
users,
};
}
export async function fetchInlineBot({ username }: { username: string }) {
const resolvedPeer = await invokeRequest(new GramJs.contacts.ResolveUsername({ username }));
if (
!resolvedPeer
|| !(
resolvedPeer.users[0] instanceof GramJs.User
&& resolvedPeer.users[0].bot
&& resolvedPeer.users[0].botInlinePlaceholder
)
) {
return undefined;
}
addUserToLocalDb(resolvedPeer.users[0]);
return {
user: buildApiUser(resolvedPeer.users[0]),
chat: buildApiChatFromPreview(resolvedPeer.users[0]),
};
}
export async function fetchInlineBotResults({
bot, chat, query, offset = '',
}: {
bot: ApiUser; chat: ApiChat; query: string; offset?: string;
}) {
const result = await invokeRequest(new GramJs.messages.GetInlineBotResults({
bot: buildInputPeer(bot.id, bot.accessHash),
peer: buildInputPeer(chat.id, chat.accessHash),
query,
offset,
}));
if (!result) {
return undefined;
}
addEntitiesToLocalDb(result.users);
return {
isGallery: Boolean(result.gallery),
help: bot.botPlaceholder,
nextOffset: getInlineBotResultsNextOffset(bot.usernames![0].username, result.nextOffset),
switchPm: buildBotSwitchPm(result.switchPm),
switchWebview: buildBotSwitchWebview(result.switchWebview),
users: result.users.map(buildApiUser).filter(Boolean),
results: processInlineBotResult(String(result.queryId), result.results),
cacheTime: result.cacheTime,
};
}
export async function sendInlineBotResult({
chat, replyInfo, resultId, queryId, sendAs, isSilent, scheduleDate,
}: {
chat: ApiChat;
replyInfo?: ApiInputMessageReplyInfo;
resultId: string;
queryId: string;
sendAs?: ApiPeer;
isSilent?: boolean;
scheduleDate?: number;
}) {
const randomId = generateRandomBigInt();
await invokeRequest(new GramJs.messages.SendInlineBotResult({
clearDraft: true,
randomId,
queryId: BigInt(queryId),
peer: buildInputPeer(chat.id, chat.accessHash),
id: resultId,
scheduleDate,
replyTo: replyInfo && buildInputReplyTo(replyInfo),
...(isSilent && { silent: true }),
...(sendAs && { sendAs: buildInputPeer(sendAs.id, sendAs.accessHash) }),
}));
}
export async function startBot({
bot, startParam,
}: {
bot: ApiUser;
startParam?: string;
}) {
const randomId = generateRandomBigInt();
await invokeRequest(new GramJs.messages.StartBot({
bot: buildInputPeer(bot.id, bot.accessHash),
peer: buildInputPeer(bot.id, bot.accessHash),
randomId,
startParam,
}));
}
export async function requestWebView({
isSilent,
peer,
bot,
url,
startParam,
replyInfo,
theme,
sendAs,
isFromBotMenu,
}: {
isSilent?: boolean;
peer: ApiPeer;
bot: ApiUser;
url?: string;
startParam?: string;
replyInfo?: ApiInputMessageReplyInfo;
theme?: ApiThemeParameters;
sendAs?: ApiPeer;
isFromBotMenu?: boolean;
}) {
const result = await invokeRequest(new GramJs.messages.RequestWebView({
silent: isSilent || undefined,
peer: buildInputPeer(peer.id, peer.accessHash),
bot: buildInputPeer(bot.id, bot.accessHash),
url,
startParam,
themeParams: theme ? buildInputThemeParams(theme) : undefined,
fromBotMenu: isFromBotMenu || undefined,
platform: WEB_APP_PLATFORM,
replyTo: replyInfo && buildInputReplyTo(replyInfo),
...(sendAs && { sendAs: buildInputPeer(sendAs.id, sendAs.accessHash) }),
}));
if (result instanceof GramJs.WebViewResultUrl) {
return {
url: result.url,
queryId: result.queryId?.toString(),
};
}
return undefined;
}
export async function requestSimpleWebView({
bot,
url,
theme,
startParam,
isFromSwitchWebView,
isFromSideMenu,
}: {
bot: ApiUser;
url?: string;
theme?: ApiThemeParameters;
startParam?: string;
isFromSwitchWebView?: boolean;
isFromSideMenu?: boolean;
}) {
const result = await invokeRequest(new GramJs.messages.RequestSimpleWebView({
url,
bot: buildInputPeer(bot.id, bot.accessHash),
themeParams: theme ? buildInputThemeParams(theme) : undefined,
platform: WEB_APP_PLATFORM,
startParam,
fromSwitchWebview: isFromSwitchWebView || undefined,
fromSideMenu: isFromSideMenu || undefined,
}));
return result?.url;
}
export async function fetchBotApp({
bot,
appName,
}: {
bot: ApiUser;
appName: string;
}) {
const result = await invokeRequest(new GramJs.messages.GetBotApp({
app: new GramJs.InputBotAppShortName({
botId: buildInputEntity(bot.id, bot.accessHash) as GramJs.InputUser,
shortName: appName,
}),
}));
if (!result || result instanceof GramJs.BotAppNotModified) {
return undefined;
}
return buildApiMessagesBotApp(result);
}
export async function requestAppWebView({
peer,
app,
startParam,
theme,
isWriteAllowed,
}: {
peer: ApiPeer;
app: ApiBotApp;
startParam?: string;
theme?: ApiThemeParameters;
isWriteAllowed?: boolean;
}) {
const result = await invokeRequest(new GramJs.messages.RequestAppWebView({
peer: buildInputPeer(peer.id, peer.accessHash),
app: buildInputBotApp(app),
startParam,
themeParams: theme ? buildInputThemeParams(theme) : undefined,
platform: WEB_APP_PLATFORM,
writeAllowed: isWriteAllowed || undefined,
}));
return result?.url;
}
export function prolongWebView({
isSilent,
peer,
bot,
queryId,
replyInfo,
sendAs,
}: {
isSilent?: boolean;
peer: ApiPeer;
bot: ApiUser;
queryId: string;
replyInfo?: ApiInputMessageReplyInfo;
sendAs?: ApiPeer;
}) {
return invokeRequest(new GramJs.messages.ProlongWebView({
silent: isSilent || undefined,
peer: buildInputPeer(peer.id, peer.accessHash),
bot: buildInputPeer(bot.id, bot.accessHash),
queryId: BigInt(queryId),
replyTo: replyInfo && buildInputReplyTo(replyInfo),
...(sendAs && { sendAs: buildInputPeer(sendAs.id, sendAs.accessHash) }),
}));
}
export async function sendWebViewData({
bot, buttonText, data,
}: {
bot: ApiUser;
buttonText: string;
data: string;
}) {
const randomId = generateRandomBigInt();
await invokeRequest(new GramJs.messages.SendWebViewData({
bot: buildInputPeer(bot.id, bot.accessHash),
buttonText,
data,
randomId,
}));
}
export async function loadAttachBots({
hash,
}: {
hash?: string;
}) {
const result = await invokeRequest(new GramJs.messages.GetAttachMenuBots({
hash: hash ? BigInt(hash) : undefined,
}));
if (result instanceof GramJs.AttachMenuBots) {
addEntitiesToLocalDb(result.users);
return {
hash: result.hash.toString(),
bots: buildCollectionByKey(result.bots.map(buildApiAttachBot), 'id'),
users: result.users.map(buildApiUser).filter(Boolean),
};
}
return undefined;
}
export async function loadAttachBot({
bot,
}: {
bot: ApiUser;
}) {
const result = await invokeRequest(new GramJs.messages.GetAttachMenuBot({
bot: buildInputPeer(bot.id, bot.accessHash),
}));
if (result instanceof GramJs.AttachMenuBotsBot) {
addEntitiesToLocalDb(result.users);
return {
bot: buildApiAttachBot(result.bot),
users: result.users.map(buildApiUser).filter(Boolean),
};
}
return undefined;
}
export function toggleAttachBot({
bot,
isWriteAllowed,
isEnabled,
}: {
bot: ApiUser;
isWriteAllowed?: boolean;
isEnabled: boolean;
}) {
return invokeRequest(new GramJs.messages.ToggleBotInAttachMenu({
bot: buildInputPeer(bot.id, bot.accessHash),
writeAllowed: isWriteAllowed || undefined,
enabled: isEnabled,
}));
}
export async function requestBotUrlAuth({
chat, buttonId, messageId,
}: {
chat: ApiChat;
buttonId: number;
messageId: number;
}) {
const result = await invokeRequest(new GramJs.messages.RequestUrlAuth({
peer: buildInputPeer(chat.id, chat.accessHash),
buttonId,
msgId: messageId,
}));
if (!result) return undefined;
const authResult = buildApiUrlAuthResult(result);
if (authResult?.type === 'request') {
onUpdate({
'@type': 'updateUser',
id: authResult.bot.id,
user: authResult.bot,
});
}
return authResult;
}
export async function acceptBotUrlAuth({
chat,
messageId,
buttonId,
isWriteAllowed,
}: {
chat: ApiChat;
messageId: number;
buttonId: number;
isWriteAllowed?: boolean;
}) {
const result = await invokeRequest(new GramJs.messages.AcceptUrlAuth({
peer: buildInputPeer(chat.id, chat.accessHash),
msgId: messageId,
buttonId,
writeAllowed: isWriteAllowed || undefined,
}));
if (!result) return undefined;
const authResult = buildApiUrlAuthResult(result);
if (authResult?.type === 'request') {
onUpdate({
'@type': 'updateUser',
id: authResult.bot.id,
user: authResult.bot,
});
}
return authResult;
}
export async function requestLinkUrlAuth({ url }: { url: string }) {
const result = await invokeRequest(new GramJs.messages.RequestUrlAuth({
url,
}));
if (!result) return undefined;
const authResult = buildApiUrlAuthResult(result);
if (authResult?.type === 'request') {
onUpdate({
'@type': 'updateUser',
id: authResult.bot.id,
user: authResult.bot,
});
}
return authResult;
}
export async function acceptLinkUrlAuth({ url, isWriteAllowed }: { url: string; isWriteAllowed?: boolean }) {
const result = await invokeRequest(new GramJs.messages.AcceptUrlAuth({
url,
writeAllowed: isWriteAllowed || undefined,
}));
if (!result) return undefined;
const authResult = buildApiUrlAuthResult(result);
if (authResult?.type === 'request') {
onUpdate({
'@type': 'updateUser',
id: authResult.bot.id,
user: authResult.bot,
});
}
return authResult;
}
export function fetchBotCanSendMessage({ bot } : { bot: ApiUser }) {
return invokeRequest(new GramJs.bots.CanSendMessage({
bot: buildInputEntity(bot.id, bot.accessHash) as GramJs.InputUser,
}));
}
export function allowBotSendMessages({ bot } : { bot: ApiUser }) {
return invokeRequest(new GramJs.bots.AllowSendMessage({
bot: buildInputEntity(bot.id, bot.accessHash) as GramJs.InputUser,
}), {
shouldReturnTrue: true,
});
}
export async function invokeWebViewCustomMethod({
bot,
customMethod,
parameters,
}: {
bot: ApiUser;
customMethod: string;
parameters: string;
}): Promise<{
result: object;
} | {
error: string;
}> {
try {
const result = await invokeRequest(new GramJs.bots.InvokeWebViewCustomMethod({
bot: buildInputPeer(bot.id, bot.accessHash),
params: new GramJs.DataJSON({
data: parameters,
}),
customMethod,
}), {
shouldThrow: true,
});
return {
result: JSON.parse(result!.data),
};
} catch (e) {
const error = e as Error;
return {
error: error.message,
};
}
}
function processInlineBotResult(queryId: string, results: GramJs.TypeBotInlineResult[]) {
return results.map((result) => {
if (result instanceof GramJs.BotInlineMediaResult) {
if (result.document instanceof GramJs.Document) {
addDocumentToLocalDb(result.document);
}
if (result.photo instanceof GramJs.Photo) {
addPhotoToLocalDb(result.photo);
}
return buildApiBotInlineMediaResult(result, queryId);
}
if (result.thumb) {
addWebDocumentToLocalDb(result.thumb);
}
return buildApiBotInlineResult(result, queryId);
});
}
function getInlineBotResultsNextOffset(username: string, nextOffset?: string) {
return username === 'gif' && nextOffset === '0' ? '' : nextOffset;
}
export function setBotInfo({
bot,
langCode,
name,
about,
description,
}: {
bot: ApiUser;
langCode: string;
name?: string;
about?: string;
description?: string;
}) {
return invokeRequest(new GramJs.bots.SetBotInfo({
bot: buildInputPeer(bot.id, bot.accessHash),
langCode,
name: name || '',
about: about || '',
description: description || '',
}), {
shouldReturnTrue: true,
});
}