refactor: improve record classes and make FilterManagerPlugin use the FilteredPlayer record + use adventure's TextComponent.Builder instead of keeping setting the component variable
This commit is contained in:
@@ -2,7 +2,7 @@ package me.chayapak1.chomens_bot.commands;
|
||||
|
||||
import me.chayapak1.chomens_bot.Bot;
|
||||
import me.chayapak1.chomens_bot.command.*;
|
||||
import me.chayapak1.chomens_bot.data.filter.FilteredPlayer;
|
||||
import me.chayapak1.chomens_bot.data.filter.PlayerFilter;
|
||||
import me.chayapak1.chomens_bot.plugins.DatabasePlugin;
|
||||
import me.chayapak1.chomens_bot.plugins.PlayerFilterPlugin;
|
||||
import net.kyori.adventure.text.Component;
|
||||
@@ -50,7 +50,7 @@ public class FilterCommand extends Command {
|
||||
|
||||
if (
|
||||
PlayerFilterPlugin.localList.stream()
|
||||
.map(FilteredPlayer::playerName)
|
||||
.map(PlayerFilter::playerName)
|
||||
.toList()
|
||||
.contains(player)
|
||||
) {
|
||||
@@ -99,7 +99,7 @@ public class FilterCommand extends Command {
|
||||
final int index = context.getInteger(true);
|
||||
|
||||
try {
|
||||
final FilteredPlayer player = PlayerFilterPlugin.localList.get(index);
|
||||
final PlayerFilter player = PlayerFilterPlugin.localList.get(index);
|
||||
|
||||
if (player == null) throw new IllegalArgumentException();
|
||||
|
||||
@@ -126,7 +126,7 @@ public class FilterCommand extends Command {
|
||||
final List<Component> filtersComponents = new ArrayList<>();
|
||||
|
||||
int index = 0;
|
||||
for (final FilteredPlayer player : PlayerFilterPlugin.localList) {
|
||||
for (final PlayerFilter player : PlayerFilterPlugin.localList) {
|
||||
Component options = Component.empty().color(NamedTextColor.DARK_GRAY);
|
||||
|
||||
if (player.ignoreCase() || player.regex()) {
|
||||
|
||||
@@ -27,15 +27,4 @@ public class BossBar {
|
||||
this.division = division;
|
||||
this.health = health;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString () {
|
||||
return "BossBar{" +
|
||||
"uuid=" + uuid +
|
||||
", title=" + title +
|
||||
", color=" + color +
|
||||
", division=" + division +
|
||||
", health=" + health +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,4 @@ import me.chayapak1.chomens_bot.data.player.PlayerEntry;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public record PlayerMessage(PlayerEntry sender, Component displayName, Component contents) {
|
||||
@Override
|
||||
public String toString () {
|
||||
return "PlayerMessage{" +
|
||||
"sender=" + sender +
|
||||
", displayName=" + displayName +
|
||||
", contents=" + contents +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package me.chayapak1.chomens_bot.data.chomeNSMod;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -31,7 +32,7 @@ public record PayloadMetadata(byte[] nonce, long timestamp) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString () {
|
||||
public @NotNull String toString () {
|
||||
return "PayloadMetadata{" +
|
||||
"nonce=" + Arrays.toString(nonce) +
|
||||
", timestamp=" + timestamp +
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
package me.chayapak1.chomens_bot.data.filter;
|
||||
|
||||
public record FilteredPlayer(String playerName, String reason, boolean regex, boolean ignoreCase) {
|
||||
@Override
|
||||
public String toString () {
|
||||
return "FilteredPlayer{" +
|
||||
"playerName='" + playerName + '\'' +
|
||||
", reason='" + reason + '\'' +
|
||||
", regex=" + regex +
|
||||
", ignoreCase=" + ignoreCase +
|
||||
'}';
|
||||
}
|
||||
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
|
||||
|
||||
public record FilteredPlayer(PlayerEntry player, String reason) {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
package me.chayapak1.chomens_bot.data.filter;
|
||||
|
||||
public record PlayerFilter(String playerName, String reason, boolean regex, boolean ignoreCase) {
|
||||
}
|
||||
@@ -17,7 +17,7 @@ public record Key(
|
||||
public @NotNull String toString () {
|
||||
return "Key{" +
|
||||
"trustLevel=" + trustLevel +
|
||||
", key='" + key + '\'' +
|
||||
", key=************" +
|
||||
", createdAt=" + createdAt +
|
||||
'}';
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package me.chayapak1.chomens_bot.data.keys;
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -13,11 +12,4 @@ public record KeysData(
|
||||
@JsonProperty ArrayList<Key> keys,
|
||||
@JsonProperty String userId
|
||||
) {
|
||||
@Override
|
||||
public @NotNull String toString () {
|
||||
return "KeysData{" +
|
||||
"keys=" + keys +
|
||||
", userId='" + userId + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,4 @@
|
||||
package me.chayapak1.chomens_bot.data.mail;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public record Mail(String sentBy, String sentTo, long timeSent, String server, String contents) {
|
||||
@Override
|
||||
public @NotNull String toString () {
|
||||
return "Mail{" +
|
||||
"sentBy='" + sentBy + '\'' +
|
||||
", sentTo='" + sentTo + '\'' +
|
||||
", timeSent=" + timeSent +
|
||||
", server='" + server + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,16 +43,17 @@ public class CommandSuggestionPlugin implements Listener {
|
||||
|
||||
final boolean hasAliases = command.aliases.length != 0;
|
||||
|
||||
Component outputComponent = Component
|
||||
.text(command.name)
|
||||
final TextComponent.Builder outputComponent = Component
|
||||
.text()
|
||||
.content(command.name)
|
||||
.append(Component.text(command.trustLevel.name()))
|
||||
.append(Component.text(hasAliases));
|
||||
|
||||
if (hasAliases) {
|
||||
for (final String alias : command.aliases) outputComponent = outputComponent.append(Component.text(alias));
|
||||
for (final String alias : command.aliases) outputComponent.append(Component.text(alias));
|
||||
}
|
||||
|
||||
output.add(outputComponent);
|
||||
output.add(outputComponent.build());
|
||||
}
|
||||
|
||||
bot.chat.tellraw(Component.join(JoinConfiguration.noSeparators(), output), player);
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
package me.chayapak1.chomens_bot.plugins;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import me.chayapak1.chomens_bot.Bot;
|
||||
import me.chayapak1.chomens_bot.data.chat.ChatPacketType;
|
||||
import me.chayapak1.chomens_bot.data.chat.PlayerMessage;
|
||||
import me.chayapak1.chomens_bot.data.filter.FilteredPlayer;
|
||||
import me.chayapak1.chomens_bot.data.listener.Listener;
|
||||
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
|
||||
import me.chayapak1.chomens_bot.util.UUIDUtilities;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.geysermc.mcprotocollib.network.event.session.DisconnectedEvent;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class FilterManagerPlugin implements Listener {
|
||||
private final Bot bot;
|
||||
|
||||
public final Map<PlayerEntry, String> list = new ConcurrentHashMap<>();
|
||||
public final List<FilteredPlayer> list = Collections.synchronizedList(new ObjectArrayList<>());
|
||||
|
||||
public FilterManagerPlugin (final Bot bot) {
|
||||
this.bot = bot;
|
||||
@@ -33,30 +35,39 @@ public class FilterManagerPlugin implements Listener {
|
||||
}
|
||||
|
||||
private void removeLeftPlayers () {
|
||||
// remove from list if player is not on the server anymore
|
||||
list.entrySet().removeIf(entry -> bot.players.getEntry(entry.getKey().profile.getId()) == null);
|
||||
synchronized (list) {
|
||||
// remove from list if player is not on the server anymore
|
||||
list.removeIf(filteredPlayer -> bot.players.getEntry(filteredPlayer.player().profile.getId()) == null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTick () {
|
||||
for (final PlayerEntry filtered : list.keySet()) {
|
||||
deOp(filtered);
|
||||
gameMode(filtered);
|
||||
clear(filtered);
|
||||
synchronized (list) {
|
||||
for (final FilteredPlayer filtered : list) {
|
||||
final PlayerEntry target = filtered.player();
|
||||
|
||||
deOp(target);
|
||||
gameMode(target);
|
||||
clear(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void kick () {
|
||||
for (final PlayerEntry filtered : list.keySet()) {
|
||||
bot.exploits.kick(filtered.profile.getId());
|
||||
synchronized (list) {
|
||||
for (final FilteredPlayer filtered : list) {
|
||||
final PlayerEntry target = filtered.player();
|
||||
bot.exploits.kick(target.profile.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommandSpyMessageReceived (final PlayerEntry sender, final String command) {
|
||||
final Pair<PlayerEntry, String> player = getFilteredFromName(sender.profile.getName());
|
||||
final FilteredPlayer filtered = getFilteredFromName(sender.profile.getName());
|
||||
|
||||
if (player == null) return;
|
||||
if (filtered == null) return;
|
||||
|
||||
if (
|
||||
command.startsWith("/mute") ||
|
||||
@@ -67,7 +78,7 @@ public class FilterManagerPlugin implements Listener {
|
||||
command.startsWith("/essentials:emute") ||
|
||||
command.startsWith("/essentials:silence") ||
|
||||
command.startsWith("/essentials:esilence")
|
||||
) mute(sender, player.getRight());
|
||||
) mute(sender, filtered.reason());
|
||||
|
||||
deOp(sender);
|
||||
gameMode(sender);
|
||||
@@ -78,11 +89,11 @@ public class FilterManagerPlugin implements Listener {
|
||||
public boolean onPlayerMessageReceived (final PlayerMessage message, final ChatPacketType packetType) {
|
||||
if (message.sender().profile.getName() == null) return true;
|
||||
|
||||
final Pair<PlayerEntry, String> player = getFilteredFromName(message.sender().profile.getName());
|
||||
final FilteredPlayer filtered = getFilteredFromName(message.sender().profile.getName());
|
||||
|
||||
if (player == null || message.sender().profile.getId().equals(new UUID(0L, 0L))) return true;
|
||||
if (filtered == null || message.sender().profile.getId().equals(new UUID(0L, 0L))) return true;
|
||||
|
||||
doAll(message.sender(), player.getRight());
|
||||
doAll(message.sender(), filtered.reason());
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -126,18 +137,22 @@ public class FilterManagerPlugin implements Listener {
|
||||
entry.profile.equals(bot.profile) // prevent self-harm !!!!!!
|
||||
) return;
|
||||
|
||||
list.put(entry, reason);
|
||||
list.add(new FilteredPlayer(entry, reason));
|
||||
|
||||
doAll(entry, reason);
|
||||
}
|
||||
|
||||
public void remove (final String name) {
|
||||
list.entrySet().removeIf(filtered -> filtered.getKey().profile.getName().equals(name));
|
||||
synchronized (list) {
|
||||
list.removeIf(filtered -> filtered.player().profile.getName().equals(name));
|
||||
}
|
||||
}
|
||||
|
||||
public Pair<PlayerEntry, String> getFilteredFromName (final String name) {
|
||||
for (final Map.Entry<PlayerEntry, String> entry : list.entrySet()) {
|
||||
if (entry.getKey().profile.getName().equals(name)) return Pair.of(entry.getKey(), entry.getValue());
|
||||
public @Nullable FilteredPlayer getFilteredFromName (final String name) {
|
||||
synchronized (list) {
|
||||
for (final FilteredPlayer filtered : list) {
|
||||
if (filtered.player().profile.getName().equals(name)) return filtered;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -12,6 +12,7 @@ import me.chayapak1.chomens_bot.song.SongLoaderThread;
|
||||
import me.chayapak1.chomens_bot.util.LoggerUtilities;
|
||||
import me.chayapak1.chomens_bot.util.MathUtilities;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.util.HSVLike;
|
||||
@@ -328,7 +329,7 @@ public class MusicPlayerPlugin implements Listener {
|
||||
bossBarColor = BossBarColor.YELLOW;
|
||||
}
|
||||
|
||||
Component component = Component.empty()
|
||||
final TextComponent.Builder component = Component.text()
|
||||
.append(Component.empty().append(Component.text(currentSong.name, nameColor)))
|
||||
.append(Component.text(" | ", NamedTextColor.DARK_GRAY))
|
||||
.append(
|
||||
@@ -342,7 +343,7 @@ public class MusicPlayerPlugin implements Listener {
|
||||
);
|
||||
|
||||
if (!bot.core.hasRateLimit()) {
|
||||
component = component
|
||||
component
|
||||
.append(Component.text(" | ", NamedTextColor.DARK_GRAY))
|
||||
.append(
|
||||
Component.translatable(
|
||||
@@ -354,7 +355,7 @@ public class MusicPlayerPlugin implements Listener {
|
||||
);
|
||||
|
||||
if (!currentLyrics.isBlank()) {
|
||||
component = component
|
||||
component
|
||||
.append(Component.text(" | ", NamedTextColor.DARK_GRAY))
|
||||
.append(Component.text(currentLyrics, NamedTextColor.BLUE));
|
||||
}
|
||||
@@ -363,16 +364,18 @@ public class MusicPlayerPlugin implements Listener {
|
||||
if (currentSong.paused) {
|
||||
return component
|
||||
.append(Component.text(" | ", NamedTextColor.DARK_GRAY))
|
||||
.append(Component.text("⏸", NamedTextColor.LIGHT_PURPLE));
|
||||
.append(Component.text("⏸", NamedTextColor.LIGHT_PURPLE))
|
||||
.build();
|
||||
}
|
||||
|
||||
if (loop != Loop.OFF) {
|
||||
return component
|
||||
.append(Component.translatable(" | ", NamedTextColor.DARK_GRAY))
|
||||
.append(Component.translatable("Looping " + ((loop == Loop.CURRENT) ? "current" : "all"), NamedTextColor.LIGHT_PURPLE));
|
||||
.append(Component.translatable("Looping " + ((loop == Loop.CURRENT) ? "current" : "all"), NamedTextColor.LIGHT_PURPLE))
|
||||
.build();
|
||||
}
|
||||
|
||||
return component;
|
||||
return component.build();
|
||||
}
|
||||
|
||||
public Component formatTime (final long millis) {
|
||||
|
||||
@@ -3,7 +3,7 @@ package me.chayapak1.chomens_bot.plugins;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import me.chayapak1.chomens_bot.Bot;
|
||||
import me.chayapak1.chomens_bot.Main;
|
||||
import me.chayapak1.chomens_bot.data.filter.FilteredPlayer;
|
||||
import me.chayapak1.chomens_bot.data.filter.PlayerFilter;
|
||||
import me.chayapak1.chomens_bot.data.listener.Listener;
|
||||
import me.chayapak1.chomens_bot.data.player.PlayerEntry;
|
||||
import me.chayapak1.chomens_bot.util.LoggerUtilities;
|
||||
@@ -23,7 +23,7 @@ public class PlayerFilterPlugin implements Listener {
|
||||
private static final String REMOVE_FILTER = "DELETE FROM filters WHERE name = ?;";
|
||||
private static final String CLEAR_FILTER = "DELETE FROM filters;";
|
||||
|
||||
public static List<FilteredPlayer> localList = new ObjectArrayList<>();
|
||||
public static List<PlayerFilter> localList = new ObjectArrayList<>();
|
||||
|
||||
static {
|
||||
if (Main.database != null) {
|
||||
@@ -49,21 +49,21 @@ public class PlayerFilterPlugin implements Listener {
|
||||
bot.listener.addListener(this);
|
||||
}
|
||||
|
||||
public static List<FilteredPlayer> list () {
|
||||
final List<FilteredPlayer> output = new ArrayList<>();
|
||||
public static List<PlayerFilter> list () {
|
||||
final List<PlayerFilter> output = new ArrayList<>();
|
||||
|
||||
try (final ResultSet result = Main.database.query(LIST_FILTERS)) {
|
||||
if (result == null) return output;
|
||||
|
||||
while (result.next()) {
|
||||
final FilteredPlayer filteredPlayer = new FilteredPlayer(
|
||||
final PlayerFilter playerFilter = new PlayerFilter(
|
||||
result.getString("name"),
|
||||
result.getString("reason"),
|
||||
result.getBoolean("regex"),
|
||||
result.getBoolean("ignoreCase")
|
||||
);
|
||||
|
||||
output.add(filteredPlayer);
|
||||
output.add(playerFilter);
|
||||
}
|
||||
} catch (final SQLException e) {
|
||||
LoggerUtilities.error(e);
|
||||
@@ -74,29 +74,29 @@ public class PlayerFilterPlugin implements Listener {
|
||||
return output;
|
||||
}
|
||||
|
||||
private FilteredPlayer getPlayer (final String name) {
|
||||
for (final FilteredPlayer filteredPlayer : localList) {
|
||||
if (matchesPlayer(name, filteredPlayer)) {
|
||||
return filteredPlayer;
|
||||
private PlayerFilter getPlayer (final String name) {
|
||||
for (final PlayerFilter playerFilter : localList) {
|
||||
if (matchesPlayer(name, playerFilter)) {
|
||||
return playerFilter;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<FilteredPlayer> getPlayers (final String name) {
|
||||
final List<FilteredPlayer> matches = new ArrayList<>();
|
||||
private List<PlayerFilter> getPlayers (final String name) {
|
||||
final List<PlayerFilter> matches = new ArrayList<>();
|
||||
|
||||
for (final FilteredPlayer filteredPlayer : localList) {
|
||||
if (matchesPlayer(name, filteredPlayer)) {
|
||||
matches.add(filteredPlayer);
|
||||
for (final PlayerFilter playerFilter : localList) {
|
||||
if (matchesPlayer(name, playerFilter)) {
|
||||
matches.add(playerFilter);
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
private boolean matchesPlayer (final String name, final FilteredPlayer player) {
|
||||
private boolean matchesPlayer (final String name, final PlayerFilter player) {
|
||||
if (player.regex()) {
|
||||
final Pattern pattern = compilePattern(player);
|
||||
|
||||
@@ -106,7 +106,7 @@ public class PlayerFilterPlugin implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
private Pattern compilePattern (final FilteredPlayer player) {
|
||||
private Pattern compilePattern (final PlayerFilter player) {
|
||||
try {
|
||||
final int flags = player.ignoreCase() ? Pattern.CASE_INSENSITIVE : 0;
|
||||
|
||||
@@ -118,7 +118,7 @@ public class PlayerFilterPlugin implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean compareNames (final String name, final FilteredPlayer player) {
|
||||
private boolean compareNames (final String name, final PlayerFilter player) {
|
||||
final String playerName = player.ignoreCase() ? player.playerName().toLowerCase() : player.playerName();
|
||||
final String targetName = player.ignoreCase() ? name.toLowerCase() : name;
|
||||
|
||||
@@ -128,7 +128,7 @@ public class PlayerFilterPlugin implements Listener {
|
||||
@Override
|
||||
public void onPlayerJoined (final PlayerEntry target) {
|
||||
bot.executorService.execute(() -> {
|
||||
final FilteredPlayer player = getPlayer(target.profile.getName());
|
||||
final PlayerFilter player = getPlayer(target.profile.getName());
|
||||
|
||||
if (player == null) return;
|
||||
|
||||
@@ -152,11 +152,11 @@ public class PlayerFilterPlugin implements Listener {
|
||||
bot.logger.error(e);
|
||||
}
|
||||
|
||||
final List<FilteredPlayer> matches = getPlayers(playerName);
|
||||
final List<PlayerFilter> matches = getPlayers(playerName);
|
||||
|
||||
// loop through all the servers too
|
||||
for (final Bot bot : bot.bots) {
|
||||
for (final FilteredPlayer match : matches) {
|
||||
for (final PlayerFilter match : matches) {
|
||||
final PlayerEntry entry = bot.players.getEntry(match.playerName());
|
||||
|
||||
if (entry == null) continue;
|
||||
@@ -183,7 +183,7 @@ public class PlayerFilterPlugin implements Listener {
|
||||
}
|
||||
|
||||
public void clear () {
|
||||
for (final FilteredPlayer player : localList) bot.filterManager.remove(player.playerName());
|
||||
for (final PlayerFilter player : localList) bot.filterManager.remove(player.playerName());
|
||||
|
||||
try {
|
||||
Main.database.update(CLEAR_FILTER);
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.chayapak1.chomens_bot.plugins;
|
||||
import me.chayapak1.chomens_bot.Bot;
|
||||
import me.chayapak1.chomens_bot.util.SNBTUtilities;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
|
||||
@@ -129,15 +130,15 @@ public class ScreensharePlugin {
|
||||
final ArrayList<Component> names = new ArrayList<>();
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
Component name = Component.empty();
|
||||
final TextComponent.Builder name = Component.text();
|
||||
|
||||
for (int x = 0; x < width; x++) {
|
||||
final Component pixel = Component.text("█", TextColor.fromHexString(screen[x][y]));
|
||||
|
||||
name = name.append(pixel);
|
||||
name.append(pixel);
|
||||
}
|
||||
|
||||
names.add(name);
|
||||
names.add(name.build());
|
||||
}
|
||||
|
||||
for (int i = 0; i < names.size(); i++) {
|
||||
|
||||
Reference in New Issue
Block a user