d20pfsrd Discord Bot Scraper Abstrakcja i wdrażanie

Aug 16 2020

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:

  1. Nasłuchuje wiadomości
  2. Weryfikuje wiadomość
  3. Pobiera zawartość strony pod adresem URL wiadomości
  4. Analizuje zawartość strony
  5. Ponownie formatuje zawartość strony
  6. Konwertuje zawartość strony na wiadomość
  7. Wysyła wiadomość

Odpowiedzi

1 TedBrownlow Aug 16 2020 at 10:57

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);
    }
  }
```