d20pfsrd Discord Bot Scraper Abstrakcja i wdrażanie
Napisałem bota niezgody, który nasłuchuje linków do instrukcji d20pfsrd na czacie na discordzie, sprawdza, czy są to wyczyny, czy magia, a następnie zeskrobuje stronę, formatuje ją i wypluwa z powrotem na czat jako sformatowany tekst. Strony, które sprawdzam, są wystarczająco formalne, więc chociaż selektory cheerio, których używam, są prawdopodobnie dość kruche, wydaje się, że w większości przypadków działają. Formaty tekstu to jednak jakieś brzydkie bestie (chociaż logika jest prawdopodobnie wyjątkowa), a wywołania axios mogą również wymagać trochę czyszczenia.
Mój problem jest następujący: Podejrzewałem, że dobre 40% kodu można refaktoryzować z istnienia, jeśli odpowiednio go uogólniłem. Moim rozwiązaniem było zrobienie czegoś, co prawdopodobnie można by opisać jako nadużycie operatora spreadu:
async function getPage(msg, url){
let message = "placeholder";
await axios.get(url).then( (response) => {
message = responder(...selectResponder(url), response);
}
).catch((error) => {
console.error(error.message);
});
sendMessage(message, msg);
}
Tydzień później zdaję sobie sprawę, że napisałem funkcje w sposób, który powoduje, że czytają od tyłu? Pomijając estetykę, kończy się to tak:
function selectResponder(siteUrl){
if (siteUrl.startsWith("https://www.d20pfsrd.com/feats/")){
return feats.featsConfig;
} else if (siteUrl.startsWith("https://www.d20pfsrd.com/magic/")){
return magic.magicConfig;
}
}
function responder(parser, formatter, messageFormatter, response){
const replyData = parser(response.data);
const formattedData = formatter(replyData);
return messageFormatter(formattedData);
}
Chodzi o to, że tyle unikalnej logiki, ile mogłem wyodrębnić, istnieje w magic.js i feats.js, jak to tylko możliwe. Jak mogę wyczyścić tę sekcję? Repozytorium github jest tutaj i chciałbym uzyskać informacje zwrotne na temat całego projektu. Zrobiłem większość tego, ponieważ uważałem to za dziwne mieć tutaj duży blok if i powielanie wywołań axios:
client.on('message', msg => {
if(validateUrl(msg.content)){
const siteUrl = encodeURI(msg.content);
getPage(msg, siteUrl);
}
});
Napisałem również sedno, w jaki sposób wdrożyłem go na malinowym pi. To działa, więc sugestie, jak to poprawić, byłyby bardzo mile widziane
W skrócie ten program:
- Nasłuchuje wiadomości
- Weryfikuje wiadomość
- Pobiera zawartość strony pod adresem URL wiadomości
- Analizuje zawartość strony
- Ponownie formatuje zawartość strony
- Konwertuje zawartość strony na wiadomość
- Wysyła wiadomość
Odpowiedzi
Oto jak bym to zrobił. Przenieś logikę określającą, które strony analizuje parser, do kodu tego parsera. Również zorganizowanie plików pomocniczych w obiekty może zmniejszyć złożoność pod względem liczby przekazywanych zmiennych, ale jest to bardziej kwestia osobistych preferencji.
class SpecificPageParser {
match(url) {
return (
url.startswith('https://www.d20pfsrd.com/magic/') &&
!url.endsWith('/magic/')
);
}
format(response) {
// do your parsing stuff
const formatted = {...response,'extra':'info'};
return formatted;
}
}
const responders = [new SpecificPageParser(),new SomeOtherParser()];
async function getPage(msg, url){
try {
const responder = responders.find(responder=>responder.match(url));
if (!responder) return;
const response = await axios.get(url);
const message = responder.format(response);
sendMessage(message, msg);
}
catch(error) {
console.error(error);
}
}
```