Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import dev.felnull.itts.core.savedata.legacy.LegacyDictData;
import dev.felnull.itts.core.savedata.legacy.LegacySaveDataLayer;
import dev.felnull.itts.core.util.JsonUtils;
import dev.felnull.itts.core.util.PatternValidator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
Expand All @@ -28,6 +29,16 @@ public class DictionaryManager implements ITTSRuntimeUse {
*/
private static final int FILE_VERSION = 0;

/**
* アップロード時の最大エントリ数
*/
private static final int MAX_DICT_ENTRIES = 1000;

/**
* エントリの最大文字数
*/
private static final int MAX_TEXT_LENGTH = 1000;

/**
* グローバル辞書
*/
Expand Down Expand Up @@ -254,35 +265,55 @@ public List<LegacyDictData> serverDictLoadFromJson(@NotNull JsonObject jo, long
int version = JsonUtils.getInt(jo, "version", -1);

if (version != FILE_VERSION) {
throw new RuntimeException("Unsupported dictionary file version.");
throw new RuntimeException("Unsupported dictionary file version");
}

JsonElement entryElement = jo.get("entry");
if (entryElement == null || !entryElement.isJsonObject()) {
throw new RuntimeException("Invalid dictionary file format");
}

JsonObject entry = jo.getAsJsonObject("entry");

if (entry.size() > MAX_DICT_ENTRIES) {
throw new RuntimeException("Dictionary entry count exceeds limit. Maximum is " + MAX_DICT_ENTRIES);
}

if (jo.get("entry").isJsonObject()) {
JsonObject entry = jo.getAsJsonObject("entry");
LegacySaveDataLayer legacySaveDataLayer = SaveDataManager.getInstance().getLegacySaveDataLayer();
LegacySaveDataLayer legacySaveDataLayer = SaveDataManager.getInstance().getLegacySaveDataLayer();

for (Map.Entry<String, JsonElement> en : entry.entrySet()) {
String target = en.getKey();

if (!en.getValue().isJsonPrimitive() || !en.getValue().getAsJsonPrimitive().isString()) {
continue;
}

String read = en.getValue().getAsString();

for (Map.Entry<String, JsonElement> en : entry.entrySet()) {
String target = en.getKey();
if (target.length() > MAX_TEXT_LENGTH || read.length() > MAX_TEXT_LENGTH) {
continue;
}

if (!en.getValue().isJsonPrimitive() || !en.getValue().getAsJsonPrimitive().isString()) {
continue;
}
if (target.isBlank() || read.isBlank()) {
continue;
}

String read = en.getValue().getAsString();
if (!PatternValidator.validate(target).valid()) {
continue;
}

LegacyDictData pre = legacySaveDataLayer.getServerDictData(guildId, target);
LegacyDictData pre = legacySaveDataLayer.getServerDictData(guildId, target);

if (!overwrite && pre != null) {
continue;
}
if (!overwrite && pre != null) {
continue;
}

legacySaveDataLayer.addServerDictData(guildId, target, read);
legacySaveDataLayer.addServerDictData(guildId, target, read);

LegacyDictData ndata = Objects.requireNonNull(legacySaveDataLayer.getServerDictData(guildId, target));
LegacyDictData ndata = Objects.requireNonNull(legacySaveDataLayer.getServerDictData(guildId, target));

if (!ndata.equals(pre)) {
ret.add(ndata);
}
if (!ndata.equals(pre)) {
ret.add(ndata);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
import java.util.Map;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* グローバル辞書
Expand Down Expand Up @@ -50,7 +52,15 @@ public int getDefaultPriority() {
protected @NotNull Map<Pattern, Function<String, String>> getReplaces(long guildId) {
LegacySaveDataLayer legacySaveDataLayer = SaveDataManager.getInstance().getLegacySaveDataLayer();
return legacySaveDataLayer.getAllGlobalDictData().stream()
.map(n -> Pair.of(Pattern.compile(n.getTarget()), n.getRead()))
.flatMap(n -> {
try {
Pattern pattern = Pattern.compile(n.getTarget());
return Stream.of(Pair.of(pattern, n.getRead()));
} catch (PatternSyntaxException e) {
getITTSLogger().warn("Invalid regex pattern in global dict: {}", n.getTarget());
return Stream.empty();
}
})
.collect(Collectors.toMap(Pair::getLeft, patternStringPair -> n -> patternStringPair.getRight()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
import java.util.Map;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* サーバー辞書
Expand Down Expand Up @@ -50,7 +52,15 @@ public int getDefaultPriority() {
protected @NotNull Map<Pattern, Function<String, String>> getReplaces(long guildId) {
LegacySaveDataLayer legacySaveDataLayer = SaveDataManager.getInstance().getLegacySaveDataLayer();
return legacySaveDataLayer.getAllServerDictData(guildId).stream()
.map(n -> Pair.of(Pattern.compile(n.getTarget()), n.getRead()))
.flatMap(n -> {
try {
Pattern pattern = Pattern.compile(n.getTarget());
return Stream.of(Pair.of(pattern, n.getRead()));
} catch (PatternSyntaxException e) {
getITTSLogger().warn("Invalid regex pattern in server dict: {}", n.getTarget());
return Stream.empty();
}
})
.collect(Collectors.toMap(Pair::getLeft, patternStringPair -> n -> patternStringPair.getRight()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ private void vnick(SlashCommandInteractionEvent event) {
if (name == null) {
sud.setNickName(null);
event.reply(DiscordUtils.getEscapedName(event.getGuild(), user) + "の読み上げユーザ名をリセットしました。").queue();
} else if (name.isBlank()) {
event.reply("名前を空にすることはできません。リセットするには名前を指定せずに実行してください。").setEphemeral(true).queue();
} else {
sud.setNickName(name);
event.reply(DiscordUtils.getEscapedName(event.getGuild(), user) + "の読み上げユーザ名を変更しました。").queue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import dev.felnull.itts.core.savedata.legacy.LegacySaveDataLayer;
import dev.felnull.itts.core.savedata.legacy.LegacyServerData;
import dev.felnull.itts.core.savedata.repository.ServerData;
import dev.felnull.itts.core.util.PatternValidator;
import dev.felnull.itts.core.voice.VoiceCategory;
import dev.felnull.itts.core.voice.VoiceManager;
import dev.felnull.itts.core.voice.VoiceType;
Expand Down Expand Up @@ -171,6 +172,13 @@ private void readIgnore(SlashCommandInteractionEvent event) {
Guild guild = Objects.requireNonNull(event.getGuild());

String op = Objects.requireNonNull(event.getOption("regex", OptionMapping::getAsString));

PatternValidator.ValidationResult validationResult = PatternValidator.validate(op);
if (!validationResult.valid()) {
event.reply(validationResult.error()).setEphemeral(true).queue();
return;
}

LegacySaveDataLayer legacySaveDataLayer = SaveDataManager.getInstance().getLegacySaveDataLayer();
LegacyServerData sd = legacySaveDataLayer.getServerData(guild.getIdLong());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ private void show(SlashCommandInteractionEvent event) {
return;
}

MessageCreateBuilder msg = new MessageCreateBuilder().addContent("読み上げ拒否されたユーザ一覧\n");
StringBuilder sb = new StringBuilder();

JDA jda = event.getJDA();
Expand All @@ -103,7 +102,15 @@ private void show(SlashCommandInteractionEvent event) {
sb.append(DiscordUtils.getEscapedName(guild, user)).append("\n");
}
}
msg.addContent("``" + sb + "``");

if (sb.isEmpty()) {
event.reply("読み上げ拒否されたユーザの情報を取得できませんでした。").setEphemeral(true).queue();
return;
}

MessageCreateBuilder msg = new MessageCreateBuilder()
.addContent("読み上げ拒否されたユーザ一覧\n")
.addContent("```\n" + sb + "```");
event.reply(msg.build()).setEphemeral(true).queue();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import dev.felnull.itts.core.dict.Dictionary;
import dev.felnull.itts.core.dict.DictionaryManager;
import dev.felnull.itts.core.dict.ServerDictionary;
import dev.felnull.itts.core.savedata.SaveDataManager;
import dev.felnull.itts.core.savedata.legacy.LegacyDictData;
import dev.felnull.itts.core.savedata.legacy.LegacySaveDataLayer;
import dev.felnull.itts.core.util.PatternValidator;
import dev.felnull.itts.core.util.StringUtils;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Guild;
Expand Down Expand Up @@ -54,6 +56,11 @@ public class DictCommand extends BaseCommand {
*/
private static final int MAX_FIELD_TEXT_LENGTH = 125;

/**
* アップロードファイルの最大サイズ (1MB)
*/
private static final int MAX_UPLOAD_FILE_SIZE = 1024 * 1024;

/**
* GSOUN
*/
Expand Down Expand Up @@ -175,11 +182,24 @@ private void upload(SlashCommandInteractionEvent event) {
Message.Attachment file = Objects.requireNonNull(event.getOption("file", OptionMapping::getAsAttachment));
boolean overwrite = Objects.requireNonNull(event.getOption("overwrite", OptionMapping::getAsBoolean));

if (file.getSize() > MAX_UPLOAD_FILE_SIZE) {
int maxSizeMB = MAX_UPLOAD_FILE_SIZE / (1024 * 1024);
event.reply(String.format("ファイルサイズが大きすぎます。最大%dMBまでです。", maxSizeMB)).setEphemeral(true).queue();
return;
}

event.deferReply().queue();

file.getProxy().download().thenApplyAsync(stream -> {
try (InputStream st = new BufferedInputStream(stream); Reader reader = new InputStreamReader(st)) {
return GSON.fromJson(reader, JsonObject.class);
try (InputStream st = new BufferedInputStream(stream);
Reader reader = new InputStreamReader(st, StandardCharsets.UTF_8)) {
JsonObject result = GSON.fromJson(reader, JsonObject.class);
if (result == null) {
throw new RuntimeException("Invalid JSON file");
}
return result;
} catch (JsonSyntaxException e) {
throw new RuntimeException("Invalid JSON format: " + e.getMessage());
} catch (IOException e) {
throw new RuntimeException(e);
}
Expand All @@ -204,7 +224,8 @@ private void upload(SlashCommandInteractionEvent event) {
event.getHook().sendMessageEmbeds(replayEmbedBuilder.build()).addContent(overwrite ? "以下の単語の読みを上書き登録しました" : "以下の単語の読みを登録しました").queue();
} else {
getITTSLogger().error("Dictionary registration failure", error);
event.getHook().sendMessage("辞書ファイルの読み込み中にエラーが発生しました").queue();
String errorMessage = error.getCause() != null ? error.getCause().getMessage() : error.getMessage();
event.getHook().sendMessage("辞書ファイルの読み込み中にエラーが発生しました: " + errorMessage).queue();
}
}, getAsyncExecutor());

Expand Down Expand Up @@ -266,8 +287,19 @@ private void add(SlashCommandInteractionEvent event) {
String word = Objects.requireNonNull(event.getOption("word", OptionMapping::getAsString));
String reading = Objects.requireNonNull(event.getOption("reading", OptionMapping::getAsString));

if (word.isBlank() || reading.isBlank()) {
event.reply("単語と読みを空にすることはできません。").setEphemeral(true).queue();
return;
}

if (word.length() > MAX_TEXT_LENGTH || reading.length() > MAX_TEXT_LENGTH) {
event.reply(String.format("登録可能な最大文字数は%d文字です", MAX_TEXT_LENGTH)).queue();
event.reply(String.format("登録可能な最大文字数は%d文字です", MAX_TEXT_LENGTH)).setEphemeral(true).queue();
return;
}

PatternValidator.ValidationResult validationResult = PatternValidator.validate(word);
if (!validationResult.valid()) {
event.reply(validationResult.error()).setEphemeral(true).queue();
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ public void commandInteraction(SlashCommandInteractionEvent event) {
if (name == null) {
sud.setNickName(null);
event.reply("自分の読み上げユーザ名をリセットしました。").setEphemeral(true).queue();
} else if (name.isBlank()) {
event.reply("名前を空にすることはできません。リセットするには名前を指定せずに実行してください。").setEphemeral(true).queue();
} else {
sud.setNickName(name);
event.reply("自分の読み上げユーザ名を変更しました。").setEphemeral(true).queue();
Expand Down
11 changes: 8 additions & 3 deletions core/src/main/java/dev/felnull/itts/core/tts/TTSManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

/**
* TTS管理
Expand Down Expand Up @@ -186,9 +187,13 @@ public void sayChat(@NotNull Guild guild, @NotNull MessageChannel messageChannel

String ignoreRegex = legacySaveDataLayer.getServerData(guild.getIdLong()).getIgnoreRegex();
if (ignoreRegex != null) {
Pattern ignorePattern = Pattern.compile(ignoreRegex);
if (ignorePattern.matcher(message.getContentDisplay()).matches()) {
return;
try {
Pattern ignorePattern = Pattern.compile(ignoreRegex);
if (ignorePattern.matcher(message.getContentDisplay()).matches()) {
return;
}
} catch (PatternSyntaxException e) {
getITTSLogger().warn("Invalid ignore regex for guild {}: {}", guild.getIdLong(), ignoreRegex);
}
}

Expand Down
Loading