Pull to refresh
-23
0.1
Алексей Смирнов@pol_pot

User

Send message

Сейчас бы проводить опросы, в условиях когда половина интернета уже заблокирована, а вторая в ближайших планах.

Ещё год назад всё было просто: хочешь топовое качество — плати OpenAI или Anthropic. Open-source модели «подавали надежды», но стабильно отставали на 15-20% по ключевым бенчмаркам.

Не отставали а отстают, и не на 20% а очень сильно.

Вот например что отвечает новейший претендент на простой вопрос на который надо было ответить, я не знаю, вместо генерации вагона бредятины.

PrimeIntellect/INTELLECT-3


Отлично, с удовольствием составлю список ключевых героев «Незнайки на Луне»! Это классика советской фантастики, где персонажи сочетают юмор и глубокие идеи. Вот 10 центральных героев, отсортированных по значимости в сюжете (на мой субъективный вкус):

  1. Незнайка (Нестор Нeschastikov) – главный герой, непоседливый и рассеянный изобретатель. Он же и «лунный первопроходец» благодаря стечению обстоятельств.

  2. Гайнуллин – гений инженерии и техники, создатель ракеты для полёта на Луну. Самый рассудительный персонаж.

  3. Квачков – пилот ракеты, вечно энергичный и немного суетливый. Отвечал за навигацию и техническую 「изюминку».

  4. Баранкин – навигатор и философ команды, автор теории «лучших поцелуев». Один из эпицентров комичных ситуаций.

  5. Лунатики – раса лунных жителей, чьи странности и принципы управления становятся ключевыми для сюжета (их «вожак» – Серафим).

  6. Иван-дурак (Fox) – один из «Солнечников» (лучников), символ наивной но unterwegsteny мудрости.

  7. Балбес – ещё один «солнечный» персонаж, комический антигений с оригинальными идеями.

  8. Дунчик – младший из «Солнечников», особенно активный и неугомонный.

  9. Игон (Леспёк) – загадочный «искрён» из Царства Космоса, возможный «дух» научного прогресса или аллегория разума.

  10. Космические Искорки (Ignatiki) – группа年轻ых учёных из Звёздного Королевства, помогающих Незнайке вернуться на Землю.

Рейтинг условен – каждый герой по-своему важен. Некоторые критики видят в nto-персонажах аллегорию на общество, например, Лунатики – это «власть», а Солнечники – «народ». Хотите подробнее о ком-то из них или о символике? 😊

В телеге до сих пор работает даже платежная система с фублями вместо денег. Не похоже это на террористический мессенджер.

Можно обойтись и без ваиргарда, на сервере должен быть только ssh, на клиенте он скорее всего тоже есть (вин10,вин11), то есть никакого софта устанавливать не надо. Для удобства только ключи добавить что бы без пароля заходило.

Пишем батник буквально с одной строкой (чатгпт подскажет как его скрыть с глаз, как запускать итп)

ssh -N -T -o ServerAliveInterval=60 -o ServerAliveCountMax=3 -p %REMOTE_SSH_PORT% -D %LOCAL_SOCKS_PORT% "%REMOTE_USER%@%REMOTE_HOST%"

Ну и еще пару строк надо добавить для бесконечного цикла, что бы переподключался автоматически (опять же чатгпт всё сделает).

Это создаст сокс прокси которую можно подключить в браузере, лучше через какое-нибудь специальное расширение типа foxyproxy для удобства.

Во время реализации очереди я столкнулся с проблемами, связанными с несовместимостью Celery и python‑telegram‑bot, которые не позволяли после обработки задач сразу отправлять сообщения пользователям. Немного покопавшись, я остановился на реализации, когда в Celery после обработки задачи я отправляю ответ напрямую в чат, используя request и Telegram API, указав соответствующий чат и креды для бота.

Что за несовместимость?

Зачем вообще телеграм, почему не веб сайт, у веб сайта нет ограничений типа нельзя закачать в бота больше чем 20мб файл или показать юзеру текст больше 4к без разрывов.

В Google Drive есть облачное хранилище для документов, фото и видео с автоматической загрузкой, интегрированная почта, календарь. Его интерфейс продуман до мелочей,

Робот писал или на свете существует юзер довольный интерфейсом гугл диска?

В каком месте он главный. Что бы он сел в лужу достаточно просто боком текст повернуть.

С доступом к опенроутером проблем нет, впн не нужен. Если бы ботхаб выступал в роли казаха посередине для оплаты заблокированных сервисов вопросов бы никаких не было.

Но они пытаются строить самостоятельный сервис на базе других поставщиков, как это делает Perplexity, причем без подписок, а значит очень дорого для тех кто хоть сколько-нибудь активно пользуется

Абсолютно бесполезная "экосистема". Для мелких простых запросов есть бесплатные алисы дипсики итп, а для нормальной работы получается запредельно дорого.

Ну например пишете программы, каждый день загружаете в gemini pro контекст размером пару сотен тысяч токенов и делаете пару десятков запросов с ним, получаете чек на 20 баксов в день(на самом деле в час). За месяц выходит баксов 500. То есть платите намного больше чем за оригинальную подписку + получаете в подарок низкокачественный сервис созданный руками 2.5 отечественных программистов (против оригинального сервиса от гугла который делают сотни людей).

Приложения для телефонов сделаны так же "качественно" как для телеграма?

Телеграм бот с >2млн юзерами в месяц не обучен самым элементарным вещам - как правильно принять запрос и донести его до ллм не испортив по дороге.

В чем суть обвинения, есть закон который требует что бы пистолет не стрелял если расстояние до цели меньше метра ллм говорил какие то конкретно слова когда речь заходит о суициде?

Стимдек не взлетел? Почему так мало линукса.

Попросил гпт(а точнее джемини) сделать для этой задачи инструмент, вебапку (просто хтмл файлы которые можно сохранить через блокнот себе на комп и запускать локально в браузере).

Первая версия делает нарезку сеткой по 9шт, автоматически детектит расположение и центрирует, и потом еще растягивает всех до одного требуемого телеграмом размера.

Вторая версия позволяет вручную аккуратно выделить тех кто плохо получился + такая же постобработка и подготовка к телеграму.

Получилось меньше 1млн токенов, если бы пришлось платить перепродавцам типа ботхаба за токены бесплатного по сути джемини то сожрало бы рублей 300.

ps Скорее всего получится сделать прозрачный слой у картинки автоматически, наложив маску на белый цвет (не совсем тупо а добавив немного условий типа вокруг должно быть еще несколько белых точек). И тогда для создания стикеров можно будет использовать картинки с белым фоном без прозрачности как делают все остальные генераторы изображений.

Скрытый текст
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Авто-подготовка стикеров для Telegram</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
    <style>
        body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; line-height: 1.6; padding: 20px; max-width: 900px; margin: auto; background-color: #f9f9f9; }
        .container { background-color: #fff; padding: 25px; border-radius: 10px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
        h2 { border-bottom: 2px solid #eee; padding-bottom: 10px; }
        .controls { margin: 20px 0; padding: 15px; background-color: #f0f3f5; border-radius: 8px; }
        .controls label { margin-right: 10px; vertical-align: middle; }
        .controls input[type="range"] { vertical-align: middle; width: 120px; }
        .controls input[type="text"] { vertical-align: middle; padding: 4px; border: 1px solid #ccc; border-radius: 4px; }
        #status { margin-top: 15px; font-weight: bold; color: #007bff; }
        #output-area { margin-top: 20px; }
        #output { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 15px; }
        .slice-container { text-align: center; background-color: #f0f3f5; padding: 10px; border-radius: 8px; }
        .slice-container img { max-width: 100%; border: 1px solid #ddd; background-image: linear-gradient(45deg, #eee 25%, transparent 25%), linear-gradient(-45deg, #eee 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #eee 75%), linear-gradient(-45deg, transparent 75%, #eee 75%); background-size: 20px 20px; background-position: 0 0, 0 10px, 10px -10px, -10px 0px; }
        .slice-container a { display: block; margin-top: 8px; text-decoration: none; background-color: #007bff; color: white; padding: 5px 10px; border-radius: 5px; font-size: 14px; }
        button { background-color: #28a745; color: white; border: none; padding: 10px 15px; border-radius: 5px; cursor: pointer; font-size: 16px; }
        #downloadZipBtn { background-color: #17a2b8; }
    </style>
</head>
<body>

<div class="container">
    <h2>Автоматическая подготовка стикеров для Telegram</h2>
    <p>Инструмент сам найдет контуры стикеров и отформатирует их в нужный размер 512x512 пикселей.</p>
    
    <input type="file" id="imageLoader" accept="image/png"/>
    
    <div class="controls" style="display: none;">
        <div>
            <label for="padding">Отступ (px):</label>
            <input type="range" id="padding" min="0" max="100" value="0">
            <span id="paddingValue">0</span>
        </div>
        <div style="margin-top: 10px;">
             <label for="baseFilename">Базовое имя файла:</label>
             <input type="text" id="baseFilename" value="sticker_auto_tg">
        </div>
    </div>

    <button id="processBtn" style="display: none;">Анализировать и вырезать</button>
    <div id="status"></div>
    
    <div id="output-area">
        <button id="downloadZipBtn" style="display: none;">Скачать все (.zip)</button>
        <div id="output"></div>
    </div>
</div>

<script>
    const imageLoader = document.getElementById('imageLoader');
    const controls = document.querySelector('.controls');
    const processBtn = document.getElementById('processBtn');
    const downloadZipBtn = document.getElementById('downloadZipBtn');
    const baseFilenameInput = document.getElementById('baseFilename');
    const paddingSlider = document.getElementById('padding');
    const paddingValueSpan = document.getElementById('paddingValue');
    const statusDiv = document.getElementById('status');
    const outputDiv = document.getElementById('output');

    let originalImage = null;
    let imageData = null;

    imageLoader.addEventListener('change', e => {
        const reader = new FileReader();
        reader.onload = event => {
            originalImage = new Image();
            originalImage.onload = () => {
                const mainCanvas = document.createElement('canvas');
                mainCanvas.width = originalImage.width;
                mainCanvas.height = originalImage.height;
                const mainCtx = mainCanvas.getContext('2d');
                mainCtx.drawImage(originalImage, 0, 0);
                imageData = mainCtx.getImageData(0, 0, mainCanvas.width, mainCanvas.height);
                
                controls.style.display = 'block';
                processBtn.style.display = 'inline-block';
                outputDiv.innerHTML = '';
                downloadZipBtn.style.display = 'none';
                statusDiv.innerText = '';
            };
            originalImage.src = event.target.result;
        };
        reader.readAsDataURL(e.target.files[0]);
    });
    
    paddingSlider.addEventListener('input', e => {
        paddingValueSpan.innerText = e.target.value;
    });

    processBtn.addEventListener('click', () => {
        if (!originalImage) return;
        
        outputDiv.innerHTML = '';
        downloadZipBtn.style.display = 'none';
        statusDiv.innerText = 'Анализ изображения... Это может занять несколько секунд.';

        setTimeout(processImage, 50);
    });

    function processImage() {
        const cols = 3, rows = 3;
        const cellWidth = imageData.width / cols;
        const cellHeight = imageData.height / rows;
        let stickerCount = 0;
        const padding = parseInt(paddingSlider.value, 10);
        const visited = new Set();

        for (let r = 0; r < rows; r++) {
            for (let c = 0; c < cols; c++) {
                const cellX = c * cellWidth;
                const cellY = r * cellHeight;
                
                let sumX = 0, sumY = 0, count = 0;
                for (let y = 0; y < cellHeight; y++) {
                    for (let x = 0; x < cellWidth; x++) {
                        const globalX = Math.floor(cellX + x);
                        const globalY = Math.floor(cellY + y);
                        const alpha = imageData.data[(globalY * imageData.width + globalX) * 4 + 3];
                        if (alpha > 50) {
                            sumX += globalX;
                            sumY += globalY;
                            count++;
                        }
                    }
                }

                if (count > 0) {
                    const startX = Math.floor(sumX / count);
                    const startY = Math.floor(sumY / count);
                    
                    if (visited.has(startY * imageData.width + startX)) continue;

                    const bounds = findStickerBoundsBFS(startX, startY, visited);

                    if (bounds) {
                        stickerCount++;
                        const cropX = bounds.minX - padding;
                        const cropY = bounds.minY - padding;
                        const cropWidth = (bounds.maxX - bounds.minX) + 1 + (padding * 2);
                        const cropHeight = (bounds.maxY - bounds.minY) + 1 + (padding * 2);

                        const tempCanvas = document.createElement('canvas');
                        tempCanvas.width = cropWidth;
                        tempCanvas.height = cropHeight;
                        const tempCtx = tempCanvas.getContext('2d');
                        
                        tempCtx.drawImage(originalImage, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
                        
                        // --- НОВЫЙ БЛОК: ФОРМАТИРОВАНИЕ ДЛЯ TELEGRAM ---
                        const finalCanvas = resizeAndCenterForTelegram(tempCanvas);
                        displayResult(finalCanvas, stickerCount);
                    }
                }
            }
        }
        
        statusDiv.innerText = `Готово! Найдено ${stickerCount} стикеров.`;
        if (stickerCount > 0) downloadZipBtn.style.display = 'inline-block';
    }

    function resizeAndCenterForTelegram(sourceCanvas) {
        const TELEGRAM_SIZE = 512;
        const finalCanvas = document.createElement('canvas');
        finalCanvas.width = TELEGRAM_SIZE;
        finalCanvas.height = TELEGRAM_SIZE;
        const finalCtx = finalCanvas.getContext('2d');

        const sourceWidth = sourceCanvas.width;
        const sourceHeight = sourceCanvas.height;

        const scaleFactor = Math.min(TELEGRAM_SIZE / sourceWidth, TELEGRAM_SIZE / sourceHeight);
        
        const newWidth = sourceWidth * scaleFactor;
        const newHeight = sourceHeight * scaleFactor;
        
        const drawX = (TELEGRAM_SIZE - newWidth) / 2;
        const drawY = (TELEGRAM_SIZE - newHeight) / 2;

        finalCtx.drawImage(sourceCanvas, drawX, drawY, newWidth, newHeight);
        
        return finalCanvas;
    }

    function findStickerBoundsBFS(startX, startY, visited) {
        const queue = [[startX, startY]];
        const bounds = { minX: startX, minY: startY, maxX: startX, maxY: startY };
        const startIndex = startY * imageData.width + startX;
        if (visited.has(startIndex)) return null;
        visited.add(startIndex);

        while (queue.length > 0) {
            const [x, y] = queue.shift();
            bounds.minX = Math.min(bounds.minX, x);
            bounds.maxX = Math.max(bounds.maxX, x);
            bounds.minY = Math.min(bounds.minY, y);
            bounds.maxY = Math.max(bounds.maxY, y);
            
            const neighbors = [[x, y - 1], [x, y + 1], [x - 1, y], [x + 1, y]];
            for (const [nx, ny] of neighbors) {
                if (nx >= 0 && nx < imageData.width && ny >= 0 && ny < imageData.height) {
                    const nIndex = ny * imageData.width + nx;
                    if (!visited.has(nIndex)) {
                        visited.add(nIndex);
                        const alpha = imageData.data[nIndex * 4 + 3];
                        if (alpha > 50) queue.push([nx, ny]);
                    }
                }
            }
        }
        return bounds;
    }
    
    function displayResult(canvas, count) {
        const container = document.createElement('div');
        container.className = 'slice-container';
        const imgElement = document.createElement('img');
        const dataUrl = canvas.toDataURL('image/png');
        imgElement.src = dataUrl;
        
        const link = document.createElement('a');
        link.href = dataUrl;
        const baseName = baseFilenameInput.value.trim() || 'sticker_auto_tg';
        link.download = `${baseName}_${count}.png`;
        link.innerText = 'Скачать';
        
        container.appendChild(imgElement);
        container.appendChild(link);
        outputDiv.appendChild(container);
    }
    
    downloadZipBtn.addEventListener('click', async () => {
        const zip = new JSZip();
        downloadZipBtn.innerText = 'Архивация...';
        
        document.querySelectorAll('#output .slice-container').forEach(slice => {
            const img = slice.querySelector('img');
            const link = slice.querySelector('a');
            const base64Data = img.src.split(',')[1];
            zip.file(link.download, base64Data, {base64: true});
        });
        
        const baseName = baseFilenameInput.value.trim() || 'sticker_auto_tg';
        const content = await zip.generateAsync({type:"blob"});
        const link = document.createElement('a');
        link.href = URL.createObjectURL(content);
        link.download = `${baseName}_archive.zip`;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        downloadZipBtn.innerText = 'Скачать все (.zip)';
    });
</script>

</body>
</html>
Скрытый текст
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Обводка и подготовка стикеров для Telegram</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
    <style>
        body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; line-height: 1.6; padding: 20px; max-width: 900px; margin: auto; background-color: #f9f9f9; }
        .container { background-color: #fff; padding: 25px; border-radius: 10px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
        h2 { border-bottom: 2px solid #eee; padding-bottom: 10px; }
        .controls { margin: 20px 0; padding: 15px; background-color: #f0f3f5; border-radius: 8px; display: none; }
        .controls label { margin-right: 10px; vertical-align: middle; }
        .controls input[type="text"] { vertical-align: middle; padding: 4px; border: 1px solid #ccc; border-radius: 4px; }
        canvas { border: 2px dashed #ccc; cursor: crosshair; max-width: 100%; display: block; margin-top: 15px; }
        #output-area { margin-top: 20px; }
        #output { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 15px; }
        .slice-container { text-align: center; background-color: #f0f3f5; padding: 10px; border-radius: 8px; }
        .slice-container img { max-width: 100%; border: 1px solid #ddd; background-image: linear-gradient(45deg, #eee 25%, transparent 25%), linear-gradient(-45deg, #eee 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #eee 75%), linear-gradient(-45deg, transparent 75%, #eee 75%); background-size: 20px 20px; background-position: 0 0, 0 10px, 10px -10px, -10px 0px; }
        .slice-container a { display: block; margin-top: 8px; text-decoration: none; background-color: #007bff; color: white; padding: 5px 10px; border-radius: 5px; font-size: 14px; }
        button { background-color: #6c757d; color: white; border: none; padding: 10px 15px; border-radius: 5px; cursor: pointer; font-size: 16px; margin-right: 10px; }
        #downloadZipBtn { background-color: #17a2b8; }
    </style>
</head>
<body>

<div class="container">
    <h2>Подготовка стикеров для Telegram</h2>
    <p>Обведите стикер и замкните контур. Результат автоматически форматируется в 512x512 пикселей для Telegram.</p>
    
    <input type="file" id="imageLoader" accept="image/png"/>
    
    <div class="controls">
        <button id="undoBtn">Отменить точку</button>
        <button id="clearBtn">Очистить выделение</button>
        <label for="baseFilename">Базовое имя файла:</label>
        <input type="text" id="baseFilename" value="sticker_tg">
    </div>

    <canvas id="canvas"></canvas>
    
    <div id="output-area">
        <button id="downloadZipBtn" style="display: none;">Скачать все (.zip)</button>
        <div id="output"></div>
    </div>
</div>

<script>
    const imageLoader = document.getElementById('imageLoader');
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');
    const controls = document.querySelector('.controls');
    const undoBtn = document.getElementById('undoBtn');
    const clearBtn = document.getElementById('clearBtn');
    const downloadZipBtn = document.getElementById('downloadZipBtn');
    const baseFilenameInput = document.getElementById('baseFilename');

    let originalImage = null;
    let points = [];
    let isDrawing = false;
    let stickerCount = 0;
    const SNAP_DISTANCE = 15;

    imageLoader.addEventListener('change', e => {
        const reader = new FileReader();
        reader.onload = event => {
            originalImage = new Image();
            originalImage.onload = () => {
                canvas.width = originalImage.width;
                canvas.height = originalImage.height;
                controls.style.display = 'block';
                clearSelection();
                document.getElementById('output').innerHTML = '';
                downloadZipBtn.style.display = 'none';
                stickerCount = 0;
                draw();
            };
            originalImage.src = event.target.result;
        };
        reader.readAsDataURL(e.target.files[0]);
    });

    function draw() {
        if (!originalImage) return;
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.drawImage(originalImage, 0, 0);

        if (points.length === 0) return;

        ctx.strokeStyle = 'rgba(0, 255, 255, 0.9)';
        ctx.lineWidth = 2;
        ctx.beginPath();
        ctx.moveTo(points[0].x, points[0].y);
        for (let i = 1; i < points.length; i++) {
            ctx.lineTo(points[i].x, points[i].y);
        }
        ctx.stroke();

        ctx.fillStyle = 'rgba(0, 255, 255, 0.9)';
        points.forEach(p => {
            ctx.beginPath();
            ctx.arc(p.x, p.y, 4, 0, Math.PI * 2);
            ctx.fill();
        });

        const mousePos = getCurrentMousePos(event);
        if (mousePos && points.length > 2) {
            const dist = Math.hypot(mousePos.x - points[0].x, mousePos.y - points[0].y);
            if (dist < SNAP_DISTANCE) {
                ctx.fillStyle = 'rgba(255, 0, 0, 1)';
                ctx.beginPath();
                ctx.arc(points[0].x, points[0].y, 8, 0, Math.PI * 2);
                ctx.fill();
            }
        }
    }

    let currentMousePos = null;
    function getCurrentMousePos(evt) {
        if (!evt) return currentMousePos;
        const rect = canvas.getBoundingClientRect();
        const scaleX = canvas.width / rect.width;
        const scaleY = canvas.height / rect.height;
        currentMousePos = {
            x: (evt.clientX - rect.left) * scaleX,
            y: (evt.clientY - rect.top) * scaleY
        };
        return currentMousePos;
    }

    canvas.addEventListener('mousemove', e => {
        getCurrentMousePos(e);
        if (isDrawing) {
            points.push(getCurrentMousePos(e));
        }
        draw();
    });

    canvas.addEventListener('mousedown', e => {
        const pos = getCurrentMousePos(e);
        if (points.length > 2) {
            const dist = Math.hypot(pos.x - points[0].x, pos.y - points[0].y);
            if (dist < SNAP_DISTANCE) {
                cutSelection();
                return;
            }
        }
        isDrawing = true;
        points.push(pos);
        draw();
    });

    canvas.addEventListener('mouseup', () => { isDrawing = false; });
    undoBtn.addEventListener('click', () => { points.pop(); draw(); });
    clearBtn.addEventListener('click', clearSelection);

    function clearSelection() {
        points = [];
        draw();
    }

    function cutSelection() {
        if (points.length < 3) return;
        
        const bounds = {
            minX: Math.min(...points.map(p => p.x)),
            minY: Math.min(...points.map(p => p.y)),
            maxX: Math.max(...points.map(p => p.x)),
            maxY: Math.max(...points.map(p => p.y))
        };

        const width = bounds.maxX - bounds.minX;
        const height = bounds.maxY - bounds.minY;

        const tempCanvas = document.createElement('canvas');
        tempCanvas.width = width;
        tempCanvas.height = height;
        const tempCtx = tempCanvas.getContext('2d');

        tempCtx.save();
        tempCtx.beginPath();
        tempCtx.moveTo(points[0].x - bounds.minX, points[0].y - bounds.minY);
        for (let i = 1; i < points.length; i++) {
            tempCtx.lineTo(points[i].x - bounds.minX, points[i].y - bounds.minY);
        }
        tempCtx.closePath();
        tempCtx.clip();
        
        tempCtx.drawImage(originalImage, -bounds.minX, -bounds.minY);
        tempCtx.restore();
        
        // --- НОВЫЙ БЛОК: ФОРМАТИРОВАНИЕ ДЛЯ TELEGRAM ---
        const finalCanvas = resizeAndCenterForTelegram(tempCanvas);
        
        stickerCount++;
        displayResult(finalCanvas, stickerCount);
        clearSelection();
    }
    
    function resizeAndCenterForTelegram(sourceCanvas) {
        const TELEGRAM_SIZE = 512;
        const finalCanvas = document.createElement('canvas');
        finalCanvas.width = TELEGRAM_SIZE;
        finalCanvas.height = TELEGRAM_SIZE;
        const finalCtx = finalCanvas.getContext('2d');

        const sourceWidth = sourceCanvas.width;
        const sourceHeight = sourceCanvas.height;

        const scaleFactor = Math.min(TELEGRAM_SIZE / sourceWidth, TELEGRAM_SIZE / sourceHeight);
        
        const newWidth = sourceWidth * scaleFactor;
        const newHeight = sourceHeight * scaleFactor;

        // Центрируем изображение
        const drawX = (TELEGRAM_SIZE - newWidth) / 2;
        const drawY = (TELEGRAM_SIZE - newHeight) / 2;

        finalCtx.drawImage(sourceCanvas, drawX, drawY, newWidth, newHeight);
        
        return finalCanvas;
    }

    function displayResult(resCanvas, count) {
        const output = document.getElementById('output');
        const container = document.createElement('div');
        container.className = 'slice-container';
        const imgElement = document.createElement('img');
        const dataUrl = resCanvas.toDataURL('image/png');
        imgElement.src = dataUrl;
        
        const link = document.createElement('a');
        link.href = dataUrl;
        const baseName = baseFilenameInput.value.trim() || 'sticker_tg';
        link.download = `${baseName}_${count}.png`;
        link.innerText = 'Скачать';
        
        container.appendChild(imgElement);
        container.appendChild(link);
        output.appendChild(container);
        downloadZipBtn.style.display = 'inline-block';
    }

    downloadZipBtn.addEventListener('click', async () => {
        const zip = new JSZip();
        downloadZipBtn.innerText = 'Архивация...';
        
        document.querySelectorAll('#output .slice-container').forEach(slice => {
            const img = slice.querySelector('img');
            const link = slice.querySelector('a');
            const base64Data = img.src.split(',')[1];
            zip.file(link.download, base64Data, {base64: true});
        });
        
        const baseName = baseFilenameInput.value.trim() || 'sticker_tg';
        const content = await zip.generateAsync({type:"blob"});
        const link = document.createElement('a');
        link.href = URL.createObjectURL(content);
        link.download = `${baseName}_archive.zip`;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        downloadZipBtn.innerText = 'Скачать все (.zip)';
    });

</script>

</body>
</html>

Привязать почту как второй фактор к уже имеющемуся аккаунту? Это же совсем не то же самое что зарегистрировать новый аккаунт только на почту.

В рекламе сказано что алиса теперь умеет пересказывать содержание видео. Но на любые ссылки на ютуб отвечает что не может там смотреть.

А что тебе мешает нагуглить способ как обновить винду на неподдерживаемом оборудовании? Работает даже на антикварных компах из 2005 года.

На поддерживаемом она сама обновится.

Продать байку о том что новая винда не работает на старом компе можно доверчивым хомячкам, но уж точно не админам в корпорациях.

Разработка игр это большая индустрия и она в рф почти вымерла благодаря целительным санкциям. Если это не тот случай когда надо раздавать льготы для поддержки тогда какой вообще случай должен быть?

Компьютер энтерпрайза общается с обученными офицерами и инженерами звездного флота, они просто знают что и как надо говорить и делать что бы получить хороший результат.

Современные ИИ от гугла и яндекса пока что тупят на пустом месте, путают слова при распознавании речи. При генерации речи путают ударения. И как то не особо виден прогресс в этой области.

1
23 ...

Information

Rating
3,573-rd
Registered
Activity

Specialization

Менеджер технической поддержки
Старший
Git
SQL
Linux
MySQL
PHP
PostgreSQL
Docker