package controllers; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import net.nationstatesplusplus.assembly.model.websocket.DataRequest; import net.nationstatesplusplus.assembly.model.websocket.PageType; import net.nationstatesplusplus.assembly.model.websocket.RequestType; import net.nationstatesplusplus.assembly.util.DatabaseAccess; import net.nationstatesplusplus.assembly.util.Utils; import org.apache.commons.dbutils.DbUtils; import org.apache.commons.lang.builder.ToStringBuilder; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; import org.joda.time.Duration; import org.spout.cereal.config.yaml.YamlConfiguration; import play.libs.Akka; import play.libs.F.Function; import play.libs.F.Promise; import play.libs.Json; import play.mvc.Http.Request; import play.mvc.Http.Response; import play.mvc.Result; import play.mvc.Results; import com.limewoodMedia.nsapi.NationStates; public class RMBController extends NationStatesController { public RMBController(DatabaseAccess access, YamlConfiguration config, NationStates api) { super(access, config, api); } public Result ratePost(int rmbPost, int rating) throws SQLException { Result ret = Utils.validateRequest(request(), response(), getAPI(), getDatabase()); if (ret != null) { return ret; } if (rmbPost < 12 || rating > 1) { return Results.badRequest(); } final String nation = Utils.sanitizeName(Utils.getPostValue(request(), "nation")); final int nationId = getDatabase().getNationId(nation); if (nationId == -1) { return Results.badRequest(); } try (Connection conn = getConnection()) { JsonNode ratings = rateRMBPost(getDatabase(), conn, nation, nationId, rmbPost, rating); Map<String, Object> data = new HashMap<String, Object>(); data.put("rmb_post_id", rmbPost); getDatabase().getWebsocketManager().onUpdate(PageType.REGION, RequestType.RMB_RATINGS, new DataRequest(RequestType.RMB_RATINGS, data), ratings); } Utils.handleDefaultPostHeaders(request(), response()); return Results.ok(); } public static JsonNode rateRMBPost(DatabaseAccess access, Connection conn, String nation, int nationId, int rmbPost, int rating) throws SQLException { if (rating < 0) { PreparedStatement delete = conn.prepareStatement("DELETE FROM assembly.rmb_ratings WHERE nation = ? AND rmb_post = ?"); delete.setInt(1, nationId); delete.setInt(2, rmbPost); delete.executeUpdate(); DbUtils.closeQuietly(delete); } else { PreparedStatement update = conn.prepareStatement("UPDATE assembly.rmb_ratings SET rating_type = ? WHERE nation = ? AND rmb_post = ?"); update.setInt(1, rating); update.setInt(2, nationId); update.setInt(3, rmbPost); if (update.executeUpdate() == 0) { PreparedStatement insert = conn.prepareStatement("INSERT INTO assembly.rmb_ratings (nation, nation_name, rating_type, rmb_post) VALUES (?, ?, ?, ?)"); insert.setInt(1, nationId); insert.setString(2, nation); insert.setInt(3, rating); insert.setInt(4, rmbPost); insert.executeUpdate(); DbUtils.closeQuietly(insert); } DbUtils.closeQuietly(update); } PreparedStatement update = conn.prepareStatement("UPDATE assembly.region SET rmb_cache = rmb_cache + 1 WHERE id = (SELECT region FROM assembly.nation WHERE nation.id = ?)"); update.setInt(1, nationId); update.executeUpdate(); DbUtils.closeQuietly(update); return calculateTotalPostRatings(access, conn, rmbPost); } private static Function<JsonNode, Promise<Result>> getAsyncResult(final Request request, final Response response, final String cacheLen) { return new Function<JsonNode, Promise<Result>>() { @Override public Promise<Result> apply(final JsonNode node) throws Throwable { return Promise.wrap(akka.dispatch.Futures.future((new Callable<Result>() { @Override public Result call() throws Exception { Result result = Utils.handleDefaultGetHeaders(request, response, String.valueOf(node.hashCode()), cacheLen); if (result != null) { return result; } return Results.ok(node).as("application/json"); } }), Akka.system().dispatcher())); } }; } public Promise<Result> getPostRatings(final int rmbPost, final int rmbCache) throws SQLException { Promise<JsonNode> promise = Promise.wrap(akka.dispatch.Futures.future((new Callable<JsonNode>() { @Override public JsonNode call() throws Exception { Connection conn = null; try { conn = getConnection(); JsonNode ratings = calculatePostRatings(conn, rmbPost); return ratings; } finally { DbUtils.closeQuietly(conn); } } }), Akka.system().dispatcher())); Promise<Result> result = promise.flatMap(getAsyncResult(request(), response(), rmbCache == -1 ? "10" : "86400")); return result; } @Deprecated public static JsonNode calculatePostRatings(Connection conn, int rmbPost) throws SQLException { List<Map<String, String>> list = new ArrayList<Map<String, String>>(); PreparedStatement select = conn.prepareStatement("SELECT nation_name, rating_type FROM assembly.rmb_ratings WHERE rmb_post = ?"); select.setInt(1, rmbPost); ResultSet result = select.executeQuery(); while(result.next()) { Map<String, String> ratings = new HashMap<String, String>(2); ratings.put("nation", result.getString(1)); ratings.put("type", String.valueOf(result.getInt(2))); ratings.put("rmb_post", String.valueOf(rmbPost)); list.add(ratings); } DbUtils.closeQuietly(result); DbUtils.closeQuietly(select); return Json.toJson(list); } public static JsonNode calculateTotalPostRatings(DatabaseAccess access, Connection conn, int rmbPost) throws SQLException { List<Map<String, String>> list = new ArrayList<Map<String, String>>(); PreparedStatement select = conn.prepareStatement("SELECT nation_name, rating_type FROM assembly.rmb_ratings WHERE rmb_post = ?"); select.setInt(1, rmbPost); ResultSet result = select.executeQuery(); while(result.next()) { Map<String, String> ratings = new HashMap<String, String>(2); ratings.put("nation", access.getNationTitle(result.getString(1))); ratings.put("type", String.valueOf(result.getInt(2))); list.add(ratings); } DbUtils.closeQuietly(result); DbUtils.closeQuietly(select); Map<String, Object> postRatings = new HashMap<String, Object>(); postRatings.put("rmb_post", rmbPost); postRatings.put("ratings", list); return Json.toJson(postRatings); } public Result getRMBCache(String region) throws SQLException { Connection conn = getConnection(); Map<String, Integer> cache = new HashMap<String, Integer>(2); try { PreparedStatement select = conn.prepareStatement("SELECT rmb_cache FROM assembly.region WHERE name = ?"); select.setString(1, region); ResultSet result = select.executeQuery(); while(result.next()) { cache.put("rmb_cache", result.getInt(1)); } DbUtils.closeQuietly(result); DbUtils.closeQuietly(select); } finally { DbUtils.closeQuietly(conn); } Result result = Utils.handleDefaultGetHeaders(request(), response(), String.valueOf(cache.hashCode()), "10"); if (result != null) { return result; } return Results.ok(Json.toJson(cache)).as("application/json"); } public Result hasComments(int rmbPost) throws SQLException { Connection conn = getConnection(); Map<String, Integer> comments = new HashMap<String, Integer>(1); try { PreparedStatement select = conn.prepareStatement("SELECT count(id) FROM assembly.rmb_comments WHERE rmb_message_id = ?"); select.setInt(1, rmbPost); ResultSet result = select.executeQuery(); if(result.next()) { comments.put("comments", result.getInt(1)); } else { comments.put("comments", 0); } DbUtils.closeQuietly(result); DbUtils.closeQuietly(select); } finally { DbUtils.closeQuietly(conn); } Result result = Utils.handleDefaultGetHeaders(request(), response(), String.valueOf(comments.hashCode())); if (result != null) { return result; } return Results.ok(Json.toJson(comments)).as("application/json"); } public Result getComments(int rmbPost) throws SQLException, ExecutionException { Connection conn = getConnection(); List<RMBComment> list = new ArrayList<RMBComment>(); try { PreparedStatement select = conn.prepareStatement("SELECT c.id, c.timestamp, c.comment, c.nation_id, sum(a.like) as likes, sum(a.flag) as flags FROM assembly.rmb_comments AS c LEFT OUTER JOIN assembly.rmb_comment_actions AS a ON a.rmb_comment_id = c.id WHERE c.rmb_message_id = ? GROUP BY c.id ORDER BY c.timestamp ASC"); select.setInt(1, rmbPost); ResultSet result = select.executeQuery(); while(result.next()) { list.add(new RMBComment(result.getInt(1), result.getLong(2), result.getString(3), getDatabase().getReverseIdCache().get(result.getInt(4)), result.getInt(5), result.getInt(6))); } DbUtils.closeQuietly(result); DbUtils.closeQuietly(select); } finally { DbUtils.closeQuietly(conn); } Result result = Utils.handleDefaultGetHeaders(request(), response(), String.valueOf(list.hashCode())); if (result != null) { return result; } return Results.ok(Json.toJson(list)).as("application/json"); } public static class RMBComment { RMBComment(int id, long timestamp, String comment, String nation, int likes, int flags) { this.id = id; this.timestamp = timestamp; this.comment = comment; this.nation = nation; this.likes = likes; this.flags = flags; } @JsonProperty int id; @JsonProperty long timestamp; @JsonProperty String comment; @JsonProperty String nation; @JsonProperty int likes; @JsonProperty int flags; @Override public int hashCode() { return id; } @Override public boolean equals(Object obj) { if (obj instanceof RMBComment) { return ((RMBComment)obj).id == id; } return false; } @Override public String toString() { return ToStringBuilder.reflectionToString(this); } } private int getRMBCommentNation(Connection conn, int commentId) throws SQLException { PreparedStatement select = null; ResultSet result = null; try { select = conn.prepareStatement("SELECT nation_id FROM assembly.rmb_comments WHERE id = ?"); select.setInt(1, commentId); result = select.executeQuery(); if (result.next()) { return result.getInt(1); } return -1; } finally { DbUtils.closeQuietly(result); DbUtils.closeQuietly(select); } } public Result flagComment(int commentId, boolean flag) throws SQLException { Result invalid = Utils.validateRequest(request(), response(), getAPI(), getDatabase()); if (invalid != null) { return invalid; } String nation = Utils.getPostValue(request(), "nation"); final int nationId = getDatabase().getNationId(nation); Connection conn = getConnection(); if (getRMBCommentNation(conn, commentId) == nationId) { Utils.handleDefaultGetHeaders(request(), response(), null, "0"); return Results.badRequest(); } try { PreparedStatement select = conn.prepareStatement("SELECT id, like FROM assembly.rmb_comment_actions WHERE rmb_message_id = ? AND nation_id = ?"); select.setInt(1, commentId); select.setInt(2, nationId); ResultSet result = select.executeQuery(); if (result.next()) { int id = result.getInt(1); byte liked = result.getByte(2); PreparedStatement update = conn.prepareStatement("UPDATE assembly.rmb_comment_actions SET flag = ?, like = ? WHERE id = ?"); update.setByte(1, (byte) (flag ? 1 : 0)); update.setByte(2, (byte) (flag ? 0 : liked)); update.setInt(3, id); update.executeUpdate(); DbUtils.closeQuietly(update); } else { PreparedStatement update = conn.prepareStatement("INSERT INTO assembly.rmb_comment_actions (rmb_comment_id, nation_id, flag, like) VALUES (?, ?, ?, )"); update.setInt(1, commentId); update.setInt(2, nationId); update.setByte(3, (byte) 1); update.setByte(4, (byte) 0); update.executeUpdate(); DbUtils.closeQuietly(update); } DbUtils.closeQuietly(result); DbUtils.closeQuietly(select); } finally { DbUtils.closeQuietly(conn); } Utils.handleDefaultGetHeaders(request(), response(), null, "0"); return Results.ok(); } public Result likeComment(int commentId, boolean like) throws SQLException { Result invalid = Utils.validateRequest(request(), response(), getAPI(), getDatabase()); if (invalid != null) { return invalid; } String nation = Utils.getPostValue(request(), "nation"); final int nationId = getDatabase().getNationId(nation); Connection conn = getConnection(); if (getRMBCommentNation(conn, commentId) == nationId) { Utils.handleDefaultGetHeaders(request(), response(), null, "0"); return Results.badRequest(); } try { PreparedStatement select = conn.prepareStatement("SELECT id, flag FROM assembly.rmb_comment_actions WHERE rmb_message_id = ? AND nation_id = ?"); select.setInt(1, commentId); select.setInt(2, nationId); ResultSet result = select.executeQuery(); if (result.next()) { int id = result.getInt(1); byte flagged = result.getByte(2); PreparedStatement update = conn.prepareStatement("UPDATE assembly.rmb_comment_actions SET flag = ?, like = ? WHERE id = ?"); update.setByte(1, (byte) (like ? 0 : flagged)); update.setByte(2, (byte) (like ? 1 : 0)); update.setInt(3, id); update.executeUpdate(); DbUtils.closeQuietly(update); } else { PreparedStatement update = conn.prepareStatement("INSERT INTO assembly.rmb_comment_actions (rmb_comment_id, nation_id, flag, like) VALUES (?, ?, ?, )"); update.setInt(1, commentId); update.setInt(2, nationId); update.setByte(3, (byte) 0); update.setByte(4, (byte) 1); update.executeUpdate(); DbUtils.closeQuietly(update); } DbUtils.closeQuietly(result); DbUtils.closeQuietly(select); } finally { DbUtils.closeQuietly(conn); } Utils.handleDefaultGetHeaders(request(), response(), null, "0"); return Results.ok(); } public Result addComment(int rmbPost) throws SQLException { Result invalid = Utils.validateRequest(request(), response(), getAPI(), getDatabase()); if (invalid != null) { return invalid; } final String comment = Utils.getPostValue(request(), "comment"); if (comment == null) { Utils.handleDefaultGetHeaders(request(), response(), null, "0"); return Results.badRequest(); } final String nation = Utils.getPostValue(request(), "nation"); final int nationId = getDatabase().getNationId(nation); Connection conn = getConnection(); try { PreparedStatement select = conn.prepareStatement("SELECT timestamp FROM assembly.rmb_comments WHERE rmb_message_id = ? AND nation_id = ? AND timestamp > ?"); select.setInt(1, rmbPost); select.setInt(2, nationId); select.setLong(3, System.currentTimeMillis() - Duration.standardSeconds(10).getMillis()); ResultSet result = select.executeQuery(); if (result.next()) { Utils.handleDefaultGetHeaders(request(), response(), null, "0"); return Results.status(429); } DbUtils.closeQuietly(result); DbUtils.closeQuietly(select); PreparedStatement insert = conn.prepareStatement("INSERT INTO assembly.rmb_comments (rmb_message_id, timestamp, comment, nation_id) VALUES (?, ?, ?, ?)"); insert.setInt(1, rmbPost); insert.setLong(2, System.currentTimeMillis()); insert.setString(3, comment); insert.setInt(4, nationId); DbUtils.closeQuietly(insert); } finally { DbUtils.closeQuietly(conn); } Utils.handleDefaultGetHeaders(request(), response(), null, "0"); return Results.ok(); } }