Commit before switching libraries
Signed-off-by: Louis Vallat <louis@louis-vallat.xyz>
This commit is contained in:
parent
3db8134153
commit
e92fdd2813
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
|||||||
.idea
|
.idea
|
||||||
logs
|
logs
|
||||||
build
|
build
|
||||||
|
*.db
|
@ -2,6 +2,7 @@ package xyz.vallat.louis;
|
|||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import xyz.vallat.louis.managers.database.DBManager;
|
||||||
import xyz.vallat.louis.managers.discord.DiscordManager;
|
import xyz.vallat.louis.managers.discord.DiscordManager;
|
||||||
|
|
||||||
public class App {
|
public class App {
|
||||||
@ -14,6 +15,8 @@ public class App {
|
|||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
logger.info("Starting {} ver. {}.", NAME, VERSION);
|
logger.info("Starting {} ver. {}.", NAME, VERSION);
|
||||||
|
DBManager.testConnection();
|
||||||
|
DBManager.initializeDatabase();
|
||||||
DiscordManager.login();
|
DiscordManager.login();
|
||||||
|
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||||
|
17
app/src/main/java/xyz/vallat/louis/codes/ExitCodes.java
Normal file
17
app/src/main/java/xyz/vallat/louis/codes/ExitCodes.java
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package xyz.vallat.louis.codes;
|
||||||
|
|
||||||
|
public enum ExitCodes {
|
||||||
|
CANNOT_CONNECT_TO_DB(1),
|
||||||
|
SQL_FATAL_ERROR(2)
|
||||||
|
;
|
||||||
|
|
||||||
|
private final int code;
|
||||||
|
|
||||||
|
ExitCodes(int code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
}
|
46
app/src/main/java/xyz/vallat/louis/commands/Inscription.java
Normal file
46
app/src/main/java/xyz/vallat/louis/commands/Inscription.java
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package xyz.vallat.louis.commands;
|
||||||
|
|
||||||
|
import biweekly.component.VEvent;
|
||||||
|
import discord4j.common.util.Snowflake;
|
||||||
|
import discord4j.core.event.domain.message.MessageCreateEvent;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
import xyz.vallat.louis.managers.calendar.CalendarManager;
|
||||||
|
import xyz.vallat.louis.managers.database.EventManager;
|
||||||
|
import xyz.vallat.louis.managers.database.dao.Student;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class Inscription extends Command {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(Inscription.class.getCanonicalName());
|
||||||
|
|
||||||
|
public Inscription(String name) {
|
||||||
|
super(name, "S'inscrire aux rappels.", name + " <identifiant ressource ade>");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> execute(MessageCreateEvent event) {
|
||||||
|
String[] args = event.getMessage().getContent().split(" ");
|
||||||
|
if (args.length < 2 || !StringUtils.isNumeric(args[1]))
|
||||||
|
return event.getMessage().getChannel().flatMap(channel -> channel.createMessage("Error: " + getUsage())).then();
|
||||||
|
return event.getMessage().getChannel().flatMap(messageChannel -> {
|
||||||
|
Snowflake snowflake = event.getMessage().getAuthor().isEmpty() ? null :
|
||||||
|
event.getMessage().getAuthor().get().getId();
|
||||||
|
Student student = new Student(snowflake, Integer.parseInt(args[1]));
|
||||||
|
List<VEvent> events = CalendarManager.getEventsFromResource(Integer.parseInt(args[1]));
|
||||||
|
Map<Student, List<VEvent>> studentListMap = new HashMap<>();
|
||||||
|
studentListMap.put(student, events);
|
||||||
|
int importedEvents = EventManager.importEvents(studentListMap);
|
||||||
|
if (importedEvents == 0) return messageChannel.createMessage(
|
||||||
|
"On dirait qu'il y a eu une erreur lors de l'importation. Es-tu sûr que c'est bien ton identifiant ADE ?");
|
||||||
|
else return messageChannel.createMessage("Hey " +
|
||||||
|
(student.getSnowflake() == null ? "" : "<@!" + student.getSnowflake().asString() + "> ")
|
||||||
|
+ " tout est bon ! Je surveille maintenant tes " + importedEvents + " prochains évènements !");
|
||||||
|
}).then().onErrorResume(throwable -> fatalError(event, throwable));
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +0,0 @@
|
|||||||
package xyz.vallat.louis.commands;
|
|
||||||
|
|
||||||
import discord4j.core.event.domain.message.MessageCreateEvent;
|
|
||||||
import reactor.core.publisher.Mono;
|
|
||||||
|
|
||||||
public class Subscribe extends Command {
|
|
||||||
|
|
||||||
public Subscribe(String name) {
|
|
||||||
super(name, "S'inscrire aux rappels.", name + " <identifiant ressource>");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<Void> execute(MessageCreateEvent event) {
|
|
||||||
return event.getMessage().getChannel().flatMap(messageChannel -> messageChannel.createMessage("Ponnnng!")).then();
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,7 +2,13 @@ package xyz.vallat.louis.environment;
|
|||||||
|
|
||||||
public enum EnvironmentVariables {
|
public enum EnvironmentVariables {
|
||||||
DISCORD_TOKEN("DISCORD_TOKEN"),
|
DISCORD_TOKEN("DISCORD_TOKEN"),
|
||||||
MOODLE_PRESENCE_LINK("MOODLE_PRESENCE_LINK")
|
MOODLE_PRESENCE_LINK("MOODLE_PRESENCE_LINK"),
|
||||||
|
DB_USERNAME("DB_USERNAME"),
|
||||||
|
DB_PASSWORD("DB_PASSWORD"),
|
||||||
|
DB_NAME("DB_NAME"),
|
||||||
|
DB_HOST("DB_HOST"),
|
||||||
|
DB_PORT("DB_PORT"),
|
||||||
|
ADE_URL("ADE_URL")
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
package xyz.vallat.louis.managers.calendar;
|
||||||
|
|
||||||
|
import biweekly.Biweekly;
|
||||||
|
import biweekly.component.VEvent;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import xyz.vallat.louis.environment.EnvironmentVariables;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
|
import java.net.http.HttpRequest;
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
public final class CalendarManager {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(CalendarManager.class.getCanonicalName());
|
||||||
|
public static final Calendar TZ_UTC = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
|
||||||
|
|
||||||
|
private CalendarManager() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<VEvent> getEventsFromResource(int resource) {
|
||||||
|
logger.debug("Getting agenda from student with id '{}'.", resource);
|
||||||
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
|
.GET()
|
||||||
|
.uri(URI.create(System.getenv(
|
||||||
|
EnvironmentVariables.ADE_URL.getValue()).replace("{}", String.valueOf(resource)))).build();
|
||||||
|
try {
|
||||||
|
HttpResponse<String> response = getHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
|
if (response.statusCode() >= 200 && response.statusCode() < 400) {
|
||||||
|
return Biweekly.parse(response.body()).first().getEvents();
|
||||||
|
} else logger.error("Got status code {}.", response.statusCode());
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("IOException:", e);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HttpClient getHttpClient() {
|
||||||
|
return HttpClient.newBuilder()
|
||||||
|
.version(HttpClient.Version.HTTP_2)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
package xyz.vallat.louis.managers.database;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import xyz.vallat.louis.codes.ExitCodes;
|
||||||
|
import xyz.vallat.louis.environment.EnvironmentVariables;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.DriverManager;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
public final class DBManager {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(DBManager.class.getCanonicalName());
|
||||||
|
private static final String CONNECTION_PREFIX = "postgresql";
|
||||||
|
private static final String USERNAME = System.getenv(EnvironmentVariables.DB_USERNAME.getValue());
|
||||||
|
private static final String PASSWORD = System.getenv(EnvironmentVariables.DB_PASSWORD.getValue());
|
||||||
|
private static final String DATABASE = System.getenv(EnvironmentVariables.DB_NAME.getValue());
|
||||||
|
private static final int PORT = System.getenv(EnvironmentVariables.DB_PORT.getValue()) == null ? 5432 :
|
||||||
|
Integer.parseInt(System.getenv(EnvironmentVariables.DB_PORT.getValue()));
|
||||||
|
private static final String HOST = System.getenv(EnvironmentVariables.DB_HOST.getValue());
|
||||||
|
|
||||||
|
private DBManager() {}
|
||||||
|
|
||||||
|
public static Connection getConnection() {
|
||||||
|
try {
|
||||||
|
return DriverManager.getConnection("jdbc:" + CONNECTION_PREFIX + "://" + HOST + ":" + PORT + "/"
|
||||||
|
+ DATABASE, USERNAME, PASSWORD);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
logger.error("Could not connect to database. Reason: {}.", e.getMessage());
|
||||||
|
System.exit(ExitCodes.CANNOT_CONNECT_TO_DB.getCode());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void testConnection() {
|
||||||
|
logger.debug("Testing database connection.");
|
||||||
|
getConnection();
|
||||||
|
logger.info("Database connection OK.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void initializeDatabase() {
|
||||||
|
logger.info("Initializing database if needed.");
|
||||||
|
try (Connection connection = getConnection()) {
|
||||||
|
connection.setAutoCommit(false);
|
||||||
|
StudentManager.initialize(connection);
|
||||||
|
EventManager.initialize(connection);
|
||||||
|
attemptCommit(connection);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
fatalSQLError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void attemptCommit(Connection connection) throws SQLException {
|
||||||
|
try {
|
||||||
|
connection.commit();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
connection.rollback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void fatalSQLError(SQLException e) {
|
||||||
|
logger.error("Fatal SQL Error:", e);
|
||||||
|
System.exit(ExitCodes.SQL_FATAL_ERROR.getCode());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
package xyz.vallat.louis.managers.database;
|
||||||
|
|
||||||
|
import biweekly.component.VEvent;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import xyz.vallat.louis.managers.calendar.CalendarManager;
|
||||||
|
import xyz.vallat.louis.managers.database.dao.Student;
|
||||||
|
|
||||||
|
import java.sql.*;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public final class EventManager {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(EventManager.class.getCanonicalName());
|
||||||
|
|
||||||
|
private EventManager() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int importEvents(Map<Student, List<VEvent>> events) {
|
||||||
|
int imported_events = 0;
|
||||||
|
try (Connection connection = DBManager.getConnection()) {
|
||||||
|
connection.setAutoCommit(false);
|
||||||
|
for (Map.Entry<Student, List<VEvent>> e : events.entrySet()) {
|
||||||
|
if (e.getValue().isEmpty()) continue;
|
||||||
|
e.getKey().setId(StudentManager
|
||||||
|
.addStudent(e.getKey().getSnowflake().asString(), e.getKey().getAde(), connection));
|
||||||
|
for (VEvent event : e.getValue())
|
||||||
|
if (importEvent(e.getKey(), event, connection) != 0) imported_events++;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
connection.commit();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
connection.rollback();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
logger.error("Couldn't import events.", e);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return imported_events;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int importEvent(Student student, VEvent event, Connection connection) throws SQLException {
|
||||||
|
String sql = "INSERT INTO events(students_id, summary, start_event, end_event) VALUES (?, ?, ?, ?);";
|
||||||
|
try (PreparedStatement stmt = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
|
||||||
|
Instant start = event.getDateStart().getValue().toInstant();
|
||||||
|
Instant end = event.getDateEnd().getValue().toInstant();
|
||||||
|
stmt.setInt(1, student.getId());
|
||||||
|
stmt.setString(2, event.getSummary().getValue());
|
||||||
|
stmt.setTimestamp(3, start == null ? null : new Timestamp(start.toEpochMilli()), event.getDateStart().getValue().getRawComponents().);
|
||||||
|
stmt.setTimestamp(4, end == null ? null : new Timestamp(end.toEpochMilli()), CalendarManager.TZ_UTC);
|
||||||
|
stmt.executeUpdate();
|
||||||
|
stmt.getGeneratedKeys().next();
|
||||||
|
assert start != null;
|
||||||
|
logger.debug("Imported event with id '{}'.", stmt.getGeneratedKeys().getInt(1));
|
||||||
|
return stmt.getGeneratedKeys().getInt(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void initialize(Connection connection) throws SQLException {
|
||||||
|
String sql = """
|
||||||
|
CREATE TABLE IF NOT EXISTS events
|
||||||
|
(
|
||||||
|
id int GENERATED ALWAYS AS IDENTITY,
|
||||||
|
students_id int NOT NULL,
|
||||||
|
summary text NOT NULL,
|
||||||
|
start_event timestamptz NOT NULL,
|
||||||
|
end_event timestamptz NOT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
FOREIGN KEY (students_id)
|
||||||
|
REFERENCES students (id)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
""";
|
||||||
|
try (Statement stmt = connection.createStatement()) {
|
||||||
|
stmt.executeUpdate(sql);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package xyz.vallat.louis.managers.database;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
|
|
||||||
|
public final class StudentManager {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(StudentManager.class.getCanonicalName());
|
||||||
|
|
||||||
|
private StudentManager() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void initialize(Connection connection) throws SQLException {
|
||||||
|
String sql = """
|
||||||
|
CREATE TABLE IF NOT EXISTS students
|
||||||
|
(
|
||||||
|
id int GENERATED ALWAYS AS IDENTITY,
|
||||||
|
snowflake text NOT NULL,
|
||||||
|
ade_resource int NOT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
UNIQUE (snowflake)
|
||||||
|
);
|
||||||
|
""";
|
||||||
|
try (Statement stmt = connection.createStatement()) {
|
||||||
|
stmt.executeUpdate(sql);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int addStudent(String snowflake, int ade, Connection connection) throws SQLException {
|
||||||
|
String sql = "INSERT INTO students(snowflake, ade_resource) VALUES (?, ?);";
|
||||||
|
try (PreparedStatement stmt = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
|
||||||
|
stmt.setString(1, snowflake);
|
||||||
|
stmt.setInt(2, ade);
|
||||||
|
stmt.executeUpdate();
|
||||||
|
stmt.getGeneratedKeys().next();
|
||||||
|
logger.debug("Inserted student with id '{}'.", stmt.getGeneratedKeys().getInt(1));
|
||||||
|
return stmt.getGeneratedKeys().getInt(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package xyz.vallat.louis.managers.database.dao;
|
||||||
|
|
||||||
|
import discord4j.common.util.Snowflake;
|
||||||
|
|
||||||
|
public class Student {
|
||||||
|
|
||||||
|
private int id;
|
||||||
|
private final Snowflake snowflake;
|
||||||
|
private final int ade;
|
||||||
|
|
||||||
|
public Student(Snowflake snowflake, int ade) {
|
||||||
|
this.snowflake = snowflake;
|
||||||
|
this.ade = ade;
|
||||||
|
this.id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Snowflake getSnowflake() {
|
||||||
|
return snowflake;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAde() {
|
||||||
|
return ade;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(int id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
@ -9,10 +9,9 @@ import discord4j.core.object.presence.Activity;
|
|||||||
import discord4j.core.object.presence.Presence;
|
import discord4j.core.object.presence.Presence;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import reactor.core.publisher.Mono;
|
|
||||||
import xyz.vallat.louis.commands.Command;
|
import xyz.vallat.louis.commands.Command;
|
||||||
|
import xyz.vallat.louis.commands.Inscription;
|
||||||
import xyz.vallat.louis.commands.Lien;
|
import xyz.vallat.louis.commands.Lien;
|
||||||
import xyz.vallat.louis.commands.Subscribe;
|
|
||||||
import xyz.vallat.louis.environment.EnvironmentVariables;
|
import xyz.vallat.louis.environment.EnvironmentVariables;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -20,14 +19,14 @@ import java.util.List;
|
|||||||
|
|
||||||
import static xyz.vallat.louis.App.PREFIX;
|
import static xyz.vallat.louis.App.PREFIX;
|
||||||
|
|
||||||
public class DiscordManager {
|
public final class DiscordManager {
|
||||||
|
|
||||||
private static final List<Command> commands = new ArrayList<>();
|
private static final List<Command> commands = new ArrayList<>();
|
||||||
private static final Logger logger = LoggerFactory.getLogger(DiscordManager.class.getCanonicalName());
|
private static final Logger logger = LoggerFactory.getLogger(DiscordManager.class.getCanonicalName());
|
||||||
private static GatewayDiscordClient discordClient;
|
private static GatewayDiscordClient discordClient;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
commands.add(new Subscribe(PREFIX + "subscribe"));
|
commands.add(new Inscription(PREFIX + "inscription"));
|
||||||
commands.add(new Lien(PREFIX + "lien"));
|
commands.add(new Lien(PREFIX + "lien"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,10 +68,6 @@ public class DiscordManager {
|
|||||||
discordClient.onDisconnect().block();
|
discordClient.onDisconnect().block();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Mono<Long> getGuilds() {
|
|
||||||
return discordClient.getGuilds().count();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<Command> getCommands() {
|
public static List<Command> getCommands() {
|
||||||
return new ArrayList<>(commands);
|
return new ArrayList<>(commands);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user