Commit 11af23bd authored by Yannick Kirschhoffer's avatar Yannick Kirschhoffer

Basic retweet feature is now implemented.

parent 5eee2d0a
Pipeline #41702839 passed with stages
in 3 minutes and 26 seconds
package org.alcibiade.socializer.dao;
import org.alcibiade.socializer.domain.TweetEntity;
import org.alcibiade.socializer.properties.AccountProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.social.twitter.api.Tweet;
import org.springframework.social.twitter.api.Twitter;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.Optional;
@Component
public class TwitterDAO {
private final TwitterFactory twitterFactory;
private ThreadLocal<Twitter> twitterThreadLocal = new ThreadLocal<>();
@Autowired
public TwitterDAO(TwitterFactory twitterFactory) {
this.twitterFactory = twitterFactory;
}
public void open(AccountProperties accountProperties) {
Twitter twitter = twitterFactory.createTwitterInstance(accountProperties);
twitterThreadLocal.set(twitter);
}
public void close() {
// TODO: Cleanup twitter resources
}
public Optional<Date> getLastRetweetTime() {
return twitterThreadLocal.get().timelineOperations().getUserTimeline().stream()
.filter(Tweet::isRetweet)
.map(Tweet::getCreatedAt)
.findFirst();
}
public Optional<TweetEntity> getMostRecentMessage(String handle) {
return twitterThreadLocal.get().timelineOperations().getUserTimeline(handle).stream()
.filter(tweet -> !tweet.isRetweet())
.map(tweet -> TweetEntity.builder()
.id(tweet.getId())
.handle(handle)
.createdAt(tweet.getCreatedAt())
.build()
)
.findFirst();
}
public void reTeweet(Long tweetId) {
twitterThreadLocal.get().timelineOperations().retweet(tweetId);
}
}
package org.alcibiade.socializer.dao;
import org.alcibiade.socializer.properties.AccountProperties;
import org.springframework.social.twitter.api.Twitter;
import org.springframework.social.twitter.api.impl.TwitterTemplate;
import org.springframework.stereotype.Component;
@Component
public class TwitterFactory {
public Twitter createTwitterInstance(AccountProperties accountProperties) {
Twitter twitter = new TwitterTemplate(
accountProperties.getApiKey(), accountProperties.getApiSecretKey(),
accountProperties.getAccessToken(), accountProperties.getAccessTokenSecret()
);
return twitter;
}
}
package org.alcibiade.socializer.domain;
import lombok.Builder;
import lombok.Data;
import java.util.Date;
@Data
@Builder
public class TweetEntity {
private Long id;
private String handle;
private Date createdAt;
private String body;
}
package org.alcibiade.socializer.jobs;
import lombok.extern.slf4j.Slf4j;
import org.alcibiade.socializer.properties.SocializerProperties;
import org.alcibiade.socializer.service.TwitterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
......@@ -7,19 +9,28 @@ import org.springframework.stereotype.Component;
import javax.transaction.Transactional;
@Slf4j
@Component
public class RetweetJob {
private TwitterService twitterService;
private SocializerProperties socializerProperties;
@Autowired
public RetweetJob(TwitterService twitterService) {
public RetweetJob(TwitterService twitterService, SocializerProperties socializerProperties) {
this.twitterService = twitterService;
this.socializerProperties = socializerProperties;
}
@Scheduled(initialDelay = 10_000L, fixedRate = 3600_000L)
@Scheduled(initialDelay = 5_000L, fixedRate = 300_000L)
@Transactional
public void retweet() {
twitterService.retweet();
log.info("Processing retweets...");
socializerProperties.getTwitter().getAccounts().entrySet().stream()
.filter(entry -> entry.getValue().getRetweet() != null)
.forEach(entry -> twitterService.retweet(entry.getKey(), entry.getValue()));
log.info("Retweets complete");
}
}
package org.alcibiade.socializer.jobs;
import org.alcibiade.socializer.service.TwitterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.transaction.Transactional;
@Component
public class UpdateActivityJob {
private TwitterService twitterService;
@Autowired
public UpdateActivityJob(TwitterService twitterService) {
this.twitterService = twitterService;
}
@Scheduled(fixedDelay = 60_000L)
@Transactional
public void updateActivity() {
twitterService.updateActivity();
}
}
......@@ -11,7 +11,7 @@ public class AccountProperties {
@NotBlank
private String apiSecretKey;
@NotBlank
private String accessTokens;
private String accessToken;
@NotBlank
private String accessTokenSecret;
......
......@@ -3,12 +3,10 @@ package org.alcibiade.socializer.properties;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
@Data
public class RetweetProperties {
@NotNull
private List<String> filters;
@NotEmpty
private List<String> users;
......
package org.alcibiade.socializer.service;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "twitter.accounts")
public class TwitterFactory {
}
package org.alcibiade.socializer.service;
import lombok.extern.slf4j.Slf4j;
import org.alcibiade.socializer.properties.SocializerProperties;
import org.alcibiade.socializer.dao.TwitterDAO;
import org.alcibiade.socializer.domain.TweetEntity;
import org.alcibiade.socializer.properties.AccountProperties;
import org.alcibiade.socializer.properties.RetweetProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
import java.util.Optional;
@Slf4j
@Service
public class TwitterService {
private final SocializerProperties socializerProperties;
private final TwitterDAO twitterDAO;
@Autowired
public TwitterService(SocializerProperties socializerProperties) {
this.socializerProperties = socializerProperties;
public TwitterService(TwitterDAO twitterDAO) {
this.twitterDAO = twitterDAO;
}
public void retweet(String accountName, AccountProperties accountProperties) {
RetweetProperties retweetProperties = accountProperties.getRetweet();
try {
twitterDAO.open(accountProperties);
Optional<Date> lastRT = twitterDAO.getLastRetweetTime();
log.info("Last retweet of {} is {}", accountName, lastRT);
for (String handle : retweetProperties.getUsers()) {
Optional<TweetEntity> tweet = twitterDAO.getMostRecentMessage(handle);
log.info("Considering re-tweeting {} with last tweet {}", handle, tweet);
if (!filterAccepts(tweet, retweetProperties.getFilters())) {
continue;
}
if (accept(lastRT, tweet)) {
log.info("Re-tweeting tweet from {} created at {}", handle, tweet.get().getCreatedAt());
twitterDAO.reTeweet(tweet.get().getId());
}
}
} finally {
twitterDAO.close();
}
}
public void retweet() {
log.debug("Retweeting for {} twitter accounts",
socializerProperties.getTwitter().getAccounts().size());
private boolean filterAccepts(Optional<TweetEntity> tweet, List<String> filters) {
if (!tweet.isPresent()) {
return false;
}
if (filters == null) {
return true;
}
for (String term : filters) {
if (tweet.get().getBody().toLowerCase().contains(term)) {
return true;
}
}
return false;
}
public void updateActivity() {
log.debug("Updating Activity...");
private boolean accept(Optional<Date> lastRT, Optional<TweetEntity> tweet) {
if (tweet.isPresent()) {
if (lastRT.isPresent()) {
return lastRT.get().before(tweet.get().getCreatedAt());
} else {
return true;
}
} else {
return false;
}
}
}
......@@ -20,7 +20,7 @@ public class SocializerApplicationTests {
public void contextLoads() {
Assertions.assertThat(properties).isNotNull();
Assertions.assertThat(properties.getTwitter()).isNotNull();
Assertions.assertThat(properties.getTwitter().getAccounts()).hasSize(2);
Assertions.assertThat(properties.getTwitter().getAccounts()).hasSize(3);
AccountProperties account1 = properties.getTwitter().getAccounts().get("account1");
......@@ -32,6 +32,12 @@ public class SocializerApplicationTests {
Assertions.assertThat(account2.getAccessTokenSecret()).isEqualTo("ffffffffffffffffffffffffffffffffff");
Assertions.assertThat(account2.getRetweet()).isNull();
AccountProperties account3 = properties.getTwitter().getAccounts().get("account3");
Assertions.assertThat(account3.getAccessTokenSecret()).isEqualTo("hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh");
Assertions.assertThat(account3.getRetweet().getFilters()).isNull();
Assertions.assertThat(account3.getRetweet().getUsers()).isNotEmpty();
}
}
......
......@@ -11,7 +11,7 @@ socializer:
account1:
apiKey: 'aaaaaaaaaaaaaaaaaaaa'
apiSecretKey: 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'
accessTokens: 'ccccccccccccc-cccccccccccccccccc'
accessToken: 'ccccccccccccc-cccccccccccccccccc'
accessTokenSecret: 'dddddddddddddddddddddddddddddddddd'
retweet:
......@@ -23,5 +23,17 @@ socializer:
account2:
apiKey: 'aaaaaaaaaaaaaaaaaaaa'
apiSecretKey: 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'
accessTokens: 'eeeeeeeeeeeee-eeeeeeeeeeeeeeeeee'
accessToken: 'eeeeeeeeeeeee-eeeeeeeeeeeeeeeeee'
accessTokenSecret: 'ffffffffffffffffffffffffffffffffff'
account3:
apiKey: 'aaaaaaaaaaaaaaaaaaaa'
apiSecretKey: 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'
accessToken: 'ggggggggggggg-gggggggggggggggggg'
accessTokenSecret: 'hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh'
retweet:
users:
- retweeteduser1
- retweeteduser2
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment