Skip to content

Commit

Permalink
Merge pull request #6 from ShindouMihou/master
Browse files Browse the repository at this point in the history
[Patch] v1.1.1 - Renovation.
  • Loading branch information
ShindouMihou authored May 1, 2021
2 parents 74b4d53 + 122b7c8 commit ad196c6
Show file tree
Hide file tree
Showing 36 changed files with 534 additions and 515 deletions.
64 changes: 32 additions & 32 deletions src/main/java/pw/mihou/amelia/Amelia.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
Expand All @@ -40,11 +41,18 @@
public class Amelia {

private static final String token = System.getenv("amelia_token");
private static final HashMap<Integer, DiscordApi> shards = new HashMap<>();

public static void main(String[] args) {
// Logger Setup.
FallbackLoggerConfiguration.setTrace(true);

Runtime.getRuntime().addShutdownHook(new Thread(() -> {
shards.values().forEach(DiscordApi::disconnect);
MongoDB.shutdown();
Scheduler.shutdown();
}));

// The DiscordAPI Builder.
new DiscordApiBuilder()
.setToken(token) // Logins with the bot token.
Expand All @@ -55,26 +63,20 @@ public static void main(String[] args) {
.loginAllShards().forEach(shard -> shard.thenAccept(Amelia::onShardLogin).exceptionally(ExceptionLogger.get())); // After they reply, we then direct each shard to a onShardLogin.
}

private static int determineNextTarget(){
return LocalDateTime.now().getMinute()%10 != 0 ? (LocalDateTime.now().getMinute() + (10 - LocalDateTime.now().getMinute() % 10)) - LocalDateTime.now().getMinute() : 0;
private static int determineNextTarget() {
return LocalDateTime.now().getMinute() % 10 != 0 ? (LocalDateTime.now().getMinute() + (10 - LocalDateTime.now().getMinute() % 10)) - LocalDateTime.now().getMinute() : 0;
}

private static void onShardLogin(DiscordApi api){
private static void onShardLogin(DiscordApi api) {

/* Performance optimizations **/
shards.put(api.getCurrentShard(), api);

/* Performance optimizations **/
api.setAutomaticMessageCacheCleanupEnabled(true);
api.setMessageCacheSize(10, 60 * 5);
api.setMessageCacheSize(10, 1);
api.setReconnectDelay(attempt -> attempt * 2);
FeedDB.preloadAllModels();
api.updateActivity(ActivityType.WATCHING, "The bot is starting up...");

Runtime.getRuntime().addShutdownHook(new Thread(() -> {
// Calls all the shutdowns needed.
api.disconnect();
MongoDB.shutdown();
Scheduler.shutdown();
}));
Terminal.log("Javacord Optimizations and Shutdown hook is now ready!");

registerAllCommands(api);
Expand All @@ -83,33 +85,31 @@ private static void onShardLogin(DiscordApi api){
Terminal.log("The bot has started!");
int initial = determineNextTarget();
Terminal.log("The scheduler will be delayed for " + initial + " minutes for synchronization.");
Scheduler.schedule(() -> {
FeedDB.retrieveAllModels().thenAccept(feedModels -> feedModels.forEach(feedModel -> {
// We want them all to be executed in different threads to speed up everything.
CompletableFuture.runAsync(() -> ReadRSS.getLatest(feedModel.getFeedURL()).ifPresentOrElse(syndEntry -> {
if(syndEntry.getPublishedDate().after(feedModel.getDate())){
api.getServerTextChannelById(feedModel.getChannel()).ifPresent(tc -> {
feedModel.setPublishedDate(syndEntry.getPublishedDate()).update(tc.getServer().getId()).thenAccept(unused ->
Message.msg(MessageDB.getFormat(tc.getServer().getId())
.replaceAll("\\{title}", syndEntry.getTitle())
.replaceAll("\\{author}", StoryHandler.getAuthor(syndEntry.getAuthor(), feedModel.getId()))
.replaceAll("\\{link}", syndEntry.getLink())
.replaceAll("\\{subscribed}", getMentions(feedModel.getMentions(), tc.getServer()))).send(tc));
System.out.printf("[%s]: RSS feed deployed for: %s with feed id: [%d]", DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss").format(LocalDateTime.now()), tc.getServer().getName(), feedModel.getUnique());
});
}
}, () -> Logger.getLogger("Amelia-chan").log(Level.SEVERE, "We couldn't connect to ScribbleHub: " + feedModel.getFeedURL())), Scheduler.getExecutorService());
}));
}, initial, 10, TimeUnit.MINUTES);
Scheduler.schedule(() -> FeedDB.retrieveAllModels().thenAccept(feedModels -> feedModels.forEach(feedModel -> {
// We want them all to be executed in different threads to speed up everything.
CompletableFuture.runAsync(() -> ReadRSS.getLatest(feedModel.getFeedURL()).ifPresentOrElse(syndEntry -> {
if (syndEntry.getPublishedDate().after(feedModel.getDate())) {
api.getServerTextChannelById(feedModel.getChannel()).ifPresent(tc -> {
feedModel.setPublishedDate(syndEntry.getPublishedDate()).update(tc.getServer().getId()).thenAccept(unused ->
Message.msg(MessageDB.getFormat(tc.getServer().getId())
.replaceAll("\\{title}", syndEntry.getTitle())
.replaceAll("\\{author}", StoryHandler.getAuthor(syndEntry.getAuthor(), feedModel.getId()))
.replaceAll("\\{link}", syndEntry.getLink())
.replaceAll("\\{subscribed}", getMentions(feedModel.getMentions(), tc.getServer()))).send(tc));
System.out.printf("[%s]: RSS feed deployed for: %s with feed id: [%d]\n", DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss").format(LocalDateTime.now()), tc.getServer().getName(), feedModel.getUnique());
});
}
}, () -> Logger.getLogger("Amelia-chan").log(Level.SEVERE, "We couldn't connect to ScribbleHub: " + feedModel.getFeedURL())), Scheduler.getExecutorService());
})), initial, 10, TimeUnit.MINUTES);
}

private static String getMentions(ArrayList<Long> roles, Server server){
private static String getMentions(ArrayList<Long> roles, Server server) {
StringBuilder builder = new StringBuilder();
roles.forEach(aLong -> builder.append(server.getRoleById(aLong).map(Role::getMentionTag).orElse("[Vanished Role]")));
return builder.toString();
}

private static void registerAllCommands(DiscordApi api){
private static void registerAllCommands(DiscordApi api) {
api.addListener(new HelpCommand());
api.addListener(new RegisterCommand());
api.addListener(new FeedsCommand());
Expand Down
16 changes: 7 additions & 9 deletions src/main/java/pw/mihou/amelia/commands/Limitations.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@

public class Limitations {

public static boolean isLimited(Server server, User user){
if (ServerDB.getServer(server.getId()).getLimit() ) {
if(server.canManage(user) || hasRole(user, server)|| server.isAdmin(user) || server.isOwner(user)){
return false;
} else {
return true;
}
}
return false;
public static boolean isLimited(Server server, User user) {
// If anarchy mode is enabled.
if(!ServerDB.getServer(server.getId()).getLimit())
return true;

// If anarchy mode is disabled, then we check if the user is any of these.
return server.canManage(user) || hasRole(user, server) || server.isAdmin(user) || server.isOwner(user);
}

}
64 changes: 33 additions & 31 deletions src/main/java/pw/mihou/amelia/commands/base/Command.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ public abstract class Command implements MessageCreateListener {
private final String command;
private final long cooldown = 5L;
private final HashMap<Long, HashMap<Long, Long>> userCooldowns = new HashMap<>();
private boolean limited;
private final boolean limited;

protected Command(String command, String description, String usage, boolean limited){
protected Command(String command, String description, String usage, boolean limited) {
this.command = command;
Commands.addCommand(command, description, usage, 5L);
this.limited = limited;
Expand All @@ -29,52 +29,54 @@ protected Command(String command, String description, String usage, boolean limi
public void onMessageCreate(MessageCreateEvent event) {

// We do not allow non-server messages (private messages, or group messages).
if(!event.isServerMessage())
if (!event.isServerMessage())
return;

// We don't want any messages from bots, since it makes sense not to.
if(!event.getMessageAuthor().isRegularUser())
if (!event.getMessageAuthor().isRegularUser())
return;

event.getServer().ifPresent(server -> {
if(event.getMessageContent().startsWith(PrefixManager.prefix(server.getId())+command)){
if (event.getMessageContent().startsWith(PrefixManager.prefix(server.getId()) + command)) {

// Checks if the user is in cooldown and the data inside the cooldown isn't null.
if(userCooldowns.containsKey(event.getMessageAuthor().getId()) &&

userCooldowns.get(event.getMessageAuthor().getId()).get(server.getId()) != null){
long secondsLeft = ((
userCooldowns.get(event.getMessageAuthor().getId()).get(event.getServer().get().getId())/1000)+cooldown) - (System.currentTimeMillis()/1000);
if(secondsLeft>0){
Message.msg("This command is still under cooldown for " + secondsLeft + " seconds!").send(event.getChannel()).thenAccept(message -> message.addReactionAddListener(e -> {
// No purpose at all.
}).removeAfter(secondsLeft, TimeUnit.SECONDS).addRemoveHandler(() -> {
message.delete("Cooldown off.");
event.getMessage().delete("Cleanliness matters.");
}));
return;
}
if (userCooldowns.containsKey(event.getMessageAuthor().getId()) &&

userCooldowns.get(event.getMessageAuthor().getId()).get(server.getId()) != null) {
long secondsLeft = ((
userCooldowns.get(event.getMessageAuthor().getId()).get(event.getServer().get().getId()) / 1000) + cooldown) - (System.currentTimeMillis() / 1000);
if (secondsLeft > 0) {
Message.msg("This command is still under cooldown for " + secondsLeft + " seconds!").send(event.getChannel()).thenAccept(message -> message.addReactionAddListener(e -> {
// No purpose at all.
}).removeAfter(secondsLeft, TimeUnit.SECONDS).addRemoveHandler(() -> {
message.delete("Cooldown off.");
event.getMessage().delete("Cleanliness matters.");
}));
return;
}
}

// Feel free to improve this one, but all it does is store the data back to the Map.
HashMap<Long, Long> n = new HashMap<>();
n.put(server.getId(), System.currentTimeMillis());
userCooldowns.put(event.getMessageAuthor().getId(), n);
// We will now begin the execution of the command.
if(limited) {
if (Limitations.isLimited(server, event.getMessageAuthor().asUser().get())) {
Message.msg("You do not have permission to use this command, required permission: Manage Server, or lacking the required role to modify feeds.").send(event.getChannel());
return;
// Feel free to improve this one, but all it does is store the data back to the Map.
HashMap<Long, Long> n = new HashMap<>();
n.put(server.getId(), System.currentTimeMillis());
userCooldowns.put(event.getMessageAuthor().getId(), n);

event.getMessageAuthor().asUser().ifPresent(user -> {

if (limited) {
if (!Limitations.isLimited(server, user)) {
Message.msg("You do not have permission to use this command, required permission: Manage Server, or lacking the required role to modify feeds.").send(event.getChannel());
return;
}
}

}
event.getMessageAuthor().asUser().ifPresent(user -> runCommand(event, event.getMessageAuthor().asUser().get(), server, event.getMessageContent().split(" ")));
runCommand(event, user, server, event.getMessageContent().split(" "));
});
}
});
}

protected abstract void runCommand(MessageCreateEvent event, User user, Server server, String[] args);



}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ public class PrefixManager {

/**
* Retrieves and returns the prefix for the server.
*
* @param serverId the server id.
* @return the prefix of the server.
*/
public static String prefix(long serverId){
public static String prefix(long serverId) {
return ServerDB.getServer(serverId).getPrefix();
}

Expand Down
53 changes: 29 additions & 24 deletions src/main/java/pw/mihou/amelia/commands/base/db/ServerDB.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ public class ServerDB {

/**
* Adds a server to the database.
*
* @param model the server model.
*/
public static ServerModel addServer(ServerModel model){
public static ServerModel addServer(ServerModel model) {
Document doc = new Document("id", model.getId()).append("prefix", model.getPrefix()).append("limit", model.getLimit())
.append("role", model.getRole().isPresent() ? model.getRole().get() : 0L);

if(validate(model.getId())){
if (validate(model.getId())) {
// If the server already exists in the databse, replace it.
db.replaceOne(eq("id", model.getId()), doc);
} else {
Expand All @@ -32,7 +33,7 @@ public static ServerModel addServer(ServerModel model){
}

// Adding it to the map, so we don't have to call our database every time which is exhausting.
if(servers.containsKey(model.getId())){
if (servers.containsKey(model.getId())) {
servers.replace(model.getId(), model);
} else {
servers.put(model.getId(), model);
Expand All @@ -44,52 +45,56 @@ public static ServerModel addServer(ServerModel model){

/**
* Deletes the server from the database.
*
* @param id the id of the server.
*/
public static void deleteServer(long id){
public static void deleteServer(long id) {
db.deleteOne(eq("id", id));
}

/**
* Checks if data of a server exists.
*
* @param id the id to check.
* @return boolean.
*/
public static boolean validate(long id){
public static boolean validate(long id) {
return db.find(eq("id", id)).first() != null;
}

/**
* Requests data for the server from the database.
*
* @param id the id of the server.
* @return a server model.
*/
public static ServerModel requestServer(long id){
// It doesn't exist then we insert data into our database and return that instead.
if (!validate(id))
return addServer(new ServerModel(id, "a.", true, 0L));

Document doc = db.find(eq("id", id)).first();
ServerModel model = new ServerModel(doc.getLong("id"), doc.getString("prefix"), doc.getBoolean("limit"), doc.getLong("role"));

// Adds it to the map if it doesn't or replaces it if it does, this is added because there could be moments where
// the data needs to be refreshed.
if(servers.containsKey(model.getId())){
servers.replace(model.getId(), model);
} else {
servers.put(model.getId(), model);
}

return model;
public static ServerModel requestServer(long id) {
// It doesn't exist then we insert data into our database and return that instead.
if (!validate(id))
return addServer(new ServerModel(id, "a.", true, 0L));

Document doc = db.find(eq("id", id)).first();
ServerModel model = new ServerModel(doc.getLong("id"), doc.getString("prefix"), doc.getBoolean("limit"), doc.getLong("role"));

// Adds it to the map if it doesn't or replaces it if it does, this is added because there could be moments where
// the data needs to be refreshed.
if (servers.containsKey(model.getId())) {
servers.replace(model.getId(), model);
} else {
servers.put(model.getId(), model);
}

return model;
}

/**
* Retrieves the server model from the Map if it exists, otherwise requests it from the database.
*
* @param id the id of the server.
* @return a server model.
*/
public static ServerModel getServer(long id){
if(!servers.containsKey(id))
public static ServerModel getServer(long id) {
if (!servers.containsKey(id))
return requestServer(id);

return servers.get(id);
Expand Down
12 changes: 7 additions & 5 deletions src/main/java/pw/mihou/amelia/commands/base/info/Commands.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,23 @@ public class Commands {

/**
* Adds meta data to a command.
* @param command the command name.
*
* @param command the command name.
* @param description the command description.
* @param usage the usage of the command.
* @param cooldown the cooldown of the command.
* @param usage the usage of the command.
* @param cooldown the cooldown of the command.
*/
public static void addCommand(String command, String description, String usage, long cooldown){
public static void addCommand(String command, String description, String usage, long cooldown) {
meta.putIfAbsent(command, new CommandMeta(command, description, usage, cooldown));
}

/**
* Returns back the metadata of a command.
*
* @param command the command name.
* @return the metadata of the command.
*/
public static CommandMeta getCommand(String command){
public static CommandMeta getCommand(String command) {
return meta.get(command);
}

Expand Down
Loading

0 comments on commit ad196c6

Please sign in to comment.