Praktyczny side projekt w 1 wieczór? - Tworzenie bota do automatyzacji zadań za pomocą TypeScript, Puppeter, Supabase i Make.com

Praktyczny side projekt w 1 wieczór? - Tworzenie bota do automatyzacji zadań za pomocą TypeScript, Puppeter, Supabase i Make.com

08/03/2023
7 min read
Projekty poboczne są genialnym sposobem na naukę, rozwijanie umiejętności programowania i eksperymentowanie z różnymi technologiami. Co więcej, nie musisz się ścigać z terminami, więc masz więcej swobody w podejmowaniu decyzji i testowaniu swoich pomysłów. Dziś chciałbym przedstawić projekt, który zbudowałem w ciągu jednego wieczoru i który okazał się bardzo przydatny i praktyczny dla mnie. Pokażę Ci, w jaki sposób zbudować bota do przeszukiwania ofert mieszkań i wysyłania wiadomości na Telegramie. Razem zdeployujemy go na Supabase i wykorzystamy Make.com, aby automatycznie uruchamiał się co określony czas.

Skąd pomysł?

Każdy potrzebuje dachu nad głową, a jako że nie posiadam swojego własnego mieszkania, muszę wynajmować. Dobre oferty mieszkań potrafią szybko zniknąć z rynku, a ja sam nie mam czasu, by co chwilę odświeżać i przeglądać strony z ofertami. Potrzebowałem narzędzia, które zrobi to za mnie i będzie mi podsyłać podsumowanie ofert - stąd zrodził się pomysł stworzenia bota.

Stack technologiczny

W projekcie użyłem:
  • https://pptr.dev/ - Puppeteer - Narzędzie do odpalania przeglądarki w trybie headless i wyciągania danych z HTML
  • https://supabase.com/ - darmowa alternatywa dla Firebase. Tutaj zdeployujemy aplikację.
  • https://www.make.com/ - narzędzie do tworzenia automatyzacji. Za jego pomocą będziemy odpalać bota.
  • https://core.telegram.org/ - bot Telegrama. Ma darmowe, dobrze opisane API, a także open sourcowe biblioteki NPM obsługujące to API.
Nigdy wcześniej nie miałem do czynienia z Supabase i Make, więc ten projekt poboczny to doskonała okazja do eksperymentowania i wypróbowania tych narzędzi.

Zacznijmy od napisania bota - puppeteer

Projekt zacząłem od napisania kodu źródłowego samego bota i uruchomienia go lokalnie za pomocą Node. Zacznijmy od zainstalowania paczek puppeteer i grammy. grammy to nakładka na API Telegrama napisana w TypeScript.
npm install --save puppeteer grammy
Zaimportujmy dwie biblioteki i stwórzmy podstawową strukturę:
import { Bot } from 'grammy'
import puppeteer from 'puppeteer'
const getFlats = () => {
console.log("find flat bot")
// tutaj będzie kod naszego bota
}
getFlats()
Po uruchomieniu node index.js otrzymamy w konsoli napis find flat bot.
Przejdźmy teraz do konfiguracji Puppeteer.
const browser = await puppeteer.connect({
browserWSEndpoint: `wss://chrome.browserless.io?token=${process.env.PUPPETEER_BROWSERLESS_IO_KEY}`,
})
const page = await browser.newPage();
await page.goto('<page url>', { waitUntil: 'domcontentloaded' });
Musimy skorzystać z portalu browserless.io i pobrać API key, ponieważ Supabase Edge Functions nie obsługuje headless Chrome.
W powyższym przykładzie mamy inicjalizację przeglądarki, a także otwarcie strony, z której chcemy pobrać dane. Mając dostęp do strony, jesteśmy w stanie wyciągnąć wszystko, czego potrzebujemy ze struktury HTML. Mój przykład będzie oparty na stronie website.
const flats = await page.$$eval('[data-cy="l-card"]', (items) => {
return items.map((i) => {
const title = i.querySelector('h6').innerText;
const link = 'https://www.olx.pl' + i.querySelector('a').getAttribute('href');
const price = parseFloat(
i.querySelector('[data-testid="ad-price"]').innerHTML.split(' zł')[0].replace(' ', '')
);
const square = i.querySelector('[data-testid="blueprint-card-param-icon"]').nextSibling.textContent;
return { link, title, price, square }
});
});
Za pomocą page.$$eval(selector) definiujemy selektor, z którego chcemy wyciągnąć dane. $$eval oznacza, że wyciągnięte zostaną wszystkie elementy o danym selektorze. Jako drugi argument funkcji, wskazujemy items, czyli wszystkie znalezione elementy o danym selektorze. Możemy iterować po nich jak po tablicy. Możemy skorzystać z funkcji .map() do otrzymania dostępu do pojedynczego elementu HTML i wyciągnięcia z niego danych za pomocą zwykłych metod JavaScript, takich jak .querySelector(). W tym przypadku pobieramy tytuł, link, cenę i metraż danego mieszkania, a następnie zwracamy je jako obiekt. Po uruchomieniu kodu w konsoli pojawi się coś takiego:
FindFlatResponseConsole
Możemy również ograniczyć wyniki do maksymalnie 10. Wszystko zależy od konfiguracji.

Konfiguracja bota Telegram

Aby skonfigurować swojego bota, należy skorzystać z dokumentacji.
W skrócie, jest to bardzo proste. Po uruchomieniu Telegrama, wystarczy napisać do konta [@BotFather](https://t.me/botfather) i wywołać komendę /newbot. Otrzymamy klucz API, który identyfikuje naszego bota. Następnie należy skonfigurować bota w kodzie. Tak przygotowane dane możemy następnie wysłać do naszego bota.
const bot = new Bot(TELEGRAM_BOT_API_KEY)
gdzie TELEGRAM_BOT_API_KEY to nasz klucz zwrócony przy tworzeniu bota.
Nastepnie przechodzę przez całą tablicę flats otrzymaną w poprzednim kroku, tworzę wiadomość, która zostanie wysłana przez bota. Każda oferta to będzie osobna wiadomość. Jest to zwykły string.
flats.forEach(async (item) => {
const message = `Link: ${item.link} \nCena: ${item.price} \nPowierzchnia: ${item.square}`;
await bot.api.sendMessage(chatId, message);
});
Po zapisaniu i uruchomieniu kodu powinniśmy otrzymać taką wiadomość na telegramie:
TelegramBotSample
Bajka! O to nam chodziło. Teraz pora zdeployować na produkcję i zautomatyzować.

Deploy na Supabase

Jakiś czas temu słyszałem o Supabase jako fajnej alternatywie open source dla Firebase i postanowiłem wykorzystać tę platformę w swoim projekcie.
Musimy zacząć od stworzenia konta i projektu. Cały proces przeprowadza nas przez Supabase w prosty i sprawnie sposób.
Następnie przechodzimy do zakładki Edge Functions, gdzie stworzymy funkcję, która będzie wywoływana przez webhook. Jednak zanim to zrobimy, musimy skonfigurować Supabase.
Najpierw musimy zalogować się do konsoli za pomocą supabase login. Następnie inicjalizujemy projekt, używając komendy supabase init, a następnie linkujemy id naszego projektu za pomocą supabase link --project-ref your-project-ref. Po wykonaniu tych trzech kroków będziemy gotowi do stworzenia funkcji. Funkcję tworzymy za pomocą komendy supabase functions new <your function name>.
Dla nas wygeneruje się struktura ./functions/<your function name>/index.ts.
W pliku index.ts znajdziemy szablon funkcji Supabase. Edge functions działają tutaj na Deno, dlatego będziemy musieli trochę zmienić nasz kod. Przede wszystkim musimy zaktualizować importy paczek. Nie możemy bezpośrednio importować z npm, a musimy korzystać z repozytorium Deno (więcej informacji o Deno znajduje się w dokumentacji https://deno.land/).
Wprowadźmy więc aktualizacje do naszych importów.
import { serve } from "https://deno.land/std@0.168.0/http/server.ts"
import { Bot } from "https://deno.land/x/grammy@v1.8.3/mod.ts";
import puppeteer from 'https://deno.land/x/puppeteer@16.2.0/mod.ts'
Wszystko co chcemy wywolać w edge funkcji musi znajdować się w środku
serve(async (req) => {
// bot code
})
Tutaj należy wkleić kod bota. Warto zauważyć, że w Deno obsługa zmiennych środowiskowych wygląda inaczej. Musimy skorzystać z funkcji Deno.env.get('TELEGRAM_BOT_API_KEY'). Aby ustawić zmienne środowiskowe w supabase, należy wywołać polecenie w terminalu.
supabase secrets set TELEGRAM_BOT_TOKEN=your_token <rest of your tokens>
I to tyle. Pozostaje nam deploy, a on odbywa się za pomocą komendy supabase functions deploy <your function name>
Po chwili dostaniemy w konsoli informację pod jakim adresem dostępna jest nasza funkcja. Aby ją wywołać, musimy wykonać zapytanie POST z tokenem autoryzacyjnym JWT, który uzyskamy w konfiguracji naszego projektu.
Tak wyglądał deploy na supabase. Nasz bot jest gotowy. Pozostała nam ostatnia rzecz - upewnijmy się, że będzie automatycznie wykonywał się co określony czas. Aby to zrobić, użyjemy make.com.

Ożywienie bota - konfiguracja na Make.com

Make.com to zaawansowane narzędzie do tworzenia automatyzacji różnego rodzaju zadań. Często słyszy się o nim dobre opinie, ale nie zawsze jest okazja, aby z niego skorzystać. W tym projekcie będziemy mieli doskonałą okazję, aby przetestować nasze umiejętności. Aby zacząć, musimy założyć konto na Make.com.
Po założeniu konta przejdźmy do zakładki Scenarios i stwórzmy nowy scenariusz, który będzie działał jako nasz bot.
Sup1
Wybierzmy moduł HTTP a następnie
SUp2
I otrzymamy okno konfiguracji naszego requestu
sup3
Teraz musimy jeszcze dokonać kilku kroków, aby nasza automatyzacja działała poprawnie. Najpierw uzupełnijmy URL, zmieniając metodę na POST i dodając nagłówek z autoryzacją. W ten sposób upewniamy się, że nasz bot będzie mógł prawidłowo się uwierzytelnić i przesłać wymagane dane.
Gdy już skonfigurujemy te ustawienia, nasza automatyzacja jest gotowa do działania! Ostatnim krokiem jest wybór interwału czasowego, w jakim chcemy, aby workflow był uruchamiany automatycznie, lub możemy uruchomić go ręcznie w dowolnym momencie, klikając przycisk "Uruchom".
sup4
W darmowej wersji Make.com mamy możliwość uruchamiania 1 000 operacji miesięcznie, co jest wystarczające dla moich potrzeb.

Podsumowanie

Podsumowując, widać, że można stworzyć interesujący i użyteczny projekt w ciągu jednego wieczoru, a jednocześnie zdobyć wiele nowych umiejętności. Mam zamiar rozwinąć ten projekt, dodając funkcjonalność zapisywania danych ofert w bazie danych w Supabase (dostępna jest opcja Postgres), a także sprawdzania, czy dana oferta została już do mnie wysłana, aby uniknąć powtarzających się ofert. Obecnie pobieram tylko 10 najnowszych ofert, więc istnieje ryzyko, że otrzymam tę samą ofertę kilka razy.
Mam nadzieję, że ten wpis zainspiruje Was do tworzenia ciekawych side projektów i zachęci do eksperymentowania z nowymi narzędziami. Jak widać, side projekt nie musi być skomplikowaną aplikacją rozwijaną przez wiele miesięcy - kilka linii kodu potrafi znacząco ułatwić codzienne życie.

Zobacz więcej wpisów