Обновить
57.98

C# *

Объектно-ориентированный язык программирования

Сначала показывать
Порог рейтинга

Отправка уведомления от имени другого приложения

Результат
Результат

В продолжение этого поста

Когда у вас AOT приложение, которое запускается от администратора - 99% библиотек для работы с COM не работают, плюс для Windows App SDK отдельно указано "Notifications for an elevated (admin) app is currently not supported.", а issue на github висит с 2023 года и поэтому для отправки системных уведомлений надо выкручиваться через другое приложение/PowerShell.

Пример как отправлять уведомления:

using System.Diagnostics;
using Windows.UI.Notifications;
using Microsoft.Toolkit.Uwp.Notifications;
using Vanara.PInvoke;
using Vanara.Windows.Shell;

namespace ConsoleNotifications;

class Program
{
    static async Task Main(string[] args)
    {
        string messageTitle = "Habr";
        string messageText = "Hello Habrahabr!";
        string targetExePath = Environment.SystemDirectory + "\\notepad.exe";
        string appUserModelId = GetAppAumid().First(var=>var.Name.Contains("Chrome")).Aumid;
        await SendNotification(appUserModelId, messageTitle, messageText, targetExePath);
    }

    private static Task SendNotification(string appUserModelId, string title, string content, string targetPath)
    {
        var tcs = new TaskCompletionSource();
        var notification = new ToastNotification(new ToastContentBuilder().AddText(title)
            .AddText(content).Content.GetXml());
        notification.Priority = ToastNotificationPriority.High;
        notification.Activated += (s, e) =>
        {
            Process.Start(new ProcessStartInfo(targetPath));
            tcs.SetResult();
        };
        notification.Failed += (s, e) => tcs.SetResult();
        notification.Dismissed += (s, e) => tcs.SetResult();
        ToastNotificationManager.CreateToastNotifier(appUserModelId).Show(notification);
        return tcs.Task;
    }
    
    public sealed record AppAumidInfo(string Name, string Aumid);
    
    public static List<AppAumidInfo> GetAppAumids()
    {
        var results = new List<AppAumidInfo>();
        var f = new ShellFolder(Shell32.KNOWNFOLDERID.FOLDERID_AppsFolder);
        foreach (ShellItem i in f.EnumerateChildren(FolderItemFilter.NonFolders | FolderItemFilter.Folders))
            results.Add(new AppAumidInfo(i.Name, i.ParsingName));
        return results;
    }
}

В вызываемом приложении надо выставить AppUserModelID и предварительно создать ссылку на него

[LibraryImport("shell32.dll", SetLastError=true)]
internal static partial void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID);

Создание .lnk ссылки с параметрами:

var link = new ShellLink()
{
        ToastActivatorId = guid, 
        AppUserModelID = appUserModelId,
        TargetPath = exePath
};
link.Save(shortcutPath);

P.S. Буду рад услышать про другие существующие решения этой проблемы

Теги:
+3
Комментарии0

Куда можно сбежать из разработки, чтобы не умереть с голоду?

Ищу работу уже несколько месяцев, и ощущение такое, что рынок полуживой. По вакансиям — требования «знай всё на свете», а если верить hh, то на некоторые позиции по 400–500 откликов.

Невольно возникает вопрос:
а есть ли вообще адекватный выход из разработки?

Я .NET-разработчик с 6+ годами опыта и хотелось бы понять, куда можно перейти, чтобы мой бэкграунд был полезен, а не обнулялся в ноль — и при этом не остаться без средств к существованию.

Возможно, у кого-то уже был похожий путь. Буду рад, если поделитесь, из чего выбирали и на чём в итоге остановились.

Теги:
+4
Комментарии13

Ответьте мне на один простой вопрос: зачем в наше время вообще нужны HR?

Ни для кого не секрет, что найм сегодня сломан. Бесконечные этапы собеседований, которые якобы должны отсекать «вкатунов в IT», по факту отсеивают и нормальных разработчиков.

Причём даже в эффективности этого «фильтра» есть серьёзные сомнения: по сети уже гуляют AI-оверлеи, которые в реальном времени анализируют вопросы интервьюеров и подсказывают ответы. Так о каком объективном отборе вообще идёт речь?

При этом у HR — бесконечный поток, условно говоря, «подопытных кроликов», на которых можно тестировать гипотезы, улучшать процессы и действительно чинить найм.
Но вместо этого они просто копируют чужие, далеко не самые успешные практики.

Почему HR не выполняют своё прямое предназначение, а действуют по шаблону?

И главный вопрос: каким образом лайв-кодинг должен подтверждать или опровергать мои навыки, если:

могут дать абсолютно любую задачу и в штатном порядке, я залезу гуглить документацию, а на собесе я это сделать не могу?

Короче говоря — как же у меня с этого горит.

Теги:
+6
Комментарии2

Что нового появилось в PVS-Studio в 2025 году

Новый 2026 год уже вовсю идёт, и мы решили оглянуться назад и вспомнить, что интересного появилось в PVS-Studio за богатый на изменения 2025 год:

  • десятки новых диагностических правил, включая проверки, ориентированные на безопасность;

  • обновления интеграций в популярные IDE, CI/CD-системы, ASOC и сборочные системы;

  • улучшения анализа;

  • расширенная поддержка стандартов.

Всё это и многое другое описано в новой статье в нашем блоге.

Теги:
+1
Комментарии0

Статьи по DDD.

Уди Дахан — один из первопроходцев DDD в C#.

Он смог показать, как именно можно применить очевидные инфраструктурные практики типа сервисной шины в контексте доменных событий.

Да, на сегодняшний день имплементация инфраструктуры для доменных событий может выглядеть совсем иначе. Но ознакомление с 3-я заметками Дахана крайне важно для понимания исторического контекста.

Статьи на сайте Дахана:

Теги:
0
Комментарии0

Required или нет?

Работая над одним из проектов, который недавно переехал из Framework 4.8 на Core 9, обнаружил множество самых разных вариантов использования модификатора required и атрибута Required, примерно каждый второй из которых был использован неправильно. Я написал это коллегам и хочу поделиться этим здесь. Это не обязательные правила, но сильно упрощают работу с кодом.

Небольшое пояснение

Атрибут Required нужен для проверки входящих преимущественно строковых данных в эндпоинтах. Возвращает ошибку, если значение null или пустая строка для строк (если не отключено параметром AllowEmptyStrings). Работает в Runtime. Также применяется в Entity Framework в подходе code-first но с включением опции <Nullable> в csproj про эти случаи можно забыть, сделав код чище.

Модификатор required нужен для обязательного указания значений полей при создании класса. Работает в Compile-time.

Примеры использования

// имеем класс с required полем
public class Example
{
    public required string Name { get; set; }
}

// пытаемся создать экземпляр в коде
var example1 = new Example();  // будет ошибка при попытке сборки проекта
var example2 = new Example { Name = string.Empty };  // тут ошибки не будет

// Вывод: модификатор required нужен для разработчика
// имеем класс с полем, у которого атрибут Required
public class Example
{
    [Required]
    public string Name { get; set; }
}

// пытаемся создать экземпляр в коде
var example = new Example();  // проект спокойно собирается

// имеем эндпоинт в контроллере
public IActionResult PostMethod([FromBody] Example model) => Ok();

/* передаём в теле запроса:
{}
или
{"Name": null}
или
{"Name": ""}
или
{"Name": "   "}
Получаем BadRequest с текстом ошибки. */

// передаём в теле запроса: {"Name": "name"}. Получаем OK.

// Вывод: атрибут Required нужен для пользователя

Как стоит и не стоит использовать.

public class BadExample
{
    public required string Field1 { get; set; } // 1
    
    public required string? Field2 { get; set; } // 2
    
    [Required]
    public required string Field3 { get; set; } // 3

    [Required]
    public string? Field4 { get; set; } // 4

    [Required]
    public int Field5 { get; set; } // 5

    public required int Field6 { get; set; } = 10; // 6
      
    public required List<int> Field7 { get; set; } // 7
}
  1. Ошибка, если класс используется как входящий параметр в эндпоинте. Соответственно, не стоит использовать, если десериализуем в него.

  2. Либо required, либо nullable.

  3. Надо выбрать одно из двух в зависимости от места использования.

  4. Либо Required, либо nullable. Тут даже AllowEmptyStrings = true не поможет.

  5. Required используется для строк. Но есть нюанс (*).

  6. Не нужно использовать required со значением по умолчанию.

  7. Не стоит усложнять жизнь, если поле можно проинициализировать при создании класса.

public class GoodExample
{
    public required string Field1 { get; set; } // 1
    
    [Required]
    public string Field2 { get; set; } = null!; // 2
    
    public string? Field3 { get; set; } // 3

    public int Field4 { get; set; } // 4

    public List<string> Field5 { get; set; } = []; // 5
}
  1. Хорошо где угодно за пределами эндпоинтов и десериализации, а значение не может принимать null.

  2. То что нужно для эндпоинта.

  3. Поле nullable. Поэтому никаких required.

  4. Не используем атрибут Required с не строками. Но есть нюанс (*).

  5. Избегаем использование required, проинициализировав коллекцию.

* - если передаётся json, в котором явно указано значение null ({"Field4": null}), то использование атрибута Required вернёт BadRequest.
Если же в json поле было опущено, то будет присвоено значение по умолчанию.

Надеюсь, это поможет сделать код чище и избежать неоднозначностей.

Теги:
Всего голосов 1: ↑1 и ↓0+1
Комментарии0

Хочу немного поплакаться о поиске работы в IT…

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

Я — .NET backend-разработчик с опытом 6+ лет. Казалось бы, с таким бэкграундом я должен быть для HR вполне «лакомым кусочком». Но реальность оказалась совсем другой.

Постоянные отказы без объяснений. При том что резюме — нормальное, вычитанное, со смыслом. Все, кому я его показывал, говорили то же самое. И это, разумеется, бьёт по самооценке: появляется ощущение, что никому не нужен.

Складывается впечатление, что компании ищут кандидатов, которые совпадают с их стеком на 100%, и им плевать, что ту или иную технологию можно подтянуть за пару вечеров. Иначе я не могу объяснить, почему в вакансиях требуют конкретные библиотеки вроде Dapper или Autofac, которые изучаются буквально за вечер.

Есть ChatGPT и Gemini — хочешь, они разжуют любую тему. Но если в резюме не указал какую-то библиотеку — красный флаг. Отказ.
«Мы лучше ещё пару месяцев поищем кандидата, который совпадает на 100%, чем возьмём тебя». Примерно так это и выглядит.

А собеседования — отдельная песня.
По три этапа, каждый по часу. И ладно бы их можно было пройти за неделю, так нет — между этапами неделя перерыва. За это время уже как-то всё равно становится, возьмут тебя или нет. А когда после третьего этапа тебе в итоге не дают оффер — ощущение, что просто выкинул время в мусорку.

Про технические интервью я вообще молчу. Иногда спрашивают такую дичь, которая в реальной работе не нужна от слова «совсем». Например, у меня спросили разницу между структурой и классом. Я ответил по классике: копирование, расположение в памяти, наследование. Но от меня ожидали услышать ещё про блок синхронизации в объекте и почему структуры нельзя использовать в lock.
Может, я и читал это когда-то у Шилдта или Троелсена, но в реальной работе это знание ни разу не пригодилось.

А лайв-кодинг — это вообще отдельный вид боли. Ты сидишь и пишешь код с ощущением, что кто-то стоит у тебя за спиной и дышит в затылок. Ни тебе привычного темпа, ни спокойной среды, ни возможности просто подумать головой. Вместо того чтобы показать реальные навыки, ты занимаешься акробатикой под взглядами людей, которые ждут от тебя идеальных движений в условиях, максимально далёких от рабочих.

И вот главный вопрос: зачем столько этапов?
Человек с каждым собеседованием становится опытнее, у него подвешивается язык, он понимает, что сейчас в моде спрашивать. Если где-то не ответил — проработает и на следующем собесе уже ответит. Так зачем превращать процесс в марафон длиной в месяц?

Да и честно говоря, всё равно все на собеседованиях врут. Потому что попробуй скажи правду — например:
«Мне надоело бесконечно ковыряться в легаси, поэтому я решил уйти».
Мгновенно получишь красный флаг.

Вместо честного диалога приходится придумывать какие-то «правильные» формулировки про профессиональный рост, новые вызовы и желание расширить экспертизу. И это тоже превращает собеседования в театр, где все играют роли: кандидат делает вид, что не устал от банального бардака в кодовой базе, а компания — что у них идеальная инженерная культура.

А ещё последнее время любят оправдывать весь этот цирк «защитой от вкатунов с курсов».
Только проблема в том, что эта защита вообще не работает.

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

В итоге фильтруют не тех, кого нужно, а просто создают лишние барьеры.

Хотите проверить, что кандидат — не выпускник трёхнедельного курса? Попросите показать трудовую книжку. Я покажу без проблем — подтвердить реальные годы опыта для меня не проблема.

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

Теги:
Всего голосов 8: ↑8 и ↓0+11
Комментарии6

Проверяем osu! и рассказываем про фишки статических анализаторов

Про существование инструментов статического анализа известно многим, но почему их часто используют и в чём конкретно заключается практическая польза? В этот раз мы предлагаем рассмотреть несколько основных особенностей этого инструмента на примере анализа исходного кода игры osu!

Первая особенность: экономит время

Одной из особенностей статических анализаторов является возможность сэкономить время на код-ревью за счёт схожего подхода (просмотра исходников), только за вас всё делает инструмент :)

Предлагаю начать с небольшой разминки: сможете ли вы самостоятельно найти ошибку?

public partial class TopScoreStatisticsSection
  : CompositeDrawable
{ 
  public ScoreInfo Score
  {
    ....

    if (score == null && value == null) 
      return;

    if (score?.Equals(value) == true)
      return;

    score = value;

    accuracyColumn.Text = value.DisplayAccuracy;

    maxComboColumn.Text = value.MaxCombo
                               .ToLocalisableString(@"0\x");

    ppColumn.Alpha = value.BeatmapInfo!
                          .Status
                          .GrantsPerformancePoints() ? 1 : 0;

   
  }
}

Если нужна подсказка или хотите убедиться в своём варианте, можно посмотреть на предупреждение PVS-Studio:

V3125 [SEC-NULL] The 'value' object was used after it was verified against null. Check lines: 128, 120. TopScoreStatisticsSection.cs 128

Нашли? Ну я в вас и не сомневался :)

Для протокола давайте всё же разберём, что произошло. Выглядит как логическая ошибка, а их не так просто заметить, потому что не все захотят высматривать все возможные сценарии. В нашем же случае один из них сразу может вызвать проблемы.

В начале есть две проверки.

Первая проверка:

if (score == null && value == null)
  return;

Вторая проверка:

if (score?.Equals(value) == true)
  return;

Скорее всего, они предназначались для обработки двух переменных по разным сценариям (если score = null, если value = null, если они равны и т. д.). Но вот если комбинация будет score = "NotNull" и value = null, то первая и вторая проверки отработают без выхода из метода, и мы пойдём дальше по коду, где непременно наткнёмся на разыменовывание свежеполученного null

accuracyColumn.Text = value.DisplayAccuracy;
maxComboColumn.Text = value.MaxCombo.ToLocalisableString(@"0\x");

А это, в свою очередь, может привести к исключению NullReferenceException.

Хотите узнать еще?
Если вас заинтересовало какие еще есть особенности статических анализаторов и что еще мы смогли найти в osu! То предлагаю прочитать полную версию статьи.

Теги:
Всего голосов 5: ↑4 и ↓1+3
Комментарии0

Статья "Код блокчейн-проектов Neo и NBitcoin VS анализатор кода. Кто-кого?"

PVS-Studio ворвался в мир блокчейн-разработки, и первыми "под удар" попали open source проекты на C# — Neo и NBitcoin!

В статье мы рассмотрели самые интересные ошибки: как явные, так и потенциальные, которые нашли в этом проекте. Если вам интересно, какие ошибки могут находить такие инструменты, как PVS-Studio, или вы желаете прокачать свой собственный "ментальный анализатор", приглашаю к прочтению :)

Теги:
Всего голосов 5: ↑5 и ↓0+6
Комментарии0

Определение типа fullscreen

Понадобилось мне как‑то определять тип fullscreen для вывода подходящего типа уведомления (звуковое/системное/окном) во время запущенных игр и родился такой код:

    public enum WindowFullscreenState
    {
        None,
        Emulated,
        Shared,
        Exclusive,
        ExclusiveGdi,
        NotOwned
    }

    public static WindowFullscreenState GetForegroundWindowFullscreenType()
    {
        var result = WindowFullscreenState.None;   
        uint dwProcessId;
        D3DKMT_QUERYVIDPNEXCLUSIVEOWNERSHIP query = new D3DKMT_QUERYVIDPNEXCLUSIVEOWNERSHIP
        {
            hWindow = GetForegroundWindow()
        };

        GetWindowThreadProcessId(query.hWindow, out dwProcessId);

        query.hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, dwProcessId);
        if (query.hProcess != IntPtr.Zero)
        {
            D3DKMTQueryVidPnExclusiveOwnership(ref query);
            CloseHandle(query.hProcess);

            result = query.OwnerType switch
            {
                D3DKMT_VIDPNSOURCEOWNER_TYPE.D3DKMT_VIDPNSOURCEOWNER_UNOWNED => WindowFullscreenState.NotOwned,
                D3DKMT_VIDPNSOURCEOWNER_TYPE.D3DKMT_VIDPNSOURCEOWNER_SHARED => WindowFullscreenState.Shared,
                D3DKMT_VIDPNSOURCEOWNER_TYPE.D3DKMT_VIDPNSOURCEOWNER_EXCLUSIVE => WindowFullscreenState.Exclusive,
                D3DKMT_VIDPNSOURCEOWNER_TYPE.D3DKMT_VIDPNSOURCEOWNER_EXCLUSIVEGDI => WindowFullscreenState.ExclusiveGdi,
                D3DKMT_VIDPNSOURCEOWNER_TYPE.D3DKMT_VIDPNSOURCEOWNER_EMULATED => WindowFullscreenState.Emulated,
                _ =>WindowFullscreenState.None
            };
        }

        return result;
    }

    public enum D3DKMT_VIDPNSOURCEOWNER_TYPE
    {
        D3DKMT_VIDPNSOURCEOWNER_UNOWNED = 0,
        D3DKMT_VIDPNSOURCEOWNER_SHARED = 1,
        D3DKMT_VIDPNSOURCEOWNER_EXCLUSIVE = 2,
        D3DKMT_VIDPNSOURCEOWNER_EXCLUSIVEGDI = 3,
        D3DKMT_VIDPNSOURCEOWNER_EMULATED = 4
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct LUID
    {
        public uint LowPart;
        public int HighPart;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct D3DKMT_QUERYVIDPNEXCLUSIVEOWNERSHIP
    {
        public IntPtr hProcess;
        public IntPtr hWindow;
        public uint VidPnSourceId;
        public LUID AdapterLuid;
        public D3DKMT_VIDPNSOURCEOWNER_TYPE OwnerType;
    }

    [DllImport("user32.dll", SetLastError = true)]
    private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool CloseHandle(IntPtr hObject);

    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    private static extern int MessageBoxW(IntPtr hWnd, string lpText, string lpCaption, uint uType);

    private const uint PROCESS_QUERY_LIMITED_INFORMATION = 0x1000;

    [DllImport("gdi32.dll", EntryPoint = "D3DKMTQueryVidPnExclusiveOwnership")]
    private static extern int D3DKMTQueryVidPnExclusiveOwnership(ref D3DKMT_QUERYVIDPNEXCLUSIVEOWNERSHIP data);
    
    [DllImport("user32.dll")]
    public static extern IntPtr GetForegroundWindow();

Схема использования с типами уведомлений:

WindowFullscreenState.Exclusive -> SendVoiceNotification
WindowFullscreenState.ExclusiveGdi -> SendVoiceNotification
WindowFullscreenState.Emulated -> ShowSystemNotification
WindowFullscreenState.Shared -> ShowSystemNotification
WindowFullscreenState.NotOwned || WindowFullscreenState.None -> IsForegroundWindowSmallerThanScreen ? ShowCustomWindow : ShowSystemNotification

Подробнее можно прочитать про виды захвата экрана тут и тут.

P.S. Некоторые игры (ExclusiveGdi) можно заставить (Emulated) получать системные уведомления через режим совместимости.

Теги:
Всего голосов 5: ↑4 и ↓1+3
Комментарии0

.NET Digest #9

Рады вам представить девятый выпуск нашего дайджеста, посвящённого новостям и событиям в мире .NET!

В этот раз мы расскажем про новые preview и RC версии .NET 10 и новую Visual Studio 2026. А ещё мы собрали для вас несколько интересных статей:

  • про производительность в .NET от Стивена Тауба;

  • спонсорство на NuGet.org;

  • и статистику популярности языков от GitHub.

Команда PVS-Studio рада представить вам подборку самых интересных и полезных материалов! Подробнее читайте в нашей статье.

Теги:
Всего голосов 4: ↑4 и ↓0+4
Комментарии0

Парсинг Сохранённых сообщений Телеграм в локальный каталог

Всем привет. Позвольте рассказать вам, как скачать содержимое Сохранённых сообщений Телеграм к себе на ПК.

Для начала, ознакомьтесь с предыдущей статьёй - Парсинг чатов Телеграм. В ней описан процесс установки и первичной настройки десктоп клиента. Кратко, что у вас должно быть настроено:
1. Путь к хранилищу (локальная БД SQLite).
2. Путь к файлу сессии (в нём сохраняется служебная текущая сессия подключения).
3. Регистрация приложения на сайте Телеграм.
4. Настройки подключения клиента (хранится в таблице приложений).

Страница настроек
Страница настроек

После успешного подключения к Телеграм, откроется доступ к меню Сохранённые сообщения. Заходим на третью вкладку Скачать, кликаем по кнопке Сбросить по-умолчанию, указываем локальный каталог на диске для скачивания файлов (например C:\OpenTgResearcher\SavedMessages). По необходимости, отредактируем первый ИД и количество потоков. Всё готово, кликаем по кнопке Запустить парсинг Телеграм. После чего можно идти пить кофе, пока ожидаем результат работы парсинга. Файлы будут скачаны в локальный каталог, а сообщения в соответствующую таблицу, их можно будет посмотреть на вкладке Содержимое.

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

🧠 Стек технологий:
- Разработка ПО: Console, ASP.NET Core Web API, Blazor, WinForms, WPF, UWP, WinUI
- Хранение и передача информации: JSON, XML, SQLite
- БД и ORM: MS SQL Server / PostgreSQL / SQLite, EF Core
- Веб технологии: REST API / RESTful API, HTTP, TCP/IP, HttpClient, WebSocket
- Брокеры сообщений: RabbitMQ (готов быстро освоить Kafka)
- Контейнеризация: Docker / Compose (готов быстро освоить Kubernetes)
- Архитектура ПО: ООП, шаблоны проектирования (Design Patterns)
- Архитектурные подходы: TDD, DRY, KISS, SOLID, YAGNI, Clean Architecture, N-Tier Architecture
- Фронт: небольшой опыт разработки Angular

Теги:
Всего голосов 3: ↑2 и ↓1+1
Комментарии4

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

Проблема часто кроется в отсутствии гибкой архитектуры. Ключ к её созданию — грамотное использование интерфейсов в C#.

17 октября в 16:00 (Мск) на бесплатном вебинаре «Основы интерфейсов C#: первые шаги к гибкой архитектуре» на простых и понятных примерах разберём:

✔️ Что такое интерфейс на самом деле и почему это не просто «контракт».

✔️ Чем интерфейс отличается от класса — убережем от главной ошибки новичков.

✔️ Как правильно объявлять и реализовывать интерфейсы в C#.

✔️ Как интерфейсы делают ваш код гибким, тестируемым и готовым к изменениям.

Этот вебинар — важный шаг от написания кода, который «просто работает», к созданию архитектуры, которая «легко масштабируется».

📅 Дата: 17 октября 2025 г.

🕓 Время: 16:00 - 17:00 (Мск)

➡️ Зарегистрироваться

Теги:
Рейтинг0
Комментарии0

Ближайшие события

Парсинг чатов Телеграм

Всем привет. Позвольте показать вам как скачать содержимое чата Телеграм к себе на ПК, включая сообщения.

Нам понадобится скачать программу OpenTgResearcher. Исходные коды лежат на ГитХаб. Комьюнити лицензию можно получить бесплатно в автоматическом режиме через меню программы. Скачать можно стабильный релиз, либо самую новую превью версию в разделе релизов. Есть несколько вариантов установки: консольная версия (кроссплатформенная), докер версия (для продвинутых), десктоп версия (под Windows).

Установим десктоп версию. Для этого нам понадобится дистрибутив. В списке файлов релиза найдите OpenTgResearcherDesktop‑win‑x64-Setup.exe (64-разрядная архитектура ПО). Браузер может запросить разрешение на скачивание и сохранение файла, а также предупредить, что файлы редко скачиваются. После чего, Windows предупредит вас, что файл скачан из интернета и спросит разрешение на установку. Для проверки дистрибутива на вирусы можете воспользоваться онлайн сервисом проверки VirusTotal. По завершении установки ПО вы должны увидеть загрузочную страницу. Из которой вы можете попасть на главную страницу.

Главная страница
Главная страница

Поздравляю, вы справились! Теперь необходимо настроить локальные каталоги. В главном меню слева выбираем Settings. В них вы можете задать путь к хранилищу (Storage: c:\OpenTgResearcher\TgStorage\TgStorage.db) и путь к файлу сессии(Session: C:\Users\<userName>\AppData\Local\OpenTgResearcherDesktop\current\OpenTgResearcher.session). После чего, сохраняем настройки и закрываем ПО, запускаем заново.

Итак, у нас есть локальное хранилище и путь к файлу сессии. Теперь нам необходимо получить авторизацию от самого Телеграм и сохранить сессию. Для этого, в главном меню слева открываем Client connection. Кликаем по ссылке my.telegram.org, в открывшейся странице браузера, вводим свой номер телефона, код авторизации получаем в боте Telegram Service Notifications. Далее, мы заполняем поля App title, Short name. Выписываем поля App api_id, App api_hash. Переходим обратно в OpenTgResearcher и заполняем поля API hash, API ID, Phone number. Эти данные сохранятся в локальном хранилище в таблице APPS. Если вы используете пароль, то заполните поле Password (это поле не сохраняется в таблице, если сессия протухнет, придётся вводить заново). После чего нажмите кнопку Connect to TG server. Возможно, к вам снова придёт код авторизации, в таком случае, его необходимо скопировать и вставить в поле Login code. После чего снова нажать Connect to TG server. В случае успеха — поле Connection state изменит своё положение на крайнее правое и описание рядом Client is connected, а также заполнятся остальные поля блока Settings without saving. В случае ошибок, смотрим содержимое поля Data request и запрашиваем помощь в Телеграм группе.

Что дальше? Просканируем доступные каналы и группы. Для этого в главном меню слева кликнем по меню Chats. Изначально список будет пуст. Заполним его, кликнув по кнопке Online update. Вы увидите прогресс парсинга ваших чатов. Сверху отображается текущий статус чтения, сколько чатов всего. Снизу отображается защитный механизм Телеграм — Flood Control. После определённой порции данных, Телеграм просит подождать несколько секунд, эта информация обновляется автоматически. По завершении парсинга, можно открыть любой чат, дважды кликнув по строке с именем. В открывшейся странице деталей чата настроим каталог для сохранения медиа (Directory). Если чат содержит темы, то можете включить переключатель Creating subdirectories. Если нам нужны комментарии - включите переключатель Parsing comments. Далее сохраним настройки — Save chat settings. Поздравляю, всё готово для скачивания — жмём кнопку Online update. Запустится процесс скачивания медиа и парсинга сообщений. В случае отмены — нажмите кнопку Stop download. Прогресс отображается в строках Message и Media.

Это мой первый пост на хабре. Оставляйте свои комментарии и пожелания.

Теги:
Всего голосов 2: ↑1 и ↓1+2
Комментарии8

Мне надоели платные приложения для учета расходов, поэтому я сделал свое: бесплатное и с открытым исходным кодом.

Согласитесь, идея довольно проста: мобильное приложение, в котором можно было бы записывать свои расходы и строить какие-нибудь планы по тратам. Количество таких инструментов в магазинах приложений, по моим ощущениям, сравнимо с количеством самописных будильников и калькуляторов - их довольно много.

Я перепробовал десятки решений, но в каждом находил какие-то ограничения. Где-то были платные подписки или ограниченный функционал, где-то требовалась регистрация и данные о моих расходах улетали на сервера (зачем?), а где-то приложение просто не подходило мне по функционалу. В итоге я решил уйти от готовых приложений к учету в таблицах Excel. Но такой подход тоже был неудобным, и я понял, что пора создать свое собственное приложение: приватное, бесплатное и, самое главное, открытое.

В общем, так появилось Profitocracy - бесплатное Open Source мобильное приложение, написанное на .NET MAUI. Его главная цель — помочь пользователям организовать учет личных расходов по популярному правилу 50-30-20, а также обеспечить конфиденциальность данных. Profitocracy хранит всю информацию локально на вашем устройстве. Приложение не передает никакие данные третьим лицам и полностью свободно от рекламы и монетизации.

Среди основных особенностей приложения я хотел бы выделить:

  • Автоматическое планирование бюджета. Вы указываете дату окончания периода (получение зарплаты, например), и приложение расчитает для вас ежедневные расходы, расходы по типам (по правилу 50-30-20), а также по категориям (которые вы можете создать сами).

  • Индивидуальная настройка. Вы можете создавать собственные профили расходов: для личных нужд, на время отпуска или поездку в командировку. Причем столько сколько вам потребуется.

  • Приватность. Все данные - на вашем устройстве и только. Также, имеется возможность создания бэкапов (выгрузка данных в файл) для переноса данных на другое устройство.

  • Открытый исходный код. Исходный код проекта выложен на GitHub (ссылка на репозиторий). Каждый может внести свой вклад, предложить новый функционал или изучить как работает приложение.

  • Кроссплатформенность. Приложение доступно как для Android, так и для iOS.

  • Перевод на несколько языков. Profitocracy поддерживает русский, английский и другие языки.

На момент написания поста проект завоевал небольшой, но значимый отклик в сообществе разработчиков:

  • 100+ звезд на GitHub;

  • 20+ форков;

  • 3 активных контрибьютора. Причем, это не мои друзья :)

Ссылки на скачивание:

Буду рад вашим отзывам, предложениям и комментариям как на GitHub, так и здесь. Надеюсь, Profitocracy поможет вам так же, как оно помогло мне!

Теги:
Всего голосов 9: ↑9 и ↓0+14
Комментарии2

🎲 Обновление бота для планирования настолок и DnD

Привет! Я сделал телеграм-бота, который помогает быстро и удобно планировать игровые сессии с друзьями — будь то настолки, DnD или что-то ещё.

🎲 Обновление бота для планирования настолок и DnD
🎲 Обновление бота для планирования настолок и DnD

Идея проста:

  • создаёте группу в боте,

  • участники отмечают, когда они свободны,

  • бот показывает, в какое время совпадают все. Больше никаких бесконечных переписок в чатах, когда «мне удобно только в среду», а у кого-то уже пятница.

💡 Что нового в последнем обновлении Я выпустил большой апдейт, который особенно пригодится игрокам в настолки и DnD:

  • ⏳ Дата окончания сессии — теперь можно указать полный промежуток времени, когда вы свободны. Например, с 14:00 до 18:00.

  • 🔁 Повторяющиеся сессии — один раз указали «понедельник с 14 до 16», и эта запись будет актуальна каждую неделю.

  • 🔔 Настраиваемые уведомления — можно включать только те напоминания, что нужны, и отключать ненужные.

  • 🗓 Новое напоминание — за день до игры бот предупредит, чтобы вы точно не забыли.

📱 Если хотите попробовать — бот бесплатный, работает прямо в Telegram.
Ссылка на бота - @game_session_scheduler_bot
Ссылка на канал с обновлениями - https://t.me/gamescheduler

Теги:
Всего голосов 1: ↑1 и ↓0+1
Комментарии0

ChatCaster - приложение для голосового ввода с помощью геймада.

Друзья, всех приветсвтую!

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

Бонусом приложение поддерживает перевод на 5 языков (самые популярые в steam). Вы просто выбираете нужный вам язык, говорите на своем родном, весь текст будет переведен и введен там где вам нужно.

Если тут есть .net разработчики которые захотят присоединиться и доработать пару фишек или внести что-то свое, буду только благодарен.

Приложение делал один в рамках пет проекта, съело много времени, пока не могу себе позволить его развивать. Нужно дальше обучаться кодить и искать работу.

Короткое видео что оно умеет делать вот тут https://youtu.be/p_exJzcF1so

Ссылка на репозиторий если кто-то захочет поучаствовать https://github.com/KOMMEHTATOP/ChatCaster

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

Пост делаю впервые, АИ для текста не использую намеренно, готов к конструктивной критике или предложениям.

Буду рад если приложение окажется кому-то полезным.

Теги:
Всего голосов 3: ↑3 и ↓0+4
Комментарии0

Есть для альтернатива Delphi в 2025 году для простых кроссплатформенных приложений?

Навеяно обсуждением статьи про Дельфи в 2025.

Q1: Если сейчас есть задача по-быстрому сбацать что то с формами под винду - какая есть альтернатива дельфи?

Q2: А если так же быстро накидать, только кроссплатформенное приложение и без зависимостей?

И мой ответ

A: Не находишь, что 3000$ за кроссплатформенный дизайнер форм слишком дорого? Даже за хороший.

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

Все может использоваться бесплатно и без особых претензий на функциональность. Единственное, иногда бывает нужно еще нарисовать какой то чарт/график и загрузить/записать данные в БД/XML/JSON - с этим могут быть нюансы с конкретным вариантом.

С чем то я работал, с чем то нет, актуальные версии вживую не проверял.

  1. GNOME Builder (ex.Anjuta). GTK multilang IDE

  2. Cambalache (ex.Glade) - GTK form builder

  3. Qt Creator

  4. FLUID for FLTK

  5. wxFormBuilder for wxWidgets 

  6. Projucer for JUCE

  7. Ultimate++ 

  8. NetBeans GUI design tool for Java Swing 

  9. TKproE (TCL/TK Programming Environment)

  10. Lazarus

  11. MSEide+MSEgui Pascal

  12. GTK# Visual Designer MonoDevelop (retired)

  13. Xamarin.Forms GTK Backend (discontinued for NET MAUI)

  14. JavaFX Scene Builder

  15. Pygubu Tkinter a GUI for Python

    Может еще что и забыл, либо не попалось на глаза.

Теги:
Всего голосов 8: ↑4 и ↓40
Комментарии15

Обновление бота: поддержка часовых поясов и улучшения стабильности!

Привет! Вышло крупное обновление бота, и вот что нового:

Ссылка на бота - @game_session_scheduler_bot
Ссылка на канал с обновлениями бота - https://t.me/gamescheduler

🕒 Поддержка часовых поясов
Теперь у каждой группы есть свой часовой пояс - по нему отправляются все уведомления.
У каждого пользователя - свой часовой пояс, и он видит все даты и время в привычном формате.
Это большое изменение, так что если что-то работает не так - обязательно напишите в канал или оставьте фидбек через бота.

Напоминание об оценке игроков
Через 2 часа после начала игры приходит уведомление: самое время оценить своих напарников!
Раньше об этом часто забывали, а теперь - точно не забудете.

🔧 Повышена стабильность запросов
Теперь бот лучше справляется с ограничениями Telegram и работает надёжнее, даже если вы активно им пользуетесь.

🧹 Удалена настройка стандартного времени
Она только путала всех - больше этой настройки нет, и нотификаций по ней тоже.

Спасибо, что пользуетесь ботом!

Если возникнут вопросы - пишите в канал или через /feedback. Ваши отзывы помогают делать бота лучше ❤️

Теги:
Всего голосов 2: ↑1 и ↓1+2
Комментарии0

Спасибо за ваш фидбэк!

Благодаря вашим отзывам бот становится лучше с каждым днём.
Нас уже больше 250 - это очень вдохновляет! Вчера вышло обновление для бота и он стал чуточку удобнее.

Вот ссылка на бота - @game_session_scheduler_bot
А вот здесь канал с новостями и обновлениями - https://t.me/gamescheduler

Что нового в этом обновлении:

  • Можно настраивать стандартное время группы, это то за час до которого всегда идёт нотификации о сессии

  • Теперь можно добавлять игру прям во время записи на сессию

  • Больше никакого стандартного списка игр, всё нужно добавлять руками (только для новых групп)

  • Удалять время тоже теперь можно пачкой

Исправления:

  • Игра теперь должна быть уникальной только в рамках одной группы, а не всего бота

  • Исправлены несколько опечаток в русской версии

  • Добавлены кнопки возвращения в меню, внутри некоторых команд

  • Спам сообщением "Enter Command" в общие чаты должен прекратиться

Если захотите предложить новые фичи - пишите прямо в боте через фидбэк или в комментариях тут.

Спасибо, что вы с нами!

Теги:
Всего голосов 2: ↑1 и ↓1+2
Комментарии0

Вклад авторов