package info.persistent.pushbot.commands;
import com.google.appengine.api.xmpp.JID;
import com.google.common.collect.ImmutableSet;
import com.sun.syndication.feed.synd.SyndFeed;
import info.persistent.pushbot.data.Subscription;
import info.persistent.pushbot.util.Feeds;
import info.persistent.pushbot.util.Hubs;
import info.persistent.pushbot.util.Persistence;
import info.persistent.pushbot.util.Xmpp;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.jdo.PersistenceManager;
public class SubscribeCommandHandler extends FeedCommandHandler {
public static final Logger logger =
Logger.getLogger(SubscribeCommandHandler.class.getName());
private static final Set<String> FEEDBURNER_HOSTS = ImmutableSet.of(
"feeds.feedburner.com",
"feeds2.feedburner.com",
"feedproxy.google.com");
private static final String FEEDBURNER_HUB_HOST = "pubsubhubbub.appspot.com";
@Override
protected void handle(JID user, URL feedUrl) {
subscribeToFeed(user, feedUrl);
}
static void subscribeToFeed(JID user, URL feedUrl) {
SyndFeed feed = fetchAndParseFeed(user, feedUrl);
if (feed == null) {
return;
}
// If possible, subscribe to the self URL, since presumably that's the one
// that the hub knows about.
List<URL> selfUrls = Feeds.getLinkUrl(feed, Feeds.SELF_RELATION);
if (!selfUrls.isEmpty()) {
feedUrl = selfUrls.get(0);
}
List<URL> hubUrls = Feeds.getLinkUrl(feed, Feeds.HUB_RELATION);
URL hubUrl = null;
for (URL candidateHubUrl : hubUrls) {
// Require absolute URLs
if (candidateHubUrl.getHost() == null ||
candidateHubUrl.getHost().isEmpty()) {
continue;
}
// If it's a burned feed, then we want to use the FeedBurner hub, not
// another one (which doesn't know about the burned feed URL). This can
// happen with TypePad.
if (FEEDBURNER_HOSTS.contains(feedUrl.getHost()) &&
!candidateHubUrl.getHost().equals(FEEDBURNER_HUB_HOST)) {
continue;
}
hubUrl = candidateHubUrl;
continue;
}
if (hubUrl == null) {
Xmpp.sendMessage(
user, "The feed " + feedUrl + " is not associated with a hub");
return;
}
saveSubscription(user, feedUrl, hubUrl, feed.getTitle());
Hubs.sendRequestToHub(user, hubUrl, feedUrl, true);
}
private static SyndFeed fetchAndParseFeed(JID user, URL feedUrl) {
SyndFeed feed;
try {
feed = Feeds.parseFeed(feedUrl.openStream());
} catch (IOException err) {
logger.log(Level.INFO, "Could not fetch feed " + feedUrl, err);
Xmpp.sendMessage(user, "Could not fetch feed " + feedUrl);
return null;
}
if (feed == null) {
Xmpp.sendMessage(user, "Could not parse feed " + feedUrl);
}
return feed;
}
private static void saveSubscription(
JID user, URL feedUrl, URL hubUrl, String title) {
List<Subscription> existingSubscriptions =
Subscription.getSubscriptionsForUserAndFeedUrl(user, feedUrl);
if (!existingSubscriptions.isEmpty()) {
Xmpp.sendMessage(user, "You're already subscribed to " + feedUrl +
" (sending request to hub anyway, in case it's out of sync)");
return;
}
final Subscription subscription =
new Subscription(user, feedUrl, hubUrl, title);
Persistence.withManager(new Persistence.Closure() {
@Override
public void run(PersistenceManager manager) {
manager.makePersistent(subscription);
}
});
}
}