feat: migrate entire codebase to TypeScript
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
const FILE_TYPES = {
|
||||
const FILE_TYPES: Record<string, number> = {
|
||||
document: 2 * 1024 * 1024 * 1024, // 2GB
|
||||
photo: 10 * 1024 * 1024, // 10MB
|
||||
video: 2 * 1024 * 1024 * 1024, // 2GB
|
||||
@@ -7,7 +7,7 @@ const FILE_TYPES = {
|
||||
animation: 2 * 1024 * 1024 * 1024 // 2GB
|
||||
};
|
||||
|
||||
export const getFileType = (mime, caption) => {
|
||||
export const getFileType = (mime: string | null, caption?: string): string => {
|
||||
const mimeUpper = mime?.split('/')[0]?.toLowerCase();
|
||||
const captionLower = caption?.toLowerCase();
|
||||
|
||||
@@ -21,12 +21,12 @@ export const getFileType = (mime, caption) => {
|
||||
return mimeUpper || 'document';
|
||||
};
|
||||
|
||||
export const checkFileSize = (sizeBytes, fileType) => {
|
||||
export const checkFileSize = (sizeBytes: number, fileType: string): boolean => {
|
||||
const limit = FILE_TYPES[fileType] || FILE_TYPES.document;
|
||||
return sizeBytes <= limit;
|
||||
};
|
||||
|
||||
export const extractFileName = (msg, request) => {
|
||||
export const extractFileName = (msg: any, request: any): string => {
|
||||
if (request?.headers?.['x-file-name']) {
|
||||
return request.headers['x-file-name'];
|
||||
}
|
||||
@@ -34,7 +34,7 @@ export const extractFileName = (msg, request) => {
|
||||
msg.voice?.fileName || msg.animation?.fileName || 'file';
|
||||
};
|
||||
|
||||
export const extractMimeType = (msg, request) => {
|
||||
export const extractMimeType = (msg: any, request: any): string => {
|
||||
if (request?.headers?.['x-mime-type']) {
|
||||
return request.headers['x-mime-type'];
|
||||
}
|
||||
@@ -1,17 +1,22 @@
|
||||
import logger from './logger.js';
|
||||
import logger from './logger';
|
||||
|
||||
const rateLimitMap = new Map();
|
||||
interface RateLimitRecord {
|
||||
count: number;
|
||||
reset: number;
|
||||
}
|
||||
|
||||
export const checkRateLimit = (key) => {
|
||||
const rateLimitMap = new Map<string, RateLimitRecord>();
|
||||
|
||||
export const checkRateLimit = (key: string): boolean => {
|
||||
const now = Date.now();
|
||||
const windowMs = parseInt(process.env.RATE_LIMIT_WINDOW_MS, 10) || 60000;
|
||||
const maxRequests = parseInt(process.env.RATE_LIMIT_MAX_REQUESTS, 10) || 30;
|
||||
const windowMs = parseInt(process.env.RATE_LIMIT_WINDOW_MS!, 10) || 60000;
|
||||
const maxRequests = parseInt(process.env.RATE_LIMIT_MAX_REQUESTS!, 10) || 30;
|
||||
|
||||
if (!rateLimitMap.has(key)) {
|
||||
rateLimitMap.set(key, { count: 0, reset: now + windowMs });
|
||||
}
|
||||
|
||||
const record = rateLimitMap.get(key);
|
||||
const record = rateLimitMap.get(key)!;
|
||||
|
||||
if (now > record.reset) {
|
||||
record.count = 0;
|
||||
@@ -27,10 +32,9 @@ export const checkRateLimit = (key) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
export const cleanupRateLimitCache = () => {
|
||||
export const cleanupRateLimitCache = (): void => {
|
||||
const now = Date.now();
|
||||
const windowMs = parseInt(process.env.RATE_LIMIT_WINDOW_MS, 10) || 60000;
|
||||
const keysToDelete = [];
|
||||
const keysToDelete: string[] = [];
|
||||
|
||||
for (const [key, record] of rateLimitMap.entries()) {
|
||||
if (now > record.reset) {
|
||||
@@ -38,5 +42,7 @@ export const cleanupRateLimitCache = () => {
|
||||
}
|
||||
}
|
||||
|
||||
keysToDelete.forEach(key => rateLimitMap.delete(key));
|
||||
for (const key of keysToDelete) {
|
||||
rateLimitMap.delete(key);
|
||||
}
|
||||
};
|
||||
@@ -1,34 +1,53 @@
|
||||
import { Telegraf } from 'telegraf';
|
||||
import logger from './logger.js';
|
||||
import { config } from '../env.js';
|
||||
import logger from './logger';
|
||||
import { config } from '../env';
|
||||
|
||||
const bot = new Telegraf(config.botToken);
|
||||
const TELEGRAM_API_URL = `https://api.telegram.org/bot${config.botToken}/`;
|
||||
|
||||
export const forwardToStorage = async (fileChunk, fileName, forceDocument = false) => {
|
||||
interface ForwardResult {
|
||||
telegramFileId: string;
|
||||
telegramFileUniqueId: string;
|
||||
storageMessageId: number;
|
||||
}
|
||||
|
||||
interface TelegramFileInfo {
|
||||
file_size: number;
|
||||
mime_type: string;
|
||||
file_path: string;
|
||||
}
|
||||
|
||||
export const forwardToStorage = async (
|
||||
fileChunk: any,
|
||||
fileName: string,
|
||||
forceDocument = false
|
||||
): Promise<ForwardResult> => {
|
||||
try {
|
||||
const caption = forceDocument ? `📁 ${fileName}` : fileName;
|
||||
const input = forceDocument ? { document: fileChunk, caption } : { photo: [fileChunk], caption };
|
||||
const input: any = forceDocument ? { document: fileChunk, caption } : { photo: [fileChunk], caption };
|
||||
|
||||
const result = await bot.api.sendPhoto(config.storageChatId, input);
|
||||
const result = await bot.telegram.sendPhoto(config.storageChatId, input);
|
||||
|
||||
logger.info('File forwarded to storage', { fileName, message: result.message_id });
|
||||
|
||||
return {
|
||||
telegramFileId: result.photo?.slice(-1)[0]?.file_id,
|
||||
telegramFileUniqueId: result.photo?.slice(-1)[0]?.file_unique_id,
|
||||
telegramFileId: result.photo?.slice(-1)[0]?.file_id || '',
|
||||
telegramFileUniqueId: result.photo?.slice(-1)[0]?.file_unique_id || '',
|
||||
storageMessageId: result.message_id
|
||||
};
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
logger.error('Failed to forward file to storage', { fileName, error: error.message });
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const getFileInfo = async (telegramFileId, telegramFileUniqueId) => {
|
||||
export const getFileInfo = async (
|
||||
telegramFileId: string,
|
||||
telegramFileUniqueId: string
|
||||
): Promise<TelegramFileInfo> => {
|
||||
try {
|
||||
const result = await fetch(`${TELEGRAM_API_URL}getFile`);
|
||||
const data = await result.json();
|
||||
const data: any = await result.json();
|
||||
|
||||
if (!data.ok) {
|
||||
throw new Error(data.description || 'Telegram API error');
|
||||
@@ -40,7 +59,7 @@ export const getFileInfo = async (telegramFileId, telegramFileUniqueId) => {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ file_id: fileId })
|
||||
});
|
||||
const fileInfo = await fileResult.json();
|
||||
const fileInfo: any = await fileResult.json();
|
||||
|
||||
if (!fileInfo.ok) {
|
||||
throw new Error(fileInfo.description || 'Telegram info error');
|
||||
@@ -51,10 +70,10 @@ export const getFileInfo = async (telegramFileId, telegramFileUniqueId) => {
|
||||
mime_type: fileInfo.result.mime_type,
|
||||
file_path: fileInfo.result.file_path
|
||||
};
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
logger.error('Failed to get file info', { error: error.message });
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const getBot = () => bot;
|
||||
export const getBot = (): Telegraf => bot;
|
||||
Reference in New Issue
Block a user