<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title><![CDATA[Серый форум &mdash; AutoHotkey]]></title>
		<link>https://forum.script-coding.com/index.php</link>
		<atom:link href="https://forum.script-coding.com/extern.php?action=feed&amp;fid=13&amp;type=rss" rel="self" type="application/rss+xml" />
		<description><![CDATA[Недавние темы раздела «Серый форум».]]></description>
		<lastBuildDate>Wed, 08 Apr 2026 01:38:44 +0000</lastBuildDate>
		<generator>PunBB</generator>
		<item>
			<title><![CDATA[AHK v2: Можно ли кнопкой X сворачивать окно в трей, а не kill процесс?]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?id=18580&amp;action=new</link>
			<description><![CDATA[<p>Здравствуйте.</p><p>Ищу способ реализовать поведение “сворачивание в трей при закрытии окна” для Snagit Editor 2021 (SnagitEditor.exe) на Windows 10.</p><p>Ситуация такая:</p><p>программа изначально не поддерживает трей<br />при закрытии (X / Alt+F4) окно всегда закрывается и завершает процесс<br />штатных настроек для “minimize/close to tray” нет</p><p>Также важно: нужно, чтобы при сворачивании в трей окно убиралось с панели задач, как у обычных tray-приложений.</p><p>Пробовал через AutoHotkey (v2):</p><p>WinHide / WinShow<br />перехват Alt+F4<br />попытки обработать закрытие окна</p><p>Но стабильного результата нет — поведение не работает, при закрытии окно всегда закрывается и завершает процесс.</p><p>Вопрос:</p><p>Можно ли вообще принудительно реализовать “tray behavior” для такого приложения?<br />Есть ли способ перехватить WM_CLOSE / системное закрытие окна через WinAPI или hooks, чтобы не давать приложению завершать процесс?<br />Или Snagit Editor в принципе нельзя заставить работать через трей из-за того, что он всегда завершает процесс при закрытии окна?</p><p>Интересуют рабочие методы (AHK / WinAPI / hooks), без сторонних tray-утилит.</p><div class="codebox"><pre><code>#SingleInstance Force
SetTitleMatchMode 2

Snagit := &quot;ahk_exe SnagitEditor.exe&quot;

; Alt+F4 → скрыть окно (в &quot;трей&quot;)
!F4:: {
    if WinExist(Snagit) {
        WinHide Snagit
        WinSetExStyle &quot;-0x80&quot;, Snagit
    }
}

; восстановить окно
^!r:: {
    if WinExist(Snagit) {
        WinShow Snagit
        WinSetExStyle &quot;+0x80&quot;, Snagit
        WinActivate Snagit
    }
}

; попытка перехвата закрытия
#HotIf WinActive(Snagit)
~!F4:: {
    WinHide Snagit
    WinSetExStyle &quot;-0x80&quot;, Snagit
}
#HotIf

; если окно пропало — контроль видимости
SetTimer CheckSnagit, 1000

CheckSnagit() {
    Snagit := &quot;ahk_exe SnagitEditor.exe&quot;

    if WinExist(Snagit) {
        if !DllCall(&quot;IsWindowVisible&quot;, &quot;ptr&quot;, WinExist(Snagit)) {
            WinShow Snagit
        }
    }
}</code></pre></div>]]></description>
			<author><![CDATA[null@example.com (leex)]]></author>
			<pubDate>Wed, 08 Apr 2026 01:38:44 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?id=18580&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[AHK v2:  compile шок]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?id=18579&amp;action=new</link>
			<description><![CDATA[<p>Сегодня по команде compile получаю сообщение</p><p>&gt;&quot;C:\Program Files\AutoHotkey\Compiler\Ahk2Exe.exe&quot; /in &quot;D:\Мои документы\AutoHotkey\YDList.ahk&quot; <br />&quot;C:\Program&quot; ¥ 異ᯠ¢㢠¥¥© ¨«¨ ¢¥譥©<br />ª®¬ ¤®©, ¨ᯮ«塞®© ¯ணࠬ¬®© ¨«¨ ¯ ª¥⭫¬ 䠩«®¬.<br />&gt;Exit code: 1&nbsp; &nbsp; Time: 0.1623</p><p>А exe не получаю. Переустановка AHK не помогла.</p>]]></description>
			<author><![CDATA[null@example.com (haridev)]]></author>
			<pubDate>Wed, 25 Mar 2026 14:42:44 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?id=18579&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[AHK: AHK V2 управление светодиодом caps lock (индикация раскладки)]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?id=18575&amp;action=new</link>
			<description><![CDATA[<p>Очень прошу помощи. Нашёл в интернете скрипт смены раскладки при нажатии capslock, но беда в том, что при работе этого скрипта светодиод кнопки не работает. А хотелось бы, что бы на английской раскладке светодиод капслока горел, а при переключении на русскую - не горел. Есть какая-то возможность управления светодиодном во второй версии программы? Гуглом нашёл только скрипт (библиотеку?) для старой версии autohotkey_L. <a href="http://www.autohotkey.com/board/topic/9587-keyboard-led-control-capslocknumlockscrolllock-lights/?p=446105">www.autohotkey.com/board/topic/9587-key … /?p=446105</a><br />Сам скрипт смены раскладки по нажатию капслока<br /></p><div class="codebox"><pre><code>#Requires AutoHotkey v2.0

SetCapsLockState(&quot;AlwaysOff&quot;)
+CapsLock::{
    if (GetKeyState(&quot;CapsLock&quot;, &quot;T&quot;)) {
        SetCapsLockState(&#039;Off&#039;)
    } else {
        SetCapsLockState(&#039;On&#039;)
    }
}
CapsLock::Send(&quot;{Ctrl down}{Shift down}{Shift up}{Ctrl up}&quot;)</code></pre></div>]]></description>
			<author><![CDATA[null@example.com (21nordson)]]></author>
			<pubDate>Fri, 20 Mar 2026 17:14:46 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?id=18575&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[AHK v2: Как отловить переключение раскладки клавиатуры]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?id=18557&amp;action=new</link>
			<description><![CDATA[<p>Как это сделать без использования таймера, на событиях windows?<br />Скажем, я хочу сделать свой простой индикатор текущего языка в виде флагов в трее. Чтобы отслеживался факт изменения языка - хоть пользователь нажал сочетание клавиш, хоть PuntoSwitcher сработал и т.п. ИИ выдает вроде осмысленные варианты, но чот не работает ничего( То с использованием HSHELL_LANGUAGE, то с WM_INPUTLANGCHANGE..</p>]]></description>
			<author><![CDATA[null@example.com (AlexPORT)]]></author>
			<pubDate>Thu, 12 Mar 2026 17:05:28 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?id=18557&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[AHK v2: список сайтов из txt в json для Amnezia]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?id=18549&amp;action=new</link>
			<description><![CDATA[<div class="codebox"><pre><code>; список сайтов из txt в json для Amnezia
if !sites := FileSelect(1,, &#039;Список сайтов&#039;, &#039;Documents (*.txt)&#039;)
	ExitApp
sitesJson := StrReplace(sites, &#039;.txt&#039;, &#039;.json&#039;,, &amp;OutputVarCount)
if OutputVarCount != 1 {
	MsgBox &#039;Недопустимое имя входного файла&#039;
	ExitApp
}
indent1 := A_Space . A_Space
indent2 := indent1 . indent1
FileDelete sitesJson
Loop Read sites, sitesJson
	FileAppend (A_Index = 1 ? &#039;[`n&#039; : &#039;,`n&#039;) . indent1 . &#039;{`n&#039; . indent2 . &#039;&quot;hostname&quot;: &quot;&#039; . A_LoopReadLine . &#039;&quot;`n&#039; . indent1 . &#039;}&#039;
FileAppend &#039;`n]&#039;, sitesJson
ExitApp</code></pre></div><p>Для замечаний.</p>]]></description>
			<author><![CDATA[null@example.com (haridev)]]></author>
			<pubDate>Wed, 11 Feb 2026 15:31:21 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?id=18549&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[AHK: Выполнение условия в определенное время, взятое из 2х переменных]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?id=18544&amp;action=new</link>
			<description><![CDATA[<p>Всем привет. Я дилетант в этом деле, но сам код длинный очень (на 1400 строк), скидываю кусок, который отрабатывает не так, как хотелось бы.</p><p>Вообщем проверка времени была когда-то реализована так:<br /></p><div class="codebox"><pre><code>FormatTime, TT,, Time
if (TT = &quot;8:55&quot; or TT = &quot;8:56&quot; or TT = &quot;8:57&quot;){
}</code></pre></div><p>в указанное время в кавычках условие выполнялось.<br />Теперь пытаюсь переделать, чтоб к текущему часу прибавлялся 1, а к минутам 1,2,3(к минутам = чтоб не пропустить момент)<br />И второй вариант это когда я сам задаю сколько к часу прибавить ( InputBox, HTIMEINPUT, ... ), суть примерно таже, просто HTIMEAFK1 := HREALTIME + HTIMEINPUT<br />Прибавка к текущему часу выполнена так:<br /></p><div class="codebox"><pre><code>		FormatTime, REALTIME,, Time
		word_array := StrSplit(REALTIME, &quot;:&quot;) ; Делит значения разделенные двоеточиями.
		HREALTIME := word_array[1] ; до двоеточия записывает в переменную (часы)
		MREALTIME := word_array[2] ; после двоеточия записывает в переменную (минуты)
		MTIMEAFK1 := MREALTIME ; минуты
		MTIMEAFK22 := MREALTIME + 1 ; минуты +1
		MTIMEAFK33 := MREALTIME + 2 ; минуты +2
		MTIMEAFK2 := Format(&quot;{:02}&quot;, MTIMEAFK22) ; добавляет 0, чтоб 2значное число было если после прибавления получилось 1-значное
		MTIMEAFK3 := Format(&quot;{:02}&quot;, MTIMEAFK33) ; добавляет 0, чтоб 2значное число было если после прибавления получилось 1-значное
		HTIMEAFK1 := HREALTIME + 1 ; +1 час
		FormatTime, TT,, Time
		if (TT := &quot;%HTIMEAFK1%:%MTIMEAFK1%&quot; or TT := &quot;%HTIMEAFK1%:%MTIMEAFK2%&quot; or TT := &quot;%HTIMEAFK1%:%MTIMEAFK3%&quot;){
		}</code></pre></div><p>		Но почему-то ahk выполняет условие, считая, что время уже наступило...<br />		Как пофиксить? Пробовал TT = &quot;%HTIMEAFK1%:%MTIMEAFK1%&quot; - условие не выполняет в нужное время...</p>]]></description>
			<author><![CDATA[null@example.com (golemjkeee)]]></author>
			<pubDate>Wed, 14 Jan 2026 11:42:57 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?id=18544&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[AHK: Хитрый hotstring (исправление орфографии и проверка себя)]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?id=18541&amp;action=new</link>
			<description><![CDATA[<p>Всех приветствую!</p><p>Очередной ступор в очередных трех соснах – помогите, пожалуйста, разобраться.&nbsp; </p><p>Тема такая. Есть много хотстрингов, правящих орфографию, вида (для примера):</p><div class="codebox"><pre><code>::карова::корова</code></pre></div><p>Пользуюсь ими уже давно, но вот сейчас осознал, что уровень грамотности от них не повышается, потому что неправильно вбитое слово заменяется на правильное, никак не информируя меня о допущенной ошибке. Решил придумать код, который станет мне сигнализировать, что я написал с ошибкой, но при этом и исправлять будет неверно написанное. Получился он вот такого вида:</p><div class="codebox"><pre><code>::карова::
SendInput корова ОШИБКА
Sleep 80
SendInput {Backspace 6}
return
</code></pre></div><p>Вроде бы просто все: набираешь слово &quot;карова&quot; – на выходе получаешь &quot;корова ОШИБКА&quot;, и &quot;ОШИБКА&quot; стирается почти моментально, но все же заметно для глаза, при этом не раздражает и не мешает печати. Но в процессе эксплуатации данного кода (громкое название, конечно) обнаружились две проблемы:</p><p>1. После такого слова невозможно поставить знак препинания: печатаешь буквально &quot;карова,&quot;, получаешь &quot;корова &quot; (знак каким-то образом съедается скриптом); </p><p>2. Выставить большую букву в таком слове теперь нельзя: печатаешь &quot;Карова&quot;, получаешь &quot;корова&quot;. То же самое получается, если печатать капсом – все буквы оказываются строчными (маленькими). Но это еще можно потерпеть, все-таки относительно редкий случай, а вот невбитие знаков препинания после таких слов – это уже существенный косяк. </p><br /><br /><p>Сама идея, что показывается слово &quot;ОШИБКА&quot; и быстренько стирается, мне нравится, ее надо оставить, в остальном реализация оставляет желать помощи знатоков. Можете подсказать, пожалуйста, как сделать так, чтобы нормально срабатывало? Заранее спасибо!</p>]]></description>
			<author><![CDATA[null@example.com (adeckwatt)]]></author>
			<pubDate>Mon, 05 Jan 2026 21:31:29 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?id=18541&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[AHK v2:  Превосходство AHK]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?id=18524&amp;action=new</link>
			<description><![CDATA[<p>Попросил у ИИ (GigaChat)<br />код на языке python, который 1) сохраняет сохранённую ранее игру паук из игры паук по клавише ctrl+S в месте, отличном от стандартного места сохранения игры паук; 2) сохраняет текущее состояние игры паук (окна игры паук) по месту стандартного сохранения игры паук.</p><p>Ответ.</p><p>Реализовать такое поведение исключительно средствами Python сложно по нескольким причинам:</p><p>Стандартная версия игры «Паук» является частью предустановленных приложений Windows и работает нативно в среде ОС. Это значит, что доступ к сохранению напрямую зависит от внутренней структуры приложения, а обычные скрипты Python не смогут легко вмешиваться в процесс работы такого приложения.<br />Сохранять состояние окна программы также непросто, поскольку стандартные инструменты Python не позволяют управлять окнами приложений таким способом (за исключением специализированных библиотек вроде PyWinAuto или pyautogui, которые всё равно требуют определённых условий).<br />Однако мы можем создать сценарий, использующий горячие клавиши для отправки команд приложению «Паук». Этот способ подразумевает эмуляцию нажатия клавиш через API Windows. Давайте рассмотрим пошагово реализацию вашего сценария.</p><p>Шаги реализации:<br />Часть 1: Запись в альтернативное место при нажатии Ctrl + S<br />Здесь мы перехватим событие сочетания клавиш и отправим команду сохранения в нестандартное место. Однако такая реализация требует вмешательства на уровне интерфейсов самого приложения «Паук», которое технически невозможно осуществить только силами Python. Поэтому эта задача невыполнима классическим способом.</p><p>Часть 2: Сохранение текущего состояния игры по стандартной схеме<br />Мы можем попробовать имитировать стандартный механизм сохранения через клавиатуру. Обычно в игре предусмотрена комбинация клавиш (например, Ctrl + S). Но тут возникает проблема: игра сама должна поддерживать такую функциональность, иначе никакие манипуляции Python не помогут.</p><p>Тем не менее, вот как можно организовать автоматическое выполнение команды сохранения в игре с использованием библиотеки pyautogui, которая позволяет отправлять события клавиатуры приложениям:</p><br /><p>import pyautogui<br />import time</p><p># Задержка перед запуском<br />print(&quot;Программа запустится через 5 секунд... Поднесите фокус к окну игры.&quot;)<br />time.sleep(5)</p><p># Отправляем комбинацию клавиш Ctrl + S для инициирования сохранения<br />pyautogui.hotkey(&#039;ctrl&#039;, &#039;s&#039;)</p><p>print(&quot;Команда сохранения выполнена.&quot;)<br />Этот скрипт просто пошлет комбинации клавиш &quot;Ctrl + S&quot; в активное окно (предполагается, что окно игры активно в момент выполнения скрипта).</p><p>Ограничения подхода:<br />Метод с эмуляцией нажатия клавиш сработает лишь тогда, когда приложение поддерживает данную комбинацию клавиш для сохранения.<br />Управление окном игры и взаимодействием с элементами окна напрямую практически невозможно реализовать без специальных расширенных методов автоматизации оконных элементов (таких как UI Automation Framework).<br />Альтернативное решение:<br />Единственный надежный способ добиться полного контроля над процессом сохранения заключается в модификации исходного кода игры либо написании своего собственного игрового клиента, поддерживающего необходимую функциональность.</p><p>Итак, полная реализация ваших требований (создание отдельной области хранения и управление состоянием окна) невозможна без изменения поведения самой игры. Вы можете рассмотреть вариант написания собственной версии игры с нужными функциями или искать готовое решение среди модификаций или аналогов игры «Паук».</p><p>-----------------<br />Решение средствами AHK.</p><div class="codebox"><pre><code>; сделать копию сохранённой игры Паук и сохранить текущую
; либо восстановить игру из копии
SaveDir1 := &quot;d:\Мои документы\&quot;
SaveDir2 := &quot;d:\Portable\&quot;
SpiderSav := &quot;spider.sav&quot;
WT_Spider := &#039;Пасьянс &quot;Паук&quot;&#039;
WT_Confirm := &quot;ahk_class #32770&quot;
if !WinExist(WT_Spider)
	ExitApp
if A_Args.Length {	; восстановить
	try FileCopy SaveDir2 SpiderSav, SaveDir1, true
	catch as e {
		MsgBox e.Message
		ExitApp
	}
	WinActivate WT_Spider
	send &quot;^o&quot;
} else {	; сохранить
	try FileMove SaveDir1 SpiderSav, SaveDir2, true
	catch as e {
		MsgBox e.Message
		ExitApp
	}
	WinActivate WT_Spider
	send &quot;^s&quot;
}
if !WinWait(WT_Confirm,, 3)
	MsgBox &#039;Нет окна подтверждения&#039;
else ControlClick &quot;Button1&quot;, WT_Confirm
ExitApp</code></pre></div>]]></description>
			<author><![CDATA[null@example.com (haridev)]]></author>
			<pubDate>Sun, 16 Nov 2025 14:12:57 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?id=18524&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[AHK: Автоматическое переключение между альтернативными раскладками]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?id=18518&amp;action=new</link>
			<description><![CDATA[<p>Здравствуйте, помогите, пожалуйста, реализовать простую идею: мне нужен скрипт, который изменяет раскладку клавиатуры с «Qwerty» на «Colemak DH», когда в Windows активен английский язык ввода, и чтобы этот же скрипт также изменял раскладку с «Йцукен» на «Диктор», когда в качестве языка ввода активен русский.</p>]]></description>
			<author><![CDATA[null@example.com (andreyr.2019)]]></author>
			<pubDate>Fri, 07 Nov 2025 15:18:22 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?id=18518&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[AHK v2: Аналог Menu, Tray, Click]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?id=18517&amp;action=new</link>
			<description><![CDATA[<p>Не найду в v2 в документации аналог Menu, Tray, Click<br />Может не так искал, подскажите.<br />Или вообще не пытаться кодить на v2, а остаться на старом добром v1?</p>]]></description>
			<author><![CDATA[null@example.com (Zohann)]]></author>
			<pubDate>Mon, 27 Oct 2025 12:39:08 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?id=18517&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[AHK v1: Windows_8_error_WinHttpRequest]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?id=18516&amp;action=new</link>
			<description><![CDATA[<p>Добрый день! Подскажите, пож-та, на Win10 всё работает прекрасно (внутренний корпоративный сервис), на Win8 ошибка при &quot;HTTP.Send()&quot;, добавлял &quot;HTTP.Option(4) := 256 <br />HTTP.Option(6) := False&quot;, не помогает. </p><div class="codebox"><pre><code>
HTTP := ComObjCreate(&quot;WinHTTP.WinHttpRequest.5.1&quot;)
HTTP.Open(&quot;GET&quot;, &quot;https://os05.........&quot;, false)
HTTP.Send()
HTTP.WaitForResponse()

MsgBox % HTTP.ResponseText
ExitApp</code></pre></div>]]></description>
			<author><![CDATA[null@example.com (inseption86)]]></author>
			<pubDate>Sun, 26 Oct 2025 07:18:31 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?id=18516&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[AHK v1: Обновить состояние скрипта, скачивающего посты ВК]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?id=18497&amp;action=new</link>
			<description><![CDATA[<p>Составил с ИИ код ниже, который за 1 запрос может скачивать до 100 постов из VK с форматированием в HTML-формат различных типов ВК-постов. Для запроса используется приведенная &quot;хранимая процедура&quot;, которую нужно создать на сайте - и токен, прописываемый в файл &quot;token.txt&quot;. Так же используется библиотека <a href="https://github.com/cocobelgica/AutoHotkey-JSON/blob/master/JSON.ahk">JSON.ahk</a>.</p><p>Код имеет странную проблему, когда после парсинга json-ответа первой итерации (offset := 0), на второй итерации (offset := 1) json-ответ не распознается как валидный. При этом достоверно известно, что все последующие ответы валидны, что проверялось подменой ответа первой итерации - ответом второй итерации, а так же при старте скрипта - запуском парсинга со второй итерации. Пытался очищать предыдущее состояние всех данных, но помогает только перезагрузка скрипта, когда JSON класс полностью обновляется. Просьба подсказать решение задачи с полным обновлением состояния скрипта после каждой итерации.</p><p><strong>Хранимая процедура</strong></p><div class="codebox"><pre><code>// execute.getMaxPosts
var response = API.wall.get({
    &quot;owner_id&quot;: Args.owner_id,
    &quot;offset&quot;: Args.offset,
    &quot;count&quot;: 25,  // 100 — МАКСИМУМ ДЛЯ WALL.GET
    &quot;extended&quot;: 1,
    &quot;fields&quot;: &quot;first_name,last_name&quot;,
    &quot;v&quot;: &quot;5.291&quot;
});

return response;</code></pre></div><p><strong>AHK v1<br />(Первая часть кода)</strong></p><br /><div class="codebox"><pre><code>#NoEnv
SendMode Input
#SingleInstance Force
SetWorkingDir %A_ScriptDir%

; Подключаем JSON.ahk
#Include JSON.ahk

; Чтение токена из файла
FileRead, access_token, token.txt
if (ErrorLevel || access_token = &quot;&quot;) {
    MsgBox, Не удалось прочитать токен из файла token.txt
    ExitApp
}

; Убираем возможные пробелы и переводы строк
access_token := Trim(access_token)

owner_id := &quot;-29534144&quot;
output_folder := &quot;VK_Posts_HTML&quot;

; Создаем папку для сохранения
FileCreateDir, %output_folder%

; Получаем и сохраняем все посты
total_posts := GetAllPostsAndSaveHTML(access_token, owner_id, output_folder)
MsgBox, % &quot;Готово! Сохранено постов: &quot; total_posts

; Основная функция для получения всех постов
GetAllPostsAndSaveHTML(access_token, owner_id, output_folder) {
    total_saved := 0
    batch_size := 25
    offset := 0
    total_count := 0  ; Добавляем переменную для общего количества постов
    
    Loop {
        ; Получаем пачку постов через execute
        posts_data := GetPostsBatch(access_token, owner_id, offset)
        if (!posts_data || !posts_data.items || posts_data.items.MaxIndex() = 0) {
            break
        }
        
        ; Сохраняем общее количество постов при первом запросе
        if (offset = 0 &amp;&amp; posts_data.count &gt; 0) {
            total_count := posts_data.count
        }
        
        ; Обрабатываем каждый пост
        for index, post_item in posts_data.items {
            ; Парсим пост в наш формат
            post_data := ParsePostItem(post_item, posts_data)
            if (post_data &amp;&amp; post_data.id) {
                html_content := ConvertPostToHTML(post_data)
                filename := output_folder &quot;\&quot; owner_id &quot;_&quot; post_data.id &quot;.html&quot;
                SaveHTMLToFile(html_content, filename)
                total_saved++
                
                ; Прогресс с информацией об общем количестве
                progress_text := &quot;Сохранено: &quot; total_saved &quot; из &quot; total_count &quot; постов (offset: &quot; offset &quot;)&quot;
                if (total_count &gt; 0) {
                    percent := Round((total_saved / total_count) * 100)
                    progress_text .= &quot; (&quot; percent &quot;%)&quot;
                }
                ToolTip, % progress_text
            }
        }
        
        offset += batch_size ; Увеличиваем offset для следующей пачки
        Sleep, 2500  ; Пауза между запросами
        
        ; если сохранили больше или равно общему количеству постов - выходим
        if (total_count &gt; 0 &amp;&amp; total_saved &gt;= total_count) {
            break
        }
        
        ; Дополнительная проверка: если в ответе нет постов
        if (posts_data.items.MaxIndex() = 0) {
            break
        }
    }
    
    ToolTip
    return total_saved
}

; Получение пачки постов через execute.getMaxPosts
GetPostsBatch(access_token, owner_id, offset) {
    url := &quot;https://api.vk.com/method/execute.getMaxPosts?owner_id=&quot; owner_id
        . &quot;&amp;offset=&quot; offset
        . &quot;&amp;access_token=&quot; access_token
        . &quot;&amp;v=5.291&quot;
    
    response := SendHTTPRequest(url)
    
    ; Сохраняем сырой ответ для отладки
    FileDelete, debug_batch_%offset%_response.txt
    FileAppend, %response%, debug_batch_%offset%_response.txt, UTF-8
    
    if (response = &quot;ERROR&quot;) {
        return &quot;&quot;
    }
    
    return ParsePostsResponse(response)
}

; Парсинг ответа от execute.getMaxPosts
ParsePostsResponse(response) {
    try {
        json := JSON.Load(response)
        
        ; Проверяем на ошибки API
        if (json.error) {
            error_msg := &quot;Ошибка API: код &quot; json.error.error_code &quot; - &quot; json.error.error_msg
            MsgBox, % error_msg
            return &quot;&quot;
        }
        
        ; Проверяем наличие response
        if (!json.response) {
            MsgBox, Нет response в ответе
            return &quot;&quot;
        }
        
        result := Object()
        result.items := json.response.items ? json.response.items : []
        result.profiles := json.response.profiles ? json.response.profiles : []
        result.groups := json.response.groups ? json.response.groups : []
        result.count := json.response.count ? json.response.count : 0  ; Это поле теперь используется
        
        return result
        
    } catch e {
        MsgBox, Ошибка парсинга JSON батча: %e%
        return &quot;&quot;
    }
}

; Конвертация элемента поста в наш формат
ParsePostItem(post_item, posts_data) {
    post := Object()
    post.photos := []
    post.links := []
    post.videos := []
    post.audios := []
    post.documents := []
    post.polls := []
    post.reposts := []
    post.albums := []
    
    ; Основные поля поста
    post.id := post_item.id
    post.owner_id := post_item.owner_id
    post.from_id := post_item.from_id
    post.date := post_item.date
    post.text := post_item.text ? post_item.text : &quot;&quot;
    post.title := post_item.title ? post_item.title : &quot;&quot;
    
    ; Статистика
    post.likes := post_item.likes.count
    post.reposts := post_item.reposts.count
    post.comments := post_item.comments.count
    post.views := post_item.views.count
    
    ; Извлекаем все виды вложений
    if (post_item.attachments &amp;&amp; post_item.attachments.Length() &gt; 0) {
        for index, attachment in post_item.attachments {
            if (attachment.type = &quot;photo&quot; &amp;&amp; attachment.photo) {
                photo_url := GetLargestPhotoUrl(attachment.photo)
                if (photo_url) {
                    post.photos.Push(photo_url)
                }
            }
            else if (attachment.type = &quot;link&quot; &amp;&amp; attachment.link) {
                link_data := ExtractLinkData(attachment.link)
                if (link_data) {
                    post.links.Push(link_data)
                }
            }
            else if (attachment.type = &quot;video&quot; &amp;&amp; attachment.video) {
                video_data := ExtractVideoData(attachment.video)
                if (video_data) {
                    post.videos.Push(video_data)
                }
            }
            else if (attachment.type = &quot;audio&quot; &amp;&amp; attachment.audio) {
                audio_data := ExtractAudioData(attachment.audio)
                if (audio_data) {
                    post.audios.Push(audio_data)
                }
            }
            else if (attachment.type = &quot;doc&quot; &amp;&amp; attachment.doc) {
                doc_data := ExtractDocumentData(attachment.doc)
                if (doc_data) {
                    post.documents.Push(doc_data)
                }
            }
            else if (attachment.type = &quot;poll&quot; &amp;&amp; attachment.poll) {
                poll_data := ExtractPollData(attachment.poll)
                if (poll_data) {
                    post.polls.Push(poll_data)
                }
            }
            else if (attachment.type = &quot;post&quot; &amp;&amp; attachment.wall) {
                repost_data := ExtractRepostData(attachment.wall)
                if (repost_data) {
                    post.reposts.Push(repost_data)
                }
            }
            else if (attachment.type = &quot;album&quot; &amp;&amp; attachment.album) {
                album_data := ExtractAlbumData(attachment.album)
                if (album_data) {
                    post.albums.Push(album_data)
                }
            }
        }
    }
    
    ; Название группы и screen_name
    post.group_name := &quot;Группа&quot;
    post.group_screen_name := &quot;&quot;
    post.group_avatar := &quot;&quot;
    if (posts_data.groups &amp;&amp; posts_data.groups.Length() &gt; 0) {
        post.group_name := posts_data.groups[1].name
        post.group_screen_name := posts_data.groups[1].screen_name ? posts_data.groups[1].screen_name : &quot;&quot;
        ; Извлекаем аватар группы (лучшее качество)
        if (posts_data.groups[1].photo_200) {
            post.group_avatar := posts_data.groups[1].photo_200
        } else if (posts_data.groups[1].photo_100) {
            post.group_avatar := posts_data.groups[1].photo_100
        } else if (posts_data.groups[1].photo_50) {
            post.group_avatar := posts_data.groups[1].photo_50
        }
    }
    
    ; Если это пост от пользователя, ищем его аватар
    post.user_avatar := &quot;&quot;
    if (post.from_id &gt; 0 &amp;&amp; posts_data.profiles &amp;&amp; posts_data.profiles.Length() &gt; 0) {
        for index, profile in posts_data.profiles {
            if (profile.id = post.from_id) {
                ; Аватар пользователя (лучшее качество)
                if (profile.photo_200) {
                    post.user_avatar := profile.photo_200
                } else if (profile.photo_100) {
                    post.user_avatar := profile.photo_100
                } else if (profile.photo_50) {
                    post.user_avatar := profile.photo_50
                }
                break
            }
        }
    }
    
    return post
}

; Извлечение данных ссылки
ExtractLinkData(link) {
    link_data := Object()
    link_data.url := link.url
    link_data.title := link.title ? link.title : &quot;&quot;
    link_data.description := link.description ? link.description : &quot;&quot;
    link_data.caption := link.caption ? link.caption : &quot;&quot;
    
    ; Извлекаем превью ссылки
    if (link.photo) {
        link_data.preview_url := GetLargestPhotoUrl(link.photo)
    } else {
        link_data.preview_url := &quot;&quot;
    }
    
    return link_data
}

; Извлечение данных видео
ExtractVideoData(video) {
    video_data := Object()
    video_data.id := video.id
    video_data.owner_id := video.owner_id
    video_data.title := video.title ? video.title : &quot;&quot;
    video_data.description := video.description ? video.description : &quot;&quot;
    video_data.duration := video.duration ? video.duration : 0
    
    ; Превью видео
    if (video.image &amp;&amp; video.image.Length() &gt; 0) {
        video_data.preview_url := GetLargestPhotoUrl({&quot;sizes&quot;: video.image})
    } else if (video.first_frame &amp;&amp; video.first_frame.Length() &gt; 0) {
        video_data.preview_url := GetLargestPhotoUrl({&quot;sizes&quot;: video.first_frame})
    } else {
        video_data.preview_url := &quot;&quot;
    }
    
    return video_data
}

; Извлечение данных аудио
ExtractAudioData(audio) {
    audio_data := Object()
    audio_data.artist := audio.artist ? audio.artist : &quot;&quot;
    audio_data.title := audio.title ? audio.title : &quot;&quot;
    audio_data.duration := audio.duration ? audio.duration : 0
    audio_data.url := audio.url ? audio.url : &quot;&quot;
    
    return audio_data
}

; Извлечение данных документа
ExtractDocumentData(doc) {
    doc_data := Object()
    doc_data.id := doc.id
    doc_data.owner_id := doc.owner_id
    doc_data.title := doc.title ? doc.title : &quot;&quot;
    doc_data.ext := doc.ext ? doc.ext : &quot;&quot;
    doc_data.url := doc.url ? doc.url : &quot;&quot;
    doc_data.size := doc.size ? doc.size : 0
    doc_data.date := doc.date ? doc.date : 0
    doc_data.access_key := doc.access_key ? doc.access_key : &quot;&quot;
    
    ; Превью документа (если есть)
    if (doc.preview &amp;&amp; doc.preview.photo &amp;&amp; doc.preview.photo.sizes) {
        doc_data.preview_url := GetLargestPhotoUrl(doc.preview.photo)
    } else {
        doc_data.preview_url := &quot;&quot;
    }
    
    return doc_data
}

; Извлечение данных опроса
ExtractPollData(poll) {
    poll_data := Object()
    poll_data.id := poll.id
    poll_data.owner_id := poll.owner_id
    poll_data.question := poll.question ? poll.question : &quot;&quot;
    poll_data.votes := poll.votes ? poll.votes : 0
    poll_data.answers := []
    
    ; НОВЫЕ ПОЛЯ ДЛЯ ОПРОСА
    poll_data.anonymous := poll.anonymous ? poll.anonymous : 0
    poll_data.multiple := poll.multiple ? poll.multiple : 0
    poll_data.end_date := poll.end_date ? poll.end_date : 0
    poll_data.closed := poll.closed ? poll.closed : 0
    poll_data.is_board := poll.is_board ? poll.is_board : 0
    poll_data.can_edit := poll.can_edit ? poll.can_edit : 0
    poll_data.can_vote := poll.can_vote ? poll.can_vote : 0
    poll_data.can_report := poll.can_report ? poll.can_report : 0
    poll_data.can_share := poll.can_share ? poll.can_share : 0
    poll_data.author_id := poll.author_id ? poll.author_id : 0
    poll_data.background := poll.background ? poll.background : &quot;&quot;
    
    if (poll.answers &amp;&amp; poll.answers.Length() &gt; 0) {
        for index, answer in poll.answers {
            answer_data := Object()
            answer_data.id := answer.id
            answer_data.text := answer.text ? answer.text : &quot;&quot;
            answer_data.votes := answer.votes ? answer.votes : 0
            answer_data.rate := answer.rate ? answer.rate : 0
            poll_data.answers.Push(answer_data)
        }
    }
    
    return poll_data
}

; Извлечение данных репоста
ExtractRepostData(wall) {
    repost_data := Object()
    repost_data.id := wall.id
    repost_data.owner_id := wall.owner_id
    repost_data.from_id := wall.from_id
    repost_data.date := wall.date
    repost_data.text := wall.text ? wall.text : &quot;&quot;
    
    ; Статистика репоста
    if (wall.likes) {
        repost_data.likes := wall.likes.count
    }
    if (wall.reposts) {
        repost_data.reposts := wall.reposts.count
    }
    if (wall.comments) {
        repost_data.comments := wall.comments.count
    }
    if (wall.views) {
        repost_data.views := wall.views.count
    }
    
    return repost_data
}

; Извлечение данных альбома
ExtractAlbumData(album) {
    album_data := Object()
    album_data.id := album.id
    album_data.owner_id := album.owner_id
    album_data.title := album.title ? album.title : &quot;&quot;
    album_data.description := album.description ? album.description : &quot;&quot;
    album_data.size := album.size ? album.size : 0
    
    ; Обложка альбома
    if (album.thumb &amp;&amp; album.thumb.sizes) {
        album_data.thumb_url := GetLargestPhotoUrl(album.thumb)
    } else {
        album_data.thumb_url := &quot;&quot;
    }
    
    return album_data
}</code></pre></div>]]></description>
			<author><![CDATA[null@example.com (1srafel)]]></author>
			<pubDate>Wed, 22 Oct 2025 20:28:10 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?id=18497&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[AHK: проблемы с кириллицей]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?id=18494&amp;action=new</link>
			<description><![CDATA[<p>Yo, я полный новичок и вообще не шарю в этом.<br />Есть такой скрипт который при нажатии на кнопку через 70 мс нажимается еще раз:</p><div class="codebox"><pre><code>$x::
    Send x
sleep 70
    Send {x}
    Return</code></pre></div><p>Если написать на новой строке<br /></p><div class="codebox"><pre><code>$ч::
    Send ч
sleep 70
    Send {ч}
    Return
</code></pre></div><p>то не работает нажатие на латинскую букву. </p><p>Вопрос: как нужно написать код, чтобы работало на кириллице и латинском при переключении раскладок?</p>]]></description>
			<author><![CDATA[null@example.com (Kiyohime)]]></author>
			<pubDate>Mon, 08 Sep 2025 16:35:03 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?id=18494&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[AHK v1: Повторение взаимовложенных Циклов для скрипта.]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?id=18489&amp;action=new</link>
			<description><![CDATA[<p>Здравствуйте. Как сделать так чтобы после выполнения 2 цикла, вновь начался с 1 и так 10 раз, и только потом перешёл к третьему.<br /></p><div class="codebox"><pre><code>F2::

Loop,  {
Loop, 1{
Loop, 10{
Send, {f Down}
Sleep, 6450
Send, {f Up}
Sleep, 500
Send, {f Down}
Sleep, 200
Send, {f Up}
Sleep, 300
Send, {f Down}
Sleep, 200
Send, {f Up}
Sleep, 300
Send, {f Down}
Sleep, 200
Send, {f Up}
Sleep, 300
Send, {f Down}
Sleep, 1600
Send, {f Up}
Sleep, 500
Send, {f Down}
Sleep, 200
Send, {f Up}
Sleep, 300
}
Send, {f Down}
Sleep, 200
Send, {f Up}
Sleep, 300
Send, {f Down}
Sleep, 200
Send, {f Up}
Sleep, 300
Send, {f Down}
Sleep, 1600
Send, {f Up}
Sleep, 500
Send, {f Down}
Sleep, 200
Send, {f Up}
Sleep, 300
Send, {f Down}
Sleep, 200
Send, {f Up}
Sleep, 300
}
Send, {f Down}
Sleep, 200
Send, {f Up}
Sleep, 300
Send, {f Down}
Sleep, 200
Send, {f Up}
Sleep, 300
Send, {f Down}
Sleep, 1600
Send, {f Up}
Sleep, 500
Send, {f Down}
Sleep, 200
Send, {f Up}
Sleep, 300
Send, {f Down}
Sleep, 200
Send, {f Up}
Sleep, 300
}
Return
F3::ExitApp
F4::Pause</code></pre></div>]]></description>
			<author><![CDATA[null@example.com (Igor S)]]></author>
			<pubDate>Wed, 03 Sep 2025 06:53:44 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?id=18489&amp;action=new</guid>
		</item>
		<item>
			<title><![CDATA[AHK v2: JSON: кто как десериализует]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?id=18483&amp;action=new</link>
			<description><![CDATA[<p>В коллекции есть вариант для AHK v1, но я затруднился перевести его на v2.</p>]]></description>
			<author><![CDATA[null@example.com (haridev)]]></author>
			<pubDate>Fri, 29 Aug 2025 17:36:04 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?id=18483&amp;action=new</guid>
		</item>
	</channel>
</rss>
