d20pfsrd Discord Bot Scraper Абстракция и развертывание
Я написал бота 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. Это работает, поэтому мы будем очень признательны за предложения по его улучшению.
В общих чертах эта программа:
- Слушает сообщение
- Проверяет сообщение
- Получает содержимое страницы по URL-адресу сообщения
- Анализирует содержимое страницы
- Переформатирует содержимое страницы
- Преобразует содержимое страницы в сообщение
- Отправляет сообщение
Ответы
Вот как бы я это сделал. Перенесите логику, определяющую, какие страницы анализатор использует, в код этого парсера. Кроме того, организация ваших вспомогательных файлов в объекты может снизить сложность с точки зрения количества переменных, которые вы передаете, но это больше вопрос личных предпочтений.
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);
    }
  }
```