things might work
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
package tech.allydoes;
|
||||
|
||||
public class Constants {
|
||||
public static final boolean PRODUCTION = false;
|
||||
public static final String TESTING_GUILD = "";
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package tech.allydoes;
|
||||
|
||||
import io.github.cdimascio.dotenv.Dotenv;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import tech.allydoes.database.DatabaseManager;
|
||||
import tech.allydoes.discord.DiscordManager;
|
||||
import tech.allydoes.web.HttpServerHandler;
|
||||
|
||||
public class Main {
|
||||
private static DiscordManager discordManager;
|
||||
private static DatabaseManager databaseManager;
|
||||
|
||||
static Logger logger = LogManager.getLogger(Main.class);
|
||||
public final int port;
|
||||
public Main(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
discordManager = new DiscordManager();
|
||||
databaseManager = new DatabaseManager();
|
||||
|
||||
new Main(8080).start();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
try {
|
||||
ServerBootstrap serverBootstrap = new ServerBootstrap();
|
||||
serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024);
|
||||
serverBootstrap.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.handler(new LoggingHandler(LogLevel.DEBUG))
|
||||
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel socketChannel) {
|
||||
ChannelPipeline channelPipeline = socketChannel.pipeline();
|
||||
channelPipeline.addLast(new HttpResponseEncoder());
|
||||
channelPipeline.addLast(new HttpRequestDecoder());
|
||||
channelPipeline.addLast(new HttpObjectAggregator(1048576));
|
||||
channelPipeline.addLast(new HttpServerHandler());
|
||||
}
|
||||
});
|
||||
Channel ch = serverBootstrap.bind(this.port).sync().channel();
|
||||
ch.closeFuture().sync();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
|
||||
public static DiscordManager getDiscordManager() {
|
||||
return discordManager;
|
||||
}
|
||||
|
||||
public static DatabaseManager getDatabaseManager() {
|
||||
return databaseManager;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package tech.allydoes.database;
|
||||
|
||||
import io.github.cdimascio.dotenv.Dotenv;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.sql.*;
|
||||
|
||||
public class DatabaseManager {
|
||||
private final Logger LOGGER = LogManager.getLogger(DatabaseManager.class);
|
||||
private final Dotenv dotenv = Dotenv.load();
|
||||
|
||||
public DatabaseManager() {
|
||||
initializeDatabase();
|
||||
}
|
||||
|
||||
private void initializeDatabase() {
|
||||
String user = dotenv.get("DB_USER");
|
||||
String password = dotenv.get("DB_PASSWORD");
|
||||
String host = dotenv.get("DB_HOST");
|
||||
String port = dotenv.get("DB_PORT");
|
||||
|
||||
String defaultDbUrl = String.format("jdbc:postgresql://%s:%s/postgres?sslmode=require", host, port);
|
||||
try (Connection conn = DriverManager.getConnection(defaultDbUrl, user, password)) {
|
||||
conn.setAutoCommit(true);
|
||||
|
||||
boolean dbExists = false;
|
||||
try (Statement statement = conn.createStatement()) {
|
||||
if (statement.execute(Queries.CHECK_IF_DATABASE_EXISTS)) {
|
||||
ResultSet resultSet = statement.getResultSet();
|
||||
resultSet.next();
|
||||
dbExists = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dbExists) {
|
||||
LOGGER.debug("Database does not exist. Creating...");
|
||||
try (Statement statement = conn.createStatement()) {
|
||||
if (statement.execute(Queries.CREATE_DATABASE)) {
|
||||
ResultSet resultSet = statement.getResultSet();
|
||||
resultSet.next();
|
||||
LOGGER.debug("Database created successfully.");
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Failed to create database: {}", e.getMessage());
|
||||
}
|
||||
|
||||
try (Statement statement = conn.createStatement()) {
|
||||
if (statement.execute(Queries.CREATE_SERVER_TABLE)) {
|
||||
ResultSet resultSet = statement.getResultSet();
|
||||
resultSet.next();
|
||||
LOGGER.debug("Server table created successfully.");
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Failed to create server table: {}", e.getMessage());
|
||||
}
|
||||
} else {
|
||||
LOGGER.debug("Database already exists.");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Database initialization failed: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void getAllNotifiers() {
|
||||
String user = dotenv.get("DB_USER");
|
||||
String password = dotenv.get("DB_PASSWORD");
|
||||
String host = dotenv.get("DB_HOST");
|
||||
String port = dotenv.get("DB_PORT");
|
||||
|
||||
String defaultDbUrl = String.format("jdbc:postgresql://%s:%s/postgres?sslmode=require", host, port);
|
||||
try (Connection conn = DriverManager.getConnection(defaultDbUrl, user, password)) {
|
||||
conn.setAutoCommit(true);
|
||||
|
||||
try (Statement statement = conn.createStatement()) {
|
||||
statement.execute(Queries.SELECT_ALL_SERVER_CHANNELS);
|
||||
ResultSet resultSet = statement.getResultSet();
|
||||
while (resultSet.next()) {
|
||||
String guildId = resultSet.getString("guild_id");
|
||||
String channelId = resultSet.getString("channel_id");
|
||||
String messageId = resultSet.getString("message_id");
|
||||
LOGGER.debug("Notifier for guild {}, channel {}, message {}", guildId, channelId, messageId);
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("getAllNotifiers: Failed to connect: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void putNotifier(String guildId, String channelId, String messageId) {
|
||||
String user = dotenv.get("DB_USER");
|
||||
String password = dotenv.get("DB_PASSWORD");
|
||||
String host = dotenv.get("DB_HOST");
|
||||
String port = dotenv.get("DB_PORT");
|
||||
|
||||
String defaultDbUrl = String.format("jdbc:postgresql://%s:%s/postgres?sslmode=require", host, port);
|
||||
try (Connection conn = DriverManager.getConnection(defaultDbUrl, user, password)) {
|
||||
conn.setAutoCommit(true);
|
||||
|
||||
try (PreparedStatement preparedStatement = conn.prepareStatement(Queries.INSERT_SERVER_CHANNEL)) {
|
||||
preparedStatement.setString(1, guildId);
|
||||
preparedStatement.setString(2, channelId);
|
||||
preparedStatement.setString(3, messageId);
|
||||
if (preparedStatement.execute()) {
|
||||
LOGGER.debug("Notifier for guild {}, channel {}, message {} added successfully.", guildId, channelId, messageId);
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("putNotifier: Failed to connect: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package tech.allydoes.database;
|
||||
|
||||
public class Queries {
|
||||
public static final String CHECK_IF_DATABASE_EXISTS = "SELECT 1 FROM pg_database WHERE datname = notifier";
|
||||
public static final String CREATE_DATABASE = "CREATE DATABASE IF NOT EXISTS notifier";
|
||||
public static final String CREATE_SERVER_TABLE = "CREATE TABLE IF NOT EXISTS notifier_channels (guild_id VARCHAR(22) PRIMARY KEY, channel_id VARCHAR(22), message_id VARCHAR(22))";
|
||||
public static final String INSERT_SERVER_CHANNEL = "INSERT INTO notifier_channels (guild_id, channel_id, message_id) VALUES (?, ?, ?)";
|
||||
public static final String DELETE_SERVER_CHANNEL = "DELETE FROM notifier_channels WHERE message_id = ?";
|
||||
public static final String SELECT_ALL_SERVER_CHANNELS = "SELECT guild_id, channel_id, message_id FROM notifier_channels";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package tech.allydoes.discord;
|
||||
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
||||
|
||||
public interface Command {
|
||||
SlashCommandData getCommandData();
|
||||
boolean isPrivateCommand();
|
||||
void processSlashCommandInteractionEvent(SlashCommandInteractionEvent event);
|
||||
void processButtonInteractionEvent(ButtonInteractionEvent event);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package tech.allydoes.discord;
|
||||
|
||||
import io.github.cdimascio.dotenv.Dotenv;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.JDABuilder;
|
||||
import net.dv8tion.jda.api.entities.Activity;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.requests.GatewayIntent;
|
||||
import net.dv8tion.jda.api.requests.restaction.CommandListUpdateAction;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import tech.allydoes.Constants;
|
||||
import tech.allydoes.discord.commands.SaveChannelCommand;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class DiscordManager {
|
||||
public final Logger LOGGER;
|
||||
private final JDA jda;
|
||||
private final HashMap<String, Command> commands;
|
||||
private final Dotenv dotenv = Dotenv.load();
|
||||
|
||||
public DiscordManager() throws InterruptedException {
|
||||
LOGGER = LogManager.getLogger(DiscordManager.class);
|
||||
commands = new HashMap<>();
|
||||
commands.put("savechannel", new SaveChannelCommand());
|
||||
|
||||
String botToken = Constants.PRODUCTION ? dotenv.get("DISCORD_TOKEN") : dotenv.get("TESTING_TOKEN");
|
||||
jda = JDABuilder
|
||||
.create(botToken, GatewayIntent.GUILD_MESSAGES)
|
||||
.build()
|
||||
.awaitReady();
|
||||
registerSlashCommands();
|
||||
jda.getPresence().setActivity(Activity.competing("in robotics!"));
|
||||
LOGGER.info("Logged in as {}", jda.getSelfUser().getName());
|
||||
}
|
||||
|
||||
private void registerSlashCommands() {
|
||||
if (Constants.PRODUCTION) {
|
||||
registerSlashCommandsGlobal();
|
||||
} else {
|
||||
registerSlashCommandsGuild(Constants.TESTING_GUILD);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerSlashCommandsGlobal() {
|
||||
CommandListUpdateAction commandListUpdateAction = jda.updateCommands();
|
||||
for (Command command : commands.values()) {
|
||||
if (command.isPrivateCommand()) continue;
|
||||
commandListUpdateAction = commandListUpdateAction.addCommands(command.getCommandData());
|
||||
LOGGER.debug("Registered global command: {}", command.getCommandData().getName());
|
||||
}
|
||||
commandListUpdateAction.queue();
|
||||
}
|
||||
|
||||
private void registerSlashCommandsGuild(String guildID) {
|
||||
Guild guild = jda.getGuildById(guildID);
|
||||
if (guild == null) return;
|
||||
|
||||
CommandListUpdateAction updateAction = guild.updateCommands();
|
||||
for (Command command : commands.values()) {
|
||||
updateAction = updateAction.addCommands(command.getCommandData());
|
||||
LOGGER.debug("Registered guild command: {}", command.getCommandData().getName());
|
||||
}
|
||||
updateAction.queue();
|
||||
}
|
||||
|
||||
public Command getCommand(String command) {
|
||||
return commands.get(command);
|
||||
}
|
||||
|
||||
public JDA getJDA() {
|
||||
return jda;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package tech.allydoes.discord;
|
||||
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
public class Embeds {
|
||||
public static MessageEmbed createUnknownLabStatus() {
|
||||
return new EmbedBuilder()
|
||||
.setTitle("Unknown Status")
|
||||
.setDescription("The status of the lab is unknown.")
|
||||
.setColor(0xD3D3D3)
|
||||
.setTimestamp(Instant.now())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package tech.allydoes.discord.commands;
|
||||
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.Commands;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
||||
import tech.allydoes.Main;
|
||||
import tech.allydoes.discord.Command;
|
||||
import tech.allydoes.discord.Embeds;
|
||||
|
||||
public class SaveChannelCommand implements Command {
|
||||
@Override
|
||||
public SlashCommandData getCommandData() {
|
||||
return Commands.slash("savechannel", "Saves the channel you are in to be used for the status updates")
|
||||
.setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.ADMINISTRATOR));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPrivateCommand() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processSlashCommandInteractionEvent(SlashCommandInteractionEvent event) {
|
||||
event.deferReply(true).queue((interactionHook) -> {
|
||||
interactionHook.getInteraction().getMessageChannel().sendMessageEmbeds(Embeds.createUnknownLabStatus()).queue((embedMessage) -> {
|
||||
String guildId = event.getGuild().getId();
|
||||
String channelId = event.getChannel().getId();
|
||||
String messageId = embedMessage.getId();
|
||||
Main.getDatabaseManager().putNotifier(guildId, channelId, messageId);
|
||||
});
|
||||
event.getHook().editOriginal("Channel saved!").queue();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processButtonInteractionEvent(ButtonInteractionEvent event) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package tech.allydoes.web;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import tech.allydoes.leaderboards.handlers.GET.*;
|
||||
import tech.allydoes.leaderboards.handlers.POST.SetPlayerProfile;
|
||||
import tech.allydoes.leaderboards.handlers.POST.SetScore;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpHeaderNames.*;
|
||||
import static io.netty.handler.codec.http.HttpHeaderValues.CLOSE;
|
||||
|
||||
public class HttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
|
||||
private static final Logger LOGGER = LogManager.getLogger(HttpServerHandler.class);
|
||||
private final HashMap<String, RequestHandler> requestHandlers = new HashMap<>();
|
||||
|
||||
public HttpServerHandler() {
|
||||
RequestHandler[] handlers = {
|
||||
|
||||
};
|
||||
for (RequestHandler handler : handlers) {
|
||||
requestHandlers.put(handler.getRequestName().toLowerCase(), handler);
|
||||
}
|
||||
}
|
||||
|
||||
public static ChannelFuture sendContent(String content, FullHttpRequest request, ChannelHandlerContext channelHandlerContext) {
|
||||
ByteBuf byteBuf = Unpooled.copiedBuffer(content, CharsetUtil.UTF_8);
|
||||
DefaultFullHttpResponse httpResponse = new DefaultFullHttpResponse(request.protocolVersion(), HttpResponseStatus.OK, byteBuf);
|
||||
httpResponse.headers().set(CONTENT_TYPE, "application/json; charset=utf-8");
|
||||
httpResponse.headers().set(CONTENT_LENGTH, byteBuf.readableBytes());
|
||||
return channelHandlerContext.writeAndFlush(httpResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelReadComplete(ChannelHandlerContext channelHandlerContext) {
|
||||
channelHandlerContext.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext channelHandlerContext, FullHttpRequest request) {
|
||||
ChannelFuture future = processRequest(channelHandlerContext, request);
|
||||
|
||||
request.headers().set(CONNECTION, CLOSE);
|
||||
future.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
|
||||
private ChannelFuture processRequest(ChannelHandlerContext channelHandlerContext, FullHttpRequest request) {
|
||||
String path = request.uri().split("\\?", 15)[0].toLowerCase();
|
||||
RequestHandler requestHandler = requestHandlers.get(path);
|
||||
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.uri());
|
||||
Map<String, List<String>> params = queryStringDecoder.parameters();
|
||||
|
||||
if (requestHandler != null && requestHandler.getRequestType().equalsIgnoreCase(request.method().name())) {
|
||||
if (!hasRequiredParameters(params, requestHandler.getRequiredParameters())) {
|
||||
return channelHandlerContext.writeAndFlush(new DefaultFullHttpResponse(request.protocolVersion(), HttpResponseStatus.BAD_REQUEST));
|
||||
}
|
||||
|
||||
return requestHandler.processRequest(channelHandlerContext, request, params);
|
||||
} else {
|
||||
return channelHandlerContext.writeAndFlush(new DefaultFullHttpResponse(request.protocolVersion(), HttpResponseStatus.NOT_FOUND));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext channelHandlerContext, Throwable cause) {
|
||||
LOGGER.info("Unhandled exception in pipeline", cause);
|
||||
|
||||
channelHandlerContext.writeAndFlush(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR));
|
||||
channelHandlerContext.close();
|
||||
}
|
||||
|
||||
private boolean hasRequiredParameters(Map<String, List<String>> parameters, String[] requiredParameters) {
|
||||
for (String requiredParameter : requiredParameters) {
|
||||
if (!parameters.containsKey(requiredParameter)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (List<String> parameterArray: parameters.values()) {
|
||||
String parameter = parameterArray.getFirst();
|
||||
if (parameter.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package tech.allydoes.web.POST;
|
||||
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import tech.allydoes.web.RequestHandler;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class PostStatus implements RequestHandler {
|
||||
public static String STATUS = "status";
|
||||
|
||||
@Override
|
||||
public ChannelFuture processRequest(ChannelHandlerContext channelHandlerContext, FullHttpRequest request, Map<String, List<String>> parameters) {
|
||||
int status = Integer.parseInt(parameters.get(STATUS).getFirst());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestName() {
|
||||
return "PostStatus";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestType() {
|
||||
return "POST";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getRequiredParameters() {
|
||||
return new String[] {
|
||||
STATUS
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package tech.allydoes.web;
|
||||
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface RequestHandler {
|
||||
ChannelFuture processRequest(ChannelHandlerContext channelHandlerContext, FullHttpRequest request, Map<String, List<String>> parameters);
|
||||
String getRequestName();
|
||||
String getRequestType();
|
||||
String[] getRequiredParameters();
|
||||
}
|
||||
Reference in New Issue
Block a user