package net.fortytwo.twitlogic;
import net.fortytwo.twitlogic.model.User;
import net.fortytwo.twitlogic.services.twitter.TwitterClient;
import net.fortytwo.twitlogic.services.twitter.TwitterClientException;
import net.fortytwo.twitlogic.util.properties.PropertyException;
import net.fortytwo.twitlogic.util.properties.TypedProperties;
import org.openrdf.model.URI;
import org.openrdf.model.impl.URIImpl;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Logger;
import java.util.regex.Pattern;
/**
* @author Joshua Shinavier (http://fortytwo.net).
*/
public class TwitLogic {
// Configuration property keys
public static final String
ALLEGROSAIL_HOST = "net.fortytwo.twitlogic.persistence.allegroSailHost",
ALLEGROSAIL_NAME = "net.fortytwo.twitlogic.persistence.allegroSailName",
ALLEGROSAIL_CATALOG_NAME = "net.fortytwo.twitlogic.persistence.allegroSailCatalogName",
ALLEGROSAIL_USERNAME = "net.fortytwo.twitlogic.persistence.allegroSailUserName",
ALLEGROSAIL_PASSWORD = "net.fortytwo.twitlogic.persistence.allegroSailPassword",
ALLOW_ALL_TWEETS = "net.fortytwo.twitlogic.allowAllTweets",
ALLOW_TWEETS_WITH_ANNOTATIONS = "net.fortytwo.twitlogic.allowTweetsWithAnnotations",
ALLOW_TWEETS_WITH_NANOSTATEMENTS = "net.fortytwo.twitlogic.allowTweetsWithNanostatements",
ALLOW_TWEETS_WITH_TOPICS = "net.fortytwo.twitlogic.allowTweetsWithTopics",
ALLOW_TWEETS_WITH_LINKS = "net.fortytwo.twitlogic.allowTweetsWithLinks",
ALLOW_TWEETS_WITH_LOCATION = "net.fortytwo.twitlogic.allowTweetsWithLocation",
ALLOW_TWEETS_WITH_PLACE = "net.fortytwo.twitlogic.allowTweetsWithPlace",
AVOID_REDUNDANT_TYPE_DESIGNATION = "net.fortytwo.twitlogic.persistence.avoidRedundantTypeDesignation",
BITLY_APIKEY = "net.fortytwo.twitlogic.services.bitly.apiKey",
BITLY_LOGIN = "net.fortytwo.twitlogic.services.bitly.login",
COVERAGE_INTERVAL_END = "net.fortytwo.twitlogic.coverageIntervalEnd",
COVERAGE_INTERVAL_START = "net.fortytwo.twitlogic.coverageIntervalStart",
DUMP_FILE = "net.fortytwo.twitlogic.persistence.dump.file",
DUMP_INTERVAL = "net.fortytwo.twitlogic.persistence.dump.interval",
FOLLOWFOLLOWED = "net.fortytwo.twitlogic.followFollowed",
FOLLOWLIST = "net.fortytwo.twitlogic.followList",
FOLLOWUSER = "net.fortytwo.twitlogic.followUser",
NATIVESTORE_DIRECTORY = "net.fortytwo.twitlogic.persistence.nativeStoreDirectory",
NATIVESTORE_INDEXES = "net.fortytwo.twitlogic.persistence.nativeStoreIndexes",
NEO4J_DIRECTORY = "net.fortytwo.twitlogic.persistence.neo4jDirectory",
METADATA_EXPIRE_TIME = "net.fortytwo.twitlogic.persistence.metadataExpireTime",
METADATA_FOLLOWEE_LIMIT = "net.fortytwo.twitlogic.persistence.metadataFolloweeLimit",
RDFAGENTS_PLATFORM_NAME = "net.fortytwo.twitlogic.rdfagents.platformName",
RDFAGENTS_PLATFORM_PORT = "net.fortytwo.twitlogic.rdfagents.platformPort",
RDFAGENTS_AGENT_NAME = "net.fortytwo.twitlogic.rdfagents.agentName",
SAIL_CLASS = "net.fortytwo.twitlogic.persistence.sailClass",
SERVER_BASEURI = "net.fortytwo.twitlogic.server.baseURI",
SERVER_PORT = "net.fortytwo.twitlogic.server.port",
SERVER_STATICCONTENTDIRECTORY = "net.fortytwo.twitlogic.server.staticContentDirectory",
LOGGING_STATSINTERVAL = "net.fortytwo.twitlogic.logging.statsInterval",
TRACKTERMS = "net.fortytwo.twitlogic.trackTerms",
GEOBOX = "net.fortytwo.twitlogic.geobox",
GEODISC = "net.fortytwo.twitlogic.geodisc",
TWITTER_ACCESS_TOKEN = "net.fortytwo.twitlogic.twitter.accessToken",
TWITTER_ACCESS_TOKEN_SECRET = "net.fortytwo.twitlogic.twitter.accessTokenSecret",
TWITTER_CONSUMER_KEY = "net.fortytwo.twitlogic.twitter.consumerKey",
TWITTER_CONSUMER_SECRET = "net.fortytwo.twitlogic.twitter.consumerSecret",
TWITTER_PASSWORD = "net.fortytwo.twitlogic.twitter.password",
TWITTER_USERNAME = "net.fortytwo.twitlogic.twitter.username",
TWITTER_WHITELISTED = "net.fortytwo.twitlogic.twitter.whitelisted",
UDP_REMOTEHOST = "net.fortytwo.twitlogic.persistence.udp.remoteHost",
UDP_REMOTEPORTS = "net.fortytwo.twitlogic.persistence.udp.remotePorts",
XMPP_PORT = "net.fortytwo.twitlogic.xmpp.port",
XMPP_REASONER_PASSWORD = "net.fortytwo.twitlogic.xmpp.reasonerPassword",
XMPP_REASONER_USERNAME = "net.fortytwo.twitlogic.xmpp.reasonerUsername",
XMPP_REPORTER_PASSWORD = "net.fortytwo.twitlogic.xmpp.reporterPassword",
XMPP_REPORTER_USERNAME = "net.fortytwo.twitlogic.xmpp.reporterUsername",
XMPP_SERVER = "net.fortytwo.twitlogic.xmpp.server";
// Note: keep these in agreement with the ResourceType enum.
public static final String
BASE_URI = "http://twitlogic.fortytwo.net/",
DATASETS_BASEURI = BASE_URI + "dataset/",
DUMPS_BASEURI = BASE_URI + "dump/",
GRAPHS_BASEURI = BASE_URI + "graph/",
DOLLARTAGS_BASEURI = BASE_URI + "dollartag/",
HASHTAGS_BASEURI = BASE_URI + "hashtag/",
LOCATIONS_BASEURI = BASE_URI + "location/",
MISCELLANEOUS_BASEURI = BASE_URI + "miscellaneous/",
PERSONS_BASEURI = BASE_URI + "person/twitter/",
TWEETS_BASEURI = BASE_URI + "post/twitter/",
TWITTER_PLACE_BASEURI = BASE_URI + "location/twitter/",
USERS_BASEURI = BASE_URI + "user/twitter/";
// This graph contains all of TwitLogic's authoritative metadata about
// named graphs, microblog posts, microblog authors, etc., as well as
// voiD metadata about the knowledge base itself.
public static final URI
CORE_GRAPH = new URIImpl(GRAPHS_BASEURI + "core");
// Other special resources
public static final String
TWITLOGIC_DATASET = DATASETS_BASEURI + "twitlogic-full",
SEMANTICTWEET_DATASET = DATASETS_BASEURI + "semantictweet",
SEMANTICTWEET_LINKSET1 = DATASETS_BASEURI + "semantictweet-linkset1";
public static final Pattern
CONFIG_LIST_PATTERN = Pattern.compile("[A-Za-z0-9-_]+/[A-Za-z0-9-_]+"),
CONFIG_USERNAME_PATTERN = Pattern.compile("[A-Za-z0-9-_]+");
private static TypedProperties CONFIGURATION;
private static final Logger LOGGER;
static {
CONFIGURATION = new TypedProperties();
/*
try {
CONFIGURATION.load(TwitLogic.class.getResourceAsStream("twitlogic.properties"));
} catch (IOException e) {
throw new ExceptionInInitializerError(e);
}*/
LOGGER = getLogger(TwitLogic.class);
LOGGER.info("initialized logging");
}
public enum ResourceType {
DataSet("dataset", "data set"),
Graph("graph", "named graph"),
Hashtag("hashtag", "hashtag resource"),
Location("location", "location"),
Miscellaneous("miscellaneous", "miscellaneous resource"),
Person("person", "person"),
Post("post", "microblog post"),
User("user", "microblog user account");
private final String uriPath;
private final String name;
private ResourceType(final String uriPath,
final String name) {
this.uriPath = uriPath;
this.name = name;
}
public String getUriPath() {
return uriPath;
}
public String getName() {
return name;
}
public static ResourceType findByName(final String name) {
for (ResourceType t : ResourceType.values()) {
if (t.name().equals(name)) {
return t;
}
}
return null;
}
}
public static String getName() {
return "TwitLogic";
}
public static String getVersion() {
return "0.6";
}
public static TypedProperties getConfiguration() {
return CONFIGURATION;
}
public static void setConfiguration(final Properties properties) {
CONFIGURATION = new TypedProperties(properties);
}
public static Logger getLogger(final Class c) {
return Logger.getLogger(c.getName());
}
public static Set<String> findTrackTerms() throws PropertyException {
TypedProperties props = TwitLogic.getConfiguration();
// Note: this doesn't really need to be an order-preserving collection,
// because Java properties are not order-preserving.
Set<String> terms = new LinkedHashSet<String>();
for (String key : props.stringPropertyNames()) {
if (key.startsWith(TwitLogic.TRACKTERMS)) {
String listVal = props.getString(key);
String[] these = listVal.split(",");
for (String t : these) {
t = t.trim();
if (0 < t.length()) {
terms.add(t);
}
}
}
}
return terms;
}
public static TwitterClient.GeoDisc findGeoDisc() throws PropertyException {
TypedProperties props = TwitLogic.getConfiguration();
String s = props.getString(TwitLogic.GEODISC, null);
return null == s ? null : new TwitterClient.GeoDisc(s);
}
public static double[][] findGeoBoxes() throws PropertyException {
TypedProperties props = TwitLogic.getConfiguration();
Set<String> matches = new LinkedHashSet<String>();
for (String key : props.stringPropertyNames()) {
if (key.startsWith(TwitLogic.GEOBOX)) {
String listVal = props.getString(key);
String[] these = listVal.split(",");
for (String t : these) {
t = t.trim();
if (0 < t.length()) {
matches.add(t);
}
}
}
}
if (matches.size() > 0) {
double[][] results = new double[matches.size()][2];
int i = 0;
for (String s : matches) {
String[] a = s.split(";");
if (2 != a.length) {
throw new PropertyException("location value '" + s + "' does not have the format long;lat");
}
results[i][0] = Double.valueOf(a[0]);
results[i][1] = Double.valueOf(a[1]);
i++;
}
return results;
} else {
return new double[][]{};
}
}
// Note: for now, lists are not persisted in any way
public static Set<User> findFollowList(final TwitterClient client) throws TwitterClientException, PropertyException {
TypedProperties props = TwitLogic.getConfiguration();
// Note: this doesn't really need to be an order-preserving collection,
// because Java properties are not order-preserving.
Set<User> users = new LinkedHashSet<User>();
for (String key : props.stringPropertyNames()) {
if (key.startsWith(TwitLogic.FOLLOWLIST)) {
String listVal = props.getString(key);
if (!CONFIG_LIST_PATTERN.matcher(listVal).matches()) {
throw new PropertyException("invalid list: " + listVal + " (should be of the form user_name/list_id)");
}
String[] parts = listVal.split("/");
User user = new User(parts[0]);
String listId = parts[1];
List<User> l = client.getListMembers(user, listId);
users.addAll(l);
} else if (key.startsWith(TwitLogic.FOLLOWUSER)) {
String screenName = props.getString(key);
if (!CONFIG_USERNAME_PATTERN.matcher(screenName).matches()) {
throw new PropertyException("invalid screen name: " + screenName);
}
// Twitter requires user IDs (as opposed to screen names) for follow filters.
User u = client.findUserInfo(screenName);
users.add(u);
} else if (key.startsWith(TwitLogic.FOLLOWFOLLOWED)) {
String screenName = props.getString(key);
if (!CONFIG_USERNAME_PATTERN.matcher(screenName).matches()) {
throw new PropertyException("invalid screen name: " + screenName);
}
// Twitter requires user IDs (as opposed to screen names) for follow filters.
User u = client.findUserInfo(screenName);
List<User> l = client.getFollowees(u, -1);
users.addAll(l);
}
}
if (0 == users.size()) {
LOGGER.info("following 0 users");
}
return users;
}
}