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
- RESTDaily — REST вопросы от StackOverflow
- BestJPA — вопросы JPA от StackOverflow
- BestMaven — Maven вопросы от StackOverflow
- BestGit — Git вопросы от 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, приветствуются в комментариях).