package com.alecgorge.minecraft.jsonapi.adminium;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.json.simpleForBukkit.JSONObject;
import com.alecgorge.java.http.MutableHttpRequest;
import com.alecgorge.minecraft.jsonapi.JSONAPI;
import com.alecgorge.minecraft.jsonapi.api.APIMethodName;
import com.alecgorge.minecraft.jsonapi.api.JSONAPICallHandler;
import com.alecgorge.minecraft.jsonapi.api.JSONAPIStream;
import com.alecgorge.minecraft.jsonapi.api.JSONAPIStreamListener;
import com.alecgorge.minecraft.jsonapi.api.JSONAPIStreamMessage;
import com.alecgorge.minecraft.jsonapi.streams.ConnectionMessage;
import com.alecgorge.minecraft.jsonapi.util.FixedSizeArrayList;
public class PushNotificationDaemon implements JSONAPIStreamListener, JSONAPICallHandler {
public class AdminiumPushNotification {
Date dateSent;
String notification;
public AdminiumPushNotification(Date d, String message) {
dateSent = d;
notification = message;
}
public Date getDateSent() {
return dateSent;
}
public void setDateSent(Date dateSent) {
this.dateSent = dateSent;
}
public String getMessage() {
return notification;
}
public void setMessage(String notification) {
this.notification = notification;
}
public String getPushNotification() {
JSONAPI api = JSONAPI.instance;
String messager = getMessage();
if (api.serverName != null && !api.serverName.isEmpty()) {
messager = (api.serverName.equals("default") ? api.getServer().getServerName() : api.serverName) + ": " + messager;
}
return messager.length() > 210 ? messager.substring(0, 208) + "\u2026" : messager;
}
}
YamlConfiguration deviceConfig = new YamlConfiguration();
File configFile;
List<String> devices = new ArrayList<String>();
Map<String, Boolean> settings = new HashMap<String, Boolean>();
Map<String, Map<String, Boolean>> deviceOverrides = new HashMap<String, Map<String, Boolean>>();
// log /calladmin & severes
List<String> calladmins = Collections.synchronizedList(new FixedSizeArrayList<String>(50));
List<String> severeLogs = Collections.synchronizedList(new FixedSizeArrayList<String>(50));
List<AdminiumPushNotification> notifications = Collections.synchronizedList(new FixedSizeArrayList<AdminiumPushNotification>(150));
private final String APNS_PUSH_ENDPOINT = "http://push.adminiumapp.com/push-message";
private JSONAPI api;
public boolean init = false;
public final boolean doTrace = false;
private Logger mcLog = Logger.getLogger("Minecraft");
private void trace(Object... args) {
if (doTrace) {
String[] na = new String[args.length];
for (int i = 0; i < args.length; i++)
na[i] = args[i] == null ? "NULL_VALUE" : args[i].toString();
mcLog.info("'" + api.join(Arrays.asList(na), "' '") + "'");
}
}
private List<String> pushTypes = new ArrayList<String>();
private List<String> pushTypeDescriptions;
public PushNotificationDaemon(File configFile, JSONAPI api) throws FileNotFoundException, IOException, InvalidConfigurationException {
this.configFile = configFile;
this.api = api;
api.registerAPICallHandler(this);
if (configFile.exists()) {
initalize();
}
}
public boolean calladmin(CommandSender from, String message) {
if (api.anyoneCanUseCallAdmin || from.hasPermission("jsonapi.calladmin")) {
String push = "Admin request from " + from.getName() + ": " + message;
if (!(settings.get("admin_call") != null && settings.get("admin_call"))) {
from.sendMessage("The admin has disabled /calladmin.");
return true;
}
calladmins.add(0, push);
pushNotification(push, "admin_call");
from.sendMessage("A message was sent to the admin(s).");
} else if (!from.hasPermission("jsonapi.calladmin")) {
from.sendMessage("You don't have the jsonapi.calladmin permission to call for an admin.");
}
return true;
}
public class ConsoleHandler extends Handler {
PushNotificationDaemon p;
long lastNotification;
public ConsoleHandler(PushNotificationDaemon d) {
p = d;
lastNotification = 0;
}
@Override
public void close() throws SecurityException {
// TODO Auto-generated method stub
}
@Override
public void flush() {
// TODO Auto-generated method stub
}
@Override
public void publish(LogRecord arg0) {
if (settings != null && arg0 != null && arg0.getLevel().equals(Level.SEVERE)) {
String message = "SEVERE message logged in the console: " + arg0.getMessage();
severeLogs.add(0, message);
if (settings.get("severe_log") != null && settings.get("severe_log")) {
long time = (new Date()).getTime();
if (time - lastNotification > 60 * 1000) {
lastNotification = time;
p.pushNotification(message, "severe_log");
}
}
}
}
}
public void addDeviceIfNotExist(String device) throws IOException {
if (!devices.contains(device)) {
registerDevice(device);
}
}
private void registerDevice(final String device) {
trace("Attempting to register", device);
if (device.length() != 64 || devices.contains(device)) {
return;
}
devices.add(device);
deviceConfig.set("devices", devices);
try {
deviceConfig.save(configFile);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void deregisterDevice(final String device) {
trace("Attempting to deregister", device);
if (device.length() != 64 || !devices.contains(device)) {
return;
}
devices.remove(device);
deviceConfig.set("devices", devices);
try {
deviceConfig.save(configFile);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void onMessage(JSONAPIStreamMessage message, JSONAPIStream sender) {
trace(message);
if (message instanceof ConnectionMessage) {
ConnectionMessage c = (ConnectionMessage) message;
if (settings.get("player_joined") != null && settings.get("player_joined") && c.TrueIsConnectedFalseIsDisconnected) {
pushNotification(c.player + " joined!", "player_joined");
} else if (settings.get("player_quit") != null && settings.get("player_quit") && !c.TrueIsConnectedFalseIsDisconnected) {
pushNotification(c.player + " quit!", "player_quit");
}
}
}
public void pushNotification(String messager, final String type) {
if (devices.size() < 1) {
return;
}
final AdminiumPushNotification not = new AdminiumPushNotification(new Date(), messager);
notifications.add(not);
new Thread(new Runnable() {
@Override
public void run() {
String msg = not.getPushNotification();
trace("pushing", msg);
MutableHttpRequest r = null;
try {
r = new MutableHttpRequest(new URL(APNS_PUSH_ENDPOINT));
for (String d : devices) {
if (deviceOverrides.containsKey(d) && deviceOverrides.get(d).containsKey(type) && deviceOverrides.get(d).get(type) || !deviceOverrides.containsKey(d))
r.addPostValue("devices[]", d);
}
r.addPostValue("message", msg);
trace("Sending Post Args:", devices, msg, r.getPostKeys(), r.getPostValues());
trace("Complete", r.post().getReponse());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (r != null)
r.close();
}
}
}).start();
}
@Override
public boolean willHandle(APIMethodName methodName) {
return (methodName.getNamespace().equals("adminium") && (methodName.getMethodName().equals("registerDevice") || methodName.getMethodName().equals("listPushTypes") || methodName.getMethodName().equals("setPushTypeEnabled") || methodName.getMethodName().equals("triggerSevere") || methodName.getMethodName().equals("getCallAdmins") || methodName.getMethodName().equals("getSeveres") || methodName.getMethodName().equals("deregisterDevice")));
}
private void initalize() {
if (!this.init) {
mcLog.addHandler(new ConsoleHandler(this));
boolean initialSetup = !configFile.exists();
try {
configFile.createNewFile();
deviceConfig.load(configFile);
if (initialSetup) {
deviceConfig.set("devices", null);
deviceConfig.set("device-map", null);
deviceConfig.set("settings", "");
deviceConfig.set("settings.player_joined", false);
deviceConfig.set("settings.player_quit", false);
deviceConfig.set("settings.admin_call", true);
deviceConfig.set("settings.severe_log", false);
deviceConfig.save(configFile);
deviceConfig.load(configFile);
}
if (!deviceConfig.contains("group_assignments")) {
deviceConfig.set("group_assignments", null);
deviceConfig.save(configFile);
}
devices = deviceConfig.getStringList("devices");
if (devices == null)
devices = new ArrayList<String>();
trace("Current Devices", devices);
Map<String, Object> tempDefaults = ((ConfigurationSection) deviceConfig.get("settings")).getValues(false);
for (String key : tempDefaults.keySet()) {
settings.put(key, Boolean.valueOf(tempDefaults.get(key).toString()));
}
// load device specific overrides
Object m = deviceConfig.get("device-map");
if (m != null) {
Map<String, Object> tempOverrides = ((ConfigurationSection) m).getValues(false);
for (String device : tempOverrides.keySet()) {
Map<String, Boolean> map = new HashMap<String, Boolean>();
Map<String, Object> tempBools = ((ConfigurationSection) deviceConfig.get("device-map." + device)).getValues(false);
for (String key : tempBools.keySet()) {
deviceOverrides.put(key, new HashMap<String, Boolean>());
map.put(key, Boolean.valueOf(tempDefaults.get(key).toString()));
}
deviceOverrides.put(device, map);
}
}
if (settings != null && settings.get("player_joined") || settings.get("player_quit")) {
api.getStreamManager().getStream("connections").registerListener(this, false);
}
} catch (Exception e) {
e.printStackTrace();
}
pushTypes.addAll(settings.keySet());
Collections.sort(pushTypes);
pushTypeDescriptions = Arrays.asList(new String[] { "On /calladmin", "On player join", "On player quit", "On SEVERE logs" });
this.init = true;
}
}
public void saveConfig() throws IOException {
/*deviceConfig.set("group_assignments", groupAssignments);
deviceConfig.set("group_permissions", groupPerms);*/
deviceConfig.save(configFile);
}
@Override
public Object handle(APIMethodName methodName, Object[] args) {
if (methodName.getNamespace().equals("adminium")) {
initalize();
}
if (methodName.getNamespace().equals("adminium") && methodName.getMethodName().equals("registerDevice") && args.length == 1) {
String deviceToken = args[0].toString();
registerDevice(deviceToken);
} else if (methodName.getNamespace().equals("adminium") && methodName.getMethodName().equals("deregisterDevice") && args.length == 1) {
String deviceToken = args[0].toString();
deregisterDevice(deviceToken);
} else if (methodName.getNamespace().equals("adminium") && methodName.getMethodName().equals("listPushTypes")) {
JSONObject o = new JSONObject();
int i = 0;
for (String k : pushTypes) {
JSONObject oo = new JSONObject();
oo.put("enabled", settings.get(k));
oo.put("description", pushTypeDescriptions.get(i));
o.put(k, oo);
i++;
}
return o;
} else if (methodName.getNamespace().equals("adminium") && methodName.getMethodName().equals("setPushTypeEnabled")) {
settings.put(args[0].toString(), Boolean.valueOf(args[1].toString()));
deviceConfig.set("settings." + args[0].toString(), settings.get(args[0].toString()));
try {
deviceConfig.save(configFile);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
} else if (methodName.getNamespace().equals("adminium") && methodName.getMethodName().equals("triggerSevere")) {
for (int i = 0; i < 10; i++)
mcLog.severe("This is a severe log: " + i);
return true;
} else if (methodName.getNamespace().equals("adminium") && methodName.getMethodName().equals("getCallAdmins")) {
return calladmins;
} else if (methodName.getNamespace().equals("adminium") && methodName.getMethodName().equals("getSeveres")) {
return severeLogs;
}
return null;
}
}