Proxy Telegram bot user avatars
All checks were successful
Build and deploy Backend / build (push) Successful in 31s
All checks were successful
Build and deploy Backend / build (push) Successful in 31s
This commit is contained in:
@@ -25,6 +25,14 @@ type TelegramBotUser = {
|
||||
language_code?: string;
|
||||
};
|
||||
|
||||
type TelegramProfilePhotos = {
|
||||
photos: { file_id: string }[][];
|
||||
};
|
||||
|
||||
type TelegramFile = {
|
||||
file_path: string;
|
||||
};
|
||||
|
||||
const loginPrefix = 'login_';
|
||||
|
||||
function randomToken() {
|
||||
@@ -43,7 +51,7 @@ function botApiUrl(method: string) {
|
||||
return `https://api.telegram.org/bot${config.telegramMiniAppBotToken}/${method}`;
|
||||
}
|
||||
|
||||
async function callTelegram(method: string, body: Record<string, unknown>) {
|
||||
async function callTelegram<T>(method: string, body: Record<string, unknown>) {
|
||||
const response = await fetch(botApiUrl(method), {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
@@ -54,10 +62,16 @@ async function callTelegram(method: string, body: Record<string, unknown>) {
|
||||
throw new Error(`Telegram ${method} failed with ${response.status}.`);
|
||||
}
|
||||
|
||||
const payload = (await response.json()) as { ok: boolean; description?: string };
|
||||
const payload = (await response.json()) as {
|
||||
ok: boolean;
|
||||
description?: string;
|
||||
result?: T;
|
||||
};
|
||||
if (!payload.ok) {
|
||||
throw new Error(payload.description ?? `Telegram ${method} failed.`);
|
||||
}
|
||||
|
||||
return payload.result as T;
|
||||
}
|
||||
|
||||
function userPayload(from: TelegramBotUser): TelegramUserPayload {
|
||||
@@ -70,6 +84,32 @@ function userPayload(from: TelegramBotUser): TelegramUserPayload {
|
||||
};
|
||||
}
|
||||
|
||||
async function telegramUserPhotoUrl(userId: number) {
|
||||
const photos = await callTelegram<TelegramProfilePhotos>('getUserProfilePhotos', {
|
||||
user_id: userId,
|
||||
limit: 1,
|
||||
});
|
||||
const variants = photos.photos[0];
|
||||
const fileId = variants?.[variants.length - 1]?.file_id;
|
||||
return fileId ? `${config.publicApiUrl}/telegram/photo/${fileId}` : undefined;
|
||||
}
|
||||
|
||||
export async function fetchTelegramPhoto(fileId: string) {
|
||||
const file = await callTelegram<TelegramFile>('getFile', { file_id: fileId });
|
||||
const response = await fetch(
|
||||
`https://api.telegram.org/file/bot${config.telegramMiniAppBotToken}/${file.file_path}`,
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Telegram file fetch failed with ${response.status}.`);
|
||||
}
|
||||
|
||||
return {
|
||||
contentType: response.headers.get('content-type') ?? 'image/jpeg',
|
||||
bytes: Buffer.from(await response.arrayBuffer()),
|
||||
};
|
||||
}
|
||||
|
||||
async function sendLoginMessage(chatId: number, text: string, token?: string) {
|
||||
const replyMarkup = token
|
||||
? {
|
||||
@@ -166,7 +206,10 @@ export async function handleTelegramBotWebhook(
|
||||
return;
|
||||
}
|
||||
|
||||
const user = await upsertTelegramUser(userPayload(from));
|
||||
const user = await upsertTelegramUser({
|
||||
...userPayload(from),
|
||||
photoUrl: await telegramUserPhotoUrl(from.id),
|
||||
});
|
||||
const sessionToken = randomToken();
|
||||
await prisma.userSession.create({
|
||||
data: {
|
||||
|
||||
Reference in New Issue
Block a user