Статьи

Чирикать вопросы StackExchange с помощью Spring Social

1. Введение

Это третья и последняя статья о небольшом стороннем проекте — боте, который автоматически отправляет в Твиттере Вопросы с различных сайтов Q & A StackExchange на специализированных учетных записях (полный список в конце статьи).

В первой статье обсуждалось создание простого клиента для REST API StackExchange. Во второй статье мы настроили взаимодействие с Twitter с помощью Spring Social.

В этой статье будет описана заключительная часть реализации — часть, отвечающая за взаимодействие между клиентом Stackexchange и TwitterTemplate .

2. Сервис обмена твитами

Взаимодействие между клиентом Stackexchange — раскрытие необработанных Вопросов и TwitterTemplate — полностью настроенным и способным твитнуть — это очень простая услуга — TweetStackexchangeService . API, опубликованный этим:

1
2
public void tweetTopQuestionBySite(String site, String twitterAccount){ ... }
public void tweetTopQuestionBySiteAndTag(String site, String twitterAccount, String tag){ ... }

Функциональность проста — эти API будут продолжать читать Вопросы из API Stackexchange REST (через клиента), пока не будет найден тот, который ранее не был написан в твиттере для этой конкретной учетной записи.

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

Например, следующий вопрос: Привязка списка в @RequestParam Была опубликована в Твиттере для учетной записи SpringAtSO .

Сущность Вопрос просто содержит:

  • идентификатор вопроса — 4596351 в данном случае
  • аккаунт в Твиттере, на который был написан вопрос — SpringAtSO
  • сайт Stackexcange, с которого возникает вопрос — stackoverflow

Мы должны отслеживать эту информацию, чтобы знать, какие вопросы уже были написаны в Твиттере, а какие нет.

3. Планировщик

Планировщик использует возможности запланированных задач Spring — они включены через конфигурацию Java:

1
2
3
4
5
@Configuration
@EnableScheduling
public class ContextConfig {
   //
}

Фактический планировщик относительно прост:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
@Component
@Profile(SpringProfileUtil.DEPLOYED)
public class TweetStackexchangeScheduler {
 
   @Autowired
   private TweetStackexchangeService service;
 
   // API
 
   @Scheduled(cron = "0 0 1,5 * * *")
   public void tweetStackExchangeTopQuestion() throws JsonProcessingException, IOException {
      service.tweetTopQuestionBySiteAndTag("StackOverflow", Tag.clojure.name(), "BestClojure", 1);
      String randomSite = StackexchangeUtil.pickOne("SuperUser", "StackOverflow");
      service.tweetTopQuestionBySiteAndTag(randomSite, Tag.bash.name(), "BestBash", 1);
   }
}

Выше были настроены две операции твита — один твит из вопросов StackOverflow, которые помечены как «clojure» в твиттер-аккаунте Best Of Clojure .

Другая операция отправляет в Твиттере вопросы, помеченные как «bash», и поскольку такие вопросы действительно появляются на нескольких сайтах из сети Stackexchange: StackOverflow , SuperUser и AskUbuntu , сначала выполняется быстрый процесс выбора одного из этих сайтов, после чего вопрос в твиттере

Наконец, задание cron планируется запускать в 1:00 и 5:00 каждый день.

4. Настройка

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

Поэтому я решил сохранить данные настройки в простом текстовом формате, который будет обновляться полуавтоматически.

Настройка состоит из двух основных шагов:

  • идентификаторы вопросов, написанных в твиттере для каждой учетной записи Twitter, извлекаются и сохраняются в текстовом файле
  • схема базы данных удаляется и приложение перезапускается — это создаст схему снова и настроит все данные из текстового файла обратно в новую базу данных

4.1. Исходные данные настройки

Процесс извлечения данных из существующей базы данных достаточно прост с JDBC; Сначала мы определим RowMapper:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
class TweetRowMapper implements RowMapper<String> {
   private Map<String, List<Long>> accountToQuestions;
 
   public TweetRowMapper(Map<String, List<Long>> accountToQuestions) {
      super();
      this.accountToQuestions = accountToQuestions;
   }
 
   public String mapRow(ResultSet rs, int line) throws SQLException {
      String questionIdAsString = rs.getString("question_id");
      long questionId = Long.parseLong(questionIdAsString);
      String account = rs.getString("account");
 
      if (accountToQuestions.get(account) == null) {
         accountToQuestions.put(account, Lists.<Long> newArrayList());
      }
      accountToQuestions.get(account).add(questionId);
      return "";
   }
}

Это создаст список вопросов для каждой учетной записи Twitter.

Далее, мы собираемся использовать это в простом тесте:

01
02
03
04
05
06
07
08
09
10
11
@Test
public void whenQuestionsAreRetrievedFromTheDB_thenNoExceptions() {
   Map<String, List<Long>> accountToQuestionsMap = Maps.newHashMap();
   jdbcTemplate.query
      ("SELECT * FROM question_tweet;", new TweetRowMapper(accountToQuestionsMap));
 
   for (String accountName : accountToQuestionsMap.keySet()) {
      System.out.println
         (accountName + "=" + valuesAsCsv(accountToQuestionsMap.get(accountName)));
   }
}

После получения Вопросов для учетной записи, тест просто перечислит их; например:

1
SpringAtSO=3652090,1079114,5908466,...

4.2. Восстановление данных настройки

Строки данных, сгенерированные на предыдущем шаге, сохраняются в файле setup.properties, который доступен для Spring:

1
2
3
4
5
@Configuration
@PropertySource({ "classpath:setup.properties" })
public class PersistenceJPAConfig {
   //
}

Когда приложение запускается, процесс установки выполняется. Этот простой процесс использует Spring ApplicationListener, прослушивающий ContextRefreshedEvent :

01
02
03
04
05
06
07
08
09
10
11
@Component
public class StackexchangeSetup implements ApplicationListener<ContextRefreshedEvent> {
    private boolean setupDone;
 
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if (!setupDone) {
            recreateAllQuestionsOnAllTwitterAccounts();
            setupDone = true;
        }
    }
}

Наконец, вопросы извлекаются из файла setup.properties и воссоздаются:

01
02
03
04
05
06
07
08
09
10
11
12
13
private void recreateAllQuestionsOnTwitterAccount(String twitterAccount) {
   String tweetedQuestions = env.getProperty(twitterAccount.name();
   String[] questionIds = tweetedQuestions.split(",");
   recreateQuestions(questionIds, twitterAccount);
}
void recreateQuestions(String[] questionIds, String twitterAccount) {
   List<String> stackSitesForTwitterAccount = twitterAccountToStackSites(twitterAccount);
   String site = stackSitesForTwitterAccount.get(0);
   for (String questionId : questionIds) {
      QuestionTweet questionTweet = new QuestionTweet(questionId, twitterAccount, site);
      questionTweetDao.save(questionTweet);
   }
}

Этот простой процесс позволяет легко обновлять структуру БД — поскольку данные полностью стираются и полностью воссоздаются, нет необходимости выполнять какую-либо фактическую миграцию .

5. Полный список счетов

Полный список учетных записей Twitter :

  • SpringAtSOвесенние вопросы от StackOverflow
  • JavaTopSO — вопросы по Java от StackOverflow
  • RESTDailyREST вопросы от StackOverflow
  • BestJPA — вопросы JPA от StackOverflow
  • BestMavenMaven вопросы от StackOverflow
  • BestGitGit вопросы от StackOverflow
  • AskUbuntuBest — лучшие общие вопросы AskUbuntu (все темы)
  • ServerFaultBest — лучшие вопросы по ServerFault (все темы)
  • BestBashлучшие вопросы Bash от StackOverflow, ServerFault и AskUbuntu
  • BestClojure — Clojure вопросы от StackOverflow
  • BestScala — Scala вопросы от StackOverflow
  • BestEclipse — Eclipse вопросы от StackOverflow
  • jQueryDaily — вопросы по jQuery от StackOverflow
  • BestAlgorithms — Алгоритм вопросы от StackOverflow

В каждой из этих учетных записей создается 2 твита в день с вопросами с самым высоким рейтингом по их конкретной теме.

6. Заключение

Эта третья статья завершает серию об интеграции со StackOverflow и другими сайтами StackExchange для получения вопросов через их REST API, а также об интеграции с Twitter и Spring Social, чтобы отправлять их в Твиттере. Потенциальное направление, которое стоит изучить, делает то же самое с Google Plus — возможно, с использованием страниц, а не учетных записей.

14 Учетные записи Twitter созданы и работают в результате этого проекта — фокусируются на различных темах и производят низкообъемный и, надеюсь, высококачественный контент (идеи для других тегов, которые заслуживают свою собственную учетную запись Twitter, приветствуются в комментариях).

Ссылка: Tweeting StackExchange Вопросы с Spring Social от нашего партнера JCG Евгения Параскива в блоге baeldung .