d20pfsrd Discord Bot Scraper Абстракция и развертывание

Aug 16 2020

Я написал бота Discord, который прослушивает ссылки на книгу правил d20pfsrd в чате Discord, проверяет, являются ли они подвигами или магией, а затем очищает страницу, форматирует ее и выводит обратно в чат как форматированный текст. Страницы, которые я проверяю, достаточно шаблонны, поэтому, хотя используемые мной селекторы Cheerio, вероятно, довольно хрупкие, в большинстве случаев это работает. Тем не менее, средства форматирования текста - некоторые уродливые твари (хотя логика, вероятно, уникальна), и вызовы axios также могут нуждаться в некоторой очистке.

Моя проблема заключается в следующем: я подозревал, что около 40% кода можно реорганизовать, если я правильно обобщу его. Мое решение заключалось в том, чтобы сделать что-то, что, вероятно, можно было бы описать как злоупотребление оператором распространения:

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

Через неделю я понимаю, что написал функции таким образом, чтобы они читались в обратном порядке? Если оставить в стороне эстетику, вот как это работает:

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

Идея состоит в том, что в magic.js и feats.js существует как можно больше уникальной логики, насколько это возможно. Как мне очистить этот раздел? Репозиторий github здесь, и я хотел бы получить отзывы обо всем проекте. Я сделал большую часть этого, потому что считал странным иметь здесь большой блок if и дублировать вызовы axios:

client.on('message', msg => {
  

  if(validateUrl(msg.content)){
    const siteUrl = encodeURI(msg.content);
    getPage(msg, siteUrl);
  }
  

});

Также я написал эту суть о том, как я развернул его на Raspberry Pi. Это работает, поэтому мы будем очень признательны за предложения по его улучшению.

В общих чертах эта программа:

  1. Слушает сообщение
  2. Проверяет сообщение
  3. Получает содержимое страницы по URL-адресу сообщения
  4. Анализирует содержимое страницы
  5. Переформатирует содержимое страницы
  6. Преобразует содержимое страницы в сообщение
  7. Отправляет сообщение

Ответы

1 TedBrownlow Aug 16 2020 at 10:57

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

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