27 заметок с тегом

программирование

Как я делал бота для 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 месяца, с возможностью дальнейшего обновления.

 Нет комментариев    26   15 дн   asp.net core   программирование

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

Недавно мне попалась интересная задачка по 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) 

 Нет комментариев    387   28 дн   sql   программирование

Parallel, Asynchronous, Multithreading programming

Многопоточное программирование

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

Параллельное программирование

Подразумевает что некоторая задача разбивается на несколько независимых подзадач, которые можно выполнить параллельно и потом объединить результаты.
Примером такой задачи может быть Parallel LINQ

IEnumerable<Data> yourData = GetYourData();
var result = yourData.AsParallel() // начинаем обрабатывать параллельно
  .Select(d => d.CalcAmount()) // Вычисляем параллельно
  .Where(amount => amount > 0)
  .ToArray(); // Возврвщаемся к синхронной модели

Асинхронное программирование

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

Пример на C#.
У нас есть продолжительная асинхронная задача, которая обращается к БД. С помощью конструкций async/await мы организовываем асинхронную работу. Пока БД готовит для нас ответ, поток, который обслуживал этот метод возвращается в пулл потоков и может тем временем выполнять полезную работу. Как только БД отдаст ответ, нашему методу снова выделяется поток и продолжается работа.

var asyncResult = await Database.GetAllUsers(); // длительная асинхронная операция
var activeUsers = asyncResult.Where(user => user.IsActive).ToList(); // работаем с результатом асинхронной операции

Еще один забавный но наглядный пример

Вам нужно выкопать во дворе бассейн.

  1. Вы взяли лопату и копаете. Это однопоточная работа
  2. Вы пригласили друга Васю и копаете вместе, периодически задевая друг-друга лопатами. Это многопоточная работа
  3. Пока вы копаете бассейн, Вася копает канаву под водопровод. Никто никому не мешает. Это распараллеливание
  4. Вы пригласили бригаду землекопов, а сами с Васей пошли пить пиво. Когда бригада все сделает, к вам придут за деньгами. Это асинхронная работа.

Количество лопат в хозяйстве — это количество ядер в системе

Ссылки

Параллелизм против многопоточности против асинхронного программирования: разъяснение
Cтатья об async/await в C#
Многопоточное vs асинхронное программирование (stackoverflow) 

 Нет комментариев    86   1 мес   .net   программирование
 Нет комментариев    365   1 мес   c#   программирование

Лучшее объяснения работы семафора

A semaphore is like a nightclub: it has a certain capacity, enforced by a bouncer. Once it’s full, no more people can enter, and a queue builds up outside. Then, for each person that leaves, one person enters from the head of the queue. The constructor requires a minimum of two arguments: the number of places currently available in the nightclub and the club’s total capacity.

 Нет комментариев    69   1 мес   .net   c#   программирование

Небольшая заметка об индексах в ms sql

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

Что из себя представляет индекс?

Внутри сервера они представлены в виде B-tree, где B означает сбалансированное, а не бинарное дерево.

Например мы хотим найти запись с Id = 2581.
Начиная с корня, выполняется поиск наименьшего значения ключа, большего или равного требуемому. Так мы найдем узел 18316, потом спустимся в узел 9031 и там мы увидим что есть прямая ссылка на лежащие данные по ключу 2581, после чего осуществляем вычитку данных.

Кластерный индекс

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

Не кластерный индекс

Структура такая же как и кластерного индекса, но с двумя отличиями:

  1. Не изменяет физическое упорядочивание данных.
  2. Страницы индекса состоят из ключа индекса и ссылки на строку.

SQL Server использует индекс для нахождения записей, совпадающих с условиями запроса.

Составной ключ в индексе

SQL Server позволяет создавать индексы по нескольким колонкам. Но в таком случае у нас появляется ограничение. Длинна составного ключа не должна быть больше 900 байт. Но бывают исключения, например у нас есть две колонки, каждая из которых длинной в 500 байт. Сервер создаст индекс, в случае если нет данных, которые будут превышать длину в 900 байт.

Также стоит помнить что индексы типа (Col1, Col2) и (Col2, Col1) разные.

Уникальные индексы

Такие индексы создаются для реализации целостности данных. Таким образом сервер гарантирует уникальные значение для указанной колонки или составного ключа.

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

  1. Очень хорошая статья о всех типах индексов, когда их стоит создавать и как использовать
  2. Индексы. Теоретические основы.
 Нет комментариев    82   2 мес   sql   программирование   структуры данных

Изучение SQL: рекурсивные запросы

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

Общий вид рекурсивного запроса

WITH <имя> (<список столбцов>)
AS (
	SELECT    -- анкорная часть
	UNION ALL -- рекурсивная часть
        SELECT FROM <имя>
        WHERE <условие продолжения интерации>
)

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

Пример 1
У нас есть табличка, в которой лежат сотрудники, у каждого сотрудника есть руководитель, который указывается в колонку ParentId. Нам надо найти менеджера, в непрямом подчинении которого есть указанный нами человек.

Наполнение таблицы.

Код запроса:

;WITH OrgStructure AS 
(
	SELECT Id, ParentId, EmployeeType, EmployeeName
	FROM Employees
	WHERE EmployeeName = 'Jim' -- отправная точка, нам надо найти менеджера сотрудника Jim

	UNION ALL

	SELECT e.Id, e.ParentId, e.EmployeeType, e.EmployeeName
	FROM Employees as e
	JOIN OrgStructure as os
	ON e.Id = os.ParentId
)

SELECT * FROM OrgStructure
WHERE EmployeeType = 'manager' -- указываем что мы ищем менеджера.

Пример 2
Найдем первые 10 чисел Фибоначчи:

;WITH FIBONACHI AS 
(
	SELECT
		1 Iteration,
		1 SecondValue,
		2 CurrentValue
	UNION ALL

	SELECT
		Iteration + 1,
		SecondValue = CurrentValue,
		CurrentValue = SecondValue + CurrentValue
	FROM FIBONACHI
	WHERE Iteration < 10
)
SELECT CurrentValue FROM FIBONACHI
 Нет комментариев    61   2 мес   sql   программирование

GIN индекс

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

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

Хорошая аналогия для этого метода — алфавитный указатель в конце книги, где для каждого термина приведен список страниц, где этот термин упоминается.

 Нет комментариев    51   3 мес   программирование   структуры данных

Алгоритм поиска Ли

Алгоритм поиска пути в планарном графе. Зачастую используется в схемотехнике и в играх (стратегиях) для поиска кратчайшего пути.

Алгоритм состоит из 3 шагов:

  1. Инициализация
  2. Распространение волны
  3. Восстановление пути

Также есть 2 способа поиска пути: ортогональный и ортогонально-диагональный. Ниже на скриншотах можно увидеть работу этих двух способов.

Ортогональный поиск.
Ортогонально-диагональный поиск.

Псевдокод

Взято из Википедии.

Инициализация

Пометить стартовую ячейку 
d := 0

Распространение волны

ЦИКЛ
  ДЛЯ каждой ячейки loc, помеченной числом d
    пометить все соседние свободные непомеченные ячейки числом d + 1
  КЦ
  d := d + 1
ПОКА (финишная ячейка не помечена) И (есть возможность распространения волны)

Восстановление пути

ЕСЛИ финишная ячейка помечена
ТО
  перейти в финишную ячейку
  ЦИКЛ
    выбрать среди соседних ячейку, помеченную числом на 1 меньше числа в текущей ячейке
    перейти в выбранную ячейку и добавить её к пути
  ПОКА текущая ячейка — не стартовая
  ВОЗВРАТ путь найден
ИНАЧЕ
  ВОЗВРАТ путь не найден

Пример кода, который реализует алгоритм и выводит на экран путь (C#).

 Нет комментариев    75   3 мес   c#   алгоритмы   программирование   структуры данных
Ранее Ctrl + ↓