{
    "version": "https:\/\/jsonfeed.org\/version\/1.1",
    "title": "Блоги: заметки с тегом проекти",
    "_rss_description": "Автоматически собираемая лента заметок, написанных в блогах на Эгее",
    "_rss_language": "ru",
    "_itunes_email": "",
    "_itunes_categories_xml": "",
    "_itunes_image": false,
    "_itunes_explicit": "no",
    "home_page_url": "https:\/\/blogengine.ru\/blogs\/tags\/proekti\/",
    "feed_url": "https:\/\/blogengine.ru\/blogs\/tags\/proekti\/json\/",
    "icon": false,
    "authors": [
        {
            "name": "Илья Бирман",
            "url": "https:\/\/blogengine.ru\/blogs\/",
            "avatar": false
        }
    ],
    "items": [
        {
            "id": "119851",
            "url": "https:\/\/stefaniuk.website\/all\/create-workplace-messenger-bot\/",
            "title": "Как я делал бота для Facebook workplace",
            "content_html": "<p>Дали мне задачу: написать бота для мессенджера workplace, с помощью которого можно получать уведомления из нашей CRM и управлять разными вещами. Расскажу о разных интересных вещах с которыми я столкнулся. Бота писал с помощью ASP.NET Core Web API.<\/p>\n<h2>Вебхуки<\/h2>\n<p>Для того чтобы бот мог обрабатывать запросы и сообщения от Facebook нам надо настроить вебхуки. Webhook — механизм оповещения системы о событиях. Для того чтобы Facebook принял наш хук, он должен обрабатывать как GET так и POST запросы.<\/p>\n<p class=\"note\"><a href=\"https:\/\/developers.facebook.com\/docs\/messenger-platform\/getting-started\/webhook-setup\">Подробнее о Webhook в официальной документации<\/a><\/p>\n<p>GET запрос служит для валидации работы нашего эндпоинта. POST принимает данные связанные с активностью пользователя, будь то нажатие на кнопки или другая активность.<\/p>\n<p class=\"cut-button\">Показать код метода-обработчика GET запроса<\/p>\n<div class=\"cut-content\"><pre class=\"e2-text-code\"><code class=\"\">public IActionResult Receive(\u2028[FromQuery(Name = &quot;hub.mode&quot;)] string mode,\u2028\r\n    [FromQuery(Name = &quot;hub.challenge&quot;)] string challenge,\u2028\r\n    [FromQuery(Name = &quot;hub.verify_token&quot;)] string verifyToken)\r\n{\u2028\r\n   if (string.IsNullOrEmpty(verifyToken)) {\u2028\r\n       return Unauthorized();\u2028\r\n    }\u2028\u2028\r\n    if (verifyToken.Equals(FacebookEnvironment.FacebookVToken)) {\u2028\r\n        return Ok(challenge);\u2028\r\n    }\u2028\r\n    return Unauthorized();\u2028\r\n}<\/code><\/pre><p>В качестве verify_token используется токен, который мы указали при регистрации нашего хука.<\/p>\n<\/div><p class=\"cut-button\">Показать код метода-обработчика POST запроса<\/p>\n<div class=\"cut-content\"><pre class=\"e2-text-code\"><code class=\"\">public async Task&lt;IActionResult&gt; Receive([FromBody]FbResponse response = null)\r\n{\r\n    if (response is null) {\r\n        return BadRequest();\r\n    }\r\n\r\n    if (response.Object != &quot;page&quot;) {\r\n        return Ok();\r\n    }\r\n\r\n    foreach (var entry in response.Entries) {\r\n        foreach (var message in entry.Messaging) {\r\n            await PrepareMessageAsync(message);\r\n        }\r\n    }\r\n    return Ok(&quot;EVENT_RECEIVED&quot;);\r\n}<\/code><\/pre><\/div><h2>Авторизация запросов Facebook<\/h2>\n<p>Для авторизации Facebook использует специальный http заголовок (X-Hub-Signature), в нем он передает некую сигнатуру с помощью которой мы можем авторизовать запрос. Для того чтобы добавить такую функциональность в наш контроллер, добавим фильтр.<\/p>\n<p class=\"cut-button\">Пример кода, для проверки подписи<\/p>\n<div class=\"cut-content\"><pre class=\"e2-text-code\"><code class=\"\">private const string Sha1Prefix = &quot;sha1=&quot;;\r\n\r\npublic static bool Validate(string signature, string contentString) {\r\n    if (!signature.StartsWith(Sha1Prefix, StringComparison.OrdinalIgnoreCase)) {\r\n        return false;\r\n    }\r\n    var secret = Encoding.ASCII.GetBytes(FacebookEnvironment.AppSecret);\r\n    var signatureWithoutPrefix = signature.Substring(Sha1Prefix.Length);\r\n    var content = Encoding.ASCII.GetBytes(contentString);\r\n    return GetIsHashValid(secret, signatureWithoutPrefix, content);\r\n}\r\n\r\nprivate static bool GetIsHashValid(byte[] secret, string signature, byte[] content) {\r\n    using var hmac = new HMACSHA1(secret);\r\n    var hash = hmac.ComputeHash(content);\r\n    var hashString = ToHexString(hash);\r\n    return hashString.Equals(signature);\r\n}\r\n\r\nprivate static string ToHexString(IReadOnlyCollection&lt;byte&gt; bytes)\r\n{\r\n    var builder = new StringBuilder(bytes.Count * 2);\r\n    foreach (var b in bytes)\r\n    {\r\n        builder.AppendFormat(&quot;{0:x2}&quot;, b);\r\n    }\r\n\r\n    return builder.ToString();\r\n}<\/code><\/pre><\/div><h2>Тестирование бота<\/h2>\n<p class=\"note-md\"><a href=\"http:\/\/winitpro.ru\/index.php\/2017\/11\/03\/ustanovka-besplatnogo-ssl-sertifikata-lets-encrypt-na-iis-v-windows-server-2012-r2\/\">Как установить letsencrypt сертификат для IIS<\/a>. Если же вы используете связку в виде ubuntu и nginx вам подойдет <a href=\"https:\/\/www.digitalocean.com\/community\/tutorials\/nginx-let-s-encrypt-ubuntu-18-04-ru\">эта инструкция<\/a>.<\/p>\n<p>Для тестирования нужно развернуть бот на сервере, который смотрит в мир. Также необходимо чтобы у сервера было доменное имя и валидный SSL сертификат. В моем случае, в качестве сервера выступала машина на винде, так как другой внутри нашей сети не было. Как мне показалось захостить приложение написанное на .NET Core намного проще под Ubuntu + nginx нежели под Windows + IIS. В качестве поставщика сертификатов выбрал letsencrypt, так как они предоставляют бесплатный сертификат на 3 месяца, с возможностью дальнейшего обновления.<\/p>\n",
            "date_published": "2019-11-26T19:22:26+05:00",
            "date_modified": "2023-06-03T05:26:31+05:00",
            "tags": [
                "asp.net core",
                "dotnet",
                "програмування",
                "проекти",
                "робота"
            ],
            "author": {
                "name": "Bohdan Stefaniuk",
                "url": "https:\/\/stefaniuk.website\/",
                "avatar": "https:\/\/stefaniuk.website\/pictures\/userpic\/userpic@2x.jpg?1565716580"
            },
            "_date_published_rfc2822": "Tue, 26 Nov 2019 19:22:26 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "119851",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "119849",
            "url": "https:\/\/stefaniuk.website\/all\/e2-power-ups\/",
            "title": "Эгея Power-Ups",
            "content_html": "<p>Решил опубликовать несколько своих доработок, которые добавляют в движок новые функции. Вы можете их свободно использовать в любых ваших целях.<\/p>\n<h2>Список улучшений<\/h2>\n<ol start=\"1\">\n<li>Кнопка «подробнее» или кат внутри заметок<\/li>\n<li>Заметки на полях<\/li>\n<li>Хайлайты<\/li>\n<li>Мини галерея<\/li>\n<\/ol>\n<h2>Кнопка «подробнее» или кат<\/h2>\n<p>Когда пишешь статью и у тебя много исходников, удобно их скрыть под кат, чтобы они не увеличивали размер статьи не отвлекали от текста. Для этого добавил поддержку катов внутри заметок. Для этого нужно сделать 2 вещи:<\/p>\n<ol start=\"1\">\n<li>Пометить текст, который будет выступать в качестве кнопки: .cut-button Название кнопки.<\/li>\n<li>Следующим элементов разместить блок div с классом cut-content и в него поместить нужный контент для скрытия.<\/li>\n<\/ol>\n<p>Как это выглядит в коде:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">.cut-button Два лучших вопроса для собеседования\r\n&lt;div class=&quot;cut-content&quot;&gt;\r\nКак бы вы описали идеального кандидата на эту должность?\r\nКак вы будете судить, хорош ли я в том, что потребуется от меня через 1-3 месяца?\r\n&lt;\/div&gt;<\/code><\/pre><p>Как это работает<\/p>\n<p class=\"cut-button\">Два лучших вопроса для собеседования<\/p>\n<div class=\"cut-content\"><p>Как бы вы описали идеального кандидата на эту должность?<br \/>\nКак вы будете судить, хорош ли я в том, что потребуется от меня через 1-3 месяца?<\/p>\n<\/div><h2>Стилизация в виде карточке<\/h2>\n<p>Если необходимо выделить какой-то блок текста в заметке можно использовать хайлайты. Для этого достаточно обернуть текст, который хотите выделить в div с классом highlight.<\/p>\n<p>Как выглядит в коде:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">&lt;div class=&quot;highlight&quot;&gt;\r\nКак бы вы описали идеального кандидата на эту должность?\r\nКак вы будете судить, хорош ли я в том, что потребуется от меня через 1-3 месяца?\r\n&lt;\/div&gt;<\/code><\/pre><p>Как работает<\/p>\n<div class=\"highlight\"><p>Как бы вы описали идеального кандидата на эту должность?<br \/>\nКак вы будете судить, хорош ли я в том, что потребуется от меня через 1-3 месяца?<\/p>\n<\/div><h2>Заметки на полях<\/h2>\n<p class=\"note\">Блог с реализацией заметок: <a href=\"http:\/\/mopsicus.ru\/all\/aegea-field-notes\/\"><a href=\"http:\/\/mopsicus.ru\">http:\/\/mopsicus.ru<\/a><\/a>.<\/p>\n<p>Эту функциональность я подсмотрел в другом блоге и решил перенести в свой. Пригодится, когда нужно указать ссылку на ресурс по теме или просто примечание. Также добавил возможность указывать тип заметки. По умолчанию заметка не занимает отдельный абзац в тексте.<\/p>\n<p>Как выглядит в коде:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">.note Блог с реализацией заметок:  ((http:\/\/mopsicus.ru\/all\/aegea-field-notes\/ http:\/\/mopsicus.ru)).\r\n.note-md Блог с реализацией заметок: ((http:\/\/mopsicus.ru\/all\/aegea-field-notes\/ http:\/\/mopsicus.ru)).<\/code><\/pre><p>Как выглядит на мобиле:<\/p>\n<div class=\"category-block\"><div class=\"e2-text-picture\">\n<img src=\"https:\/\/stefaniuk.website\/pictures\/large-notes-on-mobile.jpg\" width=\"300\" height=\"165\" alt=\"\" \/>\n<div class=\"e2-text-caption\">Во всю ширину экрана.<\/div>\n<\/div>\n<\/div><div class=\"category-block\"><div class=\"e2-text-picture\">\n<img src=\"https:\/\/stefaniuk.website\/pictures\/original-notes-on-mobile.jpg\" width=\"300\" height=\"165\" alt=\"\" \/>\n<div class=\"e2-text-caption\">Обтекаемый текст.<\/div>\n<\/div>\n<\/div><p><b>Мини галерея<\/b><br \/>\nМожно вывести несколько изображений в ряд. Пока что это реализовано тольк для 3 и 4 картинок. В будущем планирую добавить возможность открывать изображение на весь экран.<\/p>\n<p>Как выглядит в коде:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">&lt;div class=&quot;card-gallery card-gallery-4&quot;&gt;\r\n    &lt;!-- Список ссылок на изображения --&gt;\r\n&lt;\/div&gt;\r\n\r\n&lt;div class=&quot;card-gallery card-gallery-3&quot;&gt;\r\n    &lt;!-- Список ссылок на изображения --&gt;\r\n&lt;\/div&gt;<\/code><\/pre><p>Как выглядит на страницу<\/p>\n<div class=\"card-gallery card-gallery-4\"><div class=\"e2-text-picture\">\n<img src=\"https:\/\/stefaniuk.website\/pictures\/card-gallery-preview.jpg\" width=\"450\" height=\"327\" alt=\"\" \/>\n<\/div>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/stefaniuk.website\/pictures\/card-gallery-preview.jpg\" width=\"450\" height=\"327\" alt=\"\" \/>\n<\/div>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/stefaniuk.website\/pictures\/card-gallery-preview.jpg\" width=\"450\" height=\"327\" alt=\"\" \/>\n<\/div>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/stefaniuk.website\/pictures\/card-gallery-preview.jpg\" width=\"450\" height=\"327\" alt=\"\" \/>\n<\/div>\n<div><div class=\"card-gallery card-gallery-3\"><div class=\"e2-text-picture\">\n<img src=\"https:\/\/stefaniuk.website\/pictures\/card-gallery-preview.jpg\" width=\"450\" height=\"327\" alt=\"\" \/>\n<\/div>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/stefaniuk.website\/pictures\/card-gallery-preview.jpg\" width=\"450\" height=\"327\" alt=\"\" \/>\n<\/div>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/stefaniuk.website\/pictures\/card-gallery-preview.jpg\" width=\"450\" height=\"327\" alt=\"\" \/>\n<\/div>\n<div><h2>Где скачать?<\/h2>\n<p>Все исходники выложил на <a href=\"https:\/\/github.com\/teamkiller7112\/e2-power-ups\">github<\/a>. Там же вы найдете инструкцию по установке.<\/p>\n<h2>PS<\/h2>\n<p>Если у вас есть предложения, оставляйте их в комментариях, а лучше в issue на github-е. Как будет время, займусь реализацией предложений.<\/p>\n<style>\r\n\t.category-block { width: 28%;  display: inline-block; vertical-align: top; margin-right: 1%; margin-bottom: 20px;}\r\n@media (max-width: 700px) {\r\n\t.category-block { width: 100%; margin-right: unset;}\r\n}\r\n<\/style>\n",
            "date_published": "2019-11-25T19:30:51+05:00",
            "date_modified": "2023-06-03T05:24:03+05:00",
            "tags": [
                "Блог",
                "проекти"
            ],
            "author": {
                "name": "Bohdan Stefaniuk",
                "url": "https:\/\/stefaniuk.website\/",
                "avatar": "https:\/\/stefaniuk.website\/pictures\/userpic\/userpic@2x.jpg?1565716580"
            },
            "_date_published_rfc2822": "Mon, 25 Nov 2019 19:30:51 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "119849",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "119835",
            "url": "https:\/\/stefaniuk.website\/all\/first-project\/",
            "title": "Первый проект",
            "content_html": "<p>Нашел в старой переписке скриншоты игры, которую я делал когда только изучал программирование на C#. Игру делал на Unity в сеттинге сталкера. Дальше одной локации и простенького управления не зашло. Жаль что видео не сохранились.<\/p>\n<p>Это был 2014 год.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/stefaniuk.website\/pictures\/Q-uvwDZyHSk.jpg\" width=\"1000\" height=\"562\" alt=\"\" \/>\n<\/div>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/stefaniuk.website\/pictures\/_PBCIo2m35Y.jpg\" width=\"853\" height=\"417\" alt=\"\" \/>\n<\/div>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/stefaniuk.website\/pictures\/cmHa72tOn80.jpg\" width=\"869\" height=\"507\" alt=\"\" \/>\n<\/div>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/stefaniuk.website\/pictures\/DOvFs8xlGKQ.jpg\" width=\"1000\" height=\"504\" alt=\"\" \/>\n<\/div>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/stefaniuk.website\/pictures\/pBN-yhOJ8Pg.jpg\" width=\"1000\" height=\"506\" alt=\"\" \/>\n<\/div>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/stefaniuk.website\/pictures\/vI-KS-BCHDs.jpg\" width=\"1000\" height=\"492\" alt=\"\" \/>\n<\/div>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/stefaniuk.website\/pictures\/zkkjTZuP9Q0.jpg\" width=\"1000\" height=\"481\" alt=\"\" \/>\n<\/div>\n<p>Также пробовал моделировать в 3ds Max.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/stefaniuk.website\/pictures\/rD6sM_gp1bM.jpg\" width=\"1000\" height=\"1000\" alt=\"\" \/>\n<\/div>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/stefaniuk.website\/pictures\/TYDws_mfX00.jpg\" width=\"1000\" height=\"1000\" alt=\"\" \/>\n<\/div>\n",
            "date_published": "2019-06-27T12:53:18+05:00",
            "date_modified": "2023-06-03T05:19:48+05:00",
            "tags": [
                "проекти"
            ],
            "author": {
                "name": "Bohdan Stefaniuk",
                "url": "https:\/\/stefaniuk.website\/",
                "avatar": "https:\/\/stefaniuk.website\/pictures\/userpic\/userpic@2x.jpg?1565716580"
            },
            "_date_published_rfc2822": "Thu, 27 Jun 2019 12:53:18 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "119835",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        }
    ],
    "_e2_version": 4079,
    "_e2_ua_string": "Aegea 11.0 (v4079e)"
}