Обновить
-21
0.1
Алексей Смирнов@pol_pot

Пользователь

Отправить сообщение

Во время реализации очереди я столкнулся с проблемами, связанными с несовместимостью 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 года.

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

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

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

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

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

Звонки работают даже если у второй стороны нормальный клиент телеги и она в чебурнете?

Если rclone то почему не restic.

Государство так и будет сокращать расходы и повышать налоги. В Сирии на момент бегства "законно избранного" медианная зп была 20-30 долларов. Можем повторить.

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

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

Информация

В рейтинге
3 324-й
Зарегистрирован
Активность

Специализация

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