Compare commits

..

10 Commits

Author SHA1 Message Date
fc9933cbb8
feat: moved to gitea workflows for CI/CD
Some checks failed
Build and test program / Build and test (push) Failing after 9s
Signed-off-by: Louis Vallat <contact@louis-vallat.dev>
2024-09-08 17:44:50 +02:00
7e02939ee8
Updated some dependencies
Signed-off-by: Louis Vallat <louis@louis-vallat.xyz>
2021-12-27 18:16:43 +01:00
c0cb58df00
Added CONTRIBUTING.md
Signed-off-by: Louis Vallat <louis@louis-vallat.xyz>
2021-10-22 08:40:21 +02:00
eff29e9c93 Added a DEFAULT_LANGUAGE System env variable
Signed-off-by: Louis Vallat <louis@louis-vallat.xyz>
2021-05-16 00:46:35 +02:00
66e93e5c57 Moved 'createEmbedListLang' to ListLang
Signed-off-by: Louis Vallat <louis@louis-vallat.xyz>
2021-05-16 00:41:27 +02:00
32ee3c4ee1 Cleaned some code in the command execution sections
Signed-off-by: Louis Vallat <louis@louis-vallat.xyz>
2021-05-16 00:39:40 +02:00
68b3cc941c Updated README
Signed-off-by: Louis Vallat <louis@louis-vallat.xyz>
2021-05-14 00:27:33 +02:00
803c6c9527 Updated Database schema
Signed-off-by: Louis Vallat <louis@louis-vallat.xyz>
2021-05-13 23:54:02 +02:00
0ccab33131 Updated README
Signed-off-by: Louis Vallat <louis@louis-vallat.xyz>
2021-05-13 23:51:05 +02:00
cea9eeb6b7 Added a configuration system, including a default language
Signed-off-by: Louis Vallat <louis@louis-vallat.xyz>
2021-05-13 23:50:11 +02:00
21 changed files with 377 additions and 220 deletions

View File

@ -0,0 +1,22 @@
name: "Build and test program"
on: push
jobs:
build:
name: "Build and test"
runs-on: java-bookworm
steps:
- name: Install gradle
run: |
wget https://services.gradle.org/distributions/gradle-6.6.1-bin.zip
mkdir /opt/gradle
unzip -d /opt/gradle gradle-6.6.1-bin.zip
echo PATH=$PATH:/opt/gradle/gradle-6.6.1/bin >> $GITHUB_ENV
- name: Check out repository code
uses: actions/checkout@v4
- name: Compile
run: gradle --build-cache compileJava
- name: Test
run: gradle --build-cache check
- name: Package
run: gradle --build-cache assemble

View File

@ -1,88 +0,0 @@
image: docker:latest
services:
- docker:dind
stages:
- build
- test
- package
- release
- deploy
# Disable the Gradle daemon for Continuous Integration servers as correctness
# is usually a priority over speed in CI environments. Using a fresh
# runtime for each build is more reliable since the runtime is completely
# isolated from any previous builds.
variables:
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
# Use TLS https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#tls-enabled
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: "/certs"
CONTAINER_BRANCH_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
CONTAINER_RELEASE_IMAGE: $CI_REGISTRY_IMAGE:latest
before_script:
- export GRADLE_USER_HOME=`pwd`/.gradle
build:
image: gradle:jdk15
stage: build
script: gradle --build-cache compileJava
cache:
key: "$CI_COMMIT_REF_NAME"
policy: push
paths:
- build
- .gradle
test:
image: gradle:jdk15
stage: test
script: gradle --build-cache check
cache:
key: "$CI_COMMIT_REF_NAME"
policy: pull
paths:
- build
- .gradle
package:
image: gradle:jdk15
stage: package
script: gradle --build-cache assemble
cache:
key: "$CI_COMMIT_REF_NAME"
policy: push
paths:
- build
- .gradle
artifacts:
paths:
- build/libs/moviesquotebot.jar
release:
stage: release
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build . -t $CONTAINER_BRANCH_IMAGE
- docker push $CONTAINER_BRANCH_IMAGE
release-master:
stage: release
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build . -t $CONTAINER_RELEASE_IMAGE
- docker push $CONTAINER_RELEASE_IMAGE
only:
- master
deploy:
stage: deploy
before_script:
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
script:
- ssh -o StrictHostKeyChecking=no -p $SSH_PORT $SSH_DESTINATION "cd $PATH_TO_APPLICATION; $UPGRADE_COMMAND;"

21
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,21 @@
# How to contribute to MovieQuoteBot
When contributing, you have to:
- own the source code you're including in your contribution
- be polite and kind
- be patient
## Types of contribution
You can contribute to this project by:
- fixing bugs
- opening descriptive and constructive issues
- improving its documentation
- adding unit tests
## Feature requests
In order to get new features added to this project, you can open an issue describing the feature you would like to get added, so we can discuss over it, if it's a good idea to get implemented into this repo or if it justifies making a fork for it.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -1,4 +1,4 @@
DROP TABLE IF EXISTS properties, films, languages, subtitle_lines, subtitles;
DROP TABLE IF EXISTS properties, films, languages, subtitle_lines, subtitles, user_config;
/**
Store some information on the application.
@ -75,4 +75,19 @@ CREATE TABLE IF NOT EXISTS subtitle_lines
PRIMARY KEY (id),
FOREIGN KEY (subtitle_id)
REFERENCES subtitles (id)
);
/**
User configurations
*/
CREATE TABLE IF NOT EXISTS user_config
(
id int GENERATED ALWAYS AS IDENTITY,
user_id bigint NOT NULL,
guild_id bigint DEFAULT NULL,
default_language_id int DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE (user_id, guild_id),
FOREIGN KEY (default_language_id)
REFERENCES languages (id)
);

View File

@ -2,7 +2,8 @@
## DISCLAIMER
> THIS BOT IS A **PROOF OF CONCEPT** THAT NEEDED TO BE CODED WAY TOO QUICKLY TO IMPLEMENT **ANY KIND OF UNIT TESTS**. IT HASN'T BEEN TESTED CORRECTLY AND THEREFORE SHOULD ***NEVER BE USED IN PRODUCTION***.
> THIS BOT IS A **PROOF OF CONCEPT** THAT NEEDED TO BE CODED WAY TOO QUICKLY TO IMPLEMENT **ANY KIND OF UNIT TESTS**. IT HASN'T BEEN TESTED CORRECTLY AND THEREFORE SHOULD ***NEVER BE USED IN PRODUCTION***.
> MOREOVER, THIS CODE IS **VERY CURSED**. ***EWW***. DON'T LOOK AT IT. JUST DON'T.
## Invite link

View File

@ -34,11 +34,11 @@ repositories {
dependencies {
implementation 'com.discord4j:discord4j-core:3.1.5'
implementation 'com.github.wtekiela:opensub4j:0.3.0'
implementation 'org.apache.commons:commons-lang3:3.11'
implementation 'commons-cli:commons-cli:1.4'
implementation 'org.jsoup:jsoup:1.13.1'
implementation 'org.apache.commons:commons-csv:1.8'
implementation 'ch.qos.logback:logback-classic:1.2.3'
implementation 'org.postgresql:postgresql:42.2.18.jre7'
implementation 'com.google.code.gson:gson:2.8.6'
}
implementation 'org.apache.commons:commons-lang3:3.12.0'
implementation 'commons-cli:commons-cli:1.5.0'
implementation 'org.jsoup:jsoup:1.14.3'
implementation 'org.apache.commons:commons-csv:1.9.0'
implementation 'ch.qos.logback:logback-classic:1.2.10'
implementation 'org.postgresql:postgresql:42.3.1'
implementation 'com.google.code.gson:gson:2.8.9'
}

View File

@ -23,7 +23,7 @@ public class MoviesQuoteBot {
public static final String PREFIX = "!";
public static final String NAME = "Movies Quote Bot";
public static final String DESCRIPTION = "I may know quotes from movies.";
public static final String VERSION = "0.5";
public static final String VERSION = "0.7";
public static final String KILL_SWITCH_FILE = "system_locked";
public static final String MAINTENANCE_MODE_FILE = "maintenance_mode_locked";
private static final Logger logger = LoggerFactory.getLogger(MoviesQuoteBot.class.getCanonicalName());

View File

@ -1,45 +1,36 @@
package xyz.vallat.louis.commands;
import com.github.wtekiela.opensub4j.response.SubtitleInfo;
import discord4j.common.util.Snowflake;
import discord4j.core.event.domain.message.MessageCreateEvent;
import discord4j.core.object.reaction.ReactionEmoji;
import discord4j.core.spec.EmbedCreateSpec;
import discord4j.rest.util.Color;
import org.apache.commons.cli.Options;
import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;
import xyz.vallat.louis.omdb.OMDBClient;
import xyz.vallat.louis.omdb.objects.Movie;
import xyz.vallat.louis.subtitles.OpenSubtitles;
import xyz.vallat.louis.env.EnvironmentVariables;
import java.io.IOException;
import java.time.Instant;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public abstract class Command {
protected static final ReactionEmoji WAITING = ReactionEmoji.unicode("");
protected static final String DEFAULT_LANG = System.getenv(EnvironmentVariables.DEFAULT_LANGUAGE.getValue());
private static final Logger logger = LoggerFactory.getLogger(Command.class.getCanonicalName());
protected final String name;
protected final String description;
protected final String usage;
protected final int minArgs;
protected final int maxArgs;
protected final Options options;
protected Command(String name, String description, String usage, int minArgs, int maxArgs) {
protected Command(String name, String description, String usage) {
this.name = name;
this.description = description;
this.usage = usage;
this.minArgs = minArgs;
this.maxArgs = maxArgs;
this.options = new Options();
}
@ -58,18 +49,18 @@ public abstract class Command {
return this.description;
}
public int getMinArgs() {
return minArgs;
}
public int getMaxArgs() {
return maxArgs;
}
public String getName() {
return name;
}
protected Snowflake getUser(MessageCreateEvent event) {
return event.getMessage().getAuthor().isPresent() ? event.getMessage().getAuthor().get().getId() : null;
}
protected Snowflake getGuild(MessageCreateEvent event) {
return event.getMessage().getGuildId().isPresent() ? event.getMessage().getGuildId().get() : null;
}
protected Mono<Void> unknownLanguage(MessageCreateEvent event, String lang) {
return event.getMessage().getChannel()
.flatMap(channel ->
@ -97,39 +88,6 @@ public abstract class Command {
})).then();
}
protected void createEmbedListLang(String arg, EmbedCreateSpec embed, boolean isId) {
try {
Movie movie = OMDBClient.getMovie(arg, isId);
if (movie != null) {
Stream<SubtitleInfo> subtitles = OpenSubtitles.getSubtitleStreamFromMovie(movie);
if (subtitles != null) {
embed.setColor(Color.MEDIUM_SEA_GREEN);
String formattedSubtitles = subtitles.limit(20).map(SubtitleInfo::getLanguage)
.collect(Collectors.joining("\n"));
embed.setDescription("You requested a list of the languages available for a movie with the " +
(isId ? "IMDB Identifier" : "title") + " '" + arg + "'. ");
embed.addField("Movie", movie.toString(), true);
embed.addField("IMDB Link", "https://www.imdb.com/title/" + movie.getImdbID(), true);
if (movie.getPoster() != null) embed.setThumbnail(movie.getPoster());
embed.addField("Top 20 distinct languages (based on downloads)",
formattedSubtitles.isBlank() ? "None." : formattedSubtitles, false);
} else {
embed.setDescription("We couldn't find any subtitle right now. Sorry. Try again later or contact my" +
" administrator, please!");
}
} else {
embed.setDescription("I didn't find any correspondence, sorry! If you know this is an error, please " +
"contact my administrator!");
}
} catch (IOException e) {
logger.error("A network error happened: {}", e.getMessage());
logger.warn("It may be due to a website being down. If it's not" +
"the case, please fix this issue quickly.");
embed.setDescription("An error occurred. " +
"Try again later or contact my administrator.");
}
}
protected String[] getArgArray(MessageCreateEvent event) {
return event.getMessage().getContent().substring(name.length()).split(" ");
}

View File

@ -0,0 +1,59 @@
package xyz.vallat.louis.commands;
import discord4j.common.util.Snowflake;
import discord4j.core.event.domain.message.MessageCreateEvent;
import discord4j.rest.util.Color;
import org.apache.commons.cli.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;
import xyz.vallat.louis.database.LanguageManager;
import xyz.vallat.louis.database.UserConfigManager;
import xyz.vallat.louis.subtitles.dao.Lang;
import java.time.Instant;
public class Config extends Command {
private static final Logger logger = LoggerFactory.getLogger(Config.class.getCanonicalName());
private static final String NO_LANGUAGE = "None.";
private static final String LANG_FIELD_NAME = "Language";
public Config(String name) {
super(name, "Configure your own experience.", name + " [-g|--global] [-l/--lang 'default language'|-rl/--reset-lang]");
options.addOption(Option.builder("g").longOpt("global").desc("Specify that this configuration should be global.").build());
options.addOptionGroup(new OptionGroup()
.addOption(Option.builder("l").longOpt("lang").desc("Specify your default language").hasArg().build())
.addOption(Option.builder("rl").longOpt("reset-lang").desc("Reset your default language").build()));
}
@Override
public Mono<Void> execute(MessageCreateEvent event) {
try {
CommandLine cmd = new DefaultParser().parse(options,
event.getMessage().getContent().substring(name.length()).split(" "));
Snowflake user = getUser(event);
Snowflake guild = cmd.hasOption("g") ? null : getGuild(event);
Lang language = cmd.hasOption("l") ? LanguageManager.getLangFromAny(cmd.getOptionValue("l")) : null;
if (language == null && cmd.hasOption("l")) return unknownLanguage(event, cmd.getOptionValue("l"));
return event.getMessage().getChannel().flatMap(messageChannel -> messageChannel.createEmbed(embed -> {
embed.setTitle("Configuration").setColor(Color.RED).setTimestamp(Instant.now());
if (user == null)
embed.setDescription("I cannot set a null user's preference. Sorry.").setColor(Color.ORANGE);
else {
embed.setDescription("A configuration change has been noticed.");
if (cmd.hasOption("l")) UserConfigManager.setDefaultLanguage(user, guild, language);
else if (cmd.hasOption("rl")) UserConfigManager.setDefaultLanguage(user, guild, null);
else embed.setDescription("<@" + user.asString() + ">'s configuration.");
Lang l = UserConfigManager.getDefaultLanguage(user, guild);
embed.addField(LANG_FIELD_NAME, l == null ? NO_LANGUAGE : l.getEnglish(), true)
.setColor(Color.MEDIUM_SEA_GREEN);
}
})).then().onErrorResume(throwable -> fatalError(event, throwable));
} catch (
ParseException e) {
logger.debug("Parsing error: {}", e.getMessage());
return parsingError(event);
}
}
}

View File

@ -4,6 +4,7 @@ import com.github.wtekiela.opensub4j.response.ListResponse;
import com.github.wtekiela.opensub4j.response.ResponseStatus;
import com.github.wtekiela.opensub4j.response.SubtitleFile;
import com.github.wtekiela.opensub4j.response.SubtitleInfo;
import discord4j.common.util.Snowflake;
import discord4j.core.event.domain.message.MessageCreateEvent;
import discord4j.core.spec.EmbedCreateSpec;
import discord4j.rest.util.Color;
@ -15,6 +16,7 @@ import reactor.core.publisher.Mono;
import xyz.vallat.louis.database.LanguageManager;
import xyz.vallat.louis.database.SubtitleLineManager;
import xyz.vallat.louis.database.SubtitleManager;
import xyz.vallat.louis.database.UserConfigManager;
import xyz.vallat.louis.omdb.OMDBClient;
import xyz.vallat.louis.omdb.objects.Movie;
import xyz.vallat.louis.subtitles.OpenSubtitles;
@ -33,12 +35,12 @@ public class Download extends Command {
private static final Logger logger = LoggerFactory.getLogger(Download.class.getCanonicalName());
public Download(String name) {
super(name, "Download a subtitle.", name + " [-l|--lang <lang>] -i|--imdb <imdb> | -t|--title <title>", 3, 3);
super(name, "Download a subtitle.", name + " [-l|--lang <lang>] -i|--imdb <imdb> | -t|--title <title>");
OptionGroup iOrT = new OptionGroup()
.addOption(Option.builder("i").longOpt("imdb").hasArg().desc("imdb identifier for the film").build())
.addOption(Option.builder("t").longOpt("title").hasArgs().numberOfArgs(Option.UNLIMITED_VALUES).desc("movie title for the film").build());
iOrT.setRequired(true);
options.addOption(Option.builder("l").longOpt("lang").hasArg().desc("specify a language (by default, english)").build());
options.addOption(Option.builder("l").longOpt("lang").hasArg().desc("specify a language (by default, " + DEFAULT_LANG + ")").build());
options.addOptionGroup(iOrT);
}
@ -46,9 +48,16 @@ public class Download extends Command {
public Mono<Void> execute(MessageCreateEvent event) {
try {
CommandLine cmd = new DefaultParser().parse(options, getArgArray(event));
String langArg = cmd.hasOption("l") ? cmd.getOptionValue("l") : "english";
Lang language = LanguageManager.getLangFromAny(langArg);
if (language == null) return unknownLanguage(event, langArg);
Snowflake user = getUser(event);
Snowflake guild = getGuild(event);
if (user == null) return Mono.empty();
Lang l;
if (!cmd.hasOption("l")) {
l = UserConfigManager.getDefaultLanguage(user, guild);
if (l == null) l = LanguageManager.getLangFromAny(DEFAULT_LANG);
} else l = LanguageManager.getLangFromAny(cmd.getOptionValue("l"));
if (l == null) return unknownLanguage(event, cmd.getOptionValue("l"));
Lang language = l;
return event.getMessage().getChannel().flatMap(channel -> event.getMessage().addReaction(WAITING)
.then(channel.createEmbed(embed -> {
embed.setTitle("Importation").setColor(Color.RED);
@ -59,7 +68,7 @@ public class Download extends Command {
if (movie == null || movie.getId() < 0)
embed.setDescription("We couldn't find any movie with these information. Sorry!");
else if (SubtitleManager.getSubtitlesId(movie, language.getId()) > 0)
embed.setDescription("This movie already has already this language imported.")
embed.setDescription("This movie has already this language imported.")
.setColor(Color.ORANGE);
else
computeImportation(event, language, embed, movie);
@ -105,7 +114,7 @@ public class Download extends Command {
subs.get(0).getContentAsString(subtitleInfo.getEncoding()));
if (blocks.isEmpty())
embed.setDescription("The file we downloaded was empty... \uD83E\uDD28 It should be a temporary error! " +
"Try again please! And if you still have this issue, try contacting my administrator, please.");
"Try again please! And if you still have this issue, try contacting my administrator.");
else {
SubtitleLineManager.importSubtitleLines(blocks, language, movie,
event.getMember().isPresent() ? event.getMember().get().getId() : null,
@ -114,6 +123,7 @@ public class Download extends Command {
"Congratulations and thank you" +
(event.getMember().isPresent() ? " <@!" + event.getMember().get().getId().asString() + "> " : " ")
+ "for your contribution!");
embed.setImage(movie.getPoster());
embed.addField("You imported", blocks.size() + " lines into my database", true);
embed.addField("From", movie.toString(), true);
embed.addField("In", language.getEnglish(), true);

View File

@ -10,7 +10,7 @@ import java.time.Instant;
public class Help extends Command {
public Help(String name) {
super(name, "I need help. You need help. We all need help at some point.", name, 0, 0);
super(name, "I need help. You need help. We all need help at some point.", name);
}
@Override

View File

@ -1,15 +1,21 @@
package xyz.vallat.louis.commands;
import com.github.wtekiela.opensub4j.response.SubtitleInfo;
import discord4j.core.event.domain.message.MessageCreateEvent;
import discord4j.core.spec.EmbedCreateSpec;
import discord4j.rest.util.Color;
import org.apache.commons.cli.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;
import xyz.vallat.louis.omdb.OMDBClient;
import xyz.vallat.louis.omdb.objects.Movie;
import xyz.vallat.louis.subtitles.OpenSubtitles;
import xyz.vallat.louis.subtitles.exceptions.UnauthorizedException;
import java.io.IOException;
import java.time.Instant;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ListLang extends Command {
@ -17,7 +23,7 @@ public class ListLang extends Command {
public ListLang(String name) {
super(name, "List all languages attached to a movie title or an IMDB Identifier.",
name + " -i|--imdb <imdb identifier> | -t|--title <title>", 2, 2);
name + " -i|--imdb <imdb identifier> | -t|--title <title>");
OptionGroup iOrT = new OptionGroup()
.addOption(Option.builder("i").longOpt("imdb").desc("specify the imdb identifier").hasArg().build())
.addOption(Option.builder("t").longOpt("title").desc("specify the movie's title").hasArgs().numberOfArgs(Option.UNLIMITED_VALUES).build());
@ -35,12 +41,10 @@ public class ListLang extends Command {
.flatMap(channel -> event.getMessage().addReaction(WAITING).then(event.getMessage().getChannel()
.flatMap(messageChannel -> messageChannel.createEmbed(embed -> {
embed.setTitle("Subtitle languages").setColor(Color.RED);
try {
if (OpenSubtitles.isLoggedIn())
createEmbedListLang(imdbOrTitle, embed, cmd.hasOption("i"));
else throw new UnauthorizedException("isLoggedIn returned false.");
} catch (UnauthorizedException e) {
logger.error("Not logged in on OpenSubtitles! {}", e.getMessage());
if (OpenSubtitles.isLoggedIn())
createEmbedListLang(imdbOrTitle, embed, cmd.hasOption("i"));
else {
logger.error("Not logged in on OpenSubtitles! isLoggedIn returned false!");
logger.warn("It may be due to a website being down. If it's not " +
"the case, please fix this issue quickly.");
embed.setDescription("I cannot search for subtitle languages right now. Sorry.");
@ -53,4 +57,37 @@ public class ListLang extends Command {
return parsingError(event);
}
}
private void createEmbedListLang(String arg, EmbedCreateSpec embed, boolean isId) {
try {
Movie movie = OMDBClient.getMovie(arg, isId);
if (movie != null) {
Stream<SubtitleInfo> subtitles = OpenSubtitles.getSubtitleStreamFromMovie(movie);
if (subtitles != null) {
embed.setColor(Color.MEDIUM_SEA_GREEN);
String formattedSubtitles = subtitles.limit(20).map(SubtitleInfo::getLanguage)
.collect(Collectors.joining("\n"));
embed.setDescription("You requested a list of the languages available for a movie with the " +
(isId ? "IMDB Identifier" : "title") + " '" + arg + "'. ");
embed.addField("Movie", movie.toString(), true);
embed.addField("IMDB Link", "https://www.imdb.com/title/" + movie.getImdbID(), true);
if (movie.getPoster() != null) embed.setThumbnail(movie.getPoster());
embed.addField("Top 20 distinct languages (based on downloads)",
formattedSubtitles.isBlank() ? "None." : formattedSubtitles, false);
} else {
embed.setDescription("We couldn't find any subtitle right now. Sorry. Try again later or contact my" +
" administrator, please!");
}
} else {
embed.setDescription("I didn't find any correspondence, sorry! If you know this is an error, please " +
"contact my administrator!");
}
} catch (IOException e) {
logger.error("A network error happened: {}", e.getMessage());
logger.warn("It may be due to a website being down. If it's not" +
"the case, please fix this issue quickly.");
embed.setDescription("An error occurred. " +
"Try again later or contact my administrator.");
}
}
}

View File

@ -6,13 +6,11 @@ import reactor.core.publisher.Mono;
public class Ping extends Command {
public Ping(String name) {
super(name, "Replies as soon as possible to check the bot's health.", name, 0, 0);
super(name, "Replies as soon as possible to check the bot's health.", name);
}
@Override
public Mono<Void> execute(MessageCreateEvent event) {
return event.getMessage().getChannel()
.flatMap(channel -> channel.createMessage("Pong!"))
.then().onErrorResume(throwable -> fatalError(event, throwable));
return event.getMessage().getChannel().flatMap(c -> c.createMessage("Pong!")).then().onErrorResume(t -> fatalError(event, t));
}
}

View File

@ -1,5 +1,6 @@
package xyz.vallat.louis.commands;
import discord4j.common.util.Snowflake;
import discord4j.core.event.domain.message.MessageCreateEvent;
import discord4j.rest.util.Color;
import org.apache.commons.cli.*;
@ -10,6 +11,7 @@ import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;
import xyz.vallat.louis.database.LanguageManager;
import xyz.vallat.louis.database.SubtitleLineManager;
import xyz.vallat.louis.database.UserConfigManager;
import xyz.vallat.louis.subtitles.dao.FilmQuote;
import xyz.vallat.louis.subtitles.dao.Lang;
@ -21,7 +23,7 @@ public class Quote extends Command {
public Quote(String name) {
super(name, "Get a random quote from any movie.", name + " [-l|--lang <lang>] " +
"[-s|--search <quote extract>] [-i|--imdb imdb_identifier]|[-t|--title title]", 0, 1);
"[-s|--search <quote extract>] [-i|--imdb imdb_identifier]|[-t|--title title]");
OptionGroup iOrT = new OptionGroup()
.addOption(Option.builder("i").longOpt("imdb").desc("specify an IMDB identifier").hasArg().build())
.addOption(Option.builder("t").longOpt("title").desc("specify a title").hasArgs()
@ -38,27 +40,31 @@ public class Quote extends Command {
try {
CommandLine cmd = new DefaultParser().parse(options,
event.getMessage().getContent().substring(name.length()).split(" "));
Lang language = LanguageManager.getLangFromAny(cmd.hasOption("l") ? cmd.getOptionValue("l") : "english");
Lang language;
Snowflake user = getUser(event);
Snowflake guild = getGuild(event);
if (cmd.hasOption("l")) language = LanguageManager.getLangFromAny(cmd.getOptionValue("l"));
else language = user == null ? LanguageManager.getLangFromAny(DEFAULT_LANG) :
UserConfigManager.getDefaultLanguage(user, guild);
if (language == null) language = LanguageManager.getLangFromAny(DEFAULT_LANG);
String imdb = cmd.hasOption("i") ? cmd.getOptionValue("i") : null;
String title = cmd.hasOption("t") ? String.join(" ", cmd.getOptionValues("t")) : null;
String search = cmd.hasOption("s") ? String.join(" ", cmd.getOptionValues("s")) : null;
Lang finalLanguage = language;
logger.error(finalLanguage.getFrench());
return event.getMessage().getChannel().flatMap(messageChannel -> messageChannel.createEmbed(embed -> {
embed.setTitle("Quote").setColor(Color.RED);
if (language == null)
embed.setDescription("This language is unknown. Try again with another one. Or don't try at all.");
FilmQuote quote = SubtitleLineManager.getRandomLine(finalLanguage, imdb, title, search);
if (quote == null)
embed.setDescription("We don't have any quote in that language right now! Sorry!").setColor(Color.ORANGE);
else {
FilmQuote quote = SubtitleLineManager.getRandomLine(language, imdb, title, search);
if (quote == null)
embed.setDescription("We don't have any quote in that language right now! Sorry!").setColor(Color.ORANGE);
else {
embed.setDescription(Jsoup.clean(quote.getSubtitleBlock().getDialogue()
.replaceAll("<i>|</i>", "*")
.replaceAll("<b>|</b>", "**")
.replaceAll("<u>|</u>", "__"),
Whitelist.none()));
embed.setFooter(quote.getMovie().toString(), quote.getMovie().getPoster());
embed.setColor(Color.MEDIUM_SEA_GREEN);
}
embed.setDescription(Jsoup.clean(quote.getSubtitleBlock().getDialogue()
.replaceAll("<i>|</i>", "*")
.replaceAll("<b>|</b>", "**")
.replaceAll("<u>|</u>", "__"),
Whitelist.none()));
embed.setFooter(quote.getMovie().toString(), quote.getMovie().getPoster());
embed.setColor(Color.MEDIUM_SEA_GREEN);
}
embed.setTimestamp(Instant.now());
})).then().onErrorResume(throwable -> fatalError(event, throwable));

View File

@ -1,7 +1,7 @@
package xyz.vallat.louis.commands;
import discord4j.common.util.Snowflake;
import discord4j.core.event.domain.message.MessageCreateEvent;
import discord4j.core.object.entity.Guild;
import discord4j.rest.util.Color;
import reactor.core.publisher.Mono;
import xyz.vallat.louis.MoviesQuoteBot;
@ -14,38 +14,27 @@ import java.time.Instant;
public class Version extends Command {
public Version(String name) {
super(name, "Give some information about the bot.", name, 0, 0);
super(name, "Give some information about the bot.", name);
}
@Override
public Mono<Void> execute(MessageCreateEvent event) {
return event.getMessage().getChannel()
.flatMap(channel -> channel
.createEmbed(embedCreateSpec -> {
embedCreateSpec
.setColor(Color.MEDIUM_SEA_GREEN)
.setTitle(MoviesQuoteBot.NAME)
.setDescription(MoviesQuoteBot.DESCRIPTION)
.addField("Guilds", String.valueOf(DiscordManager.getGuilds().block()), true)
.addField("Subtitles imported", String.valueOf(SubtitleManager.getNumberOfSubtitles()), true)
.addField("Lines imported", String.valueOf(SubtitleLineManager.getNumberOfSubtitleLines()), true);
if (event.getGuildId().isPresent()) {
Guild guild = event.getGuild().block();
if (guild != null)
embedCreateSpec.addField("This guild imported",
SubtitleLineManager
.getNumberOfSubtitleLinesByGuild(guild.getId().asLong()) +
" subtitles lines, from " +
SubtitleManager
.getNumberOfSubtitlesByGuild(guild.getId().asLong()) + " subtitles.",
true);
}
embedCreateSpec.addField("Version", MoviesQuoteBot.VERSION, true)
.setTimestamp(Instant.now());
}
)
)
.then().onErrorResume(throwable -> fatalError(event, throwable));
return event.getMessage().getChannel().flatMap(channel -> channel.createEmbed(embed -> {
embed
.setColor(Color.MEDIUM_SEA_GREEN)
.setTitle(MoviesQuoteBot.NAME)
.setDescription(MoviesQuoteBot.DESCRIPTION)
.addField("Guilds", String.valueOf(DiscordManager.getGuilds().block()), true)
.addField("Subtitles imported", String.valueOf(SubtitleManager.getNumberOfSubtitles()), true)
.addField("Lines imported", String.valueOf(SubtitleLineManager.getNumberOfSubtitleLines()), true);
Snowflake guild = getGuild(event);
if (guild != null)
embed.addField("This guild imported",
SubtitleLineManager.getNumberOfSubtitleLinesByGuild(guild.asLong()) +
" subtitles lines, from " +
SubtitleManager.getNumberOfSubtitlesByGuild(guild.asLong()) + " subtitles.", true);
embed.addField("Version", MoviesQuoteBot.VERSION, true).setTimestamp(Instant.now());
})).then().onErrorResume(throwable -> fatalError(event, throwable));
}
}

View File

@ -16,6 +16,7 @@ import static xyz.vallat.louis.database.LanguageManager.initializeLanguages;
import static xyz.vallat.louis.database.PropertyManager.initializeProperties;
import static xyz.vallat.louis.database.SubtitleLineManager.initializeSubtitleLine;
import static xyz.vallat.louis.database.SubtitleManager.initializeSubtitle;
import static xyz.vallat.louis.database.UserConfigManager.initializeUserConfig;
public final class DBManager {
@ -57,6 +58,7 @@ public final class DBManager {
initializeFilm(connection);
initializeSubtitle(connection);
initializeSubtitleLine(connection);
initializeUserConfig(connection);
importLanguageIfNeeded(connection);
updateDatabaseIfNeeded();
} catch (SQLException e) {

View File

@ -104,13 +104,38 @@ public final class LanguageManager {
}
}
public static Lang getLangFromId(int id) {
Lang lang = null;
try (Connection connection = DBManager.getConnection()) {
String query = "SELECT * FROM languages WHERE id = ?;";
try (PreparedStatement stmt = connection.prepareStatement(query)) {
stmt.setInt(1, id);
stmt.execute();
if (stmt.getResultSet().next()) {
lang = new Lang(
stmt.getResultSet().getInt("id"),
stmt.getResultSet().getString("alpha3_b"),
stmt.getResultSet().getString("alpha3_t"),
stmt.getResultSet().getString("alpha2"),
stmt.getResultSet().getString("english"),
stmt.getResultSet().getString("french")
);
}
}
} catch (SQLException e) {
logger.error("Cannot connect to database: {}", e.getMessage());
System.exit(ExitCodes.CANNOT_CONNECT_TO_DB.getValue());
}
return lang;
}
public static Lang getLangFromAny(@NonNull String name) {
Lang lang = null;
name = name.toLowerCase();
try (Connection connection = DBManager.getConnection()) {
String query = "SELECT * FROM languages WHERE alpha3_b = ? " +
"OR alpha3_t = ? OR alpha2 = ? OR english = ? OR french = ?;";
try (PreparedStatement stmt = connection.prepareStatement(query)){
try (PreparedStatement stmt = connection.prepareStatement(query)) {
for (int i = 1; i < 6; i++) stmt.setString(i, name);
stmt.execute();
if (stmt.getResultSet().next()) {

View File

@ -0,0 +1,100 @@
package xyz.vallat.louis.database;
import discord4j.common.util.Snowflake;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.vallat.louis.codes.ExitCodes;
import xyz.vallat.louis.subtitles.dao.Lang;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
public final class UserConfigManager {
private static final Logger logger = LoggerFactory.getLogger(UserConfigManager.class.getCanonicalName());
private UserConfigManager() {
}
public static Lang getDefaultLanguage(Snowflake user, Snowflake guild) {
Lang lang = null;
try (Connection connection = DBManager.getConnection()) {
String query = "SELECT default_language_id FROM user_config " +
"WHERE user_id = ? AND guild_id " + (guild == null ? "IS NULL" : "= ?") + ";";
try (PreparedStatement stmt = connection.prepareStatement(query)) {
stmt.setLong(1, user.asLong());
if (guild != null) stmt.setLong(2, guild.asLong());
stmt.executeQuery();
if (!stmt.getResultSet().next()) return guild != null ? getDefaultLanguage(user, null) : null;
int id = stmt.getResultSet().getInt("default_language_id");
if (stmt.getResultSet().wasNull()) return null;
return LanguageManager.getLangFromId(id);
}
} catch (SQLException e) {
logger.error("Could not connect to the database right now. Reason: {}", e.getMessage());
System.exit(ExitCodes.CANNOT_CONNECT_TO_DB.getValue());
}
return lang;
}
public static void setDefaultLanguage(Snowflake user, Snowflake guild, Lang lang) {
try (Connection connection = DBManager.getConnection()) {
String query = "SELECT id FROM user_config WHERE user_id = ? AND guild_id " + (guild == null ? "IS NULL" : "= ?") + ";";
try (PreparedStatement stmt = connection.prepareStatement(query)) {
stmt.setLong(1, user.asLong());
if (guild != null) stmt.setLong(2, guild.asLong());
stmt.execute();
if (stmt.getResultSet().next()) updateDefaultLanguage(user, guild, lang, connection);
else insertDefaultLanguage(user, guild, lang, connection);
}
} catch (SQLException e) {
logger.error("Could not connect to the database right now. Reason: {}", e.getMessage());
System.exit(ExitCodes.CANNOT_CONNECT_TO_DB.getValue());
}
}
private static void insertDefaultLanguage(Snowflake user, Snowflake guild, Lang lang, Connection connection) throws SQLException {
String insert = "INSERT INTO user_config(user_id, guild_id, default_language_id) VALUES(?, ?, ?);";
try (PreparedStatement stmt = connection.prepareStatement(insert)) {
stmt.setLong(1, user.asLong());
if (guild == null) stmt.setObject(2, null);
else stmt.setLong(2, guild.asLong());
if (lang == null) stmt.setObject(3, null);
else stmt.setInt(3, lang.getId());
stmt.executeUpdate();
}
}
private static void updateDefaultLanguage(Snowflake user, Snowflake guild, Lang lang, Connection connection) throws SQLException {
String update = "UPDATE user_config SET default_language_id = ? WHERE user_id = ? AND guild_id "
+ (guild == null ? "IS NULL" : "= ?") + ";";
try (PreparedStatement stmt = connection.prepareStatement(update)) {
if (lang == null) stmt.setObject(1, null);
else stmt.setLong(1, lang.getId());
stmt.setLong(2, user.asLong());
if (guild != null) stmt.setLong(3, guild.asLong());
stmt.executeUpdate();
}
}
static void initializeUserConfig(Connection connection) throws SQLException {
logger.debug("Creating userConfigTable table.");
try (Statement stmt = connection.createStatement()) {
String query = """
CREATE TABLE IF NOT EXISTS user_config
(
id int GENERATED ALWAYS AS IDENTITY,
user_id bigint NOT NULL,
guild_id bigint DEFAULT NULL,
default_language_id int DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE (user_id, guild_id),
FOREIGN KEY (default_language_id)
REFERENCES languages (id)
);""";
stmt.executeUpdate(query);
}
}
}

View File

@ -28,6 +28,7 @@ public final class DiscordManager {
commands.add(new ListLang(PREFIX + "listLang"));
commands.add(new Download(PREFIX + "download"));
commands.add(new Quote(PREFIX + "quote"));
commands.add(new Config(PREFIX + "config"));
commands.add(new Ping(PREFIX + "ping"));
commands.add(new Help(PREFIX + "help"));
commands.add(new Version(PREFIX + "version"));

View File

@ -12,7 +12,8 @@ public enum EnvironmentVariables {
OS_USER_AGENT("OPEN_SUBTITLES_USER_AGENT"),
SOCKET_PORT("SOCKET_PORT"),
SOCKET_HOST("SOCKET_HOST"),
OMDB_API_KEY("OMDB_API_KEY");
OMDB_API_KEY("OMDB_API_KEY"),
DEFAULT_LANGUAGE("DEFAULT_LANG");
private final String value;