19hzUAMW0n
AL_1

// Author: AL_1
package mathhulk;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;

import javax.xml.bind.DatatypeConverter;

import com.eclipsesource.json.Json;
import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject;
import com.eclipsesource.json.JsonValue;

/**
 * Java hook for the theartex.net API.
 * 
 * @author AL_1
 * @see https://www.theartex.net/documentation/
 *
 */
public class MathhulkAPI {

	/**
	 * API key to use the API, this is not implemented in the API, but is placed
	 * in to future proof the hook
	 */
	private final String APIKey;
	/**
	 * Root URL to the API (Default: https://www.theartex.net/cloud/api/), this
	 * was only added to future proof the hook
	 */
	private final String APIURL;

	/**
	 * Initialize the mathhulk java API
	 * 
	 * @param APIKey
	 *            Key to use mathhulk Developer API <em>(Not in use at the
	 *            moment)</em> Can be null.
	 * @param APIURL
	 *            URL to API (Default: https://www.theartex.net/cloud/api/) Can
	 *            be null.
	 */
	public MathhulkAPI(String APIKey, String APIURL) {
		this.APIKey = APIKey;
		if (APIURL == null || APIURL.trim() == "" || APIURL.trim() == " ") {
			this.APIURL = "https://www.theartex.net/cloud/api/";
		} else {
			this.APIURL = APIURL;
		}
	}

	/**
	 * The get announcements section of the API allows you to grab a list of
	 * announcements.<br>
	 * If you encounter an error or do not receive the result you are looking
	 * for, be sure to check the message variable in the responseIf you receive
	 * a successful response, the response variable should be
	 * <strong><code>"user found"</code></strong>.
	 * 
	 * @return Json response from server
	 */
	public Announcements getAnnouncements() {
		JsonObject json = Json.parse(post("announcements", new HashMap<>())).asObject();
		Announcements ancs = new Announcements(json);
		return ancs;
	}

	/**
	 * The get alerts section of the API allows you to view the alerts of a
	 * user.<br>
	 * If you encounter an error or do not receive the result you are looking
	 * for, be sure to check the message variable in the responseIf you receive
	 * a successful response, the response variable should be
	 * <strong><code>"alerts listed"</code></strong>.
	 * 
	 * @param user
	 *            User of which to get the alerts.
	 * @return Json response from server
	 */
	public Alerts getAlerts(User user) {
		Map<String, String> args = new HashMap<>();
		args.put("key", user.key);
		JsonObject json = Json.parse(post("alerts", args)).asObject();
		Alerts alerts = new Alerts(json, user);
		return alerts;
	}

	/**
	 * The get alerts section of the API allows you to view the alerts of a
	 * user.<br>
	 * If you encounter an error or do not receive the result you are looking
	 * for, be sure to check the message variable in the responseIf you receive
	 * a successful response, the response variable should be
	 * <strong><code>"alert updated"</code></strong>.
	 * 
	 * @param user
	 *            User of which to mark the alert as read.
	 * @param alertId
	 *            Id of the alert to mark as read.
	 * @return Json response from server
	 */
	public Response markAlertAsRead(int alertId, User user) {
		Map<String, String> args = new HashMap<>();
		args.put("key", user.key);
		args.put("id", String.valueOf(alertId));
		JsonObject json = Json.parse(post("alert", args)).asObject();
		return new Response(json);
	}

	/**
	 * The send alert section of the API allows you to send an alert to a
	 * user.<br>
	 * If you encounter an error or do not receive the result you are looking
	 * for, be sure to check the message variable in the responseIf you receive
	 * a successful response, the response variable should be
	 * <strong><code>"user alerted"</code></strong>.
	 * 
	 * @param user
	 *            User of which to send the alert.
	 * @param title
	 *            Title or name of application/service
	 * @param message
	 *            Content or alert
	 * @return Alert object
	 */
	public Alert sendAlert(User user, String title, String message) {
		Map<String, String> args = new HashMap<>();
		args.put("key", user.key);
		args.put("title", title);
		args.put("message", message);
		JsonObject json = Json.parse(post("alert", args)).asObject();
		return getAlert(user, Integer.parseInt(json.get("data").asObject().get("id").asString()));
	}

	/**
	 * The get alert section of the API allows you to get a single alert from a
	 * user.<br>
	 * If you encounter an error or do not receive the result you are looking
	 * for, be sure to check the message variable in the responseIf you receive
	 * a successful response, the response variable should be
	 * <strong><code>"alerts listed"</code></strong>.
	 * 
	 * @param user
	 *            User of which to get the alert.
	 * @param id
	 *            Id of the alert.
	 * @return Alert object, can and will return null if alert is not found.
	 */

	public Alert getAlert(User user, int id) {
		for (Alert a : getAlerts(user).getAlets())
			if (a.getId() == id)
				return a;
		return null;
	}

	/**
	 * The session section of the API allows you to update the status of a
	 * user.<br>
	 * If you encounter an error or do not receive the result you are looking
	 * for, be sure to check the message variable in the responseIf you receive
	 * a successful response, the response variable should be
	 * <strong><code>"session updated"</code></strong>.
	 * 
	 * @param user
	 *            User of which to update session.
	 * @param page
	 *            Location of the user (ie. "applicationName://current
	 *            location")
	 * @param ip
	 *            IP address of the user
	 * @return Json response from server.
	 */
	public Response updateSession(User user, String page, String ip) {
		Map<String, String> args = new HashMap<>();
		args.put("key", user.key);
		args.put("page", page);
		args.put("ip", ip);
		return new Response(Json.parse(post("session", args)).asObject());
	}

	/**
	 * The send alert section of the API allows you to send an alert to a
	 * user.<br>
	 * If you encounter an error or do not receive the result you are looking
	 * for, be sure to check the message variable in the responseIf you receive
	 * a successful response, the response variable should be
	 * <strong><code>"user alerted"</code></strong>.
	 * 
	 * @param user
	 *            Username of user
	 * @param title
	 *            A non-hashed password
	 * @return User object
	 */
	public User authenticateUser(String username, String password) {
		String passwordHashed = "";
		try {
			byte[] passwordBytes = password.getBytes(StandardCharsets.UTF_8);
			MessageDigest md;
			md = MessageDigest.getInstance("MD5");
			passwordHashed = DatatypeConverter.printHexBinary(md.digest(passwordBytes));
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return null;
		}
		Map<String, String> args = new HashMap<>();
		args.put("username", username);
		args.put("password", passwordHashed);
		String response = post("login", args);
		User user = new User(Json.parse(response).asObject());
		return user;

	}

	/**
	 * The post section of the API allows you to send a post to any section of
	 * the API with custom parameters.<br>
	 * 
	 * @param section
	 *            Section of the API
	 * @param args
	 *            Parameters to send to API
	 * @return Raw response from server
	 */
	private String post(String section, Map<String, String> arguments) {
		System.out.println("Posting to: " + section + "...");
		try {
			arguments.put("sec", section);
			arguments.put("APIkey", APIKey);
			URL url = new URL(APIURL);
			URLConnection con = url.openConnection();
			HttpURLConnection http = (HttpURLConnection) con;
			http.setRequestMethod("POST");
			http.setDoOutput(true);
			http.setRequestProperty("User-Agent", "Java Mathhulk API by AL_1 / 0.0.1");
			http.setRequestProperty("sec", section);
			StringJoiner sj = new StringJoiner("&");
			for (Map.Entry<String, String> entry : arguments.entrySet())
				sj.add(URLEncoder.encode(entry.getKey(), "UTF-8") + "=" + URLEncoder.encode(entry.getValue(), "UTF-8"));
			byte[] out = sj.toString().getBytes(StandardCharsets.UTF_8);
			int length = out.length;
			http.setFixedLengthStreamingMode(length);
			http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
			http.connect();
			try (OutputStream os = http.getOutputStream()) {
				os.write(out);
			}
			InputStream in = http.getInputStream();
			StringBuilder textBuilder = new StringBuilder();
			try (Reader reader = new BufferedReader(
					new InputStreamReader(in, Charset.forName(StandardCharsets.UTF_8.name())))) {
				int c = 0;
				while ((c = reader.read()) != -1) {
					textBuilder.append((char) c);
				}
			}
			String rtn = textBuilder.toString();
			System.out.println("Returned: " + rtn);
			return rtn;
		} catch (IOException e) {
			e.printStackTrace();
			return "{\"status\":\"error\",\"code\":\"404\",\"message\":\"Post failed (" + e.getMessage() + ")\"}";
		}
	}

	/**
	 * A standard response object from the API
	 * 
	 * @author AL_1
	 *
	 */
	static class Response {
		private final int code;
		private final String status, message;

		/**
		 * Creates a new API response
		 * 
		 * @param json
		 *            Json response from server
		 */
		public Response(JsonObject json) {
			this.code = Integer.parseInt(json.get("code").asString());
			this.status = json.get("status").asString();
			this.message = json.get("message").asString();
		}

		/**
		 * Status/error code of the response
		 * 
		 * @return Response code
		 */
		public int getCode() {
			return code;
		}

		/**
		 * Gets the status of the response
		 * 
		 * @return Response status
		 */
		public String getStatus() {
			return status;
		}

		public String getMessage() {
			return message;
		}
	}

	/**
	 * User profile from API
	 * 
	 * @author AL_1
	 *
	 */
	static class User {
		private final int id;
		private final String username, role, email, key, val;
		private final boolean banned, active;
		private final Response response;

		/**
		 * Create a user object.
		 * 
		 * @param json
		 *            Json response from server
		 */
		public User(JsonObject json) {
			this.response = new Response(json);
			if (response.getCode() != 0) {
				this.id = -1;
				this.username = null;
				this.role = null;
				this.email = null;
				this.banned = false;
				this.active = false;
				this.key = null;
				this.val = null;
				return;
			}
			JsonObject userJson = json.get("data").asObject();
			this.id = Integer.parseInt(userJson.get("id").asString());
			this.username = userJson.get("username").asString();
			this.role = userJson.get("role").asString();
			this.email = userJson.get("email").asString();
			this.key = userJson.get("key").asString();
			this.val = userJson.get("val").asString();

			String banned = userJson.get("banned").asString();
			String active = userJson.get("active").asString();

			if (banned.toUpperCase() == "YES") {
				this.banned = true;
			} else {
				this.banned = false;
			}

			if (active.toUpperCase() == "YES") {
				this.active = true;
			} else {
				this.active = false;
			}
		}

		public Response getResponse() {
			return response;
		}

		/**
		 * Unique id of user.
		 * 
		 * @return Id of user
		 */
		public int getId() {
			return id;
		}

		/**
		 * Username associated with user
		 * 
		 * @return Users username
		 */
		public String getUsername() {
			return username;
		}

		/**
		 * Gets the role of the user, the current roles are
		 * <strong>User</strong> and <strong>Administrator</strong>
		 * 
		 * @return Role of user
		 */
		public String getRole() {
			return role;
		}

		/**
		 * Gets the email associated with the account.
		 * 
		 * @return Users email
		 */
		public String getEmail() {
			return email;
		}

		/**
		 * The unique key for the user to allows sending, editing, and listing
		 * of alerts for that user. This key could be used in more ways in the
		 * future.
		 * 
		 * @return Users private key
		 */
		public String getKey() {
			return key;
		}

		/**
		 * Hashed user password
		 * 
		 * @return MD5 hashed password
		 */
		public String getVal() {
			return val;
		}

		/**
		 * Is the user banned by the database
		 * 
		 * @return users ban status
		 */
		public boolean isBanned() {
			return banned;
		}

		/**
		 * Has the user activated his or her account via email
		 * 
		 * @return if users account is activated or not
		 */
		public boolean isActivated() {
			return active;
		}
	}

	/**
	 * A single announcement from API
	 * 
	 * @author AL_1
	 *
	 */
	static class Announcement {
		private final int id;
		private final String message, date;

		public Announcement(JsonObject json) {
			this.id = Integer.parseInt(json.get("id").asString());
			this.message = json.get("message").asString();
			this.date = json.get("trn-date").asString();
		}

		/**
		 * Unique id of announcement
		 * 
		 * @return id of announcement
		 */
		public int getId() {
			return id;
		}

		/**
		 * Get the content of the announcement
		 * 
		 * @return message from announcement
		 */
		public String getMessage() {
			return message;
		}

		/**
		 * Date and time that the announcement was sent
		 * 
		 * @return date and time in string format
		 */
		public String getDate() {
			return date;
		}
	}

	/**
	 * Collection of announcements with original response
	 * 
	 * @author AL_1
	 *
	 */
	class Announcements {
		private final Response response;
		private List<Announcement> announcements = new ArrayList<>();

		/**
		 * Creates collection of announcements form the Json send by the server
		 * 
		 * @param json
		 *            Json response from server
		 */
		public Announcements(JsonObject json) {
			this.response = new Response(json);
			if (response.getCode() != 0) {
				announcements = new ArrayList<>();
			} else {
				JsonArray jsonAncs = json.get("data").asArray();
				for (JsonValue item : jsonAncs)
					announcements.add(new Announcement(item.asObject()));
			}
		}

		/**
		 * Get the standard response object from the API for this collection of
		 * announcements
		 * 
		 * @return Standard response
		 */
		public Response getResponse() {
			return response;
		}

		/**
		 * Get the collection of announcements within this object
		 * 
		 * @return List of announcements
		 */
		public List<Announcement> getAnnouncements() {
			return announcements;
		}
	}

	/**
	 * A single alert from a specific user
	 * 
	 * @author AL_1
	 *
	 */
	static class Alert {
		private final int id;
		private final String title, message, date;
		private boolean isnew;
		private User user;

		/**
		 * New alert object
		 * 
		 * @param json
		 *            Json from data array in response
		 * @param user
		 *            User associated with alert
		 */
		public Alert(JsonObject json, User user) {
			this.id = Integer.parseInt(json.get("id").asString());
			this.message = json.get("message").asString();
			this.title = json.get("title").asString();
			this.date = json.get("trn_date").asString();

			String isnew = json.get("new").asString();
			if (isnew.equalsIgnoreCase("yes")) {
				this.isnew = true;
			} else {
				this.isnew = false;
			}
		}

		public Alert(int id, String title, String message, String date, boolean isnew, User user) {
			this.id = id;
			this.title = title;
			this.message = message;
			this.date = date;
			this.isnew = isnew;
			this.user = user;
		}

		/**
		 * Id of the alert
		 * 
		 * @return alert id
		 */
		public int getId() {
			return id;
		}

		/**
		 * Gets the content of the alert
		 * 
		 * @return alert message
		 */
		public String getMessage() {
			return message;
		}

		/**
		 * Date and time that the alert was sent
		 * 
		 * @return date and time in string format
		 */
		public String getDate() {
			return date;
		}

		/**
		 * Gets the title of the application/service that sent the alert
		 * 
		 * @return title of alert
		 */
		public String getTitle() {
			return title;
		}

		/**
		 * Get user associated with this alert
		 * 
		 * @return User object
		 */
		public User getUser() {
			return user;
		}

		/**
		 * Check if this alert is new or not
		 * 
		 * @return if is alert new
		 */
		public boolean isNew() {
			return isnew;
		}
	}

	/**
	 * Collection of alerts from user with original response
	 * 
	 * @author AL_1
	 *
	 */
	static class Alerts {
		private final Response response;
		private List<Alert> alerts = new ArrayList<>();
		private User user;

		/**
		 * New collection of alerts
		 * 
		 * @param json
		 *            Json response from server
		 * @param user
		 *            User associated with alert
		 */
		public Alerts(JsonObject json, User user) {
			this.response = new Response(json);
			if (response.getCode() != 0) {
				alerts = new ArrayList<>();
			} else {
				JsonArray jsonAncs = json.get("data").asArray();
				for (JsonValue item : jsonAncs) {
					alerts.add(new Alert(item.asObject(), user));
				}
			}
		}

		/**
		 * Get the standard response object from the API for this collection of
		 * alerts
		 * 
		 * @return Standard response
		 */
		public Response getResponse() {
			return response;
		}

		/**
		 * Get the collection of alerts within this object
		 * 
		 * @return List of alerts
		 */
		public List<Alert> getAlets() {
			return alerts;
		}

		/**
		 * Get user associated with these alerts
		 * 
		 * @return User object
		 */
		public User getUser() {
			return user;
		}
	}

}