Skip to content

Commit e08baba

Browse files
committed
fix(v2): panel no longer hangs, and the login entry repairs itself
Reported on a fresh install: "everything installs, but the bypass does not turn on and the program hangs". The bypass was actually fine - the service was running and YouTube and Discord answered. Two real problems were behind the symptom. 1) The log flooded and froze the window. A desktop Telegram client that cannot complete the handshake just reconnects, so the proxy logged a warning per attempt: measured 72 failed handshakes per 30 seconds, hundreds of lines a second. The logs page queued one dispatcher operation per line. That is what looked like a hang. - Failed handshakes are now counted and reported once every 30 seconds with the total, plus a hint that the Telegram settings probably carry the web proxy secret where the local MTProto secret belongs. - The logs page batches incoming entries and flushes them in one pass. Measured after the fix: 8 log lines in 40 seconds. 2) The autostart entry pointed at a file that does not exist: "C:\Program Files\YoutubeDiscordPanel.exe" while the panel actually lives in "C:\Program Files\YoutubeDiscordPanel\YoutubeDiscordPanel.exe". Nothing in the UI showed this; the bypass simply never came back after a reboot. - The panel now rewrites the login entry at every start if it does not match the running executable. Verified: the broken entry was corrected and logged. - The installer now always creates a folder named YoutubeDiscordPanel inside the chosen location instead of dropping files into the picked folder itself, which is how a mismatched path was written in the first place. Also: auto-apply after login defaults to ON. A fresh install used to come up in "panel only" mode, so the remembered strategy was never applied and it looked like the bypass had not started.
1 parent 91470a2 commit e08baba

6 files changed

Lines changed: 162 additions & 15 deletions

File tree

‎.github/release-notes/v2.0.7.md‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Zapret Panel 2.0.7
2+
3+
## Исправлено: панель не зависала
4+
5+
Симптом был «всё ставится, но обход не включается и программа зависает».
6+
Обход как раз работал — служба была запущена, YouTube и Discord отвечали.
7+
Зависание вызывал поток записей в лог.
8+
9+
Клиент Telegram, который не может пройти handshake, просто переподключается.
10+
Раньше прокси писал предупреждение на **каждую** попытку: 72 неудачных
11+
подключения за 30 секунд, сотни строк в секунду. А страница логов ставила в
12+
очередь UI по одной операции на строку. Это и выглядело как зависание.
13+
14+
Теперь неудачные подключения считаются и показываются раз в 30 секунд одним
15+
сообщением с итогом и подсказкой. Страница логов собирает записи пачкой.
16+
17+
Проверено: 8 строк лога за 40 секунд вместо сотен в секунду.
18+
19+
## Исправлено: обход не возвращался после перезагрузки
20+
21+
Запись автозапуска указывала на несуществующий файл:
22+
23+
```
24+
"C:\Program Files\YoutubeDiscordPanel.exe" ← было в реестре
25+
"C:\Program Files\YoutubeDiscordPanel\YoutubeDiscordPanel.exe" ← где панель на самом деле
26+
```
27+
28+
В интерфейсе это не видно было вообще — просто после перезагрузки обход не
29+
поднимался.
30+
31+
Теперь панель при каждом запуске сверяет запись автозапуска с тем файлом,
32+
который реально запущен, и исправляет её сама. Установщик дополнительно всегда
33+
создаёт папку `YoutubeDiscordPanel` внутри выбранного места, а не кладёт файлы
34+
в саму выбранную папку.
35+
36+
## Исправлено: обход не применялся при входе в Windows
37+
38+
Автоприменение запомненной стратегии теперь включено по умолчанию. После свежей
39+
установки панель поднималась, но обход не применяла — выглядело как «не
40+
включилось».

‎wpf/App.xaml.cs‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,12 @@ protected override void OnStartup(StartupEventArgs e)
192192
ListenForShowRequests(window);
193193
Log("window shown");
194194

195-
// `--autoconnect` comes from the login item: apply the
195+
// `--autoconnect` comes from the login item: apply the
196196
// remembered working strategy in the background, no questions.
197+
// Repair the login entry first: if it points at a path that no
198+
// longer exists, the bypass silently stays down after a reboot.
199+
AutoStart.EnsureAutoStartEntry();
200+
197201
if (AutoStart.StartedWithAutoApply())
198202
{
199203
string root = Status.ResolveRoot(Config.Get("zapretRoot", null));

‎wpf/Pages/LogsPage.xaml.cs‎

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.IO;
44
using System.Linq;
55
using System.Windows;
6+
using System.Windows.Threading;
67
using System.Windows.Controls;
78
using Ydp.Services;
89

@@ -26,18 +27,44 @@ private void Page_Loaded(object sender, RoutedEventArgs e)
2627

2728
private void OnAppended(LogEntry entry)
2829
{
29-
Dispatcher.BeginInvoke(new Action(() =>
30+
// Batch, do not queue one dispatcher item per line. A burst of a few
31+
// hundred entries (a reconnect loop, a download) used to enqueue
32+
// hundreds of dispatcher operations and freeze the window.
33+
lock (_pending)
34+
{
35+
_pending.Add(entry);
36+
if (_flushScheduled) return;
37+
_flushScheduled = true;
38+
}
39+
Dispatcher.BeginInvoke(new Action(FlushPending), DispatcherPriority.Background);
40+
}
41+
42+
private readonly List<LogEntry> _pending = new List<LogEntry>();
43+
private bool _flushScheduled;
44+
45+
private void FlushPending()
46+
{
47+
List<LogEntry> batch;
48+
lock (_pending)
49+
{
50+
batch = new List<LogEntry>(_pending);
51+
_pending.Clear();
52+
_flushScheduled = false;
53+
}
54+
if (batch.Count == 0) return;
55+
56+
// Only auto-follow when the user is looking at the tail.
57+
if (LogList.Items.Count > 400)
58+
{
59+
var items = LogList.Items.Cast<LogEntry>().Skip(200).ToList();
60+
LogList.Items.Clear();
61+
foreach (var item in items) LogList.Items.Add(item);
62+
}
63+
foreach (var entry in batch)
3064
{
31-
// Only auto-follow when the user is looking at the tail.
32-
if (LogList.Items.Count > 400)
33-
{
34-
var items = LogList.Items.Cast<LogEntry>().Skip(200).ToList();
35-
LogList.Items.Clear();
36-
foreach (var item in items) LogList.Items.Add(item);
37-
}
3865
if (Matches(entry)) LogList.Items.Add(entry);
39-
if (LogList.Items.Count > 0) LogList.ScrollIntoView(LogList.Items[LogList.Items.Count - 1]);
40-
}));
66+
}
67+
if (LogList.Items.Count > 0) LogList.ScrollIntoView(LogList.Items[LogList.Items.Count - 1]);
4168
}
4269

4370
private bool Matches(LogEntry entry)

‎wpf/Tg/TgProxy.cs‎

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,33 @@ private static IEnumerable<string> WsDomains(int dc, bool isMedia)
389389
}
390390
}
391391

392+
/// <summary>
393+
/// Summarise bad handshakes instead of logging each one.
394+
///
395+
/// A desktop Telegram client that cannot complete the handshake simply
396+
/// reconnects, so this can fire dozens of times per second. One line per
397+
/// attempt made the log useless and pushed the panel's UI thread to the
398+
/// ground, which is what looked like the program hanging.
399+
/// </summary>
400+
private static void ReportBadHandshake()
401+
{
402+
int count = Interlocked.Increment(ref _badHandshakes);
403+
DateTime now = DateTime.UtcNow;
404+
if (_lastBadHandshakeReport == DateTime.MinValue
405+
|| (now - _lastBadHandshakeReport).TotalSeconds >= 30)
406+
{
407+
_lastBadHandshakeReport = now;
408+
_log("неудачных подключений за 30 секунд: " + count
409+
+ ". Значит кто-то подключается к локальному прокси и не может"
410+
+ " пройти handshake. Чаще всего в настройках Telegram указан"
411+
+ " секрет веб-прокси, а нужен секрет локального MTProto-прокси"
412+
+ " (он показан на главной странице).", "warn");
413+
}
414+
}
415+
416+
private static int _badHandshakes;
417+
private static DateTime _lastBadHandshakeReport = DateTime.MinValue;
418+
392419
private static async Task HandleClientAsync(TcpClient client, CancellationToken token)
393420
{
394421
var stats = Stats;
@@ -401,10 +428,13 @@ private static async Task HandleClientAsync(TcpClient client, CancellationToken
401428
var handshake = await ReadExactlyAsync(stream, TgCrypto.HandshakeLen, token);
402429
if (handshake == null) return;
403430

404-
var parsed = TgCrypto.ParseHandshake(handshake, _secret);
431+
var parsed = TgCrypto.ParseHandshake(handshake, _secret);
405432
if (parsed == null)
406433
{
407-
_log("handshake parse failed (wrong secret or not MTProto)", "warn");
434+
// Telegram retries in a tight loop when the proxy answers
435+
// but the secret does not fit. Logging every attempt filled
436+
// the log with hundreds of lines a second and buried the UI.
437+
ReportBadHandshake();
408438
return;
409439
}
410440

‎wpf/Zapret/AutoConnect.cs‎

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,9 +259,47 @@ public static bool SetEnabled(bool enabled, bool withAutoApply)
259259
}
260260
}
261261

262+
/// <summary>
263+
/// Make sure the login item points at THIS executable.
264+
///
265+
/// The entry is written by the installer, which may have been run with a
266+
/// different folder than the one the panel ended up in. A stale entry is
267+
/// invisible in the UI but silently means the bypass never comes back
268+
/// after a reboot, so it is repaired here on every start.
269+
/// </summary>
270+
public static bool EnsureAutoStartEntry()
271+
{
272+
string exe = ExePath();
273+
if (string.IsNullOrEmpty(exe)) return false;
274+
string subKey = @"Software\Microsoft\Windows\CurrentVersion\Run";
275+
string current = Win.ReadRegistryString("HKCU", subKey, "YoutubeDiscordPanel");
276+
string wanted = "\"" + exe + "\" --autoconnect";
277+
if (string.Equals(current, wanted, StringComparison.OrdinalIgnoreCase)) return false;
278+
279+
try
280+
{
281+
using (var key = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(subKey))
282+
{
283+
if (key == null) return false;
284+
key.SetValue("YoutubeDiscordPanel", wanted);
285+
}
286+
Logger.Info("Запись автозапуска исправлена: " + (current ?? "не было") + " -> " + wanted, "autoconnect");
287+
return true;
288+
}
289+
catch (Exception ex)
290+
{
291+
Logger.Warn("Не удалось исправить автозапуск: " + ex.Message, "autoconnect");
292+
return false;
293+
}
294+
}
295+
262296
public static bool AutoApplyEnabled()
263297
{
264-
return string.Equals(Config.Get(AutoConnect.KeyAutoApply, "false"), "true", StringComparison.OrdinalIgnoreCase);
298+
// On by default: the whole point of the panel is that the bypass is
299+
// up after a reboot without anyone opening anything. A fresh install
300+
// used to come up in "panel only" mode, which looked like the
301+
// program had not done its job.
302+
return string.Equals(Config.Get(AutoConnect.KeyAutoApply, "true"), "true", StringComparison.OrdinalIgnoreCase);
265303
}
266304

267305
public static string ExePath()

‎wpf/setup/Program.cs‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,15 @@ private static int Install(bool silent)
171171
using (var form = new InstallForm(InstallDir))
172172
{
173173
if (form.ShowDialog() != DialogResult.OK) return 0;
174-
chosenDir = form.SelectedDirectory;
174+
// Always land in a folder named YoutubeDiscordPanel. Taking
175+
// the picked folder verbatim allowed files to be dropped
176+
// straight into "C:\Program Files", and that is exactly how
177+
// a mismatched autostart path got written.
178+
chosenDir = form.SelectedDirectory.TrimEnd('\\');
179+
if (!string.Equals(Path.GetFileName(chosenDir), ProductName, StringComparison.OrdinalIgnoreCase))
180+
{
181+
chosenDir = Path.Combine(chosenDir, ProductName);
182+
}
175183
createShortcuts = form.CreateShortcuts;
176184
createDesktopShortcut = form.CreateDesktopShortcut;
177185
addAutoStart = form.AddAutoStart;

0 commit comments

Comments
 (0)