[{"content":"","date":"вт, 5 мая 2026","externalUrl":null,"permalink":"/tags/docker/","section":"Tags","summary":"","title":"Docker","type":"tags"},{"content":"","date":"вт, 5 мая 2026","externalUrl":null,"permalink":"/tags/linux/","section":"Tags","summary":"","title":"Linux","type":"tags"},{"content":"","date":"вт, 5 мая 2026","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"вт, 5 мая 2026","externalUrl":null,"permalink":"/tags/%D0%B0%D0%B4%D0%BC%D0%B8%D0%BD%D0%B8%D1%81%D1%82%D1%80%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5/","section":"Tags","summary":"","title":"Администрирование","type":"tags"},{"content":"Также известен как cyrmax.\nЯ пишу код, тестирую, администрирую инфраструктуру и занимаюсь цифровой доступностью.\nНа этом сайте собраны мои контакты, проекты и технические статьи по Linux, Windows, Docker и DevOps.\n","date":"вт, 5 мая 2026","externalUrl":null,"permalink":"/","section":"Кирилл Белоусов","summary":"","title":"Кирилл Белоусов","type":"page"},{"content":"На правах части резюме и просто понтов ради описываю свою домашнюю лабораторию.\nЧто у меня есть Этот сайт (Hugo + CI на GitHub, который собирает и раскатывает на сервер); Nextcloud (своё облако, календари, задачи, синхронизация контактов); VaultWarden (опенсорсный сервер для менеджера паролей и ключей BitWarden); TeamTalk5 (не обращайте внимания, это очень непопулярная история); Gitea (self-hosted альтернатива GitHub); NVDARemoteServer (для удалённого управления компьютерами со скринридером NVDA); HeadScale (см. ниже про сеть); Более десятка Telegram-ботов на Python и Go (часть написал сам); Мониторинг Со всего зоопарка серверов собираются метрики при помощи node-exporter, а также подключён сбор метрик из caddy для серверов, где крутятся сайты.\nДанные собираются Prometheus, рядом с которым работает AlertManager с отправкой алертов в Telegram, а также Grafana, которой я пользуюсь достаточно редко, так как дашборды - штука визуальная и для незрячего девопса не сильно полезная.\nТакже, для централизованного сбора логов на всех серверах подняты promtail, а loki на центральном сервере всё это собирает и, конечно же, общается с AlertManager.\nМониторинг пока без репликации, но когда-нибудь я и до этого дозрею.\nDocker пока не мониторится вообще, и это плохо. Планирую заняться этим в ближайшее время. Только недавно перевёз большинство сервисов в docker compose проекты, и ещё не дошёл до мониторинга.\nСеть Все серверы связаны в сеть при помощи tailscale, где вместо проприетарного координатора используется его self-hosted-замена в виде HeadScale.\nОбщение между компонентами мониторинга происходит только через внутреннюю сеть, публичные IP серверов не прослушиваются. Ибо безопасность.\nОстальных деталей не расскажу, ибо могут прийти и сделать \u0026ldquo;атата\u0026rdquo;, но, как вы понимаете, алерты в Telegram с российских серверов тоже надо было доставлять, и пришлось искать решения.\nБэкапы, куда же без них Я, к сожалению, тот человек, который \u0026ldquo;уже делает бэкапы\u0026rdquo;.\nКопии делаются со всех серверов и за неимением средств на крутое объектное хранилище складываются на два сервера с самым большим диском, разнесённые географически.\nУведомления об успешных бэкапах, также, как и об ошибках, отсылаются в Telegram.\nПо технологиям - borgbackup + borgmatic + ansible playbook для настройки и будущего обновления всего этого при необходимости (включая конфиги).\nКак я всем этим управляю Казалось бы, парк из 8 серверов уже заставляет задумываться о плотном погружении в ansible, но пока что я закрепил в нём только настройку бэкапов и соединение серверов частной сетью через tailscale.\nВ остальном - зачастую управляю руками, и лишь иногда пишу простенькие плейбуки на один раз.\nЧто по технологиям в целом docker (достаточно активно); NFTables (потому что очень люблю его синтаксис и удобство. Но дружить его с docker надо уметь); bash (очень активно, почти каждый день, сами понимаете, надо); ansible (см. выше. Пока не везде и не всегда); До более серьёзных технологий вроде k8s ещё не дорос, да и не уверен, что на моих масштабах это реально нужно.\nПланы Большие компании о планах рассказывать не любят, ибо потом есть шанс опозориться, не сделав задуманное, или подарить идею конкурентам.\nА я всего лишь админ-одиночка, поэтому вот:\nПолноценно овладеть Ansible и в идеале добиться того, чтобы всё и на всех серверах подчинялось идее infrastructure as code; Донастроить мониторинг и определиться с тем, какие именно метрики мне нужны, а какие можно и выкинуть (только место занимают); Перенести большинство репозиториев на свой Gitea, а GitHub оставить как зеркало, например для резюме (решение не без нюансов, ибо если можно не платить за GitHub Actions и деплоить оттуда, то почему бы и нет); Поднять свой стек для музыки (а то цензура на российских площадках достала, а Яндекс Музыка позволяет загрузить только до 10_000 своих треков); Научиться поднимать php-сайты в Docker (у больших проектов есть готовые образы, но почему бы не попробовать сделать всё самому). ","date":"вт, 5 мая 2026","externalUrl":null,"permalink":"/projects/my-infra/","section":"Проекты","summary":"","title":"Моя домашняя лаборатория","type":"projects"},{"content":"","date":"вт, 5 мая 2026","externalUrl":null,"permalink":"/projects/","section":"Проекты","summary":"","title":"Проекты","type":"projects"},{"content":"","date":"вс, 19 апреля 2026","externalUrl":null,"permalink":"/tags/powershell/","section":"Tags","summary":"","title":"Powershell","type":"tags"},{"content":"","date":"вс, 19 апреля 2026","externalUrl":null,"permalink":"/tags/windows/","section":"Tags","summary":"","title":"Windows","type":"tags"},{"content":" Суть WinGet удобен для установки и обновления программ в Windows, но при регулярном использовании быстро начинает не хватать более гибкого интерактивного режима.\nНапример, хочется посмотреть список доступных обновлений и для каждого пакета выбрать действие:\nобновить; пропустить; пропустить все оставшиеся; Для этого я написал небольшой PowerShell-скрипт.\nТребования Скрипт рассчитан на PowerShell 7 и требует установленного модуля для работы с WinGet:\nInstall-Module -Name \u0026#39;Microsoft.WinGet.Client\u0026#39; После установки модуля можно запускать сам скрипт.\nЧто делает скрипт Скрипт получает список пакетов, для которых доступны обновления, а затем по очереди спрашивает, что сделать с каждым пакетом.\nДоступные действия:\nДействие Что делает Update Добавляет пакет в очередь обновления Skip Пропускает текущий пакет Skip All Пропускает все оставшиеся пакеты Remove Удаляет текущий пакет Действие Remove я добавил для себя, потому что после обновления Windows параллельно чистил систему от ненужных программ. Если оно не нужно, эту ветку можно удалить из скрипта.\nКод $ErrorActionPreference = \u0026#34;Stop\u0026#34; Set-StrictMode -Version Latest Write-Host \u0026#34;Looking for available WinGet updates...\u0026#34; $allUpdates = Get-WinGetPackage | Where-Object { $_.IsUpdateAvailable } if (-not $allUpdates) { Write-Host \u0026#34;No available WinGet updates found\u0026#34; exit } Write-Host \u0026#34;$($allUpdates.Count) updatable packages found.\u0026#34; $packagesToUpgrade = @() $skipRemaining = $false foreach ($pkg in $allUpdates) { if ($skipRemaining) { break } Write-Host \u0026#34;\u0026#34; Write-Host \u0026#34;Package: $($pkg.Name)\u0026#34; Write-Host \u0026#34;Id: $($pkg.Id)\u0026#34; Write-Host \u0026#34;Installed version: $($pkg.InstalledVersion)\u0026#34; Write-Host \u0026#34;Available version: $($pkg.AvailableVersions[0])\u0026#34; $choices = @( New-Object System.Management.Automation.Host.ChoiceDescription \u0026#34;\u0026amp;Update\u0026#34;, \u0026#34;Add to upgrade queue\u0026#34; New-Object System.Management.Automation.Host.ChoiceDescription \u0026#34;\u0026amp;Skip\u0026#34;, \u0026#34;Skip this package\u0026#34; New-Object System.Management.Automation.Host.ChoiceDescription \u0026#34;Skip \u0026amp;All\u0026#34;, \u0026#34;Skip all remaining packages\u0026#34; New-Object System.Management.Automation.Host.ChoiceDescription \u0026#34;\u0026amp;Remove\u0026#34;, \u0026#34;Remove this package now\u0026#34; ) $answer = $host.UI.PromptForChoice( \u0026#34;Action for $($pkg.Name)\u0026#34;, \u0026#34;Choose an action\u0026#34;, $choices, 0 ) switch ($answer) { 0 { $packagesToUpgrade += $pkg } 1 { Write-Host \u0026#34;Skipping\u0026#34; } 2 { Write-Host \u0026#34;Skipping all remaining packages\u0026#34; $skipRemaining = $true } 3 { Write-Host \u0026#34;Removing $($pkg.Name)\u0026#34; try { Uninstall-WinGetPackage -Id $pkg.Id -ErrorAction Stop } catch { Write-Host \u0026#34;Removal for $($pkg.Id) failed\u0026#34; Write-Host \u0026#34;Reason: $($_.Exception.Message)\u0026#34; } } } } if ($packagesToUpgrade.Count -eq 0) { Write-Host \u0026#34;No packages were selected for upgrade\u0026#34; exit } Write-Host \u0026#34;\u0026#34; Write-Host \u0026#34;Upgrading $($packagesToUpgrade.Count) packages...\u0026#34; foreach ($pkg in $packagesToUpgrade) { Write-Host \u0026#34;Upgrading $($pkg.Name)\u0026#34; try { Update-WinGetPackage -Id $pkg.Id -ErrorAction Stop } catch { Write-Host \u0026#34;Upgrade for $($pkg.Id) failed\u0026#34; Write-Host \u0026#34;Reason: $($_.Exception.Message)\u0026#34; } } Как использовать Установить модуль для работы с WinGet:\nInstall-Module -Name \u0026#39;Microsoft.WinGet.Client\u0026#39; Сохранить скрипт, например в файл:\nUpdate-WinGetPackages.ps1 Запустить его в PowerShell 7:\n.\\Update-WinGetPackages.ps1 Для каждого найденного обновления выбрать нужное действие.\nЧто можно упростить Если удаление пакетов не нужно, можно убрать этот пункт из массива $choices:\nNew-Object System.Management.Automation.Host.ChoiceDescription \u0026#34;\u0026amp;Remove\u0026#34;, \u0026#34;Remove this package now\u0026#34; И удалить ветку:\n3 { Write-Host \u0026#34;Removing $($pkg.Name)\u0026#34; try { Uninstall-WinGetPackage -Id $pkg.Id -ErrorAction Stop } catch { Write-Host \u0026#34;Removal for $($pkg.Id) failed\u0026#34; Write-Host \u0026#34;Reason: $($_.Exception.Message)\u0026#34; } } После этого останется только выбор между обновлением и пропуском пакетов.\nВозможные проблемы Модуль не установлен Если PowerShell не знает команды вроде Get-WinGetPackage или Update-WinGetPackage, сначала установите модуль:\nInstall-Module -Name \u0026#39;Microsoft.WinGet.Client\u0026#39; Скрипты запрещены политикой выполнения Если PowerShell не разрешает запуск .ps1-файлов, можно временно запустить скрипт так:\npwsh.exe -ExecutionPolicy Bypass -File .\\Update-WinGetPackages.ps1 Или для старого Windows PowerShell (если заработает):\npowershell.exe -ExecutionPolicy Bypass -File .\\Update-WinGetPackages.ps1 Некоторые пакеты не обновляются Это нормально: не все пакеты идеально взаимодействуют с WinGet. Тема подобных менеджеров пакетов в Windows ещё достаточно сырая.\nВ таком случае скрипт покажет ошибку и продолжит работу со следующими пакетами.\nИтог Этот скрипт не заменяет полноценный менеджер обновлений, но закрывает простую практическую задачу: быстро пройтись по доступным обновлениям WinGet и выбрать, что именно обновлять сейчас.\nДля регулярного ручного обслуживания Windows-системы этого вполне достаточно.\n","date":"вс, 19 апреля 2026","externalUrl":null,"permalink":"/posts/interactive-winget-updates-powershell/","section":"Технические посты","summary":"","title":"Интерактивное обновление пакетов WinGet через PowerShell","type":"posts"},{"content":"","date":"вс, 19 апреля 2026","externalUrl":null,"permalink":"/posts/","section":"Технические посты","summary":"","title":"Технические посты","type":"posts"},{"content":" Суть По следам трёхдневного ярого совокупления с поломавшимся домашним сервером.\nПодробно расписывать уже нет сил, поэтому это будет не академическая статья, а короткий чеклист: куда смотреть, если Linux-сервер ведёт себя странно.\nСимптомы примерно такие:\nсервер то принимает подключения, то не принимает; пинг может идти по 20 секунд; обновление системы зависает на подключении к внешним адресам; одни сайты открываются, другие нет; кажется, что интернет есть, но \u0026ldquo;какой-то не такой\u0026rdquo;. Если всё это знакомо, скорее всего, проблема где-то в сетевой конфигурации.\n1. Сначала проверь DNS Первым делом смотри DNS. Да, банально. Да, всё равно смотри.\nОткрой файл:\ncat /etc/resolv.conf Если внутри написано, что это заглушка от systemd-resolved, проверяй DNS через:\nresolvectl status Если там обычный список DNS-серверов без предупреждений, прочитай его глазами и проверь доступность серверов имён вручную.\nНапример:\nping 77.88.8.8 Или другой DNS-сервер, который у тебя указан в конфиге.\nВ условиях нашей весёлой сетевой реальности лишним это точно не будет.\n2. Отдели проблему DNS от проблемы сети Хороший простой тест:\nping ifconfig.me А потом:\nping 77.88.8.8 Логика такая:\nЧто происходит Возможная причина IP пингуется, домен нет проблема с DNS Не пингуется ни домен, ни IP проблема не только в DNS Работает через раз смотри DNS, маршруты, MTU и потери Важно помнить: DNS кэшируется.\nПосле изменений в конфиге проверяй не только тот домен, который уже дёргал раньше, но и что-то новое. Полезно иметь в голове или в заметке небольшой список сайтов для проверки, чтобы не ловить фантомы из кэша.\n3. Проверь маршруты Если DNS не виноват, смотри маршруты:\nip route show Как минимум должен быть маршрут по умолчанию. Обычно он выглядит примерно так:\ndefault via 192.168.1.1 dev eth0 Точный интерфейс и адрес шлюза, конечно, будут другими.\nГлавное, чтобы:\nмаршрут default был; шлюз был правильный; интерфейс был тот, через который сервер реально ходит в сеть; до шлюза был доступ. Проверить шлюз можно так:\nping 192.168.1.1 Если маршрута по умолчанию нет или он странный, смотри, кто управляет сетью:\nsystemd-networkd; ifupdown; NetworkManager; DHCP-клиент; настройки гипервизора или роутера, если сервер виртуальный или домашний. Иногда помогает перезапуск сетевой службы. Иногда не помогает, но создаёт ощущение, что ты хотя бы что-то сделал.\nЛоги всё равно читать придётся.\n4. Не забудь, что DNS может приехать по DHCP Если DNS внезапно стал неправильным, это не всегда значит, что ты руками сломал /etc/resolv.conf.\nDNS-серверы часто прилетают по DHCP от роутера, провайдера, виртуальной сети или чего-то ещё, что считает себя главным в этой маленькой трагедии.\nПоэтому, если DNS постоянно возвращается к неправильному значению, проверяй не только локальные файлы, но и источник сетевой конфигурации.\n5. Если всё выглядит нормально — проверь MTU Если одни пакеты проходят, другие нет, сайты открываются выборочно, SSH вроде работает, но обновления или загрузки зависают, стоит проверить MTU.\nОсобенно если в схеме есть:\nPPPoE; VPN; туннели; виртуальные сети; странный роутер; провайдер с фантазией. Сначала посмотри интерфейсы:\nip link show или:\nip addr show Найди основной интерфейс: тот, на котором публичный IP или локальный адрес от роутера.\nУ интерфейса будет параметр mtu, например:\nmtu 1500 Частые значения:\nMTU Где встречается 1500 обычный Ethernet 1492 PPPoE 1280 минимально допустимое значение для IPv6 другое VPN, туннели, виртуальные сети, особые случаи 6. Как проверить MTU через ping Из значения MTU нужно вычесть 28 байт: это размер заголовков IPv4 и ICMP.\nНапример, для MTU 1500:\n1500 - 28 = 1472 Проверяем:\nping -M do -c 4 -s 1472 8.8.8.8 Что здесь происходит:\n-c 4 Пинговать не бесконечно, а 4 раза.\n-s 1472 Отправить ICMP-пакет с payload размером 1472 байта.\n-M do Поставить флаг Do not fragment, то есть запретить фрагментацию.\nЕсли пакет проходит без фрагментации — хорошо.\nЕсли не проходит — начинаются те самые непредсказуемые приколы, ради которых мы все и любим администрирование.\n7. Как найти рабочее значение MTU Если 1472 не проходит, уменьшаем размер:\nping -M do -c 4 -s 1464 8.8.8.8 Потом ещё:\nping -M do -c 4 -s 1452 8.8.8.8 И так далее, пока не найдёшь максимальный размер, который проходит.\nПотом к найденному размеру добавляешь 28.\nНапример, если проходит 1464:\n1464 + 28 = 1492 Значит эффективный MTU — 1492.\nУ меня, например, из-за PPPoE на роутере максимальный MTU не 1500, как позволяет обычный Ethernet, а 1492, потому что PPPoE забирает 8 байт на свои служебные заголовки.\n8. Как временно выставить MTU Например, если интерфейс называется eth0:\nsudo ip link set dev eth0 mtu 1492 Если интерфейс называется иначе, подставь своё имя:\nip link show После этого снова проверь сеть.\nНо важно: это временная настройка. После перезагрузки она, скорее всего, пропадёт.\n9. Как сохранить MTU постоянно А вот тут начинается весёлое.\nСпособ зависит от того, кто управляет сетью:\nsystemd-networkd; ifupdown; NetworkManager; cloud-init; настройки роутера; настройки гипервизора; что-то ещё, что ты обнаружишь в самый неподходящий момент. Поэтому универсальной одной команды здесь не будет.\nДля начала можно понять, что вообще управляет сетью:\nsystemctl status systemd-networkd systemctl status NetworkManager Также можно посмотреть конфиги:\nls /etc/network/ ls /etc/systemd/network/ ls /etc/NetworkManager/ Разбор различий между systemd-networkd, ifupdown и NetworkManager — это уже тема для отдельной статьи. Возможно, когда-нибудь я её напишу. Заодно и сам ещё раз нормально разберусь.\nКороткий чеклист Если Linux-сервер странно теряет сеть:\nПроверь /etc/resolv.conf. Если используется systemd-resolved, смотри resolvectl status. Проверь пинг по домену и по IP отдельно. Помни про DNS-кэш. Проверь маршруты через ip route show. Убедись, что есть рабочий default route. Проверь, не прилетают ли DNS и маршруты по DHCP. Проверь MTU через ping -M do. Если нашёл рабочий MTU — временно выставь его через ip link set. Потом уже разбирайся, как сохранить настройку в твоём сетевом менеджере. Итог Странные сетевые проблемы редко чинятся одной магической командой.\nНо если идти по порядку — DNS, маршруты, DHCP, MTU — шанс найти проблему становится сильно выше. А шанс три дня разговаривать с сервером на повышенных тонах становится чуть ниже.\nНадеюсь, эти торопливые заметки мамкиного сисадмина кому-то помогут.\n","date":"пт, 3 апреля 2026","externalUrl":null,"permalink":"/posts/linux-network-debug-dns-routes-mtu/","section":"Технические посты","summary":"","title":"Если Linux-сервер странно теряет сеть: DNS, маршруты и MTU","type":"posts"},{"content":"","date":"пт, 3 апреля 2026","externalUrl":null,"permalink":"/tags/%D1%81%D0%B5%D1%82%D1%8C/","section":"Tags","summary":"","title":"Сеть","type":"tags"},{"content":"","date":"сб, 24 января 2026","externalUrl":null,"permalink":"/tags/wsl/","section":"Tags","summary":"","title":"Wsl","type":"tags"},{"content":" Суть Сегодня выяснил, что у меня в системе был жёсткий бардак с сетью.\nВ списке сетевых устройств обнаружились:\nBluetooth-адаптер, просто потому что его кто-то когда-то создал; несколько адаптеров от старых установок OpenVPN; зависшие мосты от старых версий VMware; прочие сетевые артефакты, которые явно не помогали жить спокойно. Из-за всего этого я не мог нормально завести режим mirrored в WSL, чтобы трафик из WSL тоже ходил через VPN: и для корпоративных ресурсов, и для сервисов вроде Gemini, OpenAI и Claude.\nWSL либо отказывался запускаться в режиме mirrored или virtioproxy, либо сваливался в nat, либо в худшем случае уходил в none, оставляя виртуалку вообще без сети.\nЧто именно мешало WSL жить — я так и не выяснил. Конкретный затык найти не удалось.\nСимптомы У меня это выглядело примерно так:\nWSL не запускался с networkingMode=mirrored; virtioproxy тоже не помогал; вместо нужного режима WSL мог откатываться в nat; иногда сеть внутри WSL пропадала полностью; VPN на хосте работал, но WSL не ходил через него так, как хотелось; в системе было много старых и подозрительных сетевых адаптеров. Короче, классика: ничего не понятно, но очень интересно.\nВажное предупреждение Этот способ радикальный.\nКоманда netcfg -d удаляет и пересоздаёт сетевые устройства Windows. После неё могут сброситься или пересоздаться:\nVPN-адаптеры; виртуальные адаптеры VMware/VirtualBox/Hyper-V; мосты; настройки некоторых сетевых клиентов; нестандартные сетевые конфигурации. Перед выполнением лучше:\nсохранить важные VPN-конфиги; убедиться, что есть локальный доступ к машине; не делать это посреди рабочего созвона; понимать, как вернуть интернет, если что-то пойдёт не так. Мне помогло. Но это не первая команда, которую стоит запускать при любой сетевой проблеме.\nЧто помогло Я до последнего пытался решить проблему без радикальных шагов, потому что остаться совсем без интернета — удовольствие сомнительное.\nНо в итоге помог такой алгоритм.\n1. Закрыть софт, который зависит от сети Сначала закрываем всё, что может плохо пережить резкое исчезновение сети:\nVPN-клиенты; браузеры; мессенджеры; TeamTalk (вряд ли вы его используете); SSH-сессии; всё, что активно использует сеть. WSL тоже останавливаем:\nwsl --shutdown 2. Сбросить сетевые адаптеры Открываем PowerShell или Windows Terminal от имени администратора и выполняем:\nnetcfg -d Команда может зависнуть секунд на 30 или больше, особенно если в системе много проблемных сетевых устройств.\nЭто нормально. Паниковать сразу не надо.\n3. Сбросить сетевой стек Windows В той же консоли администратора выполняем:\nnetsh winsock reset Затем:\nnetsh int ip reset После этого лучше не пытаться \u0026ldquo;ну сейчас ещё чуть-чуть проверю\u0026rdquo;, а сразу идти в перезагрузку.\n4. Перезагрузить Windows Перезагружаем систему:\nshutdown /r /t 0 Можно, конечно, через меню \u0026ldquo;Пуск\u0026rdquo;. Но консоль-то уже открыта, да и не по-админски это - мышкой тыкать.\nЧто произошло после ребута После перезагрузки всё завелось как часы:\nOpenVPN при необходимости пересоздал себе новый адаптер; sing-box тоже создаёт своё устройство при старте службы; WSL корректно настроился автоматически при первом запуске оболочки; VMware я на тот момент ещё не проверял, потому что он был не нужен. Если VMware после такого сброса сломается, скорее всего, поможет repair install или переустановка сетевых компонентов VMware.\nМой конфиг WSL Файл:\n%UserProfile%\\.wslconfig Содержимое:\n[wsl2] networkingMode=mirrored dnsTunneling=true firewall=true autoProxy=false После изменения .wslconfig не забудь остановить WSL:\nwsl --shutdown И запустить нужный дистрибутив заново.\nКак это ведёт себя у меня В правила sing-box для разделения трафика у меня добавлено проксирование домена:\nvpn.ifconfig.me В итоге внутри WSL получается такое поведение:\ncurl ifconfig.me возвращает мой реальный IP.\nА:\ncurl vpn.ifconfig.me возвращает IP VPN-сервера, к которому подключается sing-box.\nТо есть WSL в режиме mirrored нормально живёт вместе с VPN-маршрутизацией на хосте.\nДоступ к сервисам, которые ограничивают пользователей из России, у меня после этого работает и из Windows, и из WSL.\nВажный нюанс про sing-box У меня sing-box работает как чистое ядро, завёрнутое в WinSW для запуска в качестве службы Windows.\nЯ не использую графические клиенты вроде:\nThrone; Hiddify; Clash; других оболочек поверх proxy-core. Будет ли всё это вести себя так же с ними — не знаю. Там могут быть свои сетевые адаптеры, TUN-режимы, firewall-правила и прочая радость.\nКогда стоит пробовать этот способ Я бы рассматривал полный сброс сети, если:\nWSL не заводится в mirrored; WSL откатывается в nat или none; в системе накопились старые VPN/VMware/виртуальные адаптеры; обычные настройки .wslconfig не помогают; ты уже готов к тому, что часть сетевых компонентов придётся пересоздать. Короткий чеклист Закрыть VPN, браузеры, мессенджеры и другой сетевой софт.\nОстановить WSL:\nwsl --shutdown Открыть PowerShell от имени администратора.\nВыполнить:\nnetcfg -d Выполнить:\nnetsh winsock reset netsh int ip reset Перезагрузить Windows.\nПроверить .wslconfig.\nЗапустить WSL.\nПроверить сеть:\ncurl ifconfig.me Проверить VPN-маршрутизацию, если она настроена отдельно:\ncurl vpn.ifconfig.me Итог Иногда проблема не в одном конкретном флажке WSL, а в том, что Windows за годы накопила кладбище сетевых адаптеров, мостов и VPN-хвостов.\nВ моём случае аккуратные попытки не помогли, а полный сброс сетевых адаптеров и стека Windows внезапно оказался самым коротким путём к рабочему mirrored-режиму WSL.\nСпособ грубоватый, но рабочий. Главное — понимать, что именно он может снести по дороге.\n","date":"сб, 24 января 2026","externalUrl":null,"permalink":"/posts/wsl-mirrored-network-reset-windows/","section":"Технические посты","summary":"","title":"Как я починил WSL Mirrored Networking через полный сброс сети Windows","type":"posts"},{"content":"","date":"сб, 8 ноября 2025","externalUrl":null,"permalink":"/tags/cpp/","section":"Tags","summary":"","title":"Cpp","type":"tags"},{"content":" Суть Когда-то я долгое время писал на Swift, а в этом языке достаточно много сахара, который упрощает код и прикрывает программисту пятую точку.\nЧего только стоит guard, который позволяет проверять условия, не плодя лишние уровни вложенности и не заставляя морочиться с областями видимости.\nНо сейчас не о guard.\nБыл в Swift ещё один приятный механизм - defer.\nДа, это примерно та же идея, которая многим знакома по Go: можно отложить выполнение куска кода до момента, когда текущая функция завершится.\nКак это выглядит в Go Например, в Go часто пишут так:\nfunc main() { file, _ := os.Open(\u0026#34;myfile.txt\u0026#34;) defer file.Close() // Do something with opened file } То есть мы открываем файл, сразу говорим, что его нужно закрыть при выходе из функции, а дальше спокойно работаем с ним.\nФайл закроется независимо от того, как мы выйдем из функции: обычным путём или через ошибку. Насколько я помню, при panic deferred-вызовы тоже выполняются, если выполнение до них дошло.\nА что в C++ В C++ ключевого слова defer нет.\nЗато есть постоянная потребность освобождать ресурсы:\nзакрывать файлы; закрывать ключи реестра; освобождать дескрипторы; откатывать временные изменения; возвращать состояние обратно. И есть принцип RAII: Resource Acquisition Is Initialization.\nЕсли по-человечески: ресурс привязывается к объекту, а когда объект выходит из области видимости, вызывается его деструктор. В деструкторе и выполняется очистка.\nПосле нескольких дней работы с Windows API, где постоянно нужно закрывать то файлы, то ключи реестра, то ещё что-нибудь, я написал маленький кусочек кода, который теперь радостно таскаю из проекта в проект.\ndefer.hpp У меня это лежит в файле defer.hpp.\n#pragma once #include \u0026lt;concepts\u0026gt; #include \u0026lt;functional\u0026gt; #include \u0026lt;utility\u0026gt; template \u0026lt;std::invocable Func\u0026gt; class ScopeGuard { public: explicit ScopeGuard(Func func) noexcept : m_function(std::move(func)) {} ~ScopeGuard() noexcept { m_function(); } ScopeGuard(const ScopeGuard\u0026amp;) = delete; ScopeGuard\u0026amp; operator=(const ScopeGuard\u0026amp;) = delete; ScopeGuard(ScopeGuard\u0026amp;\u0026amp;) = delete; ScopeGuard\u0026amp; operator=(ScopeGuard\u0026amp;\u0026amp;) = delete; private: Func m_function; }; #define DEFER_CONCAT_IMPL_(x, y) x##y #define DEFER_CONCAT_(x, y) DEFER_CONCAT_IMPL_(x, y) #define defer(code) \\ auto DEFER_CONCAT_(defer_object_, __LINE__) = ScopeGuard([\u0026amp;]() noexcept { code; }) Использование Например, открываем ключ реестра через Windows API:\n#include \u0026lt;optional\u0026gt; #include \u0026lt;string\u0026gt; #include \u0026lt;windows.h\u0026gt; #include \u0026#34;defer.hpp\u0026#34; // Some code before HKEY hKey; if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, regPath.c_str(), 0, KEY_READ, \u0026amp;hKey ) != ERROR_SUCCESS) { return std::nullopt; } defer(RegCloseKey(hKey)); // Some code after Если ключ реестра был открыт успешно, то что бы ни произошло ниже, он будет закрыт при выходе из области видимости.\nМожно вернуть результат:\nreturn value; Можно выбросить исключение:\nthrow std::runtime_error(\u0026#34;something went wrong\u0026#34;); Всё равно вызовется деструктор объекта ScopeGuard, созданного макросом defer, а он вызовет переданный код:\nRegCloseKey(hKey); Почему это работает Макрос:\ndefer(RegCloseKey(hKey)); разворачивается примерно в такое:\nauto defer_object_42 = ScopeGuard([\u0026amp;]() noexcept { RegCloseKey(hKey); }); Число в имени берётся из __LINE__, чтобы на разных строках можно было писать несколько defer.\nКогда выполнение выходит из текущей области видимости, объект defer_object_42 уничтожается. При уничтожении вызывается деструктор, а в деструкторе вызывается лямбда.\nВот и весь фокус.\nНесколько defer подряд Можно написать несколько отложенных действий:\ndefer(CleanupFirst()); defer(CleanupSecond()); Важный момент: порядок выполнения будет соответствовать обычному порядку уничтожения локальных объектов C++ - в обратном порядке их создания.\nТо есть сначала выполнится CleanupSecond(), потом CleanupFirst().\nЭто похоже на стек и близко к поведению defer в Go.\nВажная оговорка про исключения Деструктор ScopeGuard объявлен как noexcept.\nЭто сделано намеренно: бросать исключения из деструктора - плохая идея, особенно если уже идёт раскрутка стека из-за другого исключения.\nПоэтому код внутри defer(...) тоже должен быть безопасным и не бросать исключения.\nХорошие кандидаты:\ndefer(RegCloseKey(hKey)); defer(CloseHandle(handle)); defer(DeleteFileW(tempFilePath.c_str())); Плохой кандидат:\ndefer(SomeFunctionThatCanThrow()); Если функция теоретически может бросить исключение, лучше обработать его внутри:\ndefer( try { SomeFunctionThatCanThrow(); } catch (...) { // log if needed } ); Хотя если таких случаев много, возможно, нужен уже не defer, а нормальная RAII-обёртка.\nКогда лучше не использовать такой defer Этот подход удобен для небольших участков кода, особенно когда работаешь с C API или WinAPI.\nНо если ресурс используется часто, лучше написать отдельную RAII-обёртку.\nНапример, для HKEY можно сделать класс, который сам вызывает RegCloseKey в деструкторе. Для HANDLE - обёртку вокруг CloseHandle.\nТо есть:\ndefer(RegCloseKey(hKey)); хорошо для быстрого локального решения.\nА для постоянного использования в проекте лучше что-то вроде:\nclass RegistryKey { public: ~RegistryKey() { if (m_key) { RegCloseKey(m_key); } } private: HKEY m_key = nullptr; }; Чем это отличается от настоящего defer Понятно, что это не полностью то же самое, что встроенный defer в Swift или Go.\nОтличия есть:\nэто не ключевое слово языка, а макрос; работает через локальный объект и деструктор; область действия - текущий C++ scope, а не обязательно вся функция; нужно следить, чтобы код очистки не бросал исключения; при очень сложном коде макрос может ухудшать читаемость. Но базовую идею конструкция реализует: написал очистку рядом с захватом ресурса и больше не держишь в голове все возможные return, throw и ветки выхода.\nИтог В C++ нет встроенного defer, но есть RAII. А RAII позволяет довольно просто собрать маленький ScopeGuard.\nДля работы с WinAPI и другим C-style API такая штука часто оказывается удобной: открыл ресурс, сразу написал, как его закрыть, и пошёл дальше.\nНе серебряная пуля, не замена нормальным RAII-классам, но как маленький инструмент в defer.hpp - вполне рабочая вещь.\nUpd сильно позже Конкретно мою задачу можно было решить использованием WIL, в которой уже всё сделано до меня, причём более безопасно и профессионально. Но тогда я о существовании этой библиотеки не знал, а сделать надо было вот прямо сейчас и на коленке.\n","date":"сб, 8 ноября 2025","externalUrl":null,"permalink":"/posts/cpp-scopeguard-defer-raii/","section":"Технические посты","summary":"","title":"defer в C++ через RAII и ScopeGuard","type":"posts"},{"content":"","date":"сб, 8 ноября 2025","externalUrl":null,"permalink":"/tags/%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5/","section":"Tags","summary":"","title":"Программирование","type":"tags"},{"content":"Speak Instead of Me - небольшая программа, которая позволяет \u0026ldquo;разговаривать\u0026rdquo; любым SAPI-синтезатором речи в выбранное звуковое устройство.\nОна полезна, если во время онлайн-общения хочется выключить микрофон, но не хочется заставлять собеседников читать текстовые сообщения в канале.\nДля кого это Программа может пригодиться:\nнезрячим пользователям; людям, которым неудобно или затруднительно говорить голосом; тем, кто хочет отвечать голосом синтезатора речи в голосовом чате; тем, кто тестирует сценарии с разными голосами и аудиоустройствами. Как это работает Программа использует SAPI-голоса, установленные в Windows.\nПользователь выбирает синтезатор речи и звуковое устройство, после чего текст можно озвучивать через выбранный голос и направлять в нужный аудиовыход.\nОграничения С некоторыми SAPI-синтезаторами есть проблемы.\nВ частности, у меня нестабильно работают:\nNVDA SAPI; венгерский Profivox. Возможные симптомы:\nсинтезатор молчит; речь выводится на основное аудиоустройство и игнорирует настройки программы; программа падает. Исправить это полностью на моей стороне, скорее всего, нельзя: часть поведения зависит от конкретного SAPI-движка.\nВ будущем я планирую отфильтровывать из списка те синтезаторы, которые гарантированно не работают с программой.\nИсходный код и загрузка Исходный код и релизы доступны в GitHub-репозитории:\ngithub.com/cyrmax/sim\nПланы В будущем я попробую перенести или продублировать репозиторий на GitVerse, если там будут доступны виртуальные машины для автоматической сборки релизов.\n","date":"ср, 16 июля 2025","externalUrl":null,"permalink":"/projects/speak-instead-of-me/","section":"Проекты","summary":"","title":"Speak Instead of Me","type":"projects"},{"content":"","date":"ср, 16 июля 2025","externalUrl":null,"permalink":"/tags/%D1%81%D0%B4%D0%B5%D0%BB%D0%B0%D0%BB-%D1%81%D0%B0%D0%BC/","section":"Tags","summary":"","title":"Сделал-Сам","type":"tags"},{"content":"","date":"ср, 16 июля 2025","externalUrl":null,"permalink":"/tags/%D1%81%D0%BE%D1%84%D1%82/","section":"Tags","summary":"","title":"Софт","type":"tags"},{"content":"","date":"пт, 16 мая 2025","externalUrl":null,"permalink":"/tags/prometheus/","section":"Tags","summary":"","title":"Prometheus","type":"tags"},{"content":"Как Ростелеком мне инфраструктуру ломал.\nПару дней назад в мой Telegram-канал с уведомлениями от Prometheus начали сыпаться постоянные предупреждения: один из серверов якобы прилёг поспать и не отвечает.\nПроверка по SSH показала, что сервер жив. Более того, часть сайтов на этой же машине спокойно открывалась снаружи. Например:\nrhvoice.hu; storage.cyrmax.ru. То есть сервер не умер, сеть не отвалилась полностью, сайты доступны, но Prometheus из домашней сети почему-то стабильно считает, что всё плохо.\nА дальше началось любимое админское развлечение: \u0026ldquo;оно вроде работает, но не у меня, не всегда и не так\u0026rdquo;.\nПервая версия: nftables Первая мысль была простая: что-то сломалось в nftables.\nФаервол был временно выключен.\nНе помогло.\nПроблема сохранилась.\nВторая версия: node_exporter Вторая идея: может быть, сломался сам node_exporter. Например, после обновления.\nЯ начал проверять подключение к целевому серверу с других машин.\nРезультат:\nподключения с других серверов проходят успешно; node_exporter отвечает; Prometheus такой же версии на другой машине с таким же конфигом работает; проблема воспроизводится только из домашней сети. То есть сам сервер, node_exporter и конфиг Prometheus, скорее всего, были ни при чём.\nИз домашней сети подключение как будто есть, но рвётся после некоторого количества прошедших пакетов.\nНа этом моя компетенция сказала \u0026ldquo;ну всё, брат, дальше сам\u0026rdquo;, и я пошёл за помощью к Gemini.\nЧто проверяли дальше С инструкциями от искусственного интеллекта было сделано следующее.\n1. Явные правила фаервола Были добавлены конкретные правила для фаерволов на обоих концах.\nНе помогло.\n2. openssl s_client без полезной нагрузки Подключение проверялось через:\nopenssl s_client Без полезной нагрузки подключение устанавливалось успешно, ошибок не было.\n3. openssl s_client с HTTP-запросом Потом через openssl s_client была отправлена полезная нагрузка, повторяющая структуру GET-запроса, который делает Prometheus при опросе метрик.\nРезультат: подключение зависает ровно там же, где и при проверке через curl.\n4. Проверка на MTUD Blackhole Была проверка на MTUD Blackhole.\nРезультат отрицательный: проблем с MTU по пути следования пакетов не видно.\nСерверы и промежуточные узлы корректно обмениваются информацией о максимальном MTU, пакеты не застревают из-за фрагментации.\n5. tcpdump на обоих концах Самое интересное показал tcpdump.\nСниффинг трафика на клиентской и серверной стороне дал наводящие на мысли результаты.\nЧто показал tcpdump Сервер и клиент:\nобмениваются пакетами; корректно устанавливают TCP-соединение; успешно проходят TLS handshake; доходят до момента обмена первыми порциями полезных данных. А потом подключение зависает.\nПричём в каждой попытке зависание происходит на одном конкретном пакете одной конкретной длины - 48 байт зашифрованных данных.\nВот это уже стало выглядеть совсем интересно.\nМагический заголовок на 50 символов Попытка подмешать в запрос левый HTTP-заголовок со случайной строкой из 50 символов внезапно пробила блокаду.\nПосле этого и через curl, и через openssl s_client обмен данными проходил успешно.\nТо есть чуть-чуть изменили внешний вид запроса - и проблема исчезла.\nНо попытка симулировать похожую последовательность пакетов кастомными средствами снова приводила к зависанию.\nНормального объяснения у меня этому нет.\nНа уровне предположения это похоже на какую-то фильтрацию, DPI, баг в промежуточном сетевом оборудовании или очень странную реакцию на конкретный паттерн трафика. Доказать конкретный механизм я не могу.\nМожет быть, роутер? Верилось, конечно, слабо, но вдруг это мой роутер шалит.\nСервер был подключён к интернету напрямую, без роутера. Я вооружился клавиатурой и наушниками, сел перед комодом, на котором всё это добро установлено, и проверил ещё раз.\nНет.\nПроблема не в роутере.\nПри прямом подключении поведение полностью идентичное.\nЧто я не проверил Единственное, чего я не проверил, это такой же паттерн из других домашних сетей.\nНапример:\nу других провайдеров; у других пользователей Ростелекома; из других регионов; с другого домашнего оборудования. Так что я не могу честно сказать, что проблема стопроцентно именно в Ростелекоме как в единственной возможной точке отказа.\nНо с моей стороны картина выглядит так: из других сетей всё работает, из домашней сети Ростелекома ломается, роутер исключён.\nСпасибо за стабильность В общем, спасибо Ростелекому за стабильность, надёжность и прозрачность.\nКонечно же, я обращусь в поддержку.\nНо давайте будем честными: все мы понимаем, чего примерно стоит ждать. Если по поводу полного отключения интернета иногда сложно достучаться, то что уж говорить про разрывы при доступе конкретным протоколом к конкретному серверу при конкретном размере конкретного пакета.\nСамое смешное Самое смешное то, что реальные подключения по протоколам из трёх букв работают безотказно.\nИначе как бы я пользовал Gemini, ChatGPT и другие сервисы, официально ушедшие из России и блокирующие российских пользователей.\nА вот Prometheus, мирно собирающий метрики с моего сервера, внезапно оказался слишком подозрительным.\nТем временем Пока я писал этот пост, Prometheus 8 раз прислал уведомление о том, что сервер лежит.\nПрикол в том, что примерно в это же время успешно выполнился GitHub Action, который обновил на этом самом сервере некоторые файлы для сайта и прислал в тот же канал сообщение об успехе.\nТо есть сервер \u0026ldquo;лежит\u0026rdquo; только для моего домашнего Prometheus. Для всего остального мира он вполне бодр и трудоспособен.\nPCAP-файлы Я сохранил два pcap-файла:\nодин с перспективы домашнего сервера, то есть Prometheus client; второй с перспективы удалённого сервера, где работает prometheus_node_exporter. Архив можно скачать здесь:\nСкачать pcap-архив\nЕсли кто-то умеет читать такие штуки лучше меня и увидит там конкретную причину - буду рад узнать, что же это всё-таки было.\nИтог Что известно:\nсервер жив; node_exporter работает; фаервол на сервере не является причиной; с других серверов подключение проходит; из домашней сети Ростелекома подключение зависает; роутер исключён, потому что при прямом подключении проблема осталась; TLS handshake проходит; зависание происходит при передаче полезных данных; изменение формы HTTP-запроса может магически починить соединение. Что не доказано:\nточный механизм поломки; конкретный виновный узел; воспроизводимость из других сетей Ростелекома. Мой текущий вывод: где-то по пути от моей домашней сети до удалённого сервера происходит очень странная обработка трафика, из-за которой Prometheus не может нормально забрать метрики.\nИ да, это тот самый случай, когда сервер работает, сайты открываются, GitHub Action проходит, SSH жив, но мониторинг орёт, что всё умерло.\nЛюблю инфраструктуру.\n","date":"пт, 16 мая 2025","externalUrl":null,"permalink":"/posts/rostelecom-prometheus-network-breakage/","section":"Технические посты","summary":"","title":"Как Ростелеком мне инфраструктуру ломал","type":"posts"},{"content":"","date":"пт, 16 мая 2025","externalUrl":null,"permalink":"/tags/%D1%80%D0%BE%D1%81%D1%82%D0%B5%D0%BB%D0%B5%D0%BA%D0%BE%D0%BC/","section":"Tags","summary":"","title":"Ростелеком","type":"tags"},{"content":"","date":"вс, 13 апреля 2025","externalUrl":null,"permalink":"/tags/python/","section":"Tags","summary":"","title":"Python","type":"tags"},{"content":"","date":"вс, 13 апреля 2025","externalUrl":null,"permalink":"/tags/%D0%BE%D0%B4%D0%BD%D0%BE%D1%81%D1%82%D1%80%D0%BE%D1%87%D0%BD%D0%B8%D0%BA%D0%B8/","section":"Tags","summary":"","title":"Однострочники","type":"tags"},{"content":"Один хороший человек на днях скинул мне страшнющий однострочник на Python, а затем сотворил вот это руководство.\nПользуйтесь и наслаждайтесь\u0026hellip; Если сможете :)\n@denizsincar29, спасибо тебе!\nРуководство по созданию чудовищных, но эффектных однострочников Цель: уместить максимальное количество логики и операций в одну строку кода, жертвуя читаемостью, поддерживаемостью и безопасностью.\nНаша цель - создать код, который никто не сможет понять, кроме самого гения-автора. Да и то не факт.\nОсновные принципы Забудьте про обработку исключений: ошибки - это весело! Пусть программа взрывается. Обработать исключения в однострочнике невозможно, поэтому мы не будем даже пытаться.\nКомбинируйте всё воедино (максимально): вложенные тернарные операторы, цепочки методов, скобки внутри скобок - чем больше операций в одном выражении, тем лучше.\na if cond1 else (b if cond2 else c) Ваш лучший друг.\na or b and c Тоже полезно для создания неочевидной логики.\nЛист-компрехеншены - наше многофункциональное оружие: превратите любой цикл и фильтрацию данных в однострочный ад. Используйте вложенные лист-компрехеншены для максимальной сложности. Да и цикл в одну строку вы не напишете.\nМгновенная индексация - для краткости и скорости: создайте список/словарь на лету и сразу же получите к нему доступ по индексу/ключу.\n[...][i] и\n{...}[key] позволяют избежать временных переменных и сделать код более лаконичным. Убедитесь, что i и key вычисляются в других сложных выражениях, чтобы увеличить уровень абстракции.\nИмпортируйте не как все (для obfuscation):\n__import__(\u0026#34;module\u0026#34;).submodule.function() Это не только красиво, но и затрудняет анализ зависимостей.\nЦиклы через itertools (для бесконечной мощи): забудьте про обычные for.\n__import__(\u0026#34;itertools\u0026#34;).count() и map() с лямбдами позволяют создавать бесконечные циклы, которые могут привести к зависанию программы, если их не остановить.\nБольше тернарных операторов (для нелинейной логики): выведите тернарные операторы на новый уровень, вложив их друг в друга несколько раз. Используйте их даже там, где они не нужны, чтобы добавить немного загадочности.\nГлавное - уместить как можно больше: каждая строка должна быть наполнена операциями до предела. Используйте все доступные приемы, чтобы уменьшить количество символов и увеличить плотность логики.\nПример Пример, стремящийся к абсолютной нечитаемости:\nmonthdays = lambda year, month: [31, (29 if (((year % 4) == 0) and ((year % 100) != 0) or ((year % 400) == 0)) else 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][([\u0026#34;январь\u0026#34;, \u0026#34;февраль\u0026#34;, \u0026#34;март\u0026#34;, \u0026#34;апрель\u0026#34;, \u0026#34;май\u0026#34;, \u0026#34;июнь\u0026#34;, \u0026#34;июль\u0026#34;, \u0026#34;август\u0026#34;, \u0026#34;сентябрь\u0026#34;, \u0026#34;октябрь\u0026#34;, \u0026#34;ноябрь\u0026#34;, \u0026#34;декабрь\u0026#34;].index(month.strip().lower()))] # Возвращает количество дней в месяце для заданного года (или взрывается с ошибкой). Не пытайтесь понять, как это работает. Полезные применения и предостережения Конкурсы на самый нечитаемый код, где побеждает тот, кто умудрится сделать больше всего в одной строке. Демонстрация мнимого мастерства владения языком. Если кто-то действительно впечатлится, вам повезло. Запутывание врагов. Только если вы уверены, что они не смогут использовать ваш код против вас. Важное предупреждение Этот код предназначен только для развлечений и экспериментов!\nВы можете конечно использовать такой код на работе, если ваша цель - отомстить вашему работодателю перед увольнением.\n","date":"вс, 13 апреля 2025","externalUrl":null,"permalink":"/posts/python-monstrous-one-liners/","section":"Технические посты","summary":"","title":"Руководство по созданию чудовищных однострочников на Python","type":"posts"},{"content":"","date":"вс, 13 апреля 2025","externalUrl":null,"permalink":"/tags/%D1%8D%D0%B7%D0%BE%D1%82%D0%B5%D1%80%D0%B8%D0%BA%D0%B0/","section":"Tags","summary":"","title":"Эзотерика","type":"tags"},{"content":"","date":"вт, 7 января 2025","externalUrl":null,"permalink":"/tags/sed/","section":"Tags","summary":"","title":"Sed","type":"tags"},{"content":"","date":"вт, 7 января 2025","externalUrl":null,"permalink":"/tags/shell/","section":"Tags","summary":"","title":"Shell","type":"tags"},{"content":"Все знают команду tree для вывода дерева директорий и файлов рекурсивно и в красивом формате.\nВ красивом для зрячих, но неудобном для нас. Неудобном в плане чтения отступов.\nА сегодня я узнал, что можно вот так:\ntree | sed \u0026#39;s/[├─│└]/ /g\u0026#39; Все украшательства заменяются на обычные пробелы, что позволяет скринридеру корректно обработать отступы и проговорить либо пропищать их сигналом.\nА разве tree не умеет так сам? Я проверил: у tree есть ключ -i, который отключает линии отступов, и есть опции для другого стиля символов, например -A, -S и --charset.\nНо это не совсем то же самое.\ntree -i убирает линии отступов. А мне хочется сохранить саму идею дерева, только заменить псевдографику на обычные пробелы, чтобы скринридеру было легче читать структуру.\nПоэтому вариант с sed пока выглядит самым простым и рабочим.\nОбёртка stree Можно создать скрипт вот с таким содержимым:\n#!/bin/sh tree \u0026#34;$@\u0026#34; | sed \u0026#39;s/[├─│└]/ /g\u0026#39; И положить его, например, в:\n/usr/local/bin/stree После этого сделать исполняемым:\nsudo chmod +x /usr/local/bin/stree И вызывать вместо tree команду stree с любыми желаемыми параметрами от tree.\nНапример:\nstree -L 2 или:\nstree -a --dirsfirst Всё будет работать, ведь мы передаём утилите весь набор параметров, делая наш скрипт просто обёрткой над оригинальной программой.\nВажная мелочь В скрипте лучше писать именно так:\ntree \u0026#34;$@\u0026#34; А не так:\ntree $@ Первый вариант корректнее обрабатывает аргументы с пробелами и другими весёлыми символами в именах файлов и каталогов.\nВместо заключения Если вы незрячий, пользуйтесь и радуйтесь.\nНе великое инженерное решение, конечно, но свою задачу выполняет идеально.\n","date":"вт, 7 января 2025","externalUrl":null,"permalink":"/posts/screenreader-friendly-tree-output/","section":"Технические посты","summary":"","title":"tree, но удобнее для скринридера","type":"posts"},{"content":"","date":"вт, 7 января 2025","externalUrl":null,"permalink":"/tags/%D0%B4%D0%BE%D1%81%D1%82%D1%83%D0%BF%D0%BD%D0%BE%D1%81%D1%82%D1%8C/","section":"Tags","summary":"","title":"Доступность","type":"tags"},{"content":"","date":"вт, 7 января 2025","externalUrl":null,"permalink":"/tags/%D0%B7%D0%B0%D0%BA%D0%BB%D0%B8%D0%BD%D0%B0%D0%BD%D0%B8%D0%B5/","section":"Tags","summary":"","title":"Заклинание","type":"tags"},{"content":"","date":"вт, 22 октября 2024","externalUrl":null,"permalink":"/tags/debian/","section":"Tags","summary":"","title":"Debian","type":"tags"},{"content":"","date":"вт, 22 октября 2024","externalUrl":null,"permalink":"/tags/imagick/","section":"Tags","summary":"","title":"Imagick","type":"tags"},{"content":"","date":"вт, 22 октября 2024","externalUrl":null,"permalink":"/tags/nextcloud/","section":"Tags","summary":"","title":"Nextcloud","type":"tags"},{"content":"Ставим Nextcloud на Debian 12, устанавливаем php8.3-imagick, а в настройках всё равно видим сообщение о том, что модуль Imagick не установлен или работает не полностью.\nРешается неочевидно: нужно установить ещё один пакет:\nsudo apt install libmagickcore-6.q16-6-extra После этого обновляем страницу проверки в настройках Nextcloud, тем самым перезапуская все тесты работоспособности, и видим, что ошибка исчезла.\nПочему так Судя по похожим случаям, проблема обычно не в том, что PHP-модуль Imagick вообще отсутствует, а в том, что установленной связке ImageMagick/Imagick не хватает поддержки некоторых форматов. В Nextcloud это часто всплывает как предупреждение про Imagick или SVG support. Пакет libmagickcore-6.q16-6-extra как раз добавляет дополнительные компоненты ImageMagick, из-за отсутствия которых такие проверки могут ругаться.\nПроверка После установки можно проверить, что пакет стоит:\ndpkg -l | grep libmagickcore-6.q16-6-extra И что PHP видит Imagick:\nphp -m | grep -i imagick Если используется PHP-FPM, на всякий случай можно перезапустить его:\nsudo systemctl restart php8.3-fpm Если Apache с mod_php:\nsudo systemctl restart apache2 Актуальность Проверено на такой связке:\nNextcloud 29 Debian 12 PHP 8.3 Для более новых версий Debian пакет может называться иначе. Например, в обсуждении Docker-образа Nextcloud для более свежей базы уже всплывал переход от libmagickcore-6.q16-6-extra к libmagickcore-7.q16-10-extra. Поэтому для Debian 12 команда выше актуальна, а для Debian 13 и новее название лучше проверить через apt search.\nИтог Если после установки php8.3-imagick Nextcloud всё ещё ругается на Imagick, попробуйте:\nsudo apt install libmagickcore-6.q16-6-extra Не великое шаманство, но достаточно неочевидное, чтобы записать.\n","date":"вт, 22 октября 2024","externalUrl":null,"permalink":"/posts/nextcloud-debian12-imagick-extra-package/","section":"Технические посты","summary":"","title":"Nextcloud на Debian 12: Imagick установлен, а предупреждение не исчезает","type":"posts"},{"content":"","date":"вт, 22 октября 2024","externalUrl":null,"permalink":"/tags/php/","section":"Tags","summary":"","title":"Php","type":"tags"},{"content":"Сегодня мне пришлось практически взламывать свою собственную учётную запись в Windows.\nДело в том, что некоторые крупные обновления винды немножечко так ломают ваш пароль, и даже если вы не доверяли своей памяти и записали пароль на бумажку, это вам не поможет.\nВинда начинает считать любой пароль неверным, как будто бы внутри системы что-то сломалось.\nКак понимаете, сменить пароль в таком случае штатными средствами не получается.\nОпция \u0026ldquo;забыл пароль\u0026rdquo; почему-то у меня вообще не отображалась, и мне пришлось искать другие способы решения проблемы.\nБлаго хоть в саму систему я мог войти без проблем, ибо и пинкод, и отпечаток пальца продолжали работать как ни в чём не бывало.\nВ чём засада В панели управления Windows 11 опции смены пароля больше нет. Вернее, она есть, но только у аккаунта локального администратора, который включать я не советую, ибо риск безопасности.\nВ новомодных параметрах системы изменить пароль не получится без ввода старого, а его-то винда и не принимает.\nПолучается прекрасная ситуация:\nпароль вроде бы есть; Windows его не принимает; PIN работает; отпечаток пальца работает; сменить пароль через параметры нельзя; \u0026ldquo;забыл пароль\u0026rdquo; где-то потерялся по дороге. Люблю современные операционные системы.\nВажное уточнение Это заклинание имеет смысл, если вы уже вошли в систему и можете открыть консоль с правами администратора.\nЭто не инструкция по взлому чужих машин и не способ получить доступ в обход защиты.\nЕщё один нюанс: net user работает с локальными учётными записями Windows. Для аккаунтов Microsoft всё может быть иначе, так что воспринимайте этот текст как описание моего случая, а не как универсальный рецепт на все варианты входа.\nРешение Единственный и неожиданно простой способ без сторонних подозрительных утилит с левых сайтов - просто ввести одну команду в консоли.\nОткрываем командную строку с правами администратора.\nДля этого нажимаем:\nWin + R Пишем:\ncmd или:\npowershell или:\nwt если у вас Windows 11 и установлен Windows Terminal.\nДальше вместо Enter нажимаем:\nCtrl + Shift + Enter Если у вас не выключен контроль учётных записей, то вы получите стандартное защищённое окно с подтверждением. Нажимаем в нём \u0026ldquo;Да\u0026rdquo; и попадаем в командную строку с правами администратора.\nСначала можно посмотреть имя пользователя На всякий случай можно выполнить:\nnet user Команда покажет список пользователей на компьютере.\nЧаще всего имя пользователя совпадает с именем папки в:\nC:\\Users\\ Например, в моём случае это:\nC:\\Users\\cyrmax Значит имя пользователя - cyrmax.\nСмена пароля Дальше всё ограничивается одной простой командой:\nnet user username password Где:\nusername - ваше имя пользователя; password - новый пароль. Например:\nnet user cyrmax NewStrongPassword123 Команда выполняется быстро, без подтверждений и даже ничего не переспрашивает по десять раз.\nЛучше вводить пароль скрыто Есть более аккуратный вариант:\nnet user username * Например:\nnet user cyrmax * После этого Windows попросит ввести новый пароль в консоли.\nЭтот вариант лучше тем, что пароль не светится прямо в командной строке и не остаётся видимым на экране.\nПолезно, если вам смотрят в монитор, вы записываете экран или просто печётесь о своей цифровой безопасности.\nПосле смены пароля Единственное, к чему нужно быть готовым: на следующем входе в систему ничто кроме пароля работать не будет.\nЧтобы снова использовать PIN-код, сканер лица или отпечаток пальца, нужно будет один раз войти в Windows с новым паролем.\nПосле этого Windows Hello обычно снова начинает нормально жить.\nВ заключение Пользуйтесь и не попадайте в ситуации, похожие на мою.\nА если попали - не бегите сразу качать странные \u0026ldquo;password recovery ultimate super tool 3000\u0026rdquo; с сайтов, где одна кнопка \u0026ldquo;скачать\u0026rdquo; и пять троянов вокруг.\nИногда вся магия уже лежит в Windows и называется:\nnet user ","date":"ср, 14 августа 2024","externalUrl":null,"permalink":"/posts/windows-reset-broken-local-password-net-user/","section":"Технические посты","summary":"","title":"Windows сломала пароль: сброс через net user","type":"posts"},{"content":"","date":"ср, 10 июля 2024","externalUrl":null,"permalink":"/tags/markov-chain/","section":"Tags","summary":"","title":"Markov-Chain","type":"tags"},{"content":"","date":"ср, 10 июля 2024","externalUrl":null,"permalink":"/tags/%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B/","section":"Tags","summary":"","title":"Алгоритмы","type":"tags"},{"content":"","date":"ср, 10 июля 2024","externalUrl":null,"permalink":"/tags/%D0%B3%D0%B5%D0%BD%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D1%8F/","section":"Tags","summary":"","title":"Генерация","type":"tags"},{"content":"","date":"ср, 10 июля 2024","externalUrl":null,"permalink":"/tags/%D0%B7%D0%B0%D0%BD%D1%8F%D1%82%D0%BD%D0%BE/","section":"Tags","summary":"","title":"Занятно","type":"tags"},{"content":"Мне всегда было интересно, как работают многочисленные генераторы фентезийных имён, никнеймов и прочих псевдослучайных слов в каком-либо стиле, звучании или просто пытающихся быть похожими на слова из какого-то конкретного языка.\nИскусственный интеллект с этой задачей справляется неплохо, но вряд ли такие языковые модели существовали в 2010 году. И вряд ли какая-нибудь небольшая браузерная игра каждый раз дёргает большую модель ради каждого нового пользователя.\nЯ гуглил, ничего толком не нашёл, и поэтому придумал, как приспособить цепь Маркова для подобной задачи.\nИдея Берём кучу текстов на нужном нам языке.\nПричём чем больше, тем лучше. Хотя наверняка есть предел, после которого добавление новых текстов уже не будет сильно улучшать результат.\nДальше пишем небольшой манифест для выбранного языка:\nчто считается буквой; что буквой не считается; с каких букв может начинаться слово; с каких букв слово начинаться не должно. А дальше просто идём по тексту и собираем статистику.\nЧто анализируем Первое: после каждой встреченной буквы смотрим, с какой вероятностью встречается любая другая буква или конец слова.\nВторое, опционально: считаем среднюю длину слова в языке.\nМожно упороться и ещё прикрутить анализ длины слова в зависимости от его состава, но частично эту задачу уже покрывает анализ вероятностей. Конец слова ведь тоже учитывается как возможное следующее состояние.\nНа Python такой анализ можно сделать простым использованием Counter.\nНу или можно написать свою реализацию, если хочется почувствовать себя человеком, который изобрёл collections.\nКак выглядит модель После анализа получается моделька.\nПо сути это просто словарь:\n{ \u0026#34;а\u0026#34;: { \u0026#34;н\u0026#34;: 42, \u0026#34;л\u0026#34;: 17, \u0026#34;р\u0026#34;: 9, \u0026#34;\u0026lt;END\u0026gt;\u0026#34;: 6 }, \u0026#34;н\u0026#34;: { \u0026#34;а\u0026#34;: 31, \u0026#34;о\u0026#34;: 14, \u0026#34;и\u0026#34;: 12 } } То есть для каждой буквы мы знаем, какие буквы могут идти после неё и насколько часто это происходило в обучающем наборе.\nПотом частоты можно превратить в вероятности, а можно выбирать с весами прямо по частотам.\nКак генерировать слово Дальше по этой модельке можно начать создавать вымышленные слова.\nАлгоритм примерно такой:\nВыбираем случайную стартовую букву. Смотрим, какие буквы могут следовать за ней. С учётом вероятностей выбираем следующую букву. Повторяем. Останавливаемся, если выпал конец слова или достигнута максимальная длина. Получается не настоящее слово, но что-то похожее на слово из нужного языка или стиля.\nСимвол начала слова Можно сделать чуть аккуратнее.\nПри сборе статистики добавить специальный символ начала слова, например:\n\u0026lt;START\u0026gt; И складывать в него вероятности букв, с которых начинаются слова в языке.\nТогда генератор будет стартовать не с любой буквы алфавита, а с тех букв, с которых слова реально начинаются.\nМодель будет выглядеть примерно так:\n{ \u0026#34;\u0026lt;START\u0026gt;\u0026#34;: { \u0026#34;а\u0026#34;: 15, \u0026#34;б\u0026#34;: 9, \u0026#34;м\u0026#34;: 13 }, \u0026#34;а\u0026#34;: { \u0026#34;н\u0026#34;: 42, \u0026#34;л\u0026#34;: 17, \u0026#34;\u0026lt;END\u0026gt;\u0026#34;: 6 } } Наверное, так будет достовернее.\nПочему это должно работать Цепь Маркова здесь не понимает язык.\nОна не знает смысла слов, грамматики, морфологии и всей этой прекрасной лингвистической боли.\nОна просто знает, что после одних символов чаще встречаются другие символы.\nИ этого уже достаточно, чтобы генерировать слова, которые выглядят \u0026ldquo;похожими\u0026rdquo; на исходный корпус.\nЕсли скормить модели русские слова, она будет выдавать что-то русскоподобное.\nЕсли скормить эльфийские имена из фентези, она начнёт выдавать всяких Наливалиэлей.\nЕсли скормить имена из книг по типу Властелина Колец, на выходе появятся Тророподобные и Дуриноподобные имена.\nЕсли скормить что-то совсем странное, ну\u0026hellip; вы сами виноваты.\nТак и живём Я думаю, что всякие фентезийные генераторы имён работают примерно по такой схеме.\nНаверняка им просто скармливают тысячи имён персонажей на заданную тематику, а они на основе этого создают всяких:\nМиэлаульфов; Бринготов; Айнетанаминов. Звучит достаточно фентезийно, чтобы кто-нибудь обязательно оказался древним королём, магом или хотя бы подозрительным NPC у таверны.\nЧто можно улучшить Можно анализировать не одну предыдущую букву, а две или три.\nНапример, не просто:\nа -\u0026gt; н А так:\nма -\u0026gt; р или:\nпро -\u0026gt; с Так слова будут выглядеть естественнее, но модель станет больше и требовательнее к размеру обучающего корпуса.\nЕщё можно:\nотдельно хранить стартовые сочетания; отдельно учитывать окончания; запрещать слишком длинные повторы; фильтровать слова, которые уже есть в исходном корпусе; отбрасывать слишком короткие или слишком длинные результаты; делать разные модели для разных рас, языков или сеттингов. Итог Получается довольно простая идея:\nСобрать корпус слов. Посчитать вероятности переходов между буквами. Добавить начало и конец слова. Генерировать новые слова случайным проходом по этой модели. Не нейросеть, не магия, не драконья лингвистика. Просто статистика и немного рандома.\nupd сильно позже Недавно начал по верхам изучать основы нейронных сетей, наткнулся на вот эту статью и загорелся идеей построить генератор фентезийных имён уже на полноценной нейронке (но всё ещё без LLM).\nupd2: На 4 мая 2026 года статья с Хабра по какой-то причине удалена автором.\nТак что вот вам ссылка на Web Archive, где статья доступна к прочтению.\n","date":"ср, 10 июля 2024","externalUrl":null,"permalink":"/posts/markov-chain-fantasy-name-generator/","section":"Технические посты","summary":"","title":"Как можно генерировать фентезийные имена через цепи Маркова","type":"posts"},{"content":"","date":"сб, 25 мая 2024","externalUrl":null,"permalink":"/tags/foma/","section":"Tags","summary":"","title":"Foma","type":"tags"},{"content":"","date":"сб, 25 мая 2024","externalUrl":null,"permalink":"/tags/rhvoice/","section":"Tags","summary":"","title":"Rhvoice","type":"tags"},{"content":"Давненько я ничего не писал в своём программерском блоге, но пройти мимо такого странного инфоповода просто не мог.\nДело в том, что, как ни странно, мы вместе со Звонимиром, лингвистом из Хорватии, разрабатываем венгерский языковой модуль для синтезатора RHVoice.\nДа да, русский и хорват работают над венгерским языком. Чего только в этом мире не случается.\nИ сегодня я написал по сути небольшой, но важный код, преобразующий написанные цифрами числа в их буквенное представление.\nЭто как:\n1123 = одна тысяча сто двадцать три Только для венгерского языка.\nИ именно этим кодом я хочу с вами поделиться, ибо код этот страшен и написан на таком странном эзотерическом языке под названием Foma.\nКод Добавлю переводов, ибо читатель вероятно с венгерским языком не знаком.\ndefine NZDigit 1|2|3|4|5|6|7|8|9;\rdefine Digit %0|NZDigit;\rdefine PlusOrMinus %+|%-;\rdefine Number [(PlusOrMinus) [%0|[NZDigit Digit^\u0026lt;15]] (%%|%°)];\rdefine RB (%%|%°) .#. ;\r# Цифры от \u0026#34;один\u0026#34; до \u0026#34;девять\u0026#34; прописью\rdefine Units %0:0|1:egy|2:kettő|3:három|4:négy|5:öt|6:hat|7:hét|8:nyolc|9:kilenc;\r# Числа от \u0026#34;десять\u0026#34; до \u0026#34;девятнадцать\u0026#34; прописью\rdefine Teens 1:0 [%0:tíz|1:tizenegy|2:tizenkettő|3:tizenhárom|4:tizennégy|5:tizenöt|6:tizenhat|7:tizenhét|8:tizennyolc|9:tizenkilenc];\r# Круглые десятки прописью от \u0026#34;двадцать\u0026#34; до \u0026#34;девяносто\u0026#34;\rdefine Tens %0:0|2:húsz|3:harminc|4:negyven|5:ötven|6:hatvan|7:hetven|8:nyolcvan|9:kilencven;\r# Круглые сотни прописью\rdefine Hundreds %0:0|1:egyszáz|2:kettőszáz|3:háromszáz|4:négyszáz|5:ötszáz|6:hatszáz|7:hétszáz|8:nyolcszáz|9:kilencszáz;\r# Тут думаю всё понятно, но nulla - это ноль\rdefine UpToThousand %0:nulla .P. [[(Hundreds) Teens] | [((Hundreds) Tens) Units]];\rdefine DigitsToWords UpToThousand @-\u0026gt; ,, %+ @-\u0026gt; plusz ,, %- @-\u0026gt; minusz ,, %% @-\u0026gt; százalék ,, # название символа \u0026#34;процент\u0026#34;\r%° -\u0026gt; fok ; # Название символа \u0026#34;градус\u0026#34;\rdefine InsertThousands [..] -\u0026gt; ezer || Digit _ Digit^3 RB; # ezer - тысяча\rdefine InsertMillions [..] -\u0026gt; millió || Digit _ Digit^3 ezer;\rdefine InsertBillions [..] -\u0026gt; milliárd || Digit _ Digit^3 millió;\rdefine InsertTrillions [..] -\u0026gt; billió || Digit _ Digit^3 milliárd;\rdefine RemoveSkippedParts %0 %0 %0 [ezer|millió|milliárd|trillió] -\u0026gt; 0 ;\r# Немного лингвистики, объяснение в комментарии не влезет\rdefine FixTwenty húsz @-\u0026gt; huszon || _ egy|kettő|három|négy|öt|hat|hét|nyolc|kilenc;\r# Ещё немного лингвистики, потому что \u0026#34;egyszáz\u0026#34; - это как на русском сказать \u0026#34;одинсто\u0026#34;\rdefine FixHundred egyszáz -\u0026gt; száz || .#. _ ;\r# Потому что венгры никогда не говорят \u0026#34;одна тысяча\u0026#34; - всегда только \u0026#34;тысяча\u0026#34;\rdefine FixThousand egy -\u0026gt; 0 || .#.|millió|milliárd|billió _ ezer ;\r# Финал, ради которого и страдали\rregex Number .o. InsertThousands .o. InsertMillions .o. InsertBillions .o. InsertTrillions .o. RemoveSkippedParts .o. DigitsToWords .o. FixTwenty .o. FixHundred .o. FixThousand; Что это вообще такое Foma - это как регулярные выражения, только на максималках.\nПомимо обычных регулярок тут есть:\nправила трансформации по определённым условиям; встроенные замены; композиция правил; конечные автоматы; ещё куча адских приколов, разобраться с которыми получается далеко не сразу. Но именно вот такие страшные конструкции лежат в основе любого языка в RHVoice.\nИ работает это пусть и сложно, но офигеть как быстро.\nЧто делает этот конкретный кусок Если сильно упростить, тут происходит несколько этапов.\nСначала описывается, что вообще считается числом:\ndefine NZDigit 1|2|3|4|5|6|7|8|9;\rdefine Digit %0|NZDigit;\rdefine PlusOrMinus %+|%-;\rdefine Number [(PlusOrMinus) [%0|[NZDigit Digit^\u0026lt;15]] (%%|%°)]; То есть число может быть:\nположительным; отрицательным; с процентом; с градусом; с ограничением по длине. Потом задаются базовые слова для единиц, десятков и сотен:\ndefine Units %0:0|1:egy|2:kettő|3:három|4:négy|5:öt|6:hat|7:hét|8:nyolc|9:kilenc; define Tens %0:0|2:húsz|3:harminc|4:negyven|5:ötven|6:hatvan|7:hetven|8:nyolcvan|9:kilencven; define Hundreds %0:0|1:egyszáz|2:kettőszáz|3:háromszáz|4:négyszáz|5:ötszáz|6:hatszáz|7:hétszáz|8:nyolcszáz|9:kilencszáz; Дальше начинается вставка тысяч, миллионов, миллиардов и прочих радостей:\ndefine InsertThousands [..] -\u0026gt; ezer || Digit _ Digit^3 RB; define InsertMillions [..] -\u0026gt; millió || Digit _ Digit^3 ezer; И в конце вся эта красота собирается в один конвейер:\nregex Number .o. InsertThousands .o. InsertMillions .o. InsertBillions .o. InsertTrillions .o. RemoveSkippedParts .o. DigitsToWords .o. FixTwenty .o. FixHundred .o. FixThousand; Выглядит страшно. Работает быстро. Добро пожаловать в мир правил для синтеза речи.\nМаленькие правки после основной конвертации Отдельно тут есть фиксы для случаев, где простая механическая сборка слова даёт не совсем правильную форму.\nНапример:\ndefine FixTwenty húsz @-\u0026gt; huszon || _ egy|kettő|három|négy|öt|hat|hét|nyolc|kilenc; То есть húsz (двадцать) превращается в huszon, если это, например, 21, 22, 23, а не просто 20.\nЕщё пример:\ndefine FixHundred egyszáz -\u0026gt; száz || .#. _ ; Потому что не всё, что механически собирается из кусочков, должно так же механически звучать в реальной речи.\nЯзыки, как обычно, решили, что программистам слишком легко живётся.\nИтог Это всего лишь кусок кода для преобразования чисел в слова.\nНо этот кусок находится на пересечении:\nпрограммирования; лингвистики; синтеза речи; конечных автоматов; и какой-то локальной эзотерики. Русский и хорват пишут венгерский модуль для RHVoice на языке Foma.\nНормальный вторник.\n","date":"сб, 25 мая 2024","externalUrl":null,"permalink":"/posts/foma-hungarian-number-expansion-rhvoice/","section":"Технические посты","summary":"","title":"Числа прописью на венгерском языке через Foma","type":"posts"},{"content":"","externalUrl":null,"permalink":"/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"},{"content":"Связаться со мной можно по email, в Telegram или во ВКонтакте. Для рабочих и подробных вопросов лучше использовать email.\nОсновные контакты Email: cyrmax@internet.ru Telegram: @cyrmax ВК: Кирилл Белоусов Профессиональные профили GitHub: github.com/cyrmax LinkedIn: linkedin.com/in/cyrmax Mastodon: @cyrmax@mastodon.ml Дополнительно Twitch: twitch.tv/cyrmax_it vCard Можно сохранить мои контакты в адресную книгу:\nСкачать vCard Если QR-код не распознался, используйте ссылку на vCard выше.\n","externalUrl":null,"permalink":"/contacts/","section":"Кирилл Белоусов","summary":"","title":"Контакты","type":"page"},{"content":"Если не указано иное, тексты и материалы на сайте cyrmax.ru распространяются по лицензии Creative Commons Attribution 4.0 International.\nПри использовании материалов, пожалуйста:\nуказывайте автора: Кирилл Белоусов; указывайте источник: cyrmax.ru; добавляйте ссылку на оригинальную страницу материала; сохраняйте ссылку на лицензию. ","externalUrl":null,"permalink":"/license/","section":"Кирилл Белоусов","summary":"","title":"Лицензия","type":"page"},{"content":"Этот сайт - моя личная техническая площадка: здесь лежат контакты, проекты и заметки по разработке, администрированию, Linux, Windows, DevOps и цифровой доступности.\nСайт статический: страницы собираются заранее через Hugo, используя тему Blowfish, и раздаются Caddy.\nТехническая информация ЗАчем - не знаю, но могу, так что почему бы и нет.\nГенератор\rHugo 0.161.0\rОкружение\rproduction\rСтраниц\r16\rСтатей\r11\rПроектов\r2\rТегов\r30\r","externalUrl":null,"permalink":"/site/","section":"Кирилл Белоусов","summary":"","title":"О сайте","type":"page"}]