Как обрабатывать входящие уведомления#
Общая концепция получения данных в Green API описана здесь Для старта получения сообщений через HTTP API требуется выполнить метод библиотеки:
greenApi.webhooks.startReceivingNotifications(onEvent)
onEvent — ваш метод, который должен содержать параметры:
| Параметр | Описание |
|---|---|
| typeWebhook | тип полученного сообщения (строка) |
| body | тело сообщения (json) |
Типы и форматы тел сообщений здесь
Этот метод будет вызываться при получении входящего сообщения. Далее обрабатываете сообщения согласно бизнес-логике вашей системы.
Полный список примеров#
| Описание | Модуль |
|---|---|
| Пример отправки текста | sendTextMessage.py |
| Пример отправки картинки по URL | sendPictureByLink.py |
| Пример отправки картинки загрузкой с диска | sendPictureByUpload.py |
| Пример создание группы и отправка сообщения в группу | createGroupAndSendMessage.py |
| Пример получения входящих уведомлений | receiveNotification.py |
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the «Software»), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED «AS IS», WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. —>
Библиотека Requests: HTTP for Humans
Язык Python является универсальным языком программирования. С его помощью можно решать разнообразные задачи в сфере разработки.
Одной из таких сфер, в которой Python занял уверенную позицию, является веб-разработка. Немаловажную роль в этом сыграло обширное комьюнити, различные фреймворки и подключаемые библиотеки, созданные для облегчения жизни программистов. Об одной из таких библиотек сегодня пойдёт речь.
Библиотека Requests: HTTP for Humans
При решении различных задач в сфере веб-разработки, нам часто приходится взаимодействовать с HTTP. Это не самая простая задача для любого языка программирования и Python в этом не исключение. Язык, конечно, содержит встроенные модули, позволяющие отправлять HTTP запросы , но, как это ни парадоксально, их использование едва ли можно отнести к Pythonic-way.
В своё время чтобы обойти монструозность и сложность использования встроенных модулей появилась библиотека Requests . На данный момент она является одной из самых популярных библиотек на github: более 40 000 «звёзд» и используется более, чем в 20 000 open-source проектах. Нередко она используется и в коммерческой разработке.
Для чего и где мы можем применять Requests?
Как было отмечено выше, библиотека позволяет нам легко и с минимальным количеством кода взаимодействовать с веб-приложениями. Это необходимо нам для решения любых задач, связанных с передачей информации от пользователя к серверу и обратно. Но, чтобы что-то хорошо понять, необходимо закрепить теоретические знания практикой, поэтому перейдём от слов к делу.
Примеры использования библиотеки Requests
Для работы с примерами мы будем использовать два способа: работа в командной строке и написание скриптов в .py-файлах для их дальнейшего запуска.
Я показываю использование командной строки, так как это полезный навык, который пригодится в дальнейшем, когда надо будет быстро что-то проверить.
Для начала, поскольку библиотека является внешней, нам необходимо её установить. Делается это очень просто в командной строке:
$ pip install requests
Продолжим использовать консоль. После установки в нашем расположении (у меня это диск D) введем команду:
D:\> python Python 3.7.4 (tags/v3.7.4:e09359112e, Jul 8 2019, 20:34:20) [MSC v.1916 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>>
Теперь можем проверить установку библиотеки произведя импорт:
>>> import requests
Получаем ответ от сайта
Первое, что мы сделаем — проверим статус-код ресурса. Иными словами: узнаем, работает ли сайт. Для этого мы создадим простой запрос GET. Подробнее с кодами состояния HTTP можно ознакомиться на сайте Веб-документация MDN .
>>> r = requests.get('http://learn.python.ru')
Проверим, что возвращает нам данный запрос:
Как мы можем видеть, в качестве ответа мы получили объект класса Response и код 200. Этот код говорит, что ресурс работает и можно с ним взаимодействовать.
А что будет, если мы отправим неправильный запрос?
>>> r = requests.get('http://learn.python.ru/user') >>> r
Как вы можете заметить, этот запрос вернул нам всё тот же объект класса Response, но с другим кодом — 404. Это означает, что страницы ‘/user’ на сайте http://learn.python.ru нет.
Теперь, когда мы познакомились с тем, как отправлять запрос GET с помощью Requests, мы можем продвинуться дальше и сделать что-то более интересное.
Скачаем изображение с сайта
В этом примере мы будем применять всё тот же запрос GET, но, в отличие от предыдущего, мы будем работать с содержимым ответа.
>>> import requests >>> image = requests.get('https://learn.python.ru/media/projects/sl1_Cj4bKxp.png') >>> with open('new_image.png', 'wb') as f: . f.write(image.content)
После выполнения данного скрипта, мы увидим новое изображение под названием new_image.png в директории, из которой у нас запущен скрипт.
Подробнее с классом Response и методами получения его содержимого можно ознакомиться на официальном сайте библиотеки Requests .
Отправим сообщение в WhatsApp
В этом примере, в отличие от примеров выше, мы познакомимся с другим методом — POST. Стоит отметить, что запросы POST и GET являются самыми часто применяемыми при работе с HTTP.
Для реализации этого примера воспользуемся сервисом, предоставляющим API для отправки сообщений. Я буду использовать сервис Chat-Api . Это платный сервис, но у них есть демо-режим, предоставляющий бесплатный доступ на 3 дня, чего нам для реализации этой задачи будет достаточно. После регистрации на сайте и получении Api URL и токена, мы сможем выполнить отправку сообщений в WhatsApp. Вот что нам для этого потребуется:
>>> import requests >>> url = '/message?token=' >>> data = ", "body": ""> >>> message = requests.post(url, json=data)
И проверим, отправлено ли сообщение:
>>> print(message.text)
Вы должны будете увидеть что-то вроде этого:
@c.us","id":"true_@c.us_3EB07324DCD36BD13CC4","queueNumber":1>
Загрузим файл на сервер
Для следующего примера воспользуемся сайтом для тестирования HTTP запросов Webhook.site . В качестве URL мы будем использовать ссылку, которая генерируется на сайте автоматически. У вас она будет отличаться от использованной в примере.
>>> import requests >>> url = 'https://webhook.site/c253969f-1ad8-4888-812e-e57f4eb4924e' >>> with open('test.txt', 'w') as f: . f.write('текст для проверки загрузки файла') >>> with open('test.txt', 'rb') as f: . r = requests.post(url, ) . print(r)
После выполнения скрипта в терминале мы увидим уже знакомый нам объект класса Response и статус код — 200. Это означает, что запрос выполнился. Вернемся снова на сайт Webhook.site, выберем в правой колонке наш запрос и сможем посмотреть содержание выполненного запроса:
Авторизуемся на сайте
В этом примере будем использовать сайт, который нам также потребуется в следующем примере. Для начала зарегистрируемся на сайте WorldWeatherOnline . После удачной регистрации на сайте в личном кабинете вы увидите API KEY, который потребуется для следующего примера.
Для авторизации на сайте нам необходимо в запросе отправить пользовательские данные на сайт. Помимо логина и пароля, мы должны отправить сайту ключи, которые содержатся в заголовках пакета. Чтобы узнать эти ключи нужно выйти из нашей учётной записи, перейти на страницу входа, затем открыть в браузере Developer tools (F12), перейти на вкладку Network и снова авторизоваться. После того как страница загрузится в Developer tools во вкладке Network пролистайте вверх и найдите строчку login.aspx.
Как мы можем заметить во вкладке Headers, при входе мы осуществили запрос POST для передачи данных серверу. Пролистав вниз до блока Form Data, мы найдём все ключи, которые нам необходимо отправить сайту для авторизации.

>>> import requests >>> url = 'https://www.worldweatheronline.com/developer/login.aspx' >>> data = > >>> s = requests.Session() >>> s = requests.post(url, data=data)
Для проверки выполнения авторизации выведем содержание ответа сервера:
>>> print(s.text)
После выполнения данной функции в консоли мы увидим содержимое страницы в виде текста, то есть так, как страницу “видит” наш браузер — в формате html. Подтверждением успешной авторизации служат следующие строки:
Неавторизованному пользователю данные кнопки недоступны.
Узнаем погоду в Москве
В этом примере мы будем использовать API KEY с сайта WorldWeatherOnline, на котором мы зарегистрировались в прошлом примере. В разделе документации на сайте WorldWeatherOnline приведено подробное описание параметров.
Теперь уже с использованием вашей любимой IDE напишем скрипт, который будет принимать в качестве аргумента из командной строки город, а возвращать значение температуры.
Создадим файл и назовём его weather.py. Запишем в него:
import sys import requests def get_weather(): url = 'http://api.worldweatheronline.com/premium/v1/weather.ashx' city = sys.argv[1] params = < 'key': '', 'q': city, 'format': 'json', 'num_of_days': 1, 'lang': 'ru'> r = requests.get(url, params=params) the_weather = r.json() # данной строкой мы преобразуем полученное содержимое страницы # в формат json, который очень похож на тип данных dict() в Python # и с которым очень удобно работать if 'data' in the_weather: if 'current_condition' in the_weather['data']: try: return the_weather['data']['current_condition'][0] except(IndexError, TypeError): return 'Server Error' return 'Server Error' if __name__ == '__main__': weather = get_weather() print(f'Погода сейчас , ощущается как ')
Запустим наш скрипт в командной строке:
$ python weather.py Moscow
В качестве ответа мы должны увидеть:
Погода сейчас 20, ощущается как 20
В этом примере мы не просто получили какую-то информацию от сервера, но и обработали содержимое полученного ответа. В следующем примере мы разберем еще один метод обработки полученного содержимого ответа.
Напишем простой парсер новостей
В качестве источника новостей будем использовать хаб о Python сайта Habr.com . В этом примере, помимо Requests нам также понадобится библиотека BeautifulSoup. С данной библиотеке можно подробно познакомиться на официальной странице документации Beautiful Soup . Устанавливается она также просто, как и Requests:
$ pip install beautifulsoup4
Создадим файл с названием news.py и запишем в него следующее:
import requests from bs4 import BeautifulSoup class HabrPythonNews: def __init__(self): self.url = 'https://habr.com/ru/hub/python/' self.html = self.get_html() def get_html(self): try: result = requests.get(self.url) result.raise_for_status() return result.text except(requests.RequestException, ValueError): print('Server error') return False
Метод get_html() класса HabrPythonNews с помощью библиотеки Requests отправляет наш запрос GET к сайту Habr.com и возвращает содержимое страницы в виде текста. Иными словами, мы получаем html страницы. Если мы посмотрим на содержимое страницы (ctrl + U в Google Chrome), то обнаружим там различные блоки, в которых содержится информация. Нас интересуют только заголовки новостей и ссылки на них. Чтобы извлечь нужную нам информацию из содержимого страницы, мы воспользуемся библиотекой Beautiful Soup. Нужный нам блок называется:
Давайте теперь добавим функцию для работы с этим блоком:
def get_python_news(self): soup = BeautifulSoup(self.html, 'html.parser') news_list = soup.findAll('h2', class_='post__title') return news_list
Метод get_python_news() возвращает нам все элементы страницы, с тегом
И добавим завершающий блок нашего класса:
if __name__ == "__main__": news = HabrPythonNews() print(news.get_python_news())
Итоговый код должен выглядеть у нас так:
import requests from bs4 import BeautifulSoup class HabrPythonNews: def __init__(self): self.url = 'https://habr.com/ru/hub/python/' self.html = self.get_html() def get_html(self): try: result = requests.get(self.url) result.raise_for_status() return result.text except(requests.RequestException, ValueError): print('Server error') return False def get_python_news(self): soup = BeautifulSoup(self.html, 'html.parser') news_list = soup.findAll('h2', class_='post__title') return news_list if __name__ == "__main__": news = HabrPythonNews() print(news.get_python_news())
При выполнении данного скрипта мы получим ссылки и заголовки новостей с первой страницы хаба. Изменяя параметры поиска
news_list = soup.findAll('h2', class_='post__title')
мы можем получать различные значения.
В качестве заключения
Использование библиотеки Requests не ограничивается приведенными выше примерами. Данная библиотека является очень удобным инструментом для взаимодействия с HTTP. Продолжить знакомство с библиотекой можно на официальном сайте Requests .
Автор: Виталий Калинин
Новости
16 ноября 2019
Учимся использовать API сервиса Yandex SpeechKit
8 ноября 2019
Learn Python в Пензе
Python-скрипт на 20 строк, который каждый день желает родителям доброго утра через WhatsApp
Автор материала, перевод которого мы сегодня публикуем, говорит, что современные люди, жизнь которых переполнена работой, часто забывают писать сообщения своим родным и близким. Он, глядя на то, как его родители каждое утро шлют ему в WhatsApp вдохновляющие цитаты и полезные советы о здоровье, решил, что пришло время ответить им взаимностью.
В этом руководстве мы напишем простой Python-скрипт, предназначенный для отправки WhatsApp-сообщений. В ходе работы будем пользоваться Python-пакетом Twilio. Для организации ежедневного запуска скрипта в заданное время разместим код в облаке.

Одним из подходов к решению этой задачи является использование Python-пакета Selenium и веб-версии WhatsApp вместо сервиса Twilio, на который, после исчерпания возможностей бесплатного предложения, нужно оформлять платную подписку. Но так как работа с веб-версией WhatsApp требует периодического сканирования QR-кода с помощью мобильного телефона, автоматизировать отправку сообщений при таком подходе не получится.
Работа над проектом будет состоять из трёх шагов:
- Настройка Twilio.
- Изучение и модификация кода.
- Развёртывание проекта в облаке и настройка триггера.
Шаг 1. Настройка Twilio
Создадим бесплатную учётную запись на сайте Twilio, подтвердим адрес электронной почты и номер телефона.

Регистрация учётной записи Twilio
Кроме того, в рамках бесплатного предложения Twilio необходимо использовать WhatsApp-песочницу (WhatsApp Sandbox). Это означает, что вы не сможете пользоваться собственным номером телефона, и то, что вам придётся пройти через процедуру выдачи разрешения на получение WhatsApp-сообщений.
Собственным номером для работы с сообщениями можно пользоваться после того, как WhatsApp разрешит Twilio использовать ваш номер. Для того чтобы получить разрешение, нужно заполнить форму. Из материалов техподдержки Twilio можно узнать о том, что, хотя Twilio и работает напрямую с WhatsApp, на получение разрешения может понадобиться некоторое время. К тому же, там говорится об ограниченных масштабах выдачи разрешений. В результате многим приходится пользоваться WhatsApp-песочницей Twilio.
Всё это особой радости не вызывает, но то, что доступно нам в рамках бесплатного предложения Twilio, позволяет решить нашу задачу. Кроме того, получается, что WhatsApp-песочница — это пока единственный широкодоступный вариант.
Теперь нужно подключить телефон получателя сообщений к песочнице, после чего на этот телефон можно будет отправлять сообщения. Для того чтобы узнать о том, как это сделать, вам нужно перейти в раздел консоли Twilio, который посвящён WhatsApp. В частности, тут предлагается отправить WhatsApp-сообщение с указанным текстом на определённый номер.

Страница консоли Twilio с инструкциями по подключению телефона, на который можно будет отправлять сообщения

Запрос на подключение, отправленный с телефона
Сохраните выданный вам WhatsApp-номер в контактах. Ему можно назначить любое имя. Я, чтобы не усложнять себе жизнь, назвал этот контакт Twilio Sandbox, а потом отправил на него сообщение с телефона отца (это можно видеть на предыдущем рисунке). Эта процедура выполняется лишь один раз.
Теперь нужно перейти в консоль Twilio и получить SID и токен аутентификации для своей учётной записи. Эти данные помогут Twilio узнать вас при программной работе с сервисом.
Шаг 2. Изучение и модификация кода
Загрузите этот GitHub-репозиторий и распакуйте архив.

Содержимое zip-файла
Здесь вы найдёте файл с исходным кодом ( whatsapp_messaging.py ) и пакет для развёртывания проекта ( aws_lambda_deploy.zip ).
Вот код скрипта:
from twilio.rest import Client def msg_mom_and_dad(event=None, context=None): # тут нужно использовать SID и токен аутентификации, которые вы получили на Twilio twilio_sid = 'AC84c9f1602d7fb6af4eda5b0c39a03b37' auth_token = '4a2021b28f1aa606d9c6945d3c248ebd' whatsapp_client = Client(twilio_sid, auth_token) # в этот словарь можно добавлять контактные сведения тех, # кому вы хотите отправлять сообщения contact_directory = for key, value in contact_directory.items(): msg_loved_ones = whatsapp_client.messages.create( body = 'good morning <> !'.format(key), from_= 'whatsapp:+14155238886', to='whatsapp:' + value, ) print(msg_loved_ones.sid)
Сейчас мы разберём этот код. Вот, для удобства, скриншот с пронумерованными строками.

Код файла whatsapp_messaging.py с пронумерованными строками
- Строка 1. Импорт клиента для работы с REST-API Twilio.
- Строка 3. Создание функции msg_mom_and_dad . Эту функцию мы передадим AWS. Она будет вызываться ежедневно в заданное время.
- Строки 6-7. Здесь вам нужно заменить существующие в коде sid и auth_token на собственные (об их получении мы говорили в конце предыдущего раздела).
- Строка 9. Создание объекта клиента Twilio с использованием учётных данных.
- Строка 13. Создание словаря. В качестве ключа тут используется имя получателя сообщений, в качестве значения — номер его телефона. В этот словарь можно добавить и дополнительные контактные сведения.
- Строка 15. Цикл for , в котором осуществляется обход словаря (в нём пока имеется лишь одна запись). В body нужно указать текст сообщения. Я создал простое сообщение с текстом «good morning», за которым следует значение, взятое из ключа текущего элемента словаря. В моём случае это приводит к формированию сообщения «good morning daddy !». Во from_ указывается тот WhatsApp-номер, который мы получили ранее. В to записывают номер получателя сообщения — тот, с которого ранее отправляли запрос на подключение к WhatsApp-песочнице Twilio.
- Строка 23. Тут мы, в целях проверки состояния сообщения, выводим его SID. Мы этими сведениями пользоваться не будем.
- twilio_sid
auth_token
contact_directory
from_
body (это необязательно)
Шаг 3. Развёртывание проекта на AWS и настройка триггера
Код готов к запуску и к тому, чтобы отправлять WhatsApp-сообщения. Если вы интересуетесь тем, какую роль в проекте играют другие файлы из архива aws_lambda_deploy.zip , то знайте, что среди этих файлов находятся пакет Twilio и все остальные зависимости проекта. Всё это нам нужно из-за того, что мы планируем использовать функции AWS Lambda в Python-окружении, в котором нет пакета Twilio. А почему бы нам это не исправить, просто выполнив для установки нужного пакета команду pip install twilio ? Дело в том, что тут у нас нет сервера.
Для этого мы используем AWS Lambda — это бессерверная вычислительная среда, в которой можно размещать фрагменты кода, вызов которых, в соответствии с нуждами пользователя, инициируют различные события и триггеры. Наш код планируется запускать всего раз в день, поэтому использование чего-то вроде EC2-сервера на AWS, работающего круглосуточно, окажется пустой тратой вычислительных ресурсов и денег. Наша Lambda-функция будет вызываться ежедневно в определённое время по запросу из триггера, а её выполнение будет занимать совсем немного времени.
Начало создания Lambda-функции
Войдите в свою учётную запись AWS. После этого пройдите по пути Services → Compute → Lambda → Create a function .

Экран создания Lambda-функции
Дадим функции имя.
В качестве окружения, в котором будет выполняться функция, выберем Python 3.6. Нам не нужно подключаться к другим сервисам AWS. Поэтому нас устроит уровень разрешений, задаваемый вариантом Create a new role with basic Lambda permissions .
После выполнения настроек достаточно нажать на кнопку Create function . Теперь мы окажемся на главной панели управления.

Панель управления
Здесь, в разделе Function code , нужно указать обработчик ( Handler ), с помощью которого система сможет запускать наш код. В нашем случае в поле Handler нужно записать строку whatsapp_messaging. msg_mom_and_dad , указав имя файла с кодом и функцию, которую мы хотим вызывать.
В поле Function package нужно загрузить наш .zip-файл, то есть — тот файл, который мы создали на предыдущем шаге.
Теперь код готов к запуску. Проверить — работает ли функция и отправляет ли она сообщения, можно, нажав на кнопку Test .
Финальным шагом нашей работы станет настройка триггера, который будет вызывать функцию ежедневно в заданное время. Пройдите по пути Add trigger → CloudWatch Events .

Настройка триггера
Нам нужно создать новое правило ( Rule ). Ему необходимо назначить имя ( Rule name ). При желании можно добавить к нему описание ( Rule description ).
Укажем тип правила ( Rule type ) как Schedule expression .
Время вызова функции задаётся с помощью конструкции cron(30 1 * * ? *) . Разберём эту конструкцию:
- 30 1 означает UTC-время 1:30 утра. Это — 7 утра по моему IST-времени.
- Следующие два символа, * * , служат для указания дня месяца и месяца.
- Следующие два символа, ? * , позволяют указать день недели и год. Эта конструкция означает, что функция будет вызываться ежедневно, без ограничений по годам и месяцам.
После того, как настройка триггера завершена, проверьте — установлен ли флажок Enable trigger , включающий триггер. Теперь осталось лишь нажать на кнопку Add и триггер будет создан.

Сведения о триггере

Сведения о Lambda-функции
На панели управления Lambda-функции можно видеть, что к функции прикреплена сущность CloudWatch Events , и то, что функция может вызываться по срабатыванию соответствующего триггера.
Итоги

Сообщение, отправленное Python-скриптом
Мы завершили работу над проектом. Теперь можно, в телефоне получателя сообщений, в контактах WhatsApp, поменять имя контакта Twilio Sandbox на собственное (я поменял его на Son). Наша система, кроме того, позволяет общаться с получателем сообщений из панели управления Twilio.
Уважаемые читатели! Используете ли вы Twilio в своих проектах?

- Блог компании RUVDS.com
- Веб-разработка
- Мессенджеры
- Python
CheckWhatsapp#
Метод проверяет наличие аккаунта WhatsApp на номере телефона.
Запрос#
Для проверки наличия аккаунта WhatsApp требуется выполнить запрос по адресу:
POST https://api.green-api.com/waInstance>/checkWhatsapp/>
Для получения параметров запроса idInstance и apiTokenInstance обратитесь к разделу Перед началом работы.
Параметры запроса#
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
| phoneNumber | integer | Да | Номер телефона получателя в международном формате: 11 или 12 цифр; Пример: 11001234567 или 380123456789 |
Пример тела запроса#
Ответ#
Поля ответа#
| Поле | Тип | Описание |
|---|---|---|
| existsWhatsapp | boolean | Флаг наличия WhatsApp на номере телефона |
Пример тела ответа#
Ошибки CheckWhatsapp#
Перечень общих для всех методов ошибок смотрите в разделе Стандартные ошибки
| Код HTTP | Идентификатор ошибки | Описание |
|---|---|---|
| 400 | bad phone number, valid 11 or 12 digits | Неверный формат номера телефона, должен быть 11 или 12 цифр |
| 400 | check phone number timeout limit exceeded | Превышен лимит времени ожидания ответа о проверке номера телефона |
Примеры кода#
Python cURL Java — Spring Java — Unirest
curl --location 'https://api.green-api.com/waInstance>/checkWhatsapp/>' \ --header 'Content-Type: application/json' \ --data '< "phoneNumber": 441234567890 >'
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the «Software»), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED «AS IS», WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. —>
