Work on Dashboard, settings, web API, etc etc

This commit is contained in:
NovaFox161
2017-12-17 14:44:41 -06:00
parent 50703bdc4c
commit 3ae253ae67
58 changed files with 2667 additions and 252 deletions

12
pom.xml
View File

@@ -110,6 +110,18 @@
<artifactId>slf4j-simple</artifactId>
<version>1.7.21</version>
</dependency>
<!--Thymeleaf-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
<version>3.0.1.RELEASE</version>
</dependency>
</dependencies>
<build>

View File

@@ -1,7 +1,6 @@
package com.cloudcraftgaming.discal;
import com.cloudcraftgaming.discal.api.database.DatabaseManager;
import com.cloudcraftgaming.discal.api.file.ReadFile;
import com.cloudcraftgaming.discal.api.message.MessageManager;
import com.cloudcraftgaming.discal.api.network.google.Authorization;
import com.cloudcraftgaming.discal.api.object.BotSettings;
@@ -9,14 +8,17 @@ import com.cloudcraftgaming.discal.bot.internal.consolecommand.ConsoleCommandExe
import com.cloudcraftgaming.discal.bot.internal.network.discordpw.UpdateListData;
import com.cloudcraftgaming.discal.bot.listeners.ReadyEventListener;
import com.cloudcraftgaming.discal.bot.module.command.*;
import com.cloudcraftgaming.discal.web.endpoints.v1.*;
import com.cloudcraftgaming.discal.web.utils.SparkUtils;
import sx.blah.discord.api.ClientBuilder;
import sx.blah.discord.api.IDiscordClient;
import sx.blah.discord.api.events.EventDispatcher;
import sx.blah.discord.handle.obj.IUser;
import sx.blah.discord.util.DiscordException;
import static spark.Spark.*;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
/**
* Created by Nova Fox on 1/2/2017.
@@ -27,25 +29,23 @@ import static spark.Spark.*;
public class Main {
public static String version = "1.1.0";
public static IDiscordClient client;
public static BotSettings botSettings;
public static void main(String[] args) {
if (args.length < 1) // Needs a bot token provided
throw new IllegalArgumentException("BotSettings file has not been specified!!");
public static void main(String[] args) throws IOException {
//Get bot settings
botSettings = ReadFile.readBotSettings(args[0]);
Properties p = new Properties();
p.load(new FileReader(new File("settings.properties")));
BotSettings.init(p);
client = createClient(botSettings.getBotToken());
client = createClient(BotSettings.TOKEN.get());
if (client == null)
throw new NullPointerException("Failed to log in! Client cannot be null!");
UpdateListData.init(botSettings);
UpdateListData.init();
Authorization.getAuth().init(botSettings);
Authorization.getAuth().init();
//Connect to MySQL
DatabaseManager.getManager().connectToMySQL(botSettings);
DatabaseManager.getManager().connectToMySQL();
DatabaseManager.getManager().createTables();
//Register events
@@ -69,49 +69,8 @@ public class Main {
//Load language files.
MessageManager.loadLangs();
//Register Spark endpoints...
if (botSettings.shouldRunWebAPI()) {
before("/api/*", (request, response) -> {
if (!request.requestMethod().equalsIgnoreCase("POST")) {
System.out.println("Denied '" + request.requestMethod() + "' access from: " + request.ip());
halt(405, "Method not allowed");
}
//Check authorization
if (request.headers().contains("Authorization") && !request.headers("Authorization").equals("API_KEY")) {
halt(401, "Unauthorized");
}
if (!request.contentType().equalsIgnoreCase("application/json")) {
halt(400, "Bad Request");
}
});
path("/api/v1/discal", () -> {
before("/*", (q, a) -> System.out.println("Received API call from: " + q.ip() + "; Host:" + q.host()));
path("/guild", () -> {
path("/settings", () -> {
post("/get", GuildEndpoint::getSettings);
post("/update", GuildEndpoint::updateSettings);
});
path("/info", () -> post("/from-user/list", GuildEndpoint::getUserGuilds));
});
path("/announcement", () -> {
post("/get", AnnouncementEndpoint::getAnnouncement);
post("/create", AnnouncementEndpoint::createAnnouncement);
post("/update", AnnouncementEndpoint::updateAnnouncement);
post("/delete", AnnouncementEndpoint::deleteAnnouncement);
post("/list", AnnouncementEndpoint::listAnnouncements);
});
path("/calendar", () -> {
post("/get", CalendarEndpoint::getCalendar);
post("/list", CalendarEndpoint::listCalendars);
post("time", TimeEndpoint::getTime);
});
path("/rsvp", () -> {
post("/get", RsvpEndpoint::getRsvp);
post("/update", RsvpEndpoint::updateRsvp);
});
});
}
//Start spark
SparkUtils.initSpark();
//Accept commands
ConsoleCommandExecutor.init();

View File

@@ -43,11 +43,10 @@ public class DatabaseManager {
/**
* Connects to the MySQL server specified.
*
* @param bs The BotSettings with Db info to connect to.
*/
public void connectToMySQL(BotSettings bs) {
public void connectToMySQL() {
try {
MySQL mySQL = new MySQL(bs.getDbHostName(), bs.getDbPort(), bs.getDbDatabase(), bs.getDbPrefix(), bs.getDbUser(), bs.getDbPass());
MySQL mySQL = new MySQL(BotSettings.SQL_HOST.get(), BotSettings.SQL_PORT.get(), BotSettings.SQL_DB.get(), BotSettings.SQL_PREFIX.get(), BotSettings.SQL_USER.get(), BotSettings.SQL_PASSWORD.get());
Connection mySQLConnection = mySQL.openConnection();
databaseInfo = new DatabaseInfo(mySQL, mySQLConnection, mySQL.getPrefix());

View File

@@ -1,12 +1,12 @@
package com.cloudcraftgaming.discal.api.file;
import com.cloudcraftgaming.discal.Main;
import com.cloudcraftgaming.discal.api.object.BotSettings;
import com.cloudcraftgaming.discal.api.utils.ExceptionHandler;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.io.*;
import java.io.File;
import java.io.FileReader;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
@@ -17,77 +17,12 @@ import java.util.Map;
* For Project: DisCal-Discord-Bot
*/
public class ReadFile {
public static BotSettings readBotSettings(String fileAndPath) {
BotSettings settings = new BotSettings();
try {
// Open the file that is the first
FileInputStream fstream = new FileInputStream(fileAndPath);
// Get the object of DataInputStream
DataInputStream in = new DataInputStream(fstream);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String strLine;
//Read File Line By Line
int line = 0;
while ((strLine = br.readLine()) != null) {
if (line == 0) {
settings.setBotToken(strLine);
}
if (line == 1) {
settings.setDbHostName(strLine);
}
if (line == 2) {
settings.setDbPort(strLine);
}
if (line == 3) {
settings.setDbDatabase(strLine);
}
if (line == 4) {
settings.setDbPrefix(strLine);
}
if (line == 5) {
settings.setDbUser(strLine);
}
if (line == 6) {
settings.setDbPass(strLine);
}
if (line == 7) {
settings.setLangPath(strLine);
}
if (line == 9) {
settings.setBotsPwToken(strLine);
}
if (line == 10) {
settings.setGoogleClientId(strLine);
}
if (line == 11) {
settings.setGoogleClientSecret(strLine);
}
if (line == 12) {
settings.setRunDatabaseUpdater(strLine.equalsIgnoreCase("true"));
}
if (line == 13) {
settings.setRunWebAPI(strLine.equalsIgnoreCase("true"));
}
line++;
}
//Close the input stream
br.close();
in.close();
fstream.close();
} catch (Exception e) {//Catch exception if any
System.err.println("Error: " + e.getMessage());
}
return settings;
}
@SuppressWarnings({"unchecked", "ConstantConditions"})
public static Map<String, Map<String, String>> readAllLangFiles() {
Map<String, Map<String, String>> langs = new HashMap<>();
try {
File langDir = new File(Main.botSettings.getLangPath());
File langDir = new File(BotSettings.LANG_PATH.get());
for (File f : langDir.listFiles()) {
// Open the file
@@ -106,4 +41,4 @@ public class ReadFile {
}
return langs;
}
}
}

View File

@@ -51,8 +51,8 @@ public class Authorization {
return instance;
}
public void init(BotSettings settings) {
clientData = new ClientData(settings.getGoogleClientId(), settings.getGoogleClientSecret());
public void init() {
clientData = new ClientData(BotSettings.GOOGLE_CLIENT_ID.get(), BotSettings.GOOGLE_CLIENT_SECRET.get());
}
public void requestCode(MessageReceivedEvent event, GuildSettings settings) {

View File

@@ -1,134 +1,34 @@
package com.cloudcraftgaming.discal.api.object;
import java.util.Properties;
/**
* Created by Nova Fox on 11/10/17.
* Website: www.cloudcraftgaming.com
* For Project: DisCal-Discord-Bot
*/
public class BotSettings {
private String botToken;
public enum BotSettings {
SQL_HOST, SQL_USER, SQL_PASSWORD,
SQL_DB, SQL_PORT, SQL_PREFIX, TOKEN, SECRET,
LANG_PATH, PW_TOKEN, GOOGLE_CLIENT_ID,
GOOGLE_CLIENT_SECRET, RUN_API;
private String dbHostName;
private String dbPort;
private String dbDatabase;
private String dbPrefix;
private String dbUser;
private String dbPass;
private String val;
private String langPath;
private String botsPwToken;
private String googleClientId;
private String googleClientSecret;
private boolean runDatabaseUpdater;
private boolean runWebAPI;
//Getters
public String getBotToken() {
return botToken;
BotSettings() {
}
public String getDbHostName() {
return dbHostName;
public static void init(Properties properties) {
for (BotSettings s : values()) {
s.set(properties.getProperty(s.name()));
}
}
public String getDbPort() {
return dbPort;
public String get() {
return val;
}
public String getDbDatabase() {
return dbDatabase;
}
public String getDbPrefix() {
return dbPrefix;
}
public String getDbUser() {
return dbUser;
}
public String getDbPass() {
return dbPass;
}
public String getLangPath() {
return langPath;
}
public String getBotsPwToken() {
return botsPwToken;
}
public String getGoogleClientId() {
return googleClientId;
}
public String getGoogleClientSecret() {
return googleClientSecret;
}
public boolean shouldRunDatabaseUpdater() {
return runDatabaseUpdater;
}
public boolean shouldRunWebAPI() {
return runWebAPI;
}
//Setters
public void setBotToken(String _botToken) {
botToken = _botToken;
}
public void setDbHostName(String _dbHostName) {
dbHostName = _dbHostName;
}
public void setDbPort(String _port) {
dbPort = _port;
}
public void setDbDatabase(String _database) {
dbDatabase = _database;
}
public void setDbPrefix(String _prefix) {
dbPrefix = _prefix;
}
public void setDbUser(String _user) {
dbUser = _user;
}
public void setDbPass(String _pass) {
dbPass = _pass;
}
public void setLangPath(String _path) {
langPath = _path;
}
public void setBotsPwToken(String _token) {
botsPwToken = _token;
}
public void setGoogleClientId(String _id) {
googleClientId = _id;
}
public void setGoogleClientSecret(String _secret) {
googleClientSecret = _secret;
}
public void setRunDatabaseUpdater(boolean _run) {
runDatabaseUpdater = _run;
}
public void setRunWebAPI(boolean _run) {
runWebAPI = _run;
public void set(String val) {
this.val = val;
}
}

View File

@@ -0,0 +1,74 @@
package com.cloudcraftgaming.discal.api.object.web;
import com.cloudcraftgaming.discal.Main;
import com.cloudcraftgaming.discal.api.database.DatabaseManager;
import sx.blah.discord.handle.obj.IGuild;
/**
* Created by Nova Fox on 12/17/17.
* Website: www.cloudcraftgaming.com
* For Project: DisCal-Discord-Bot
*/
public class WebGuild {
private String id;
private String name;
private String iconUrl;
//Bot settings
private String botNick;
private String prefix;
//Getters
public String getId() {
return id;
}
public String getName() {
return name;
}
public String getIcon() {
return iconUrl;
}
public String getBotNick() {
return botNick;
}
public String getPrefix() {
return prefix;
}
//Setters
public void setId(String _id) {
id = _id;
}
public void setName(String _name) {
name = _name;
}
public void setIcon(String _url) {
iconUrl = _url;
}
public void setBotNick(String _nick) {
botNick = _nick;
}
public void setPrefix(String _prefix) {
prefix = _prefix;
}
//Functions
public WebGuild fromGuild(IGuild g) {
id = g.getStringID();
name = g.getName();
iconUrl = g.getIconURL();
botNick = Main.getSelfUser().getNicknameForGuild(g);
prefix = DatabaseManager.getManager().getSettings(g.getLongID()).getPrefix();
return this;
}
}

View File

@@ -0,0 +1,25 @@
package com.cloudcraftgaming.discal.api.utils;
import com.cloudcraftgaming.discal.Main;
import com.cloudcraftgaming.discal.api.object.web.WebGuild;
import sx.blah.discord.handle.obj.IGuild;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Nova Fox on 12/17/17.
* Website: www.cloudcraftgaming.com
* For Project: DisCal-Discord-Bot
*/
public class GuildUtils {
public static List<WebGuild> getGuilds(String userId) {
List<WebGuild> guilds = new ArrayList<>();
for (IGuild g : Main.client.getGuilds()) {
if (g.getUserByID(Long.valueOf(userId)) != null) {
guilds.add(new WebGuild().fromGuild(g));
}
}
return guilds;
}
}

View File

@@ -14,15 +14,12 @@ import org.json.JSONObject;
* For Project: DisCal
*/
public class UpdateListData {
private static String token;
/**
* Initiates the data updater with a valid token.
*
* @param settings BotSettings containing the API token.
*/
public static void init(BotSettings settings) {
token = settings.getBotsPwToken();
public static void init() {
}
/**
@@ -34,7 +31,7 @@ public class UpdateListData {
JSONObject json = new JSONObject().put("server_count", serverCount);
HttpResponse<JsonNode> response = Unirest.post("https://bots.discord.pw/api/bots/265523588918935552/stats").header("Authorization", token).header("Content-Type", "application/json").body(json).asJson();
HttpResponse<JsonNode> response = Unirest.post("https://bots.discord.pw/api/bots/265523588918935552/stats").header("Authorization", BotSettings.PW_TOKEN.get()).header("Content-Type", "application/json").body(json).asJson();
} catch (Exception e) {
//Handle issue.
System.out.println("Failed to update Discord PW list metadata!");

View File

@@ -0,0 +1,120 @@
package com.cloudcraftgaming.discal.web.handler;
import com.cloudcraftgaming.discal.Main;
import com.cloudcraftgaming.discal.api.database.DatabaseManager;
import com.cloudcraftgaming.discal.api.object.GuildSettings;
import com.cloudcraftgaming.discal.api.object.web.WebGuild;
import org.json.JSONException;
import spark.Request;
import spark.Response;
import sx.blah.discord.handle.obj.IGuild;
import java.util.HashMap;
import java.util.Map;
import static spark.Spark.halt;
/**
* Created by Nova Fox on 12/17/17.
* Website: www.cloudcraftgaming.com
* For Project: DisCal-Discord-Bot
*/
@SuppressWarnings({"unchecked", "ThrowableNotThrown"})
public class DashboardHandler {
public static String handleGuildSelect(Request request, Response response) {
try {
String guildId = request.queryParams("guild");
WebGuild wg = new WebGuild().fromGuild(Main.client.getGuildByID(Long.valueOf(guildId)));
Map m = DiscordAccountHandler.getHandler().getAccount(request.session().id());
if (m.containsKey("selected")) {
m.remove("selected");
}
m.put("selected", wg);
if (m.containsKey("settings")) {
m.remove("settings");
}
if (m.containsKey("ext")) {
m.remove("ext");
}
DiscordAccountHandler.getHandler().appendAccount(m, request.session().id());
response.redirect("/dashboard/guild");
} catch (JSONException e) {
e.printStackTrace();
response.redirect("/dashboard");
} catch (Exception e) {
e.printStackTrace();
halt(500, "Internal Server Exception");
}
return response.body();
}
public static String handleSettingsSelect(Request request, Response response) {
try {
String settings = request.queryParams("settings");
Map m = new HashMap();
m.put("settings", settings);
if (settings.equalsIgnoreCase("ext")) {
//Extension handling
String ext = request.queryParams("ext");
m.put("ext", ext);
}
DiscordAccountHandler.getHandler().appendAccount(m, request.session().id());
response.redirect("/dashboard/guild");
} catch (JSONException e) {
e.printStackTrace();
response.redirect("/dashboard");
} catch (Exception e) {
e.printStackTrace();
halt(500, "Internal Server Exception");
}
return response.body();
}
public static String handleSettingsUpdate(Request request, Response response) {
try {
if (request.queryParams().contains("bot-nick")) {
//Update bot nickname...
Map m = DiscordAccountHandler.getHandler().getAccount(request.session().id());
WebGuild g = (WebGuild) m.get("selected");
g.setBotNick(request.queryParams("bot-nick"));
//TODO: Set Bot nickname!!!
IGuild guild = Main.client.getGuildByID(Long.valueOf(g.getId()));
guild.setUserNickname(Main.getSelfUser(), g.getBotNick());
} else if (request.queryParams().contains("prefix")) {
//Update prefix...
Map m = DiscordAccountHandler.getHandler().getAccount(request.session().id());
WebGuild g = (WebGuild) m.get("selected");
g.setPrefix(request.queryParams("prefix"));
GuildSettings settings = DatabaseManager.getManager().getSettings(Long.valueOf(g.getId()));
settings.setPrefix(g.getPrefix());
DatabaseManager.getManager().updateSettings(settings);
}
//Finally redirect back to the dashboard
response.redirect("/dashboard/guild");
} catch (Exception e) {
e.printStackTrace();
halt(500, "Internal Server Exception");
}
return response.body();
}
}

View File

@@ -0,0 +1,88 @@
package com.cloudcraftgaming.discal.web.handler;
import com.cloudcraftgaming.discal.api.utils.GuildUtils;
import java.util.HashMap;
import java.util.Map;
/**
* Created by Nova Fox on 12/17/17.
* Website: www.cloudcraftgaming.com
* For Project: DisCal-Discord-Bot
*/
@SuppressWarnings("unchecked")
public class DiscordAccountHandler {
private static DiscordAccountHandler instance;
private HashMap<String, Map> discordAccounts = new HashMap<>();
//Instance handling
private DiscordAccountHandler() {
} //Prevent initialization
public static DiscordAccountHandler getHandler() {
if (instance == null) {
instance = new DiscordAccountHandler();
}
return instance;
}
//Boolean/checkers
public boolean hasAccount(String sessionId) {
return discordAccounts.containsKey(sessionId);
}
//Getters
public Map getAccount(String sessionId) {
if (discordAccounts.containsKey(sessionId)) {
return discordAccounts.get(sessionId);
} else {
//Not logged in...
Map m = new HashMap();
m.put("loggedIn", false);
return m;
}
}
public Map findAccount(String userId) {
for (Map m : discordAccounts.values()) {
if (m.containsKey("id")) {
if (m.get("id").equals(userId)) {
return m;
}
}
}
return null;
}
//Functions
public void addAccount(Map m, String sessionId) {
if (discordAccounts.containsKey(sessionId)) {
discordAccounts.remove(sessionId);
}
discordAccounts.put(sessionId, m);
}
public void appendAccount(Map m, String sessionId) {
if (discordAccounts.containsKey(sessionId)) {
Map exist = discordAccounts.get(sessionId);
exist.putAll(m);
} else {
discordAccounts.put(sessionId, m);
}
}
public void updateAccount(String userId) {
Map m = findAccount(userId);
if (m != null) {
m.remove("guilds");
m.put("guilds", GuildUtils.getGuilds(userId));
}
}
public void removeAccount(String sessionId) {
if (hasAccount(sessionId)) {
discordAccounts.remove(sessionId);
}
}
}

View File

@@ -0,0 +1,77 @@
package com.cloudcraftgaming.discal.web.oauth;
import com.cloudcraftgaming.discal.Main;
import com.cloudcraftgaming.discal.api.object.BotSettings;
import com.cloudcraftgaming.discal.api.utils.GuildUtils;
import com.cloudcraftgaming.discal.web.handler.DiscordAccountHandler;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.Unirest;
import org.json.JSONException;
import org.json.JSONObject;
import spark.Request;
import spark.Response;
import java.util.HashMap;
import java.util.Map;
import static spark.Spark.halt;
/**
* Created by Nova Fox on 12/17/17.
* Website: www.cloudcraftgaming.com
* For Project: DisCal-Discord-Bot
*/
@SuppressWarnings({"ThrowableNotThrown", "unchecked"})
public class DiscordLoginHandler {
public static String handleDiscordCode(Request request, Response response) {
try {
String code = request.queryParams("code");
//POST request to discord for access...
HttpResponse<JsonNode> httpResponse = Unirest.post("https://discordapp.com/api/v6/oauth2/token").header("Content-Type", "application/x-www-form-urlencoded").field("client_id", Main.client.getApplicationClientID()).field("client_secret", BotSettings.SECRET.get()).field("grant_type", "authorization_code").field("code", code).field("redirect_uri", "http://localhost:4567/account/login").asJson();
JSONObject info = new JSONObject(httpResponse.getBody()).getJSONObject("object");
//GET request for user info...
HttpResponse<JsonNode> userDataResponse = Unirest.get("https://discordapp.com/api/v6/users/@me").header("Authorization", "Bearer " + info.getString("access_token")).asJson();
JSONObject userInfo = new JSONObject(userDataResponse.getBody()).getJSONObject("object");
//Saving session info and access info to memory until moved into the database...
Map m = new HashMap();
m.put("loggedIn", true);
m.put("id", userInfo.getString("id"));
m.put("username", userInfo.getString("username"));
m.put("discrim", userInfo.getString("discriminator"));
//Get guilds...
m.put("guilds", GuildUtils.getGuilds(userInfo.getString("id")));
DiscordAccountHandler.getHandler().addAccount(m, request.session().id());
//Finally redirect to the dashboard seamlessly.
response.redirect("/dashboard");
} catch (JSONException e) {
e.printStackTrace();
response.redirect("/dashboard");
} catch (Exception e) {
e.printStackTrace();
halt(500, "Internal Server Exception");
}
return response.body();
}
public static String handleLogout(Request request, Response response) {
try {
DiscordAccountHandler.getHandler().removeAccount(request.session().id());
response.redirect("/");
} catch (Exception e) {
e.printStackTrace();
halt(500, "Internal Server Exception");
}
return response.body();
}
}

View File

@@ -0,0 +1,84 @@
package com.cloudcraftgaming.discal.web.utils;
import com.cloudcraftgaming.discal.web.endpoints.v1.*;
import com.cloudcraftgaming.discal.web.handler.DashboardHandler;
import com.cloudcraftgaming.discal.web.handler.DiscordAccountHandler;
import com.cloudcraftgaming.discal.web.oauth.DiscordLoginHandler;
import spark.ModelAndView;
import static spark.Spark.*;
/**
* Created by Nova Fox on 12/17/17.
* Website: www.cloudcraftgaming.com
* For Project: DisCal-Discord-Bot
*/
public class SparkUtils {
@SuppressWarnings("ThrowableNotThrown")
public static void initSpark() {
staticFileLocation("/web/public"); // Main site location
//Register the API Endpoints
before("/api/*", (request, response) -> {
if (!request.requestMethod().equalsIgnoreCase("POST")) {
System.out.println("Denied '" + request.requestMethod() + "' access from: " + request.ip());
halt(405, "Method not allowed");
}
//Check authorization
if (request.headers().contains("Authorization") && !request.headers("Authorization").equals("API_KEY")) {
//TODO: Actually check auth!!! < Just lazy right now
halt(401, "Unauthorized");
}
//Only accept json because its easier to parse and handle.
if (!request.contentType().equalsIgnoreCase("application/json")) {
halt(400, "Bad Request");
}
});
//API endpoints
path("/api/v1", () -> {
before("/*", (q, a) -> System.out.println("Received API call from: " + q.ip() + "; Host:" + q.host()));
path("/guild", () -> {
path("/settings", () -> {
post("/get", GuildEndpoint::getSettings);
post("/update", GuildEndpoint::updateSettings);
});
path("/info", () -> post("/from-user/list", GuildEndpoint::getUserGuilds));
});
path("/announcement", () -> {
post("/get", AnnouncementEndpoint::getAnnouncement);
post("/create", AnnouncementEndpoint::createAnnouncement);
post("/update", AnnouncementEndpoint::updateAnnouncement);
post("/delete", AnnouncementEndpoint::deleteAnnouncement);
post("/list", AnnouncementEndpoint::listAnnouncements);
});
path("/calendar", () -> {
post("/get", CalendarEndpoint::getCalendar);
post("/list", CalendarEndpoint::listCalendars);
post("time", TimeEndpoint::getTime);
});
path("/rsvp", () -> {
post("/get", RsvpEndpoint::getRsvp);
post("/update", RsvpEndpoint::updateRsvp);
});
});
//Various endpoints for thyme because yeah...
path("/account", () -> {
get("/login", DiscordLoginHandler::handleDiscordCode);
get("/logout", DiscordLoginHandler::handleLogout);
path("/dashboard", () -> {
get("/select", DashboardHandler::handleGuildSelect);
get("/guild", DashboardHandler::handleSettingsSelect);
get("/update", DashboardHandler::handleSettingsUpdate);
});
});
//Templates and pages...
get("/", (rq, rs) -> new ModelAndView(DiscordAccountHandler.getHandler().getAccount(rq.session().id()), "pages/index"), new ThymeleafTemplateEngine());
get("/home", (rq, rs) -> new ModelAndView(DiscordAccountHandler.getHandler().getAccount(rq.session().id()), "pages/index"), new ThymeleafTemplateEngine());
get("/dashboard", (rq, rs) -> new ModelAndView(DiscordAccountHandler.getHandler().getAccount(rq.session().id()), "pages/dashboard/dashboard"), new ThymeleafTemplateEngine());
get("/dashboard/guild", (rq, rs) -> new ModelAndView(DiscordAccountHandler.getHandler().getAccount(rq.session().id()), "pages/dashboard/guild"), new ThymeleafTemplateEngine());
}
}

View File

@@ -0,0 +1,107 @@
package com.cloudcraftgaming.discal.web.utils;
import org.thymeleaf.context.Context;
import org.thymeleaf.extras.java8time.dialect.Java8TimeDialect;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
import org.thymeleaf.templateresolver.ITemplateResolver;
import spark.ModelAndView;
import spark.TemplateEngine;
import java.util.Locale;
import java.util.Map;
/**
* Created by Nova Fox on 12/17/17.
* Website: www.cloudcraftgaming.com
* For Project: DisCal-Discord-Bot
*/
@SuppressWarnings("unchecked")
public class ThymeleafTemplateEngine extends TemplateEngine {
private static final String DEFAULT_PREFIX = "web/public/";
private static final String DEFAULT_SUFFIX = ".html";
private static final long DEFAULT_CACHE_TTL_MS = 3600000L;
private org.thymeleaf.TemplateEngine templateEngine;
/**
* Constructs a default thymeleaf template engine.
* Defaults prefix (template directory in resource path) to templates/ and suffix to .html
*/
public ThymeleafTemplateEngine() {
this(DEFAULT_PREFIX, DEFAULT_SUFFIX);
}
/**
* Constructs a thymeleaf template engine with specified prefix and suffix
*
* @param prefix the prefix (template directory in resource path)
* @param suffix the suffix (e.g. .html)
*/
public ThymeleafTemplateEngine(String prefix, String suffix) {
ITemplateResolver defaultTemplateResolver = createDefaultTemplateResolver(prefix, suffix);
initialize(defaultTemplateResolver);
}
/**
* Constructs a thymeleaf template engine with a proprietary initialize
*
* @param templateResolver the template resolver.
*/
public ThymeleafTemplateEngine(ITemplateResolver templateResolver) {
initialize(templateResolver);
}
private static ITemplateResolver createDefaultTemplateResolver(String prefix, String suffix) {
final ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setPrefix(
prefix != null ? prefix : DEFAULT_PREFIX
);
templateResolver.setSuffix(
suffix != null ? suffix : DEFAULT_SUFFIX
);
templateResolver.setCacheTTLMs(DEFAULT_CACHE_TTL_MS);
return templateResolver;
}
/**
* Initializes and sets the template resolver
*/
private void initialize(ITemplateResolver templateResolver) {
templateEngine = new org.thymeleaf.TemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
templateEngine.addDialect(new Java8TimeDialect());
}
@Override
@SuppressWarnings("unchecked")
public String render(ModelAndView modelAndView) {
return render(modelAndView, Locale.getDefault());
}
/**
* Process the specified template (usually the template name).
* Output will be written into a String that will be returned from calling this method,
* once template processing has finished.
*
* @param modelAndView model and view
* @param locale A Locale object represents a specific geographical, political, or cultural region
* @return processed template
*/
public String render(ModelAndView modelAndView, Locale locale) {
Object model = modelAndView.getModel();
if (model instanceof Map) {
Context context = new Context(locale);
context.setVariables((Map<String, Object>) model);
return templateEngine.process(modelAndView.getViewName(), context);
} else {
throw new IllegalArgumentException("modelAndView.getModel() must return a java.util.Map");
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 909 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 581 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,160 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/styles/global.css">
<title>About - DisCal Bot</title>
</head>
<body>
<div class="top-nav">
<h1>DISCAL BOT</h1>
<a href="/index.html">Home</a>
<a class="active" href="/pages/about.html">About</a>
<a href="/pages/commands.html">Commands</a>
<a href="setup.html">Setup</a>
<a href="/pages/lazy-discal.html">Lazy DisCal</a>
<a href="https://discord.gg/AmAMGeN" target="_blank">Support</a>
<a href="https://www.patreon.com/Novafox" target="_blank">Patreon</a>
</div>
<div id="content">
<h1>DisCal Bot - About</h1>
<div id="invite">
<div class="image-wrapper" style="float: left">
<img src="/assets/images/logos/discord-logo-white.png" style="max-width: 300px; max-height: 300px;">
</div>
<h3>Invite To Discord Server</h3>
<p>
Seen enough of this amazing bot? Add it to your Discord server by clicking the button below!
<br><br>
Once you add, scroll down to the "DisCal Setup" Button and click it!
</p>
<a href="https://discordapp.com/oauth2/authorize?client_id=265523588918935552&scope=bot&permissions=201845824redirect_uri=https%3A%2F%2Fwww.discalbot.com%2Fpages%2Fsetup.html"
target="_blank">
<button>Invite to Server Now!</button>
</a>
<p>
Need to see more to convince you? Scroll down the page! You'll learn just how useful DisCal really
is and why you need it!
</p>
</div>
<hr>
<div id="commands">
<div class="image-wrapper" style="float: right">
<img src="/assets/images/examples/DisCal-Event-Creation.jpg"
style="max-width: 300px; max-height: 300px;">
</div>
<h3>Commands</h3>
<p>
DisCal is designed to be intuitive and powerful.
<br><br><br>
Discal uses simple to understand commands. With a customizable prefix, ability to mention DisCal
directly, and more DisCal's command system is designed for users in mind.
<br><br><br>
Because of this, DisCal has a ton of commands that allow you to fully utilize it! View all of the
documentation on the commands for DisCal by clicking the button below!
</p>
<a href="/pages/commands.html">
<button>View Commands</button>
</a>
</div>
<hr>
<div id="trello">
<div class="image-wrapper" style="float: left">
<img src="/assets/images/other/discal-trello.png" style="max-width: 450px; max-height: 300px;">
</div>
<h3>Trello & To-Do</h3>
<p>
DisCal is meant to be useful, collaborative, and easy to use.
<br><br><br>
We use Trello to track everything we are currently working on. This way, you can see what our
progress is, comment on what you want us to work on, or add suggestions and requests.
<br><br><br>
</p>
<a href="https://trello.com/b/OuFo5aXu/discal" target="_blank">
<button>Check out DisCal's Trello</button>
</a>
</div>
<hr>
<div id="support">
<div class="image-wrapper" style="float: right">
<img src="/assets/images/other/support-server.png" style="max-width: 450px; max-height: 300px;">
</div>
<h3>Official Support Server</h3>
<p>
If DisCal was meant to be useful, so are we! Join our official DisCal Discord server for extra info
and help.
<br><br><br>
Need help with something? Visit our support channel!
<br><br>
Want to request a feature we somehow didn't think to add? Send us a message in our feature request
channel!
<br><br><br>
Just want to be part of the amazing community using this Bot? Join the discord!
</p>
<a href="https://discord.gg/AmAMGeN" target="_blank">
<button>Join The Support Server</button>
</a>
</div>
<hr>
<div id="source">
<div class="image-wrapper" style="float: left">
<img src="/assets/images/other/discal-github.jpg" style="max-width: 450px; max-height: 300px;">
</div>
<h3>Source Code</h3>
<p>
DisCal was built upon open source APIs. It wouldn't be fair if we didn't open source DisCal.
<br><br><br>
All of the source code behind this amazing bot can be found on the GitHub repo linked below!
<br><br><br>
We encourage those who can to fork the repository and help code DisCal! We are community driven and
really love any help we can get. Even just a simple typo fix.
<br><br>
And of course, we will credit you with the changes and even give you a special contributor role!!!
</p>
<a href="https://github.com/NovaFox161/DisCal-Discord-Bot" target="_blank">
<button>DisCal GitHub</button>
</a>
</div>
<hr>
<div id="patreon">
<div class="image-wrapper" style="float: left">
<img src="/assets/images/other/novafox-patreon-page.png"
style="max-width: 450px; max-height: 300px;">
</div>
<h3>Become a Patron Today!</h3>
<p>
DisCal is developed freely, open source, and completely unlimited. However, we still need to support
ourselves.
<br> <br>
Rather than making DisCal limited for free users, we decided to open a patreon, by becoming a patron
you help support and fund the further development and support for DisCal. Please consider becoming a
patron!
</p>
<a target="_blank" href="https://www.patreon.com/Novafox">
<button>Become a Patron!</button>
</a>
<p>
By becoming a patron, you not only support us and DisCal's development, but will get exclusive
access to features early before anyone else!
</p>
</div>
</div>
</body>
<footer id="footer">
<p>© Nova Maday 2017. All rights reserved. <a href="/pages/policy/privacy.html">Privacy Policy</a></p>
</footer>
</html>

View File

@@ -0,0 +1,704 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/styles/global.css">
<title>Commands - DisCal Bot</title>
</head>
<body>
<div class="top-nav">
<h1>DISCAL BOT</h1>
<a href="/index.html">Home</a>
<a href="/pages/about.html">About</a>
<a class="active" href="/pages/commands.html">Commands</a>
<a href="setup.html">Setup</a>
<a href="/pages/lazy-discal.html">Lazy DisCal</a>
<a href="https://discord.gg/AmAMGeN" target="_blank">Support</a>
<a href="https://www.patreon.com/Novafox" target="_blank">Patreon</a>
</div>
<div id="content">
<h1>DisCal Bot - About</h1>
<p>
Below is full documentation on DisCal commands.
<br> <br> <br>
This documentation includes the command, acceptable arguments, an example, and an explanation for each
and every command DisCal has to offer.
<br> <br>
Since many commands have sub-commands and functions, this page will be split into sections for each.
</p>
<hr>
<div class="image-wrapper" style="max-width: 900px; position: center;">
<img src="/assets/images/examples/event_demo.gif"
style="max-width: 300px; max-height: 300px; padding: 10px;">
<img src="/assets/images/examples/calendar_demo.gif"
style="max-width: 300px; max-height: 300px; padding: 10px;">
<img src="/assets/images/examples/announcement_demo.gif"
style="max-width: 300px; max-height: 300px; padding: 10px;">
</div>
<hr>
<div id="calendar">
<h2>!calendar commands</h2>
<h3>Alias: !cal</h3>
<table style="border-color: #ef0813;" border="#ef0813" cellspacing="0" cellpadding="4">
<tbody>
<tr>
<td style="max-width: 250px; text-align: center; vertical-align: top;" scope="col">
<h3><span style="text-decoration: underline;"><strong>Command</strong></span></h3>
</td>
<td style="max-width: 250px; text-align: center; vertical-align: top;" scope="col">
<h3><span style="text-decoration: underline;"><strong>Acceptable Arguments</strong></span>
</h3>
</td>
<td style="max-width: 250px; text-align: center; vertical-align: top;" scope="col">
<h3><span style="text-decoration: underline;"><strong>Example</strong></span></h3>
</td>
<td style="max-width: 250px; text-align: center; vertical-align: top;" scope="col">
<h3><span style="text-decoration: underline;"><strong>Description</strong></span></h3>
</td>
</tr>
<tr>
<td style="text-align: center;">&nbsp;!calendar create</td>
<td style="text-align: center;">&nbsp;!calendar create &lt;name, spaces allowed&gt;</td>
<td style="text-align: center;">&nbsp;!calendar create example Calendar</td>
<td style="text-align: center;">&nbsp;Creates a calendar with the specified name (and starts the
creation process). DisCal will walk you through this process as well.
</td>
</tr>
<tr>
<td style="text-align: center;">&nbsp;!calendar edit</td>
<td style="text-align: center;">&nbsp;!calendar edit</td>
<td style="text-align: center;">&nbsp;!calendar edit</td>
<td style="text-align: center;">&nbsp;This launches the calendar editor if you have already
created a calendar. Use this to change any calendar values with the regular calendar
creation commands if you entered something wrong.
</td>
</tr>
<tr>
<td style="text-align: center;">&nbsp;!calendar description</td>
<td style="text-align: center;">&nbsp;!calendar description &lt;desc, spaces allowed)</td>
<td style="text-align: center;">&nbsp;!calendar create My example calendar</td>
<td style="text-align: center;">&nbsp;Sets the description of the new calendar.</td>
</tr>
<tr>
<td style="text-align: center;">!calendar timezone&nbsp;</td>
<td style="text-align: center;">!calendar timezone &lt;timezone&gt;&nbsp;</td>
<td style="text-align: center;">!calendar timezone America/Chicago&nbsp;</td>
<td style="text-align: center;">&nbsp;Sets the calendar's timezone. Must be a valid timezone
according to the&nbsp;<a
href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List"
target="_blank">TZ database.</a></td>
</tr>
<tr>
<td style="text-align: center;">!calendar review</td>
<td style="text-align: center;">!calendar review</td>
<td style="text-align: center;">!calendar review</td>
<td style="text-align: center;">Displays info about the calendar you are creating, allows
verifying of correct date before creation.&nbsp;&nbsp;&nbsp;
</td>
</tr>
<tr>
<td style="text-align: center;">!calendar cancel</td>
<td style="text-align: center;">!calendar cancel</td>
<td style="text-align: center;">!calendar cancel</td>
<td style="text-align: center;">Cancels the creation of a calendar.&nbsp;</td>
</tr>
<tr>
<td style="text-align: center;">&nbsp;!calendar confirm</td>
<td style="text-align: center;">&nbsp;!calendar confirm</td>
<td style="text-align: center;">&nbsp;!calendar confirm</td>
<td style="text-align: center;">&nbsp;Confirms that the calendar info is correct and creates the
new calendar
</td>
</tr>
<tr>
<td style="text-align: center;">!calendar delete</td>
<td style="text-align: center;">!calendar delete</td>
<td style="text-align: center;">!calendar delete</td>
<td style="text-align: center;">Deletes the calendar from Google and DisCal's database.
<br><br>
<strong>NOTE:&nbsp;</strong>You must have the "MANAGE SERVER" permission in order to delete
the calendar!!!
<br><br>
<strong>NOTE:&nbsp;</strong>Once deleted, you may create a new calendar.
</td>
</tr>
<tr>
<td style="text-align: center;">!calendar&nbsp;remove</td>
<td style="text-align: center;">!calendar&nbsp;remove</td>
<td style="text-align: center;">!calendar&nbsp;remove</td>
<td style="text-align: center;">Deletes the calendar from Google and DisCal's database.
<br><br>
<strong>NOTE:&nbsp;</strong>You must have the "MANAGE SERVER" permission in order to delete
the calendar!!!
<br><br>
<strong>NOTE:&nbsp;</strong>Once deleted, you may create a new calendar.
</td>
</tr>
</tbody>
</table>
</div>
<hr>
<div id="event">
<h2>!event commands</h2>
<table style="border-color: #ef0813;" border="#ef0813" cellspacing="0" cellpadding="4">
<tbody>
<tr>
<td style="max-width: 250px; text-align: center; vertical-align: top;" scope="col">
<h3><span style="text-decoration: underline;"><strong>Command</strong></span></h3>
</td>
<td style="max-width: 250px; text-align: center; vertical-align: top;" scope="col">
<h3><span style="text-decoration: underline;"><strong>Acceptable Arguments</strong></span>
</h3>
</td>
<td style="max-width: 250px; text-align: center; vertical-align: top;" scope="col">
<h3><span style="text-decoration: underline;"><strong>Example</strong></span></h3>
</td>
<td style="max-width: 250px; text-align: center; vertical-align: top;" scope="col">
<h3><span style="text-decoration: underline;"><strong>Description</strong></span></h3>
</td>
</tr>
<tr>
<td style="text-align: center;">&nbsp;!event create</td>
<td style="text-align: center;">!event create<br>!event create &lt;name/summary&gt;&nbsp;</td>
<td style="text-align: center;">&nbsp;!event create<br>!event create My event name/summary</td>
<td style="text-align: center;">Begins creation of a new event. DisCal will walk you through
this process as well! The event summary is technically it's name according to google!&nbsp;
</td>
</tr>
<tr>
<td style="text-align: center;">&nbsp;!event edit</td>
<td style="text-align: center;">!event edit &lt;event ID&gt;&nbsp;</td>
<td style="text-align: center;">&nbsp;!event edit &nbsp;9aiifsgpbaqfjh00iufeu2qsu8&nbsp;</td>
<td style="text-align: center;">Begins editing an already created event that has been confirmed.
Use this to go back and change anything in the event with the same commands you used during
creation.
</td>
</tr>
<tr>
<td style="text-align: center;">!event copy</td>
<td style="text-align: center;">!event copy &lt;event ID&gt;</td>
<td style="text-align: center;">!event copy&nbsp;9aiifsgpbaqfjh00iufeu2qsu8&nbsp;</td>
<td style="text-align: center;">Copies an event from the Guild's calendar. Once the event has
been copied, DisCal will walk you through editing the event's settings to add it to the
calendar.<br/><br/>NOTE: As of the latest version, this WILL NOT copy recurring event's
recurring properties. This is a work in progress!
</td>
</tr>
<tr>
<td style="text-align: center;">&nbsp;!event summary</td>
<td style="text-align: center;">!event summary &lt;summary, spaces allowed&gt;</td>
<td style="text-align: center;">!event summary My event summary&nbsp;</td>
<td style="text-align: center;">Sets the event's summary. This is technically its name according
to Google!&nbsp;
</td>
</tr>
<tr>
<td style="text-align: center;">!event description&nbsp;</td>
<td style="text-align: center;">!event description &lt;description, spaces allowed&gt;</td>
<td style="text-align: center;">!event description My example Event&nbsp;</td>
<td style="text-align: center;">Sets the event's description.&nbsp;</td>
</tr>
<tr>
<td style="text-align: center;">!event startDate&nbsp;<br/>!event start</td>
<td style="text-align: center;">
<p>&nbsp;!event startDate&nbsp;&lt;date/time&gt;</p>
<p>!event start &lt;date/time&gt;</p>
</td>
<td style="text-align: center;">!event startDate 2017/01/31-10:00:00<br/>!event start
2017/04/20-09:15:00
</td>
<td style="text-align: center;">Sets the event's start time. Format is shown. This is in
military/24 hour time.&nbsp;
</td>
</tr>
<tr>
<td style="text-align: center;">!event endDate&nbsp;<br/>!event end</td>
<td style="text-align: center;">!event endDate &lt;date/Time&gt;<br/>!event end &lt;date/Time&gt;&nbsp;
</td>
<td style="text-align: center;">
<p>&nbsp; !event endDate 2017/01/31-14:45:00<br/>!event end 2017/04/31-03:30:00</p>
</td>
<td style="text-align: center;">Sets the event's ending time and date. Format is shown. This is
in military/24 hour time. <br>**NOTE** This is just how long the event lasts, like an hour
or a day. In the event you want an event repeating over months etc, use the RECUR commands!&nbsp;
</td>
</tr>
<tr>
<td style="text-align: center;">!event review</td>
<td style="text-align: center;">!event review&nbsp;</td>
<td style="text-align: center;">!event review&nbsp;</td>
<td style="text-align: center;">&nbsp;Displays the event's info before entered onto the
calendar.
</td>
</tr>
<tr>
<td style="text-align: center;">!event cancel&nbsp;</td>
<td style="text-align: center;">&nbsp;!event cancel</td>
<td style="text-align: center;">!event cancel&nbsp;</td>
<td style="text-align: center;">Cancels creation of a new event.&nbsp;</td>
</tr>
<tr>
<td style="text-align: center;">!event confirm&nbsp;</td>
<td style="text-align: center;">!event confirm</td>
<td style="text-align: center;">!event confirm</td>
<td style="text-align: center;">Confirms the event you have been creating and adds it to the
calendar.&nbsp;
</td>
</tr>
<tr>
<td style="text-align: center;">!event delete&nbsp;</td>
<td style="text-align: center;">!event delete &lt;event id&gt;&nbsp;</td>
<td style="text-align: center;">!event delete 9aiifsgpbaqfjh00iufeu2qsu8&nbsp;</td>
<td style="text-align: center;">&nbsp;Deletes the event with the given ID. This is the event Id
from the calendar. found when the event is listed from the !events command.
</td>
</tr>
<tr>
<td style="text-align: center;">&nbsp;!event view</td>
<td style="text-align: center;">!event view &lt;event id&gt;</td>
<td style="text-align: center;">!event view 9aiifsgpbaqfjh00iufeu2qsu8&nbsp;&nbsp;</td>
<td style="text-align: center;">Prints out detailed info on the specified event.&nbsp;</td>
</tr>
<tr>
<td style="text-align: center;">!event color</td>
<td style="text-align: center;">
<p>!event color &lt;color name&gt;<br/>!event color &lt;color ID&gt;<br/>!event color &lt;hex&gt;<br/>!event
color list</p>
</td>
<td style="text-align: center;">
<p>!event color Mac_And_Cheese</p>
<p>!event color 1<br/>!event color E1E1E1<br/>!event color list</p>
</td>
<td style="text-align: center;">Sets the event's color.<br/>At the moment this color is only
reflected in the event and announcement embeds and does not appear on the google calendar
itself.<br/>Please visit <a
href="https://www.cloudcraftgaming.com/discal/command/event/color" target="_blank">here</a>&nbsp;to
see all supported colors (Google supported colors only)
</td>
</tr>
<tr>
<td style="text-align: center;">!event image</td>
<td style="text-align: center;">
<p>!event image &lt;image url&gt
</td>
<td style="text-align: center;">
<p>!event image http://i.imgur.com/b7r1f.jpg</p>
</td>
<td style="text-align: center;">This will add an image to the event which will be displayed
during announcements as well as when the event is listed with the !events command. At the
moment .jpg, .jpeg and .png image links are supported.
</td>
</tr>
<tr>
<td style="text-align: center;">!event recur</td>
<td style="text-align: center;">
<p>!event recur &lt;true/false&gt;</p>
</td>
<td style="text-align: center;">
<p>!event recur true</p>
</td>
<td style="text-align: center;">
<p>Sets whether or not the event should recur (repeat).</p>
<p>If set to 'true' DisCal will walk you through the steps to configure its recurrence to
how you would like.</p>
</td>
</tr>
<tr>
<td style="text-align: center;">!event&nbsp;frequency</td>
<td style="text-align: center;">
<p>!event&nbsp;frequency &lt;TYPE&gt;</p>
</td>
<td style="text-align: center;">
<p>!event frequency DAILY</p>
</td>
<td style="text-align: center;">
<p>Sets the frequency of the recurring event.</p>
<p>Valid types are: DAILY, WEEKLY, MONTHLY, YEARLY</p>
</td>
</tr>
<tr>
<td style="text-align: center;">!event count</td>
<td style="text-align: center;">
<p>!event count &lt;amount&gt;</p>
</td>
<td style="text-align: center;">
<p>!event count 10</p>
</td>
<td style="text-align: center;">
<p>Sets how many times the event should recur.</p>
<p>NOTE: Use '-1' or '0' to make it infinitely recur.</p>
</td>
</tr>
<tr>
<td style="text-align: center;">!event interval</td>
<td style="text-align: center;">
<p>!event interval &lt;number&gt;</p>
</td>
<td style="text-align: center;">
<p>!event interval 2</p>
</td>
<td style="text-align: center;">Sets how the event will recur based on it's respective
frequency.<br/><br/>EXAMPLE: If frequency is MONTHLY, setting interval to '2' will make the
event recur every OTHER month, whereas 1 will recur EVERY month.
</td>
</tr>
<tr>
<td style="text-align: center;">!rsvp</td>
<td style="text-align: center;">
<p>!rsvp &lt;option&gt; &lt;eventId&gt;</p>
</td>
<td style="text-align: center;">
<p>!rsvp ontime rtk8sua2bolcs8votf02lgep9s</p>
</td>
<td style="text-align: center;">This allows you to set your rsvp status for an event. Status
commands include: Ontime, Late, Unsure and Not. List may also be used to list all rsvps and
remove may be used to remove your rsvp from the list.
</td>
</tr>
</tbody>
</table>
</div>
<hr>
<div id="announcement">
<h2>!announcement commands</h2>
<h3>Aliases: !a !ann !announce !alert !alerts</h3>
<table style="border-color: #ef0813;" border="#ef0813" cellspacing="0" cellpadding="4">
<tbody>
<tr>
<td style="max-width: 250px; text-align: center; vertical-align: top;" scope="col">
<h3><span style="text-decoration: underline;"><strong>Command</strong></span></h3>
</td>
<td style="max-width: 250px; text-align: center; vertical-align: top;" scope="col">
<h3><span style="text-decoration: underline;"><strong>Acceptable Arguments</strong></span>
</h3>
</td>
<td style="max-width: 250px; text-align: center; vertical-align: top;" scope="col">
<h3><span style="text-decoration: underline;"><strong>Example</strong></span></h3>
</td>
<td style="max-width: 250px; text-align: center; vertical-align: top;" scope="col">
<h3><span style="text-decoration: underline;"><strong>Description</strong></span></h3>
</td>
</tr>
<tr>
<td style="text-align: center;">!announcement create</td>
<td style="text-align: center;">!announcement create</td>
<td style="text-align: center;">!announcement create</td>
<td style="text-align: center;">Starts the Announcement Creator. DisCal will now walk you
through the process of creating new announcements!
</td>
</tr>
<tr>
<td style="text-align: center;">!announcement edit</td>
<td style="text-align: center;">!announcement edit &lt;ID&gt;</td>
<td style="text-align: center;">!announcement edit&nbsp;55210ce8-8fed-43ca-9642-3225525baed9
</td>
<td style="text-align: center;">Edits an existing announcement that has already been confirmed.
Use this to go back and change any announcement values with the normal creation commands.
</td>
</tr>
<tr>
<td style="text-align: center;">!announcement copy</td>
<td style="text-align: center;">!announcement copy &lt;ID&gt;</td>
<td style="text-align: center;">!announcement copy&nbsp;55210ce8-8fed-43ca-9642-3225525baed9
</td>
<td style="text-align: center;">Copies an existing announcement as a "template" so you may
quickly create another announcement.
</td>
</tr>
<tr>
<td style="text-align: center;">!announcement type&nbsp;</td>
<td style="text-align: center;">!announcement type &lt;type&gt;</td>
<td style="text-align: center;">!announcement type UNIVERSAL</td>
<td style="text-align: center;">
<p>The type of announcement this is.<br/>UNIVERSAL means it is for ALL future events.<br/>SPECIFIC
means it is for one specific event (will auto delete once fired).<br/>COLOR (or COLOUR)
means it will only fire for an event with the same color.<br/>RECUR functions like a
SPECIFIC type but for recurring events (and does not auto delete).</p>
</td>
</tr>
<tr>
<td style="text-align: center;">&nbsp;!announcement event</td>
<td style="text-align: center;">!announcement event &lt;id&gt;&nbsp;</td>
<td style="text-align: center;">&nbsp;!announcement event&nbsp;9aiifsgpbaqfjh00iufeu2qsu8</td>
<td style="text-align: center;">Specifies the event this announcement is for.<br/>ONLY NEEDED IF
TYPE = SPECIFIC or RECUR<br/><br/>If type is RECUR: Please specify the "base" ID which is a
recurring event's ID without the "_" and everything that follows that. (If the underscore
and following is specified, DisCal will parse it out and ignore it).
</td>
</tr>
<tr>
<td style="text-align: center;">&nbsp;!announcement channel</td>
<td style="text-align: center;">!announcement channel &lt;channel name&gt;</td>
<td style="text-align: center;">!announcement channel calendar</td>
<td style="text-align: center;">
<p>Sets the channel the announcement will be sent in. This is the channel NAME not ID,
DisCal handles that for you.&nbsp;</p>
<p>**Please note: If the channel is deleted the announcement will silently fail to send.</p>
</td>
</tr>
<tr>
<td style="text-align: center;">!announcement&nbsp;hours</td>
<td style="text-align: center;">!announcement hours &lt;amount&gt;</td>
<td style="text-align: center;">!announcement hours 1</td>
<td style="text-align: center;">The amount of hours PRIOR to the event this announcement will be
fired.<br/>This number is added to the minutes in DisCal.
</td>
</tr>
<tr>
<td style="text-align: center;">!announcement minutes&nbsp;</td>
<td style="text-align: center;">!announcement minutes &lt;amount&gt;</td>
<td style="text-align: center;">!announcement minutes 25</td>
<td style="text-align: center;">The amount of minutes PRIOR to the event this announcement will
be fired.<br/>This number is added to the hours in DisCal. The minimum amount of time an
announcement can be set for is 1 minute.
</td>
</tr>
<tr>
<td style="text-align: center;">!announcement review&nbsp;</td>
<td style="text-align: center;">!announcement review</td>
<td style="text-align: center;">!announcement review</td>
<td style="text-align: center;">&nbsp;Allows you to review the Announcement info before
completion, so you may correct any errors or alter the settings.
</td>
</tr>
<tr>
<td style="text-align: center;">&nbsp;!announcement confirm</td>
<td style="text-align: center;">!announcement confirm</td>
<td style="text-align: center;">!announcement confirm</td>
<td style="text-align: center;">Confirms and finishes Announcement creation.</td>
</tr>
<tr>
<td style="text-align: center;">&nbsp;!announcement delete</td>
<td style="text-align: center;">!announcement delete &lt;id&gt;</td>
<td style="text-align: center;">!announcement delete&nbsp;55210ce8-8fed-43ca-9642-3225525baed9
</td>
<td style="text-align: center;">&nbsp;Deletes an existing announcement and stops it from being
fired or used again.
</td>
</tr>
<tr>
<td style="text-align: center;">!announcement subscribe&nbsp;</td>
<td style="text-align: center;">!announcement subscribe &lt;id&gt; (option)</td>
<td style="text-align: center;">
<p>!announcement subscribe&nbsp;55210ce8-8fed-43ca-9642-3225525baed9</p>
<p>!announcement subscribe&nbsp;</p>
<p>55210ce8-8fed-43ca-9642-3225525baed9 user1 user2 user3 etc</p>
<p>!announcement subscribe&nbsp;</p>
<p>55210ce8-8fed-43ca-9642-3225525baed9 role1 role2 user1 etc</p>
<p>!announcement subscribe&nbsp;</p>
<p>55210ce8-8fed-43ca-9642-3225525baed9 everyone</p>
<p>!announcement subscribe&nbsp;</p>
<p>55210ce8-8fed-43ca-9642-3225525baed9 here</p>
</td>
<td style="text-align: center;">
<p>Subscribes you to the announcement. Whenever this announcement sends, you will be
mentioned at the top of it.</p>
<p>&nbsp;</p>
<p>Optionally, you may FORCE subscribe people or roles to announcements. Include their name,
role or there is built in support for everyone and here mentions. No @ required. (At the
time of writing this @ will actually not work at all)</p>
</td>
</tr>
<tr>
<td style="text-align: center;">!announcement unsubscribe</td>
<td style="text-align: center;">!announcement unsubscribe &lt;id&gt; (option)</td>
<td style="text-align: center;">
<p>!announcement subscribe&nbsp;55210ce8-8fed-43ca-9642-3225525baed9</p>
<p>!announcement unsubscribe&nbsp;</p>
<p>55210ce8-8fed-43ca-9642-3225525baed9 user1 user2</p>
<p>!announcement unsubscribe&nbsp;</p>
<p>55210ce8-8fed-43ca-9642-3225525baed9 role1 role2</p>
<p>!announcement unsubscribe&nbsp;</p>
<p>55210ce8-8fed-43ca-9642-3225525baed9 everyone</p>
<p>!announcement unsubscribe&nbsp;</p>
<p>55210ce8-8fed-43ca-9642-3225525baed9 here</p>
</td>
<td style="text-align: center;">
<p>Unsubscribes you from the announcement. Now whenever it sends, you will NO LONGER be
mentioned.&nbsp;</p>
<p>&nbsp;</p>
<p>Optionally, you may FORCE unsubscribe people to announcements. Include their name, role
or there is built in support for everyone and here mentions. No @ required. (At the time
of writing this @ will actually not work at all)</p>
</td>
</tr>
<tr>
<td style="text-align: center;">!announcement&nbsp;view</td>
<td style="text-align: center;">!announcement view &lt;id&gt;</td>
<td style="text-align: center;">!announcement view 55210ce8-8fed-43ca-9642-3225525baed9</td>
<td style="text-align: center;">Displays info about the specif announcement, if the announcement
with that ID exists.
</td>
</tr>
<tr>
<td style="text-align: center;">!announcement&nbsp;list</td>
<td style="text-align: center;">!announcement list &lt;amount OR all&gt;</td>
<td style="text-align: center;">!announcement list 10<br/>or<br/>!announcement list ALL</td>
<td style="text-align: center;">Will list either the amount of IDs specified or if "ALL" will
list every announcement for your Guild.
</td>
</tr>
<tr>
<td style="text-align: center;">!announcement color</td>
<td style="text-align: center;">!announcement color &lt;COLOR&gt;</td>
<td style="text-align: center;">!announcement color BLUE</td>
<td style="text-align: center;">Functions just like '!event color &lt;COLOR&gt;' and sets the
announcement's color.<br/><br/>NOTE: Only applicable if type is COLOR
</td>
</tr>
</tbody>
</table>
</div>
<hr>
<div id="other">
<h2>All Other Commands</h2>
<table style="border-color: #ef0813;" border="#ef0813" cellspacing="0" cellpadding="4">
<tbody>
<tr>
<td style="max-width: 250px; text-align: center; vertical-align: top;" scope="col">
<h3><span style="text-decoration: underline;"><strong>Command</strong></span></h3>
</td>
<td style="max-width: 250px; text-align: center; vertical-align: top;" scope="col">
<h3><span style="text-decoration: underline;"><strong>Acceptable Arguments</strong></span>
</h3>
</td>
<td style="max-width: 250px; text-align: center; vertical-align: top;" scope="col">
<h3><span style="text-decoration: underline;"><strong>Example</strong></span></h3>
</td>
<td style="max-width: 250px; text-align: center; vertical-align: top;" scope="col">
<h3><span style="text-decoration: underline;"><strong>Description</strong></span></h3>
</td>
</tr>
<tr>
<td style="text-align: center;">!linkCalendar&nbsp;</td>
<td style="text-align: center;">!linkCalendar&nbsp;</td>
<td style="text-align: center;">!linkCalendar<br>!linkcal&nbsp;</td>
<td style="text-align: center;">Links the calendar associated with your Discord server so that
you may open it in a web browser.&nbsp;
</td>
</tr>
<tr>
<td style="text-align: center;">!time&nbsp;</td>
<td style="text-align: center;">!time&nbsp;</td>
<td style="text-align: center;">!!time&nbsp;</td>
<td style="text-align: center;">Displays the current time based on the calendar timezone. Useful
for ensuring you have selected the proper timezone, and useful for people to know what time
events are compared to their local time.&nbsp;
</td>
</tr>
<tr>
<td style="text-align: center;">&nbsp;!events</td>
<td style="text-align: center;">!events &lt;amount&gt;</td>
<td style="text-align: center;">!events 5</td>
<td style="text-align: center;">Displays the next upcoming events. The amount is the amount you
wish to display.!events 5&nbsp;&nbsp;
</td>
</tr>
<tr>
<td style="text-align: center;">!help&nbsp;</td>
<td style="text-align: center;">!help&nbsp;</td>
<td style="text-align: center;">!help</td>
<td style="text-align: center;">Displays a list of valid commands and links you to our pages for
extra help with DisCal.
</td>
</tr>
<tr>
<td style="text-align: center;">!discal&nbsp;</td>
<td style="text-align: center;">!discal&nbsp;</td>
<td style="text-align: center;">&nbsp;!discal</td>
<td style="text-align: center;">Displays info about DisCal and links to the main page. This can
be used to easily connect to pages like this without memorizing the URL or bookmarking.&nbsp;
</td>
</tr>
<tr>
<td style="text-align: center;">!discal prefix&nbsp;</td>
<td style="text-align: center;">!discal prefix &lt;prefix&gt;&nbsp;</td>
<td style="text-align: center;">!discal prefix #</td>
<td style="text-align: center;">This changes the prefix DisCal will respond too from the default
! to the prefix of your choice. This allows you to prevent any mix ups with other Discord
bots commands. @ Mentioning also functions as a prefix. Use `@Discal discal prefix !` to
reset if something goes wrong.
</td>
</tr>
<tr>
<td style="text-align: center;">!discal role&nbsp;</td>
<td style="text-align: center;">!discal role &lt;roleName or roleId&gt;&nbsp;</td>
<td style="text-align: center;">!discal role myAdminRole</td>
<td style="text-align: center;">
<p>Sets the role a user most possess in order to control DisCal's event/calendar creation
modules. By default, this will be the "@everyone" role (meaning all users can control
all modules). Should you want to disable needing a specific role, simply use the command
again by specify "everyone" as the role, this will reset it to allow all users proper
access.&nbsp;</p>
<p>**NOTE: If the role is deleted or does not exist, DisCal will reset to all everyone to
use it.</p>
</td>
</tr>
<tr>
<td style="text-align: center;">!discal channel</td>
<td style="text-align: center;">!discal channel &lt;channelName&gt;</td>
<td style="text-align: center;">!discal channel calendar</td>
<td style="text-align: center;">
<p>Sets the channel discal will use and respond in. Should a discal message be used outside
this channel, it will not recognise it.<br/>**Please note: By default, this is not set.
DisCal will work in ALL channels until this is set.</p>
<p>**ANOTHER NOTE: To reset, simply use "all" instead of a valid channel.</p>
<p>**IF the channel does not exist or is deleted, DisCal will reset to use ALL channels.</p>
</td>
</tr>
<tr>
<td style="text-align: center;">!discal simpleAnnouncement</td>
<td style="text-align: center;">!discal simpleAnnouncement</td>
<td style="text-align: center;">!discal simpleAnnouncement</td>
<td style="text-align: center;">
<p>Enables/Disables "simple announcements"<br/>Simple announcements will not list the event
ID or announcement ID. This can be useful for making them more user friendly but the
downside is &nbsp;you cannot debug the announcement should something fail (which rarely
if ever happens).</p>
</td>
</tr>
<tr>
<td style="text-align: center;">!discal invite</td>
<td style="text-align: center;">!discal invite</td>
<td style="text-align: center;">!discal invite</td>
<td style="text-align: center;">
<p>This command will have the bot output an invite link for the DisCal server. Need help?
Come join us and we will get you fixed up. Just want to hang out? That's cool too! We
appreciate all feedback and ideas :)
</td>
</tr>
<tr>
<td style="text-align: center;">!discal settings</td>
<td style="text-align: center;">!discal settings</td>
<td style="text-align: center;">!discal settings</td>
<td style="text-align: center;">
<p>Display's the guild's DisCal settings in a pretty embed.<br/>Settings displayed include
but are not limited to: max calendars, whether your guild is a patron guild, control
role, channel, etc etc.</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
<footer id="footer">
<p>© Nova Maday 2017. All rights reserved. <a href="/pages/policy/privacy.html">Privacy Policy</a></p>
</footer>
</html>

View File

@@ -0,0 +1,66 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<!--Meta stuffs-->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="DisCal Bot brings Google Calendar integration seamlessly into Discord">
<meta property="og:title" content="DisCal Bot"/>
<meta property="og:url" content="https://www.discalbot.com"/>
<meta property="og:description"
content="DisCal Bot brings Google Calendar integration seamlessly into Discord"/>
<!--meta property="og:image" content="I WILL PUT THE LOGO IN WHEN I MAKE IT SMALL"/-->
<title>Dashboard - DisCal</title>
<!--Locally hosted-->
<link href="styles/global.css" rel="stylesheet">
<!--Other stuffs-->
<link href="https://fonts.googleapis.com/css?family=Spectral+SC" rel="stylesheet">
</head>
<body>
<div class="top-nav">
<h1>DisCal</h1>
<a href="/">Home</a>
<a href="/pages/about.html">About</a>
<a href="/pages/commands.html">Commands</a>
<a href="/pages/setup.html">Setup</a>
<a href="/pages/lazy-discal.html">Lazy DisCal</a>
<a class="active" href="/dashboard">Dashboard</a>
<a href="https://discord.gg/AmAMGeN" target="_blank">Support</a>
<a href="https://www.patreon.com/Novafox" target="_blank">Patreon</a>
<a class="account" th:if="${loggedIn}" href="/account/logout">Log out</a>
<!--TODO: Change this when bot goes live to proper auth url-->
<a class="account" th:unless="${loggedIn}"
href="https://discordapp.com/oauth2/authorize?client_id=348893450227286017&scope=guilds+identify&permissions=0&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A4567%2Faccount%2Flogin">Log
in</a>
</div>
<div id="content">
<!--Check if logged in, if not, handle login...-->
<div th:if="${loggedIn}">
<h1 th:text="'Welcome, ' + ${username} + '!'"></h1>
<h3>Select a guild below!</h3>
<!--TODO: IF YOU CAN MAKE THIS LOOP WORK WITH THE METHOD CALL I WILL PAY YOU-->
<th:block th:each="guild : ${guilds}">
<button onclick="goToGuild(this.id)" class="guild" th:id="${guild.id}"
th:text="${guild.name}"></button>
<br>
</th:block>
</div>
<div th:unless="${loggedIn}">
<h1>YOU ARE NOT LOGGED IN!</h1>
<p>Please login to continue.</p>
</div>
</div>
</body>
<footer>
</footer>
</html>

View File

@@ -0,0 +1,108 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<!--Meta stuffs-->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="DisCal Bot brings Google Calendar integration seamlessly into Discord">
<meta property="og:title" content="DisCal Bot"/>
<meta property="og:url" content="https://www.discalbot.com"/>
<meta property="og:description"
content="DisCal Bot brings Google Calendar integration seamlessly into Discord"/>
<!--meta property="og:image" content="I WILL PUT THE LOGO IN WHEN I MAKE IT SMALL"/-->
<title>Dashboard - DisCal</title>
<!--Locally hosted-->
<link href="styles/global.css" rel="stylesheet">
<!--Other stuffs-->
<link href="https://fonts.googleapis.com/css?family=Spectral+SC" rel="stylesheet">
</head>
<body>
<div class="top-nav">
<h1>DisCal</h1>
<a href="/">Home</a>
<a href="/pages/about.html">About</a>
<a href="/pages/commands.html">Commands</a>
<a href="/pages/setup.html">Setup</a>
<a href="/pages/lazy-discal.html">Lazy DisCal</a>
<a class="active" href="/dashboard">Dashboard</a>
<a href="https://discord.gg/AmAMGeN" target="_blank">Support</a>
<a href="https://www.patreon.com/Novafox" target="_blank">Patreon</a>
<a class="account" th:if="${loggedIn}" href="/account/logout">Log out</a>
<!--TODO: Change this when bot goes live to proper auth url-->
<a class="account" th:unless="${loggedIn}"
href="https://discordapp.com/oauth2/authorize?client_id=348893450227286017&scope=guilds+identify&permissions=0&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A4567%2Faccount%2Flogin">Log
In</a>
</div>
<div id="content">
<!--Check if logged in, if not, handle login...-->
<div th:if="${loggedIn}">
<div id="side-nav">
<h4>Main Server Settings</h4>
<a href="/account/dashboard/guild?settings=bot">
<button>Bot Settings</button>
</a>
<br>
<a href="/account/dashboard/guild?settings=guild">
<button>Guild Settings</button>
</a>
<hr>
<h4>Extensions</h4>
<a href="/account/dashboard/guild?settings=ext?ext=test">
<button>Example</button>
</a>
</div>
<div style="display: flow-root">
<!--suppress RequiredAttributes -->
<img th:src="${selected.icon}"
style="display: flex;float: left;max-width: 64px;margin-right: 15px;">
<h3 th:text="'Managing Server: ' + ${selected.name}" style="text-align: left;"></h3>
<hr>
<p th:if="${settings} == null">Please select the settings to edit on the left.</p>
<div th:if="${settings} != null">
<div th:if="${settings} == bot">
<h6>Bot Settings</h6>
<form action="/account/dashboard/update" style="text-align: left">
<label>Bot Nickname
<br>
<input type="text" name="bot-nick" maxlength="32" th:value="${selected.botnick}">
</label>
<input type="submit" class="submit" value="Update">
</form>
<br>
<br>
<form action="/account/dashboard/update" style="text-align: left">
<label>Bot Prefix
<br>
<input type="text" name="prefix" maxlength="32" th:value="${selected.prefix}">
<input type="submit" class="submit" value="Update">
</label>
</form>
<br>
</div>
<div th:if="${settings} == guild">
<h6>Guild Settings</h6>
<p>I dunno if there really even will be settings here</p>
</div>
</div>
</div>
</div>
<div th:unless="${loggedIn}">
<h1>YOU ARE NOT LOGGED IN!</h1>
<!--TODO: Change this when bot goes live to proper auth URL-->
<a href="https://discordapp.com/oauth2/authorize?client_id=390210567639662614&scope=guilds+identify&permissions=0&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A4567%2Faccount%2Flogin">
<button>Login With Discord!</button>
</a>
</div>
</div>
</body>
<footer>
</footer>
</html>

View File

@@ -0,0 +1,90 @@
<!DOCTYPE HTML>
<html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<!--Meta stuffs-->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="DisCal Bot brings Google Calendar integration seamlessly into Discord">
<meta property="og:title" content="DisCal Bot"/>
<meta property="og:url" content="https://www.discalbot.com"/>
<meta property="og:description"
content="DisCal Bot brings Google Calendar integration seamlessly into Discord"/>
<!--meta property="og:image" content="I WILL PUT THE LOGO IN WHEN I MAKE IT SMALL"/-->
<title>Home - DisCal</title>
<!--Locally hosted-->
<link href="styles/global.css" rel="stylesheet">
<!--Other stuffs-->
<link href="https://fonts.googleapis.com/css?family=Spectral+SC" rel="stylesheet">
</head>
<body>
<div class="top-nav">
<h1>DISCAL BOT</h1>
<a class="active" href="/index.html">Home</a>
<a href="/pages/about.html">About</a>
<a href="/pages/commands.html">Commands</a>
<a href="/pages/setup.html">Setup</a>
<a href="/pages/lazy-discal.html">Lazy DisCal</a>
<a href="/dashboard">Dashboard</a>
<a href="https://discord.gg/AmAMGeN" target="_blank">Support</a>
<a href="https://www.patreon.com/Novafox" target="_blank">Patreon</a>
<a class="account" th:if="${loggedIn}" href="/account/logout">Log out</a>
<!--TODO: Change this when bot goes live to proper auth url-->
<a class="account" th:unless="${loggedIn}"
href="https://discordapp.com/oauth2/authorize?client_id=348893450227286017&scope=guilds+identify&permissions=0&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A4567%2Faccount%2Flogin">Log
in</a>
</div>
<div id="content">
<div class="image-wrapper" style="float: left">
<img src="/assets/images/logos/Dark/Opaque/Logo%20Dark%20+bg.png"
style="max-width: 200px; max-height: 200px;">
</div>
<h1>DisCal Bot</h1>
<h3>The Official Discord Calendar Bot</h3>
<p>
DisCal is a powerful Discord bot allowing you to connect Google Calendar to Discord in a seamless
fashion. With superior support and features such as custom calendars, events, automated
announcements,
RSVPs and more, DisCal is the ultimate calendar bot.
</p>
<a href="https://discordapp.com/oauth2/authorize?client_id=265523588918935552&scope=bot&permissions=201845824redirect_uri=https%3A%2F%2Fwww.discalbot.com%2Fpages%2Fsetup.html"
target="_blank">
<button>Add To Discord!</button>
</a>
<hr>
<h2>See What Actual Users Are Saying</h2>
<div class="quote-block">
<div class="quote">
<h4>Casey#8173</h4>
<p>"Keep up the good work guys, this is so desperately needed!"</p>
</div>
<div class="quote">
<h4>ConfigSys.boy!#0189</h4>
<p>"Kudos for finally doing something I've been waiting for since my first day in Discord. Our
multi-game community has been using Google Docs, Images and Calendars for years and linking
the
Calendar to Discord will be a huge boon."</p>
</div>
<div class="quote">
<h4>Bank_CW [DD-R]#3927</h4>
<p>"It is a bot that I see a great demand for"</p>
</div>
</div>
</div>
</body>
<footer id="footer">
<p>© Nova Maday 2017. All rights reserved. <a href="/pages/policy/privacy.html">Privacy Policy</a></p>
</footer>
</html>

View File

@@ -0,0 +1,61 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/styles/global.css">
<title>Lazy DisCal - DisCal Bot</title>
</head>
<body>
<div class="top-nav">
<h1>DISCAL BOT</h1>
<a href="/index.html">Home</a>
<a href="/pages/about.html">About</a>
<a href="/pages/commands.html">Commands</a>
<a href="/pages/setup.html">Setup</a>
<a class="active" href="/pages/lazy-discal.html">Lazy DisCal</a>
<a href="https://discord.gg/AmAMGeN" target="_blank">Support</a>
<a href="https://www.patreon.com/Novafox" target="_blank">Patreon</a>
</div>
<div id="content">
<div id="about">
<div class="image-wrapper" style="float: left">
<img src="/assets/images/lazy-discal/lazy-discal-program.jpg"
style="max-width: 300px; max-height: 300px;">
</div>
<h3>About Lazy DisCal</h3>
<p>
Lazy DisCal is an application scripted by Callador using Auto Hotkey. This application helps to make
it easy to create events, announcements, and calendars for DisCal with a very simple and easy to use
GUI.
<br>
<br>
Built for one purpose: Making your life easier by remembering all the DisCal commands for you! Can't
remember what format to put your start date? No problem!
<br>
<br>
All you have to do is fill out the various fields in LD and then click one of the submit buttons. LD
will wait a few seconds for you to bring up discord and then send all the appropriate commands to
your channel for you.
<br>
<br>
It supports saving and loading of events as text files for quick event creation. It can build events
and announcements separately or together and has the capability to build up to 50 announcements at
one time.
</p>
<a download="" href="/assets/files/Lazy-DisCal/Windows/Lazy_Discal_GUI-1.0.8.0.exe">
<button>Windows</button>
</a>
<hr>
<p>*Note: until Lazy DisCal is developed for linux/mac you can run it via Wine.</p>
</div>
</div>
</body>
<footer id="footer">
<p>© Nova Maday 2017. All rights reserved. <a href="/pages/policy/privacy.html">Privacy Policy</a></p>
</footer>
</html>

View File

@@ -0,0 +1,494 @@
<!DOCTYPE HTML>
<html>
<head>
<link rel="stylesheet" href="/styles/global.css">
<title>Privacy Policy - DisCal Bot</title>
</head>
<body>
<div class="top-nav">
<h1>DISCAL BOT</h1>
<a href="/index.html">Home</a>
<a href="/pages/about.html">About</a>
<a href="/pages/commands.html">Commands</a>
<a href="/pages/setup.html">Setup</a>
<a href="/pages/lazy-discal.html">Lazy DisCal</a>
<a href="https://discord.gg/AmAMGeN" target="_blank">Support</a>
<a href="https://www.patreon.com/Novafox" target="_blank">Patreon</a>
</div>
<div id="content">
<h1>Privacy Policy</h1>
<p>
This privacy policy has been compiled to better serve those who are concerned with how their 'Personally
Identifiable
Information' (PII) is being used online. PII, as described in US privacy law and information security,
is
information
that can be used on its own or with other information to identify, contact, or locate a single person,
or to
identify an
individual in context. Please read our privacy policy carefully to get a clear understanding of how we
collect,
use,
protect or otherwise handle your Personally Identifiable Information in accordance with our website.<br>
<br>
<br>
<br>
What personal information do we collect from the people that visit our blog, website or app?<br>
<br>
<br>
<br>
When ordering or registering on our site, as appropriate, you may be asked to enter your name, email
address,
mailing
address, phone number, credit card information or other details to help you with your experience.<br>
<br>
<br>
<br>
When do we collect information?<br>
<br>
<br>
<br>
We collect information from you when you place an order, subscribe to a newsletter, fill out a form or
enter
information
on our site.<br>
<br>
<br>
Provide us with feedback on our products or services Register with MineCash&#160;<br>
<br>
How do we use your information?<br>
<br>
<br>
<br>
We may use the information we collect from you when you register, make a purchase, sign up for our
newsletter,
respond
to a survey or marketing communication, surf the website, or use certain other site features in the
following
ways:<br>
<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;To personalize your experience and to allow us to
deliver
the
type of
content and product offerings in which you are most interested.<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;To improve our website in order to better serve you.<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;To allow us to better service you in responding to your
customer
service
requests.<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;To quickly process your transactions.<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;To follow up with them after correspondence (live chat,
email or
phone
inquiries)<br>
<br>
<br>
<br>
How do we protect your information?<br>
<br>
<br>
<br>
Our website is scanned on a regular basis for security holes and known vulnerabilities in order to make
your
visit to
our site as safe as possible.<br>
<br>
<br>
We use regular Malware Scanning.<br>
<br>
<br>
Your personal information is contained behind secured networks and is only accessible by a limited
number of
persons who
have special access rights to such systems, and are required to keep the information confidential. In
addition,
all
sensitive/credit information you supply is encrypted via Secure Socket Layer (SSL) technology.<br>
<br>
<br>
<br>
We implement a variety of security measures when a user places an order to maintain the safety of your
personal
information.<br>
<br>
<br>
<br>
All transactions are processed through a gateway provider and are not stored or processed on our
servers.<br>
<br>
<br>
<br>
Do we use 'cookies'?<br>
<br>
<br>
<br>
Yes. Cookies are small files that a site or its service provider transfers to your computer's hard drive
through
your
Web browser (if you allow) that enables the site's or service provider's systems to recognize your
browser
and
capture
and remember certain information. For instance, we use cookies to help us remember and process the items
in
your
shopping cart. They are also used to help us understand your preferences based on previous or current
site
activity,
which enables us to provide you with improved services. We also use cookies to help us compile aggregate
data
about site
traffic and site interaction so that we can offer better site experiences and tools in the future.<br>
<br>
<br>
We use cookies to:<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;Help remember and process the items in the shopping
cart.<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;Understand and save user's preferences for future
visits.<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;Compile aggregate data about site traffic and site
interactions
in order
to offer better site experiences and tools in the future. We may also use trusted third-party services
that
track this
information on our behalf.<br>
<br>
<br>
You can choose to have your computer warn you each time a cookie is being sent, or you can choose to
turn
off
all
cookies. You do this through your browser settings. Since browser is a little different, look at your
browser's
Help
Menu to learn the correct way to modify your cookies.<br>
<br>
<br>
<br>
If you turn cookies off, some features will be disabled. It won't affect the user's experience that make
your
site
experience more efficient and may not function properly.<br>
<br>
<br>
<br>
However, you will still be able to place orders .<br>
<br>
<br>
<br>
Third-party disclosure<br>
<br>
<br>
<br>
We do not sell, trade, or otherwise transfer to outside parties your Personally Identifiable Information
unless
we
provide users with advance notice. This does not include website hosting partners and other parties who
assist
us in
operating our website, conducting our business, or serving our users, so long as those parties agree to
keep
this
information confidential. We may also release information when it's release is appropriate to comply
with
the
law,
enforce our site policies, or protect ours or others' rights, property or safety.&#160;<br>
<br>
However, non-personally identifiable visitor information may be provided to other parties for marketing,
advertising, or
other uses.<br>
<br>
<br>
<br>
Third-party links<br>
<br>
<br>
<br>
Occasionally, at our discretion, we may include or offer third-party products or services on our
website.
These
third-party sites have separate and independent privacy policies. We therefore have no responsibility or
liability for
the content and activities of these linked sites. Nonetheless, we seek to protect the integrity of our
site
and
welcome
any feedback about these sites.<br>
<br>
<br>
<br>
Google<br>
<br>
<br>
<br>
Google's advertising requirements can be summed up by Google's Advertising Principles. They are put in
place
to
provide
a positive experience for users. https://support.google.com/adwordspolicy/answer/1316548?hl=en<br>
<br>
<br>
We use Google AdSense Advertising on our website.<br>
<br>
<br>
Google, as a third-party vendor, uses cookies to serve ads on our site. Google's use of the DART cookie
enables
it to
serve ads to our users based on previous visits to our site and other sites on the Internet. Users may
opt-out
of the
use of the DART cookie by visiting the Google Ad and Content Network privacy policy.<br>
<br>
<br>
We have implemented the following:<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;Remarketing with Google AdSense<br>
<br>
<br>
<br>
We, along with third-party vendors such as Google use first-party cookies (such as the Google Analytics
cookies)
and
third-party cookies (such as the DoubleClick cookie) or other third-party identifiers together to
compile
data
regarding
user interactions with ad impressions and other ad service functions as they relate to our website.<br>
<br>
<br>
Opting out:<br>
Users can set preferences for how Google advertises to you using the Google Ad Settings page.
Alternatively,
you
can opt
out by visiting the Network Advertising Initiative Opt Out page or by using the Google Analytics Opt Out
Browser
add on.<br>
<br>
<br>
<br>
California Online Privacy Protection Act<br>
<br>
<br>
<br>
CalOPPA is the first state law in the nation to require commercial websites and online services to post
a
privacy
policy. The law's reach stretches well beyond California to require any person or company in the United
States
(and
conceivably the world) that operates websites collecting Personally Identifiable Information from
California
consumers
to post a conspicuous privacy policy on its website stating exactly the information being collected and
those
individuals or companies with whom it is being shared. - See more at:
http://consumercal.org/california-online-privacy-protection-act-caloppa/#sthash.0FdRbT51.dpuf<br>
<br>
<br>
According to CalOPPA, we agree to the following:<br>
<br>
Users can visit our site anonymously.<br>
<br>
Once this privacy policy is created, we will add a link to it on our home page or as a minimum, on the
first
significant
page after entering our website.<br>
<br>
Our Privacy Policy link includes the word 'Privacy' and can easily be found on the page specified above.<br>
<br>
<br>
You will be notified of any Privacy Policy changes:<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;On our Privacy Policy Page<br>
<br>
Can change your personal information:<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;By emailing us<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;By logging in to your account<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;By chatting with us or by sending us a support
ticket<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;Others<br>
<br>
<br>
How does our site handle Do Not Track signals?<br>
<br>
We honor Do Not Track signals and Do Not Track, plant cookies, or use advertising when a Do Not Track
(DNT)
browser
mechanism is in place.<br>
<br>
<br>
Does our site allow third-party behavioral tracking?<br>
<br>
It's also important to note that we do not allow third-party behavioral tracking<br>
<br>
<br>
<br>
COPPA (Children Online Privacy Protection Act)<br>
<br>
<br>
<br>
When it comes to the collection of personal information from children under the age of 13 years old, the
Children's
Online Privacy Protection Act (COPPA) puts parents in control. The Federal Trade Commission, United
States'
consumer
protection agency, enforces the COPPA Rule, which spells out what operators of websites and online
services
must
do to
protect children's privacy and safety online.<br>
<br>
<br>
We market to<br>
<br>
We do not collect information from children under 13<br>
<br>
children under 13.<br>
<br>
No<br>
<br>
<br>
In order to remove your child's information please contact the following personnel:<br>
<br>
<br>
We adhere to the following COPPA tenants:<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;Parents can review, delete, manage or refuse with whom
their
child's
information is shared through contacting us directly.<br>
<br>
or contacting us directly.<br>
<br>
<br>
<br>
<br>
Fair Information Practices<br>
<br>
<br>
<br>
The Fair Information Practices Principles form the backbone of privacy law in the United States and the
concepts
they
include have played a significant role in the development of data protection laws around the globe.
Understanding the
Fair Information Practice Principles and how they should be implemented is critical to comply with the
various
privacy
laws that protect personal information.<br>
<br>
<br>
In order to be in line with Fair Information Practices we will take the following responsive action,
should
a
data
breach occur:<br>
<br>
We will notify you via email<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;Within 7 business days<br>
<br>
We will notify the users via in-site notification<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;Within 7 business days<br>
<br>
<br>
We also agree to the Individual Redress Principle which requires that individuals have the right to
legally
pursue
enforceable rights against data collectors and processors who fail to adhere to the law. This principle
requires
not
only that individuals have enforceable rights against data users, but also that individuals have
recourse to
courts or
government agencies to investigate and/or prosecute non-compliance by data processors.<br>
<br>
<br>
<br>
CAN SPAM Act<br>
<br>
<br>
<br>
The CAN-SPAM Act is a law that sets the rules for commercial email, establishes requirements for
commercial
messages,
gives recipients the right to have emails stopped from being sent to them, and spells out tough
penalties
for
violations.<br>
<br>
<br>
We collect your email address in order to:<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;Send information, respond to inquiries, and/or other
requests or
questions<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;Process orders and to send information and updates
pertaining to
orders.<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;Send you additional information related to your product
and/or
service<br>
<br>
<br>
To be in accordance with CANSPAM, we agree to the following:<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;Not use false or misleading subjects or email addresses.<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;Identify the message as an advertisement in some
reasonable
way.<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;Include the physical address of our business or site
headquarters.<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;Monitor third-party email marketing services for
compliance,
if
one is
used.<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;Honor opt-out/unsubscribe requests quickly.<br>
<br>
&#160;&#160;&#160;&#160;&#160;&#160;&bull;&#160;Allow users to unsubscribe by using the link at the
bottom
of
each
email.<br>
<br>
<br>
If at any time you would like to unsubscribe from receiving future emails, you can email us at<br>
<br>
cloudcraftcontact@gmail.com and we will promptly remove you from ALL&#160;correspondence.<br>
<br>
<br>
Last Edited on 2017-11-12
</p>
</div>
</body>
<footer id="footer">
<p>© Nova Maday 2017. All rights reserved. <a href="/pages/policy/privacy.html">Privacy Policy</a></p>
</footer>
</html>

View File

@@ -0,0 +1,111 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/styles/global.css">
<title>Setup - DisCal Bot</title>
</head>
<body>
<div class="top-nav">
<h1>DISCAL BOT</h1>
<a href="/index.html">Home</a>
<a href="/pages/about.html">About</a>
<a href="/pages/commands.html">Commands</a>
<a class="active" href="setup.html">Setup</a>
<a href="/pages/lazy-discal.html">Lazy DisCal</a>
<a href="https://discord.gg/AmAMGeN" target="_blank">Support</a>
<a href="https://www.patreon.com/Novafox" target="_blank">Patreon</a>
</div>
<div id="content">
<h1>DisCal Bot - Setup</h1>
<p>
The following page is a step by step instruction for how to properly setup DisCal.
<br><br>
It's quite simple, but you must create a new calendar in order for DisCal to function fully.
</p>
<div id="invite">
<h3>1. Add DisCal to your Discord server</h3>
<div class="image-wrapper" style="float: left">
<img src="/assets/images/examples/DisCal-Invite-Perms.jpg"
style="max-width: 300px; max-height: 300px;">
</div>
<p>
<strong>Skip this step if you have just added DisCal to your server.</strong>
<br><br><br>
Click the button below to authorize DisCal and select the server you want it on.
<br><br><br><br>
You must have the "Manage Server" permission to add the bot. Contact the server owner or admin to
invite DisCal if you cannot.
<br><br><br>
</p>
<a href="https://discordapp.com/oauth2/authorize?client_id=265523588918935552&scope=bot&permissions=201845824redirect_uri=https%3A%2F%2Fwww.discalbot.com%2Fpages%2Fsetup.html"
target="_blank">
<button>Add DisCal to Discord Server</button>
</a>
</div>
<hr>
<div id="role">
<div class="image-wrapper" style="float: left">
<img src="/assets/images/examples/DisCal-Role-Perms.jpg"
style="max-width: 300px; max-height: 300px;">
</div>
<h3>2. Give DisCal permissions at server level and not at channel level (channel permissions don't
always work right)</h3>
<p>
If you have a Bot permission group, assign DisCal that permission.
<br><br>
If not, create a new permission role with the following permissions so that DisCal may read and send
messages based on its commands:
<br><br>
- Read Messages*
<br>
- Send Messages
<br>
- Embed Links
<br>
- Mention Everyone
<br>
- Manager Messages
<br>
- Reactions/External Emojis
<br><br><br>
* DisCal will not log or save any messages.
<br><br>
</p>
</div>
<hr>
<div id="calendar">
<div class="image-wrapper" style="float: left">
<img src="/assets/images/examples/DisCal-Calendar-Creation.jpg"
style="max-width: 300px; max-height: 300px;">
</div>
<h3>3. Create a Calendar for your Discord</h3>
<p>
DisCal currently only allows 1 calendar per server. Use the command "!calendar create [calendar
name]" in any channel. (You can create a special channel just for DisCal commands if you want.)
<br> <br>
Once that command is issued, DisCal will handle the rest and walk you through a step by step guide
into creating your new calendar!
<br> <br>
Optionally you may attach an external calendar. (This feature is currently in open testing for
Patreon supporters. Visit our Discord server for more information.)
<br><br><br>
For all information on existing commands as well as some tutorial .gifs, click the button below.
</p>
<a href="commands.html">
<button>View Commands</button>
</a>
</div>
</div>
</body>
<footer id="footer">
<p>© Nova Maday 2017. All rights reserved. <a href="/pages/policy/privacy.html">Privacy Policy</a></p>
</footer>
</html>

View File

@@ -0,0 +1,3 @@
function goToGuild(guildId) {
window.location.href = "/account/dashboard/select?guild=" + guildId;
}

View File

@@ -0,0 +1,241 @@
/* The basics */
html {
position: relative;
min-height: 100%;
overflow: auto;
}
body {
background: #242428;
min-height: 100%;
margin: 0 0 25px; /* bottom = footer height */
overflow-x: hidden;
}
footer {
position: absolute;
bottom: 0;
height: 25px;
}
h1, h2, h3, h4, h5, h6 {
text-align: center;
color: #E67C3F;
}
h1 {
font-size: 32pt;
}
h2 {
font-size: 28pt;
}
h3 {
font-size: 24pt
}
h4, h5, h6 {
font-size: 20pt;
}
p {
font-size: 18pt;
color: white;
margin: 20px;
}
button {
font-size: 18px;
background-color: #E67C3F;
color: white;
padding: 10px;
border: 2px black;
margin: 10px;
width: auto;
height: auto;
}
button:hover {
color: black;
}
hr {
color: #E67C3F;
background-color: #E67C3F;
border-color: transparent;
height: 1px;
max-width: 100%;
width: auto;
}
table {
height: 454px;
max-width: 820px;
border: #ef0813 2px;
margin-left: 10px;
margin-right: 10px;
position: center;
}
td {
color: white;
}
textarea {
resize: none;
}
select {
border: 1px solid black;
border-radius: 2px;
padding-top: 6px;
padding-bottom: 6px;
width: 200px;
}
input.submit {
padding-top: 8px;
padding-bottom: 8px;
border: 1px solid #E67C3F;
background: #E67C3F;
}
input.submit:hover {
padding-top: 8px;
padding-bottom: 8px;
border: 1px solid white;
background: white;
color: black;
}
label {
color: white;
}
/* Wrappers */
#content {
margin-left: 5%;
margin-right: 5%;
margin-bottom: auto;
background-color: #3c3d41;
padding: 10px;
text-align: center;
display: flow-root;
}
.image-wrapper {
position: center;
margin: 10px;
display: inline-flex;
}
.image-wrapper img {
width: 100%;
display: inline-block;
}
.quote-block {
width: auto;
display: inline-flex;
position: center;
}
.quote {
margin: 5px;
padding: 5px;
float: left;
display: inline-block;
background: #bcbcbc;
border-radius: 6px;
border: 4px #E67C3F;
max-width: 400px;
height: auto;
}
.quote p {
text-align: center;
color: #242428;
}
.quote h4 {
color: #ef0813;
}
#side-nav {
margin-left: 0;
margin-right: 10px;
margin-bottom: auto;
background-color: #3c3d41;
padding: 10px;
text-align: center;
float: left;
border: #E67C3F 2px solid;
border-radius: 10px;
}
/* Navigation bar */
.top-nav {
overflow: hidden;
background-color: #E67C3F;
}
.top-nav h1 {
float: left;
color: white;
text-align: center;
margin-left: 15px;
margin-top: auto;
margin-bottom: auto;
font-family: 'Spectral SC', serif;
font-size: 25pt;
}
.top-nav a {
float: left;
color: white;
text-align: center;
padding: 15px 18px;
font-size: 20px;
text-decoration: none;
}
.top-nav a:hover {
background-color: white;
color: #090809;
}
.top-nav a.active {
background-color: #3c3d41;
color: white;
}
.top-nav a.account {
float: right;
text-align: center;
padding: 15px 18px;
font-size: 20px;
text-decoration: none;
}
.top-nav p.account {
float: right;
color: white;
text-align: center;
padding: 15px 18px;
font-size: 20px;
text-decoration: none;
}