Статьи

Мои боты теперь безупречны.

Я обычно слежу за различными сайтами — за последними публикациями, горячими предложениями, играми и конкурсами в ограниченное время и тому подобное.

Большинство из них не предлагают «чистой» системы уведомлений, такой как канал RSS. Поэтому мне часто приходится скрести свой HTML, чтобы получить то, что мне нужно.

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

И мне нужно, чтобы оно было периодическим (кто знает, когда появится следующее горячее обновление?).

И автоматический (у меня есть более важные дела в течение дня).

И удаленный хостинг (я не хочу, чтобы мой ноутбук работал 24 × 7 с непрерывным подключением к Интернету).

До сих пор я полагался на Google Apps Script (а в последнее время и на Google App Engine ) для создания подобных «отрывков» домашней интеграции; однако, с целым миром, погружающимся в сервер , почему я не должен?

Поэтому я решил перенести один из моих скриптов, написанных для мониторинга китайского сайта розничной торговли. Сайт периодически публикует различные предложения со скидками и сезонные игры, где я могу зарабатывать хорошие купоны и кредиты через ежедневные игры. Но по какой-то причине сайт не рассылает рекламные письма на мой адрес электронной почты, а это значит, что мне приходится периодически проверять сайт, чтобы убедиться, что я не пропущу что-нибудь интересное.

И ты знаешь тренировку.

Я легко забываю вещи. Иногда, когда я далеко от своего компьютера, я также пропускаю напоминание. Иногда мне просто лень искать вещи, потому что я ничего нового не получаю, 75-80% времени. Так много оправданий …

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

Я начал с AWS Lambda , очевидного выбора для бесплатных безсерверных вычислений. Его не истекающий бесплатный уровень дает мне невероятные 3.2M (да, миллион ) секунд времени выполнения в месяц — я могу фактически поддерживать одну лямбду, работающую вечно, и немного больше! — через 1 млн (снова миллион !) Вызовов. Раньше в скрипте приложений или в App Engine у ​​меня было всего 90 минут в день — чуть более 160 тысяч секунд в месяц — это означало, что мне приходилось использовать свои квоты очень экономно; но теперь я могу отпустить свои страхи и полностью насладиться своей свободой развития. Не говоря уже о полноценной контейнерной среде в отличие от рамочных ограничений Apps Script или App Engine.

Хватит разговоров. Давайте код!

Вместо стандартного пути я выбрал Sigma из SLAppForge в качестве среды разработки; прежде всего потому, что у него была некоторая репутация поддержки внешних зависимостей, а также заботы об упаковке и развертывании чего-либо от моего имени, включая все внешние службы (API, таблицы, cron и еще много чего).

Сначала я должен был подписаться на Sigma . Хотя я мог бы воспользоваться их демонстрационной функцией (большая желтая кнопка), у меня уже была учетная запись AWS и учетная запись GitHub (не говоря уже об адресе электронной почты); так почему бы не попробовать?

бессерверной

Когда регистрация завершилась и я вошел в систему, меня встретила панель выбора проекта, где я выбрал новый проект с именем site-monitor :

Приложение было невероятно быстрым, и редактор появился, как только я нажал « Создать проект» :

Без лишних слов я взял содержимое моей бывшей функции «Сценарий приложений» и поместил его в Sigma!

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
let AWS = require('aws-sdk');
 
exports.handler = function(event, context, callback) {
 
    // Here Goes Nothing
 
    PROPS = PropertiesService.getScriptProperties();
    page = UrlFetchApp.fetch("http://www.my-favorite-site.com").getResponseText();
    url = page.match(/(lp|\?r=specialTopic)\/[^"]*/)[0];
    if (url != PROPS.getProperty("latest")) {
        GmailApp.sendEmail("[email protected]", "MyFavSite Update!", url);
        PROPS.setProperty("latest", url);
    }
 
    // end of Here Goes Nothing
 
    callback(null,'Successfully executed');
}

(Я знаю, я знаю, это не сработало. Терпите меня :))

Следующие несколько минут я потратил на преобразование своего кода скрипта приложений в NodeJS. Это было не так сложно (в конце концов, оба JS!), Как только я добавил модуль request в свой проект:

Но я должен сказать, что пропустил знакомый синхронный синтаксис модуля UrlFetchApp .

В App Engine у ​​меня был удивительно простой PropertiesService который служил «памятью» моего бота. При Sigma (AWS) все было не так просто; после некоторого осмотра я решил пойти с DynamoDB (хотя я все еще чувствовал, что это было слишком много).

После того, как я извлек URL со страницы, мне нужно было проверить, уведомил ли я себя об этом; эквивалент запроса моей таблицы (ранее PropertiesService ) для существующей записи. В DynamoDB-land это была операция Get Document , поэтому я попытался перетащить DynamoDB в мой код:

После удаления запись DynamoDB превратилась во всплывающее окно, где я мог определить свою таблицу и также предоставить параметры уровня кода. Надеюсь, Sigma запомнит конфигурацию таблицы, поэтому мне не придется вводить ее снова и снова, по всему моему коду.

Поскольку DynamoDB — это не простая вещь с ключом-значением, я потратил несколько минут, ломая голову над тем, как хранить там свою «ценность»; в конце концов я решил использовать структуру документа в форме

1
2
3
4
{
    "domain": "my-favorite-site.com",
    "url": "{the stored URL value}"
}

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

В моем старом коде я использовал GmailApp.sendEmail() для отправки себе уведомления, когда я получил что-то новое. В Sigma я попытался сделать то же самое, перетащив элемент Simple Email Service ( SES ) :

Здесь произошел небольшой сбой, так как оказалось, что мне нужно будет проверить адрес электронной почты, прежде чем я смогу что-то отправить. Я не был уверен, насколько ухабистой будет моя поездка, так или иначе, я ввел свой адрес электронной почты и нажал « Отправить подтверждение по электронной почте» .

Конечно же, я получил ссылку для подтверждения по электронной почте, которая при нажатии перенаправила меня на страницу «Проверка успешна» .

И угадайте, что: когда я снова переключился на Sigma, всплывающее окно обновилось, сообщив, что электронная почта была проверена, и проведя меня по следующим шагам!

Я сразу же заполнил детали ( для себя, без CC или BCC, Subject MyFavSite Update! И Text Body @{url} (их собственный синтаксис переменной; хотя я бы хотел, чтобы это был ${} ):

При обратном вызове отправителя электронной почты SES мне пришлось обновить таблицу DynamoDB, чтобы отразить новую запись, которая была отправлена ​​по электронной почте (поэтому я не буду отправлять ее снова по электронной почте). Так же, как PROPS.setProperty("latest", url) в моем оригинальном боте.

Это было легко, с той же вещью перетаскивания: выбрав ранее созданную таблицу в разделе « Существующие таблицы» и выбрав операцию « Поместить документ» с domain настроенным на my-favorite-site.com (мой «поисковый запрос»; эквивалент "latest" в старом боте) и url установленный по электронной почте URL:

В конце концов я получил довольно хороший кусок кода (хотя он был намного длиннее, чем мой дорогой старый бот Apps Script):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
let AWS = require('aws-sdk');
const ses = new AWS.SES();
const ddb = new AWS.DynamoDB.DocumentClient();
const request = require("request");
 
exports.handler = function (event, context, callback) {
    request.get("http://www.my-favorite-site.com",
        (error, response, body) => {
            if (!body) {
                throw new Error("Failed to fetch homepage!");
            }
 
            let urls = page.match(/(lp|\?r=specialTopic)\/[^"]*/);
            if (!urls) { // nothing found; no point in proceeding
                return;
            }
            let url = urls[0];
 
            ddb.get({
                TableName: 'site-data',
                Key: { 'domain': 'my-favorite-site.com' }
            }, function (err, data) {
                if (err) {
                    throw err;
                } else {
                    if (!data.Item || data.Item.url != url) {
                        ses.sendEmail({
                            Destination: {
                                ToAddresses: ['[email protected]'],
                                CcAddresses: [],
                                BccAddresses: []
                            },
                            Message: {
                                Body: {
                                    Text: {
                                        Data: url
                                    }
                                },
                                Subject: {
                                    Data: 'MyFavSite Update!'
                                }
                            },
                            Source: '[email protected]',
                        }, function (err, data) {
                            if (err) {
                                throw err;
                            }
                            ddb.put({
                                TableName: 'site-data',
                                Item: { 'domain': 'my-favorite-site.com', 'url': url }
                            }, function (err, data) {
                                if (err) {
                                    throw err;
                                } else {
                                    console.log("New URL saved successfully!");
                                }
                            });
                        });
                    } else {
                        console.log("URL already sent out; ignoring");
                    }
                }
            });
        });
 
    callback(null, 'Successfully executed');
}

Sigma пыталась помочь мне до конца, предоставляя удобную помощь в редактировании (завершение кода, выделение синтаксиса, предложения переменных …) и даже выделяя операции DynamoDB и SES и отображая крошечные значки спереди; на котором при нажатии отображались (пере) всплывающие окна конфигурации, аналогичные тем, которые я получил, когда перетаскивал их в первый раз.

Из-за асинхронного синтаксиса, основанного на обратном вызове, мне пришлось перемещаться по кусочкам «n» фрагментов моего кода несколько раз. Сигма справился с этим довольно хорошо, заново делая подсвечивание через секунду или две после того, как я вставил код в новое место.

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

Теперь, как я могу периодически запускать мой бот?

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

Ага. Их документы говорят то же самое.

Документы AWS и собственные документы Sigma указали мне на запланированные триггеры событий CloudWatch, которые могут запускать лямбду с предопределенным расписанием — как триггеры Apps Script, но гораздо более мощные; больше как выражения cron App Engine .

Как упоминалось в их документах , я перетащил запись CloudWatch в переменную event и настроил ее так:

И все event изменилось с красного на зеленый, что может указывать на то, что мой триггер был успешно установлен.

Правильно. Время проверить это.

На панели инструментов есть кнопка « Тест (игра)» с выпадающим меню для выбора тестового примера. Как и в сценарии приложений , но гораздо лучше в том смысле, что вы можете определить полезную нагрузку ввода для вызова (тогда как сценарий приложений просто запускает функцию без каких-либо входных аргументов):

Как только я настроил тестовый пример и нажал кнопку запуска, строка состояния начала показывать ход выполнения:

Через несколько секунд автоматически появилось окно вывода журнала SigmaTrail , в котором начали отображаться некоторые журналы:

1
2
3
4
5
6
errorMessage:"RequestId: 87c59aba-8822-11e8-b912-0f46b6510aa8 Process exited before completing request"
[7/15/2018][5:00:52 PM] Updating dependencies. This might make runtime longer than usual.
[7/15/2018][5:00:55 PM] Dependencies updated.
[7/15/2018][5:00:57 PM] ReferenceError: page is not defined
at Request.request.get [as _callback] (/tmp/site-monitor/lambda.js:13:24)
at Request.self.callback (/tmp/site-monitor/node_modules/request/request.js:185:22)

Ой, похоже, что я неправильно понял имя переменной.

Простое редактирование и еще один тест.

1
2
3
[7/15/2018][5:04:50 PM] ResourceNotFoundException: Requested resource not found
at Request.extractError (/tmp/site-monitor/node_modules/aws-sdk/lib/protocol/json.js:48:27)
at Request.callListeners (/tmp/site-monitor/node_modules/aws-sdk/lib/sequential_executor.js:105:20)

Хм, что это значит?

Похоже, что это происходит от самого AWS SDK.

Возможно, «ресурсы» AWS, которые я перетащил в свое приложение, еще не доступны на стороне AWS; кроме того, во многих руководствах по Sigma упоминается этап «развертывания» перед началом тестирования.

Ну что ж, давайте попробуем развернуть эту штуку.

Я надеялся на беспроблемное «развертывание одним щелчком», но когда я нажал кнопку « Развернуть» , у меня появилось всплывающее окно, в котором говорилось, что мне нужно пройти аутентификацию на GitHub. Сигма, вероятно, может сохранить мои вещи в репозитории GitHub, а затем использовать их для остальной части развертывания.

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

В моем аккаунте не было site-monitor репо, поэтому мне было любопытно посмотреть, что сделает Сигма. Как я и подозревал, через несколько секунд после нажатия кнопки « Принять » всплыло другое диалоговое окно с вопросом, хочу ли я создать новое хранилище от моего имени.

Сигма была настолько любезна, что даже предложила создать частный репозиторий; но, увы, у меня не было такой роскоши, поэтому я просто нажал « Создать репо и зафиксировать» .

С этого момента все было довольно автоматизировано: после уведомления «Успешно подтверждено» произошел молниеносный шаг «сборки» (сопровождаемый индикатором прогресса в нижней панели состояния).

Затем я получил еще одно всплывающее окно, на этот раз сводку изменений ; которая через несколько секунд заполнилась некой «сводкой по развертыванию»:

Меня не очень интересовали подробности низкого уровня (хотя я распознал cweOneAM как мой триггер cron и siteMonitorLambda как мой бот), поэтому я просто siteMonitorLambda Execute ; и на этот раз было довольно долгое ожидание (сопровождаемое другим индикатором выполнения, на этот раз в самом всплывающем окне).

Как только он достиг отметки 100%, Сигма заявил, что мое развертывание завершено с состоянием CREATE_COMPLETE (звучит хорошо!).

Теперь давайте попробуем это снова.

1
2
"Successfully executed"
[7/15/2018][5:39:34 PM] New URL saved successfully!

Ура!

Подождите, будет ли повторная отправка, если я запустлю его снова?

1
2
"Successfully executed"
[7/15/2018][5:39:41 PM] URL already sent out; ignoring

Все хорошо; нет дубликатов!

Теперь, чтобы проверить мой почтовый ящик, чтобы увидеть, говорит ли Сигма правду.

Первоначально я был немного смущен, потому что я фактически не получил электронное письмо; но в конце концов я обнаружил, что он находится в папке «Спам» (вероятно, потому, что он был отправлен третьей стороной (AWS)?) и снял пометку со спама.

Надеюсь, мой триггер CloudWatch сработает завтра, в 1:00, и принесет мне хорошие новости, если они появятся!

В общем, графическая среда IDE довольно удобна и рекомендуется моим коллегам. За исключением времени развертывания (которое, я полагаю, характерно для серверных приложений, или Lambda, или, возможно, AWS), я чувствовал себя почти как дома — и даже более того, со всеми изящными функциями — автозаполнение, перетаскивание, настройки GUI , тестирование, логи и пр.

Пришло время выпить чашечку кофе, а затем начать переводить других моих ботов в Sigma … хм … AWS.

Смотрите оригинальную статью здесь: мои боты теперь безупречны. Бездомный. Serverless.

Мнения, высказанные участниками Java Code Geeks, являются их собственными.