<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Артур Фаттахов</title><generator>teletype.in</generator><description><![CDATA[Я Артур, дизайнер  с 14-летним опытом и кейсами для Альфа-Банка, МТС, Роснефти, СБИС, Т2 и Фармленда. Автоматизирую рутину]]></description><image><url>https://img2.teletype.in/files/1b/c7/1bc715c1-560f-4517-bc69-979c5e05f123.png</url><title>Артур Фаттахов</title><link>https://teletype.in/@rhv2006</link></image><link>https://teletype.in/@rhv2006?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=rhv2006</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/rhv2006?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/rhv2006?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Mon, 19 Oct 2026 12:24:14 GMT</pubDate><lastBuildDate>Mon, 19 Oct 2026 12:24:14 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@rhv2006/850dZpOdUEY</guid><link>https://teletype.in/@rhv2006/850dZpOdUEY?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=rhv2006</link><comments>https://teletype.in/@rhv2006/850dZpOdUEY?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=rhv2006#comments</comments><dc:creator>rhv2006</dc:creator><title>Бот, который откликается на вакансии за меня</title><pubDate>Wed, 19 Aug 2026 08:53:49 GMT</pubDate><description><![CDATA[После истории со скриптом, который вылавливал вакансии в телеграм-каналах, я подумал: а почему бы не пойти дальше? На hh.ru та же самая проблема, только в других декорациях — свежие вакансии по 3D и моушену появляются, откликов набирается уже полсотни в первые полчаса, и если ты не в числе первых десяти, шансы просесть очень сильно. Ручками сидеть и обновлять поиск каждые пять минут — это не жизнь. Так родился второй скрипт, который уже не просто присылает уведомление, а сам заходит на сайт, читает вакансию, решает, стоит ли откликаться, и если да — откликается вместо меня, с сопроводительным письмом.]]></description><content:encoded><![CDATA[
  <p id="3Zm5">После истории со скриптом, который вылавливал вакансии в телеграм-каналах, я подумал: а почему бы не пойти дальше? На hh.ru та же самая проблема, только в других декорациях — свежие вакансии по 3D и моушену появляются, откликов набирается уже полсотни в первые полчаса, и если ты не в числе первых десяти, шансы просесть очень сильно. Ручками сидеть и обновлять поиск каждые пять минут — это не жизнь. Так родился второй скрипт, который уже не просто присылает уведомление, а сам заходит на сайт, читает вакансию, решает, стоит ли откликаться, и если да — откликается вместо меня, с сопроводительным письмом.</p>
  <h2 id="nNho">Из чего это собрано</h2>
  <p id="VB9I">В основе — Playwright, библиотека для управления браузером из кода. По сути это тот же браузер, что открывается у тебя на компьютере, только им управляет скрипт: он умеет кликать, скроллить, читать текст со страницы, заполнять формы. Я специально запускаю его не в headless-режиме, а с видимым окном — так проще заметить, если что-то пошло не так, и вручную вмешаться.</p>
  <p id="vGID">Для распознавания «подходит вакансия или нет» используется та же связка, что и в телеграм-версии — локальная модель через Ollama. Ключевые слова и правила фильтрации вынесены в отдельный файл <code>filters.txt</code>, чтобы не лезть в код каждый раз, когда нужно подправить список профессий. Модель получает текст вакансии и по инструкции решает: да, подходит, вот название должности — или нет.</p>
  <h2 id="XL4m">Как проходит один цикл</h2>
  <p id="30pE">Скрипт открывает поиск на hh.ru с готовыми параметрами — по сути, обычная страница результатов, только с сортировкой по свежести и периодом в один день. Дальше он прокручивает страницу вниз несколько раз, чтобы подгрузились все вакансии, и собирает ссылки на каждую карточку.</p>
  <p id="J4Tf">По каждой ссылке скрипт заходит на страницу вакансии по очереди. Первым делом проверяет, не откликался ли уже на эту вакансию раньше — для этого ведётся простой текстовый журнал <code>applied_links.log</code>, куда пишутся уже пройденные ссылки без параметров запроса (чтобы одна и та же вакансия с разными UTM-метками не считалась дважды).</p>
  <p id="uAGT">Дальше идёт проверка на «вакансию с тестом» — на hh.ru часто вместо обычного отклика просят сначала пройти тестовое задание или ответить на вопросы прямо на странице. Такие вакансии скрипт узнаёт по характерным фразам в описании вроде «пройти тест» или «заполните анкету», а также по слову question в самой ссылке. Если это тестовая вакансия — скрипт помечает её как обработанную, но вкладку намеренно не закрывает, чтобы я потом мог сам вручную пройти тест, если вакансия того стоит. Автоматизировать прохождение произвольных тестов — идея так себе, там слишком много нестандартного.</p>
  <p id="LL8W">Если тестов нет, весь текст страницы отправляется в модель на вердикт. Если ответ отрицательный, вакансия просто помечается пройденной и скрипт идёт дальше. Если положительный — начинается самое интересное.</p>
  <h2 id="c3vE">Автоматический отклик</h2>
  <p id="Qrx8">Скрипт ищет кнопку отклика на странице, кликает по ней, и дальше по цепочке разбирает возможные всплывающие окна: предупреждение про переезд в другой город («Всё равно откликнуться»), выбор резюме из списка, если их несколько, поле для сопроводительного письма. Письмо берётся не из головы модели, а из заранее заготовленных шаблонов в файле <code>templates.txt</code> — они чередуются по кругу через <code>itertools.cycle</code>, чтобы не заваливать разных работодателей одинаковым текстом слово в слово. После заполнения скрипт нажимает «Отправить» и записывает результат.</p>
  <p id="287y">Каждый успешный отклик попадает в Excel-таблицу — через <code>pandas</code> дописывается строка с названием вакансии, компанией, ссылкой, статусом и датой. Получается что-то вроде личного трекера откликов, который ведётся сам, без единой лишней вкладки Google Sheets.</p>
  <h2 id="vYw8">Мелочи, которые оказались важны на практике</h2>
  <p id="psw2">Часть решений в коде — это не архитектурные изыски, а просто следы набитых шишек. Например, отдельно оговорено, что текст вакансии для проверки на «тест» берётся именно из блока описания, а не со всей страницы целиком — потому что в шапке или подвале сайта тоже может случайно встретиться слово вроде «тестирование», и тогда скрипт зря пропускал бы нормальную вакансию. Ещё один нюанс — переменные окружения <code>NO_PROXY</code> и <code>all_proxy</code> выставляются в начале файла, чтобы запросы к локальной Ollama не улетали через прокси, который используется для остального трафика.</p>
  <p id="iRGA">В конце работы, после того как список вакансий пройден, скрипт не завершается сразу — он ждёт, пока я не закрою вручную все оставшиеся вкладки с тестовыми заданиями. Это осознанный компромисс: полностью бросать открытые вкладки не хочется, а автоматически их закрывать — значит потерять те вакансии, которые могли бы того стоить.</p>
  <p id="Fujd">В сумме получилось не идеальное, но вполне рабочее решение: я просыпаюсь, а часть вакансий, на которые стоило откликнуться, уже обработана, письмо отправлено, запись в таблице есть, и в браузере меня ждут только те случаи, где действительно нужна моя голова, а не набор правил.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@rhv2006/ZN7TeEHCqY3</guid><link>https://teletype.in/@rhv2006/ZN7TeEHCqY3?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=rhv2006</link><comments>https://teletype.in/@rhv2006/ZN7TeEHCqY3?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=rhv2006#comments</comments><dc:creator>rhv2006</dc:creator><title>Как я написал бота, который ищет мне вакансии, пока я сплю</title><pubDate>Wed, 19 Aug 2026 08:49:53 GMT</pubDate><media:content medium="image" url="https://img4.teletype.in/files/b3/3c/b33c5afa-37bb-4bac-a96b-37b007e5c77f.png"></media:content><description><![CDATA[<img src="https://img3.teletype.in/files/2f/4b/2f4bb919-39a0-4e42-b0a3-0019c11f04e7.jpeg"></img>Есть такая штука в жизни 3D-художника и моушен-дизайнера: хорошие заказы разлетаются за пятнадцать минут после публикации. Пока ты долистываешь ленту в метро, вакансия уже занята кем-то, кто сидел в чате и обновлял канал каждые полминуты. Раньше я и сам так делал — держал открытыми штук двадцать телеграм-каналов с вакансиями и время от времени листал их вручную. Занятие такое себе: скучно, отнимает время и всё равно что-то пропускаешь.]]></description><content:encoded><![CDATA[
  <p id="PGis">Есть такая штука в жизни 3D-художника и моушен-дизайнера: хорошие заказы разлетаются за пятнадцать минут после публикации. Пока ты долистываешь ленту в метро, вакансия уже занята кем-то, кто сидел в чате и обновлял канал каждые полминуты. Раньше я и сам так делал — держал открытыми штук двадцать телеграм-каналов с вакансиями и время от времени листал их вручную. Занятие такое себе: скучно, отнимает время и всё равно что-то пропускаешь.</p>
  <p id="LbmX">В какой-то момент я решил, что раз я умею писать код, глупо продолжать заниматься этим руками. Так появился скрипт, который я в шутку назвал <code>stalker.py</code> — он действительно «выслеживает» вакансии по каналам, только не человека, а посты нужного профиля.</p>
  <h2 id="LhIb">Что он вообще делает</h2>
  <p id="QUil">Логика простая, если разложить по шагам. Скрипт подключается к моему телеграм-аккаунту через Pyrogram — это библиотека, которая позволяет работать с Telegram API как с обычным пользователем, а не через бота. Разница важна: боту нельзя просто так читать историю каналов, на которые он не подписан от имени владельца, а через юзер-аккаунт — можно, если ты сам туда вступил.</p>
  <p id="6cpw">Дальше он проходит по списку каналов из файла <code>channels.txt</code> и смотрит последние сообщения. Есть два режима глубины поиска: если скрипт запустился утром, между 11:40 и 11:59, он сканирует последние 15 часов — то есть подчищает всё, что накопилось за ночь. В остальное время смотрит только последний час. Это запускается через cron или планировщик — сам скрипт не крутится в фоне постоянно, а стартует по расписанию, делает свою работу и завершается.</p>
  <h2 id="4e24">Фильтрация в два прохода</h2>
  <p id="gK4o">Самое интересное — как отсеивается мусор. Каналов с вакансиями для дизайнеров много, но процентов восемьдесят контента там не имеет ко мне отношения: UI/UX, инфографика, обложки для ютуба, таргетологи, резюме других людей, которые ищут работу в том же чате. Если пропускать это через нейросеть каждый раз, будет и медленно, и накладно.</p>
  <p id="2PBj">Поэтому первым делом идёт грубый фильтр по ключевым словам. Список стоп-слов (<code>GARBAGE_KEYWORDS</code>) отсекает явно нерелевантные посты сразу, без всякого ИИ — просто проверка вхождения подстроки. Отдельно отсекаются резюме — по фразам вроде «меня зовут» или «ищу проекты», потому что это не вакансия, а чьё-то самопродвижение. И только если пост прошёл эти фильтры и в нём встретилось хоть одно слово из списка нужных тем (3D, motion, Cinema 4D, Houdini, Redshift и так далее), он попадает на следующий этап.</p>
  <p id="FXSF">Второй этап — локальная модель. Скрипт стучится в Ollama, который крутится на том же компьютере на порту 11434, и просит модель <code>gpt-oss:20b</code> вынести финальный вердикт: подходит вакансия под профиль 3D/motion-дизайнера или нет. Тут смысл в том, что ключевые слова — штука грубая, они ловят и ложные срабатывания («ищем UI/UX дизайнера с опытом в 3D-иконках» тоже пройдёт по словам). Модель уже смотрит на контекст и отсеивает то, что формально совпало, но по сути не подходит.</p>
  <p id="O1O7">Важный момент — почему это локальная модель, а не запрос к условному GPT через API. Во-первых, дешевле: постов проходит через фильтр по ключевым словам не так много, но за месяц набегает прилично, и гонять их все через платный API смысла нет. Во-вторых, скорость и приватность — не нужно ждать ответа от внешнего сервера и не нужно отправлять куда-то содержимое чужих каналов.</p>
  <h2 id="Hc6f">Что происходит с найденным</h2>
  <p id="o9Y5">Если модель говорит «да», скрипт формирует короткую карточку — заголовок вакансии, название канала, ссылка на пост (или пометка «Private Link», если у канала нет юзернейма) и обрезанное описание — и отправляет всё это самому себе в Telegram, в «Избранное». Получается что-то вроде личной ленты уже отфильтрованных вакансий, куда можно просто иногда заглядывать вместо того, чтобы шерстить два десятка каналов.</p>
  <p id="ROqM">Чтобы не присылать одно и то же дважды, есть простейшая база данных — обычный текстовый файл <code>sent_tasks.txt</code>, куда пишется уникальный идентификатор сообщения (айди чата плюс айди поста). Перед обработкой скрипт проверяет, нет ли такого идентификатора уже в файле. Никакой базы данных в полном смысле слова тут не нужно — задача маленькая, и текстовый файл справляется без проблем.</p>
  <h2 id="Teuq">Почему так, а не иначе</h2>
  <p id="tPSb">Можно было бы сделать то же самое через официальный Bot API, но тогда пришлось бы добавлять бота в каждый канал отдельно, а часть каналов админы просто не пустят. Через юзер-сессию эта проблема снимается — если я сам состою в канале, скрипт может читать историю переписки.</p>
  <p id="pMiB">Получилось не идеально — иногда модель ошибается, иногда список ключевых слов приходится донастраивать, когда появляется новый тип мусора в лентах. Но по сравнению с ручным листанием двадцати каналов каждый день это уже совсем другой уровень комфорта: просыпаешься, а в «Избранном» уже лежит десяток отфильтрованных вариантов, и можно сразу решать, куда откликаться.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@rhv2006/hKFw6GKi-yF</guid><link>https://teletype.in/@rhv2006/hKFw6GKi-yF?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=rhv2006</link><comments>https://teletype.in/@rhv2006/hKFw6GKi-yF?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=rhv2006#comments</comments><dc:creator>rhv2006</dc:creator><title>«Больше никаких 1000 кликов»: Как я заставил C4D рендерить сотни конфигураций без человека</title><pubDate>Fri, 14 Aug 2026 12:41:05 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/d2/e4/d2e47002-0ce4-4b69-a49d-68cb798ab8c1.png"></media:content><description><![CDATA[Скрипт на Python для Cinema 4D, заменяющий ручной перебор десятков комбинаций «профиль × наполнитель × поддон × угол × материал» единым GUI-инструментом с автоматическим сопоставлением файлов и пакетным рендером.]]></description><content:encoded><![CDATA[
  <figure id="H2Wz" class="m_original">
    <img src="https://img1.teletype.in/files/03/19/03196195-cf07-49dc-be02-9aee05b59f1d.png" width="2541" />
  </figure>
  <p id="bCRg"><em>Скрипт на Python для Cinema 4D, заменяющий ручной перебор десятков комбинаций «профиль × наполнитель × поддон × угол × материал» единым GUI-инструментом с автоматическим сопоставлением файлов и пакетным рендером.</em></p>
  <h2 id="Tiqx"><strong>Контекст и задача</strong></h2>
  <p id="9lZt">Проект выполнен для компании «Техностиль», занимающейся изготовлением стеновых и кровельных сэндвич-панелей (профилированный металлический лист + утеплитель). Отдел визуализации использует Cinema 4D для генерации PNG-иконок каждого профиля панели, наполнителя (утеплителя — минвата/пенополистирол/PIR-PUR) и поддона — эти изображения затем используются в веб-конфигураторе продукции на сайте компании, где клиент собирает панель нужной комплектации «по кубикам».</p>
  <p id="S31g">До автоматизации 3D-художнику приходилось вручную для каждой единицы номенклатуры: подгружать нужный .ai-файл контура профиля панели, переключать видимость слоёв в сцене, выставлять углы стыковки (90°/135°), задавать материал утеплителя (вата/пенополистирол/PIR-PUR), запускать рендер и раскладывать PNG по правильным папкам. При десятках профилей и наполнителей это часы монотонной ручной работы и источник ошибок в путях сохранения.</p>
  <p id="7q22">Задача скрипта — свести всю эту работу к нескольким кликам: пользователь один раз выбирает угол, материал и режим, отмечает нужные профили/наполнители в чек-листе, и скрипт сам рендерит и раскладывает результат по дисковой структуре.</p>
  <h2 id="ukGC"><strong>Технологический стек</strong></h2>
  <p id="FKRa">—  Python (Cinema 4D Python API — модуль c4d)</p>
  <p id="f97k">—  c4d.gui.GeDialog — кастомные модальные GUI-диалоги (чекбоксы, группы, кнопки)</p>
  <p id="jScU">—  c4d.documents.RenderDocument + MultipassBitmap — программный рендер и сохранение PNG с альфа-каналом</p>
  <p id="El4p">—  User Data сцены (ID_USERDATA) — как «пульт управления» видимостью объектов и назначением текстур из скрипта</p>
  <p id="KqY4">—  os / файловая система — сканирование, парсинг имён файлов, построение путей сохранения</p>
  <h2 id="I2c6"><strong>Архитектура решения</strong></h2>
  <p id="4ymo">Скрипт построен вокруг одного управляющего объекта сцены RENDER_CONTROL, чьи User Data-параметры выступают интерфейсом между Python-кодом и содержимым 3D-сцены: путь к .ai-файлу контура, путь к файлу наполнителя, путь к поддону, набор булевых «выключателей видимости» (профиль/вата/поддон, замки LZ/PR/PZ, matte-параметры торцов lw/pw) и переключатели угла. Это классический паттерн «оживления» статичной сцены данными снаружи — сцена не меняется, меняются только входные параметры перед каждым рендером.</p>
  <p id="gqcm">Логика выполнения делится на пять последовательных этапов:</p>
  <p id="Msvg">—  Выбор угла раскроя (90° / 135° / без угла) — через нативный c4d.gui.ShowPopupDialog.</p>
  <p id="FWRQ">—  Выбор материала наполнителя (VATA / POLY / PIR) — с автоматическим подбором материала сцены по имени и donастройкой UV-тайлинга текстуры под конкретный материал.</p>
  <p id="bJu5">—  Выбор логического режима рендера (всё вместе, весь профиль, LZ/PR/PZ по отдельности, только наполнитель, только поддон).</p>
  <p id="X4Og">—  Сканирование каталога источника, разбор имён .ai-файлов и группировка через собственные GUI-диалоги (ProfileSelectDialog, NapolnitelSelectDialog).</p>
  <p id="5CUU">—  Цикл рендера: для каждой выбранной позиции скрипт выставляет нужную комбинацию «переключателей» через set_all_switches(), инициирует пересчёт сцены и рендерит кадр в вычисленную по правилам папку.</p>
  <h2 id="XUC1">Ключевые инженерные решения</h2>
  <h3 id="tfNy"><strong>Самодокументируемый диспетчер состояний сцены</strong></h3>
  <p id="e8Oa">Функция set_all_switches() — сердце скрипта. Вместо разбросанных по коду присвоений она собирает все восемь переключателей сцены (видимость профиля/ваты/поддона, три типа замков, два matte-флага торцов) в одну именованную сигнатуру с понятными умолчаниями и докстрингом, объясняющим смысл каждого флага. Любой следующий разработчик может понять логику состояния сцены за 10 секунд, не читая C4D-документацию.</p>
  <h3 id="3r3g"><strong>Эвристический парсер и «спаривание» файлов</strong></h3>
  <p id="jlQu">Файлы поставляются с говорящими, но не строго стандартизированными именами (profil_lz.ai, napolnitel_lz pirpur.ai и т.п.). Функция find_paired_file() очищает префикс и расширение, нормализует регистр и пробелы/подчёркивания и ищет по «чистому» имени соответствующий файл наполнителя или поддона для текущего профиля — с поддержкой суффикса материала (например, pirpur), который отличает, скажем, наполнитель под PIR-PUR от такого же наполнителя под вату.</p>
  <h3 id="6O3r"><strong>Собственные GUI-диалоги с группировкой по смыслу</strong></h3>
  <p id="XxKJ">Вместо стандартного системного диалога «выбери файлы» реализованы два кастомных c4d.gui.GeDialog:</p>
  <p id="pLji">—  ProfileSelectDialog — список профилей, автоматически разделённый на «обычные» и «замковые» (по суффиксу _z), с кнопками «Выбрать все» / «Снять все».</p>
  <p id="tUby">—  NapolnitelSelectDialog — трёхколоночный диалог наполнителей (без префикса / группа Z / группа VENT) с быстрыми фильтрами «только Z», «только VENT» и т. д., чтобы не листать десятки чекбоксов вручную.</p>
  <p id="AbOL">Оба диалога используют единый приём: диапазон ID чекбоксов (CHECK_BASE) плюс индекс в объединённом списке, что позволяет одной строкой восстановить, какие позиции пользователь отметил, без хранения отдельного состояния на каждый чекбокс.</p>
  <h3 id="6k9Z"><strong>Предсказуемая структура сохранения результатов</strong></h3>
  <p id="HcWo">Путь сохранения PNG собирается динамически из нескольких независимых переменных: базовая папка → флаг «замки» → тип объекта (profil/napolnitel/poddon) → материал наполнителя → подпапка угла (90°/135°). Такой подход гарантирует, что структура папок веб-конфигуратора всегда предсказуема и не зависит от порядка выбора пользователем — а для повторяющегося поддона (одинакового для группы профилей) отдельный флаг poddon_rendered_once исключает лишние дубли рендера.</p>
  <h3 id="u7t0"><strong>Защита от «пустого» рендера</strong></h3>
  <p id="CIqX">Перед каждым фактическим рендером сцена дважды прогоняется через wait_for_scene() (ExecutePasses + GeSyncMessage), чтобы Cinema 4D успела пересчитать зависимые генераторы и текстуры после смены User Data — иначе первый кадр в серии рискует захватить ещё не обновлённое состояние сцены. Это распространённая, но неочевидная проблема программного рендера в C4D, решённая явным ожиданием синхронизации.</p>
  <h2 id="9RDC"><strong>Пример потока выполнения</strong></h2>
  <p id="IDCt">—  находит парный файл наполнителя под PIR-PUR и рендерит его отдельным проходом с другим набором переключателей видимости;</p>
  <p id="PgTx">—  рендерит поддон один раз на группу профилей без замков;</p>
  <p id="Z74w">—  сохраняет все PNG по маршруту вида .../zamki/profil/uglovie/135°/... и по завершении открывает соответствующие папки в проводнике.</p>
  <h2 id="FgF7"><strong>Итог</strong></h2>
  <p id="hjS5">Скрипт превращает многочасовую рутинную задачу подготовки графики для конфигуратора в управляемый процесс из нескольких кликов: 3D-художник задаёт только высокоуровневые параметры (угол, материал, что рендерить), а сопоставление файлов, переключение состояний сцены, построение путей и пакетный рендер выполняются автоматически и воспроизводимо.</p>
  <p id="QC9R">С инженерной точки зрения работа демонстрирует практическое владение Python-интеграцией со сторонним DCC-приложением (Cinema 4D SDK), проектирование пользовательских GUI поверх непривычного тулкита, устойчивый к вариативности входных данных парсинг имён файлов и аккуратную декомпозицию сложной ветвящейся логики (шесть режимов рендера) на переиспользуемые функции.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@rhv2006/5_4evRjEqCe</guid><link>https://teletype.in/@rhv2006/5_4evRjEqCe?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=rhv2006</link><comments>https://teletype.in/@rhv2006/5_4evRjEqCe?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=rhv2006#comments</comments><dc:creator>rhv2006</dc:creator><title>Минус 80% рутины на ресайзах: Как я заавтоматизировал верстку для T2 с помощью ExtendScript</title><pubDate>Fri, 14 Aug 2026 10:13:27 GMT</pubDate><description><![CDATA[<img src="https://img3.teletype.in/files/21/72/21720210-d84b-43bf-a4c3-645cd736b6a1.png"></img>Комплекс скриптов написан на JavaScript / ExtendScript для автоматизации повседневных рутинных операций в Adobe Illustrator. Набор инструментов полностью убирает человеческий фактор при предпечатной подготовке (prepress), построении модульных сеток, выравнивании элементов и адаптивном экспорте.]]></description><content:encoded><![CDATA[
  <figure id="aWeT" class="m_original">
    <img src="https://img3.teletype.in/files/21/72/21720210-d84b-43bf-a4c3-645cd736b6a1.png" width="475" />
  </figure>
  <h2 id="lT5Q">Контекст и решаемая задача</h2>
  <p id="LaUM">Комплекс скриптов написан на <strong>JavaScript / ExtendScript</strong> для автоматизации повседневных рутинных операций в Adobe Illustrator. Набор инструментов полностью убирает человеческий фактор при предпечатной подготовке (prepress), построении модульных сеток, выравнивании элементов и адаптивном экспорте.</p>
  <p id="VX14"><strong>Ключевые показатели эффективности:</strong></p>
  <ul id="oqsD">
    <li id="86HU"><strong>В 10–15 раз</strong> ускорение пакетного экспорта и верстки ресайзов.</li>
    <li id="7EEz"><strong>0% ошибок</strong> при предпечатной подготовке (исключены пропущенные шрифты, незалинкованные файлы и некорректная цветовая модель).</li>
    <li id="jj1H"><strong>100% соблюдение гайдлайнов</strong> за счёт математического расчёта полей и отступов</li>
  </ul>
  <h2 id="IE6l">Разбор ключевых модулей</h2>
  <p id="S7Dk"><strong>Модульная сетка и гайды (<code>runT2Grid</code>, <code>scriptOne</code>, <code>scriptTwo</code>)</strong></p>
  <ul id="FlCJ">
    <li id="g5Ju"><strong>Что делает:</strong> Автоматически строит внешнюю рамку безопасности и 6 опорных направляющих (центральные оси + пропорциональные сдвиги).</li>
    <li id="ds9t"><strong>Логика:</strong> Все отступы рассчитываются динамически от меньшей стороны артборда: <code>var minSide = Math.min(width, height)</code>.</li>
    <li id="RPga"><strong>Зачем:</strong> Мгновенная разметка артборда любого формата под требования брендбука.</li>
  </ul>
  <p id="R3dZ"><strong>Умное распределение элементов (<code>runPaddings</code>, <code>scriptSeven</code>, <code>scriptEight</code>)</strong></p>
  <ul id="lFN0">
    <li id="iUHL"><strong>Что делает:</strong> Выравнивает выбранные объекты вдоль вертикальной или горизонтальной оси с фиксированным шагом.</li>
    <li id="H7r6"><strong>Логика:</strong> Определяет координаты, сортирует объекты по порядку (<code>top</code> или <code>left</code>) и выстраивает их с идеальным равным интервалом, не позволяя вылезти за края артборда.</li>
  </ul>
  <p id="dy4S"><strong>Пропорциональное скругление углов (<code>runRoundCorners</code>, <code>scriptThree</code>, <code>scriptFour</code>)</strong></p>
  <ul id="w0HD">
    <li id="QVrW"><strong>Что делает:</strong> Находит среди выделения замкнутые прямоугольники и применяет к ним адаптивный радиус скругления.</li>
    <li id="2IX3"><strong>Логика:</strong> Проверяет топологию объекта (4 точки, равенство противоположных сторон) и пересоздаёт его как <code>roundedRectangle</code> с сохранением исходных заливок и обводок.</li>
  </ul>
  <p id="BnpF"><strong>Пакетный Prepress и экспорт (<code>scriptFive</code>, <code>scriptSix</code>, <code>scriptTwelve</code>)</strong></p>
  <ul id="KS9w">
    <li id="a6nc"><strong>Что делает:</strong> Полный цикл подготовки к печати:</li>
    <ol id="JX5u">
      <li id="n3cP">Автоматический перевод всего текста в кривые.</li>
      <li id="1Rc2">Сбор всех связанных изображений (placed items) в единую папку <code>/links</code>.</li>
      <li id="K8cc">Экспорт векторов в PDF с вылетами под обрез и метками реза.</li>
      <li id="1gEZ">Генерация сборных JPEG-превью для всех артбордов.</li>
    </ol>
  </ul>
  <p id="2Pjl"><strong>Пакетный поиск и замена «Ищейка» (<code>scriptThirteen_Ischeika</code>)</strong></p>
  <ul id="3ngx">
    <li id="FQii"><strong>Что делает:</strong> Рекурсивно сканирует вложенные папки, находит текстовые блоки в AI-файлах, делает замену, пересчитывает размеры рамок и сохраняет результаты.</li>
    <li id="dLOk"><strong>Уникальная фича (<code>fitTextInFrame</code>):</strong> Если новый текст не помещается, скрипт автоматически конвертирует <code>PointText</code> в <code>AreaText</code> и динамически увеличивает рамку вниз до устранения переполнения (<code>overflows === false</code>).</li>
  </ul>
  <p id="JoIS"><strong>Генерация ресайзов из данных (<code>scriptTen</code>, <code>scriptEleven_MassVizitki</code>)</strong></p>
  <ul id="sGab">
    <li id="FbZl"><strong>Что делает:</strong> Считывает CSV-файлы с размерами или техническими данными и автоматически генерирует файлы нужных габаритов с точной привязкой элементов.</li>
  </ul>
  <h2 id="QnlJ">Технические особенности реализации</h2>
  <p id="p-rc_bdf5d1ea8ab7ef62-75"><strong>Безопасная работа с файловой системой (I/O):</strong> Реализованы кастомные функции контроля записи <code>waitForFileWritten</code> и освобождения памяти <code>waitForDocClosed</code>, что исключает появление битых PDF/JPG или зависание Illustrator при фоновой обработке сотен файлов.</p>
  <p id="p-rc_bdf5d1ea8ab7ef62-76"><strong>Адаптивная математика (Relative Scaling):</strong> Отсутствие жесткого хардкода размеры/отступы считаются в процентах/долях от меньшей стороны текущего артборда (<code>Math.min(W, H)</code>), что делает скрипты универсальными для макетов любого масштаба.</p>
  <p id="p-rc_bdf5d1ea8ab7ef62-77"><strong>UI/UX интерфейс:</strong> Все сложные скрипты снабжены удобными диалоговыми окнами (<code>ScriptUI</code>) с выбором параметров экспорта (DPI 72/150/300, качество JPEG, метки реза, выбор папок).</p>
  <p id="VYEJ">Эти скрипты — пример того, как я оптимизирую рутину в дизайне и верстке. Вместо того чтобы вручную переводить 50 макетов в кривые, выставлять метки реза и раскладывать файлы по папкам, я написал скрипты на JS/ExtendScript. Они выполняют часовую работу за пару минут, гарантируют отсутствие технических ошибок и позволяют команде фокусироваться на креативных задачах, а не на технической рутине</p>

]]></content:encoded></item></channel></rss>