Compare commits
10 Commits
3632e77b0b
...
fc9933cbb8
Author | SHA1 | Date | |
---|---|---|---|
fc9933cbb8 | |||
7e02939ee8 | |||
c0cb58df00 | |||
eff29e9c93 | |||
66e93e5c57 | |||
32ee3c4ee1 | |||
68b3cc941c | |||
803c6c9527 | |||
0ccab33131 | |||
cea9eeb6b7 |
22
.gitea/workflows/build_and_test.yml
Normal file
22
.gitea/workflows/build_and_test.yml
Normal 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
|
@ -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
21
CONTRIBUTING.md
Normal 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 |
@ -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.
|
||||
@ -76,3 +76,18 @@ CREATE TABLE IF NOT EXISTS subtitle_lines
|
||||
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)
|
||||
);
|
@ -3,6 +3,7 @@
|
||||
## 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***.
|
||||
> MOREOVER, THIS CODE IS **VERY CURSED**. ***EWW***. DON'T LOOK AT IT. JUST DON'T.
|
||||
|
||||
## Invite link
|
||||
|
||||
|
14
build.gradle
14
build.gradle
@ -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'
|
||||
}
|
@ -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());
|
||||
|
@ -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(" ");
|
||||
}
|
||||
|
59
src/main/java/xyz/vallat/louis/commands/Config.java
Normal file
59
src/main/java/xyz/vallat/louis/commands/Config.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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()) {
|
||||
|
100
src/main/java/xyz/vallat/louis/database/UserConfigManager.java
Normal file
100
src/main/java/xyz/vallat/louis/database/UserConfigManager.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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"));
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user