Merge pull request #25 from Salmonllama/database-rewrite

2.0 Mega-Merge
This commit is contained in:
Aleksei Gryczewski 2020-07-27 23:23:33 -04:00 committed by GitHub
commit 3244ef8133
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
68 changed files with 3458 additions and 1520 deletions

View File

@ -8,10 +8,10 @@ plugins {
id 'application'
}
group 'studio.spiderling'
group 'dev.salmonllama'
version '1.1.11'
sourceCompatibility = 1.8
sourceCompatibility = 11
repositories {
mavenCentral()
@ -21,13 +21,13 @@ repositories {
}
dependencies {
implementation 'org.xerial:sqlite-jdbc:3.30.1'
implementation 'com.github.Kaaz:ConfigurationBuilder:0.4'
implementation group: 'org.javacord', name: 'javacord', version:'3.0.5'
implementation group: 'com.rethinkdb', name: 'rethinkdb-driver', version: '2.3.3'
implementation 'org.javacord:javacord:3.0.6'
implementation 'com.vdurmont:emoji-java:4.0.0'
implementation 'com.squareup.okhttp3:okhttp:4.4.0'
testCompile group: 'junit', name: 'junit', version: '4.12'
testImplementation group: 'junit', name: 'junit', version: '4.12'
}
mainClassName = 'dev.salmonllama.fsbot.Main'

0
gradlew vendored Normal file → Executable file
View File

47
privacy.md Normal file
View File

@ -0,0 +1,47 @@
# Privacy Policy
To comply with Discord's new Terms of Service, this document exists
to provide an overview of Fashionscape Bot and the information it
collects, and why.
This document is also accessible to users within Discord through the
`~privacy` command.
## The data we store
### Guild Data:
Guild Snowflakes (IDs) - Generated by Discord
### User Data:
User Snowflakes (IDs) - Generated by Discord
### User Content:
Permanent links to user-provided images supplied in gallery channels.
## Why we store it and how we use it
### Guild Data:
Guild IDs are stored so that the bot may use custom prefixes and settings
as a guild's administrators see fit. It is also used to prevent the bot
from joining a guild, should the guild prove to be abusing or misusing
either the bot, or Discord/Jagex Terms of Service.
### User Data:
User IDs are stored mainly for image attribution, to give proper credit
to users who submit their screenshots to the gallery.
User IDs may also be used to prevent a user from utilizing some or all
features of the bot, if the user is proven to be abusing or misusing it.
User Content:
Images posted to gallery channels are not stored directly. The bot
first uploads them to a private imgur gallery, and retrieves the link.
This link is then stored in the bot's database as part of the gallery.
## Accessing and deleting your data
You may request deletion of your submitted content through myself
or another administrator. To do this, either join our discord and
ping one of us, or create an issue on GitHub.
## Questions, Comments, Concerns
Any further questions or concerns may be submitted through the
[Fashionscape Server](https://discord.com/invite/Tfvxe22)'s #report-issues-here channel, or directed to
Salmonllama#5727 or LisaaRS#2604.

View File

@ -5,16 +5,13 @@
package dev.salmonllama.fsbot;
import com.rethinkdb.RethinkDB;
import com.rethinkdb.net.Connection;
import dev.salmonllama.fsbot.config.BotConfig;
import dev.salmonllama.fsbot.database.FSDB;
import dev.salmonllama.fsbot.guthix.Guthix;
import dev.salmonllama.fsbot.listeners.*;
import org.javacord.api.DiscordApiBuilder;
import dev.salmonllama.fsbot.utilities.Constants;
import dev.salmonllama.fsbot.utilities.database.DatabaseUtilities;
// TODO: auto-switching status messages.
// TODO: Add an official Logger --> logging to Discord, not console
@ -23,22 +20,19 @@ public class Main {
public static void main(String[] args) {
String configLocation = Constants.BOT_FOLDER.concat(Constants.CONFIG_NAME);
BotConfig.initConfig(configLocation);
BotConfig.initConfig(configLocation, false); // TODO: Use args to dictate newFiling. Also use args to dictate database setup.
// Initialise the database with values from the bot's config file
RethinkDB r = RethinkDB.r;
Connection conn = r.connection().hostname(BotConfig.DB_HOST).port(BotConfig.DB_PORT).connect();
FSDB.init();
new DiscordApiBuilder().setToken(BotConfig.TOKEN).login().thenAccept(api -> {
DatabaseUtilities db = new DatabaseUtilities(r, conn, api);
db.tableSetup();
Guthix guthix = new Guthix(api, db);
@SuppressWarnings("unused")
Guthix guthix = new Guthix(api);
// Register listeners
api.addMessageCreateListener(new ImageListener(r, conn));
api.addServerMemberJoinListener(new NewMemberListener(api));
api.addServerJoinListener(new ServerJoined(api, db));
api.addMessageCreateListener(new ImageListener());
api.addServerMemberJoinListener(new NewMemberListener());
api.addServerJoinListener(new ServerJoined(api));
api.addMessageCreateListener(new ThumbsListener());
api.addMessageCreateListener(new AchievementListener());
api.addMessageCreateListener(new ReportListener());

View File

@ -0,0 +1,61 @@
package dev.salmonllama.fsbot.commands.developer;
import dev.salmonllama.fsbot.database.controllers.UserBlacklistController;
import dev.salmonllama.fsbot.database.models.UserBlacklist;
import dev.salmonllama.fsbot.guthix.Command;
import dev.salmonllama.fsbot.guthix.CommandContext;
import dev.salmonllama.fsbot.guthix.CommandPermission;
import dev.salmonllama.fsbot.guthix.PermissionType;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import java.util.Arrays;
import java.util.Collection;
public class BlacklistUserCommand extends Command {
@Override public String name() { return "Blacklist User"; }
@Override public String description() { return "Adds the user to the bot's blacklist, preventing them from using any commands or features"; }
@Override public String usage() { return "blacklistuser <userId> <reason>"; }
@Override public String category() { return "Developer"; }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.STATIC, "owner"); }
@Override public Collection<String> aliases() { return Arrays.asList("blacklistuser", "bluser", "sabusr"); }
@Override
public void onCommand(CommandContext ctx) {
String[] args = ctx.getArgs();
if (args.length < 1) {
// Did it wrong
return;
}
// If the user is on the blacklist, remove them, otherwise, add them with the reason.
UserBlacklistController.get(args[0]).thenAcceptAsync(possibleBlacklist -> {
possibleBlacklist.ifPresentOrElse(blacklist -> {
// Remove user from the blacklist
UserBlacklistController.delete(blacklist).thenAcceptAsync((Void) -> {
EmbedBuilder response = new EmbedBuilder()
.setTitle("Removed User from Blacklist")
.addField("User ID:", blacklist.getId())
.addField("Reason for Add", blacklist.getReason())
.addField("Added:", blacklist.getAdded().toString());
ctx.reply(response);
});
}, () -> {
// Add user to the blacklist, check args
UserBlacklist.UserBlacklistBuilder blBuilder = new UserBlacklist.UserBlacklistBuilder(args[0]);
EmbedBuilder response = new EmbedBuilder().setTitle("Added User to Blacklist").addField("User ID:", args[0]);
if (args.length > 1) {
String reason = String.join(" ", Arrays.copyOfRange(args, 1, args.length));
blBuilder.setReason(reason);
response.addField("With reason:", reason);
}
UserBlacklistController.insert(blBuilder.build()).thenAcceptAsync((Void) -> {
ctx.reply(response);
});
});
});
}
}

View File

@ -5,92 +5,82 @@
package dev.salmonllama.fsbot.commands.developer;
import com.rethinkdb.RethinkDB;
import com.rethinkdb.net.Connection;
import com.vdurmont.emoji.EmojiManager;
import com.vdurmont.emoji.EmojiParser;
import dev.salmonllama.fsbot.config.BotConfig;
import dev.salmonllama.fsbot.database.controllers.GalleryController;
import dev.salmonllama.fsbot.database.models.GalleryChannel;
import dev.salmonllama.fsbot.guthix.Command;
import dev.salmonllama.fsbot.guthix.CommandContext;
import dev.salmonllama.fsbot.guthix.CommandPermission;
import dev.salmonllama.fsbot.guthix.PermissionType;
import org.javacord.api.entity.channel.TextChannel;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import org.javacord.api.entity.server.Server;
import org.javacord.api.util.logging.ExceptionLogger;
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Optional;
public class CreateGalleryCommand extends Command { // TODO: This command needs help.
@Override public String name() { return "Create Gallery"; }
@Override public String description() { return "Creates a channel gallery, tracking any posted images"; }
@Override public String usage() { return "creategallery <String tag>"; }
@Override public String category() { return "Developer"; }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.OWNER); }
@Override public Collection<String> aliases() { return new ArrayList<>(Arrays.asList("creategallery", "addgallery", "addgall")); }
static final RethinkDB R = RethinkDB.r;
static final Connection CONN = R.connection().hostname("localhost").port(28015).connect();
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.STATIC, "owner"); }
@Override public Collection<String> aliases() { return new ArrayList<>(Arrays.asList("creategallery", "addgallery", "newgallery")); }
@Override
public void onCommand(CommandContext ctx) {
Optional<Server> opServer = ctx.getServer();
TextChannel channel = ctx.getChannel();
if (ctx.isPrivateMessage()) {
ctx.reply("This command can only be used in a server!"); // TODO: Stop this. Turn this into a preset no-no embed.
return;
}
if (ctx.getArgs().length < 1) {
ctx.reply("Args are incorrect");
return;
}
String[] args = ctx.getArgs();
String targetChannelId = channel.getIdAsString();
String targetChannelName = channel.asServerChannel().get().getName(); // TODO: un-band-aid this.
// Check if the channel is already a registered gallery channel.
// Create a gallery channel of the current channel.
// Store the gallery channel in the database.
if (!opServer.isPresent()) {
ctx.reply("This command can only be used in a server");
return;
}
Server server = opServer.get();
String targetServerName = server.getName();
String targetServerId = server.getIdAsString();
if (!server.getIdAsString().equals(BotConfig.HOME_SERVER)) {
channel.sendMessage("Fashion galleries can only be created in the Fashionscape server");
return;
}
if (args.length < 1) {
channel.sendMessage("No Arguments provided");
return;
}
else if (args.length > 1) {
channel.sendMessage("Too many arguments provided.");
String channelId = ctx.getChannel().getIdAsString();
if (GalleryController.galleryExists(channelId).join()) { // This is a value that is needed immediately.
ctx.reply("A gallery already exists in this channel, can not create a new one.");
return;
}
String tag = args[0];
if (R.db("fsbot").table("channels").getField("cId").contains(targetChannelId).run(CONN)) {
EmbedBuilder embed = new EmbedBuilder()
.setColor(Color.RED)
.addField("ERROR", "This channel is already gathering images");
channel.sendMessage(embed);
String emoji;
if (args.length == 2) {
emoji = EmojiParser.parseToAliases(args[1]);
} else {
emoji = BotConfig.DEFAULT_REACTION;
}
else {
R.db("fsbot").table("channels").insert(
R.hashMap("sName", targetServerName)
.with("sId", targetServerId)
.with("cName", targetChannelName)
.with("cId", targetChannelId)
.with("tag", tag)
).run(CONN);
R.db("fsbot").tableCreate(tag).run(CONN);
GalleryChannel.GalleryBuilder galleryBuilder = new GalleryChannel.GalleryBuilder();
galleryBuilder.setChannelId(channelId);
galleryBuilder.setTag(tag);
galleryBuilder.setEmoji(emoji);
ctx.getServer().ifPresent(server -> {
galleryBuilder.setServerId(server.getIdAsString());
});
GalleryChannel gallery = galleryBuilder.build();
GalleryController.insert(gallery).thenAcceptAsync((Void) -> {
EmbedBuilder embed = new EmbedBuilder()
.setColor(Color.GREEN)
.addField("Success", "Channel added to storage with the following values:")
.addField("Channel Name:", targetChannelName)
.addField("Channel Id:", targetChannelId)
.addField("Success", "Gallery has been created:")
.addField("Channel Id:", gallery.getChannelId())
.addField("Tag:", tag)
.addField("End:", String.format("Table \"%s\" created for images in this channel", tag));
channel.sendMessage(embed);
}
.addField("Emoji:", EmojiParser.parseToUnicode(gallery.getEmoji()))
.addField("End:", String.format("This channel is now being tracked under: %s", tag));
ctx.reply(embed);
});
}
}

View File

@ -0,0 +1,29 @@
package dev.salmonllama.fsbot.commands.developer;
import dev.salmonllama.fsbot.guthix.Command;
import dev.salmonllama.fsbot.guthix.CommandContext;
import dev.salmonllama.fsbot.guthix.CommandPermission;
import dev.salmonllama.fsbot.guthix.PermissionType;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import java.util.Collection;
import java.util.Collections;
public class DefaultCommand extends Command {
@Override public String name() { return "Default"; }
@Override public String description() { return "The command that gets invoked when the prefix is used, but the command is not recognized"; }
@Override public String usage() { return "you don't use this command"; }
@Override public String category() { return "Invisible"; }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.OWNER); }
@Override public Collection<String> aliases() { return Collections.singletonList("default"); }
@Override
public void onCommand(CommandContext ctx) {
EmbedBuilder response = new EmbedBuilder()
.setTitle("Oops!")
.setAuthor(ctx.getApi().getYourself())
.setDescription("That's my prefix, but I don't know that command! Try using `~help` to see what I can do!");
ctx.reply(response);
}
}

View File

@ -1,51 +0,0 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.commands.developer;
import dev.salmonllama.fsbot.guthix.Command;
import dev.salmonllama.fsbot.guthix.CommandContext;
import dev.salmonllama.fsbot.guthix.CommandPermission;
import dev.salmonllama.fsbot.guthix.PermissionType;
import dev.salmonllama.fsbot.utilities.database.DatabaseUtilities;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class EvalCommand extends Command {
@Override public String name() { return "Eval"; }
@Override public String description() { return "Evaluates the given parameters"; }
@Override public String usage() { return "eval <statement>"; }
@Override public String category() { return "Developer"; }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.OWNER); }
@Override public Collection<String> aliases() { return new ArrayList<>(Arrays.asList("eval", "ev")); }
private final DatabaseUtilities db;
public EvalCommand(DatabaseUtilities db) {
this.db = db;
}
@Override
public void onCommand(CommandContext ctx) {
String statement = String.join(" ", ctx.getArgs());
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("JavaScript");
engine.put("db", db);
try {
Object result = engine.eval(statement);
ctx.reply(result.toString());
}
catch (Exception e) {
ctx.reply(e.getMessage());
}
}
}

View File

@ -21,7 +21,7 @@ public class InviteCommand extends Command {
@Override public String description() { return "Spits out a bot invite"; }
@Override public String usage() { return "invite"; }
@Override public String category() { return "Developer"; }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.OWNER); }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.STATIC, "owner"); }
@Override public Collection<String> aliases() { return new ArrayList<>(Arrays.asList("invite", "inv")); }
@Override

View File

@ -0,0 +1,134 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.commands.developer;
import dev.salmonllama.fsbot.database.controllers.StaticPermissionController;
import dev.salmonllama.fsbot.database.models.StaticPermission;
import dev.salmonllama.fsbot.guthix.Command;
import dev.salmonllama.fsbot.guthix.CommandContext;
import dev.salmonllama.fsbot.guthix.CommandPermission;
import dev.salmonllama.fsbot.guthix.PermissionType;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import org.javacord.api.entity.user.User;
import org.javacord.api.util.logging.ExceptionLogger;
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class PermissionCommand extends Command {
@Override public String name() { return "Permission"; }
@Override public String description() { return "Manages a user's static permissions"; }
@Override public String usage() { return "permission <list|add|remove> <keyword>"; }
@Override public String category() { return "Staff"; }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.OWNER); }
@Override public Collection<String> aliases() { return new ArrayList<>(Arrays.asList("permission", "permissions", "perm", "perms")); }
@Override
public void onCommand(CommandContext ctx) {
String[] args = ctx.getArgs();
switch(args[0]) {
case "list":
// List all the static permissions
list(ctx);
break;
case "add":
// Add a static permission to the mentioned user, if any
add(ctx);
break;
case "remove":
// Remove a static permission from the mentioned user, if any
remove(ctx);
break;
default:
// You don't know how to use this command LUL
}
}
private void list(CommandContext ctx) {
if (!ctx.getMessage().getMentionedUsers().isEmpty()) {
User mentionedUser = ctx.getMessage().getMentionedUsers().get(0);
StaticPermissionController.getByUser(mentionedUser.getIdAsString()).thenAcceptAsync(possiblePerms -> {
possiblePerms.ifPresentOrElse(perms -> {
EmbedBuilder embed = new EmbedBuilder()
.setTitle(String.format("Permissions for %s", mentionedUser.getName()))
.setFooter(String.format("User has %s total static permissions", perms.size()))
.setColor(Color.GREEN);
perms.forEach(perm -> {
embed.addField(perm.getUserId(), perm.getPermission());
});
ctx.reply(embed);
}, () -> {
EmbedBuilder embed = new EmbedBuilder()
.setTitle("No Permissions Found")
.setDescription(String.format("User %s has no static permissions", mentionedUser.getName()));
ctx.reply(embed);
});
});
} else {
StaticPermissionController.getAll().thenAcceptAsync(possiblePerms -> {
possiblePerms.ifPresentOrElse(perms -> {
EmbedBuilder embed = new EmbedBuilder()
.setTitle("All static permissions")
.setFooter(String.format("Found %s total static permissions", perms.size()))
.setColor(Color.green);
perms.forEach(perm -> {
embed.addField(perm.getUserId(), perm.getPermission());
});
ctx.reply(embed);
}, () -> {
EmbedBuilder embed = new EmbedBuilder()
.setTitle("No Permissions Found")
.setDescription("No permissions have been added.");
ctx.reply(embed);
});
});
}
}
private void add(CommandContext ctx) {
if (ctx.getMessage().getMentionedUsers().isEmpty()) {
System.out.println("No mentioned users");
// If no mentioned users, improper usage
return;
}
String userId = ctx.getMessage().getMentionedUsers().get(0).getIdAsString();
StaticPermission perm = new StaticPermission.StaticPermissionBuilder(userId).setPermission(ctx.getArgs()[2]).build();
StaticPermissionController.insert(perm).thenAcceptAsync((Void) -> {
EmbedBuilder response = new EmbedBuilder()
.setTitle("Permissions Added")
.addField("User:", userId)
.addField("Permission:", ctx.getArgs()[2]);
ctx.reply(response);
});
}
private void remove(CommandContext ctx) { // TODO: Remove is not functional
if (ctx.getMessage().getMentionedUsers().isEmpty()) {
// If no mentioned users, improper usage
return;
}
String userId = ctx.getMessage().getMentionedUsers().get(0).getIdAsString();
StaticPermissionController.delete(userId, ctx.getArgs()[2]).thenAcceptAsync((Void) -> {
System.out.println("Permission removed");
}).exceptionally(ExceptionLogger.get());
}
}

View File

@ -5,14 +5,18 @@
package dev.salmonllama.fsbot.commands.developer;
import dev.salmonllama.fsbot.database.controllers.OutfitController;
import dev.salmonllama.fsbot.guthix.Command;
import dev.salmonllama.fsbot.guthix.CommandContext;
import dev.salmonllama.fsbot.guthix.CommandPermission;
import dev.salmonllama.fsbot.guthix.PermissionType;
import org.javacord.api.entity.message.Message;
import org.javacord.api.entity.permission.Role;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;
public class TestCommand extends Command {
@Override public String name() { return "Test"; }
@ -24,6 +28,15 @@ public class TestCommand extends Command {
@Override
public void onCommand(CommandContext ctx) {
ctx.getChannel().sendMessage("Test Command has been invoked");
Message msg = ctx.getMessage();
Collection<Role> roles = msg.getMentionedRoles();
roles.stream().map(Role::getIdAsString).collect(Collectors.toList()).forEach(id -> {
ctx.getServer().ifPresent(server -> {
Role r = server.getRoleById(id).orElse(null);
ctx.reply(r.getMentionTag());
});
});
}
}

View File

@ -5,26 +5,14 @@
package dev.salmonllama.fsbot.commands.general;
import dev.salmonllama.fsbot.config.BotConfig;
import dev.salmonllama.fsbot.guthix.Command;
import dev.salmonllama.fsbot.guthix.CommandContext;
import dev.salmonllama.fsbot.guthix.CommandPermission;
import dev.salmonllama.fsbot.guthix.PermissionType;
import dev.salmonllama.fsbot.utilities.ColorRole;
import dev.salmonllama.fsbot.utilities.database.RoleColourUtility;
import org.javacord.api.DiscordApi;
import org.javacord.api.entity.channel.TextChannel;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import org.javacord.api.entity.permission.Role;
import org.javacord.api.entity.server.Server;
import dev.salmonllama.fsbot.utilities.warnings.Warning;
import org.javacord.api.entity.user.User;
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
public class ColorCommand extends Command {
@Override public String name() { return "Color"; }
@ -36,72 +24,6 @@ public class ColorCommand extends Command {
@Override
public void onCommand(CommandContext ctx) {
if (!ctx.getServer().isPresent()) {
ctx.reply("This command must be used inside a server.");
return;
}
String[] args = ctx.getArgs();
DiscordApi api = ctx.getApi();
Server server = ctx.getServer().get();
User user = ctx.getUser();
TextChannel channel = ctx.getChannel();
// Live server
if (!server.getIdAsString().equals(BotConfig.HOME_SERVER)) { return; }
if (!user.getRoles(server).contains(server.getRoleById(BotConfig.HYDRIX_ROLE).orElseThrow(AssertionError::new))) {
channel.sendMessage(new Warning("You need the Hydrix role to use this").sendWarning());
return;
}
if (args.length != 1) {
channel.sendMessage(new Warning("Not enough arguments provided").sendWarning());
return;
}
// args[0] is the subcommand, either list, or a colour
if (args[0].equals("list")) {
System.out.println(RoleColourUtility.getAllRoleInfo());
return;
}
String roleId = RoleColourUtility.getColour(args[0]);
Role role;
if (!api.getRoleById(roleId).isPresent()) {
channel.sendMessage(new Warning("That role doesn't exist, mate!").sendWarning());
return;
}
else {
role = api.getRoleById(roleId).get();
}
// Actual command logic now
// Remove all the colour roles from the user, except the target one
List<ColorRole> allRoles = RoleColourUtility.getAllRoles();
allRoles.forEach(r -> {
if (r.id.equals(roleId)) {
server.removeRoleFromUser(user, server.getRoleById(r.id).orElseThrow(AssertionError::new));
}
});
// If user already has the role, remove it
if (user.getRoles(server).contains(role)) {
user.removeRole(role);
channel.sendMessage(new EmbedBuilder()
.setColor(Color.GREEN)
.setTitle("Role Removed")
.addInlineField("Role:", role.getName())
);
}
else {
user.addRole(role);
channel.sendMessage(new EmbedBuilder()
.setColor(Color.GREEN)
.setTitle("Role Added")
.addInlineField("Role:", role.getName())
);
}
ctx.reply("This command is a WIP and will be available soon");
}
}

View File

@ -9,10 +9,6 @@ import dev.salmonllama.fsbot.guthix.Command;
import dev.salmonllama.fsbot.guthix.CommandContext;
import dev.salmonllama.fsbot.guthix.CommandPermission;
import dev.salmonllama.fsbot.guthix.PermissionType;
import org.javacord.api.entity.channel.TextChannel;
import org.javacord.api.entity.server.Server;
import dev.salmonllama.fsbot.config.BotConfig;
import dev.salmonllama.fsbot.utilities.ColorRole;
import java.util.ArrayList;
import java.util.Arrays;
@ -28,17 +24,6 @@ public class ColorsCommand extends Command {
@Override
public void onCommand(CommandContext ctx) {
if (!ctx.getServer().isPresent()) {
ctx.reply("This command must be used in a server.");
return;
}
Server server = ctx.getServer().get();
TextChannel channel = ctx.getChannel();
if (!server.getIdAsString().equals(BotConfig.HOME_SERVER)) {
return;
}
channel.sendMessage(ColorRole.rolesListEmbed());
ctx.reply("This command is a WIP and will be available soon.");
}
}

View File

@ -5,85 +5,217 @@
package dev.salmonllama.fsbot.commands.general;
import dev.salmonllama.fsbot.database.controllers.OutfitController;
import dev.salmonllama.fsbot.database.models.Outfit;
import dev.salmonllama.fsbot.guthix.Command;
import dev.salmonllama.fsbot.guthix.CommandContext;
import dev.salmonllama.fsbot.guthix.CommandPermission;
import dev.salmonllama.fsbot.guthix.PermissionType;
import org.javacord.api.entity.channel.TextChannel;
import org.javacord.api.entity.message.Message;
import dev.salmonllama.fsbot.utilities.database.DatabaseUtilities;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import org.javacord.api.util.logging.ExceptionLogger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
public class OutfitCommand extends Command {
private final int MAX_OUTFITS = 5;
private final Collection<String> NON_TAG_ALIASES = new ArrayList<>(Arrays.asList("outfit", "o"));
@Override public String name() { return "Outfit"; }
@Override public String description() { return "Generates a random image with the given tag. Use ~tags to see valid tags."; }
@Override public String usage() { return "outfit <String tag>"; }
@Override public String category() { return "General"; }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.NONE); }
@Override public Collection<String> aliases() { return new ArrayList<>(Arrays.asList("outfit", "o")); }
private final DatabaseUtilities db;
public OutfitCommand(DatabaseUtilities db) {
this.db = db;
}
@Override public Collection<String> aliases() { return initAliases(); }
@Override
public void onCommand(CommandContext ctx) {
// Parse command used for logic path
// if "outfit" -> look for tags. if no tags -> show random
// if not "outfit" -> match the command to existing tags -> look for numbers
String command = ctx.getUsedAlias();
String[] args = ctx.getArgs();
TextChannel channel = ctx.getChannel();
Message message = ctx.getMessage();
// Find the first arg for tag/submitter/naught, find the next arg for number to send
int MAX_OUTFITS = 5;
OutfitController.getDistinctTags().thenAccept(tags -> {
if (tags.contains(command)) {
// args parsing and command logic with tag as caller
handleTagCommand(command, args, ctx);
} else if (NON_TAG_ALIASES.contains(command)) {
// args parsing and command logic with name as caller
handleNameCommand(command, args, ctx);
}
});
}
if (args.length == 0) {
channel.sendMessage(db.randomOutfit().generateEmbed());
}
else if (message.getMentionedUsers().size() > 0) {
// Outfit needs to be from submitter
String userId = message.getMentionedUsers().get(0).getIdAsString();
private void handleTagCommand(String command, String[] args, CommandContext ctx) {
switch (args.length) {
case 0:
// Send one single random outfit of the given tag
OutfitController.findRandomByTag(command).thenAccept(possibleOutfit -> { // TODO: Add an orElse case
possibleOutfit.ifPresent(outfit -> {
ctx.getApi().getUserById(outfit.getSubmitter()).thenAcceptAsync(user -> {
EmbedBuilder response = new EmbedBuilder()
.setTitle(user.getDiscriminatedName())
.setFooter(String.format("%s | %s", outfit.getTag(), outfit.getId()))
.setImage(outfit.getLink())
.setUrl(outfit.getLink());
if (args.length == 2) {
// Send a number of outfits
int iterator = ((Integer.parseInt(args[1]) > MAX_OUTFITS) ? MAX_OUTFITS : Integer.parseInt(args[1]));
if (!outfit.getMeta().equals("")) {
response.setDescription(outfit.getMeta());
}
for (int i = 0; i < iterator; i++) {
channel.sendMessage(db.getOutfitBySubmitter(userId).generateEmbed());
ctx.reply(response);
});
});
}).exceptionally(ExceptionLogger.get());
case 1:
// Send the given number of outfits, not to exceed 5 for ratelimit reasons
if (isNumeric(args[0])) {
int num = Math.min(Integer.parseInt(args[0]), MAX_OUTFITS);
OutfitController.findRandomByTag(command, num).thenAccept(possibleOutfits -> {
possibleOutfits.ifPresent(outfits -> {
outfits.forEach(outfit -> {
ctx.getApi().getUserById(outfit.getSubmitter()).thenAcceptAsync(user -> {
EmbedBuilder response = new EmbedBuilder()
.setTitle(user.getDiscriminatedName())
.setFooter(String.format("%s | %s", outfit.getTag(), outfit.getId()))
.setImage(outfit.getLink())
.setUrl(outfit.getLink());
if (!outfit.getMeta().equals("")) {
response.setDescription(outfit.getMeta());
}
ctx.reply(response);
});
});
});
});
} else {
ctx.reply("Improper command usage"); // TODO: Logging update reminder
}
}
else {
channel.sendMessage(db.getOutfitBySubmitter(userId).generateEmbed());
}
private void handleNameCommand(String command, String[] args, CommandContext ctx) {
switch (args.length) {
case 0:
// Send one truly random outfit
OutfitController.findRandom().thenAccept(possibleOutfit -> {
possibleOutfit.ifPresent(outfit -> {
ctx.getApi().getUserById(outfit.getSubmitter()).thenAcceptAsync(user -> {
EmbedBuilder response = new EmbedBuilder()
.setTitle(user.getDiscriminatedName())
.setFooter(String.format("%s | %s", outfit.getTag(), outfit.getId()))
.setImage(outfit.getLink())
.setUrl(outfit.getLink());
if (!outfit.getMeta().equals("")) {
response.setDescription(outfit.getMeta());
}
else if (db.getTags().contains(args[0].toLowerCase())) {
// Outfit needs to be tagged
ctx.reply(response);
});
});
});
case 1:
// Send the given number of outfits, not to exceed 5 for ratelimit reasons
if (isNumeric(args[0])) {
int num = Math.min(Integer.parseInt(args[0]), MAX_OUTFITS);
OutfitController.findRandom(num).thenAccept(possibleOutfits -> {
possibleOutfits.ifPresent(outfits -> {
outfits.forEach(outfit -> {
ctx.getApi().getUserById(outfit.getSubmitter()).thenAcceptAsync(user -> {
EmbedBuilder response = new EmbedBuilder()
.setTitle(user.getDiscriminatedName())
.setFooter(String.format("%s | %s", outfit.getTag(), outfit.getId()))
.setImage(outfit.getLink())
.setUrl(outfit.getLink());
if (!outfit.getMeta().equals("")) {
response.setDescription(outfit.getMeta());
}
ctx.reply(response);
});
});
});
});
} else {
// First arg is not a number, send an outfit of the given tag
String tag = args[0];
OutfitController.findRandomByTag(tag).thenAccept(possibleOutfit -> {
possibleOutfit.ifPresent(outfit -> {
ctx.getApi().getUserById(outfit.getSubmitter()).thenAcceptAsync(user -> {
EmbedBuilder response = new EmbedBuilder()
.setTitle(user.getDiscriminatedName())
.setFooter(String.format("%s | %s", outfit.getTag(), outfit.getId()))
.setImage(outfit.getLink())
.setUrl(outfit.getLink());
if (args.length == 2) {
// Send a number of outfits
int iterator = ((Integer.parseInt(args[1]) > MAX_OUTFITS) ? MAX_OUTFITS : Integer.parseInt(args[1]));
if (!outfit.getMeta().equals("")) {
response.setDescription(outfit.getMeta());
}
for (int i = 0; i < iterator; i++) {
channel.sendMessage(db.randomTaggedOutfit(tag).generateEmbed());
ctx.reply(response);
});
});
});
}
}
else {
channel.sendMessage(db.randomTaggedOutfit(tag).generateEmbed());
}
}
else if (args.length == 1 && args[0].matches("\\d")) {
int iterator = ((Integer.parseInt(args[0]) > MAX_OUTFITS) ? MAX_OUTFITS : Integer.parseInt(args[0]));
case 2:
// Send the given number of outfits of the given tag
if (isNumeric(args[1])) {
int num = Math.min(Integer.parseInt(args[1]), MAX_OUTFITS);
for (int i = 0; i < iterator; i++) {
channel.sendMessage(db.randomOutfit().generateEmbed());
OutfitController.findRandomByTag(args[0], num).thenAccept(possibleOutfits -> {
possibleOutfits.ifPresent(outfits -> {
outfits.forEach(outfit -> {
ctx.getApi().getUserById(outfit.getSubmitter()).thenAcceptAsync(user -> {
EmbedBuilder response = new EmbedBuilder()
.setTitle(user.getDiscriminatedName())
.setFooter(String.format("%s | %s", outfit.getTag(), outfit.getId()))
.setImage(outfit.getLink())
.setUrl(outfit.getLink());
if (!outfit.getMeta().equals("")) {
response.setDescription(outfit.getMeta());
}
ctx.reply(response);
});
});
});
});
}
}
else {
channel.sendMessage("Something went wrong");
}
private Collection<String> initAliases() {
Collection<String> aliases = OutfitController.getDistinctTags().join();
aliases.addAll(NON_TAG_ALIASES);
return aliases;
}
private boolean isNumeric(String input) { // If this is needed elsewhere, it will be moved to a public utility
if (input == null) {
return false;
}
try {
Double.parseDouble(input);
} catch (NumberFormatException e) {
return false;
}
return true;
}
private CompletableFuture<Message> sendOutfit(Outfit outfit) {
// TODO: Replace sending with this
return null;
}
}

View File

@ -0,0 +1,36 @@
package dev.salmonllama.fsbot.commands.general;
import dev.salmonllama.fsbot.guthix.Command;
import dev.salmonllama.fsbot.guthix.CommandContext;
import dev.salmonllama.fsbot.guthix.CommandPermission;
import dev.salmonllama.fsbot.guthix.PermissionType;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import java.util.Collection;
import java.util.Collections;
public class PrivacyCommand extends Command {
@Override public String name() { return "Privacy"; }
@Override public String description() { return "Directs users to the bot's privacy policy"; }
@Override public String usage() { return "privacy"; }
@Override public String category() { return "General"; }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.NONE); }
@Override public Collection<String> aliases() { return Collections.singletonList("privacy"); }
@Override
public void onCommand(CommandContext ctx) {
String privacyUrl = "https://github.com/Salmonllama/Fashionscape-Bot/blob/database-rewrite/privacy.md"; // TODO: Change this to master
EmbedBuilder response = new EmbedBuilder()
.setTitle("Click Here to open")
.setUrl(privacyUrl);
EmbedBuilder response2 = new EmbedBuilder()
.setTitle("Link")
.setDescription(privacyUrl)
.setAuthor(ctx.getApi().getYourself());
ctx.reply(response).thenAcceptAsync(msg -> ctx.reply(response2));
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.commands.general;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;
import org.javacord.api.entity.channel.ServerTextChannel;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import org.javacord.api.entity.server.Server;
import dev.salmonllama.fsbot.database.controllers.GalleryController;
import dev.salmonllama.fsbot.database.models.GalleryChannel;
import dev.salmonllama.fsbot.guthix.Command;
import dev.salmonllama.fsbot.guthix.CommandContext;
import dev.salmonllama.fsbot.guthix.CommandPermission;
import dev.salmonllama.fsbot.guthix.PermissionType;
import org.javacord.api.util.logging.ExceptionLogger;
public class ShowGalleriesCommand extends Command {
@Override public String name() { return "Show Galleries"; }
@Override public String description() { return "Shows registered gallery channels in the current server"; }
@Override public String usage() { return "showgalleries"; }
@Override public String category() { return "General"; }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.NONE); }
@Override public Collection<String> aliases() { return new ArrayList<>(Arrays.asList("showgalleries", "listgalleries")); }
@Override public void onCommand(CommandContext ctx) {
if (ctx.isPrivateMessage()) {
ctx.reply("This command can only be used within a server"); // TODO: Preset embeds again, yeah
return;
}
ctx.getServer().ifPresent(server -> {
GalleryController.getGalleriesByServer(server.getIdAsString())
.exceptionally(ExceptionLogger.get())
.thenAccept(galleries -> {
ctx.reply(galleryEmbed(galleries, server));
});
});
}
EmbedBuilder galleryEmbed(Collection<GalleryChannel> galleries, Server server) { // TODO: Base FSBot embed.
EmbedBuilder embed = new EmbedBuilder()
.setTitle("Server Gallery Channels");
Collection<String> mentionTags = galleries.stream().map(
galleryChannel -> server.getChannelById(
galleryChannel.getChannelId()).get() // Can call get safely -> The channel is not stored if it is not a valid ServerTextChannel
.asServerTextChannel().get()) // Same as above
.map(ServerTextChannel::getMentionTag).collect(Collectors.toList());
Collection<String> tags = galleries.stream().map(GalleryChannel::getTag).collect(Collectors.toList());
embed.addField("Channels", String.join("\n", mentionTags), true);
embed.addField("Tags", String.join("\n", tags), true);
return embed;
}
}

View File

@ -1,57 +0,0 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.commands.general;
import dev.salmonllama.fsbot.guthix.Command;
import dev.salmonllama.fsbot.guthix.CommandContext;
import dev.salmonllama.fsbot.guthix.CommandPermission;
import dev.salmonllama.fsbot.guthix.PermissionType;
import org.javacord.api.entity.channel.ServerTextChannel;
import dev.salmonllama.fsbot.utilities.database.DatabaseUtilities;
import org.javacord.api.entity.channel.TextChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.ArrayBlockingQueue;
public class SpecificOutfitCommand extends Command {
@Override public String name() { return "Specific Outfit"; }
@Override public String description() { return "Generates an outfit of a specific tag"; }
@Override public String usage() { return "<String tag> [int number]"; }
@Override public String category() { return "General"; }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.NONE); }
@Override public Collection<String> aliases() { return new ArrayList<>(db.getTags()); }
private final DatabaseUtilities db;
public SpecificOutfitCommand(DatabaseUtilities db) {
this.db = db;
}
@Override
public void onCommand(CommandContext ctx) {
String[] args = ctx.getArgs();
String tag = ctx.getUsedAlias();
TextChannel channel = ctx.getChannel();
if (args.length == 1) {
// Send the specific number of outfits with a max of 5
int outfitNumber = Integer.parseInt(args[0]);
if (outfitNumber > 5) { outfitNumber = 5; }
for (int i = 0; i < outfitNumber; i++) {
channel.sendMessage(db.randomTaggedOutfit(tag).generateEmbed());
}
}
else if (args.length > 1) {
channel.sendMessage("You did that wrong, mate");
}
else {
channel.sendMessage(db.randomTaggedOutfit(tag).generateEmbed());
}
}
}

View File

@ -0,0 +1,46 @@
package dev.salmonllama.fsbot.commands.general;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import dev.salmonllama.fsbot.database.controllers.OutfitController;
import dev.salmonllama.fsbot.guthix.Command;
import dev.salmonllama.fsbot.guthix.CommandContext;
import dev.salmonllama.fsbot.guthix.CommandPermission;
import dev.salmonllama.fsbot.guthix.PermissionType;
public class StatsCommand extends Command {
@Override public String name() { return "Stats"; }
@Override public String description() { return "Shows various stats from Fashionscape Bot"; }
@Override public String usage() { return "stats"; }
@Override public String category() { return "General"; }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.NONE); }
@Override public Collection<String> aliases() { return new ArrayList<>(Arrays.asList("stats")); }
@Override
public void onCommand(CommandContext ctx) {
// Stats to display:
// Number of users
// Number of servers
// Number of galleries
// Number of non-deleted images
// Number of total images
// CPU and RAM?
int userCount = ctx.getApi().getCachedUsers().size(); // Will these be accurate with sharding?
int serverCount = ctx.getApi().getServers().size();
EmbedBuilder embed = new EmbedBuilder(); // TODO: Standard embeds yeah?
embed.setTitle("Stats");
embed.addField("Users:", String.valueOf(userCount));
embed.addField("Servers:", String.valueOf(serverCount));
OutfitController.countActiveOutfits().thenAccept(count -> embed.addField("Active outfits:", String.valueOf(count))).join();
OutfitController.countTotalOutfits().thenAccept(count -> embed.addField("Total outifts:", String.valueOf(count))).join();
OutfitController.countFeaturedOutfits().thenAccept(count -> embed.addField("Featured outfits:", String.valueOf(count))).join();
ctx.reply(embed);
}
}

View File

@ -10,14 +10,7 @@ import dev.salmonllama.fsbot.guthix.Command;
import dev.salmonllama.fsbot.guthix.CommandContext;
import dev.salmonllama.fsbot.guthix.CommandPermission;
import dev.salmonllama.fsbot.guthix.PermissionType;
import dev.salmonllama.fsbot.utilities.database.RoleColourUtility;
import dev.salmonllama.fsbot.utilities.warnings.Warning;
import org.javacord.api.DiscordApi;
import org.javacord.api.entity.channel.TextChannel;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import org.javacord.api.entity.permission.Role;
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -27,36 +20,11 @@ public class AddColorCommand extends Command {
@Override public String description() { return "adds the provided role to the toggleable cosmetic roles."; }
@Override public String usage() { return "addcolor <colorName> <roleId>"; }
@Override public String category() { return "Staff"; }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.ROLE, BotConfig.STAFF_ROLE); }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.STATIC, "staff"); }
@Override public Collection<String> aliases() { return new ArrayList<>(Arrays.asList("addcolor", "addcolour", "addclr")); }
@Override
public void onCommand(CommandContext ctx) {
String[] args = (String[]) ctx.getArgs();
TextChannel channel = ctx.getChannel();
DiscordApi api = ctx.getApi();
if (args.length != 2) {
channel.sendMessage(new Warning("Insufficient arguments").sendWarning());
return;
}
String colour = args[0];
String id = args[1];
Role role = api.getRoleById(args[1]).orElse(null);
if (role == null) {
channel.sendMessage(new Warning("Supplied roleId isn't a roleId").sendWarning());
return;
}
RoleColourUtility.addColourRole(colour, id);
channel.sendMessage(new EmbedBuilder()
.setTitle(role.getName())
.setColor(role.getColor().orElse(Color.GREEN))
.addField("Role id:", role.getIdAsString())
.addField("Colour to be stored as:", args[0])
);
ctx.reply("This command is a WIP and will be available soon.");
}
}

View File

@ -20,7 +20,7 @@ public class EchoCommand extends Command {
@Override public String description() { return "Echos your message. Typical bash"; }
@Override public String usage() { return "echo <message>"; }
@Override public String category() { return "Staff"; }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.ROLE, BotConfig.STAFF_ROLE); }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.STATIC, "staff"); }
@Override public Collection<String> aliases() { return new ArrayList<>(Collections.singletonList("echo")); }
@Override

View File

@ -5,112 +5,36 @@
package dev.salmonllama.fsbot.commands.staff;
import com.rethinkdb.RethinkDB;
import com.rethinkdb.net.Connection;
import com.rethinkdb.net.Cursor;
import dev.salmonllama.fsbot.config.BotConfig;
import dev.salmonllama.fsbot.database.controllers.OutfitController;
import dev.salmonllama.fsbot.guthix.Command;
import dev.salmonllama.fsbot.guthix.CommandContext;
import dev.salmonllama.fsbot.guthix.CommandPermission;
import dev.salmonllama.fsbot.guthix.PermissionType;
import org.javacord.api.entity.channel.TextChannel;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
public class GetOutfitCommand extends Command { // TODO: This command also needs HELP
@Override public String name() { return "Get Outift"; }
@Override public String description() { return "Shows the outfit, given an ID"; }
@Override public String usage() { return "getoutfit <String id>"; }
@Override public String category() { return "Staff"; }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.ROLE, BotConfig.STAFF_ROLE); }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.STATIC, "staff"); }
@Override public Collection<String> aliases() { return new ArrayList<>(Arrays.asList("getoutfit", "get")); }
static final RethinkDB r = RethinkDB.r;
static final Connection conn = r.connection().hostname("localhost").port(28015).connect();
@Override
public void onCommand(CommandContext ctx) {
// args is ONLY the ID
String[] args = ctx.getArgs();
TextChannel channel = ctx.getChannel();
if (args.length != 1) {
channel.sendMessage("you did that wrong mate");
return;
}
String id = args[0];
// Check if the given id exists in the table
try {
r.db("fsbot").table("outfits").filter(row -> row.getField("id").eq(id)).run(conn);
}
catch (Exception e) {
EmbedBuilder embed = new EmbedBuilder()
.setColor(Color.RED)
.addField("ERROR:", "That id does not exist in that table")
.addField("Output:", e.toString())
.setFooter("Contact Salmonllama#5727 if you see this");
channel.sendMessage(embed);
return;
}
// Get the entry for the given table/tag and id, build the embed, and send it.
try {
String embedLink = null;
String embedSubmitter = null;
Cursor linkCursor = r.db("fsbot")
.table("outfits")
.filter(row -> row.getField("id").eq(id))
.getField("link")
.run(conn);
Cursor submitterCursor = r.db("fsbot")
.table("outfits")
.filter(row -> row.getField("id").eq(id))
.getField("submitter")
.run(conn);
List links = linkCursor.toList();
for (Object link : links) {
embedLink = link.toString();
}
List submitters = submitterCursor.toList();
for (Object submitter : submitters) {
embedSubmitter = submitter.toString();
}
// Send a diff message if any of the things are null
if (embedLink == null) {
EmbedBuilder embed = new EmbedBuilder()
.setColor(Color.RED)
.addField("ERROR: ", "The entry you tried to access doesn't exist.");
channel.sendMessage(embed);
return;
}
// Send the final Embed with the outfit image as a link, the submitter, and the id of the image.
EmbedBuilder embed = new EmbedBuilder()
.setColor(Color.GREEN)
.setAuthor(embedSubmitter)
.setImage(embedLink)
.setFooter(id);
channel.sendMessage(embed);
}
catch (Exception e) {
EmbedBuilder embed = new EmbedBuilder()
.setColor(Color.RED)
.addField("ERROR:", "Something went wrong...")
.addField("Output:", e.toString())
.setFooter("Contact Crablet#9999 and show him this error.");
channel.sendMessage(embed);
}
OutfitController.findById(id).thenAccept(outfitOpt -> {
outfitOpt.ifPresentOrElse(
outfit -> ctx.reply(outfit.toString()),
() -> ctx.reply("Outfit not found, did you get the id right?")
);
});
}
}

View File

@ -24,7 +24,7 @@ public class GetServersCommand extends Command {
@Override public String description() { return "Lists all the servers the bot is in"; }
@Override public String usage() { return "getservers"; }
@Override public String category() { return "Staff"; }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.ROLE, BotConfig.STAFF_ROLE); }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.STATIC, "staff"); }
@Override public Collection<String> aliases() { return new ArrayList<>(Arrays.asList("getservers", "servers")); }
@Override

View File

@ -6,15 +6,12 @@
package dev.salmonllama.fsbot.commands.staff;
import dev.salmonllama.fsbot.config.BotConfig;
import dev.salmonllama.fsbot.database.controllers.OutfitController;
import dev.salmonllama.fsbot.guthix.Command;
import dev.salmonllama.fsbot.guthix.CommandContext;
import dev.salmonllama.fsbot.guthix.CommandPermission;
import dev.salmonllama.fsbot.guthix.PermissionType;
import org.javacord.api.DiscordApi;
import dev.salmonllama.fsbot.utilities.database.DatabaseUtilities;
import dev.salmonllama.fsbot.utilities.exceptions.DiscordError;
import dev.salmonllama.fsbot.utilities.exceptions.OutfitNotFoundException;
import org.javacord.api.entity.channel.TextChannel;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import java.util.ArrayList;
import java.util.Arrays;
@ -25,31 +22,36 @@ public class OutfitInfoCommand extends Command {
@Override public String description() { return "Shows all related info about the outfit"; }
@Override public String usage() { return "outfitinfo <String id>"; }
@Override public String category() { return "Staff"; }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.ROLE, BotConfig.STAFF_ROLE); }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.STATIC, "staff"); }
@Override public Collection<String> aliases() { return new ArrayList<>(Arrays.asList("outfitinfo", "oinfo")); }
private final DatabaseUtilities db;
public OutfitInfoCommand(DatabaseUtilities db) {
this.db = db;
}
@Override
public void onCommand(CommandContext ctx) {
String[] args = ctx.getArgs();
DiscordApi api = ctx.getApi();
TextChannel channel = ctx.getChannel();
if (args.length != 1) {
channel.sendMessage(String.format("you did that wrong. bother %s to make these more specific.", api.getOwner().join().getDiscriminatedName()));
ctx.reply("You must supply an outfit ID.");
return;
}
try {
channel.sendMessage(db.getOutfitFromId(args[0]).generateInfo());
}
catch (OutfitNotFoundException e) {
channel.sendMessage(new DiscordError(e.getMessage()).get());
}
String id = args[0];
OutfitController.findById(id).thenAcceptAsync(possibleOutfit -> {
possibleOutfit.ifPresentOrElse(outfit -> {
EmbedBuilder embed = new EmbedBuilder()
.setTitle("Outfit Info")
.setThumbnail(outfit.getLink())
.setAuthor(ctx.getApi().getYourself())
.setUrl(outfit.getLink())
.setFooter(String.format("Tag: %s", outfit.getTag()))
.addField("Added", outfit.getCreated().toString(), true)
.addField("Updated", outfit.getUpdated().toString(), true)
.addField("Submitted by:", ctx.getApi().getUserById(outfit.getSubmitter()).join().getDiscriminatedName())
.addField("Deleted", outfit.isDeleted() ? "True" : "False", true)
.addField("Featured", outfit.isFeatured() ? "True" : "False", true);
ctx.reply(embed);
}, () -> {
ctx.reply("Outfit not found");
});
});
}
}

View File

@ -7,17 +7,13 @@ package dev.salmonllama.fsbot.commands.staff;
import com.vdurmont.emoji.EmojiParser;
import dev.salmonllama.fsbot.config.BotConfig;
import dev.salmonllama.fsbot.database.controllers.OutfitController;
import dev.salmonllama.fsbot.guthix.Command;
import dev.salmonllama.fsbot.guthix.CommandContext;
import dev.salmonllama.fsbot.guthix.CommandPermission;
import dev.salmonllama.fsbot.guthix.PermissionType;
import org.javacord.api.entity.channel.TextChannel;
import org.javacord.api.entity.user.User;
import dev.salmonllama.fsbot.listeners.ReactionDeleteConfirmationListener;
import dev.salmonllama.fsbot.utilities.Outfit;
import dev.salmonllama.fsbot.utilities.database.DatabaseUtilities;
import dev.salmonllama.fsbot.utilities.exceptions.DiscordError;
import dev.salmonllama.fsbot.utilities.exceptions.OutfitNotFoundException;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import java.util.ArrayList;
import java.util.Arrays;
@ -28,38 +24,82 @@ public class RemoveOutfitCommand extends Command {
@Override public String description() { return "Removes an outfit from the database given an id"; }
@Override public String usage() { return "remove <String id>"; }
@Override public String category() { return "Staff"; }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.ROLE, BotConfig.STAFF_ROLE); }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.STATIC, "staff"); }
@Override public Collection<String> aliases() { return new ArrayList<>(Arrays.asList("removeoutfit", "remove")); }
private final DatabaseUtilities db;
public RemoveOutfitCommand(DatabaseUtilities db) {
this.db = db;
}
@Override
public void onCommand(CommandContext ctx) {
String[] args = ctx.getArgs();
TextChannel channel = ctx.getChannel();
User author = ctx.getUser();
long authorId = ctx.getUser().getId();
if (args.length != 1) {
channel.sendMessage("You did that wrong, mate");
channel.sendMessage("You must supply a valid outfit ID.");
return;
}
// get the outfit, confirm deletion through reaction.
try {
Outfit outfit = db.getOutfitFromId(args[0]);
String outfitId = args[0];
OutfitController.findById(outfitId).thenAcceptAsync(possibleOutfit -> {
possibleOutfit.ifPresentOrElse(outfit -> {
// Send outfit info, react with selectors, add a listener to the message
EmbedBuilder embed = new EmbedBuilder()
.setTitle("Confirm Outfit Deletion")
.setThumbnail(outfit.getLink())
.setAuthor(ctx.getApi().getUserById(outfit.getSubmitter()).join())
.setUrl(outfit.getLink())
.setFooter(String.format("Tag: %s", outfit.getTag()))
.addField("Added", outfit.getCreated().toString(), true)
.addField("Updated", outfit.getUpdated().toString(), true)
.addField("Submitted by:", ctx.getApi().getUserById(outfit.getSubmitter()).join().getDiscriminatedName())
.addField("Deleted", outfit.isDeleted() ? "True" : "False", true)
.addField("Featured", outfit.isFeatured() ? "True" : "False", true);
channel.sendMessage(outfit.generateInfo().setTitle("Confirm Outfit Deletion")).thenAccept(message -> {
message.addReaction(EmojiParser.parseToUnicode(":white_check_mark:"));
message.addReaction(EmojiParser.parseToUnicode(":octagonal_sign:"));
message.addReactionAddListener(new ReactionDeleteConfirmationListener(author, message, outfit, db));
ctx.reply(embed).thenAcceptAsync(msg -> {
msg.addReaction(EmojiParser.parseToUnicode(":white_check_mark:"));
msg.addReaction(EmojiParser.parseToUnicode(":octagonal_sign:"));
msg.addReactionAddListener(event -> {
if (event.getUser().getId() != authorId) {
return;
}
if (event.getEmoji().equalsEmoji(EmojiParser.parseToUnicode(":white_check_mark:"))) {
// Delete the outfit
OutfitController.delete(outfit.getId());
EmbedBuilder response = new EmbedBuilder()
.setTitle("Deletion Successful!")
.setDescription(String.format("Outfit %s marked as deleted", outfit.getId()));
ctx.reply(response);
// TODO: Log the action in FSBot-Log
EmbedBuilder log = new EmbedBuilder()
.setTitle("Outfit Marked as Deleted")
.setThumbnail(outfit.getLink())
.addField("Deleted By:", ctx.getAuthor().getDiscriminatedName());
ctx.getApi().getServerTextChannelById(BotConfig.OUTFIT_LOG).ifPresent(chnl -> {
chnl.sendMessage(log);
});
} else if (event.getEmoji().equalsEmoji(EmojiParser.parseToUnicode(":octagonal_sign:"))) {
// Do nothing
EmbedBuilder response = new EmbedBuilder()
.setTitle("Deletion Aborted")
.setDescription(String.format("No modifications were made to %s", outfit.getId()));
ctx.reply(response);
}
});
});
}, () -> {
EmbedBuilder response = new EmbedBuilder()
.setTitle("Outfit not Found")
.setDescription(String.format("ID %s does not exist", outfitId));
ctx.reply(response);
});
});
}
catch (OutfitNotFoundException e) {
channel.sendMessage(new DiscordError(e.getMessage()).get());
}
}
}

View File

@ -7,18 +7,17 @@ package dev.salmonllama.fsbot.commands.staff;
import com.vdurmont.emoji.EmojiParser;
import dev.salmonllama.fsbot.config.BotConfig;
import dev.salmonllama.fsbot.database.controllers.OutfitController;
import dev.salmonllama.fsbot.database.models.Outfit;
import dev.salmonllama.fsbot.guthix.Command;
import dev.salmonllama.fsbot.guthix.CommandContext;
import dev.salmonllama.fsbot.guthix.CommandPermission;
import dev.salmonllama.fsbot.guthix.PermissionType;
import org.javacord.api.entity.channel.TextChannel;
import org.javacord.api.entity.user.User;
import dev.salmonllama.fsbot.listeners.ReactionRetagConfirmationListener;
import dev.salmonllama.fsbot.utilities.Outfit;
import dev.salmonllama.fsbot.utilities.database.DatabaseUtilities;
import dev.salmonllama.fsbot.utilities.exceptions.DiscordError;
import dev.salmonllama.fsbot.utilities.exceptions.OutfitNotFoundException;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import java.awt.*;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -28,37 +27,90 @@ public class RetagCommand extends Command {
@Override public String description() { return "Changes the tag of the given outfit"; }
@Override public String usage() { return "retag <String id> <String newtag>"; }
@Override public String category() { return "Staff"; }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.ROLE, BotConfig.STAFF_ROLE); }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.STATIC, "staff"); }
@Override public Collection<String> aliases() { return new ArrayList<>(Collections.singletonList("retag")); }
private final DatabaseUtilities db;
public RetagCommand(DatabaseUtilities db) {
this.db = db;
}
@Override
public void onCommand(CommandContext ctx) {
String[] args = ctx.getArgs();
TextChannel channel = ctx.getChannel();
User author = ctx.getUser();
long authorId = ctx.getUser().getId();
if (args.length != 2) {
channel.sendMessage("You did that wrong mate. check the help command.");
channel.sendMessage("Improper usage");
return;
}
try {
Outfit outfit = this.db.getOutfitFromId(args[0]);
// Get the outfit, confirm update through reaction
String outfitId = args[0];
String newTag = args[1];
channel.sendMessage(outfit.generateInfo().setTitle(String.format("Update tag to %s?", args[1]))).thenAcceptAsync(message -> {
message.addReaction(EmojiParser.parseToUnicode(":white_check_mark:"));
message.addReaction(EmojiParser.parseToUnicode(":octagonal_sign:"));
message.addReactionAddListener(new ReactionRetagConfirmationListener(author, message, outfit, db, outfit.getTag(), args[1]));
OutfitController.findById(outfitId).thenAcceptAsync(possibleOutfit -> {
possibleOutfit.ifPresentOrElse(outfit -> {
// Send info, confirmation, and add reaction listener
EmbedBuilder response = new EmbedBuilder()
.setTitle("Confirm Tag Edit")
.setThumbnail(outfit.getLink())
.setAuthor(ctx.getApi().getUserById(outfit.getSubmitter()).join())
.setUrl(outfit.getLink())
.addField("Current Tag:", outfit.getTag())
.addField("New Tag:", newTag);
ctx.reply(response).thenAcceptAsync(msg -> {
msg.addReaction(EmojiParser.parseToUnicode(":white_check_mark:"));
msg.addReaction(EmojiParser.parseToUnicode(":octagonal_sign:"));
msg.addReactionAddListener(event -> {
if (event.getUser().getId() != authorId) {
return;
}
if (event.getEmoji().equalsEmoji(EmojiParser.parseToUnicode(":white_check_mark:"))) {
// Update the outfit
Outfit newOutfit = new Outfit.OutfitBuilder(outfit)
.setTag(newTag)
.setUpdated(new Timestamp(System.currentTimeMillis()))
.build();
OutfitController.update(newOutfit).thenAcceptAsync((Void) -> {
EmbedBuilder embed = new EmbedBuilder()
.setTitle("Outfit retagged successfully!")
.setDescription(String.format("Outfit %s will now display as %s", newOutfit.getId(), newOutfit.getTag()));
msg.delete();
ctx.reply(embed);
// TODO: Log the action in FSBot-log
EmbedBuilder log = new EmbedBuilder()
.setTitle("Outfit Retagged")
.setThumbnail(outfit.getLink())
.addField("New tag:", newTag);
ctx.getApi().getServerTextChannelById(BotConfig.OUTFIT_LOG).ifPresent(chnl -> {
chnl.sendMessage(log);
});
});
} else if (event.getEmoji().equalsEmoji(EmojiParser.parseToUnicode(":octagonal_sign:"))) {
// Do nothing
msg.delete();
EmbedBuilder embed = new EmbedBuilder()
.setTitle("Update Cancelled")
.setDescription("No modifications were made");
ctx.reply(embed);
}
});
});
}, () -> {
// Err, outfit not found
EmbedBuilder response = new EmbedBuilder()
.setTitle("Error occurred")
.setDescription("That ID was not found in the database")
.setColor(Color.RED);
ctx.reply(response);
});
});
}
catch (OutfitNotFoundException e) {
channel.sendMessage(new DiscordError(e.getMessage()).get());
}
}
}

View File

@ -23,7 +23,7 @@ public class SetStatusCommand extends Command {
@Override public String description() { return "Updates the bot's current status"; }
@Override public String usage() { return "updatestatus <String status>"; }
@Override public String category() { return "Staff"; }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.ROLE, BotConfig.STAFF_ROLE); }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.STATIC, "staff"); }
@Override public Collection<String> aliases() { return new ArrayList<>(Arrays.asList("setstatus", "status")); }
@Override

View File

@ -6,17 +6,14 @@
package dev.salmonllama.fsbot.commands.staff;
import dev.salmonllama.fsbot.config.BotConfig;
import dev.salmonllama.fsbot.database.controllers.ServerConfigController;
import dev.salmonllama.fsbot.database.models.ServerConfig;
import dev.salmonllama.fsbot.guthix.Command;
import dev.salmonllama.fsbot.guthix.CommandContext;
import dev.salmonllama.fsbot.guthix.CommandPermission;
import dev.salmonllama.fsbot.guthix.PermissionType;
import dev.salmonllama.fsbot.utilities.database.ServerConfUtility;
import dev.salmonllama.fsbot.utilities.warnings.Warning;
import org.javacord.api.entity.channel.TextChannel;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import org.javacord.api.entity.server.Server;
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -26,89 +23,80 @@ public class WelcomeMessageCommand extends Command {
@Override public String description() { return "View or update the server welcome message. Options: get|set|getchannel|setchannel."; }
@Override public String usage() { return "welcomemessage <String opt> [String newMessage]"; }
@Override public String category() { return "Staff"; }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.ROLE, BotConfig.STAFF_ROLE); }
@Override public CommandPermission permission() { return new CommandPermission(PermissionType.STATIC, "staff"); }
@Override public Collection<String> aliases() { return new ArrayList<>(Arrays.asList("welcomemessage", "wmsg")); }
@Override
public void onCommand(CommandContext ctx) {
if (!ctx.getServer().isPresent()) {
ctx.reply("You must use this command in a server");
if (ctx.isPrivateMessage()) {
ctx.reply("This command can only be used within a server");
return;
}
if (ctx.getArgs().length < 1) {
ctx.reply("You need to supply arguments for this command");
return;
}
String[] args = ctx.getArgs();
TextChannel channel = ctx.getChannel();
Server server = ctx.getServer().get();
if (!server.getIdAsString().equals(BotConfig.HOME_SERVER)) {
return;
}
if (args.length == 0) channel.sendMessage(new Warning("Not enough arguments provided").sendWarning());
switch (args[0]) {
case ("get"):
channel.sendMessage(fetchWelcomeMsg(server));
break;
case ("set"):
StringBuilder sb = new StringBuilder();
for (int i = 1; i < args.length; i++) {
sb.append(String.format("%s ", args[i]));
}
channel.sendMessage(updateWelcomeMsg(sb.toString(), server));
break;
case ("getchannel"):
channel.sendMessage(fetchWelcomeChannel(server));
break;
case ("setchannel"):
if (args.length < 2) channel.sendMessage(new Warning("Not enough arguments provided").sendWarning());
channel.sendMessage(updateWelcomeChannel(args[1], server));
break;
case "get":
get(ctx);
case "set": // TODO: check for args here
set(ctx, args[1]);
}
}
private EmbedBuilder fetchWelcomeMsg(Server server) {
private void get(CommandContext ctx) {
ServerConfigController.get(ctx.getServer().get().getIdAsString()).thenAcceptAsync(possibleConf -> {
possibleConf.ifPresentOrElse(conf -> {
EmbedBuilder response = new EmbedBuilder()
.setTitle("Current Welcome Message")
.setDescription(conf.getWelcomeMessage());
ServerConfUtility conf = new ServerConfUtility(server.getIdAsString());
String msg = conf.getWelcomeMsg();
ctx.reply(response);
}, () -> {
EmbedBuilder response = new EmbedBuilder()
.setTitle("Does not exist!")
.setDescription("No welcome message was found! use `~wmsg set` to set one!");
return new EmbedBuilder()
.setColor(Color.GREEN)
.setTitle("Current welcome message:")
.setDescription(msg);
ctx.reply(response);
});
});
}
private EmbedBuilder updateWelcomeMsg(String msg, Server server) {
private void set(CommandContext ctx, String newMsg) {
ServerConfigController.get(ctx.getServer().get().getIdAsString()).thenAcceptAsync(possibleConf -> {
possibleConf.ifPresentOrElse(conf -> {
// Update the config
ServerConfig config = new ServerConfig.ServerConfigBuilder().from(conf)
.setWelcomeMessage(newMsg)
.build();
ServerConfUtility conf = new ServerConfUtility(server.getIdAsString());
conf.setWelcomeMsg(msg);
ServerConfigController.update(config);
return new EmbedBuilder()
.setColor(Color.GREEN)
.setTitle("Welcome message updated")
.addField("New welcome message:", msg);
}
EmbedBuilder response = new EmbedBuilder()
.setTitle("Welcome Message Set")
.addField("New Welcome Message:", config.getWelcomeMessage());
private EmbedBuilder fetchWelcomeChannel(Server server) {
ctx.reply(response);
}, () -> {
// Create a config and set the welcome message
ServerConfig config = new ServerConfig.ServerConfigBuilder()
.setId(ctx.getServer().get().getIdAsString())
.setPrefix(BotConfig.DEFAULT_PREFIX)
.setWelcomeMessage(newMsg)
.build();
ServerConfUtility conf = new ServerConfUtility(server.getIdAsString());
String welcomeChannel = conf.getWelcomeChannel();
ServerConfigController.insert(config);
return new EmbedBuilder()
.setColor(Color.GREEN)
.setTitle("Current welcome channel:")
.setDescription(welcomeChannel);
}
EmbedBuilder response = new EmbedBuilder()
.setTitle("Welcome Message Set!")
.setDescription("server conf has been created")
.addField("New Welcome Message:", newMsg);
private EmbedBuilder updateWelcomeChannel(String id, Server server) {
ServerConfUtility conf = new ServerConfUtility(server.getIdAsString());
conf.setWelcomeChannel(id);
return new EmbedBuilder()
.setColor(Color.GREEN)
.setTitle("Welcome channel updated:")
.addField("New welcome channel:", id);
ctx.reply(response);
});
});
}
}

View File

@ -3,6 +3,7 @@
* All rights reserved.
*/
// Shoutout to Kaaz (again) for a kickass config service: https://github.com/Kaaz/ConfigurationBuilder
package dev.salmonllama.fsbot.config;
import com.kaaz.configuration.ConfigurationBuilder;
@ -15,10 +16,10 @@ public class BotConfig {
public static String TOKEN = "token-goes-here";
@ConfigurationOption
public static String DB_HOST = "localhost";
public static String DB_ADDR = "fsbot.db";
@ConfigurationOption
public static int DB_PORT = 28015;
public static String DB_NAME = "fsbot";
@ConfigurationOption
public static String DEFAULT_PREFIX = "~";
@ -44,6 +45,12 @@ public class BotConfig {
@ConfigurationOption
public static String VOTE_CHANNEL = "vote channel here";
@ConfigurationOption
public static String WELCOME_CHANNEL = "welcome channel here";
@ConfigurationOption
public static String JOIN_LOG = "join log channel";
@ConfigurationOption
public static String REPORT_LOG = "report_log_channel";
@ -63,11 +70,14 @@ public class BotConfig {
public static String IMGUR_BEARER = "imgur bearer here";
@ConfigurationOption
public static String HOME_SERVER = "340511685024546816";
public static String DEFAULT_REACTION = ":heartpulse:";
public static void initConfig(String filePath) {
@ConfigurationOption
public static String HOME_SERVER = "Home server here";
public static void initConfig(String filePath, boolean cleanfile) {
try {
new ConfigurationBuilder(BotConfig.class, new File(filePath)).build(false);
new ConfigurationBuilder(BotConfig.class, new File(filePath)).build(cleanfile);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);

View File

@ -0,0 +1,9 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.database;
public class DatabaseModel {
}

View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
// Heavily inspired by Kaaz's Emily database connection: https://github.com/Kaaz/DiscordBot/tree/master/src/main/java/emily/db
package dev.salmonllama.fsbot.database;
import dev.salmonllama.fsbot.exceptions.UnknownParameterException;
import dev.salmonllama.fsbot.config.BotConfig;
import dev.salmonllama.fsbot.utilities.Constants;
import org.sqlite.javax.SQLiteConnectionPoolDataSource;
import java.sql.*;
public class DatabaseProvider {
private final String DB_ADDR;
private final String DB_NAME;
private Connection c;
public DatabaseProvider(String dbName) {
DB_NAME = dbName;
DB_ADDR = "jdbc:sqlite:".concat(Constants.BOT_FOLDER).concat(BotConfig.DB_ADDR);
}
private Connection createConnection() {
try {
SQLiteConnectionPoolDataSource dataSource = new SQLiteConnectionPoolDataSource();
dataSource.setDatabaseName(DB_NAME);
dataSource.setUrl(DB_ADDR);
return dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
System.out.println("Could not connect to database, double check config values");
System.exit(1);
}
return null;
}
public Connection getConnection() {
if (c == null) {
return createConnection();
}
return c;
}
private void resolveParameters(PreparedStatement query, Object... params) throws SQLException {
int index = 1;
for (Object p : params) {
if (p instanceof String) {
query.setString(index, (String) p);
}
else if (p instanceof Integer) {
query.setInt(index, (int) p);
}
else if (p instanceof Boolean) {
query.setBoolean(index, (boolean) p);
}
else if (p instanceof Timestamp) {
query.setTimestamp(index, (Timestamp) p);
} else {
throw new UnknownParameterException(p, index);
}
index++;
}
}
public int query(String sql) throws SQLException {
try (Statement stmt = getConnection().createStatement()) {
return stmt.executeUpdate(sql);
}
}
public int query(String sql, Object... params) throws SQLException {
try (PreparedStatement stmt = getConnection().prepareStatement(sql)) {
resolveParameters(stmt, params);
return stmt.executeUpdate();
}
}
public int insert(String sql, Object... params) throws SQLException {
try (
PreparedStatement query = getConnection().prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ResultSet rs = query.getGeneratedKeys()
) {
resolveParameters(query, params);
query.executeUpdate();
if (rs.next()) {
return rs.getInt(1);
}
return -1;
}
}
public ResultSet select(String sql, Object... params) throws SQLException {
PreparedStatement query;
query = getConnection().prepareStatement(sql);
resolveParameters(query, params);
return query.executeQuery();
}
public void close(ResultSet rs) throws SQLException {
rs.getStatement().close();
rs.close();
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
// Heavily inspired by Kaaz's Emily database connection: https://github.com/Kaaz/DiscordBot/tree/master/src/main/java/emily/db
package dev.salmonllama.fsbot.database;
import dev.salmonllama.fsbot.config.BotConfig;
import dev.salmonllama.fsbot.database.models.*;
import java.sql.SQLException;
import java.util.HashMap;
public class FSDB {
private static final String DEFAULT_CONNECTION = "fsbot";
private static final HashMap<String, DatabaseProvider> connections = new HashMap<>();
public static DatabaseProvider get(String key) {
if (connections.containsKey(key)) {
return connections.get(key);
}
System.out.println(String.format("Specified connection %s has not been set.", key));
return null;
}
public static DatabaseProvider get() {
return connections.get(DEFAULT_CONNECTION);
}
public static void init() {
connections.clear();
connections.put(DEFAULT_CONNECTION, new DatabaseProvider(BotConfig.DB_NAME));
prepareTables();
}
private static void prepareTables() {
try {
get().query(Outfit.schema());
get().query(GalleryChannel.schema());
get().query(ServerConfig.schema());
get().query(ServerBlacklist.schema());
get().query(UserBlacklist.schema());
get().query(StaticPermission.schema());
} catch (SQLException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,171 @@
package dev.salmonllama.fsbot.database.controllers;
import dev.salmonllama.fsbot.database.FSDB;
import dev.salmonllama.fsbot.database.models.ColorRole;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
public class ColorRoleController {
// Need insert, get by color, exists by color, exists by role, get by server, count, and delete
public CompletableFuture<Void> insert(ColorRole cr) {
return CompletableFuture.runAsync(() -> {
try {
insertExec(cr);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public CompletableFuture<Boolean> exists(String color) {
return CompletableFuture.supplyAsync(() -> {
try {
return existsExec(color);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public CompletableFuture<Boolean> exists(long roleId) {
return CompletableFuture.supplyAsync(() -> {
try {
return existsExec(roleId);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public CompletableFuture<Optional<ColorRole>> get(String color) {
return CompletableFuture.supplyAsync(() -> {
try {
return getExec(color);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public CompletableFuture<Optional<ColorRole>> get(long roleId) {
return CompletableFuture.supplyAsync(() -> {
try {
return getExec(roleId);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public CompletableFuture<Integer> count(long serverId) {
return CompletableFuture.supplyAsync(() -> {
try {
return countExec(serverId);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public CompletableFuture<Void> delete(long roleId) {
return CompletableFuture.runAsync(() -> {
try {
deleteExec(roleId);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public CompletableFuture<Void> delete(String color) {
return CompletableFuture.runAsync(() -> {
try {
deleteExec(color);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
private void insertExec(ColorRole cr) throws SQLException {
FSDB.get().insert("INSERT INTO color_roles (role_id, server_id, color) VALUES (?, ?, ?)",
cr.getRoleId(),
cr.getServerId(),
cr.getColor()
);
}
private boolean existsExec(String color) throws SQLException {
ResultSet rs = FSDB.get().select("SELECT EXISTS(SELECT 1 FROM color_roles WHERE color = ?) AS hmm", color);
boolean exists = rs.getBoolean("hmm");
FSDB.get().close(rs);
return exists;
}
private boolean existsExec(long roleId) throws SQLException {
ResultSet rs = FSDB.get().select("SELECT EXISTS(SELECT 1 FROM color_roles WHERE role_id = ?) AS hmm", roleId);
boolean exists = rs.getBoolean("hmm");
FSDB.get().close(rs);
return exists;
}
private Optional<ColorRole> getExec(String color) throws SQLException {
ResultSet rs = FSDB.get().select("SELECT * FROM color_roles WHERE color = ?", color);
if (rs.next()) {
ColorRole colorRole = mapObject(rs);
FSDB.get().close(rs);
return Optional.of(colorRole);
}
FSDB.get().close(rs);
return Optional.empty();
}
private Optional<ColorRole> getExec(Long roleId) throws SQLException {
ResultSet rs = FSDB.get().select("SELECT * FROM color_roles WHERE role_id = ?", roleId);
if (rs.next()) {
ColorRole colorRole = mapObject(rs);
FSDB.get().close(rs);
return Optional.of(colorRole);
}
FSDB.get().close(rs);
return Optional.empty();
}
private int countExec(long serverId) throws SQLException {
ResultSet rs = FSDB.get().select("SELECT COUNT(*) AS count FROM color_roles WHERE server_id = ?", serverId);
if (rs.next()) {
int count = rs.getInt("count");
FSDB.get().close(rs);
return count;
}
FSDB.get().close(rs);
return 0;
}
private void deleteExec(long roleId) throws SQLException {
FSDB.get().query("DELETE FROM color_roles WHERE role_id = ?", roleId);
}
private void deleteExec(String color) throws SQLException {
FSDB.get().query("DELETE FROM color_roles WHERE color = ?", color);
}
private ColorRole mapObject(ResultSet rs) throws SQLException {
return new ColorRole.ColorRoleBuilder(rs.getLong("role_id"))
.setServerId(rs.getLong("server_id"))
.setColor(rs.getString("color"))
.build();
}
}

View File

@ -0,0 +1,124 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.database.controllers;
import dev.salmonllama.fsbot.database.FSDB;
import dev.salmonllama.fsbot.database.models.GalleryChannel;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
public class GalleryController {
public static CompletableFuture<Void> insert(GalleryChannel gallery) {
return CompletableFuture.runAsync(() -> {
try {
insertExec(gallery);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Collection<GalleryChannel>> getGalleriesByServer(String serverId) {
return CompletableFuture.supplyAsync(() -> {
try {
return getGalleriesByServerExec(serverId);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Boolean> galleryExists(String channelId) {
return CompletableFuture.supplyAsync(() -> {
try {
return galleryExistsExec(channelId);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<String> getTag(String channelId) {
return CompletableFuture.supplyAsync(() -> {
try {
return getTagExec(channelId);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<String> getEmoji(String channelId) {
return CompletableFuture.supplyAsync(() -> {
try {
return getEmojiExec(channelId);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
private static void insertExec(GalleryChannel gallery) throws SQLException {
FSDB.get().insert("INSERT INTO galleries(server_id, channel_id, tag, emoji)" +
"VALUES(?, ?, ?, ?)",
gallery.getServerId(),
gallery.getChannelId(),
gallery.getTag(),
gallery.getEmoji()
);
}
private static Collection<GalleryChannel> getGalleriesByServerExec(String serverId) throws SQLException { // TODO: What if the server has no galleries eh?
ResultSet rs = FSDB.get().select("SELECT * FROM galleries WHERE server_id = ?", serverId);
Collection<GalleryChannel> galleries = new ArrayList<>();
while (rs.next()) {
galleries.add(mapObject(rs));
}
FSDB.get().close(rs);
return galleries;
}
private static boolean galleryExistsExec(String channelId) throws SQLException {
ResultSet rs = FSDB.get().select("SELECT EXISTS(SELECT 1 FROM galleries WHERE channel_id = ?) AS hmm", channelId);
boolean exists = rs.getBoolean("hmm");
FSDB.get().close(rs);
return exists;
}
private static String getTagExec(String channelId) throws SQLException {
ResultSet rs = FSDB.get().select("SELECT * FROM galleries WHERE channel_id = ?", channelId);
String tag = rs.getString("tag");
FSDB.get().close(rs);
return tag;
}
private static String getEmojiExec(String channelId) throws SQLException {
// Does not need to be an optional. CreateGalleryCommand populates it automatically with the default.
ResultSet rs = FSDB.get().select("SELECT * FROM galleries WHERE channel_id = ?", channelId);
String emoji = rs.getString("emoji");
FSDB.get().close(rs);
return emoji;
}
private static GalleryChannel mapObject(ResultSet rs) throws SQLException { // TODO: Builder this
return new GalleryChannel.GalleryBuilder()
.setServerId(rs.getString("server_id"))
.setChannelId(rs.getString("channel_id"))
.setTag(rs.getString("tag"))
.setEmoji(rs.getString("emoji"))
.build();
}
}

View File

@ -0,0 +1,412 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.database.controllers;
import dev.salmonllama.fsbot.database.FSDB;
import dev.salmonllama.fsbot.database.models.Outfit;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
public class OutfitController {
// There is no delete method. 'Deletions' should be executed as an update to the deleted flag.
public static CompletableFuture<Void> insert(Outfit outfit) {
return CompletableFuture.runAsync(() -> {
try {
insertExec(outfit);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Optional<Outfit>> findById(String id) {
return CompletableFuture.supplyAsync(() -> {
try {
return findByIdExec(id);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Optional<Outfit>> findRandom() {
return CompletableFuture.supplyAsync(() -> {
try {
return findRandomExec();
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Optional<Collection<Outfit>>> findRandom(int amount) {
return CompletableFuture.supplyAsync(() -> {
try {
return findRandomExec(amount);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Optional<Outfit>> findRandomByTag(String tag) {
return CompletableFuture.supplyAsync(() -> {
try {
return findRandomByTagExec(tag);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Optional<Collection<Outfit>>> findRandomByTag(String tag, int amount) {
return CompletableFuture.supplyAsync(() -> {
try {
return findRandomByTagExec(tag, amount);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Optional<Outfit>> findRandomBySubmitter(String submitterId) {
return CompletableFuture.supplyAsync(() -> {
try {
return findRandomBySubmitterExec(submitterId);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Optional<Collection<Outfit>>> findRandomBySubmitter(String submitterId, int amount) {
return CompletableFuture.supplyAsync(() -> {
try {
return findRandomBySubmitterExec(submitterId, amount);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Integer> countTotalOutfits() {
return CompletableFuture.supplyAsync(() -> {
try {
return countTotalOutfitsExec();
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Integer> countDeletedOutfits() {
return CompletableFuture.supplyAsync(() -> {
try {
return countDeletedOutfitsExec();
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Integer> countActiveOutfits() {
return CompletableFuture.supplyAsync(() -> {
try {
return countActiveOutfitsExec();
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Integer> countFeaturedOutfits() {
return CompletableFuture.supplyAsync(() -> {
try {
return countFeaturedOutfitsExec();
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Integer> countOutfits(boolean deleted, boolean featured) {
return CompletableFuture.supplyAsync(() -> {
try {
return countOutfitsExec(deleted, featured);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Integer> countOutfitsBySubmitter(String submitterId) {
return CompletableFuture.supplyAsync(() -> {
try {
return countOutfitsBySubmitterExec(submitterId);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Collection<String>> getDistinctTags() {
return CompletableFuture.supplyAsync(() -> {
try {
return getDistinctTagsExec();
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Void> update(Outfit outfit) {
return CompletableFuture.runAsync(() -> {
try {
updateExec(outfit);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Void> delete(String id) {
return CompletableFuture.runAsync(() -> {
try {
deleteExec(id);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Void> delete(Outfit outfit) {
return CompletableFuture.runAsync(() -> {
try {
deleteExec(outfit.getId());
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
private static void insertExec(Outfit outfit) throws SQLException {
if (outfit.getCreated() == null) {
outfit.setCreated(new Timestamp(System.currentTimeMillis()));
}
if (outfit.getUpdated() == null) {
outfit.setUpdated(new Timestamp(System.currentTimeMillis()));
}
FSDB.get().insert(
"INSERT INTO " +
"outfits('id', 'link', 'submitter', 'tag', 'meta', 'created', 'updated', 'deleted', 'featured', 'display_count', 'delete_hash') " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
outfit.getId(),
outfit.getLink(),
outfit.getSubmitter(),
outfit.getTag(),
outfit.getMeta(),
outfit.getCreated(),
outfit.getUpdated(),
outfit.isDeleted(),
outfit.isFeatured(),
outfit.getDisplayCount(),
outfit.getDeleteHash()
);
}
private static Optional<Outfit> findByIdExec(String id) throws SQLException {
ResultSet rs = FSDB.get().select("SELECT * FROM outfits WHERE id = ?", id);
if (rs.next()) {
Outfit outfit = mapObject(rs);
FSDB.get().close(rs);
return Optional.of(outfit);
}
FSDB.get().close(rs);
return Optional.empty();
}
private static Optional<Outfit> findRandomExec() throws SQLException {
ResultSet rs = FSDB.get().select("SELECT * FROM outfits WHERE deleted = 0 ORDER BY random() LIMIT 1");
if (rs.next()) {
Outfit outfit = mapObject(rs);
FSDB.get().close(rs);
return Optional.of(outfit);
}
FSDB.get().close(rs);
return Optional.empty();
}
private static Optional<Collection<Outfit>> findRandomExec(int amount) throws SQLException {
ResultSet rs = FSDB.get().select("SELECT * FROM outfits WHERE deleted = 0 ORDER BY random() LIMIT ?", amount);
return extractMultiple(rs);
}
private static Optional<Outfit> findRandomByTagExec(String tag) throws SQLException {
ResultSet rs = FSDB.get().select("SELECT * FROM outfits WHERE tag = ? AND deleted = 0 ORDER BY random() LIMIT 1", tag);
if (rs.next()) {
Outfit outfit = mapObject(rs);
FSDB.get().close(rs);
return Optional.of(outfit);
}
FSDB.get().close(rs);
return Optional.empty();
}
private static Optional<Collection<Outfit>> findRandomByTagExec(String tag, int amount) throws SQLException { // Still has to be an optional, the tag may not exist. It is user-supplied.
ResultSet rs = FSDB.get().select("SELECT * FROM outfits WHERE tag = ? AND deleted = 0 ORDER BY random() LIMIT ?", tag, amount);
return extractMultiple(rs);
}
private static Optional<Outfit> findRandomBySubmitterExec(String submitterId) throws SQLException {
ResultSet rs = FSDB.get().select("SELECT * FROM outfits WHERE submitter = ? AND deleted = 0 ORDER BY random() LIMIT 1", submitterId);
if (rs.next()) {
Outfit outfit = mapObject(rs);
FSDB.get().close(rs);
return Optional.of(outfit);
}
FSDB.get().close(rs);
return Optional.empty();
}
private static Optional<Collection<Outfit>> findRandomBySubmitterExec(String submitterId, int amount) throws SQLException {
ResultSet rs = FSDB.get().select("SELECT * FROM outfits WHERE submitter = ? AND deleted = 0 ORDER BY random() LIMIT ?", submitterId, amount);
return extractMultiple(rs);
}
private static int countTotalOutfitsExec() throws SQLException {
ResultSet rs = FSDB.get().select("SELECT COUNT(*) AS count FROM outfits");
return extractCount(rs);
}
private static int countDeletedOutfitsExec() throws SQLException {
ResultSet rs = FSDB.get().select("SELECT COUNT(*) AS count FROM outfits WHERE deleted = 1");
return extractCount(rs);
}
private static int countActiveOutfitsExec() throws SQLException {
ResultSet rs = FSDB.get().select("SELECT COUNT(*) AS count FROM outfits WHERE deleted = 0");
return extractCount(rs);
}
private static int countFeaturedOutfitsExec() throws SQLException {
ResultSet rs = FSDB.get().select("SELECT COUNT(*) AS count FROM outfits WHERE featured = 1");
return extractCount(rs);
}
private static int countOutfitsExec(boolean deleted, boolean featured) throws SQLException {
ResultSet rs = FSDB.get().select("SELECT COUNT(*) AS count FROM outfits WHERE deleted = ? AND featured = ?", deleted, featured);
return extractCount(rs);
}
private static int countOutfitsBySubmitterExec(String submitterId) throws SQLException {
ResultSet rs = FSDB.get().select("SELECT COUNT(*) AS count FROM outfits WHERE submitter = ? AND deleted = 0", submitterId);
return extractCount(rs);
}
private static Collection<String> getDistinctTagsExec() throws SQLException {
ResultSet rs = FSDB.get().select("SELECT DISTINCT tag FROM outfits");
Collection<String> tags = new ArrayList<>();
while (rs.next()) {
tags.add(rs.getString("tag"));
}
FSDB.get().close(rs);
return tags;
}
private static void updateExec(Outfit outfit) throws SQLException {
FSDB.get().query("UPDATE outfits SET " +
"link = ?," +
"submitter = ?," +
"tag = ?," +
"meta = ?," +
"updated = ?," +
"featured = ?," +
"display_count = ?" +
"WHERE id = ?",
outfit.getLink(),
outfit.getSubmitter(),
outfit.getTag(),
outfit.getMeta(),
outfit.getUpdated(),
outfit.isFeatured(),
outfit.getDisplayCount(),
outfit.getId());
}
private static void deleteExec(String id) throws SQLException {
FSDB.get().query("UPDATE outfits SET deleted = true WHERE id = ?", id);
}
private static Optional<Collection<Outfit>> extractMultiple(ResultSet rs) throws SQLException {
Collection<Outfit> outfits = new ArrayList<>();
while (rs.next()) {
outfits.add(mapObject(rs));
}
FSDB.get().close(rs);
if (outfits.size() == 0) {
return Optional.empty();
}
return Optional.of(outfits);
}
private static int extractCount(ResultSet rs) throws SQLException {
int count = 0;
if (rs.next()) {
count = rs.getInt("count");
}
FSDB.get().close(rs);
return count;
}
private static Outfit mapObject(ResultSet rs) throws SQLException {
return new Outfit.OutfitBuilder()
.setId(rs.getString("id"))
.setLink(rs.getString("link"))
.setSubmitter(rs.getString("submitter"))
.setTag(rs.getString("tag"))
.setMeta(rs.getString("meta"))
.setCreated(new Timestamp(rs.getLong("created")))
.setUpdated(new Timestamp((rs.getLong("updated"))))
.setDeleted(rs.getBoolean("deleted"))
.setFeatured(rs.getBoolean("featured"))
.setDisplayCount(rs.getInt("display_count"))
.setDeleteHash(rs.getString("delete_hash"))
.build();
}
}

View File

@ -0,0 +1,122 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.database.controllers;
import dev.salmonllama.fsbot.database.FSDB;
import dev.salmonllama.fsbot.database.models.ServerBlacklist;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
public class ServerBlacklistController {
// Need to create, read, and delete. No need for update?
public static CompletableFuture<Void> insert(ServerBlacklist bl) {
return CompletableFuture.runAsync(() -> {
try {
insertExec(bl);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Boolean> exists(String id) {
return CompletableFuture.supplyAsync(() -> {
try {
return existsExec(id);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Boolean> exists(ServerBlacklist bl) {
return CompletableFuture.supplyAsync(() -> {
try {
return existsExec(bl.getId());
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Optional<ServerBlacklist>> get(String id) {
return CompletableFuture.supplyAsync(() -> {
try {
return getExec(id);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Void> delete(String id) {
return CompletableFuture.runAsync(() -> {
try {
deleteExec(id);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Void> delete(ServerBlacklist bl) {
return CompletableFuture.runAsync(() -> {
try {
deleteExec(bl.getId());
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
private static void insertExec(ServerBlacklist bl) throws SQLException {
// TODO: Check for null added timestamp and init
FSDB.get().insert("INSERT INTO blacklist_servers ('id', 'name', 'owner_id', 'added') VALUES (?, ?, ?, ?)",
bl.getId(),
bl.getName(),
bl.getOwnerId(),
bl.getAdded()
);
}
private static boolean existsExec(String id) throws SQLException {
ResultSet rs = FSDB.get().select("SELECT EXISTS(SELECT 1 FROM blacklist_servers WHERE id = ?) AS hmm", id);
boolean exists = rs.getBoolean("hmm");
FSDB.get().close(rs);
return exists;
}
private static Optional<ServerBlacklist> getExec(String id) throws SQLException {
ResultSet rs = FSDB.get().select("SELECT * FROM blacklist_servers WHERE id = ?", id);
if (rs.next()) {
ServerBlacklist bl = mapObject(rs);
FSDB.get().close(rs);
return Optional.of(bl);
}
FSDB.get().close(rs);
return Optional.empty();
}
private static void deleteExec(String id) throws SQLException {
FSDB.get().query("DELETE FROM blacklist_servers WHERE id = ?", id);
}
private static ServerBlacklist mapObject(ResultSet rs) throws SQLException {
return new ServerBlacklist.ServerBlacklistBuilder(rs.getString("id"))
.setName(rs.getString("name"))
.setOwnerId(rs.getString("owner_id"))
.setAdded(new Timestamp(rs.getLong("added")))
.build();
}
}

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.database.controllers;
import dev.salmonllama.fsbot.database.FSDB;
import dev.salmonllama.fsbot.database.models.ServerConfig;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
public class ServerConfigController {
// insert
// retrieve
// update
// No need for delete -> guilds should persist after leaving.
public static CompletableFuture<Void> insert(ServerConfig config) {
return CompletableFuture.runAsync(() -> {
try {
insertExec(config);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Optional<ServerConfig>> get(String serverId) {
return CompletableFuture.supplyAsync(() -> {
try {
return getExec(serverId);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Void> update(ServerConfig config) {
return CompletableFuture.runAsync(() -> {
try {
updateExec(config);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
private static void insertExec(ServerConfig config) throws SQLException {
FSDB.get().insert("INSERT INTO server_config(id, prefix, welcome_message) VALUES (?, ?, ?)",
config.getId(),
config.getPrefix(),
config.getWelcomeMessage()
);
}
private static Optional<ServerConfig> getExec(String serverId) throws SQLException {
ResultSet rs = FSDB.get().select("SELECT * FROM server_config WHERE id = ?", serverId);
if (rs.next()) {
ServerConfig config = mapObject(rs);
FSDB.get().close(rs);
return Optional.of(config);
}
FSDB.get().close(rs);
return Optional.empty();
}
private static void updateExec(ServerConfig config) throws SQLException {
FSDB.get().query("UPDATE server_config SET prefix = ?, welcome_message = ?, welcome_channel = ?, WHERE id = ?",
config.getPrefix(),
config.getWelcomeMessage(),
config.getWelcomeChannel(),
config.getId()
);
}
private static ServerConfig mapObject(ResultSet rs) throws SQLException {
return new ServerConfig.ServerConfigBuilder()
.setId(rs.getString("id"))
.setPrefix(rs.getString("prefix"))
.setWelcomeMessage(rs.getString("welcome_message"))
.setWelcomeChannel(rs.getString("welcome_channel"))
.build();
}
}

View File

@ -0,0 +1,149 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.database.controllers;
import dev.salmonllama.fsbot.database.FSDB;
import dev.salmonllama.fsbot.database.models.StaticPermission;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
public class StaticPermissionController {
// Need to insert, get all per user, get per keyword, and delete
public static CompletableFuture<Void> insert(StaticPermission perm) {
return CompletableFuture.runAsync(() -> {
try {
insertExec(perm);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Optional<Collection<StaticPermission>>> getByUser(String userId) {
return CompletableFuture.supplyAsync(() -> {
try {
return getByUserExec(userId);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Optional<Collection<StaticPermission>>> getByPermission(String perm) {
return CompletableFuture.supplyAsync(() -> {
try {
return getByPermissionExec(perm);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Optional<Collection<StaticPermission>>> getAll() {
return CompletableFuture.supplyAsync(() -> {
try {
return getAllExec();
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Void> delete(StaticPermission perm) {
return delete(perm.getUserId(), perm.getPermission());
}
public static CompletableFuture<Void> delete(String userId, String perm) {
return CompletableFuture.runAsync(() -> {
try {
deleteExec(userId, perm);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
private static void insertExec(StaticPermission perm) throws SQLException {
if (perm.getAdded() == null) {
perm.setAdded(new Timestamp(System.currentTimeMillis()));
}
FSDB.get().insert("INSERT INTO static_permissions (user_id, permission, date_added) VALUES (?, ?, ?)",
perm.getUserId(),
perm.getPermission(),
perm.getAdded()
);
}
private static Optional<Collection<StaticPermission>> getByUserExec(String userId) throws SQLException {
ResultSet rs = FSDB.get().select("SELECT * FROM static_permissions WHERE user_id = ?", userId);
Collection<StaticPermission> permissions = new ArrayList<>();
while (rs.next()) {
permissions.add(mapObject(rs));
}
FSDB.get().close(rs);
if (permissions.isEmpty()) {
return Optional.empty();
}
return Optional.of(permissions);
}
private static Optional<Collection<StaticPermission>> getByPermissionExec(String perm) throws SQLException {
ResultSet rs = FSDB.get().select("SELECT * FROM static_permissions WHERE permission = ?", perm);
Collection<StaticPermission> users = new ArrayList<>();
while (rs.next()) {
users.add(mapObject(rs));
}
FSDB.get().close(rs);
if (users.isEmpty()) {
return Optional.empty();
}
return Optional.of(users);
}
private static Optional<Collection<StaticPermission>> getAllExec() throws SQLException {
ResultSet rs = FSDB.get().select("SELECT * FROM static_permissions");
Collection<StaticPermission> permissions = new ArrayList<>();
while (rs.next()) {
permissions.add(mapObject(rs));
}
FSDB.get().close(rs);
if (permissions.isEmpty()) {
return Optional.empty();
}
return Optional.of(permissions);
}
private static void deleteExec(String userId, String perm) throws SQLException {
FSDB.get().query("DELETE FROM static_permissions WHERE user_id = ? AND permission = ?", userId, perm);
}
private static StaticPermission mapObject(ResultSet rs) throws SQLException {
return new StaticPermission.StaticPermissionBuilder(rs.getString("user_id"))
.setPermission(rs.getString("permission"))
.setAdded(new Timestamp(rs.getLong("date_added")))
.build();
}
}

View File

@ -0,0 +1,127 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.database.controllers;
import dev.salmonllama.fsbot.database.FSDB;
import dev.salmonllama.fsbot.database.models.ServerBlacklist;
import dev.salmonllama.fsbot.database.models.UserBlacklist;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
public class UserBlacklistController {
// Need to create, read, and delete. No need for update?
// Exists, insert, get, delete
public static CompletableFuture<Void> insert(UserBlacklist bl) {
return CompletableFuture.runAsync(() -> {
try {
insertExec(bl);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Boolean> exists(String id) {
return CompletableFuture.supplyAsync(() -> {
try {
return existsExec(id);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Boolean> exists(UserBlacklist bl) {
return CompletableFuture.supplyAsync(() -> {
try {
return existsExec(bl.getId());
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Optional<UserBlacklist>> get(String id) {
return CompletableFuture.supplyAsync(() -> {
try {
return getExec(id);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Void> delete(String id) {
return CompletableFuture.runAsync(() -> {
try {
deleteExec(id);
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
public static CompletableFuture<Void> delete(UserBlacklist bl) {
return CompletableFuture.runAsync(() -> {
try {
deleteExec(bl.getId());
} catch (SQLException e) {
throw new CompletionException(e);
}
});
}
private static void insertExec(UserBlacklist bl) throws SQLException {
if (bl.getAdded() == null) {
bl.setAdded(new Timestamp(System.currentTimeMillis()));
}
FSDB.get().insert("INSERT INTO blacklist_users ('id', 'reason', 'added') VALUES (?, ?, ?)",
bl.getId(),
bl.getReason(),
bl.getAdded()
);
}
private static boolean existsExec(String id) throws SQLException {
ResultSet rs = FSDB.get().select("SELECT EXISTS(SELECT 1 FROM blacklist_users WHERE id = ?) AS hmm", id);
boolean exists = rs.getBoolean("hmm");
FSDB.get().close(rs);
return exists;
}
private static Optional<UserBlacklist> getExec(String id) throws SQLException {
ResultSet rs = FSDB.get().select("SELECT * FROM blacklist_users WHERE id = ?", id);
if (rs.next()) {
UserBlacklist bl = mapObject(rs);
FSDB.get().close(rs);
return Optional.of(bl);
}
FSDB.get().close(rs);
return Optional.empty();
}
private static void deleteExec(String id) throws SQLException {
FSDB.get().query("DELETE FROM blacklist_users WHERE id = ?", id);
}
private static UserBlacklist mapObject(ResultSet rs) throws SQLException {
return new UserBlacklist.UserBlacklistBuilder(rs.getString("id"))
.setReason(rs.getString("reason"))
.setAdded(new Timestamp(rs.getLong("added")))
.build();
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.database.models;
import dev.salmonllama.fsbot.database.DatabaseModel;
public class ColorRole extends DatabaseModel {
private long roleId;
private long serverId;
private String color;
public ColorRole(ColorRoleBuilder builder) {
roleId = builder.roleId;
serverId = builder.serverId;
color = builder.color;
}
public long getRoleId() {
return roleId;
}
public long getServerId() {
return serverId;
}
public String getColor() {
return color;
}
public static String schema() {
return "CREATE TABLE IF NOT EXISTS color_roles (role_id INTEGER, server_id INTEGER, color TEXT)";
}
public static class ColorRoleBuilder {
private long roleId;
private long serverId;
private String color;
public ColorRoleBuilder(long roleId) {
this.roleId = roleId;
}
public ColorRoleBuilder setServerId(long serverId) {
this.serverId = serverId;
return this;
}
public ColorRoleBuilder setColor(String color) {
this.color = color;
return this;
}
public ColorRole build() {
return new ColorRole(this);
}
}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.database.models;
import dev.salmonllama.fsbot.database.DatabaseModel;
public class GalleryChannel extends DatabaseModel {
private final String serverId;
private final String channelId;
private final String tag;
// Normal emojis will be stored in plain text as :emoji:
// Server emojis will be stored in plain text as <:emoji:emoji-id>
// This can be acquired through CustomEmoji#getMentionTag()
private final String emoji;
public GalleryChannel(GalleryBuilder builder) {
serverId = builder.serverId;
channelId = builder.channelId;
tag = builder.tag;
emoji = builder.emoji;
}
public String getServerId() {
return serverId;
}
public String getChannelId() {
return channelId;
}
public String getTag() {
return tag;
}
public String getEmoji() {
return emoji;
}
public static String schema() {
return "CREATE TABLE IF NOT EXISTS galleries (" +
"server_id TEXT," +
"channel_id TEXT," +
"emoji TEXT," +
"tag TEXT)";
}
@Override
public String toString() {
return String.format("Gallery: [server id: %s, channel id: %s, tag: %s, emoji: %s]",
serverId, channelId, tag, emoji);
}
public static class GalleryBuilder {
private String serverId;
private String channelId;
private String tag;
private String emoji;
public GalleryBuilder() {
}
public GalleryBuilder setServerId(String serverId) {
this.serverId = serverId;
return this;
}
public GalleryBuilder setChannelId(String channelId) {
this.channelId = channelId;
return this;
}
public GalleryBuilder setTag(String tag) {
this.tag = tag;
return this;
}
public GalleryBuilder setEmoji(String emoji) {
this.emoji = emoji;
return this;
}
public GalleryChannel build() {
return new GalleryChannel(this);
}
}
}

View File

@ -0,0 +1,202 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.database.models;
import dev.salmonllama.fsbot.database.DatabaseModel;
import java.sql.Timestamp;
public class Outfit extends DatabaseModel {
private String id;
private String link;
private String submitter;
private String tag;
private String meta;
private Timestamp created;
private Timestamp updated;
private boolean deleted;
private boolean featured;
private int displayCount;
private String deleteHash;
public Outfit(OutfitBuilder builder) {
id = builder.id;
link = builder.link;
submitter = builder.submitter;
tag = builder.tag;
meta = builder.meta;
created = builder.created;
updated = builder.updated;
deleted = builder.deleted;
featured = builder.featured;
displayCount = builder.displayCount;
deleteHash = builder.deleteHash;
}
public String getId() {
return id;
}
public String getLink() {
return link;
}
public String getSubmitter() {
return submitter;
}
public String getTag() {
return tag;
}
public String getMeta() {
return meta;
}
public Timestamp getCreated() {
return created;
}
public void setCreated(Timestamp c) {
this.created = c;
}
public Timestamp getUpdated() {
return updated;
}
public void setUpdated(Timestamp u) {
this.updated = u;
}
public boolean isDeleted() {
return deleted;
}
public boolean isFeatured() {
return featured;
}
public int getDisplayCount() {
return displayCount;
}
public String getDeleteHash() {
return deleteHash;
}
public static String schema() {
return "CREATE TABLE IF NOT EXISTS outfits (" +
"id TEXT, " +
"link TEXT," +
"submitter TEXT," +
"tag TEXT," +
"meta TEXT," +
"created TEXT," +
"updated TEXT," +
"deleted TEXT," +
"featured TEXT," +
"display_count INT," +
"delete_hash TEXT)";
}
@Override
public String toString() {
return String.format("Outfit: [id: %s, link: %s, submitter: %s, tag: %s, created: %s, updated: %s, deleted: %s, featured: %s, display count: %s, deletion hash: %s, meta: %s]",
id, link, submitter, tag, created, updated, deleted, featured, displayCount, deleteHash, meta
);
}
public static class OutfitBuilder {
private String id;
private String link;
private String submitter;
private String tag;
private String meta;
private Timestamp created = null;
private Timestamp updated = null;
private boolean deleted = false;
private boolean featured = false;
private int displayCount = 0;
private String deleteHash = "";
public OutfitBuilder() {
}
public OutfitBuilder(Outfit outfit) {
this.id = outfit.getId();
this.link = outfit.getLink();
this.submitter = outfit.getSubmitter();
this.tag = outfit.getTag();
this.meta = outfit.getMeta();
this.created = outfit.getCreated();
this.updated = outfit.getUpdated();
this.deleted = outfit.isDeleted();
this.featured = outfit.isFeatured();
this.displayCount = outfit.getDisplayCount();
this.deleteHash = outfit.getDeleteHash();
}
public OutfitBuilder setId(String id) {
this.id = id;
return this;
}
public OutfitBuilder setLink(String link) {
this.link = link;
return this;
}
public OutfitBuilder setSubmitter(String submitter) {
this.submitter = submitter;
return this;
}
public OutfitBuilder setTag(String tag) {
this.tag = tag;
return this;
}
public OutfitBuilder setMeta(String meta) {
this.meta = meta;
return this;
}
public OutfitBuilder setCreated(Timestamp created) {
this.created = created;
return this;
}
public OutfitBuilder setUpdated(Timestamp updated) {
this.updated = updated;
return this;
}
public OutfitBuilder setDeleted(boolean deleted) {
this.deleted = deleted;
return this;
}
public OutfitBuilder setFeatured(boolean featured) {
this.featured = featured;
return this;
}
public OutfitBuilder setDisplayCount(int displayCount) {
this.displayCount = displayCount;
return this;
}
public OutfitBuilder setDeleteHash(String deletionHash) {
this.deleteHash = deletionHash;
return this;
}
public Outfit build() {
return new Outfit(this);
}
}
}

View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.database.models;
import dev.salmonllama.fsbot.database.DatabaseModel;
import java.sql.Timestamp;
public class ServerBlacklist extends DatabaseModel { // TODO: Add a reason?
private String id;
private String name;
private String ownerId;
private Timestamp added;
private ServerBlacklist(ServerBlacklistBuilder builder) {
this.id = builder.id;
this.name = builder.name;
this.ownerId = builder.ownerId;
this.added = builder.added;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public String getOwnerId() {
return ownerId;
}
public Timestamp getAdded() {
return added;
}
public static String schema() {
return "CREATE TABLE IF NOT EXISTS blacklist_servers (" +
"id TEXT PRIMARY KEY," +
"name TEXT," +
"owner_id TEXT," +
"added TEXT)";
}
@Override
public String toString() {
return String.format("Server Blacklist: [id: %s, name: %s, owner_id: %s, added %s]",
this.getId(),
this.getName(),
this.getOwnerId(),
this.getAdded()
);
}
public static class ServerBlacklistBuilder {
private String id;
private String name;
private String ownerId;
private Timestamp added;
public ServerBlacklistBuilder(String id) {
this.id = id;
}
public ServerBlacklistBuilder setName(String name) {
this.name = name;
return this;
}
public ServerBlacklistBuilder setOwnerId(String ownerId) {
this.ownerId = ownerId;
return this;
}
public ServerBlacklistBuilder setAdded(Timestamp added) {
this.added = added;
return this;
}
public ServerBlacklist build() {
return new ServerBlacklist(this);
}
}
}

View File

@ -0,0 +1,91 @@
package dev.salmonllama.fsbot.database.models;
import dev.salmonllama.fsbot.database.DatabaseModel;
public class ServerConfig extends DatabaseModel {
private final String id;
private final String prefix;
private final String welcomeMessage;
private final String welcomeChannel;
public ServerConfig(ServerConfigBuilder builder) {
this.id = builder.id;
this.prefix = builder.prefix;
this.welcomeMessage = builder.welcomeMessage;
this.welcomeChannel = builder.welcomeChannel;
}
public String getId() {
return id;
}
public String getPrefix() {
return prefix;
}
public String getWelcomeMessage() {
return welcomeMessage;
}
public String getWelcomeChannel() {
return welcomeChannel;
}
public static String schema() {
return "CREATE TABLE IF NOT EXISTS server_config (" +
"id TEXT," +
"prefix TEXT," +
"welcome_message TEXT," +
"welcome_channel TEXT)";
}
@Override
public String toString() {
return String.format("Server Config: [id: %s, prefix: %s, welcome_message: %s, welcome_channel: %s]",
id, prefix, welcomeMessage, welcomeChannel
);
}
public static class ServerConfigBuilder {
private String id;
private String prefix;
private String welcomeMessage;
private String welcomeChannel;
public ServerConfigBuilder() {
}
public ServerConfigBuilder from(ServerConfig config) {
this.id = config.id;
this.prefix = config.prefix;
this.welcomeMessage = config.welcomeMessage;
this.welcomeChannel = config.welcomeChannel;
return this;
}
public ServerConfigBuilder setId(String id) {
this.id = id;
return this;
}
public ServerConfigBuilder setPrefix(String prefix) {
this.prefix = prefix;
return this;
}
public ServerConfigBuilder setWelcomeMessage(String welcomeMessage) {
this.welcomeMessage = welcomeMessage;
return this;
}
public ServerConfigBuilder setWelcomeChannel(String welcomeChannel) {
this.welcomeChannel = welcomeChannel;
return this;
}
public ServerConfig build() {
return new ServerConfig(this);
}
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.database.models;
import dev.salmonllama.fsbot.database.DatabaseModel;
import java.sql.Timestamp;
import java.time.Instant;
public class StaticPermission extends DatabaseModel {
private String userId;
private String permission;
private Timestamp added;
private StaticPermission(StaticPermissionBuilder builder) {
userId = builder.userId;
permission = builder.permission;
added = builder.added;
}
public String getUserId() {
return userId;
}
public String getPermission() {
return permission;
}
public Timestamp getAdded() {
return added;
}
public void setAdded(Timestamp added) {
this.added = added;
}
public static String schema() {
return "CREATE TABLE IF NOT EXISTS static_permissions (user_id TEXT, permission TEXT, date_added TEXT)";
}
@Override
public String toString() {
return String.format("Static Permission [userId: %s, permission: %s, added: %s", userId, permission, added.toString());
}
public static class StaticPermissionBuilder {
private String userId;
private String permission = null;
private Timestamp added = null;
public StaticPermissionBuilder(String userId) {
this.userId = userId;
}
public StaticPermissionBuilder setPermission(String permission) {
this.permission = permission;
return this;
}
public StaticPermissionBuilder setAdded(Timestamp added) {
this.added = added;
return this;
}
public StaticPermission build() {
return new StaticPermission(this);
}
}
}

View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.database.models;
import dev.salmonllama.fsbot.database.DatabaseModel;
import java.sql.Timestamp;
public class UserBlacklist extends DatabaseModel {
private String id;
private String reason;
private Timestamp added;
private UserBlacklist(UserBlacklistBuilder builder) {
this.id = builder.id;
this.reason = builder.reason;
this.added = builder.added;
}
public String getId() {
return id;
}
public String getReason() {
return reason;
}
public Timestamp getAdded() {
return added;
}
public void setAdded(Timestamp added) {
this.added = added;
}
public static String schema() {
return "CREATE TABLE IF NOT EXISTS blacklist_users (" +
"id TEXT PRIMARY KEY," +
"reason TEXT," +
"added TEXT)";
}
@Override
public String toString() {
return String.format("User Blacklist: [id: %s, reason: %s, added: %s]",
this.getId(),
this.getReason(),
this.getAdded()
);
}
public static class UserBlacklistBuilder {
private String id;
private String reason;
private Timestamp added;
public UserBlacklistBuilder(String id) {
this.id = id;
}
public UserBlacklistBuilder setId(String id) {
this.id = id;
return this;
}
public UserBlacklistBuilder setReason(String reason) {
this.reason = reason;
return this;
}
public UserBlacklistBuilder setAdded(Timestamp added) {
this.added = added;
return this;
}
public UserBlacklist build() {
return new UserBlacklist(this);
}
}
}

View File

@ -0,0 +1,85 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.endpoints.imgur;
import dev.salmonllama.fsbot.config.BotConfig;
import okhttp3.*;
import org.json.JSONObject;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
public class ImgurAPIConnection {
private final String REQUEST_URL = "https://api.imgur.com/3/image/";
private final String CLIENT_ID; // Required for uploading
private final String BEARER_TOKEN; // Required for deleting
// Create the okhttp objects. Use methods to complete requests.
private final OkHttpClient client;
private final Request.Builder requestBuilder;
public ImgurAPIConnection() {
CLIENT_ID = BotConfig.IMGUR_ID;
BEARER_TOKEN = BotConfig.IMGUR_BEARER;
client = new OkHttpClient().newBuilder().build();
requestBuilder = new Request.Builder();
}
public CompletableFuture<ImgurUpload> uploadImage(String link) {
return CompletableFuture.supplyAsync(() -> uploadImageExec(link));
}
public CompletableFuture<Boolean> deleteImage(String deleteHash) {
return CompletableFuture.supplyAsync(() -> deleteImageExec(deleteHash));
}
private ImgurUpload uploadImageExec(String discordLink) {
RequestBody body = new MultipartBody.Builder().setType(MultipartBody.FORM).addFormDataPart("image", discordLink).build();
Request request = requestBuilder
.url(REQUEST_URL)
.method("POST", body)
.addHeader("Authorization", CLIENT_ID)
.build();
JSONObject json;
try (Response response = client.newCall(request).execute()) {
json = new JSONObject(response.body().string()).getJSONObject("data");
} catch (IOException e) {
e.printStackTrace(); // TODO: Do tha logging thang.
return null;
}
return new ImgurUpload.ImgurUploadBuilder()
.setId(json.getString("id"))
.setDateTime(json.getLong("datetime"))
.setDeleteHash(json.getString("deletehash"))
.setLink(json.getString("link"))
.build();
}
private boolean deleteImageExec(String deleteHash) {
RequestBody body = new MultipartBody.Builder().setType(MultipartBody.FORM).build();
Request request = requestBuilder
.url(REQUEST_URL.concat(deleteHash))
.method("DELETE", body)
.addHeader("Authorization", BEARER_TOKEN)
.build();
boolean success;
try (Response response = client.newCall(request).execute()) {
success = new JSONObject(response.body().string()).getBoolean("success");
} catch (IOException e) {
e.printStackTrace();
return false;
}
return success;
}
}

View File

@ -0,0 +1,71 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.endpoints.imgur;
public class ImgurUpload {
private final String id;
private final long dateTime;
private final String deleteHash;
private final String link;
public ImgurUpload(ImgurUploadBuilder builder) {
id = builder.id;
dateTime = builder.dateTime;
deleteHash = builder.deleteHash;
link = builder.link;
}
public String getId() {
return id;
}
public long getDateTime() {
return dateTime;
}
public String getDeleteHash() {
return deleteHash;
}
public String getLink() {
return link;
}
public static class ImgurUploadBuilder {
private String id;
private long dateTime;
private String deleteHash;
private String link;
public ImgurUploadBuilder() {
}
public ImgurUploadBuilder setId(String id) {
this.id = id;
return this;
}
public ImgurUploadBuilder setDateTime(long dateTime) {
this.dateTime = dateTime;
return this;
}
public ImgurUploadBuilder setDeleteHash(String deleteHash) {
this.deleteHash = deleteHash;
return this;
}
public ImgurUploadBuilder setLink(String link) {
this.link = link;
return this;
}
public ImgurUpload build() {
return new ImgurUpload(this);
}
}
}

View File

@ -0,0 +1,19 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.exceptions;
public class FailedUploadException extends Exception {
private String message;
public FailedUploadException(String imageLink) {
message = "Failed to upload: ".concat(imageLink);
}
@Override
public String toString() {
return message;
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.exceptions;
import java.sql.SQLException;
public class UnknownParameterException extends SQLException {
private String message;
public UnknownParameterException(Object param) {
message = "Unknown parameter type: " + param;
}
public UnknownParameterException(Object param, int index) {
message = String.format("Unknown parameter type %s at %d", param, index);
}
@Override
public String toString() {
return message;
}
}

View File

@ -6,6 +6,7 @@
package dev.salmonllama.fsbot.guthix;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
public abstract class Command {
public abstract String name();
@ -18,7 +19,6 @@ public abstract class Command {
public abstract void onCommand(CommandContext ctx);
public void invoke(final CommandContext ctx) {
Thread thread = new Thread(() -> onCommand(ctx));
thread.start();
CompletableFuture.runAsync(() -> onCommand(ctx));
}
}

View File

@ -125,6 +125,7 @@ public class CommandContext {
String usedAlias,
String[] args
) {
this.event = event;
this.api = event.getApi();
this.message = event.getMessage();
this.author = event.getMessageAuthor();

View File

@ -5,47 +5,33 @@
package dev.salmonllama.fsbot.guthix;
import dev.salmonllama.fsbot.commands.general.HelpCommand;
import dev.salmonllama.fsbot.commands.general.SpecificOutfitCommand;
import dev.salmonllama.fsbot.commands.developer.*;
import dev.salmonllama.fsbot.commands.general.*;
import dev.salmonllama.fsbot.commands.staff.OutfitInfoCommand;
import dev.salmonllama.fsbot.commands.general.OutfitCommand;
import dev.salmonllama.fsbot.commands.staff.*;
import dev.salmonllama.fsbot.commands.developer.InviteCommand;
import dev.salmonllama.fsbot.commands.developer.CreateGalleryCommand;
import dev.salmonllama.fsbot.commands.general.ColorsCommand;
import dev.salmonllama.fsbot.commands.general.ColorCommand;
import dev.salmonllama.fsbot.commands.general.PingCommand;
import dev.salmonllama.fsbot.commands.developer.EvalCommand;
import dev.salmonllama.fsbot.utilities.database.DatabaseUtilities;
import org.javacord.api.DiscordApi;
import org.javacord.api.entity.channel.TextChannel;
import org.javacord.api.entity.message.Message;
import org.javacord.api.entity.message.MessageAuthor;
import org.javacord.api.entity.server.Server;
import org.javacord.api.event.message.MessageCreateEvent;
import org.javacord.api.listener.message.MessageCreateListener;
import dev.salmonllama.fsbot.commands.developer.TestCommand;
import java.util.Collection;
import java.util.HashMap;
/*
* Guthix is Fashionscape Bot's command repository and dispatcher
* Guthix is Fashionscape Bot's command repository and dispatcher
*/
public class Guthix implements MessageCreateListener {
@SuppressWarnings("unused")
private DiscordApi api;
private DatabaseUtilities db;
private Registry registry;
private PermissionManager manager;
public Guthix(DiscordApi api, DatabaseUtilities db) {
public Guthix(DiscordApi api) {
this.api = api;
api.addMessageCreateListener(this);
this.db = db;
manager = new PermissionManager(api);
manager = new PermissionManager();
registry = new Registry();
initCommands();
@ -54,28 +40,30 @@ public class Guthix implements MessageCreateListener {
public void initCommands() {
// Developer Commands
addCommand(new TestCommand());
addCommand(new EvalCommand(db));
addCommand(new CreateGalleryCommand());
addCommand(new InviteCommand());
addCommand(new PermissionCommand());
// Staff Commands
addCommand(new EchoCommand());
addCommand(new GetServersCommand());
addCommand(new AddColorCommand());
addCommand(new GetOutfitCommand());
addCommand(new RetagCommand(db));
addCommand(new RemoveOutfitCommand(db));
addCommand(new OutfitInfoCommand(db));
addCommand(new RetagCommand());
addCommand(new RemoveOutfitCommand());
addCommand(new OutfitInfoCommand());
addCommand(new SetStatusCommand());
addCommand(new WelcomeMessageCommand());
addCommand(new ShowGalleriesCommand());
// General Commands
addCommand(new PingCommand());
addCommand(new ColorCommand());
addCommand(new ColorsCommand());
addCommand(new OutfitCommand(db));
addCommand(new SpecificOutfitCommand(db));
addCommand(new OutfitCommand());
addCommand(new HelpCommand(this));
addCommand(new StatsCommand());
addCommand(new PrivacyCommand());
}
public void addCommand(Command cmd) {
@ -101,29 +89,27 @@ public class Guthix implements MessageCreateListener {
return;
}
String content = event.getMessageContent().toLowerCase();
String content = event.getMessageContent();
String contentLower = content.toLowerCase();
if (registry.startsWithPrefix(content)) {
if (registry.startsWithPrefix(contentLower)) {
} else {
return;
}
RegistryCommand rComm = registry.getCommandInfo(content);
String cmdString = rComm.getCommand();
String cmdString = rComm.getCommand().toLowerCase();
if (registry.isCommandAlias(cmdString)) {
// if (registry.isCommandAlias(cmdString)) {
//
// } else {
// return;
// }
} else {
return;
}
Message msg = event.getMessage();
TextChannel channel = event.getChannel();
Server server = event.getServer().orElse(null);
String[] cmdArgs = rComm.getArgs();
Command cmd = registry.findCommand(cmdString).orElse(null); // TODO: default command here
Command cmd = registry.findCommand(cmdString).orElse(new DefaultCommand()); // TODO: default command here
CommandContext ctx = new CommandContext.CommandContextBuilder(
event,

View File

@ -5,35 +5,42 @@
package dev.salmonllama.fsbot.guthix;
import org.javacord.api.DiscordApi;
import java.util.concurrent.atomic.AtomicBoolean;
import org.javacord.api.entity.message.MessageAuthor;
public class PermissionManager {
private DiscordApi api;
import dev.salmonllama.fsbot.database.controllers.StaticPermissionController;
import dev.salmonllama.fsbot.database.models.StaticPermission;
public PermissionManager(DiscordApi api) {
this.api = api;
public class PermissionManager {
public PermissionManager() {
}
public boolean hasPermission(CommandPermission reqPerm, CommandContext ctx) {
PermissionType permtype = reqPerm.getType();
PermissionType permType = reqPerm.getType();
String permValue = reqPerm.getValue();
switch (permtype) {
switch (permType) {
case NONE:
return true;
case ROLE:
// If the author has the role, yes. Doesn't work in DM
return roleHandler(reqPerm.getValue(), ctx);
return roleHandler(permValue, ctx);
case STATIC:
return staticHandler(permValue, ctx);
case PERMISSION:
return permissionHandler(ctx);
case OWNER:
// If the author is the owner, yes.
return ownerHandler(ctx);
}
default:
return false;
}
}
private boolean roleHandler(String roleId, CommandContext ctx) {
if (!ctx.getUserRoles().isPresent()) {
if (ctx.getUserRoles().isEmpty()) {
ctx.reply("This command can only be used in a server");
return false;
}
@ -44,6 +51,27 @@ public class PermissionManager {
return ctx.isUserOwner();
}
private boolean permissionHandler(CommandContext ctx) {
// Not implemented yet
return false;
}
private boolean staticHandler(String staticPerm, CommandContext ctx) {
AtomicBoolean ret = new AtomicBoolean(false);
StaticPermissionController.getByUser(ctx.getAuthor().getIdAsString()).thenAccept(possiblePerms -> {
possiblePerms.ifPresent(staticPermissions -> {
for (StaticPermission perm : staticPermissions) {
if (perm.getPermission().equals(staticPerm)) {
ret.set(true);
}
}
});
}).join(); // TODO: Figure out a way to have this not join
return ret.get();
}
boolean sourceIsValid(MessageAuthor author) {
return !author.isBotUser() && !author.isWebhook();
}

View File

@ -7,6 +7,8 @@ package dev.salmonllama.fsbot.guthix;
public enum PermissionType {
ROLE, // User has a specific role inside the server
PERMISSION, // User has a specific Discord permission bit
STATIC, // User has a pre-defined permission stored in the database
OWNER, // User is the bot owner
NONE // No requirement
}

View File

@ -73,7 +73,6 @@ class Registry {
if (input.contains(" ")) {
input = removePrefix(input);
String[] splits = input.split(" ");
System.out.println(Arrays.toString(splits));
return cleanSpaces(splits);
} else {
input = removePrefix(input);

View File

@ -5,153 +5,99 @@
package dev.salmonllama.fsbot.listeners;
import com.rethinkdb.RethinkDB;
import com.rethinkdb.net.Connection;
import com.rethinkdb.net.Cursor;
import com.vdurmont.emoji.EmojiParser;
import okhttp3.*;
import org.javacord.api.entity.message.Message;
import dev.salmonllama.fsbot.config.BotConfig;
import dev.salmonllama.fsbot.database.controllers.GalleryController;
import dev.salmonllama.fsbot.database.controllers.OutfitController;
import dev.salmonllama.fsbot.database.models.Outfit;
import dev.salmonllama.fsbot.endpoints.imgur.ImgurAPIConnection;
import org.javacord.api.entity.message.MessageAttachment;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import org.javacord.api.event.message.MessageCreateEvent;
import org.javacord.api.listener.message.MessageCreateListener;
import org.json.JSONObject;
import dev.salmonllama.fsbot.config.BotConfig;
import org.javacord.api.util.logging.ExceptionLogger;
import java.awt.*;
import java.util.List;
import java.sql.Timestamp;
public class ImageListener implements MessageCreateListener {
final RethinkDB r;
final Connection conn;
public ImageListener(RethinkDB r, Connection conn) {
this.r = r;
this.conn = conn;
}
@Override
public void onMessageCreate(MessageCreateEvent event) {
public void onMessageCreate(MessageCreateEvent event) { // TODO: This needs immediate help
// Check for valid source -> DONE -> WORKING
// Check for gallery channel presence -> DONE -> WORKING
// Check for images (attached files and links from approved sources) -> DONE -> WORKING (approved links to be added later)
// Upload the image(s) to imgur -> DONE -> WORKING
// Store the image in the database -> DONE -> WORKING
// Send confirmation && log event -> IN PROGRESS (waiting for logger upgrade)
event.getMessage().getUserAuthor().ifPresent(author -> {
if (author.isBot()) {
return;
}
});
Message message = event.getMessage();
String channel = event.getChannel().getIdAsString();
String submitter = event.getMessage().getAuthor().getIdAsString();
if (message.getAttachments().stream().noneMatch(MessageAttachment::isImage)) {
if (!event.getMessageAuthor().isRegularUser()) {
// Ignore anything that is a webhook or a bot message
return;
}
// If the message contains an image, check if the channel is being listened to for image storage
// Only works in Server Text Channels
event.getChannel().asServerTextChannel().ifPresent(channel -> {
// Only works in registered Gallery Channels
GalleryController.galleryExists(channel.getIdAsString()).thenAccept(exists -> {
if (exists) {
// Check the message for images
if (event.getMessageAttachments().stream().anyMatch(MessageAttachment::isImage)) {
// Upload the image(s) to Imgur, store in database, log the stored images.
ImgurAPIConnection imgur = new ImgurAPIConnection();
String dbTable = null;
event.getMessageAttachments()
.stream()
.filter(MessageAttachment::isImage)
.forEach(image -> {
// Upload to Imgur
imgur.uploadImage(image.getUrl().toString()).thenAccept(upload -> {
// Store in the database
Outfit.OutfitBuilder outfitBuilder = new Outfit.OutfitBuilder()
.setId(upload.getId())
.setMeta(event.getMessageContent())
.setLink(upload.getLink())
.setSubmitter(event.getMessageAuthor().getIdAsString())
.setDeleteHash(upload.getDeleteHash())
.setCreated(new Timestamp(upload.getDateTime()));
if(r.db("fsbot").table("channels").g("cId").contains(channel).run(conn)) {
Cursor cursor = r.db("fsbot")
.table("channels")
.filter(row -> row.getField("cId").eq(channel))
.getField("tag")
.run(conn);
GalleryController.getTag(channel.getIdAsString()).thenAccept(tag -> {
outfitBuilder.setTag(tag);
Outfit outfit = outfitBuilder.build();
List tags = cursor.toList();
OutfitController.insert(outfit).thenAcceptAsync((Void) -> {
// Log the outfit
event.getApi().getServerTextChannelById(BotConfig.OUTFIT_LOG).ifPresentOrElse(chnl -> {
EmbedBuilder response = new EmbedBuilder()
.setTitle("Outfit Added")
.setAuthor(event.getMessageAuthor())
.setThumbnail(outfit.getLink())
.setFooter(String.format("%s | %s", outfit.getTag(), outfit.getId()))
.setUrl(outfit.getLink())
.addField("Uploaded:", outfit.getCreated().toString());
for (Object tag : tags) {
dbTable = tag.toString();
}
}
else {
return;
if (!outfit.getMeta().equals("")) {
response.addField("Meta:", outfit.getMeta());
}
List<MessageAttachment> attachments = message.getAttachments();
chnl.sendMessage(response);
for (MessageAttachment attachment : attachments) {
String discordLink = attachment.getUrl().toString();
String imgurLink = null;
// Upload the image to imgur
try {
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW");
RequestBody body = RequestBody.create(
mediaType,
String.format("------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"image\"\r\n\r\n%s\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--", discordLink));
Request request = new Request.Builder()
.url("https://api.imgur.com/3/image")
.method("POST", body)
.header("Authorization", BotConfig.IMGUR_ID)
.header("Authorization", BotConfig.IMGUR_BEARER)
.build();
Response response = client.newCall(request).execute();
if (response.body() == null) {
event.getChannel().sendMessage("Something went wrong!");
return;
}
String jsonData = response.body().string();
imgurLink = new JSONObject(jsonData).getJSONObject("data").getString("link");
if (imgurLink == null) {
event.getChannel().sendMessage("Something went wrong!");
return;
}
}
catch (Exception e) {
e.printStackTrace();
}
r.db("fsbot").table("outfits").insert(
r.hashMap("link", imgurLink)
.with("submitter", submitter)
.with("tag", dbTable)
).run(conn);
final String finalLink = imgurLink;
Cursor imgId = r.db("fsbot")
.table("outfits")
.filter(row -> row.getField("link").eq(finalLink))
.getField("id")
.run(conn);
String embedId = null;
List imgIds = imgId.toList();
for (Object id : imgIds) {
embedId = id.toString();
}
EmbedBuilder embed = new EmbedBuilder()
.setTitle("Added an entry of tag " + dbTable)
.setColor(Color.GREEN)
.setThumbnail(imgurLink)
.setAuthor("Placeholder")
.setFooter(embedId);
event.getApi().getUserById(submitter).thenAccept(user -> embed.setAuthor(user.getDiscriminatedName()));
event.getApi().getChannelById(BotConfig.OUTFIT_LOG).ifPresent(chnl -> {
chnl.asTextChannel().ifPresent(txtchnl -> {
txtchnl.sendMessage(embed).join();
// Add the reaction to the original message
GalleryController.getEmoji(channel.getIdAsString()).thenAcceptAsync(emoji -> {
event.getMessage().addReaction(EmojiParser.parseToUnicode(emoji));
}).exceptionally(ExceptionLogger.get());
}, () -> {
// Fallback error message to me
event.getApi().getUserById(BotConfig.BOT_OWNER).thenAcceptAsync(user -> {
user.sendMessage("Could not find OUTFIT LOG");
});
});
if (dbTable.equals("disaster")) {
// Add custom panda emoji: <:PandaWut:433045737245376522>
message.addReaction("PandaWut:433045737245376522");
});
});
}).exceptionally(ExceptionLogger.get());
});
}
message.addReaction(EmojiParser.parseToUnicode(":heartpulse:"));
}
}).exceptionally(ExceptionLogger.get());
});
}
}

View File

@ -5,33 +5,20 @@
package dev.salmonllama.fsbot.listeners;
import dev.salmonllama.fsbot.utilities.database.ServerConfUtility;
import org.javacord.api.DiscordApi;
import dev.salmonllama.fsbot.config.BotConfig;
import org.javacord.api.event.server.member.ServerMemberJoinEvent;
import org.javacord.api.listener.server.member.ServerMemberJoinListener;
public class NewMemberListener implements ServerMemberJoinListener {
private DiscordApi api;
public NewMemberListener(DiscordApi api) {
this.api = api;
}
public void onServerMemberJoin(ServerMemberJoinEvent event) {
if (!event.getServer().getIdAsString().equals("340511685024546816")) return;
if (!event.getServer().getIdAsString().equals(BotConfig.HOME_SERVER)) {
return;
}
ServerConfUtility conf = new ServerConfUtility(event.getServer().getIdAsString());
String welcomeMsg = conf.getWelcomeMsg();
String welcomeChannel = conf.getWelcomeChannel();
// String logMessage = String.format(welcomeMsg, event.getUser().getMentionTag());
String logMessage = String.format(welcomeMsg, event.getUser().getMentionTag());
api.getChannelById(welcomeChannel).ifPresent(chnl -> {
chnl.asServerTextChannel().ifPresent(channel -> {
channel.sendMessage(logMessage);
});
});
event.getApi().getServerTextChannelById(BotConfig.WELCOME_CHANNEL).ifPresent(channel -> channel.sendMessage("Welcome!"));
}
}

View File

@ -1,93 +0,0 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.listeners;
import com.vdurmont.emoji.EmojiParser;
import dev.salmonllama.fsbot.config.BotConfig;
import org.javacord.api.entity.channel.Channel;
import org.javacord.api.entity.message.Message;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import org.javacord.api.entity.user.User;
import org.javacord.api.event.message.reaction.ReactionAddEvent;
import org.javacord.api.listener.message.reaction.ReactionAddListener;
import dev.salmonllama.fsbot.utilities.Outfit;
import dev.salmonllama.fsbot.utilities.database.DatabaseUtilities;
import dev.salmonllama.fsbot.utilities.exceptions.DiscordError;
import dev.salmonllama.fsbot.utilities.exceptions.OutfitNotFoundException;
import java.awt.*;
public class ReactionDeleteConfirmationListener implements ReactionAddListener {
private final User author;
private final Message message;
private final Outfit outfit;
private final DatabaseUtilities db;
public ReactionDeleteConfirmationListener(User author, Message message, Outfit outfit, DatabaseUtilities db) {
this.author = author;
this.message = message;
this.outfit = outfit;
this.db = db;
}
public void onReactionAdd(ReactionAddEvent event) {
if (!event.getUser().getIdAsString().equals(author.getIdAsString())) {
return;
}
if (event.getEmoji().equalsEmoji(EmojiParser.parseToUnicode(":white_check_mark:"))) {
// Delete the message and send confirmation
String completed = "";
try {
completed = db.removeFromDatabase(outfit.getId());
}
catch(OutfitNotFoundException e) {
event.getChannel().sendMessage(new DiscordError(e.getMessage()).get().addField("Info:", "This message was sent by a reaction listener. **YOU SHOULD NOT BE SEEING THIS!**"));
}
if (Integer.parseInt(completed) > 0) {
// Successful deletion
EmbedBuilder embed = new EmbedBuilder()
.setTitle("Success!")
.setColor(Color.GREEN)
.setDescription("Outfit deleted successfully");
message.removeAllReactions();
message.edit(embed);
event.getApi().getChannelById(BotConfig.OUTFIT_LOG).map(Channel::asServerTextChannel).orElseThrow(AssertionError::new).map(channel ->
channel.sendMessage(outfit.generateInfo().setTitle(String.format("Outfit Deleted by %s", event.getUser().getDiscriminatedName())).setColor(Color.RED))
);
}
else {
// Deletion failure
EmbedBuilder embed = new EmbedBuilder()
.setTitle("Error!")
.setColor(Color.RED)
.setDescription("An error occurred and the outfit was not deleted. Did you use the correct ID?")
.setFooter(String.format("Bother %s about making these stupid things more useful.", event.getApi().getOwner().thenAccept(User::getDiscriminatedName)));
message.removeAllReactions();
message.edit(embed);
}
}
else if (event.getEmoji().equalsEmoji(EmojiParser.parseToUnicode(":octagonal_sign:"))) {
// Cancel deletion and do nothing
EmbedBuilder embed = new EmbedBuilder()
.setTitle("Oops!")
.setColor(Color.GREEN)
.setDescription("Deletion cancelled. No changes were made.");
message.removeAllReactions();
message.edit(embed);
}
event.getApi().removeListener(this);
}
}

View File

@ -1,107 +0,0 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.listeners;
import com.vdurmont.emoji.EmojiParser;
import dev.salmonllama.fsbot.config.BotConfig;
import org.javacord.api.entity.message.Message;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import org.javacord.api.entity.user.User;
import org.javacord.api.event.message.reaction.ReactionAddEvent;
import org.javacord.api.listener.message.reaction.ReactionAddListener;
import dev.salmonllama.fsbot.utilities.Outfit;
import dev.salmonllama.fsbot.utilities.database.DatabaseUtilities;
import dev.salmonllama.fsbot.utilities.exceptions.DiscordError;
import dev.salmonllama.fsbot.utilities.exceptions.OutfitNotFoundException;
import java.awt.*;
public class ReactionRetagConfirmationListener implements ReactionAddListener {
private User author;
private final Message message;
private final Outfit outfit;
private final DatabaseUtilities db;
private final String oldTag;
private final String newTag;
public ReactionRetagConfirmationListener(User author, Message message, Outfit outfit, DatabaseUtilities db, String oldTag, String newTag) {
this.author = author;
this.message = message;
this.outfit = outfit;
this.db = db;
this.oldTag = oldTag;
this.newTag = newTag;
}
public void onReactionAdd(ReactionAddEvent event) {
if (!event.getUser().getIdAsString().equals(this.author.getIdAsString())) {
return;
}
if (event.getEmoji().equalsEmoji(EmojiParser.parseToUnicode(":white_check_mark:"))) {
// retag the image, send a confirmation, or an error if it failed for whatever reason.
String result = "";
try {
result = db.changeOutfitTag(outfit.getId(), this.newTag);
}
catch (OutfitNotFoundException e) {
message.delete();
event.getChannel().sendMessage(new DiscordError(e.getMessage()).get().addField("Info", "This message was generated by a listener thread. YOU SHOULD NOT BE SEEING THIS ERROR!"));
}
message.removeAllReactions();
if (Integer.parseInt(result) > 0) {
// success, send confirmation
EmbedBuilder embed = new EmbedBuilder()
.setTitle("Success")
.setColor(Color.GREEN)
.setDescription("Outfit retagged successfully")
.setFooter("Check the log for more information");
message.edit(embed);
// log message with new tag and old tag
event.getApi().getChannelById(BotConfig.OUTFIT_LOG).ifPresent(channel -> {
channel.asServerTextChannel().ifPresent(chnl -> {
chnl.sendMessage(outfit.tagChangeEmbed(this.author.getDiscriminatedName(), this.oldTag, this.newTag));
});
});
}
else {
// failure, something went wrong
EmbedBuilder embed = new EmbedBuilder()
.setTitle("Error!")
.setColor(Color.RED)
.setFooter("Big oopsie");
event.getApi().getOwner().thenAcceptAsync(user -> {
embed.setDescription(String.format("Something has gone horribly wrong, contact %s", user.getDiscriminatedName()));
});
message.edit(embed);
}
}
else if (event.getEmoji().equalsEmoji(EmojiParser.parseToUnicode(":octagonal_sign:"))) {
// Cancel the image retagging.
message.removeAllReactions();
EmbedBuilder embed = new EmbedBuilder()
.setTitle("Operation cancelled!")
.setDescription("No changes made.")
.setColor(Color.GREEN)
.setFooter("boop");
message.edit(embed);
}
event.getApi().removeListener(this);
}
}

View File

@ -5,31 +5,25 @@
package dev.salmonllama.fsbot.listeners;
import dev.salmonllama.fsbot.utilities.database.DatabaseUtilities;
import org.javacord.api.DiscordApi;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import org.javacord.api.event.server.ServerJoinEvent;
import org.javacord.api.listener.server.ServerJoinListener;
import java.awt.*;
public class ServerJoined implements ServerJoinListener {
private DiscordApi api;
private DatabaseUtilities db;
public ServerJoined(DiscordApi api, DatabaseUtilities db) {
public ServerJoined(DiscordApi api) {
this.api = api;
this.db = db;
}
public void onServerJoin(ServerJoinEvent event) {
db.newServerProcess(event.getServer());
public void onServerJoin(ServerJoinEvent event) { // TODO: This needs fixing yo
// db.newServerProcess(event.getServer());
EmbedBuilder embed = new EmbedBuilder()
.setTitle("Server joined")
.setColor(Color.GREEN)
.addInlineField("Server name:", event.getServer().getName())
.addInlineField("Server Id:", event.getServer().getIdAsString());
// EmbedBuilder embed = new EmbedBuilder()
// .setTitle("Server joined")
// .setColor(Color.GREEN)
// .addInlineField("Server name:", event.getServer().getName())
// .addInlineField("Server Id:", event.getServer().getIdAsString());
}
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.logging;
import dev.salmonllama.fsbot.config.BotConfig;
import org.javacord.api.DiscordApi;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import java.util.function.Function;
public class Logger {
private final DiscordApi api;
private final String OUTFIT_LOG = BotConfig.OUTFIT_LOG;
private final String REPORT_LOG = BotConfig.REPORT_LOG;
private final String JOIN_LOG = BotConfig.JOIN_LOG;
private final String BOT_LOG = BotConfig.BOT_LOG;
private final String SALMONLLAMA = BotConfig.BOT_OWNER;
private EmbedBuilder reportEmbed;
private EmbedBuilder errorEmbed;
public Logger(DiscordApi api) {
this.api = api;
}
public void logOutfit() {
api.getServerTextChannelById(OUTFIT_LOG).ifPresentOrElse(channel -> {
// Log the thing
channel.sendMessage("outfit");
}, () -> {
// DM me
api.getUserById(SALMONLLAMA).thenAcceptAsync(user -> {
user.sendMessage("Outfit log failed and was not found");
});
});
}
public void logReport() {
api.getServerTextChannelById(REPORT_LOG).ifPresentOrElse(channel -> {
// Log the thing
channel.sendMessage("report");
}, () -> {
// DM me
api.getUserById(SALMONLLAMA).thenAcceptAsync(user -> {
user.sendMessage("Report log failed and was not found");
});
});
}
public void logError(String errorMsg) {
api.getServerTextChannelById(BOT_LOG).ifPresentOrElse(channel -> {
// Log the thing
channel.sendMessage("error");
}, () -> {
// DM me
api.getUserById(SALMONLLAMA).thenAcceptAsync(user -> {
user.sendMessage("Error log failed and was not found");
});
});
}
public void logMovement() {
api.getServerTextChannelById(JOIN_LOG).ifPresentOrElse(channel -> {
// Log the thing
channel.sendMessage("User joined/Left");
}, () -> {
// DM me
api.getUserById(SALMONLLAMA).thenAcceptAsync(user -> {
user.sendMessage("Movement log failed and was not found");
});
});
}
}

View File

@ -0,0 +1,21 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.logging;
import java.awt.*;
public enum ResponseType {
ERROR (Color.RED, "Error"),
WARN (Color.YELLOW, "Warning"),
INFO (Color.BLUE, "Info"),
LOG (Color.GRAY, "Log");
private Color color;
private String title;
ResponseType(Color color, String title) {
}
}

View File

@ -0,0 +1,14 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.logging;
import org.javacord.api.entity.message.embed.EmbedBuilder;
public class StandardEmbed extends EmbedBuilder {
public StandardEmbed() {
super();
}
}

View File

@ -1,41 +0,0 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.utilities;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import dev.salmonllama.fsbot.utilities.database.RoleColourUtility;
import java.util.List;
import java.awt.Color;
public class ColorRole {
public String id;
public String name;
public ColorRole(String id, String name) {
this.id = id;
this.name = name;
}
public static EmbedBuilder rolesListEmbed() {
List<ColorRole> roles = RoleColourUtility.getAllRoles();
EmbedBuilder embed = new EmbedBuilder()
.setTitle("Available ColorRoles")
.setColor(Color.GREEN)
.setFooter("Showing " + roles.size() + " roles");
StringBuilder builder = new StringBuilder();
roles.forEach(role -> {
builder.append(role.name).append("\n");
});
embed.addField("Roles:", builder.toString());
return embed;
}
}

View File

@ -1,70 +0,0 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.utilities;
import org.javacord.api.DiscordApi;
import org.javacord.api.entity.message.embed.EmbedBuilder;
import java.awt.*;
public class Outfit {
private String id;
private String tag;
private String submitter;
private String link;
private DiscordApi api;
public Outfit(String id, String tag, String submitter, String link) {
this.id = id;
this.tag = tag;
this.submitter = submitter;
this.link = link;
}
public void setId(String id) { this.id = id; }
public String getId() { return this.id; }
public String getTag() { return this.tag; }
// public void setTag(String tag) { this.tag = tag; }
// public String getTag() { return this.tag; }
// public void setSubmitter(String submitter) { this.submitter = submitter; }
// public String getSubmitter() { return this.submitter; }
// public void setLink(String link) { this.link = link; }
// public String getLink() { return this.link; }
public EmbedBuilder generateEmbed() {
return new EmbedBuilder()
.setColor(Color.GREEN)
.setTitle(submitter + " | " + tag)
.setImage(this.link)
.setFooter(this.id);
}
public EmbedBuilder generateInfo() {
return new EmbedBuilder()
.setColor(Color.GREEN)
.setTitle("Outfit Information")
.setThumbnail(this.link)
.addField("Submitter:", this.submitter, true)
.addField("Tag:", this.tag, true)
.addField("Id:", this.id)
.addField("Link:", this.link);
}
public EmbedBuilder tagChangeEmbed(String changer, String oldTag, String newTag) {
return new EmbedBuilder()
.setColor(Color.YELLOW)
.setTitle(String.format("Tag changed by %s", changer))
.setThumbnail(this.link)
.addField("Old Tag:", oldTag)
.addField("New Tag:", newTag)
.setFooter(this.id);
}
}

View File

@ -1,246 +0,0 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.utilities.database;
import com.rethinkdb.RethinkDB;
import com.rethinkdb.gen.ast.Table;
import com.rethinkdb.net.Connection;
import com.rethinkdb.net.Cursor;
import org.javacord.api.DiscordApi;
import dev.salmonllama.fsbot.utilities.Outfit;
import org.javacord.api.entity.server.Server;
import dev.salmonllama.fsbot.utilities.exceptions.OutfitNotFoundException;
import java.util.*;
public class DatabaseUtilities {
private final RethinkDB r;
private final Connection conn;
private final DiscordApi api;
public DatabaseUtilities(RethinkDB r, Connection conn, DiscordApi api) {
this.r = r;
this.conn = conn;
this.api = api;
}
private Table getTable(String table) {
if (this.r.db("fsbot").tableList().contains(table).run(this.conn)) {
return this.r.db("fsbot").table(table);
}
else {
return null;
}
}
private boolean validateId(String id) {
return r.db("fsbot").table("outfits").getField("id").contains(id).run(this.conn);
}
public boolean tagExists(String tag) {
return r.db("fsbot").table("outfits").getField("tag").distinct().contains(tag).run(this.conn);
}
public int countTags() {
return this.r.db("fsbot").table("outfits").getField("tag").distinct().count().run(this.conn);
}
public ArrayList<String> getTags() {
return r.db("fsbot").table("outfits").getField("tag").distinct().run(this.conn);
}
public Outfit getOutfitFromId(String id) throws OutfitNotFoundException {
if (!this.validateId(id)) throw new OutfitNotFoundException();
HashMap sample = this.r.db("fsbot").table("outfits")
.get(id)
.run(this.conn);
String tag = sample.get("tag").toString();
String link = sample.get("link").toString();
String submitterId = sample.get("submitter").toString();
String submitterName = api.getUserById(submitterId).join().getDiscriminatedName();
return new Outfit(id, tag, submitterName, link);
}
public String removeFromDatabase(String id) throws OutfitNotFoundException {
if (!this.validateId(id)) throw new OutfitNotFoundException();
// Remove outfit return deletion status. 0 = failed, >= 1 = success
HashMap deletion = r.db("fsbot").table("outfits").get(id).delete().run(conn);
return deletion.get("deleted").toString();
}
public Outfit randomOutfit() {
HashMap sample = this.r.db("fsbot").table("outfits")
.sample(1).nth(0)
.run(this.conn);
String id = sample.get("id").toString();
String tag = sample.get("tag").toString();
String link = sample.get("link").toString();
String submitterId = sample.get("submitter").toString();
String submitterName = api.getUserById(submitterId).join().getDiscriminatedName();
return new Outfit(id, tag, submitterName, link);
}
public Outfit randomTaggedOutfit(String targetTag) {
HashMap sample = r.db("fsbot").table("outfits")
.filter(row -> row.getField("tag").eq(targetTag))
.sample(1).nth(0)
.run(conn);
String id = String.valueOf(sample.get("id"));
String tag = String.valueOf(sample.get("tag"));
String link = String.valueOf(sample.get("link"));
String submitterId = String.valueOf(sample.get("submitter"));
String submitterName = api.getUserById(submitterId).join().getDiscriminatedName(); // Try a thenAccept with a thenApply
return new Outfit(id, tag, submitterName, link);
}
public String changeOutfitTag(String outfitId, String newTag) throws OutfitNotFoundException {
if (!this.validateId(outfitId)) throw new OutfitNotFoundException();
HashMap replacement = this.r.db("fsbot").table("outfits").get(outfitId).update(r.hashMap("tag", newTag)).run(this.conn);
return replacement.get("replaced").toString();
}
public Outfit getOutfitBySubmitter(String userId) {
HashMap sample = r.db("fsbot").table("outfits")
.filter(row -> row.getField("submitter").eq(userId))
.sample(1).nth(0)
.run(conn);
String id = String.valueOf(sample.get("id"));
String tag = String.valueOf(sample.get("tag"));
String link = String.valueOf(sample.get("link"));
String submitterId = String.valueOf(sample.get("submitter"));
String submitterName = api.getUserById(submitterId).join().getDiscriminatedName();
return new Outfit(id, tag, submitterName, link);
}
public long getSubmitterCount(String submitter) {
return r.db("fsbot").table("outfits")
.filter(
row -> row.getField("submitter").eq(submitter)
)
.count()
.run(conn);
}
public List<String> getSubmitterIds(String submitter) {
List<String> ids = new ArrayList<>();
Cursor<String> cursor = r.db("fsbot").table("outfits")
.filter(
row -> row.getField("submitter").eq(submitter)
)
.getField("id")
.run(conn);
cursor.forEach(ids::add);
return ids;
}
public void updateSubmitter(String submitter, String newSubmitter) {
// Add feature to return update-error?
r.db("fsbot").table("outfits")
.filter(r.hashMap("submitter", submitter))
.update(r.hashMap("submitter", newSubmitter))
.run(conn);
}
public void newServerProcess(Server server) {
if (this.r.db("fsbot").table("serverConf").contains(server.getIdAsString()).run(this.conn)) {
return;
}
String serverName = server.getName();
String serverId = server.getIdAsString();
String logChannel = "null";
String giveawayChannel = "null";
String welcomeChannel = "null";
String defaultWelcome = "welcome to the server";
this.r.db("fsbot").table("serverConf").insert(
this.r.hashMap("id", serverId)
.with("name", serverName)
.with("logChannel", logChannel)
.with("giveawayChannel", giveawayChannel)
.with("welcomeMsg", defaultWelcome)
.with("welcomeChannel", welcomeChannel)
.with("prefix", "~")
).run(this.conn);
}
public void tableSetup() { // TODO: Fix this -- invert conditionals, just create the tables. -> if *not* exist then create
// Check for database existence, if not, create
if (r.dbList().contains("fsbot").run(conn)) {
// System.out.println("database 'fsbot' already exists.");
}
else {
r.dbCreate("fsbot").run(conn);
System.out.println("database fsbot did not exist, and has been created");
}
// Check for channels table existence, if not, create
if (r.db("fsbot").tableList().contains("channels").run(conn)) {
// System.out.println("table channels already exists");
}
else {
r.db("fsbot").tableCreate("channels").run(conn);
System.out.println("table channels did not exist, and has been created.");
}
// Check for serverconf table existence, if not, create
if (r.db("fsbot").tableList().contains("serverConf").run(conn)) {
// System.out.println("table serverConf already exists");
}
else {
r.db("fsbot").tableCreate("serverConf").run(conn);
System.out.println("table serverConf did not exist, and has been created");
}
// Check for permissions table existene, if not, create
if (r.db("fsbot").tableList().contains("permissions").run(conn)) {
// System.out.println("table permissions already exists");
}
else {
r.db("fsbot").tableCreate("permissions").run(conn);
System.out.println("table permissions did not exist and has been created");
}
// Check for outfits table existence, if not, create
if (r.db("fsbot").tableList().contains("outfits").run(conn)) {
// System.out.println("table outfits already exists");
}
else {
r.db("fsbot").tableCreate("outfits").run(conn);
System.out.println("table outfits did not exist and has been created");
}
// Check for colourRoles table existence, if not, create
if (r.db("fsbot").tableList().contains("colourRoles").run(conn)) {
// System.out.println("table colourRoles already exists");
}
else {
r.db("fsbot").tableCreate("colourRoles").run(conn);
System.out.println("table colourRoles did not exist and has been created");
}
}
}

View File

@ -1,68 +0,0 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.utilities.database;
import com.rethinkdb.RethinkDB;
import com.rethinkdb.net.Connection;
import com.rethinkdb.net.Cursor;
import dev.salmonllama.fsbot.utilities.ColorRole;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class RoleColourUtility {
private static RethinkDB r = RethinkDB.r;
private static Connection conn = r.connection().hostname("localhost").port(28015).connect();
public static void addColourRole(String colourName, String roleId) {
r.db("fsbot").table("colourRoles").insert(
r.hashMap("id", roleId)
.with("name", colourName)).run(conn);
}
public static void deleteColourRole() {
}
public static String getColour(String colourName) {
String roleId = null;
Cursor cursor = r.db("fsbot").table("colourRoles")
.filter(row -> row.getField("name").eq(colourName))
.getField("id")
.run(conn);
List roleIds = cursor.toList();
for (Object id : roleIds) {
roleId = id.toString();
}
return roleId;
}
public static List<ColorRole> getAllRoles() {
List<ColorRole> allRoles = new ArrayList<>();
Cursor cursor = r.db("fsbot").table("colourRoles")
.run(conn);
while (cursor.hasNext()) {
HashMap role = (HashMap) cursor.next();
String id = String.valueOf(role.get("id"));
String name = String.valueOf(role.get("name"));
allRoles.add(new ColorRole(id, name));
}
return allRoles;
}
public static String getAllRoleInfo() {
return r.db("fsbot").table("colourRoles").run(conn);
}
}

View File

@ -1,74 +0,0 @@
/*
* Copyright (c) 2020. Aleksei Gryczewski
* All rights reserved.
*/
package dev.salmonllama.fsbot.utilities.database;
import com.rethinkdb.RethinkDB;
import com.rethinkdb.net.Connection;
import com.rethinkdb.net.Cursor;
import java.util.List;
public class ServerConfUtility {
private static final RethinkDB r = RethinkDB.r;
private static final Connection conn = r.connection().hostname("localhost").port(28015).connect();
private static String serverId;
public ServerConfUtility(String sId) {
serverId = sId;
}
// TODO: Turn server into method args, not class field.
public String getWelcomeMsg() {
String welcomeMsg = null;
Cursor welcomes = r.db("fsbot").table("serverConf")
.filter(row -> row.getField("id").eq(serverId))
.getField("welcomeMsg")
.run(conn);
List welcomeMsgs = welcomes.toList();
for (Object msg : welcomeMsgs) {
welcomeMsg = msg.toString();
}
return welcomeMsg;
}
public void setWelcomeMsg(String msg) {
r.db("fsbot").table("serverConf")
.get(serverId)
.update(r.hashMap("welcomeMsg", msg))
.run(conn);
}
public String getWelcomeChannel() {
String welcomeChannel = null;
Cursor channels = r.db("fsbot").table("serverConf")
.filter(row -> row.getField("id").eq(serverId))
.getField("welcomeChannel")
.run(conn);
List welcomeChannels = channels.toList();
for (Object chnl : welcomeChannels) {
welcomeChannel = chnl.toString();
}
return welcomeChannel;
}
public void setWelcomeChannel(String id) {
r.db("fsbot").table("serverConf")
.get(serverId)
.update(r.hashMap("welcomeChannel", id))
.run(conn);
}
}