1. 소개
이것은 소규모 사이드 프로젝트에 대한 세 번째이자 마지막 기사입니다. 이 봇은 다양한 Q&A StackExchange 사이트의 질문을 전문 계정에 자동으로 트윗합니다(기사 끝에 전체 List이 있음).
첫 번째 기사 에서는 StackExchange REST API를 위한 간단한 클라이언트 구축에 대해 설명 했습니다. 두 번째 기사에서는 Spring Social을 사용하여 Twitter와의 상호 작용을 설정했습니다.
이 기사에서는 구현의 마지막 부분인 Stackexchange 클라이언트와 TwitterTemplate 간의 상호 작용을 담당하는 부분을 설명 합니다 .
2. 트윗 스택 교환 서비스
원시 질문을 노출하는 Stackexchange 클라이언트와 완전히 설정되고 트윗할 수 있는 TwitterTemplate 간의 상호 작용은 매우 간단한 서비스인 TweetStackexchangeService 입니다. 이 게시한 API는 다음과 같습니다.
public void tweetTopQuestionBySite(String site, String twitterAccount){ ... }
public void tweetTopQuestionBySiteAndTag(String site, String twitterAccount, String tag){ ... }
기능은 간단합니다. 이러한 API는 해당 특정 계정에서 이전에 트윗되지 않은 질문을 찾을 때까지 (클라이언트를 통해) Stackexchange REST API에서 질문을 계속 읽습니다 .
해당 질문이 발견되면 해당 계정에 해당하는 TwitterTemplate 을 통해 트윗 되고 매우 간단한 질문 엔터티가 로컬에 저장됩니다. 이 엔터티는 질문의 ID와 트윗된 Twitter 계정만 저장합니다.
예를 들어 다음 질문:
질문 엔터티에는 다음이 포함됩니다.
- 질문 의 id – 이 경우 4596351
- 질문이 트윗된 트위터 계정 – SpringAtSO
- 질문이 시작된 Stackexcange 사이트 – Stackoverflow
어떤 질문이 이미 트윗되고 어떤 질문이 그렇지 않은지 알 수 있도록 이 정보를 추적해야 합니다.
3. 스케줄러
스케줄러는 Spring의 예약된 작업 기능을 사용합니다. 이러한 기능은 Java 구성을 통해 활성화됩니다.
@Configuration
@EnableScheduling
public class ContextConfig {
//
}
실제 스케줄러는 비교적 간단합니다.
@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);
}
}
위에 구성된 두 개의 트윗 작업이 있습니다. 하나는 Best Of Clojure 트위터 계정 에서 "clojure"로 태그가 지정된 StackOverflow 질문의 트윗 입니다.
다른 작업은 "bash" 태그가 붙은 질문을 트윗합니다. 이러한 유형의 질문은 실제로 Stackexchange 네트워크의 여러 사이트( StackOverflow , SuperUser 및 AskUbuntu )에 나타나기 때문에 먼저 이러한 사이트 중 하나를 선택하는 빠른 선택 프로세스가 있습니다. 질문이 트윗되었습니다.
마지막으로 cron 작업은 매일 오전 1시와 오전 5시에 실행되도록 예약됩니다.
4. 설정
이것은 애완용 프로젝트 였기 때문에 매우 단순한 데이터베이스 구조 로 시작했습니다 . 지금은 여전히 간단하지만 훨씬 더 그렇습니다. 따라서 주요 목표 중 하나는 데이터베이스 구조를 쉽게 변경할 수 있도록 하는 것이었습니다. 물론 데이터베이스 마이그레이션을 위한 여러 도구 가 있지만 이러한 간단한 프로젝트에는 모두 과도합니다.
그래서 반자동으로 업데이트 되는 간단한 텍스트 형식으로 설정 데이터를 유지하기로 결정했습니다.
설정에는 두 가지 주요 단계가 있습니다.
- 각 트위터 계정에 트윗된 질문의 ID를 검색하여 텍스트 파일에 저장합니다.
- 데이터베이스 스키마가 삭제되고 애플리케이션이 다시 시작됩니다. 이렇게 하면 스키마가 다시 생성되고 텍스트 파일의 모든 데이터가 다시 새 데이터베이스로 설정됩니다.
4.1. 원시 설정 데이터
기존 데이터베이스의 데이터를 검색하는 프로세스는 JDBC로 충분히 간단합니다. 먼저 RowMapper를 정의합니다.
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 계정에 대한 질문 List이 만들어집니다.
다음으로 간단한 테스트에서 이것을 사용할 것입니다.
@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)));
}
}
계정에 대한 질문을 검색한 후 테스트는 단순히 질문을 나열합니다. 예를 들어:
SpringAtSO=3652090,1079114,5908466,...
4.2. 설정 데이터 복원
이전 단계에서 생성된 데이터 라인은 Spring에서 사용할 수 있는 setup.properties 파일 에 저장됩니다.
@Configuration
@PropertySource({ "classpath:setup.properties" })
public class PersistenceJPAConfig {
//
}
응용 프로그램이 시작되면 설정 프로세스가 수행됩니다. 이 간단한 프로세스는 ContextRefreshedEvent 를 수신하는 Spring ApplicationListener를 사용합니다 .
@Component
public class StackexchangeSetup implements ApplicationListener<ContextRefreshedEvent> {
private boolean setupDone;
public void onApplicationEvent(ContextRefreshedEvent event) {
if (!setupDone) {
recreateAllQuestionsOnAllTwitterAccounts();
setupDone = true;
}
}
}
마지막으로 setup.properties 파일에서 질문을 검색하고 다시 만듭니다.
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);
}
}
이 간단한 프로세스를 통해 DB 구조를 쉽게 업데이트할 수 있습니다. 데이터가 완전히 삭제되고 완전히 재생성되기 때문에 실제 마이그레이션을 수행할 필요가 없습니다.
5. 전체 계정 List
Twitter 계정 의 전체 List 은 다음과 같습니다.
- SpringTip – StackOverflow의 스프링 질문
- JavaTopSO – StackOverflow의 Java 질문
- RESTDaily – StackOverflow의 REST 질문
- BestJPA – StackOverflow의 JPA 질문
- MavenFact – StackOverflow의 Maven 질문
- BestGit – StackOverflow의 Git 질문
- AskUbuntuBest – AskUbuntu 최고의 전반적인 질문(모든 주제)
- ServerFaultBest – ServerFault 베스트 질문(모든 주제)
- BestBash – StackOverflow, ServerFault 및 AskUbuntu의 최고의 Bash 질문
- ClojureFact – StackOverflow의 Clojure 질문
- ScalaFact – StackOverflow의 스칼라 질문
- EclipseFacts – StackOverflow의 Eclipse 질문
- jQueryDaily – StackOverflow의 jQuery 질문
- BestAlgorithms – StackOverflow의 알고리즘 질문
각 계정에는 하루에 2개의 트윗이 생성되며 특정 주제에 대해 가장 높은 평가를 받은 질문이 포함됩니다.
6. 결론
이 세 번째 기사에서는 REST API를 통해 질문을 검색하기 위해 StackOverflow 및 기타 StackExchange 사이트와 통합하고 이러한 질문을 트윗하기 위해 Twitter 및 Spring Social과 통합하는 방법에 대한 시리즈를 마칩니다. 조사할 가치가 있는 잠재적인 방향은 Google Plus에서도 동일한 작업을 수행하는 것입니다. 아마도 계정이 아닌 페이지를 사용하는 것입니다.
이 프로젝트의 결과로 14개의 Twitter 계정 이 만들어지고 운영 중입니다. 다양한 주제에 초점을 맞추고 소량의 고품질 콘텐츠를 생산합니다(자신의 Twitter 계정에 적합한 다른 태그에 대한 아이디어는 댓글로 환영합니다).