Богдан Стефанюк

Заметки о программировании, путешествиях, плёнке и разных интересностях
Обо мне  •  Список заметок

Kodak ColorPlus 200 pushed to 400 ISO

Выбрался на прогулку, взяв с собой фотоаппарат, зарядил пленку, но ISO не выставил. Cделал 30 кадров и только тогда заметил что сделал я их с ISO 400. Оказывается ColorPlus может пушится, посмотрел на примеры и вот что получилось у меня. Очень хороший результат как для самой дешевой пленки.

Как я делал бота для Facebook workplace

Дали мне задачу: написать бота для мессенджера workplace, с помощью которого можно получать уведомления из нашей CRM и управлять разными вещами. Расскажу о разных интересных вещах с которыми я столкнулся. Бота писал с помощью ASP.NET Core Web API.

Вебхуки

Для того чтобы бот мог обрабатывать запросы и сообщения от Facebook нам надо настроить вебхуки. Webhook — механизм оповещения системы о событиях. Для того чтобы Facebook принял наш хук, он должен обрабатывать как GET так и POST запросы.

Подробнее о Webhook в официальной документации

GET запрос служит для валидации работы нашего эндпоинта. POST принимает данные связанные с активностью пользователя, будь то нажатие на кнопки или другая активность.

Показать код метода-обработчика GET запроса

public IActionResult Receive(
[FromQuery(Name = "hub.mode")] string mode,

    [FromQuery(Name = "hub.challenge")] string challenge,

    [FromQuery(Name = "hub.verify_token")] string verifyToken)
{

   if (string.IsNullOrEmpty(verifyToken)) {

       return Unauthorized();

    }


    if (verifyToken.Equals(FacebookEnvironment.FacebookVToken)) {

        return Ok(challenge);

    }

    return Unauthorized();

}

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

Показать код метода-обработчика POST запроса

public async Task<IActionResult> Receive([FromBody]FbResponse response = null)
{
    if (response is null) {
        return BadRequest();
    }

    if (response.Object != "page") {
        return Ok();
    }

    foreach (var entry in response.Entries) {
        foreach (var message in entry.Messaging) {
            await PrepareMessageAsync(message);
        }
    }
    return Ok("EVENT_RECEIVED");
}

Авторизация запросов Facebook

Для авторизации Facebook использует специальный http заголовок (X-Hub-Signature), в нем он передает некую сигнатуру с помощью которой мы можем авторизовать запрос. Для того чтобы добавить такую функциональность в наш контроллер, добавим фильтр.

Пример кода, для проверки подписи

private const string Sha1Prefix = "sha1=";

public static bool Validate(string signature, string contentString) {
    if (!signature.StartsWith(Sha1Prefix, StringComparison.OrdinalIgnoreCase)) {
        return false;
    }
    var secret = Encoding.ASCII.GetBytes(FacebookEnvironment.AppSecret);
    var signatureWithoutPrefix = signature.Substring(Sha1Prefix.Length);
    var content = Encoding.ASCII.GetBytes(contentString);
    return GetIsHashValid(secret, signatureWithoutPrefix, content);
}

private static bool GetIsHashValid(byte[] secret, string signature, byte[] content) {
    using var hmac = new HMACSHA1(secret);
    var hash = hmac.ComputeHash(content);
    var hashString = ToHexString(hash);
    return hashString.Equals(signature);
}

private static string ToHexString(IReadOnlyCollection<byte> bytes)
{
    var builder = new StringBuilder(bytes.Count * 2);
    foreach (var b in bytes)
    {
        builder.AppendFormat("{0:x2}", b);
    }

    return builder.ToString();
}

Тестирование бота

Как установить letsencrypt сертификат для IIS. Если же вы используете связку в виде ubuntu и nginx вам подойдет эта инструкция.

Для тестирования нужно развернуть бот на сервере, который смотрит в мир. Также необходимо чтобы у сервера было доменное имя и валидный SSL сертификат. В моем случае, в качестве сервера выступала машина на винде, так как другой внутри нашей сети не было. Как мне показалось захостить приложение написанное на .NET Core намного проще под Ubuntu + nginx нежели под Windows + IIS. В качестве поставщика сертификатов выбрал letsencrypt, так как они предоставляют бесплатный сертификат на 3 месяца, с возможностью дальнейшего обновления.

Эгея Power-Ups

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

Список улучшений

  1. Кнопка «подробнее» или кат внутри заметок
  2. Заметки на полях
  3. Хайлайты
  4. Мини галерея

Кнопка «подробнее» или кат

Когда пишешь статью и у тебя много исходников, удобно их скрыть под кат, чтобы они не увеличивали размер статьи не отвлекали от текста. Для этого добавил поддержку катов внутри заметок. Для этого нужно сделать 2 вещи:

  1. Пометить текст, который будет выступать в качестве кнопки: .cut-button Название кнопки.
  2. Следующим элементов разместить блок div с классом cut-content и в него поместить нужный контент для скрытия.

Как это выглядит в коде:

.cut-button Два лучших вопроса для собеседования
<div class="cut-content">
Как бы вы описали идеального кандидата на эту должность?
Как вы будете судить, хорош ли я в том, что потребуется от меня через 1-3 месяца?
</div>

Как это работает

Два лучших вопроса для собеседования

Как бы вы описали идеального кандидата на эту должность?
Как вы будете судить, хорош ли я в том, что потребуется от меня через 1-3 месяца?

Стилизация в виде карточке

Если необходимо выделить какой-то блок текста в заметке можно использовать хайлайты. Для этого достаточно обернуть текст, который хотите выделить в div с классом highlight.

Как выглядит в коде:

<div class="highlight">
Как бы вы описали идеального кандидата на эту должность?
Как вы будете судить, хорош ли я в том, что потребуется от меня через 1-3 месяца?
</div>

Как работает

Как бы вы описали идеального кандидата на эту должность?
Как вы будете судить, хорош ли я в том, что потребуется от меня через 1-3 месяца?

Заметки на полях

Блог с реализацией заметок: http://mopsicus.ru.

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

Как выглядит в коде:

.note Блог с реализацией заметок:  ((http://mopsicus.ru/all/aegea-field-notes/ http://mopsicus.ru)).
.note-md Блог с реализацией заметок: ((http://mopsicus.ru/all/aegea-field-notes/ http://mopsicus.ru)).

Как выглядит на мобиле:

Во всю ширину экрана.
Обтекаемый текст.

Мини галерея
Можно вывести несколько изображений в ряд. Пока что это реализовано тольк для 3 и 4 картинок. В будущем планирую добавить возможность открывать изображение на весь экран.

Как выглядит в коде:

<div class="card-gallery card-gallery-4">
    <!-- Список ссылок на изображения -->
</div>

<div class="card-gallery card-gallery-3">
    <!-- Список ссылок на изображения -->
</div>

Как выглядит на страницу

Terrasoft → Epam

Сегодня был мой последний день в Террасофте. Проработал здесь 2,2 года (801 день). Это были забавные времена? было много позитивных вещей и часть негативных. Точно я могу сказать одно, я получил массу полезного опыта, новые знакомства, скиллы. А теперь пришло время идти дальше.

Вопросы на собеседованиях

Совсем недавно я проходил ряд собеседований и разобрался, какие вопросы стоит задать компании. Ведь интервьюируют не только вас, но и вы компанию.

Первыми хочу выделить 2 смелых вопроса:

Как бы вы описали идеального кандидата на эту должность?
Как вы будете судить, хорош ли я в том, что потребуется от меня через 1-3 месяца?

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

Еще вопросы

Как выглядит карьерный рост с этой должности?
Как бы вы описали культуру внутри компании, команды?
Как вы собираете обратную связь от сотрудников, если она будет?

Для тех. специалиста

Что можно улучшить в компании?
Как часто вы делаете релиз?
Насколько легко писать у вас тесты? И есть ли такая практика вообще?
Как у вас организована выкатка нового функционала?

Видео по теме
Почему вы хотите у нас работать? Вопросы на интервью.

 Нет комментариев    95   26 дн  

Удаления дубликатов строк

Недавно мне попалась интересная задачка по SQL.
У вас есть таблица с 2 колонками: Id, Name. Но на колонках нет никаких ограничений. И получилось так что там если дубли и нам надо их удалить.

Пример таблички

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

; WITH DuplicatesForDelete(Row) AS (
	SELECT ROW_NUMBER() OVER(PARTITION BY t1.Id, t1.Name ORDER BY Id, Name) AS Row 
	FROM TableWithDuplicates t1
)
DELETE FROM DuplicatesForDelete WHERE Row > 1

Статья о том что такое оконные функции (OVER) 

Крым на кинопленке Kodak Vision3 250D

В сентябре мне повезло вырваться на несколько дней в солнечный Крым. Очень хотелось поснимать контрастные пейзажи полуострова, и я решил поэкспериментировать с катушкой кинопленки Kodak Vision3 250D.

Проявку делал в лаборатории Фотофонд. Это одно из немногих, если не единственное место, где занимаются проявкой кинопленки в Киеве.

Изначально, я не надеялся получить хорошие фотки, ведь пленку проявляли по процессу С-41 (а не по родному процессу ECN-2).

Но результат меня порадовал! Первое, что удивило — приятная цветовая палитра и мягкий свет. Из минусов: на готовых сканах видны разводы — вероятно, из-за смывки сажи или неисправности проявочной машины, guess we’ll never know🤷🏻‍♂️. Вам решать — оставить эти случайные несовершенства, или убрать их в любом удобном редакторе. Свой выбор я сделал в пользу Фотошопа :)

С текстом помогала моя девушка :-)

Полезные SQL скрипты: запросы, которые страдают от блокировок

SELECT TOP 10
    [Average Time Blocked] = (total_elapsed_time - total_worker_time) / qs.execution_count,
    [Total Time Blocked] = total_elapsed_time - total_worker_time,
    [Execution count] = qs.execution_count,
    [Individual Query] = SUBSTRING (qt.text,qs.statement_start_offset/2,
        (CASE
        WHEN qs.statement_end_offset = -1 THEN LEN(CONVERT(NVARCHAR(MAX), qt.text)) * 2
        ELSE qs.statement_end_offset
        END - qs.statement_start_offset)/2),
    [Parent Query] = qt.text,
    [DatabaseName] = DB_NAME(qt.dbid)
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) as qt
ORDER BY [Average Time Blocked] DESC;
 Нет комментариев    347   1 мес   mssql   sql
Ранее Ctrl + ↓